Revision: emacs@sv.gnu.org/emacs--devo--0--patch-163
[bpt/emacs.git] / lisp / gnus / gnus-art.el
CommitLineData
eec82323 1;;; gnus-art.el --- article mode commands for Gnus
e84b4b86
TTN
2
3;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
c4288669 4;; 2005, 2006 Free Software Foundation, Inc.
eec82323 5
6748645f 6;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
eec82323
LMI
7;; Keywords: news
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation; either version 2, or (at your option)
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24;; Boston, MA 02110-1301, USA.
eec82323
LMI
25
26;;; Commentary:
27
28;;; Code:
29
23f87bed
MB
30(eval-when-compile
31 (require 'cl)
9e36e870
JB
32 (defvar tool-bar-map)
33 (defvar w3m-minor-mode-map))
5ab7173c 34
eec82323
LMI
35(require 'gnus)
36(require 'gnus-sum)
37(require 'gnus-spec)
38(require 'gnus-int)
23f87bed 39(require 'gnus-win)
16409b0b
GM
40(require 'mm-bodies)
41(require 'mail-parse)
42(require 'mm-decode)
43(require 'mm-view)
44(require 'wid-edit)
45(require 'mm-uu)
23f87bed
MB
46(require 'message)
47
48(autoload 'gnus-msg-mail "gnus-msg" nil t)
49(autoload 'gnus-button-mailto "gnus-msg")
50(autoload 'gnus-button-reply "gnus-msg" nil t)
498063ec 51(autoload 'parse-time-string "parse-time" nil nil)
531bedc3 52(autoload 'mm-extern-cache-contents "mm-extern")
eec82323
LMI
53
54(defgroup gnus-article nil
55 "Article display."
23f87bed 56 :link '(custom-manual "(gnus)Article Buffer")
eec82323
LMI
57 :group 'gnus)
58
16409b0b
GM
59(defgroup gnus-article-treat nil
60 "Treating article parts."
61 :link '(custom-manual "(gnus)Article Hiding")
62 :group 'gnus-article)
63
eec82323
LMI
64(defgroup gnus-article-hiding nil
65 "Hiding article parts."
66 :link '(custom-manual "(gnus)Article Hiding")
67 :group 'gnus-article)
68
69(defgroup gnus-article-highlight nil
70 "Article highlighting."
71 :link '(custom-manual "(gnus)Article Highlighting")
72 :group 'gnus-article
73 :group 'gnus-visual)
74
75(defgroup gnus-article-signature nil
76 "Article signatures."
77 :link '(custom-manual "(gnus)Article Signature")
78 :group 'gnus-article)
79
80(defgroup gnus-article-headers nil
81 "Article headers."
82 :link '(custom-manual "(gnus)Hiding Headers")
83 :group 'gnus-article)
84
85(defgroup gnus-article-washing nil
86 "Special commands on articles."
87 :link '(custom-manual "(gnus)Article Washing")
88 :group 'gnus-article)
89
90(defgroup gnus-article-emphasis nil
91 "Fontisizing articles."
92 :link '(custom-manual "(gnus)Article Fontisizing")
93 :group 'gnus-article)
94
95(defgroup gnus-article-saving nil
96 "Saving articles."
97 :link '(custom-manual "(gnus)Saving Articles")
98 :group 'gnus-article)
99
100(defgroup gnus-article-mime nil
101 "Worshiping the MIME wonder."
102 :link '(custom-manual "(gnus)Using MIME")
103 :group 'gnus-article)
104
105(defgroup gnus-article-buttons nil
106 "Pushable buttons in the article buffer."
107 :link '(custom-manual "(gnus)Article Buttons")
108 :group 'gnus-article)
109
110(defgroup gnus-article-various nil
111 "Other article options."
112 :link '(custom-manual "(gnus)Misc Article")
113 :group 'gnus-article)
114
115(defcustom gnus-ignored-headers
23f87bed
MB
116 (mapcar
117 (lambda (header)
118 (concat "^" header ":"))
119 '("Path" "Expires" "Date-Received" "References" "Xref" "Lines"
120 "Relay-Version" "Message-ID" "Approved" "Sender" "Received"
121 "X-UIDL" "MIME-Version" "Return-Path" "In-Reply-To"
122 "Content-Type" "Content-Transfer-Encoding" "X-WebTV-Signature"
123 "X-MimeOLE" "X-MSMail-Priority" "X-Priority" "X-Loop"
124 "X-Authentication-Warning" "X-MIME-Autoconverted" "X-Face"
125 "X-Attribution" "X-Originating-IP" "Delivered-To"
126 "NNTP-[-A-Za-z]+" "Distribution" "X-no-archive" "X-Trace"
127 "X-Complaints-To" "X-NNTP-Posting-Host" "X-Orig.*"
128 "Abuse-Reports-To" "Cache-Post-Path" "X-Article-Creation-Date"
129 "X-Poster" "X-Mail2News-Path" "X-Server-Date" "X-Cache"
130 "Originator" "X-Problems-To" "X-Auth-User" "X-Post-Time"
131 "X-Admin" "X-UID" "Resent-[-A-Za-z]+" "X-Mailing-List"
132 "Precedence" "Original-[-A-Za-z]+" "X-filename" "X-Orcpt"
133 "Old-Received" "X-Pgp" "X-Auth" "X-From-Line"
134 "X-Gnus-Article-Number" "X-Majordomo" "X-Url" "X-Sender"
135 "MBOX-Line" "Priority" "X400-[-A-Za-z]+"
136 "Status" "X-Gnus-Mail-Source" "Cancel-Lock"
137 "X-FTN" "X-EXP32-SerialNo" "Encoding" "Importance"
138 "Autoforwarded" "Original-Encoded-Information-Types" "X-Ya-Pop3"
139 "X-Face-Version" "X-Vms-To" "X-ML-NAME" "X-ML-COUNT"
140 "Mailing-List" "X-finfo" "X-md5sum" "X-md5sum-Origin"
141 "X-Sun-Charset" "X-Accept-Language" "X-Envelope-Sender"
142 "List-[A-Za-z]+" "X-Listprocessor-Version"
143 "X-Received" "X-Distribute" "X-Sequence" "X-Juno-Line-Breaks"
144 "X-Notes-Item" "X-MS-TNEF-Correlator" "x-uunet-gateway"
145 "X-Received" "Content-length" "X-precedence"
146 "X-Authenticated-User" "X-Comment" "X-Report" "X-Abuse-Info"
147 "X-HTTP-Proxy" "X-Mydeja-Info" "X-Copyright" "X-No-Markup"
148 "X-Abuse-Info" "X-From_" "X-Accept-Language" "Errors-To"
149 "X-BeenThere" "X-Mailman-Version" "List-Help" "List-Post"
150 "List-Subscribe" "List-Id" "List-Unsubscribe" "List-Archive"
151 "X-Content-length" "X-Posting-Agent" "Original-Received"
152 "X-Request-PGP" "X-Fingerprint" "X-WRIEnvto" "X-WRIEnvfrom"
153 "X-Virus-Scanned" "X-Delivery-Agent" "Posted-Date" "X-Gateway"
154 "X-Local-Origin" "X-Local-Destination" "X-UserInfo1"
155 "X-Received-Date" "X-Hashcash" "Face" "X-DMCA-Notifications"
156 "X-Abuse-and-DMCA-Info" "X-Postfilter" "X-Gpg-.*" "X-Disclaimer"))
6748645f 157 "*All headers that start with this regexp will be hidden.
eec82323
LMI
158This variable can also be a list of regexps of headers to be ignored.
159If `gnus-visible-headers' is non-nil, this variable will be ignored."
160 :type '(choice :custom-show nil
161 regexp
162 (repeat regexp))
163 :group 'gnus-article-hiding)
164
165(defcustom gnus-visible-headers
23f87bed 166 "^From:\\|^Newsgroups:\\|^Subject:\\|^Date:\\|^Followup-To:\\|^Reply-To:\\|^Organization:\\|^Summary:\\|^Keywords:\\|^To:\\|^[BGF]?Cc:\\|^Posted-To:\\|^Mail-Copies-To:\\|^Mail-Followup-To:\\|^Apparently-To:\\|^Gnus-Warning:\\|^Resent-From:\\|^X-Sent:"
6748645f 167 "*All headers that do not match this regexp will be hidden.
eec82323
LMI
168This variable can also be a list of regexp of headers to remain visible.
169If this variable is non-nil, `gnus-ignored-headers' will be ignored."
170 :type '(repeat :value-to-internal (lambda (widget value)
171 (custom-split-regexp-maybe value))
172 :match (lambda (widget value)
173 (or (stringp value)
174 (widget-editable-list-match widget value)))
175 regexp)
176 :group 'gnus-article-hiding)
177
178(defcustom gnus-sorted-header-list
179 '("^From:" "^Subject:" "^Summary:" "^Keywords:" "^Newsgroups:"
180 "^Followup-To:" "^To:" "^Cc:" "^Date:" "^Organization:")
6748645f 181 "*This variable is a list of regular expressions.
eec82323
LMI
182If it is non-nil, headers that match the regular expressions will
183be placed first in the article buffer in the sequence specified by
184this list."
185 :type '(repeat regexp)
186 :group 'gnus-article-hiding)
187
188(defcustom gnus-boring-article-headers '(empty followup-to reply-to)
189 "Headers that are only to be displayed if they have interesting data.
23f87bed
MB
190Possible values in this list are:
191
192 'empty Headers with no content.
193 'newsgroups Newsgroup identical to Gnus group.
194 'to-address To identical to To-address.
195 'to-list To identical to To-list.
196 'cc-list CC identical to To-list.
197 'followup-to Followup-to identical to Newsgroups.
198 'reply-to Reply-to identical to From.
199 'date Date less than four days old.
200 'long-to To and/or Cc longer than 1024 characters.
201 'many-to Multiple To and/or Cc."
eec82323 202 :type '(set (const :tag "Headers with no content." empty)
23f87bed
MB
203 (const :tag "Newsgroups identical to Gnus group." newsgroups)
204 (const :tag "To identical to To-address." to-address)
205 (const :tag "To identical to To-list." to-list)
206 (const :tag "CC identical to To-list." cc-list)
207 (const :tag "Followup-to identical to Newsgroups." followup-to)
208 (const :tag "Reply-to identical to From." reply-to)
6748645f 209 (const :tag "Date less than four days old." date)
23f87bed 210 (const :tag "To and/or Cc longer than 1024 characters." long-to)
16409b0b 211 (const :tag "Multiple To and/or Cc headers." many-to))
eec82323
LMI
212 :group 'gnus-article-hiding)
213
23f87bed
MB
214(defcustom gnus-article-skip-boring nil
215 "Skip over text that is not worth reading.
216By default, if you set this t, then Gnus will display citations and
217signatures, but will never scroll down to show you a page consisting
218only of boring text. Boring text is controlled by
219`gnus-article-boring-faces'."
bf247b6e 220 :version "22.1"
23f87bed
MB
221 :type 'boolean
222 :group 'gnus-article-hiding)
223
eec82323
LMI
224(defcustom gnus-signature-separator '("^-- $" "^-- *$")
225 "Regexp matching signature separator.
226This can also be a list of regexps. In that case, it will be checked
227from head to tail looking for a separator. Searches will be done from
228the end of the buffer."
3031d8b0
MB
229 :type '(choice :format "%{%t%}: %[Value Menu%]\n%v"
230 (regexp)
231 (repeat :tag "List of regexp" regexp))
eec82323
LMI
232 :group 'gnus-article-signature)
233
234(defcustom gnus-signature-limit nil
16409b0b 235 "Provide a limit to what is considered a signature.
eec82323
LMI
236If it is a number, no signature may not be longer (in characters) than
237that number. If it is a floating point number, no signature may be
238longer (in lines) than that number. If it is a function, the function
239will be called without any parameters, and if it returns nil, there is
240no signature in the buffer. If it is a string, it will be used as a
241regexp. If it matches, the text in question is not a signature."
4a2358e9
MB
242 :type '(choice (const nil)
243 (integer :value 200)
6748645f
LMI
244 (number :value 4.0)
245 (function :value fun)
246 (regexp :value ".*"))
eec82323
LMI
247 :group 'gnus-article-signature)
248
249(defcustom gnus-hidden-properties '(invisible t intangible t)
250 "Property list to use for hiding text."
251 :type 'sexp
252 :group 'gnus-article-hiding)
253
23f87bed
MB
254;; Fixme: This isn't the right thing for mixed graphical and non-graphical
255;; frames in a session.
eec82323 256(defcustom gnus-article-x-face-command
23f87bed
MB
257 (if (featurep 'xemacs)
258 (if (or (gnus-image-type-available-p 'xface)
259 (gnus-image-type-available-p 'pbm))
260 'gnus-display-x-face-in-from
261 "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | ee -")
262 (if (gnus-image-type-available-p 'pbm)
263 'gnus-display-x-face-in-from
e0bad764
DL
264 "{ echo '/* Width=48, Height=48 */'; uncompface; } | icontopbm | \
265display -"))
6748645f 266 "*String or function to be executed to display an X-Face header.
eec82323
LMI
267If it is a string, the command will be executed in a sub-shell
268asynchronously. The compressed face will be piped to this command."
23f87bed
MB
269 :type `(choice string
270 (function-item gnus-display-x-face-in-from)
16409b0b 271 function)
b5a206e7 272 :version "21.1"
23f87bed 273 :group 'gnus-picon
eec82323
LMI
274 :group 'gnus-article-washing)
275
276(defcustom gnus-article-x-face-too-ugly nil
277 "Regexp matching posters whose face shouldn't be shown automatically."
4bb6a3a6 278 :type '(choice regexp (const nil))
eec82323
LMI
279 :group 'gnus-article-washing)
280
e0bad764
DL
281(defcustom gnus-article-banner-alist nil
282 "Banner alist for stripping.
a1506d29 283For example,
23f87bed 284 ((egroups . \"^[ \\t\\n]*-------------------+\\\\( \\\\(e\\\\|Yahoo! \\\\)Groups Sponsor -+\\\\)?....\\n\\\\(.+\\n\\\\)+\"))"
fc2c2db8 285 :version "21.1"
e0bad764
DL
286 :type '(repeat (cons symbol regexp))
287 :group 'gnus-article-washing)
288
23f87bed
MB
289(gnus-define-group-parameter
290 banner
291 :variable-document
292 "Alist of regexps (to match group names) and banner."
293 :variable-group gnus-article-washing
294 :parameter-type
295 '(choice :tag "Banner"
296 :value nil
297 (const :tag "Remove signature" signature)
298 (symbol :tag "Item in `gnus-article-banner-alist'" none)
299 regexp
300 (const :tag "None" nil))
301 :parameter-document
302 "If non-nil, specify how to remove `banners' from articles.
303
304Symbol `signature' means to remove signatures delimited by
305`gnus-signature-separator'. Any other symbol is used to look up a
306regular expression to match the banner in `gnus-article-banner-alist'.
307A string is used as a regular expression to match the banner
308directly.")
309
310(defcustom gnus-article-address-banner-alist nil
311 "Alist of mail addresses and banners.
312Each element has the form (ADDRESS . BANNER), where ADDRESS is a regexp
313to match a mail address in the From: header, BANNER is one of a symbol
314`signature', an item in `gnus-article-banner-alist', a regexp and nil.
315If ADDRESS matches author's mail address, it will remove things like
316advertisements. For example:
317
318\((\"@yoo-hoo\\\\.co\\\\.jp\\\\'\" . \"\\n_+\\nDo You Yoo-hoo!\\\\?\\n.*\\n.*\\n\"))
319"
320 :type '(repeat
321 (cons
322 (regexp :tag "Address")
323 (choice :tag "Banner" :value nil
324 (const :tag "Remove signature" signature)
325 (symbol :tag "Item in `gnus-article-banner-alist'" none)
326 regexp
327 (const :tag "None" nil))))
bf247b6e 328 :version "22.1"
23f87bed
MB
329 :group 'gnus-article-washing)
330
ae465fa7
MB
331(defmacro gnus-emphasis-custom-with-format (&rest body)
332 `(let ((format "\
333\\(\\s-\\|^\\|\\=\\|[-\"]\\|\\s(\\)\\(%s\\(\\w+\\(\\s-+\\w+\\)*[.,]?\\)%s\\)\
334\\(\\([-,.;:!?\"]\\|\\s)\\)+\\s-\\|[?!.]\\s-\\|\\s)\\|\\s-\\)"))
335 ,@body))
336
337(defun gnus-emphasis-custom-value-to-external (value)
338 (gnus-emphasis-custom-with-format
339 (if (consp (car value))
340 (list (format format (car (car value)) (cdr (car value)))
341 2
342 (if (nth 1 value) 2 3)
343 (nth 2 value))
344 value)))
345
346(defun gnus-emphasis-custom-value-to-internal (value)
347 (gnus-emphasis-custom-with-format
348 (let ((regexp (concat "\\`"
349 (format (regexp-quote format)
350 "\\([^()]+\\)" "\\([^()]+\\)")
351 "\\'"))
352 pattern)
353 (if (string-match regexp (setq pattern (car value)))
354 (list (cons (match-string 1 pattern) (match-string 2 pattern))
355 (= (nth 2 value) 2)
356 (nth 3 value))
357 value))))
358
eec82323 359(defcustom gnus-emphasis-alist
ae465fa7
MB
360 (let ((types
361 '(("\\*" "\\*" bold nil 2)
23f87bed 362 ("_" "_" underline)
eec82323 363 ("/" "/" italic)
eec82323
LMI
364 ("_/" "/_" underline-italic)
365 ("_\\*" "\\*_" underline-bold)
366 ("\\*/" "/\\*" bold-italic)
367 ("_\\*/" "/\\*_" underline-bold-italic))))
ae465fa7
MB
368 (nconc
369 (gnus-emphasis-custom-with-format
370 (mapcar (lambda (spec)
371 (list (format format (car spec) (cadr spec))
372 (or (nth 3 spec) 2)
373 (or (nth 4 spec) 3)
374 (intern (format "gnus-emphasis-%s" (nth 2 spec)))))
375 types))
95f75c75
SM
376 '(;; I've never seen anyone use this strikethru convention whereas I've
377 ;; several times seen it triggered by normal text. --Stef
378 ;; Miles suggests that this form is sometimes used but for italics,
379 ;; so maybe we should map it to `italic'.
380 ;; ("\\(\\s-\\|^\\)\\(-\\(\\(\\w\\|-[^-]\\)+\\)-\\)\\(\\s-\\|[?!.,;]\\)"
381 ;; 2 3 gnus-emphasis-strikethru)
ae465fa7
MB
382 ("\\(\\s-\\|^\\)\\(_\\(\\(\\w\\|_[^_]\\)+\\)_\\)\\(\\s-\\|[?!.,;]\\)"
383 2 3 gnus-emphasis-underline))))
6748645f 384 "*Alist that says how to fontify certain phrases.
eec82323
LMI
385Each item looks like this:
386
387 (\"_\\\\(\\\\w+\\\\)_\" 0 1 'underline)
388
389The first element is a regular expression to be matched. The second
390is a number that says what regular expression grouping used to find
391the entire emphasized word. The third is a number that says what
392regexp grouping should be displayed and highlighted. The fourth
393is the face used for highlighting."
ae465fa7
MB
394 :type
395 '(repeat
396 (menu-choice
397 :format "%[Customizing Style%]\n%v"
398 :indent 2
399 (group :tag "Default"
400 :value ("" 0 0 default)
401 :value-create
402 (lambda (widget)
403 (let ((value (widget-get
404 (cadr (widget-get (widget-get widget :parent)
405 :args))
406 :value)))
407 (if (not (eq (nth 2 value) 'default))
408 (widget-put
409 widget
410 :value
411 (gnus-emphasis-custom-value-to-external value))))
412 (widget-group-value-create widget))
ad136a7c
MB
413 regexp
414 (integer :format "Match group: %v")
415 (integer :format "Emphasize group: %v")
ae465fa7
MB
416 face)
417 (group :tag "Simple"
418 :value (("_" . "_") nil default)
419 (cons :format "%v"
ad136a7c
MB
420 (regexp :format "Start regexp: %v")
421 (regexp :format "End regexp: %v"))
ae465fa7
MB
422 (boolean :format "Show start and end patterns: %[%v%]\n"
423 :on " On " :off " Off ")
424 face)))
425 :get (lambda (symbol)
426 (mapcar 'gnus-emphasis-custom-value-to-internal
427 (default-value symbol)))
428 :set (lambda (symbol value)
429 (set-default symbol (mapcar 'gnus-emphasis-custom-value-to-external
430 value)))
eec82323
LMI
431 :group 'gnus-article-emphasis)
432
16409b0b
GM
433(defcustom gnus-emphasize-whitespace-regexp "^[ \t]+\\|[ \t]*\n"
434 "A regexp to describe whitespace which should not be emphasized.
435Typical values are \"^[ \\t]+\\\\|[ \\t]*\\n\" and \"[ \\t]+\\\\|[ \\t]*\\n\".
436The former avoids underlining of leading and trailing whitespace,
437and the latter avoids underlining any whitespace at all."
fc2c2db8 438 :version "21.1"
16409b0b
GM
439 :group 'gnus-article-emphasis
440 :type 'regexp)
441
23f87bed 442(defface gnus-emphasis-bold '((t (:bold t)))
eec82323
LMI
443 "Face used for displaying strong emphasized text (*word*)."
444 :group 'gnus-article-emphasis)
445
23f87bed 446(defface gnus-emphasis-italic '((t (:italic t)))
eec82323
LMI
447 "Face used for displaying italic emphasized text (/word/)."
448 :group 'gnus-article-emphasis)
449
450(defface gnus-emphasis-underline '((t (:underline t)))
451 "Face used for displaying underlined emphasized text (_word_)."
452 :group 'gnus-article-emphasis)
453
23f87bed 454(defface gnus-emphasis-underline-bold '((t (:bold t :underline t)))
eec82323
LMI
455 "Face used for displaying underlined bold emphasized text (_*word*_)."
456 :group 'gnus-article-emphasis)
457
23f87bed 458(defface gnus-emphasis-underline-italic '((t (:italic t :underline t)))
8de38c21 459 "Face used for displaying underlined italic emphasized text (_/word/_)."
eec82323
LMI
460 :group 'gnus-article-emphasis)
461
23f87bed 462(defface gnus-emphasis-bold-italic '((t (:bold t :italic t)))
eec82323
LMI
463 "Face used for displaying bold italic emphasized text (/*word*/)."
464 :group 'gnus-article-emphasis)
465
466(defface gnus-emphasis-underline-bold-italic
23f87bed 467 '((t (:bold t :italic t :underline t)))
eec82323 468 "Face used for displaying underlined bold italic emphasized text.
8f688cb0 469Example: (_/*word*/_)."
eec82323
LMI
470 :group 'gnus-article-emphasis)
471
23f87bed
MB
472(defface gnus-emphasis-strikethru (if (featurep 'xemacs)
473 '((t (:strikethru t)))
474 '((t (:strike-through t))))
475 "Face used for displaying strike-through text (-word-)."
476 :group 'gnus-article-emphasis)
477
16409b0b
GM
478(defface gnus-emphasis-highlight-words
479 '((t (:background "black" :foreground "yellow")))
480 "Face used for displaying highlighted words."
481 :group 'gnus-article-emphasis)
482
eec82323
LMI
483(defcustom gnus-article-time-format "%a, %b %d %Y %T %Z"
484 "Format for display of Date headers in article bodies.
6748645f
LMI
485See `format-time-string' for the possible values.
486
487The variable can also be function, which should return a complete Date
488header. The function is called with one argument, the time, which can
489be fed to `format-time-string'."
490 :type '(choice string symbol)
eec82323
LMI
491 :link '(custom-manual "(gnus)Article Date")
492 :group 'gnus-article-washing)
493
eec82323
LMI
494(defcustom gnus-save-all-headers t
495 "*If non-nil, don't remove any headers before saving."
496 :group 'gnus-article-saving
497 :type 'boolean)
498
499(defcustom gnus-prompt-before-saving 'always
500 "*This variable says how much prompting is to be done when saving articles.
501If it is nil, no prompting will be done, and the articles will be
502saved to the default files. If this variable is `always', each and
503every article that is saved will be preceded by a prompt, even when
504saving large batches of articles. If this variable is neither nil not
505`always', there the user will be prompted once for a file name for
506each invocation of the saving commands."
507 :group 'gnus-article-saving
508 :type '(choice (item always)
509 (item :tag "never" nil)
6748645f 510 (sexp :tag "once" :format "%t\n" :value t)))
eec82323
LMI
511
512(defcustom gnus-saved-headers gnus-visible-headers
513 "Headers to keep if `gnus-save-all-headers' is nil.
514If `gnus-save-all-headers' is non-nil, this variable will be ignored.
515If that variable is nil, however, all headers that match this regexp
516will be kept while the rest will be deleted before saving."
517 :group 'gnus-article-saving
4bb6a3a6 518 :type 'regexp)
eec82323
LMI
519
520(defcustom gnus-default-article-saver 'gnus-summary-save-in-rmail
521 "A function to save articles in your favourite format.
522The function must be interactively callable (in other words, it must
523be an Emacs command).
524
525Gnus provides the following functions:
526
527* gnus-summary-save-in-rmail (Rmail format)
528* gnus-summary-save-in-mail (Unix mail format)
529* gnus-summary-save-in-folder (MH folder)
530* gnus-summary-save-in-file (article format)
23f87bed 531* gnus-summary-save-body-in-file (article body)
eec82323
LMI
532* gnus-summary-save-in-vm (use VM's folder format)
533* gnus-summary-write-to-file (article format -- overwrite)."
534 :group 'gnus-article-saving
535 :type '(radio (function-item gnus-summary-save-in-rmail)
536 (function-item gnus-summary-save-in-mail)
537 (function-item gnus-summary-save-in-folder)
538 (function-item gnus-summary-save-in-file)
23f87bed 539 (function-item gnus-summary-save-body-in-file)
eec82323 540 (function-item gnus-summary-save-in-vm)
58090a8d
MB
541 (function-item gnus-summary-write-to-file)
542 (function)))
eec82323
LMI
543
544(defcustom gnus-rmail-save-name 'gnus-plain-save-name
545 "A function generating a file name to save articles in Rmail format.
546The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE."
547 :group 'gnus-article-saving
548 :type 'function)
549
550(defcustom gnus-mail-save-name 'gnus-plain-save-name
551 "A function generating a file name to save articles in Unix mail format.
552The function is called with NEWSGROUP, HEADERS, and optional LAST-FILE."
553 :group 'gnus-article-saving
554 :type 'function)
555
556(defcustom gnus-folder-save-name 'gnus-folder-save-name
557 "A function generating a file name to save articles in MH folder.
558The function is called with NEWSGROUP, HEADERS, and optional LAST-FOLDER."
559 :group 'gnus-article-saving
560 :type 'function)
561
562(defcustom gnus-file-save-name 'gnus-numeric-save-name
563 "A function generating a file name to save articles in article format.
564The function is called with NEWSGROUP, HEADERS, and optional
565LAST-FILE."
566 :group 'gnus-article-saving
567 :type 'function)
568
569(defcustom gnus-split-methods
570 '((gnus-article-archive-name)
571 (gnus-article-nndoc-name))
6748645f 572 "*Variable used to suggest where articles are to be saved.
eec82323
LMI
573For instance, if you would like to save articles related to Gnus in
574the file \"gnus-stuff\", and articles related to VM in \"vm-stuff\",
575you could set this variable to something like:
576
577 '((\"^Subject:.*gnus\\|^Newsgroups:.*gnus\" \"gnus-stuff\")
578 (\"^Subject:.*vm\\|^Xref:.*vm\" \"vm-stuff\"))
579
580This variable is an alist where the where the key is the match and the
581value is a list of possible files to save in if the match is non-nil.
582
583If the match is a string, it is used as a regexp match on the
584article. If the match is a symbol, that symbol will be funcalled
585from the buffer of the article to be saved with the newsgroup as the
586parameter. If it is a list, it will be evaled in the same buffer.
587
588If this form or function returns a string, this string will be used as
589a possible file name; and if it returns a non-nil list, that list will
590be used as possible file names."
591 :group 'gnus-article-saving
6748645f
LMI
592 :type '(repeat (choice (list :value (fun) function)
593 (cons :value ("" "") regexp (repeat string))
594 (sexp :value nil))))
eec82323 595
eec82323
LMI
596(defcustom gnus-page-delimiter "^\^L"
597 "*Regexp describing what to use as article page delimiters.
598The default value is \"^\^L\", which is a form linefeed at the
599beginning of a line."
600 :type 'regexp
601 :group 'gnus-article-various)
602
16409b0b 603(defcustom gnus-article-mode-line-format "Gnus: %g [%w] %S%m"
eec82323 604 "*The format specification for the article mode line.
16409b0b
GM
605See `gnus-summary-mode-line-format' for a closer description.
606
607The following additional specs are available:
608
609%w The article washing status.
610%m The number of MIME parts in the article."
eec82323
LMI
611 :type 'string
612 :group 'gnus-article-various)
613
614(defcustom gnus-article-mode-hook nil
615 "*A hook for Gnus article mode."
616 :type 'hook
617 :group 'gnus-article-various)
618
23f87bed
MB
619(when (featurep 'xemacs)
620 ;; Extracted from gnus-xmas-define in order to preserve user settings
621 (when (fboundp 'turn-off-scroll-in-place)
622 (add-hook 'gnus-article-mode-hook 'turn-off-scroll-in-place))
623 ;; Extracted from gnus-xmas-redefine in order to preserve user settings
624 (add-hook 'gnus-article-mode-hook 'gnus-xmas-article-menu-add))
625
eec82323
LMI
626(defcustom gnus-article-menu-hook nil
627 "*Hook run after the creation of the article mode menu."
628 :type 'hook
629 :group 'gnus-article-various)
630
631(defcustom gnus-article-prepare-hook nil
16409b0b 632 "*A hook called after an article has been prepared in the article buffer."
eec82323
LMI
633 :type 'hook
634 :group 'gnus-article-various)
635
23f87bed
MB
636(make-obsolete-variable 'gnus-article-hide-pgp-hook
637 "This variable is obsolete in Gnus 5.10.")
a8151ef7 638
eec82323
LMI
639(defcustom gnus-article-button-face 'bold
640 "Face used for highlighting buttons in the article buffer.
641
642An article button is a piece of text that you can activate by pressing
643`RET' or `mouse-2' above it."
644 :type 'face
645 :group 'gnus-article-buttons)
646
647(defcustom gnus-article-mouse-face 'highlight
648 "Face used for mouse highlighting in the article buffer.
649
650Article buttons will be displayed in this face when the cursor is
651above them."
652 :type 'face
653 :group 'gnus-article-buttons)
654
0f49874b 655(defcustom gnus-signature-face 'gnus-signature
a8151ef7 656 "Face used for highlighting a signature in the article buffer.
0f49874b 657Obsolete; use the face `gnus-signature' for customizations instead."
eec82323
LMI
658 :type 'face
659 :group 'gnus-article-highlight
660 :group 'gnus-article-signature)
661
0f49874b 662(defface gnus-signature
f59a3415 663 '((t
23f87bed 664 (:italic t)))
a8151ef7
LMI
665 "Face used for highlighting a signature in the article buffer."
666 :group 'gnus-article-highlight
667 :group 'gnus-article-signature)
0f49874b
MB
668;; backward-compatibility alias
669(put 'gnus-signature-face 'face-alias 'gnus-signature)
a8151ef7 670
0f49874b 671(defface gnus-header-from
eec82323
LMI
672 '((((class color)
673 (background dark))
6748645f 674 (:foreground "spring green"))
eec82323
LMI
675 (((class color)
676 (background light))
6748645f 677 (:foreground "red3"))
eec82323 678 (t
23f87bed 679 (:italic t)))
eec82323
LMI
680 "Face used for displaying from headers."
681 :group 'gnus-article-headers
682 :group 'gnus-article-highlight)
0f49874b
MB
683;; backward-compatibility alias
684(put 'gnus-header-from-face 'face-alias 'gnus-header-from)
eec82323 685
0f49874b 686(defface gnus-header-subject
eec82323
LMI
687 '((((class color)
688 (background dark))
6748645f 689 (:foreground "SeaGreen3"))
eec82323
LMI
690 (((class color)
691 (background light))
6748645f 692 (:foreground "red4"))
eec82323 693 (t
23f87bed 694 (:bold t :italic t)))
eec82323
LMI
695 "Face used for displaying subject headers."
696 :group 'gnus-article-headers
697 :group 'gnus-article-highlight)
0f49874b
MB
698;; backward-compatibility alias
699(put 'gnus-header-subject-face 'face-alias 'gnus-header-subject)
eec82323 700
0f49874b 701(defface gnus-header-newsgroups
eec82323
LMI
702 '((((class color)
703 (background dark))
23f87bed 704 (:foreground "yellow" :italic t))
eec82323
LMI
705 (((class color)
706 (background light))
23f87bed 707 (:foreground "MidnightBlue" :italic t))
eec82323 708 (t
23f87bed
MB
709 (:italic t)))
710 "Face used for displaying newsgroups headers.
711In the default setup this face is only used for crossposted
712articles."
eec82323
LMI
713 :group 'gnus-article-headers
714 :group 'gnus-article-highlight)
0f49874b
MB
715;; backward-compatibility alias
716(put 'gnus-header-newsgroups-face 'face-alias 'gnus-header-newsgroups)
eec82323 717
0f49874b 718(defface gnus-header-name
eec82323
LMI
719 '((((class color)
720 (background dark))
721 (:foreground "SeaGreen"))
722 (((class color)
723 (background light))
724 (:foreground "maroon"))
725 (t
23f87bed 726 (:bold t)))
eec82323
LMI
727 "Face used for displaying header names."
728 :group 'gnus-article-headers
729 :group 'gnus-article-highlight)
0f49874b
MB
730;; backward-compatibility alias
731(put 'gnus-header-name-face 'face-alias 'gnus-header-name)
eec82323 732
0f49874b 733(defface gnus-header-content
eec82323
LMI
734 '((((class color)
735 (background dark))
23f87bed 736 (:foreground "forest green" :italic t))
eec82323
LMI
737 (((class color)
738 (background light))
23f87bed 739 (:foreground "indianred4" :italic t))
eec82323 740 (t
23f87bed 741 (:italic t))) "Face used for displaying header content."
eec82323
LMI
742 :group 'gnus-article-headers
743 :group 'gnus-article-highlight)
0f49874b
MB
744;; backward-compatibility alias
745(put 'gnus-header-content-face 'face-alias 'gnus-header-content)
eec82323
LMI
746
747(defcustom gnus-header-face-alist
0f49874b
MB
748 '(("From" nil gnus-header-from)
749 ("Subject" nil gnus-header-subject)
750 ("Newsgroups:.*," nil gnus-header-newsgroups)
751 ("" gnus-header-name gnus-header-content))
23f87bed 752 "*Controls highlighting of article headers.
eec82323
LMI
753
754An alist of the form (HEADER NAME CONTENT).
755
23f87bed
MB
756HEADER is a regular expression which should match the name of a
757header and NAME and CONTENT are either face names or nil.
eec82323
LMI
758
759The name of each header field will be displayed using the face
23f87bed
MB
760specified by the first element in the list where HEADER matches
761the header name and NAME is non-nil. Similarly, the content will
762be displayed by the first non-nil matching CONTENT face."
eec82323
LMI
763 :group 'gnus-article-headers
764 :group 'gnus-article-highlight
765 :type '(repeat (list (regexp :tag "Header")
766 (choice :tag "Name"
767 (item :tag "skip" nil)
768 (face :value default))
769 (choice :tag "Content"
770 (item :tag "skip" nil)
771 (face :value default)))))
772
16409b0b 773(defcustom gnus-article-decode-hook
23f87bed
MB
774 '(article-decode-charset article-decode-encoded-words
775 article-decode-group-name article-decode-idna-rhs)
16409b0b
GM
776 "*Hook run to decode charsets in articles."
777 :group 'gnus-article-headers
778 :type 'hook)
779
780(defcustom gnus-display-mime-function 'gnus-display-mime
781 "Function to display MIME articles."
782 :group 'gnus-article-mime
783 :type 'function)
784
785(defvar gnus-decode-header-function 'mail-decode-encoded-word-region
786 "Function used to decode headers.")
787
788(defvar gnus-article-dumbquotes-map
23f87bed
MB
789 '(("\200" "EUR")
790 ("\202" ",")
16409b0b
GM
791 ("\203" "f")
792 ("\204" ",,")
793 ("\205" "...")
794 ("\213" "<")
795 ("\214" "OE")
796 ("\221" "`")
797 ("\222" "'")
798 ("\223" "``")
799 ("\224" "\"")
800 ("\225" "*")
e0bad764
DL
801 ("\226" "-")
802 ("\227" "--")
23f87bed 803 ("\230" "~")
16409b0b
GM
804 ("\231" "(TM)")
805 ("\233" ">")
806 ("\234" "oe")
807 ("\264" "'"))
808 "Table for MS-to-Latin1 translation.")
809
810(defcustom gnus-ignored-mime-types nil
811 "List of MIME types that should be ignored by Gnus."
fc2c2db8 812 :version "21.1"
16409b0b
GM
813 :group 'gnus-article-mime
814 :type '(repeat regexp))
815
816(defcustom gnus-unbuttonized-mime-types '(".*/.*")
23f87bed
MB
817 "List of MIME types that should not be given buttons when rendered inline.
818See also `gnus-buttonized-mime-types' which may override this variable.
819This variable is only used when `gnus-inhibit-mime-unbuttonizing' is nil."
820 :version "21.1"
821 :group 'gnus-article-mime
822 :type '(repeat regexp))
823
824(defcustom gnus-buttonized-mime-types nil
825 "List of MIME types that should be given buttons when rendered inline.
826If set, this variable overrides `gnus-unbuttonized-mime-types'.
827To see e.g. security buttons you could set this to
3031d8b0
MB
828`(\"multipart/signed\")'. You could also add \"multipart/alternative\" to
829this list to display radio buttons that allow you to choose one of two
830media types those mails include. See also `mm-discouraged-alternatives'.
23f87bed 831This variable is only used when `gnus-inhibit-mime-unbuttonizing' is nil."
bf247b6e 832 :version "22.1"
16409b0b
GM
833 :group 'gnus-article-mime
834 :type '(repeat regexp))
835
23f87bed
MB
836(defcustom gnus-inhibit-mime-unbuttonizing nil
837 "If non-nil, all MIME parts get buttons.
838When nil (the default value), then some MIME parts do not get buttons,
839as described by the variables `gnus-buttonized-mime-types' and
840`gnus-unbuttonized-mime-types'."
bf247b6e 841 :version "22.1"
d0859c9a 842 :group 'gnus-article-mime
23f87bed
MB
843 :type 'boolean)
844
845(defcustom gnus-body-boundary-delimiter "_"
846 "String used to delimit header and body.
847This variable is used by `gnus-article-treat-body-boundary' which can
848be controlled by `gnus-treat-body-boundary'."
bf247b6e 849 :version "22.1"
23f87bed
MB
850 :group 'gnus-article-various
851 :type '(choice (item :tag "None" :value nil)
852 string))
853
97f78c9b
MB
854(defcustom gnus-picon-databases '("/usr/lib/picon" "/usr/local/faces"
855 "/usr/share/picons")
23f87bed
MB
856 "Defines the location of the faces database.
857For information on obtaining this database of pretty pictures, please
858see http://www.cs.indiana.edu/picons/ftp/index.html"
bf247b6e 859 :version "22.1"
23f87bed
MB
860 :type '(repeat directory)
861 :link '(url-link :tag "download"
862 "http://www.cs.indiana.edu/picons/ftp/index.html")
863 :link '(custom-manual "(gnus)Picons")
864 :group 'gnus-picon)
865
866(defun gnus-picons-installed-p ()
867 "Say whether picons are installed on your machine."
868 (let ((installed nil))
869 (dolist (database gnus-picon-databases)
870 (when (file-exists-p database)
871 (setq installed t)))
872 installed))
873
16409b0b
GM
874(defcustom gnus-article-mime-part-function nil
875 "Function called with a MIME handle as the argument.
876This is meant for people who want to do something automatic based
877on parts -- for instance, adding Vcard info to a database."
878 :group 'gnus-article-mime
4a2358e9
MB
879 :type '(choice (const nil)
880 function))
16409b0b
GM
881
882(defcustom gnus-mime-multipart-functions nil
fc2c2db8
DL
883 "An alist of MIME types to functions to display them."
884 :version "21.1"
885 :group 'gnus-article-mime
886 :type 'alist)
16409b0b
GM
887
888(defcustom gnus-article-date-lapsed-new-header nil
889 "Whether the X-Sent and Date headers can coexist.
890When using `gnus-treat-date-lapsed', the \"X-Sent:\" header will
891either replace the old \"Date:\" header (if this variable is nil), or
892be added below it (otherwise)."
fc2c2db8 893 :version "21.1"
16409b0b
GM
894 :group 'gnus-article-headers
895 :type 'boolean)
896
897(defcustom gnus-article-mime-match-handle-function 'undisplayed-alternative
898 "Function called with a MIME handle as the argument.
899This is meant for people who want to view first matched part.
a1506d29
JB
900For `undisplayed-alternative' (default), the first undisplayed
901part or alternative part is used. For `undisplayed', the first
902undisplayed part is used. For a function, the first part which
f20b2f5c 903the function return t is used. For nil, the first part is
16409b0b 904used."
fc2c2db8 905 :version "21.1"
16409b0b 906 :group 'gnus-article-mime
a1506d29 907 :type '(choice
16409b0b
GM
908 (item :tag "first" :value nil)
909 (item :tag "undisplayed" :value undisplayed)
a1506d29 910 (item :tag "undisplayed or alternative"
16409b0b
GM
911 :value undisplayed-alternative)
912 (function)))
913
e0bad764
DL
914(defcustom gnus-mime-action-alist
915 '(("save to file" . gnus-mime-save-part)
23f87bed
MB
916 ("save and strip" . gnus-mime-save-part-and-strip)
917 ("delete part" . gnus-mime-delete-part)
e0bad764
DL
918 ("display as text" . gnus-mime-inline-part)
919 ("view the part" . gnus-mime-view-part)
920 ("pipe to command" . gnus-mime-pipe-part)
921 ("toggle display" . gnus-article-press-button)
23f87bed 922 ("toggle display" . gnus-article-view-part-as-charset)
e0bad764 923 ("view as type" . gnus-mime-view-part-as-type)
23f87bed
MB
924 ("view internally" . gnus-mime-view-part-internally)
925 ("view externally" . gnus-mime-view-part-externally))
e0bad764
DL
926 "An alist of actions that run on the MIME attachment."
927 :group 'gnus-article-mime
928 :type '(repeat (cons (string :tag "name")
929 (function))))
930
16409b0b
GM
931;;;
932;;; The treatment variables
933;;;
934
935(defvar gnus-part-display-hook nil
936 "Hook called on parts that are to receive treatment.")
937
938(defvar gnus-article-treat-custom
939 '(choice (const :tag "Off" nil)
940 (const :tag "On" t)
941 (const :tag "Header" head)
942 (const :tag "Last" last)
943 (integer :tag "Less")
944 (repeat :tag "Groups" regexp)
945 (sexp :tag "Predicate")))
946
947(defvar gnus-article-treat-head-custom
948 '(choice (const :tag "Off" nil)
949 (const :tag "Header" head)))
950
951(defvar gnus-article-treat-types '("text/plain")
952 "Parts to treat.")
953
954(defvar gnus-inhibit-treatment nil
955 "Whether to inhibit treatment.")
956
23f87bed 957(defcustom gnus-treat-highlight-signature '(or t (typep "text/x-vcard"))
16409b0b
GM
958 "Highlight the signature.
959Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 960See Info node `(gnus)Customizing Articles'."
16409b0b 961 :group 'gnus-article-treat
23f87bed 962 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
963 :type gnus-article-treat-custom)
964(put 'gnus-treat-highlight-signature 'highlight t)
965
966(defcustom gnus-treat-buttonize 100000
967 "Add buttons.
968Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 969See Info node `(gnus)Customizing Articles'."
16409b0b 970 :group 'gnus-article-treat
23f87bed 971 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
972 :type gnus-article-treat-custom)
973(put 'gnus-treat-buttonize 'highlight t)
974
975(defcustom gnus-treat-buttonize-head 'head
976 "Add buttons to the head.
977Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 978See Info node `(gnus)Customizing Articles' for details."
16409b0b 979 :group 'gnus-article-treat
23f87bed 980 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
981 :type gnus-article-treat-head-custom)
982(put 'gnus-treat-buttonize-head 'highlight t)
983
a1506d29 984(defcustom gnus-treat-emphasize
74dd1b0d
SZ
985 (and (or window-system
986 (featurep 'xemacs)
987 (>= (string-to-number emacs-version) 21))
988 50000)
16409b0b
GM
989 "Emphasize text.
990Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 991See Info node `(gnus)Customizing Articles' for details."
16409b0b 992 :group 'gnus-article-treat
23f87bed 993 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
994 :type gnus-article-treat-custom)
995(put 'gnus-treat-emphasize 'highlight t)
996
997(defcustom gnus-treat-strip-cr nil
998 "Remove carriage returns.
999Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1000See Info node `(gnus)Customizing Articles' for details."
bf247b6e 1001 :version "22.1"
16409b0b 1002 :group 'gnus-article-treat
23f87bed
MB
1003 :link '(custom-manual "(gnus)Customizing Articles")
1004 :type gnus-article-treat-custom)
1005
1006(defcustom gnus-treat-unsplit-urls nil
1007 "Remove newlines from within URLs.
1008Valid values are nil, t, `head', `last', an integer or a predicate.
1009See Info node `(gnus)Customizing Articles' for details."
bf247b6e 1010 :version "22.1"
23f87bed
MB
1011 :group 'gnus-article-treat
1012 :link '(custom-manual "(gnus)Customizing Articles")
1013 :type gnus-article-treat-custom)
1014
1015(defcustom gnus-treat-leading-whitespace nil
1016 "Remove leading whitespace in headers.
1017Valid values are nil, t, `head', `last', an integer or a predicate.
1018See Info node `(gnus)Customizing Articles' for details."
bf247b6e 1019 :version "22.1"
23f87bed
MB
1020 :group 'gnus-article-treat
1021 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1022 :type gnus-article-treat-custom)
1023
1024(defcustom gnus-treat-hide-headers 'head
1025 "Hide headers.
1026Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1027See Info node `(gnus)Customizing Articles' for details."
16409b0b 1028 :group 'gnus-article-treat
23f87bed 1029 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1030 :type gnus-article-treat-head-custom)
1031
1032(defcustom gnus-treat-hide-boring-headers nil
1033 "Hide boring headers.
1034Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1035See Info node `(gnus)Customizing Articles' for details."
16409b0b 1036 :group 'gnus-article-treat
23f87bed 1037 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1038 :type gnus-article-treat-head-custom)
1039
1040(defcustom gnus-treat-hide-signature nil
1041 "Hide the signature.
1042Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1043See Info node `(gnus)Customizing Articles' for details."
16409b0b 1044 :group 'gnus-article-treat
23f87bed 1045 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1046 :type gnus-article-treat-custom)
1047
1048(defcustom gnus-treat-fill-article nil
1049 "Fill the article.
1050Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1051See Info node `(gnus)Customizing Articles' for details."
16409b0b 1052 :group 'gnus-article-treat
23f87bed 1053 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1054 :type gnus-article-treat-custom)
1055
1056(defcustom gnus-treat-hide-citation nil
1057 "Hide cited text.
1058Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1059See Info node `(gnus)Customizing Articles' for details."
16409b0b 1060 :group 'gnus-article-treat
23f87bed 1061 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1062 :type gnus-article-treat-custom)
1063
e0bad764
DL
1064(defcustom gnus-treat-hide-citation-maybe nil
1065 "Hide cited text.
1066Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1067See Info node `(gnus)Customizing Articles' for details."
e0bad764 1068 :group 'gnus-article-treat
23f87bed 1069 :link '(custom-manual "(gnus)Customizing Articles")
e0bad764
DL
1070 :type gnus-article-treat-custom)
1071
16409b0b
GM
1072(defcustom gnus-treat-strip-list-identifiers 'head
1073 "Strip list identifiers from `gnus-list-identifiers`.
1074Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1075See Info node `(gnus)Customizing Articles' for details."
fc2c2db8 1076 :version "21.1"
16409b0b 1077 :group 'gnus-article-treat
23f87bed 1078 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1079 :type gnus-article-treat-custom)
1080
23f87bed
MB
1081(make-obsolete-variable 'gnus-treat-strip-pgp
1082 "This option is obsolete in Gnus 5.10.")
16409b0b
GM
1083
1084(defcustom gnus-treat-strip-pem nil
1085 "Strip PEM signatures.
1086Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1087See Info node `(gnus)Customizing Articles' for details."
16409b0b 1088 :group 'gnus-article-treat
23f87bed 1089 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1090 :type gnus-article-treat-custom)
1091
1092(defcustom gnus-treat-strip-banner t
1093 "Strip banners from articles.
1094The banner to be stripped is specified in the `banner' group parameter.
1095Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1096See Info node `(gnus)Customizing Articles' for details."
16409b0b 1097 :group 'gnus-article-treat
23f87bed 1098 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1099 :type gnus-article-treat-custom)
1100
1101(defcustom gnus-treat-highlight-headers 'head
1102 "Highlight the headers.
1103Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1104See Info node `(gnus)Customizing Articles' for details."
16409b0b 1105 :group 'gnus-article-treat
23f87bed 1106 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1107 :type gnus-article-treat-head-custom)
1108(put 'gnus-treat-highlight-headers 'highlight t)
1109
1110(defcustom gnus-treat-highlight-citation t
1111 "Highlight cited text.
1112Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1113See Info node `(gnus)Customizing Articles' for details."
16409b0b 1114 :group 'gnus-article-treat
23f87bed 1115 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1116 :type gnus-article-treat-custom)
1117(put 'gnus-treat-highlight-citation 'highlight t)
1118
1119(defcustom gnus-treat-date-ut nil
1120 "Display the Date in UT (GMT).
1121Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1122See Info node `(gnus)Customizing Articles' for details."
16409b0b 1123 :group 'gnus-article-treat
23f87bed 1124 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1125 :type gnus-article-treat-head-custom)
1126
1127(defcustom gnus-treat-date-local nil
1128 "Display the Date in the local timezone.
1129Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed
MB
1130See Info node `(gnus)Customizing Articles' for details."
1131 :group 'gnus-article-treat
1132 :link '(custom-manual "(gnus)Customizing Articles")
1133 :type gnus-article-treat-head-custom)
1134
1135(defcustom gnus-treat-date-english nil
1136 "Display the Date in a format that can be read aloud in English.
1137Valid values are nil, t, `head', `last', an integer or a predicate.
1138See Info node `(gnus)Customizing Articles' for details."
bf247b6e 1139 :version "22.1"
16409b0b 1140 :group 'gnus-article-treat
23f87bed 1141 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1142 :type gnus-article-treat-head-custom)
1143
1144(defcustom gnus-treat-date-lapsed nil
1145 "Display the Date header in a way that says how much time has elapsed.
1146Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1147See Info node `(gnus)Customizing Articles' for details."
16409b0b 1148 :group 'gnus-article-treat
23f87bed 1149 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1150 :type gnus-article-treat-head-custom)
1151
1152(defcustom gnus-treat-date-original nil
1153 "Display the date in the original timezone.
1154Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1155See Info node `(gnus)Customizing Articles' for details."
16409b0b 1156 :group 'gnus-article-treat
23f87bed 1157 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1158 :type gnus-article-treat-head-custom)
1159
1160(defcustom gnus-treat-date-iso8601 nil
1161 "Display the date in the ISO8601 format.
1162Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1163See Info node `(gnus)Customizing Articles' for details."
fc2c2db8 1164 :version "21.1"
16409b0b 1165 :group 'gnus-article-treat
23f87bed 1166 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1167 :type gnus-article-treat-head-custom)
1168
1169(defcustom gnus-treat-date-user-defined nil
1170 "Display the date in a user-defined format.
1171The format is defined by the `gnus-article-time-format' variable.
1172Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1173See Info node `(gnus)Customizing Articles' for details."
16409b0b 1174 :group 'gnus-article-treat
23f87bed 1175 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1176 :type gnus-article-treat-head-custom)
1177
1178(defcustom gnus-treat-strip-headers-in-body t
1179 "Strip the X-No-Archive header line from the beginning of the body.
1180Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1181See Info node `(gnus)Customizing Articles' for details."
fc2c2db8 1182 :version "21.1"
16409b0b 1183 :group 'gnus-article-treat
23f87bed 1184 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1185 :type gnus-article-treat-custom)
1186
1187(defcustom gnus-treat-strip-trailing-blank-lines nil
1188 "Strip trailing blank lines.
1189Valid values are nil, t, `head', `last', an integer or a predicate.
292f71fe
MB
1190See Info node `(gnus)Customizing Articles' for details.
1191
1192When set to t, it also strips trailing blanks in all MIME parts.
1193Consider to use `last' instead."
16409b0b 1194 :group 'gnus-article-treat
23f87bed 1195 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1196 :type gnus-article-treat-custom)
1197
1198(defcustom gnus-treat-strip-leading-blank-lines nil
1199 "Strip leading blank lines.
1200Valid values are nil, t, `head', `last', an integer or a predicate.
292f71fe
MB
1201See Info node `(gnus)Customizing Articles' for details.
1202
1203When set to t, it also strips trailing blanks in all MIME parts."
16409b0b 1204 :group 'gnus-article-treat
23f87bed 1205 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1206 :type gnus-article-treat-custom)
1207
1208(defcustom gnus-treat-strip-multiple-blank-lines nil
1209 "Strip multiple blank lines.
1210Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1211See Info node `(gnus)Customizing Articles' for details."
16409b0b 1212 :group 'gnus-article-treat
23f87bed
MB
1213 :link '(custom-manual "(gnus)Customizing Articles")
1214 :type gnus-article-treat-custom)
1215
1216(defcustom gnus-treat-unfold-headers 'head
1217 "Unfold folded header lines.
1218Valid values are nil, t, `head', `last', an integer or a predicate.
1219See Info node `(gnus)Customizing Articles' for details."
bf247b6e 1220 :version "22.1"
23f87bed
MB
1221 :group 'gnus-article-treat
1222 :link '(custom-manual "(gnus)Customizing Articles")
1223 :type gnus-article-treat-custom)
1224
1225(defcustom gnus-treat-fold-headers nil
1226 "Fold headers.
1227Valid values are nil, t, `head', `last', an integer or a predicate.
1228See Info node `(gnus)Customizing Articles' for details."
bf247b6e 1229 :version "22.1"
23f87bed
MB
1230 :group 'gnus-article-treat
1231 :link '(custom-manual "(gnus)Customizing Articles")
1232 :type gnus-article-treat-custom)
1233
1234(defcustom gnus-treat-fold-newsgroups 'head
1235 "Fold the Newsgroups and Followup-To headers.
1236Valid values are nil, t, `head', `last', an integer or a predicate.
1237See Info node `(gnus)Customizing Articles' for details."
bf247b6e 1238 :version "22.1"
23f87bed
MB
1239 :group 'gnus-article-treat
1240 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1241 :type gnus-article-treat-custom)
1242
1243(defcustom gnus-treat-overstrike t
1244 "Treat overstrike highlighting.
1245Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1246See Info node `(gnus)Customizing Articles' for details."
16409b0b 1247 :group 'gnus-article-treat
23f87bed 1248 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1249 :type gnus-article-treat-custom)
1250(put 'gnus-treat-overstrike 'highlight t)
1251
23f87bed
MB
1252(make-obsolete-variable 'gnus-treat-display-xface
1253 'gnus-treat-display-x-face)
1254
1255(defcustom gnus-treat-display-x-face
1256 (and (not noninteractive)
1257 (or (and (fboundp 'image-type-available-p)
e0bad764 1258 (image-type-available-p 'xbm)
23f87bed
MB
1259 (string-match "^0x" (shell-command-to-string "uncompface"))
1260 (executable-find "icontopbm"))
1261 (and (featurep 'xemacs)
1262 (featurep 'xface)))
8b93df01 1263 'head)
16409b0b
GM
1264 "Display X-Face headers.
1265Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed
MB
1266See Info node `(gnus)Customizing Articles' and Info node
1267`(gnus)X-Face' for details."
1268 :group 'gnus-article-treat
1269 :version "21.1"
1270 :link '(custom-manual "(gnus)Customizing Articles")
1271 :link '(custom-manual "(gnus)X-Face")
1272 :type gnus-article-treat-head-custom
1273 :set (lambda (symbol value)
1274 (set-default
1275 symbol
1276 (cond ((or (boundp symbol) (get symbol 'saved-value))
1277 value)
1278 ((boundp 'gnus-treat-display-xface)
1279 (message "\
1280** gnus-treat-display-xface is an obsolete variable;\
1281 use gnus-treat-display-x-face instead")
1282 (default-value 'gnus-treat-display-xface))
1283 ((get 'gnus-treat-display-xface 'saved-value)
1284 (message "\
1285** gnus-treat-display-xface is an obsolete variable;\
1286 use gnus-treat-display-x-face instead")
1287 (eval (car (get 'gnus-treat-display-xface 'saved-value))))
1288 (t
1289 value)))))
1290(put 'gnus-treat-display-x-face 'highlight t)
1291
1292(defcustom gnus-treat-display-face
1293 (and (not noninteractive)
1294 (or (and (fboundp 'image-type-available-p)
1295 (image-type-available-p 'png))
1296 (and (featurep 'xemacs)
1297 (featurep 'png)))
1298 'head)
1299 "Display Face headers.
1300Valid values are nil, t, `head', `last', an integer or a predicate.
1301See Info node `(gnus)Customizing Articles' and Info node
1302`(gnus)X-Face' for details."
16409b0b 1303 :group 'gnus-article-treat
bf247b6e 1304 :version "22.1"
23f87bed
MB
1305 :link '(custom-manual "(gnus)Customizing Articles")
1306 :link '(custom-manual "(gnus)X-Face")
16409b0b 1307 :type gnus-article-treat-head-custom)
23f87bed 1308(put 'gnus-treat-display-face 'highlight t)
16409b0b 1309
a1506d29 1310(defcustom gnus-treat-display-smileys
e0bad764
DL
1311 (if (or (and (featurep 'xemacs)
1312 (featurep 'xpm))
1313 (and (fboundp 'image-type-available-p)
1314 (image-type-available-p 'pbm)))
1315 t nil)
16409b0b
GM
1316 "Display smileys.
1317Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed
MB
1318See Info node `(gnus)Customizing Articles' and Info node
1319`(gnus)Smileys' for details."
16409b0b 1320 :group 'gnus-article-treat
b5a206e7 1321 :version "21.1"
23f87bed
MB
1322 :link '(custom-manual "(gnus)Customizing Articles")
1323 :link '(custom-manual "(gnus)Smileys")
16409b0b
GM
1324 :type gnus-article-treat-custom)
1325(put 'gnus-treat-display-smileys 'highlight t)
1326
23f87bed
MB
1327(defcustom gnus-treat-from-picon
1328 (if (and (gnus-image-type-available-p 'xpm)
1329 (gnus-picons-installed-p))
1330 'head nil)
1331 "Display picons in the From header.
16409b0b 1332Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed
MB
1333See Info node `(gnus)Customizing Articles' and Info node
1334`(gnus)Picons' for details."
bf247b6e 1335 :version "22.1"
16409b0b 1336 :group 'gnus-article-treat
23f87bed
MB
1337 :group 'gnus-picon
1338 :link '(custom-manual "(gnus)Customizing Articles")
1339 :link '(custom-manual "(gnus)Picons")
1340 :type gnus-article-treat-head-custom)
1341(put 'gnus-treat-from-picon 'highlight t)
1342
1343(defcustom gnus-treat-mail-picon
1344 (if (and (gnus-image-type-available-p 'xpm)
1345 (gnus-picons-installed-p))
1346 'head nil)
1347 "Display picons in To and Cc headers.
1348Valid values are nil, t, `head', `last', an integer or a predicate.
1349See Info node `(gnus)Customizing Articles' and Info node
1350`(gnus)Picons' for details."
bf247b6e 1351 :version "22.1"
23f87bed
MB
1352 :group 'gnus-article-treat
1353 :group 'gnus-picon
1354 :link '(custom-manual "(gnus)Customizing Articles")
1355 :link '(custom-manual "(gnus)Picons")
1356 :type gnus-article-treat-head-custom)
1357(put 'gnus-treat-mail-picon 'highlight t)
1358
1359(defcustom gnus-treat-newsgroups-picon
1360 (if (and (gnus-image-type-available-p 'xpm)
1361 (gnus-picons-installed-p))
1362 'head nil)
1363 "Display picons in the Newsgroups and Followup-To headers.
1364Valid values are nil, t, `head', `last', an integer or a predicate.
1365See Info node `(gnus)Customizing Articles' and Info node
1366`(gnus)Picons' for details."
bf247b6e 1367 :version "22.1"
23f87bed
MB
1368 :group 'gnus-article-treat
1369 :group 'gnus-picon
1370 :link '(custom-manual "(gnus)Customizing Articles")
1371 :link '(custom-manual "(gnus)Picons")
1372 :type gnus-article-treat-head-custom)
1373(put 'gnus-treat-newsgroups-picon 'highlight t)
1374
1375(defcustom gnus-treat-body-boundary
1376 (if (or gnus-treat-newsgroups-picon
1377 gnus-treat-mail-picon
1378 gnus-treat-from-picon)
1379 'head nil)
1380 "Draw a boundary at the end of the headers.
1381Valid values are nil and `head'.
1382See Info node `(gnus)Customizing Articles' for details."
bf247b6e 1383 :version "22.1"
23f87bed
MB
1384 :group 'gnus-article-treat
1385 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b 1386 :type gnus-article-treat-head-custom)
16409b0b
GM
1387
1388(defcustom gnus-treat-capitalize-sentences nil
1389 "Capitalize sentence-starting words.
1390Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1391See Info node `(gnus)Customizing Articles' for details."
fc2c2db8 1392 :version "21.1"
16409b0b 1393 :group 'gnus-article-treat
23f87bed
MB
1394 :link '(custom-manual "(gnus)Customizing Articles")
1395 :type gnus-article-treat-custom)
1396
1397(defcustom gnus-treat-wash-html nil
1398 "Format as HTML.
1399Valid values are nil, t, `head', `last', an integer or a predicate.
1400See Info node `(gnus)Customizing Articles' for details."
bf247b6e 1401 :version "22.1"
23f87bed
MB
1402 :group 'gnus-article-treat
1403 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1404 :type gnus-article-treat-custom)
1405
1406(defcustom gnus-treat-fill-long-lines nil
1407 "Fill long lines.
1408Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1409See Info node `(gnus)Customizing Articles' for details."
16409b0b 1410 :group 'gnus-article-treat
23f87bed 1411 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1412 :type gnus-article-treat-custom)
1413
1414(defcustom gnus-treat-play-sounds nil
1415 "Play sounds.
1416Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1417See Info node `(gnus)Customizing Articles' for details."
fc2c2db8 1418 :version "21.1"
16409b0b 1419 :group 'gnus-article-treat
23f87bed 1420 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1421 :type gnus-article-treat-custom)
1422
1423(defcustom gnus-treat-translate nil
1424 "Translate articles from one language to another.
1425Valid values are nil, t, `head', `last', an integer or a predicate.
23f87bed 1426See Info node `(gnus)Customizing Articles' for details."
fc2c2db8 1427 :version "21.1"
16409b0b 1428 :group 'gnus-article-treat
23f87bed 1429 :link '(custom-manual "(gnus)Customizing Articles")
16409b0b
GM
1430 :type gnus-article-treat-custom)
1431
23f87bed
MB
1432(defcustom gnus-treat-x-pgp-sig nil
1433 "Verify X-PGP-Sig.
1434To automatically treat X-PGP-Sig, set it to head.
1435Valid values are nil, t, `head', `last', an integer or a predicate.
1436See Info node `(gnus)Customizing Articles' for details."
bf247b6e 1437 :version "22.1"
23f87bed
MB
1438 :group 'gnus-article-treat
1439 :group 'mime-security
1440 :link '(custom-manual "(gnus)Customizing Articles")
1441 :type gnus-article-treat-custom)
1442
1443(defvar gnus-article-encrypt-protocol-alist
1444 '(("PGP" . mml2015-self-encrypt)))
1445
1446;; Set to nil if more than one protocol added to
1447;; gnus-article-encrypt-protocol-alist.
1448(defcustom gnus-article-encrypt-protocol "PGP"
1449 "The protocol used for encrypt articles.
1450It is a string, such as \"PGP\". If nil, ask user."
bf247b6e 1451 :version "22.1"
23f87bed
MB
1452 :type 'string
1453 :group 'mime-security)
1454
1455(defvar gnus-article-wash-function nil
1456 "Function used for converting HTML into text.")
1457
1458(defcustom gnus-use-idna (and (condition-case nil (require 'idna) (file-error))
1459 (mm-coding-system-p 'utf-8)
1460 (executable-find idna-program))
1461 "Whether IDNA decoding of headers is used when viewing messages.
1462This requires GNU Libidn, and by default only enabled if it is found."
bf247b6e 1463 :version "22.1"
23f87bed
MB
1464 :group 'gnus-article-headers
1465 :type 'boolean)
1466
1467(defcustom gnus-article-over-scroll nil
1468 "If non-nil, allow scrolling the article buffer even when there no more text."
bf247b6e 1469 :version "22.1"
23f87bed
MB
1470 :group 'gnus-article
1471 :type 'boolean)
1472
eec82323
LMI
1473;;; Internal variables
1474
23f87bed
MB
1475(defvar gnus-english-month-names
1476 '("January" "February" "March" "April" "May" "June" "July" "August"
1477 "September" "October" "November" "December"))
1478
16409b0b
GM
1479(defvar article-goto-body-goes-to-point-min-p nil)
1480(defvar gnus-article-wash-types nil)
1481(defvar gnus-article-emphasis-alist nil)
23f87bed 1482(defvar gnus-article-image-alist nil)
16409b0b
GM
1483
1484(defvar gnus-article-mime-handle-alist-1 nil)
1485(defvar gnus-treatment-function-alist
23f87bed
MB
1486 '((gnus-treat-x-pgp-sig gnus-article-verify-x-pgp-sig)
1487 (gnus-treat-strip-banner gnus-article-strip-banner)
16409b0b
GM
1488 (gnus-treat-strip-headers-in-body gnus-article-strip-headers-in-body)
1489 (gnus-treat-highlight-signature gnus-article-highlight-signature)
1490 (gnus-treat-buttonize gnus-article-add-buttons)
1491 (gnus-treat-fill-article gnus-article-fill-cited-article)
1492 (gnus-treat-fill-long-lines gnus-article-fill-long-lines)
1493 (gnus-treat-strip-cr gnus-article-remove-cr)
23f87bed
MB
1494 (gnus-treat-unsplit-urls gnus-article-unsplit-urls)
1495 (gnus-treat-date-ut gnus-article-date-ut)
1496 (gnus-treat-date-local gnus-article-date-local)
1497 (gnus-treat-date-english gnus-article-date-english)
23f87bed
MB
1498 (gnus-treat-date-original gnus-article-date-original)
1499 (gnus-treat-date-user-defined gnus-article-date-user)
1500 (gnus-treat-date-iso8601 gnus-article-date-iso8601)
f3f01d5d 1501 (gnus-treat-date-lapsed gnus-article-date-lapsed)
23f87bed
MB
1502 (gnus-treat-display-x-face gnus-article-display-x-face)
1503 (gnus-treat-display-face gnus-article-display-face)
16409b0b
GM
1504 (gnus-treat-hide-headers gnus-article-maybe-hide-headers)
1505 (gnus-treat-hide-boring-headers gnus-article-hide-boring-headers)
1506 (gnus-treat-hide-signature gnus-article-hide-signature)
16409b0b 1507 (gnus-treat-strip-list-identifiers gnus-article-hide-list-identifiers)
23f87bed 1508 (gnus-treat-leading-whitespace gnus-article-remove-leading-whitespace)
16409b0b 1509 (gnus-treat-strip-pem gnus-article-hide-pem)
23f87bed
MB
1510 (gnus-treat-from-picon gnus-treat-from-picon)
1511 (gnus-treat-mail-picon gnus-treat-mail-picon)
1512 (gnus-treat-newsgroups-picon gnus-treat-newsgroups-picon)
16409b0b 1513 (gnus-treat-highlight-headers gnus-article-highlight-headers)
16409b0b 1514 (gnus-treat-highlight-signature gnus-article-highlight-signature)
16409b0b
GM
1515 (gnus-treat-strip-trailing-blank-lines
1516 gnus-article-remove-trailing-blank-lines)
1517 (gnus-treat-strip-leading-blank-lines
1518 gnus-article-strip-leading-blank-lines)
1519 (gnus-treat-strip-multiple-blank-lines
1520 gnus-article-strip-multiple-blank-lines)
1521 (gnus-treat-overstrike gnus-article-treat-overstrike)
23f87bed
MB
1522 (gnus-treat-unfold-headers gnus-article-treat-unfold-headers)
1523 (gnus-treat-fold-headers gnus-article-treat-fold-headers)
1524 (gnus-treat-fold-newsgroups gnus-article-treat-fold-newsgroups)
16409b0b 1525 (gnus-treat-buttonize-head gnus-article-add-buttons-to-head)
23f87bed 1526 (gnus-treat-display-smileys gnus-treat-smiley)
16409b0b 1527 (gnus-treat-capitalize-sentences gnus-article-capitalize-sentences)
23f87bed
MB
1528 (gnus-treat-wash-html gnus-article-wash-html)
1529 (gnus-treat-emphasize gnus-article-emphasize)
1530 (gnus-treat-hide-citation gnus-article-hide-citation)
1531 (gnus-treat-hide-citation-maybe gnus-article-hide-citation-maybe)
1532 (gnus-treat-highlight-citation gnus-article-highlight-citation)
1533 (gnus-treat-body-boundary gnus-article-treat-body-boundary)
16409b0b
GM
1534 (gnus-treat-play-sounds gnus-earcon-display)))
1535
1536(defvar gnus-article-mime-handle-alist nil)
6748645f
LMI
1537(defvar article-lapsed-timer nil)
1538(defvar gnus-article-current-summary nil)
1539
eec82323
LMI
1540(defvar gnus-article-mode-syntax-table
1541 (let ((table (copy-syntax-table text-mode-syntax-table)))
23f87bed
MB
1542 ;; This causes the citation match run O(2^n).
1543 ;; (modify-syntax-entry ?- "w" table)
1544 (modify-syntax-entry ?> ")<" table)
1545 (modify-syntax-entry ?< "(>" table)
1546 ;; make M-. in article buffers work for `foo' strings
1547 (modify-syntax-entry ?' " " table)
1548 (modify-syntax-entry ?` " " table)
eec82323
LMI
1549 table)
1550 "Syntax table used in article mode buffers.
1551Initialized from `text-mode-syntax-table.")
1552
1553(defvar gnus-save-article-buffer nil)
1554
1555(defvar gnus-article-mode-line-format-alist
16409b0b
GM
1556 (nconc '((?w (gnus-article-wash-status) ?s)
1557 (?m (gnus-article-mime-part-status) ?s))
6748645f 1558 gnus-summary-mode-line-format-alist))
eec82323
LMI
1559
1560(defvar gnus-number-of-articles-to-be-saved nil)
1561
1562(defvar gnus-inhibit-hiding nil)
1563
c1d7d285
MB
1564(defvar gnus-article-edit-mode nil)
1565
23f87bed
MB
1566;;; Macros for dealing with the article buffer.
1567
1568(defmacro gnus-with-article-headers (&rest forms)
1569 `(save-excursion
1570 (set-buffer gnus-article-buffer)
1571 (save-restriction
1572 (let ((inhibit-read-only t)
1573 (inhibit-point-motion-hooks t)
1574 (case-fold-search t))
1575 (article-narrow-to-head)
1576 ,@forms))))
1577
1578(put 'gnus-with-article-headers 'lisp-indent-function 0)
1579(put 'gnus-with-article-headers 'edebug-form-spec '(body))
1580
1581(defmacro gnus-with-article-buffer (&rest forms)
1582 `(save-excursion
1583 (set-buffer gnus-article-buffer)
1584 (let ((inhibit-read-only t))
1585 ,@forms)))
1586
1587(put 'gnus-with-article-buffer 'lisp-indent-function 0)
1588(put 'gnus-with-article-buffer 'edebug-form-spec '(body))
1589
1590(defun gnus-article-goto-header (header)
1591 "Go to HEADER, which is a regular expression."
1592 (re-search-forward (concat "^\\(" header "\\):") nil t))
1593
eec82323
LMI
1594(defsubst gnus-article-hide-text (b e props)
1595 "Set text PROPS on the B to E region, extending `intangible' 1 past B."
520aa572 1596 (gnus-add-text-properties-when 'article-type nil b e props)
eec82323
LMI
1597 (when (memq 'intangible props)
1598 (put-text-property
1599 (max (1- b) (point-min))
1600 b 'intangible (cddr (memq 'intangible props)))))
520aa572 1601
eec82323
LMI
1602(defsubst gnus-article-unhide-text (b e)
1603 "Remove hidden text properties from region between B and E."
1604 (remove-text-properties b e gnus-hidden-properties)
1605 (when (memq 'intangible gnus-hidden-properties)
1606 (put-text-property (max (1- b) (point-min))
1607 b 'intangible nil)))
1608
1609(defun gnus-article-hide-text-type (b e type)
1610 "Hide text of TYPE between B and E."
23f87bed 1611 (gnus-add-wash-type type)
eec82323
LMI
1612 (gnus-article-hide-text
1613 b e (cons 'article-type (cons type gnus-hidden-properties))))
1614
1615(defun gnus-article-unhide-text-type (b e type)
6748645f 1616 "Unhide text of TYPE between B and E."
23f87bed 1617 (gnus-delete-wash-type type)
eec82323
LMI
1618 (remove-text-properties
1619 b e (cons 'article-type (cons type gnus-hidden-properties)))
1620 (when (memq 'intangible gnus-hidden-properties)
1621 (put-text-property (max (1- b) (point-min))
1622 b 'intangible nil)))
1623
1624(defun gnus-article-hide-text-of-type (type)
1625 "Hide text of TYPE in the current buffer."
1626 (save-excursion
1627 (let ((b (point-min))
1628 (e (point-max)))
1629 (while (setq b (text-property-any b e 'article-type type))
1630 (add-text-properties b (incf b) gnus-hidden-properties)))))
1631
1632(defun gnus-article-delete-text-of-type (type)
1633 "Delete text of TYPE in the current buffer."
1634 (save-excursion
a8151ef7 1635 (let ((b (point-min)))
7dafe00b
MB
1636 (if (eq type 'multipart)
1637 ;; Remove MIME buttons associated with multipart/alternative parts.
1638 (progn
1639 (goto-char b)
1640 (while (if (get-text-property (point) 'gnus-part)
1641 (setq b (point))
1642 (when (setq b (next-single-property-change (point)
1643 'gnus-part))
1644 (goto-char b)
1645 t))
1646 (end-of-line)
1647 (skip-chars-forward "\n")
1648 (when (eq (get-text-property b 'article-type) 'multipart)
1649 (delete-region b (point)))))
1650 (while (setq b (text-property-any b (point-max) 'article-type type))
1651 (delete-region
1652 b (or (text-property-not-all b (point-max) 'article-type type)
1653 (point-max))))))))
eec82323
LMI
1654
1655(defun gnus-article-delete-invisible-text ()
1656 "Delete all invisible text in the current buffer."
1657 (save-excursion
a8151ef7
LMI
1658 (let ((b (point-min)))
1659 (while (setq b (text-property-any b (point-max) 'invisible t))
1660 (delete-region
1661 b (or (text-property-not-all b (point-max) 'invisible t)
1662 (point-max)))))))
eec82323
LMI
1663
1664(defun gnus-article-text-type-exists-p (type)
1665 "Say whether any text of type TYPE exists in the buffer."
1666 (text-property-any (point-min) (point-max) 'article-type type))
1667
1668(defsubst gnus-article-header-rank ()
1669 "Give the rank of the string HEADER as given by `gnus-sorted-header-list'."
1670 (let ((list gnus-sorted-header-list)
23f87bed 1671 (i 1))
eec82323 1672 (while list
23f87bed
MB
1673 (if (looking-at (car list))
1674 (setq list nil)
1675 (setq list (cdr list))
1676 (incf i)))
1677 i))
eec82323
LMI
1678
1679(defun article-hide-headers (&optional arg delete)
16409b0b
GM
1680 "Hide unwanted headers and possibly sort them as well."
1681 (interactive)
1682 ;; This function might be inhibited.
1683 (unless gnus-inhibit-hiding
23f87bed
MB
1684 (let ((inhibit-read-only nil)
1685 (case-fold-search t)
1686 (max (1+ (length gnus-sorted-header-list)))
1687 (inhibit-point-motion-hooks t)
1688 (cur (current-buffer))
1689 ignored visible beg)
1690 (save-excursion
1691 ;; `gnus-ignored-headers' and `gnus-visible-headers' may be
1692 ;; group parameters, so we should go to the summary buffer.
1693 (when (prog1
1694 (condition-case nil
1695 (progn (set-buffer gnus-summary-buffer) t)
1696 (error nil))
1697 (setq ignored (when (not gnus-visible-headers)
1698 (cond ((stringp gnus-ignored-headers)
1699 gnus-ignored-headers)
1700 ((listp gnus-ignored-headers)
1701 (mapconcat 'identity
1702 gnus-ignored-headers
1703 "\\|"))))
1704 visible (cond ((stringp gnus-visible-headers)
1705 gnus-visible-headers)
1706 ((and gnus-visible-headers
1707 (listp gnus-visible-headers))
1708 (mapconcat 'identity
1709 gnus-visible-headers
1710 "\\|")))))
1711 (set-buffer cur))
1712 (save-restriction
16409b0b
GM
1713 ;; First we narrow to just the headers.
1714 (article-narrow-to-head)
1715 ;; Hide any "From " lines at the beginning of (mail) articles.
1716 (while (looking-at "From ")
1717 (forward-line 1))
1718 (unless (bobp)
1719 (delete-region (point-min) (point)))
1720 ;; Then treat the rest of the header lines.
1721 ;; Then we use the two regular expressions
1722 ;; `gnus-ignored-headers' and `gnus-visible-headers' to
1723 ;; select which header lines is to remain visible in the
1724 ;; article buffer.
23f87bed 1725 (while (re-search-forward "^[^ \t:]*:" nil t)
16409b0b
GM
1726 (beginning-of-line)
1727 ;; Mark the rank of the header.
1728 (put-text-property
1729 (point) (1+ (point)) 'message-rank
1730 (if (or (and visible (looking-at visible))
1731 (and ignored
1732 (not (looking-at ignored))))
1733 (gnus-article-header-rank)
1734 (+ 2 max)))
1735 (forward-line 1))
1736 (message-sort-headers-1)
1737 (when (setq beg (text-property-any
1738 (point-min) (point-max) 'message-rank (+ 2 max)))
1739 ;; We delete the unwanted headers.
23f87bed 1740 (gnus-add-wash-type 'headers)
16409b0b
GM
1741 (add-text-properties (point-min) (+ 5 (point-min))
1742 '(article-type headers dummy-invisible t))
1743 (delete-region beg (point-max))))))))
eec82323
LMI
1744
1745(defun article-hide-boring-headers (&optional arg)
1746 "Toggle hiding of headers that aren't very interesting.
1747If given a negative prefix, always show; if given a positive prefix,
1748always hide."
1749 (interactive (gnus-article-hidden-arg))
1750 (when (and (not (gnus-article-check-hidden-text 'boring-headers arg))
1751 (not gnus-show-all-headers))
1752 (save-excursion
1753 (save-restriction
4e7d0221 1754 (let ((inhibit-read-only t)
eec82323
LMI
1755 (list gnus-boring-article-headers)
1756 (inhibit-point-motion-hooks t)
1757 elem)
16409b0b 1758 (article-narrow-to-head)
eec82323
LMI
1759 (while list
1760 (setq elem (pop list))
1761 (goto-char (point-min))
1762 (cond
1763 ;; Hide empty headers.
1764 ((eq elem 'empty)
16409b0b 1765 (while (re-search-forward "^[^: \t]+:[ \t]*\n[^ \t]" nil t)
eec82323
LMI
1766 (forward-line -1)
1767 (gnus-article-hide-text-type
23f87bed 1768 (gnus-point-at-bol)
eec82323
LMI
1769 (progn
1770 (end-of-line)
1771 (if (re-search-forward "^[^ \t]" nil t)
1772 (match-beginning 0)
1773 (point-max)))
1774 'boring-headers)))
1775 ;; Hide boring Newsgroups header.
1776 ((eq elem 'newsgroups)
23f87bed
MB
1777 (when (gnus-string-equal
1778 (gnus-fetch-field "newsgroups")
1779 (gnus-group-real-name
1780 (if (boundp 'gnus-newsgroup-name)
1781 gnus-newsgroup-name
1782 "")))
eec82323 1783 (gnus-article-hide-header "newsgroups")))
23f87bed
MB
1784 ((eq elem 'to-address)
1785 (let ((to (message-fetch-field "to"))
1786 (to-address
1787 (gnus-parameter-to-address
1788 (if (boundp 'gnus-newsgroup-name)
1789 gnus-newsgroup-name ""))))
1790 (when (and to to-address
1791 (ignore-errors
1792 (gnus-string-equal
1793 ;; only one address in To
1794 (nth 1 (mail-extract-address-components to))
1795 to-address)))
1796 (gnus-article-hide-header "to"))))
1797 ((eq elem 'to-list)
1798 (let ((to (message-fetch-field "to"))
1799 (to-list
1800 (gnus-parameter-to-list
1801 (if (boundp 'gnus-newsgroup-name)
1802 gnus-newsgroup-name ""))))
1803 (when (and to to-list
1804 (ignore-errors
1805 (gnus-string-equal
1806 ;; only one address in To
1807 (nth 1 (mail-extract-address-components to))
1808 to-list)))
1809 (gnus-article-hide-header "to"))))
1810 ((eq elem 'cc-list)
1811 (let ((cc (message-fetch-field "cc"))
1812 (to-list
1813 (gnus-parameter-to-list
1814 (if (boundp 'gnus-newsgroup-name)
1815 gnus-newsgroup-name ""))))
1816 (when (and cc to-list
1817 (ignore-errors
1818 (gnus-string-equal
1819 ;; only one address in CC
1820 (nth 1 (mail-extract-address-components cc))
1821 to-list)))
1822 (gnus-article-hide-header "cc"))))
eec82323 1823 ((eq elem 'followup-to)
23f87bed
MB
1824 (when (gnus-string-equal
1825 (message-fetch-field "followup-to")
1826 (message-fetch-field "newsgroups"))
eec82323
LMI
1827 (gnus-article-hide-header "followup-to")))
1828 ((eq elem 'reply-to)
23f87bed
MB
1829 (if (gnus-group-find-parameter
1830 gnus-newsgroup-name 'broken-reply-to)
1831 (gnus-article-hide-header "reply-to")
1832 (let ((from (message-fetch-field "from"))
1833 (reply-to (message-fetch-field "reply-to")))
1834 (when
1835 (and
eec82323
LMI
1836 from reply-to
1837 (ignore-errors
1838 (equal
23f87bed
MB
1839 (sort (mapcar
1840 (lambda (x) (downcase (cadr x)))
1841 (mail-extract-address-components from t))
1842 'string<)
1843 (sort (mapcar
1844 (lambda (x) (downcase (cadr x)))
1845 (mail-extract-address-components reply-to t))
1846 'string<))))
1847 (gnus-article-hide-header "reply-to")))))
eec82323
LMI
1848 ((eq elem 'date)
1849 (let ((date (message-fetch-field "date")))
1850 (when (and date
16409b0b 1851 (< (days-between (current-time-string) date)
eec82323 1852 4))
6748645f
LMI
1853 (gnus-article-hide-header "date"))))
1854 ((eq elem 'long-to)
16409b0b
GM
1855 (let ((to (message-fetch-field "to"))
1856 (cc (message-fetch-field "cc")))
6748645f 1857 (when (> (length to) 1024)
16409b0b
GM
1858 (gnus-article-hide-header "to"))
1859 (when (> (length cc) 1024)
1860 (gnus-article-hide-header "cc"))))
6748645f 1861 ((eq elem 'many-to)
16409b0b
GM
1862 (let ((to-count 0)
1863 (cc-count 0))
6748645f
LMI
1864 (goto-char (point-min))
1865 (while (re-search-forward "^to:" nil t)
1866 (setq to-count (1+ to-count)))
1867 (when (> to-count 1)
1868 (while (> to-count 0)
1869 (goto-char (point-min))
1870 (save-restriction
1871 (re-search-forward "^to:" nil nil to-count)
1872 (forward-line -1)
1873 (narrow-to-region (point) (point-max))
1874 (gnus-article-hide-header "to"))
16409b0b
GM
1875 (setq to-count (1- to-count))))
1876 (goto-char (point-min))
1877 (while (re-search-forward "^cc:" nil t)
1878 (setq cc-count (1+ cc-count)))
1879 (when (> cc-count 1)
1880 (while (> cc-count 0)
1881 (goto-char (point-min))
1882 (save-restriction
1883 (re-search-forward "^cc:" nil nil cc-count)
1884 (forward-line -1)
1885 (narrow-to-region (point) (point-max))
1886 (gnus-article-hide-header "cc"))
1887 (setq cc-count (1- cc-count)))))))))))))
eec82323
LMI
1888
1889(defun gnus-article-hide-header (header)
1890 (save-excursion
1891 (goto-char (point-min))
1892 (when (re-search-forward (concat "^" header ":") nil t)
1893 (gnus-article-hide-text-type
23f87bed 1894 (gnus-point-at-bol)
eec82323
LMI
1895 (progn
1896 (end-of-line)
1897 (if (re-search-forward "^[^ \t]" nil t)
1898 (match-beginning 0)
1899 (point-max)))
1900 'boring-headers))))
1901
16409b0b
GM
1902(defvar gnus-article-normalized-header-length 40
1903 "Length of normalized headers.")
1904
1905(defun article-normalize-headers ()
1906 "Make all header lines 40 characters long."
1907 (interactive)
4e7d0221 1908 (let ((inhibit-read-only t)
16409b0b
GM
1909 column)
1910 (save-excursion
1911 (save-restriction
1912 (article-narrow-to-head)
1913 (while (not (eobp))
1914 (cond
1915 ((< (setq column (- (gnus-point-at-eol) (point)))
1916 gnus-article-normalized-header-length)
1917 (end-of-line)
1918 (insert (make-string
1919 (- gnus-article-normalized-header-length column)
1920 ? )))
1921 ((> column gnus-article-normalized-header-length)
1922 (gnus-put-text-property
1923 (progn
1924 (forward-char gnus-article-normalized-header-length)
1925 (point))
1926 (gnus-point-at-eol)
1927 'invisible t))
1928 (t
1929 ;; Do nothing.
1930 ))
1931 (forward-line 1))))))
1932
6748645f 1933(defun article-treat-dumbquotes ()
23f87bed 1934 "Translate M****s*** sm*rtq**t*s and other symbols into proper text.
16409b0b 1935Note that this function guesses whether a character is a sm*rtq**t* or
74dd1b0d
SZ
1936not, so it should only be used interactively.
1937
23f87bed
MB
1938Sm*rtq**t*s are M****s***'s unilateral extension to the
1939iso-8859-1 character map in an attempt to provide more quoting
1940characters. If you see something like \\222 or \\264 where
1941you're expecting some kind of apostrophe or quotation mark, then
1942try this wash."
6748645f 1943 (interactive)
16409b0b 1944 (article-translate-strings gnus-article-dumbquotes-map))
6748645f
LMI
1945
1946(defun article-translate-characters (from to)
1947 "Translate all characters in the body of the article according to FROM and TO.
1948FROM is a string of characters to translate from; to is a string of
1949characters to translate to."
1950 (save-excursion
16409b0b 1951 (when (article-goto-body)
4e7d0221 1952 (let ((inhibit-read-only t)
6748645f
LMI
1953 (x (make-string 225 ?x))
1954 (i -1))
1955 (while (< (incf i) (length x))
1956 (aset x i i))
1957 (setq i 0)
1958 (while (< i (length from))
1959 (aset x (aref from i) (aref to i))
1960 (incf i))
1961 (translate-region (point) (point-max) x)))))
1962
16409b0b
GM
1963(defun article-translate-strings (map)
1964 "Translate all string in the body of the article according to MAP.
1965MAP is an alist where the elements are on the form (\"from\" \"to\")."
1966 (save-excursion
1967 (when (article-goto-body)
4e7d0221 1968 (let ((inhibit-read-only t)
16409b0b
GM
1969 elem)
1970 (while (setq elem (pop map))
1971 (save-excursion
1972 (while (search-forward (car elem) nil t)
1973 (replace-match (cadr elem)))))))))
1974
eec82323
LMI
1975(defun article-treat-overstrike ()
1976 "Translate overstrikes into bold text."
1977 (interactive)
1978 (save-excursion
16409b0b 1979 (when (article-goto-body)
4e7d0221 1980 (let ((inhibit-read-only t))
eec82323 1981 (while (search-forward "\b" nil t)
16409b0b 1982 (let ((next (char-after))
eec82323
LMI
1983 (previous (char-after (- (point) 2))))
1984 ;; We do the boldification/underlining by hiding the
1985 ;; overstrikes and putting the proper text property
1986 ;; on the letters.
1987 (cond
1988 ((eq next previous)
1989 (gnus-article-hide-text-type (- (point) 2) (point) 'overstrike)
1990 (put-text-property (point) (1+ (point)) 'face 'bold))
1991 ((eq next ?_)
1992 (gnus-article-hide-text-type
1993 (1- (point)) (1+ (point)) 'overstrike)
1994 (put-text-property
1995 (- (point) 2) (1- (point)) 'face 'underline))
1996 ((eq previous ?_)
1997 (gnus-article-hide-text-type (- (point) 2) (point) 'overstrike)
1998 (put-text-property
1999 (point) (1+ (point)) 'face 'underline)))))))))
2000
23f87bed
MB
2001(defun gnus-article-treat-unfold-headers ()
2002 "Unfold folded message headers.
2003Only the headers that fit into the current window width will be
2004unfolded."
2005 (interactive)
2006 (gnus-with-article-headers
2007 (let (length)
2008 (while (not (eobp))
2009 (save-restriction
2010 (mail-header-narrow-to-field)
2011 (let ((header (buffer-string)))
2012 (with-temp-buffer
2013 (insert header)
2014 (goto-char (point-min))
2015 (while (re-search-forward "\n[\t ]" nil t)
2016 (replace-match " " t t)))
2017 (setq length (- (point-max) (point-min) 1)))
2018 (when (< length (window-width))
2019 (while (re-search-forward "\n[\t ]" nil t)
2020 (replace-match " " t t)))
2021 (goto-char (point-max)))))))
2022
2023(defun gnus-article-treat-fold-headers ()
2024 "Fold message headers."
2025 (interactive)
2026 (gnus-with-article-headers
2027 (while (not (eobp))
2028 (save-restriction
2029 (mail-header-narrow-to-field)
2030 (mail-header-fold-field)
2031 (goto-char (point-max))))))
2032
2033(defun gnus-treat-smiley ()
2034 "Toggle display of textual emoticons (\"smileys\") as small graphical icons."
2035 (interactive)
2036 (gnus-with-article-buffer
2037 (if (memq 'smiley gnus-article-wash-types)
2038 (gnus-delete-images 'smiley)
2039 (article-goto-body)
2040 (let ((images (smiley-region (point) (point-max))))
2041 (when images
2042 (gnus-add-wash-type 'smiley)
2043 (dolist (image images)
2044 (gnus-add-image 'smiley image)))))))
2045
2046(defun gnus-article-remove-images ()
2047 "Remove all images from the article buffer."
2048 (interactive)
2049 (gnus-with-article-buffer
2050 (dolist (elem gnus-article-image-alist)
2051 (gnus-delete-images (car elem)))))
2052
2053(defun gnus-article-treat-fold-newsgroups ()
2054 "Unfold folded message headers.
2055Only the headers that fit into the current window width will be
2056unfolded."
2057 (interactive)
2058 (gnus-with-article-headers
2059 (while (gnus-article-goto-header "newsgroups\\|followup-to")
2060 (save-restriction
2061 (mail-header-narrow-to-field)
2062 (while (re-search-forward ", *" nil t)
2063 (replace-match ", " t t))
2064 (mail-header-fold-field)
2065 (goto-char (point-max))))))
2066
2067(defun gnus-article-treat-body-boundary ()
2068 "Place a boundary line at the end of the headers."
2069 (interactive)
2070 (when (and gnus-body-boundary-delimiter
2071 (> (length gnus-body-boundary-delimiter) 0))
2072 (gnus-with-article-headers
2073 (goto-char (point-max))
2074 (let ((start (point)))
2075 (insert "X-Boundary: ")
2076 (gnus-add-text-properties start (point) '(invisible t intangible t))
2077 (insert (let (str)
2078 (while (>= (1- (window-width)) (length str))
2079 (setq str (concat str gnus-body-boundary-delimiter)))
2080 (substring str 0 (1- (window-width))))
2081 "\n")
2082 (gnus-put-text-property start (point) 'gnus-decoration 'header)))))
2083
16409b0b
GM
2084(defun article-fill-long-lines ()
2085 "Fill lines that are wider than the window width."
eec82323
LMI
2086 (interactive)
2087 (save-excursion
4e7d0221 2088 (let ((inhibit-read-only t)
16409b0b
GM
2089 (width (window-width (get-buffer-window (current-buffer)))))
2090 (save-restriction
2091 (article-goto-body)
80b47379 2092 (let ((adaptive-fill-mode nil)) ;Why? -sm
16409b0b
GM
2093 (while (not (eobp))
2094 (end-of-line)
2095 (when (>= (current-column) (min fill-column width))
23f87bed
MB
2096 (narrow-to-region (min (1+ (point)) (point-max))
2097 (gnus-point-at-bol))
2098 (let ((goback (point-marker)))
2099 (fill-paragraph nil)
2100 (goto-char (marker-position goback)))
16409b0b
GM
2101 (widen))
2102 (forward-line 1)))))))
2103
2104(defun article-capitalize-sentences ()
2105 "Capitalize the first word in each sentence."
2106 (interactive)
2107 (save-excursion
4e7d0221 2108 (let ((inhibit-read-only t)
16409b0b
GM
2109 (paragraph-start "^[\n\^L]"))
2110 (article-goto-body)
2111 (while (not (eobp))
2112 (capitalize-word 1)
2113 (forward-sentence)))))
eec82323
LMI
2114
2115(defun article-remove-cr ()
16409b0b 2116 "Remove trailing CRs and then translate remaining CRs into LFs."
eec82323
LMI
2117 (interactive)
2118 (save-excursion
4e7d0221 2119 (let ((inhibit-read-only t))
eec82323 2120 (goto-char (point-min))
16409b0b
GM
2121 (while (re-search-forward "\r+$" nil t)
2122 (replace-match "" t t))
2123 (goto-char (point-min))
eec82323 2124 (while (search-forward "\r" nil t)
16409b0b 2125 (replace-match "\n" t t)))))
eec82323
LMI
2126
2127(defun article-remove-trailing-blank-lines ()
2128 "Remove all trailing blank lines from the article."
2129 (interactive)
2130 (save-excursion
4e7d0221 2131 (let ((inhibit-read-only t))
eec82323
LMI
2132 (goto-char (point-max))
2133 (delete-region
2134 (point)
2135 (progn
2136 (while (and (not (bobp))
16409b0b
GM
2137 (looking-at "^[ \t]*$")
2138 (not (gnus-annotation-in-region-p
2139 (point) (gnus-point-at-eol))))
eec82323
LMI
2140 (forward-line -1))
2141 (forward-line 1)
2142 (point))))))
2143
23f87bed
MB
2144(defun article-display-face ()
2145 "Display any Face headers in the header."
2146 (interactive)
2147 (let ((wash-face-p buffer-read-only))
2148 (gnus-with-article-headers
2149 ;; When displaying parts, this function can be called several times on
2150 ;; the same article, without any intended toggle semantic (as typing `W
2151 ;; D d' would have). So face deletion must occur only when we come from
2152 ;; an interactive command, that is when the *Article* buffer is
2153 ;; read-only.
2154 (if (and wash-face-p (memq 'face gnus-article-wash-types))
2155 (gnus-delete-images 'face)
7d0c69be
MB
2156 (let (face faces from)
2157 (save-current-buffer
23f87bed 2158 (when (and wash-face-p
7d0c69be
MB
2159 (gnus-buffer-live-p gnus-original-article-buffer)
2160 (not (re-search-forward "^Face:[\t ]*" nil t)))
23f87bed
MB
2161 (set-buffer gnus-original-article-buffer))
2162 (save-restriction
2163 (mail-narrow-to-head)
2164 (while (gnus-article-goto-header "Face")
2165 (push (mail-header-field-value) faces))))
d6697c02 2166 (when faces
7d0c69be
MB
2167 (goto-char (point-min))
2168 (let ((from (gnus-article-goto-header "from"))
2169 png image)
2170 (unless from
2171 (insert "From:")
2172 (setq from (point))
2173 (insert "[no `from' set]\n"))
2174 (while faces
2175 (when (setq png (gnus-convert-face-to-png (pop faces)))
d6697c02
MB
2176 (setq image (gnus-create-image png 'png t))
2177 (goto-char from)
2178 (gnus-add-wash-type 'face)
2179 (gnus-add-image 'face image)
2180 (gnus-put-image image nil 'face))))))))))
23f87bed 2181
eec82323
LMI
2182(defun article-display-x-face (&optional force)
2183 "Look for an X-Face header and display it if present."
2184 (interactive (list 'force))
23f87bed
MB
2185 (let ((wash-face-p buffer-read-only)) ;; When type `W f'
2186 (gnus-with-article-headers
2187 ;; Delete the old process, if any.
2188 (when (process-status "article-x-face")
2189 (delete-process "article-x-face"))
2190 ;; See the comment in `article-display-face'.
2191 (if (and wash-face-p (memq 'xface gnus-article-wash-types))
2192 ;; We have already displayed X-Faces, so we remove them
2193 ;; instead.
2194 (gnus-delete-images 'xface)
2195 ;; Display X-Faces.
2196 (let (x-faces from face)
7d0c69be 2197 (save-current-buffer
23f87bed 2198 (when (and wash-face-p
7d0c69be
MB
2199 (gnus-buffer-live-p gnus-original-article-buffer)
2200 (not (re-search-forward "^X-Face:[\t ]*" nil t)))
23f87bed
MB
2201 ;; If type `W f', use gnus-original-article-buffer,
2202 ;; otherwise use the current buffer because displaying
2203 ;; RFC822 parts calls this function too.
2204 (set-buffer gnus-original-article-buffer))
2205 (save-restriction
2206 (mail-narrow-to-head)
2207 (while (gnus-article-goto-header "X-Face")
2208 (push (mail-header-field-value) x-faces))
2209 (setq from (message-fetch-field "from"))))
2210 ;; Sending multiple EOFs to xv doesn't work, so we only do a
2211 ;; single external face.
2212 (when (stringp gnus-article-x-face-command)
2213 (setq x-faces (list (car x-faces))))
7d0c69be
MB
2214 (when (and x-faces
2215 gnus-article-x-face-command
2216 (or force
2217 ;; Check whether this face is censored.
2218 (not gnus-article-x-face-too-ugly)
2219 (and from
2220 (not (string-match gnus-article-x-face-too-ugly
2221 from)))))
2222 (while (setq face (pop x-faces))
2223 ;; We display the face.
2224 (cond ((stringp gnus-article-x-face-command)
2225 ;; The command is a string, so we interpret the command
2226 ;; as a, well, command, and fork it off.
2227 (let ((process-connection-type nil))
2228 (gnus-set-process-query-on-exit-flag
2229 (start-process
2230 "article-x-face" nil shell-file-name
2231 shell-command-switch gnus-article-x-face-command)
2232 nil)
2233 (with-temp-buffer
2234 (insert face)
2235 (process-send-region "article-x-face"
2236 (point-min) (point-max)))
2237 (process-send-eof "article-x-face")))
2238 ((functionp gnus-article-x-face-command)
2239 ;; The command is a lisp function, so we call it.
2240 (funcall gnus-article-x-face-command face))
2241 (t
2242 (error "%s is not a function"
2243 gnus-article-x-face-command))))))))))
a8151ef7 2244
16409b0b
GM
2245(defun article-decode-mime-words ()
2246 "Decode all MIME-encoded words in the article."
2247 (interactive)
2248 (save-excursion
2249 (set-buffer gnus-article-buffer)
2250 (let ((inhibit-point-motion-hooks t)
23f87bed 2251 (inhibit-read-only t)
16409b0b 2252 (mail-parse-charset gnus-newsgroup-charset)
a1506d29 2253 (mail-parse-ignored-charsets
16409b0b
GM
2254 (save-excursion (set-buffer gnus-summary-buffer)
2255 gnus-newsgroup-ignored-charsets)))
2256 (mail-decode-encoded-word-region (point-min) (point-max)))))
2257
2258(defun article-decode-charset (&optional prompt)
2259 "Decode charset-encoded text in the article.
2260If PROMPT (the prefix), prompt for a coding system to use."
2261 (interactive "P")
2262 (let ((inhibit-point-motion-hooks t) (case-fold-search t)
23f87bed 2263 (inhibit-read-only t)
16409b0b 2264 (mail-parse-charset gnus-newsgroup-charset)
a1506d29 2265 (mail-parse-ignored-charsets
16409b0b
GM
2266 (save-excursion (condition-case nil
2267 (set-buffer gnus-summary-buffer)
2268 (error))
2269 gnus-newsgroup-ignored-charsets))
2270 ct cte ctl charset format)
c96ec15a
MB
2271 (save-excursion
2272 (save-restriction
2273 (article-narrow-to-head)
2274 (setq ct (message-fetch-field "Content-Type" t)
2275 cte (message-fetch-field "Content-Transfer-Encoding" t)
2276 ctl (and ct (mail-header-parse-content-type ct))
2277 charset (cond
2278 (prompt
2279 (mm-read-coding-system "Charset to decode: "))
2280 (ctl
2281 (mail-content-type-get ctl 'charset)))
2282 format (and ctl (mail-content-type-get ctl 'format)))
2283 (when cte
2284 (setq cte (mail-header-strip cte)))
2285 (if (and ctl (not (string-match "/" (car ctl))))
2286 (setq ctl nil))
2287 (goto-char (point-max)))
2288 (forward-line 1)
2289 (save-restriction
2290 (narrow-to-region (point) (point-max))
2291 (when (and (eq mail-parse-charset 'gnus-decoded)
2292 (eq (mm-body-7-or-8) '8bit))
2293 ;; The text code could have been decoded.
2294 (setq charset mail-parse-charset))
2295 (when (and (or (not ctl)
2296 (equal (car ctl) "text/plain"))
2297 (not format)) ;; article with format will decode later.
2298 (mm-decode-body
2299 charset (and cte (intern (downcase
2300 (gnus-strip-whitespace cte))))
2301 (car ctl)))))))
16409b0b
GM
2302
2303(defun article-decode-encoded-words ()
2304 "Remove encoded-word encoding from headers."
2305 (let ((inhibit-point-motion-hooks t)
2306 (mail-parse-charset gnus-newsgroup-charset)
a1506d29 2307 (mail-parse-ignored-charsets
16409b0b
GM
2308 (save-excursion (condition-case nil
2309 (set-buffer gnus-summary-buffer)
2310 (error))
2311 gnus-newsgroup-ignored-charsets))
23f87bed 2312 (inhibit-read-only t))
16409b0b
GM
2313 (save-restriction
2314 (article-narrow-to-head)
2315 (funcall gnus-decode-header-function (point-min) (point-max)))))
eec82323 2316
23f87bed
MB
2317(defun article-decode-group-name ()
2318 "Decode group names in `Newsgroups:'."
2319 (let ((inhibit-point-motion-hooks t)
2320 (inhibit-read-only t)
2321 (method (gnus-find-method-for-group gnus-newsgroup-name)))
2322 (when (and (or gnus-group-name-charset-method-alist
2323 gnus-group-name-charset-group-alist)
2324 (gnus-buffer-live-p gnus-original-article-buffer))
2325 (save-restriction
2326 (article-narrow-to-head)
2327 (with-current-buffer gnus-original-article-buffer
2328 (goto-char (point-min)))
2329 (while (re-search-forward
2330 "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
2331 (replace-match (save-match-data
2332 (gnus-decode-newsgroups
2333 ;; XXX how to use data in article buffer?
2334 (with-current-buffer gnus-original-article-buffer
2335 (re-search-forward
2336 "^Newsgroups:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
2337 nil t)
2338 (match-string 1))
2339 gnus-newsgroup-name method))
2340 t t nil 1))
2341 (goto-char (point-min))
2342 (with-current-buffer gnus-original-article-buffer
2343 (goto-char (point-min)))
2344 (while (re-search-forward
2345 "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]" nil t)
2346 (replace-match (save-match-data
2347 (gnus-decode-newsgroups
2348 ;; XXX how to use data in article buffer?
2349 (with-current-buffer gnus-original-article-buffer
2350 (re-search-forward
2351 "^Followup-To:\\(\\(.\\|\n[\t ]\\)*\\)\n[^\t ]"
2352 nil t)
2353 (match-string 1))
2354 gnus-newsgroup-name method))
2355 t t nil 1))))))
2356
2357(autoload 'idna-to-unicode "idna")
2358
2359(defun article-decode-idna-rhs ()
53cfefc8
MB
2360 "Decode IDNA strings in RHS in various headers in current buffer.
2361The following headers are decoded: From:, To:, Cc:, Reply-To:,
2362Mail-Reply-To: and Mail-Followup-To:."
23f87bed
MB
2363 (when gnus-use-idna
2364 (save-restriction
2365 (let ((inhibit-point-motion-hooks t)
2366 (inhibit-read-only t))
2367 (article-narrow-to-head)
2368 (goto-char (point-min))
53cfefc8 2369 (while (re-search-forward "@[^ \t\n\r,>]*\\(xn--[-A-Za-z0-9.]*\\)[ \t\n\r,>]" nil t)
23f87bed
MB
2370 (let (ace unicode)
2371 (when (save-match-data
2372 (and (setq ace (match-string 1))
2373 (save-excursion
2374 (and (re-search-backward "^[^ \t]" nil t)
53cfefc8 2375 (looking-at "From\\|To\\|Cc\\|Reply-To\\|Mail-Reply-To\\|Mail-Followup-To")))
23f87bed
MB
2376 (setq unicode (idna-to-unicode ace))))
2377 (unless (string= ace unicode)
2378 (replace-match unicode nil nil nil 1)))))))))
2379
2380(defun article-de-quoted-unreadable (&optional force read-charset)
16409b0b 2381 "Translate a quoted-printable-encoded article.
eec82323 2382If FORCE, decode the article whether it is marked as quoted-printable
23f87bed
MB
2383or not.
2384If READ-CHARSET, ask for a coding system."
2385 (interactive (list 'force current-prefix-arg))
eec82323 2386 (save-excursion
4e7d0221 2387 (let ((inhibit-read-only t) type charset)
8b93df01
DL
2388 (if (gnus-buffer-live-p gnus-original-article-buffer)
2389 (with-current-buffer gnus-original-article-buffer
2390 (setq type
2391 (gnus-fetch-field "content-transfer-encoding"))
2392 (let* ((ct (gnus-fetch-field "content-type"))
c96ec15a 2393 (ctl (and ct (mail-header-parse-content-type ct))))
8b93df01
DL
2394 (setq charset (and ctl
2395 (mail-content-type-get ctl 'charset)))
2396 (if (stringp charset)
2397 (setq charset (intern (downcase charset)))))))
23f87bed
MB
2398 (if read-charset
2399 (setq charset (mm-read-coding-system "Charset: " charset)))
a1506d29 2400 (unless charset
8b93df01 2401 (setq charset gnus-newsgroup-charset))
eec82323 2402 (when (or force
eb806ef3
DL
2403 (and type (let ((case-fold-search t))
2404 (string-match "quoted-printable" type))))
16409b0b 2405 (article-goto-body)
eb806ef3
DL
2406 (quoted-printable-decode-region
2407 (point) (point-max) (mm-charset-to-coding-system charset))))))
16409b0b 2408
23f87bed 2409(defun article-de-base64-unreadable (&optional force read-charset)
16409b0b 2410 "Translate a base64 article.
23f87bed
MB
2411If FORCE, decode the article whether it is marked as base64 not.
2412If READ-CHARSET, ask for a coding system."
2413 (interactive (list 'force current-prefix-arg))
16409b0b 2414 (save-excursion
4e7d0221 2415 (let ((inhibit-read-only t) type charset)
8b93df01
DL
2416 (if (gnus-buffer-live-p gnus-original-article-buffer)
2417 (with-current-buffer gnus-original-article-buffer
2418 (setq type
2419 (gnus-fetch-field "content-transfer-encoding"))
2420 (let* ((ct (gnus-fetch-field "content-type"))
c96ec15a 2421 (ctl (and ct (mail-header-parse-content-type ct))))
8b93df01
DL
2422 (setq charset (and ctl
2423 (mail-content-type-get ctl 'charset)))
2424 (if (stringp charset)
2425 (setq charset (intern (downcase charset)))))))
23f87bed
MB
2426 (if read-charset
2427 (setq charset (mm-read-coding-system "Charset: " charset)))
a1506d29 2428 (unless charset
8b93df01 2429 (setq charset gnus-newsgroup-charset))
16409b0b 2430 (when (or force
eb806ef3
DL
2431 (and type (let ((case-fold-search t))
2432 (string-match "base64" type))))
16409b0b
GM
2433 (article-goto-body)
2434 (save-restriction
2435 (narrow-to-region (point) (point-max))
2436 (base64-decode-region (point-min) (point-max))
eb806ef3
DL
2437 (mm-decode-coding-region
2438 (point-min) (point-max) (mm-charset-to-coding-system charset)))))))
16409b0b
GM
2439
2440(eval-when-compile
2441 (require 'rfc1843))
2442
2443(defun article-decode-HZ ()
2444 "Translate a HZ-encoded article."
2445 (interactive)
2446 (require 'rfc1843)
2447 (save-excursion
4e7d0221 2448 (let ((inhibit-read-only t))
16409b0b
GM
2449 (rfc1843-decode-region (point-min) (point-max)))))
2450
23f87bed
MB
2451(defun article-unsplit-urls ()
2452 "Remove the newlines that some other mailers insert into URLs."
16409b0b 2453 (interactive)
23f87bed
MB
2454 (save-excursion
2455 (let ((inhibit-read-only t))
2456 (goto-char (point-min))
2457 (while (re-search-forward
97f78c9b 2458 "\\(\\(https?\\|ftp\\)://\\S-+\\) *\n\\(\\S-+\\)" nil t)
23f87bed
MB
2459 (replace-match "\\1\\3" t)))
2460 (when (interactive-p)
2461 (gnus-treat-article nil))))
2462
2463
2464(defun article-wash-html (&optional read-charset)
2465 "Format an HTML article.
73043f7d
MB
2466If READ-CHARSET, ask for a coding system. If it is a number, the
2467charset defined in `gnus-summary-show-article-charset-alist' is used."
23f87bed 2468 (interactive "P")
16409b0b 2469 (save-excursion
4e7d0221 2470 (let ((inhibit-read-only t)
8b93df01 2471 charset)
73043f7d
MB
2472 (if read-charset
2473 (if (or (and (numberp read-charset)
2474 (setq charset
2475 (cdr
2476 (assq read-charset
2477 gnus-summary-show-article-charset-alist))))
2478 (setq charset (mm-read-coding-system "Charset: ")))
2479 (let ((gnus-summary-show-article-charset-alist
2480 (list (cons 1 charset))))
2481 (with-current-buffer gnus-summary-buffer
2482 (gnus-summary-show-article 1)))
2483 (error "No charset is given"))
2484 (when (gnus-buffer-live-p gnus-original-article-buffer)
2485 (with-current-buffer gnus-original-article-buffer
2486 (let* ((ct (gnus-fetch-field "content-type"))
c96ec15a 2487 (ctl (and ct (mail-header-parse-content-type ct))))
73043f7d
MB
2488 (setq charset (and ctl
2489 (mail-content-type-get ctl 'charset)))
2490 (when (stringp charset)
2491 (setq charset (intern (downcase charset)))))))
2492 (unless charset
2493 (setq charset gnus-newsgroup-charset)))
16409b0b
GM
2494 (article-goto-body)
2495 (save-window-excursion
2496 (save-restriction
2497 (narrow-to-region (point) (point-max))
23f87bed
MB
2498 (let* ((func (or gnus-article-wash-function mm-text-html-renderer))
2499 (entry (assq func mm-text-html-washer-alist)))
2500 (when entry
2501 (setq func (cdr entry)))
2502 (cond
2503 ((functionp func)
2504 (funcall func))
2505 (t
2506 (apply (car func) (cdr func))))))))))
2507
2508(defun gnus-article-wash-html-with-w3 ()
2509 "Wash the current buffer with w3."
2510 (mm-setup-w3)
2511 (let ((w3-strict-width (window-width))
2512 (url-standalone-mode t)
2513 (url-gateway-unplugged t)
2514 (w3-honor-stylesheets nil))
2515 (condition-case ()
2516 (w3-region (point-min) (point-max))
2517 (error))))
2518
2519(defun gnus-article-wash-html-with-w3m ()
2520 "Wash the current buffer with emacs-w3m."
2521 (mm-setup-w3m)
7dafe00b
MB
2522 (let ((w3m-safe-url-regexp mm-w3m-safe-url-regexp)
2523 w3m-force-redisplay)
2524 (w3m-region (point-min) (point-max)))
2525 (when (and mm-inline-text-html-with-w3m-keymap
2526 (boundp 'w3m-minor-mode-map)
2527 w3m-minor-mode-map)
2528 (add-text-properties
2529 (point-min) (point-max)
2530 (list 'keymap w3m-minor-mode-map
2531 ;; Put the mark meaning this part was rendered by emacs-w3m.
2532 'mm-inline-text-html-with-w3m t))))
16409b0b 2533
73043f7d
MB
2534(eval-when-compile (defvar charset)) ;; Bound by `article-wash-html'.
2535
2536(defun gnus-article-wash-html-with-w3m-standalone ()
2537 "Wash the current buffer with w3m."
7347faa8
MB
2538 (if (mm-w3m-standalone-supports-m17n-p)
2539 (progn
2540 (unless (mm-coding-system-p charset) ;; Bound by `article-wash-html'.
2541 ;; The default.
2542 (setq charset 'iso-8859-1))
2543 (let ((coding-system-for-write charset)
2544 (coding-system-for-read charset))
2545 (call-process-region
2546 (point-min) (point-max)
2547 "w3m" t t nil "-dump" "-T" "text/html"
2548 "-I" (symbol-name charset) "-O" (symbol-name charset))))
2549 (mm-inline-wash-with-stdin nil "w3m" "-dump" "-T" "text/html")))
73043f7d 2550
16409b0b
GM
2551(defun article-hide-list-identifiers ()
2552 "Remove list identifies from the Subject header.
2553The `gnus-list-identifiers' variable specifies what to do."
2554 (interactive)
23f87bed
MB
2555 (let ((inhibit-point-motion-hooks t)
2556 (regexp (if (consp gnus-list-identifiers)
2557 (mapconcat 'identity gnus-list-identifiers " *\\|")
2558 gnus-list-identifiers))
2559 (inhibit-read-only t))
2560 (when regexp
2561 (save-excursion
2562 (save-restriction
2563 (article-narrow-to-head)
2564 (goto-char (point-min))
2565 (while (re-search-forward
2566 (concat "^Subject: +\\(R[Ee]: +\\)*\\(" regexp " *\\)")
2567 nil t)
2568 (delete-region (match-beginning 2) (match-end 0))
2569 (beginning-of-line))
2570 (when (re-search-forward
2571 "^Subject: +\\(\\(R[Ee]: +\\)+\\)R[Ee]: +" nil t)
2572 (delete-region (match-beginning 1) (match-end 1))))))))
eec82323
LMI
2573
2574(defun article-hide-pem (&optional arg)
2575 "Toggle hiding of any PEM headers and signatures in the current article.
2576If given a negative prefix, always show; if given a positive prefix,
2577always hide."
2578 (interactive (gnus-article-hidden-arg))
2579 (unless (gnus-article-check-hidden-text 'pem arg)
2580 (save-excursion
23f87bed 2581 (let ((inhibit-read-only t) end)
eec82323 2582 (goto-char (point-min))
16409b0b
GM
2583 ;; Hide the horrendously ugly "header".
2584 (when (and (search-forward
2585 "\n-----BEGIN PRIVACY-ENHANCED MESSAGE-----\n"
2586 nil t)
2587 (setq end (1+ (match-beginning 0))))
23f87bed 2588 (gnus-add-wash-type 'pem)
16409b0b
GM
2589 (gnus-article-hide-text-type
2590 end
2591 (if (search-forward "\n\n" nil t)
2592 (match-end 0)
2593 (point-max))
2594 'pem)
2595 ;; Hide the trailer as well
2596 (when (search-forward "\n-----END PRIVACY-ENHANCED MESSAGE-----\n"
2597 nil t)
2598 (gnus-article-hide-text-type
2599 (match-beginning 0) (match-end 0) 'pem)))))))
2600
2601(defun article-strip-banner ()
23f87bed
MB
2602 "Strip the banners specified by the `banner' group parameter and by
2603`gnus-article-address-banner-alist'."
16409b0b 2604 (interactive)
23f87bed
MB
2605 (save-excursion
2606 (save-restriction
2607 (let ((inhibit-point-motion-hooks t))
2608 (when (gnus-parameter-banner gnus-newsgroup-name)
2609 (article-really-strip-banner
2610 (gnus-parameter-banner gnus-newsgroup-name)))
2611 (when gnus-article-address-banner-alist
cf5a5c38
MB
2612 ;; Note that the From header is decoded here, so it is
2613 ;; required that the *-extract-address-components function
2614 ;; supports non-ASCII text.
d71c0855
MB
2615 (let ((from (save-restriction
2616 (widen)
2617 (article-narrow-to-head)
2618 (mail-fetch-field "from"))))
2619 (when (and from
2620 (setq from
2621 (cadr (funcall gnus-extract-address-components
2622 from))))
2623 (catch 'found
2624 (dolist (pair gnus-article-address-banner-alist)
2625 (when (string-match (car pair) from)
2626 (throw 'found
2627 (article-really-strip-banner (cdr pair)))))))))))))
23f87bed
MB
2628
2629(defun article-really-strip-banner (banner)
2630 "Strip the banner specified by the argument."
16409b0b
GM
2631 (save-excursion
2632 (save-restriction
2633 (let ((inhibit-point-motion-hooks t)
16409b0b 2634 (gnus-signature-limit nil)
23f87bed
MB
2635 (inhibit-read-only t))
2636 (article-goto-body)
2637 (cond
2638 ((eq banner 'signature)
2639 (when (gnus-article-narrow-to-signature)
2640 (widen)
2641 (forward-line -1)
2642 (delete-region (point) (point-max))))
2643 ((symbolp banner)
2644 (if (setq banner (cdr (assq banner gnus-article-banner-alist)))
2645 (while (re-search-forward banner nil t)
2646 (delete-region (match-beginning 0) (match-end 0)))))
2647 ((stringp banner)
2648 (while (re-search-forward banner nil t)
2649 (delete-region (match-beginning 0) (match-end 0)))))))))
16409b0b
GM
2650
2651(defun article-babel ()
2652 "Translate article using an online translation service."
2653 (interactive)
2654 (require 'babel)
2655 (save-excursion
2656 (set-buffer gnus-article-buffer)
2657 (when (article-goto-body)
4e7d0221 2658 (let* ((inhibit-read-only t)
16409b0b
GM
2659 (start (point))
2660 (end (point-max))
2661 (orig (buffer-substring start end))
23f87bed 2662 (trans (babel-as-string orig)))
16409b0b
GM
2663 (save-restriction
2664 (narrow-to-region start end)
2665 (delete-region start end)
23f87bed 2666 (insert trans))))))
eec82323
LMI
2667
2668(defun article-hide-signature (&optional arg)
2669 "Hide the signature in the current article.
2670If given a negative prefix, always show; if given a positive prefix,
2671always hide."
2672 (interactive (gnus-article-hidden-arg))
2673 (unless (gnus-article-check-hidden-text 'signature arg)
2674 (save-excursion
2675 (save-restriction
4e7d0221 2676 (let ((inhibit-read-only t))
eec82323
LMI
2677 (when (gnus-article-narrow-to-signature)
2678 (gnus-article-hide-text-type
23f87bed
MB
2679 (point-min) (point-max) 'signature))))))
2680 (gnus-set-mode-line 'article))
eec82323 2681
16409b0b
GM
2682(defun article-strip-headers-in-body ()
2683 "Strip offensive headers from bodies."
2684 (interactive)
2685 (save-excursion
2686 (article-goto-body)
2687 (let ((case-fold-search t))
2688 (when (looking-at "x-no-archive:")
2689 (gnus-delete-line)))))
2690
eec82323
LMI
2691(defun article-strip-leading-blank-lines ()
2692 "Remove all blank lines from the beginning of the article."
2693 (interactive)
2694 (save-excursion
2695 (let ((inhibit-point-motion-hooks t)
23f87bed 2696 (inhibit-read-only t))
16409b0b 2697 (when (article-goto-body)
eec82323
LMI
2698 (while (and (not (eobp))
2699 (looking-at "[ \t]*$"))
2700 (gnus-delete-line))))))
2701
16409b0b
GM
2702(defun article-narrow-to-head ()
2703 "Narrow the buffer to the head of the message.
2704Point is left at the beginning of the narrowed-to region."
2705 (narrow-to-region
2706 (goto-char (point-min))
2707 (if (search-forward "\n\n" nil 1)
2708 (1- (point))
2709 (point-max)))
2710 (goto-char (point-min)))
2711
2712(defun article-goto-body ()
2713 "Place point at the start of the body."
2714 (goto-char (point-min))
2715 (cond
2716 ;; This variable is only bound when dealing with separate
2717 ;; MIME body parts.
2718 (article-goto-body-goes-to-point-min-p
2719 t)
2720 ((search-forward "\n\n" nil t)
2721 t)
2722 (t
2723 (goto-char (point-max))
2724 nil)))
2725
eec82323
LMI
2726(defun article-strip-multiple-blank-lines ()
2727 "Replace consecutive blank lines with one empty line."
2728 (interactive)
2729 (save-excursion
2730 (let ((inhibit-point-motion-hooks t)
23f87bed 2731 (inhibit-read-only t))
eec82323 2732 ;; First make all blank lines empty.
16409b0b 2733 (article-goto-body)
eec82323 2734 (while (re-search-forward "^[ \t]+$" nil t)
16409b0b
GM
2735 (unless (gnus-annotation-in-region-p
2736 (match-beginning 0) (match-end 0))
2737 (replace-match "" nil t)))
eec82323 2738 ;; Then replace multiple empty lines with a single empty line.
16409b0b 2739 (article-goto-body)
23f87bed 2740 (while (re-search-forward "\n\n\\(\n+\\)" nil t)
16409b0b
GM
2741 (unless (gnus-annotation-in-region-p
2742 (match-beginning 0) (match-end 0))
23f87bed 2743 (delete-region (match-beginning 1) (match-end 1)))))))
eec82323
LMI
2744
2745(defun article-strip-leading-space ()
2746 "Remove all white space from the beginning of the lines in the article."
2747 (interactive)
2748 (save-excursion
2749 (let ((inhibit-point-motion-hooks t)
23f87bed 2750 (inhibit-read-only t))
16409b0b 2751 (article-goto-body)
eec82323
LMI
2752 (while (re-search-forward "^[ \t]+" nil t)
2753 (replace-match "" t t)))))
2754
16409b0b
GM
2755(defun article-strip-trailing-space ()
2756 "Remove all white space from the end of the lines in the article."
2757 (interactive)
2758 (save-excursion
2759 (let ((inhibit-point-motion-hooks t)
23f87bed 2760 (inhibit-read-only t))
16409b0b
GM
2761 (article-goto-body)
2762 (while (re-search-forward "[ \t]+$" nil t)
2763 (replace-match "" t t)))))
2764
eec82323
LMI
2765(defun article-strip-blank-lines ()
2766 "Strip leading, trailing and multiple blank lines."
2767 (interactive)
2768 (article-strip-leading-blank-lines)
2769 (article-remove-trailing-blank-lines)
2770 (article-strip-multiple-blank-lines))
2771
6748645f
LMI
2772(defun article-strip-all-blank-lines ()
2773 "Strip all blank lines."
2774 (interactive)
2775 (save-excursion
2776 (let ((inhibit-point-motion-hooks t)
23f87bed 2777 (inhibit-read-only t))
16409b0b 2778 (article-goto-body)
6748645f
LMI
2779 (while (re-search-forward "^[ \t]*\n" nil t)
2780 (replace-match "" t t)))))
2781
eec82323
LMI
2782(defun gnus-article-narrow-to-signature ()
2783 "Narrow to the signature; return t if a signature is found, else nil."
6748645f 2784 (let ((inhibit-point-motion-hooks t))
6748645f
LMI
2785 (when (gnus-article-search-signature)
2786 (forward-line 1)
2787 ;; Check whether we have some limits to what we consider
2788 ;; to be a signature.
2789 (let ((limits (if (listp gnus-signature-limit) gnus-signature-limit
2790 (list gnus-signature-limit)))
2791 limit limited)
2792 (while (setq limit (pop limits))
2793 (if (or (and (integerp limit)
2794 (< (- (point-max) (point)) limit))
2795 (and (floatp limit)
2796 (< (count-lines (point) (point-max)) limit))
23f87bed 2797 (and (functionp limit)
6748645f
LMI
2798 (funcall limit))
2799 (and (stringp limit)
2800 (not (re-search-forward limit nil t))))
2801 () ; This limit did not succeed.
2802 (setq limited t
2803 limits nil)))
2804 (unless limited
2805 (narrow-to-region (point) (point-max))
2806 t)))))
eec82323
LMI
2807
2808(defun gnus-article-search-signature ()
2809 "Search the current buffer for the signature separator.
2810Put point at the beginning of the signature separator."
2811 (let ((cur (point)))
2812 (goto-char (point-max))
2813 (if (if (stringp gnus-signature-separator)
2814 (re-search-backward gnus-signature-separator nil t)
2815 (let ((seps gnus-signature-separator))
2816 (while (and seps
2817 (not (re-search-backward (car seps) nil t)))
2818 (pop seps))
2819 seps))
2820 t
2821 (goto-char cur)
2822 nil)))
2823
eec82323
LMI
2824(defun gnus-article-hidden-arg ()
2825 "Return the current prefix arg as a number, or 0 if no prefix."
2826 (list (if current-prefix-arg
2827 (prefix-numeric-value current-prefix-arg)
2828 0)))
2829
2830(defun gnus-article-check-hidden-text (type arg)
2831 "Return nil if hiding is necessary.
f0529b5b 2832Arg can be nil or a number. nil and positive means hide, negative
eec82323
LMI
2833means show, 0 means toggle."
2834 (save-excursion
2835 (save-restriction
eec82323
LMI
2836 (let ((hide (gnus-article-hidden-text-p type)))
2837 (cond
2838 ((or (null arg)
2839 (> arg 0))
2840 nil)
2841 ((< arg 0)
4481aa98
SZ
2842 (gnus-article-show-hidden-text type)
2843 t)
eec82323
LMI
2844 (t
2845 (if (eq hide 'hidden)
4481aa98
SZ
2846 (progn
2847 (gnus-article-show-hidden-text type)
2848 t)
eec82323
LMI
2849 nil)))))))
2850
2851(defun gnus-article-hidden-text-p (type)
2852 "Say whether the current buffer contains hidden text of type TYPE."
6748645f 2853 (let ((pos (text-property-any (point-min) (point-max) 'article-type type)))
eec82323 2854 (while (and pos
16409b0b
GM
2855 (not (get-text-property pos 'invisible))
2856 (not (get-text-property pos 'dummy-invisible)))
eec82323
LMI
2857 (setq pos
2858 (text-property-any (1+ pos) (point-max) 'article-type type)))
2859 (if pos
2860 'hidden
16409b0b 2861 nil)))
eec82323 2862
520aa572 2863(defun gnus-article-show-hidden-text (type &optional dummy)
eec82323 2864 "Show all hidden text of type TYPE.
520aa572 2865Originally it is hide instead of DUMMY."
4e7d0221 2866 (let ((inhibit-read-only t)
520aa572 2867 (inhibit-point-motion-hooks t))
a1506d29 2868 (gnus-remove-text-properties-when
520aa572 2869 'article-type type
a1506d29 2870 (point-min) (point-max)
520aa572 2871 (cons 'article-type (cons type
23f87bed
MB
2872 gnus-hidden-properties)))
2873 (gnus-delete-wash-type type)))
eec82323
LMI
2874
2875(defconst article-time-units
2876 `((year . ,(* 365.25 24 60 60))
2877 (week . ,(* 7 24 60 60))
2878 (day . ,(* 24 60 60))
2879 (hour . ,(* 60 60))
2880 (minute . 60)
2881 (second . 1))
2882 "Mapping from time units to seconds.")
2883
23f87bed
MB
2884(defun gnus-article-forward-header ()
2885 "Move point to the start of the next header.
2886If the current header is a continuation header, this can be several
2887lines forward."
2888 (let ((ended nil))
2889 (while (not ended)
2890 (forward-line 1)
2891 (if (looking-at "[ \t]+[^ \t]")
2892 (forward-line 1)
2893 (setq ended t)))))
2894
31640842 2895(defun article-date-ut (&optional type highlight)
eec82323
LMI
2896 "Convert DATE date to universal time in the current article.
2897If TYPE is `local', convert to local time; if it is `lapsed', output
16409b0b
GM
2898how much time has lapsed since DATE. For `lapsed', the value of
2899`gnus-article-date-lapsed-new-header' says whether the \"X-Sent:\" header
2900should replace the \"Date:\" one, or should be added below it."
eec82323 2901 (interactive (list 'ut t))
31640842
MB
2902 (let* ((tdate-regexp "^Date:[ \t]\\|^X-Sent:[ \t]")
2903 (date-regexp (cond ((not gnus-article-date-lapsed-new-header)
2904 tdate-regexp)
2905 ((eq type 'lapsed)
2906 "^X-Sent:[ \t]")
2907 (article-lapsed-timer
2908 "^Date:[ \t]")
2909 (t
2910 tdate-regexp)))
2911 (case-fold-search t)
2912 (inhibit-read-only t)
eec82323 2913 (inhibit-point-motion-hooks t)
31640842 2914 pos date bface eface)
16409b0b
GM
2915 (save-excursion
2916 (save-restriction
31640842
MB
2917 (widen)
2918 (goto-char (point-min))
2919 (while (or (setq date (get-text-property (setq pos (point))
2920 'original-date))
2921 (when (setq pos (next-single-property-change
2922 (point) 'original-date))
2923 (setq date (get-text-property pos 'original-date))
2924 t))
2925 (narrow-to-region pos (or (text-property-any pos (point-max)
2926 'original-date nil)
2927 (point-max)))
16409b0b 2928 (goto-char (point-min))
31640842
MB
2929 (when (re-search-forward tdate-regexp nil t)
2930 (setq bface (get-text-property (gnus-point-at-bol) 'face)
2931 eface (get-text-property (1- (gnus-point-at-eol)) 'face)))
2932 (goto-char (point-min))
2933 (setq pos nil)
2934 ;; Delete any old Date headers.
2935 (while (re-search-forward date-regexp nil t)
2936 (if pos
2937 (delete-region (gnus-point-at-bol)
2938 (progn
2939 (gnus-article-forward-header)
2940 (point)))
2941 (delete-region (gnus-point-at-bol)
2942 (progn
2943 (gnus-article-forward-header)
2944 (forward-char -1)
2945 (point)))
2946 (setq pos (point))))
2947 (when (and (not pos)
2948 (re-search-forward tdate-regexp nil t))
2949 (forward-line 1))
2950 (gnus-goto-char pos)
2951 (insert (article-make-date-line date (or type 'ut)))
2952 (unless pos
2953 (insert "\n")
2954 (forward-line -1))
2955 ;; Do highlighting.
2956 (beginning-of-line)
2957 (when (looking-at "\\([^:]+\\): *\\(.*\\)$")
2958 (put-text-property (match-beginning 1) (1+ (match-end 1))
2959 'face bface)
2960 (put-text-property (match-beginning 2) (match-end 2)
2961 'face eface))
2962 (put-text-property (point-min) (1- (point-max)) 'original-date date)
2963 (goto-char (point-max))
2964 (widen))))))
eec82323
LMI
2965
2966(defun article-make-date-line (date type)
2967 "Return a DATE line of TYPE."
23f87bed
MB
2968 (unless (memq type '(local ut original user iso8601 lapsed english))
2969 (error "Unknown conversion type: %s" type))
2970 (condition-case ()
2971 (let ((time (date-to-time date)))
16409b0b 2972 (cond
23f87bed
MB
2973 ;; Convert to the local timezone.
2974 ((eq type 'local)
2975 (let ((tz (car (current-time-zone time))))
2976 (format "Date: %s %s%02d%02d" (current-time-string time)
2977 (if (> tz 0) "+" "-") (/ (abs tz) 3600)
2978 (/ (% (abs tz) 3600) 60))))
2979 ;; Convert to Universal Time.
2980 ((eq type 'ut)
2981 (concat "Date: "
2982 (current-time-string
2983 (let* ((e (parse-time-string date))
2984 (tm (apply 'encode-time e))
2985 (ms (car tm))
2986 (ls (- (cadr tm) (car (current-time-zone time)))))
2987 (cond ((< ls 0) (list (1- ms) (+ ls 65536)))
2988 ((> ls 65535) (list (1+ ms) (- ls 65536)))
2989 (t (list ms ls)))))
2990 " UT"))
2991 ;; Get the original date from the article.
2992 ((eq type 'original)
2993 (concat "Date: " (if (string-match "\n+$" date)
2994 (substring date 0 (match-beginning 0))
2995 date)))
2996 ;; Let the user define the format.
2997 ((eq type 'user)
2998 (let ((format (or (condition-case nil
2999 (with-current-buffer gnus-summary-buffer
3000 gnus-article-time-format)
3001 (error nil))
3002 gnus-article-time-format)))
3003 (if (functionp format)
3004 (funcall format time)
3005 (concat "Date: " (format-time-string format time)))))
3006 ;; ISO 8601.
3007 ((eq type 'iso8601)
3008 (let ((tz (car (current-time-zone time))))
3009 (concat
3010 "Date: "
3011 (format-time-string "%Y%m%dT%H%M%S" time)
3012 (format "%s%02d%02d"
3013 (if (> tz 0) "+" "-") (/ (abs tz) 3600)
3014 (/ (% (abs tz) 3600) 60)))))
3015 ;; Do an X-Sent lapsed format.
3016 ((eq type 'lapsed)
3017 ;; If the date is seriously mangled, the timezone functions are
3018 ;; liable to bug out, so we ignore all errors.
3019 (let* ((now (current-time))
3020 (real-time (subtract-time now time))
3021 (real-sec (and real-time
3022 (+ (* (float (car real-time)) 65536)
3023 (cadr real-time))))
3024 (sec (and real-time (abs real-sec)))
3025 num prev)
3026 (cond
3027 ((null real-time)
3028 "X-Sent: Unknown")
3029 ((zerop sec)
3030 "X-Sent: Now")
3031 (t
3032 (concat
3033 "X-Sent: "
3034 ;; This is a bit convoluted, but basically we go
3035 ;; through the time units for years, weeks, etc,
3036 ;; and divide things to see whether that results
3037 ;; in positive answers.
3038 (mapconcat
3039 (lambda (unit)
3040 (if (zerop (setq num (ffloor (/ sec (cdr unit)))))
3041 ;; The (remaining) seconds are too few to
3042 ;; be divided into this time unit.
3043 ""
3044 ;; It's big enough, so we output it.
3045 (setq sec (- sec (* num (cdr unit))))
3046 (prog1
3047 (concat (if prev ", " "") (int-to-string
3048 (floor num))
3049 " " (symbol-name (car unit))
3050 (if (> num 1) "s" ""))
3051 (setq prev t))))
3052 article-time-units "")
3053 ;; If dates are odd, then it might appear like the
3054 ;; article was sent in the future.
3055 (if (> real-sec 0)
3056 " ago"
3057 " in the future"))))))
3058 ;; Display the date in proper English
3059 ((eq type 'english)
3060 (let ((dtime (decode-time time)))
3061 (concat
3062 "Date: the "
3063 (number-to-string (nth 3 dtime))
3064 (let ((digit (% (nth 3 dtime) 10)))
3065 (cond
3066 ((memq (nth 3 dtime) '(11 12 13)) "th")
3067 ((= digit 1) "st")
3068 ((= digit 2) "nd")
3069 ((= digit 3) "rd")
3070 (t "th")))
3071 " of "
3072 (nth (1- (nth 4 dtime)) gnus-english-month-names)
3073 " "
3074 (number-to-string (nth 5 dtime))
3075 " at "
3076 (format "%02d" (nth 2 dtime))
3077 ":"
3078 (format "%02d" (nth 1 dtime)))))))
3079 (error
3080 (format "Date: %s (from Gnus)" date))))
eec82323
LMI
3081
3082(defun article-date-local (&optional highlight)
3083 "Convert the current article date to the local timezone."
3084 (interactive (list t))
3085 (article-date-ut 'local highlight))
3086
23f87bed
MB
3087(defun article-date-english (&optional highlight)
3088 "Convert the current article date to something that is proper English."
3089 (interactive (list t))
3090 (article-date-ut 'english highlight))
3091
eec82323
LMI
3092(defun article-date-original (&optional highlight)
3093 "Convert the current article date to what it was originally.
3094This is only useful if you have used some other date conversion
3095function and want to see what the date was before converting."
3096 (interactive (list t))
3097 (article-date-ut 'original highlight))
3098
3099(defun article-date-lapsed (&optional highlight)
3100 "Convert the current article date to time lapsed since it was sent."
3101 (interactive (list t))
3102 (article-date-ut 'lapsed highlight))
3103
6748645f
LMI
3104(defun article-update-date-lapsed ()
3105 "Function to be run from a timer to update the lapsed time line."
e0a8aa09
LT
3106 (save-match-data
3107 (let (deactivate-mark)
3108 (save-excursion
3109 (ignore-errors
3110 (walk-windows
3111 (lambda (w)
3112 (set-buffer (window-buffer w))
3113 (when (eq major-mode 'gnus-article-mode)
3114 (let ((mark (point-marker)))
3115 (goto-char (point-min))
3116 (when (re-search-forward "^X-Sent:" nil t)
3117 (article-date-lapsed t))
3118 (goto-char (marker-position mark))
3119 (move-marker mark nil))))
3120 nil 'visible))))))
6748645f
LMI
3121
3122(defun gnus-start-date-timer (&optional n)
3123 "Start a timer to update the X-Sent header in the article buffers.
3124The numerical prefix says how frequently (in seconds) the function
3125is to run."
3126 (interactive "p")
3127 (unless n
3128 (setq n 1))
3129 (gnus-stop-date-timer)
3130 (setq article-lapsed-timer
3131 (nnheader-run-at-time 1 n 'article-update-date-lapsed)))
3132
3133(defun gnus-stop-date-timer ()
3134 "Stop the X-Sent timer."
3135 (interactive)
3136 (when article-lapsed-timer
3137 (nnheader-cancel-timer article-lapsed-timer)
3138 (setq article-lapsed-timer nil)))
3139
eec82323
LMI
3140(defun article-date-user (&optional highlight)
3141 "Convert the current article date to the user-defined format.
3142This format is defined by the `gnus-article-time-format' variable."
3143 (interactive (list t))
3144 (article-date-ut 'user highlight))
3145
6748645f
LMI
3146(defun article-date-iso8601 (&optional highlight)
3147 "Convert the current article date to ISO8601."
3148 (interactive (list t))
3149 (article-date-ut 'iso8601 highlight))
3150
31640842
MB
3151(defmacro gnus-article-save-original-date (&rest forms)
3152 "Save the original date as a text property and evaluate FORMS."
3153 `(let* ((case-fold-search t)
3154 (start (progn
3155 (goto-char (point-min))
3156 (when (and (re-search-forward "^date:[\t\n ]+" nil t)
3157 (not (bolp)))
3158 (match-end 0))))
3159 (date (when (and start
3160 (re-search-forward "[\t ]*\n\\([^\t ]\\|\\'\\)"
3161 nil t))
3162 (buffer-substring-no-properties start
3163 (match-beginning 0)))))
3164 (goto-char (point-max))
3165 (skip-chars-backward "\n")
3166 (put-text-property (point-min) (point) 'original-date date)
3167 ,@forms
3168 (goto-char (point-max))
3169 (skip-chars-backward "\n")
3170 (put-text-property (point-min) (point) 'original-date date)))
3171
23f87bed
MB
3172;; (defun article-show-all ()
3173;; "Show all hidden text in the article buffer."
3174;; (interactive)
3175;; (save-excursion
3176;; (let ((inhibit-read-only t))
3177;; (gnus-article-unhide-text (point-min) (point-max)))))
3178
3179(defun article-remove-leading-whitespace ()
3180 "Remove excessive whitespace from all headers."
eec82323
LMI
3181 (interactive)
3182 (save-excursion
23f87bed
MB
3183 (save-restriction
3184 (let ((inhibit-read-only t))
3185 (article-narrow-to-head)
3186 (goto-char (point-min))
3187 (while (re-search-forward "^[^ :]+: \\([ \t]+\\)" nil t)
3188 (delete-region (match-beginning 1) (match-end 1)))))))
eec82323
LMI
3189
3190(defun article-emphasize (&optional arg)
3191 "Emphasize text according to `gnus-emphasis-alist'."
3192 (interactive (gnus-article-hidden-arg))
3193 (unless (gnus-article-check-hidden-text 'emphasis arg)
3194 (save-excursion
a1506d29 3195 (let ((alist (or
16409b0b 3196 (condition-case nil
a1506d29
JB
3197 (with-current-buffer gnus-summary-buffer
3198 gnus-article-emphasis-alist)
16409b0b
GM
3199 (error))
3200 gnus-emphasis-alist))
4e7d0221 3201 (inhibit-read-only t)
eec82323
LMI
3202 (props (append '(article-type emphasis)
3203 gnus-hidden-properties))
3204 regexp elem beg invisible visible face)
16409b0b 3205 (article-goto-body)
eec82323
LMI
3206 (setq beg (point))
3207 (while (setq elem (pop alist))
3208 (goto-char beg)
3209 (setq regexp (car elem)
3210 invisible (nth 1 elem)
3211 visible (nth 2 elem)
3212 face (nth 3 elem))
3213 (while (re-search-forward regexp nil t)
23f87bed
MB
3214 (when (and (match-beginning visible) (match-beginning invisible))
3215 (gnus-article-hide-text
3216 (match-beginning invisible) (match-end invisible) props)
3217 (gnus-article-unhide-text-type
3218 (match-beginning visible) (match-end visible) 'emphasis)
3219 (gnus-put-overlay-excluding-newlines
3220 (match-beginning visible) (match-end visible) 'face face)
3221 (gnus-add-wash-type 'emphasis)
3222 (goto-char (match-end invisible)))))))))
eec82323 3223
16409b0b
GM
3224(defun gnus-article-setup-highlight-words (&optional highlight-words)
3225 "Setup newsgroup emphasis alist."
3226 (unless gnus-article-emphasis-alist
3227 (let ((name (and gnus-newsgroup-name
3228 (gnus-group-real-name gnus-newsgroup-name))))
3229 (make-local-variable 'gnus-article-emphasis-alist)
a1506d29
JB
3230 (setq gnus-article-emphasis-alist
3231 (nconc
16409b0b
GM
3232 (let ((alist gnus-group-highlight-words-alist) elem highlight)
3233 (while (setq elem (pop alist))
3234 (when (and name (string-match (car elem) name))
3235 (setq alist nil
3236 highlight (copy-sequence (cdr elem)))))
3237 highlight)
3238 (copy-sequence highlight-words)
3239 (if gnus-newsgroup-name
a1506d29 3240 (copy-sequence (gnus-group-find-parameter
16409b0b
GM
3241 gnus-newsgroup-name 'highlight-words t)))
3242 gnus-emphasis-alist)))))
3243
1653df0f
SZ
3244(eval-when-compile
3245 (defvar gnus-summary-article-menu)
3246 (defvar gnus-summary-post-menu))
eec82323
LMI
3247
3248;;; Saving functions.
3249
3250(defun gnus-article-save (save-buffer file &optional num)
3251 "Save the currently selected article."
3252 (unless gnus-save-all-headers
3253 ;; Remove headers according to `gnus-saved-headers'.
3254 (let ((gnus-visible-headers
3255 (or gnus-saved-headers gnus-visible-headers))
3256 (gnus-article-buffer save-buffer))
6748645f
LMI
3257 (save-excursion
3258 (set-buffer save-buffer)
3259 (article-hide-headers 1 t))))
eec82323
LMI
3260 (save-window-excursion
3261 (if (not gnus-default-article-saver)
a8151ef7 3262 (error "No default saver is defined")
eec82323 3263 ;; !!! Magic! The saving functions all save
16409b0b 3264 ;; `gnus-save-article-buffer' (or so they think), but we
eec82323
LMI
3265 ;; bind that variable to our save-buffer.
3266 (set-buffer gnus-article-buffer)
3267 (let* ((gnus-save-article-buffer save-buffer)
3268 (filename
3269 (cond
3270 ((not gnus-prompt-before-saving) 'default)
3271 ((eq gnus-prompt-before-saving 'always) nil)
3272 (t file)))
3273 (gnus-number-of-articles-to-be-saved
3274 (when (eq gnus-prompt-before-saving t)
3275 num))) ; Magic
6748645f 3276 (set-buffer gnus-article-current-summary)
eec82323
LMI
3277 (funcall gnus-default-article-saver filename)))))
3278
3279(defun gnus-read-save-file-name (prompt &optional filename
3280 function group headers variable)
3281 (let ((default-name
3282 (funcall function group headers (symbol-value variable)))
3283 result)
4325195c 3284 (setq result
a1506d29 3285 (expand-file-name
4325195c
DL
3286 (cond
3287 ((eq filename 'default)
3288 default-name)
3289 ((eq filename t)
3290 default-name)
3291 (filename filename)
3292 (t
3293 (let* ((split-name (gnus-get-split-value gnus-split-methods))
3294 (prompt
3295 (format prompt
3296 (if (and gnus-number-of-articles-to-be-saved
3297 (> gnus-number-of-articles-to-be-saved 1))
3298 (format "these %d articles"
3299 gnus-number-of-articles-to-be-saved)
3300 "this article")))
3301 (file
3302 ;; Let the split methods have their say.
3303 (cond
3304 ;; No split name was found.
3305 ((null split-name)
3306 (read-file-name
3307 (concat prompt " (default "
81df110a 3308 (file-name-nondirectory default-name) "): ")
4325195c
DL
3309 (file-name-directory default-name)
3310 default-name))
3311 ;; A single group name is returned.
3312 ((stringp split-name)
3313 (setq default-name
3314 (funcall function split-name headers
3315 (symbol-value variable)))
3316 (read-file-name
3317 (concat prompt " (default "
81df110a 3318 (file-name-nondirectory default-name) "): ")
4325195c
DL
3319 (file-name-directory default-name)
3320 default-name))
3321 ;; A single split name was found
3322 ((= 1 (length split-name))
3323 (let* ((name (expand-file-name
23f87bed
MB
3324 (car split-name)
3325 gnus-article-save-directory))
4325195c
DL
3326 (dir (cond ((file-directory-p name)
3327 (file-name-as-directory name))
3328 ((file-exists-p name) name)
3329 (t gnus-article-save-directory))))
3330 (read-file-name
81df110a 3331 (concat prompt " (default " name "): ")
4325195c
DL
3332 dir name)))
3333 ;; A list of splits was found.
3334 (t
3335 (setq split-name (nreverse split-name))
3336 (let (result)
3337 (let ((file-name-history
3338 (nconc split-name file-name-history)))
3339 (setq result
3340 (expand-file-name
3341 (read-file-name
81df110a 3342 (concat prompt " (`M-p' for defaults): ")
4325195c
DL
3343 gnus-article-save-directory
3344 (car split-name))
3345 gnus-article-save-directory)))
3346 (car (push result file-name-history)))))))
3347 ;; Create the directory.
3348 (gnus-make-directory (file-name-directory file))
23f87bed 3349 ;; If we have read a directory, we append the default file name.
4325195c 3350 (when (file-directory-p file)
23f87bed
MB
3351 (setq file (expand-file-name (file-name-nondirectory
3352 default-name)
4325195c
DL
3353 (file-name-as-directory file))))
3354 ;; Possibly translate some characters.
3355 (nnheader-translate-file-chars file))))))
eec82323
LMI
3356 (gnus-make-directory (file-name-directory result))
3357 (set variable result)))
3358
3359(defun gnus-article-archive-name (group)
3360 "Return the first instance of an \"Archive-name\" in the current buffer."
3361 (let ((case-fold-search t))
3362 (when (re-search-forward "archive-name: *\\([^ \n\t]+\\)[ \t]*$" nil t)
3363 (nnheader-concat gnus-article-save-directory
3364 (match-string 1)))))
3365
3366(defun gnus-article-nndoc-name (group)
3367 "If GROUP is an nndoc group, return the name of the parent group."
3368 (when (eq (car (gnus-find-method-for-group group)) 'nndoc)
3369 (gnus-group-get-parameter group 'save-article-group)))
3370
3371(defun gnus-summary-save-in-rmail (&optional filename)
3372 "Append this article to Rmail file.
3373Optional argument FILENAME specifies file name.
3374Directory to save to is default to `gnus-article-save-directory'."
eec82323 3375 (setq filename (gnus-read-save-file-name
81df110a 3376 "Save %s in rmail file" filename
eec82323
LMI
3377 gnus-rmail-save-name gnus-newsgroup-name
3378 gnus-current-headers 'gnus-newsgroup-last-rmail))
3379 (gnus-eval-in-buffer-window gnus-save-article-buffer
3380 (save-excursion
3381 (save-restriction
3382 (widen)
6748645f
LMI
3383 (gnus-output-to-rmail filename))))
3384 filename)
eec82323
LMI
3385
3386(defun gnus-summary-save-in-mail (&optional filename)
3387 "Append this article to Unix mail file.
3388Optional argument FILENAME specifies file name.
3389Directory to save to is default to `gnus-article-save-directory'."
eec82323 3390 (setq filename (gnus-read-save-file-name
81df110a 3391 "Save %s in Unix mail file" filename
eec82323
LMI
3392 gnus-mail-save-name gnus-newsgroup-name
3393 gnus-current-headers 'gnus-newsgroup-last-mail))
3394 (gnus-eval-in-buffer-window gnus-save-article-buffer
3395 (save-excursion
3396 (save-restriction
3397 (widen)
3398 (if (and (file-readable-p filename)
23f87bed 3399 (file-regular-p filename)
eec82323 3400 (mail-file-babyl-p filename))
16409b0b 3401 (rmail-output-to-rmail-file filename t)
6748645f
LMI
3402 (gnus-output-to-mail filename)))))
3403 filename)
eec82323
LMI
3404
3405(defun gnus-summary-save-in-file (&optional filename overwrite)
3406 "Append this article to file.
3407Optional argument FILENAME specifies file name.
3408Directory to save to is default to `gnus-article-save-directory'."
eec82323 3409 (setq filename (gnus-read-save-file-name
81df110a 3410 "Save %s in file" filename
eec82323
LMI
3411 gnus-file-save-name gnus-newsgroup-name
3412 gnus-current-headers 'gnus-newsgroup-last-file))
3413 (gnus-eval-in-buffer-window gnus-save-article-buffer
3414 (save-excursion
3415 (save-restriction
3416 (widen)
3417 (when (and overwrite
3418 (file-exists-p filename))
3419 (delete-file filename))
6748645f
LMI
3420 (gnus-output-to-file filename))))
3421 filename)
eec82323
LMI
3422
3423(defun gnus-summary-write-to-file (&optional filename)
23f87bed 3424 "Write this article to a file, overwriting it if the file exists.
eec82323
LMI
3425Optional argument FILENAME specifies file name.
3426The directory to save in defaults to `gnus-article-save-directory'."
eec82323
LMI
3427 (gnus-summary-save-in-file nil t))
3428
3429(defun gnus-summary-save-body-in-file (&optional filename)
3430 "Append this article body to a file.
3431Optional argument FILENAME specifies file name.
3432The directory to save in defaults to `gnus-article-save-directory'."
eec82323 3433 (setq filename (gnus-read-save-file-name
81df110a 3434 "Save %s body in file" filename
eec82323
LMI
3435 gnus-file-save-name gnus-newsgroup-name
3436 gnus-current-headers 'gnus-newsgroup-last-file))
3437 (gnus-eval-in-buffer-window gnus-save-article-buffer
3438 (save-excursion
3439 (save-restriction
3440 (widen)
16409b0b 3441 (when (article-goto-body)
eec82323 3442 (narrow-to-region (point) (point-max)))
6748645f
LMI
3443 (gnus-output-to-file filename))))
3444 filename)
eec82323
LMI
3445
3446(defun gnus-summary-save-in-pipe (&optional command)
3447 "Pipe this article to subprocess."
eec82323 3448 (setq command
16409b0b
GM
3449 (cond ((and (eq command 'default)
3450 gnus-last-shell-command)
eec82323 3451 gnus-last-shell-command)
676a7cc9
SZ
3452 ((stringp command)
3453 command)
eec82323
LMI
3454 (t (read-string
3455 (format
3456 "Shell command on %s: "
3457 (if (and gnus-number-of-articles-to-be-saved
3458 (> gnus-number-of-articles-to-be-saved 1))
3459 (format "these %d articles"
3460 gnus-number-of-articles-to-be-saved)
3461 "this article"))
3462 gnus-last-shell-command))))
3463 (when (string-equal command "")
676a7cc9
SZ
3464 (if gnus-last-shell-command
3465 (setq command gnus-last-shell-command)
715a2ca2 3466 (error "A command is required")))
eec82323
LMI
3467 (gnus-eval-in-buffer-window gnus-article-buffer
3468 (save-restriction
3469 (widen)
3470 (shell-command-on-region (point-min) (point-max) command nil)))
3471 (setq gnus-last-shell-command command))
3472
23f87bed
MB
3473(defmacro gnus-read-string (prompt &optional initial-contents history
3474 default-value)
3475 "Like `read-string' but allow for older XEmacsen that don't have the 5th arg."
3476 (if (and (featurep 'xemacs)
3477 (< emacs-minor-version 2))
3478 `(read-string ,prompt ,initial-contents ,history)
3479 `(read-string ,prompt ,initial-contents ,history ,default-value)))
3480
3481(defun gnus-summary-pipe-to-muttprint (&optional command)
3482 "Pipe this article to muttprint."
3483 (setq command (gnus-read-string
3484 "Print using command: " gnus-summary-muttprint-program
3485 nil gnus-summary-muttprint-program))
3486 (gnus-summary-save-in-pipe command))
3487
eec82323
LMI
3488;;; Article file names when saving.
3489
3490(defun gnus-capitalize-newsgroup (newsgroup)
3491 "Capitalize NEWSGROUP name."
3492 (when (not (zerop (length newsgroup)))
3493 (concat (char-to-string (upcase (aref newsgroup 0)))
3494 (substring newsgroup 1))))
3495
3496(defun gnus-Numeric-save-name (newsgroup headers &optional last-file)
3497 "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
3498If variable `gnus-use-long-file-name' is non-nil, it is ~/News/News.group/num.
3499Otherwise, it is like ~/News/news/group/num."
3500 (let ((default
3501 (expand-file-name
3502 (concat (if (gnus-use-long-file-name 'not-save)
3503 (gnus-capitalize-newsgroup newsgroup)
3504 (gnus-newsgroup-directory-form newsgroup))
3505 "/" (int-to-string (mail-header-number headers)))
3506 gnus-article-save-directory)))
3507 (if (and last-file
3508 (string-equal (file-name-directory default)
3509 (file-name-directory last-file))
3510 (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
3511 default
3512 (or last-file default))))
3513
3514(defun gnus-numeric-save-name (newsgroup headers &optional last-file)
3515 "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
3516If variable `gnus-use-long-file-name' is non-nil, it is
3517~/News/news.group/num. Otherwise, it is like ~/News/news/group/num."
3518 (let ((default
3519 (expand-file-name
3520 (concat (if (gnus-use-long-file-name 'not-save)
3521 newsgroup
3522 (gnus-newsgroup-directory-form newsgroup))
3523 "/" (int-to-string (mail-header-number headers)))
3524 gnus-article-save-directory)))
3525 (if (and last-file
3526 (string-equal (file-name-directory default)
3527 (file-name-directory last-file))
3528 (string-match "^[0-9]+$" (file-name-nondirectory last-file)))
3529 default
3530 (or last-file default))))
3531
eec82323
LMI
3532(defun gnus-plain-save-name (newsgroup headers &optional last-file)
3533 "Generate file name from NEWSGROUP, HEADERS, and optional LAST-FILE.
3534If variable `gnus-use-long-file-name' is non-nil, it is
3535~/News/news.group. Otherwise, it is like ~/News/news/group/news."
3536 (or last-file
3537 (expand-file-name
3538 (if (gnus-use-long-file-name 'not-save)
3539 newsgroup
23f87bed
MB
3540 (file-relative-name
3541 (expand-file-name "news" (gnus-newsgroup-directory-form newsgroup))
3542 default-directory))
eec82323
LMI
3543 gnus-article-save-directory)))
3544
23f87bed
MB
3545(defun gnus-sender-save-name (newsgroup headers &optional last-file)
3546 "Generate file name from sender."
3547 (let ((from (mail-header-from headers)))
3548 (expand-file-name
3549 (if (and from (string-match "\\([^ <]+\\)@" from))
3550 (match-string 1 from)
3551 "nobody")
3552 gnus-article-save-directory)))
3553
3554(defun article-verify-x-pgp-sig ()
3555 "Verify X-PGP-Sig."
3556 (interactive)
3557 (if (gnus-buffer-live-p gnus-original-article-buffer)
3558 (let ((sig (with-current-buffer gnus-original-article-buffer
3559 (gnus-fetch-field "X-PGP-Sig")))
3560 items info headers)
3561 (when (and sig
3562 mml2015-use
3563 (mml2015-clear-verify-function))
3564 (with-temp-buffer
3565 (insert-buffer-substring gnus-original-article-buffer)
3566 (setq items (split-string sig))
3567 (message-narrow-to-head)
3568 (let ((inhibit-point-motion-hooks t)
3569 (case-fold-search t))
3570 ;; Don't verify multiple headers.
3571 (setq headers (mapconcat (lambda (header)
3572 (concat header ": "
3573 (mail-fetch-field header)
3574 "\n"))
3575 (split-string (nth 1 items) ",") "")))
3576 (delete-region (point-min) (point-max))
3577 (insert "-----BEGIN PGP SIGNED MESSAGE-----\n\n")
3578 (insert "X-Signed-Headers: " (nth 1 items) "\n")
3579 (insert headers)
3580 (widen)
3581 (forward-line)
3582 (while (not (eobp))
3583 (if (looking-at "^-")
3584 (insert "- "))
3585 (forward-line))
3586 (insert "\n-----BEGIN PGP SIGNATURE-----\n")
3587 (insert "Version: " (car items) "\n\n")
3588 (insert (mapconcat 'identity (cddr items) "\n"))
3589 (insert "\n-----END PGP SIGNATURE-----\n")
3590 (let ((mm-security-handle (list (format "multipart/signed"))))
3591 (mml2015-clean-buffer)
3592 (let ((coding-system-for-write (or gnus-newsgroup-charset
3593 'iso-8859-1)))
3594 (funcall (mml2015-clear-verify-function)))
3595 (setq info
3596 (or (mm-handle-multipart-ctl-parameter
3597 mm-security-handle 'gnus-details)
3598 (mm-handle-multipart-ctl-parameter
3599 mm-security-handle 'gnus-info)))))
3600 (when info
3601 (let ((inhibit-read-only t) bface eface)
3602 (save-restriction
3603 (message-narrow-to-head)
3604 (goto-char (point-max))
3605 (forward-line -1)
3606 (setq bface (get-text-property (gnus-point-at-bol) 'face)
3607 eface (get-text-property (1- (gnus-point-at-eol)) 'face))
3608 (message-remove-header "X-Gnus-PGP-Verify")
3609 (if (re-search-forward "^X-PGP-Sig:" nil t)
3610 (forward-line)
3611 (goto-char (point-max)))
3612 (narrow-to-region (point) (point))
3613 (insert "X-Gnus-PGP-Verify: " info "\n")
3614 (goto-char (point-min))
3615 (forward-line)
3616 (while (not (eobp))
3617 (if (not (looking-at "^[ \t]"))
3618 (insert " "))
3619 (forward-line))
3620 ;; Do highlighting.
3621 (goto-char (point-min))
3622 (when (looking-at "\\([^:]+\\): *")
3623 (put-text-property (match-beginning 1) (1+ (match-end 1))
3624 'face bface)
3625 (put-text-property (match-end 0) (point-max)
3626 'face eface)))))))))
3627
3628(defun article-verify-cancel-lock ()
3629 "Verify Cancel-Lock header."
3630 (interactive)
3631 (if (gnus-buffer-live-p gnus-original-article-buffer)
3632 (canlock-verify gnus-original-article-buffer)))
3633
eec82323
LMI
3634(eval-and-compile
3635 (mapcar
3636 (lambda (func)
3637 (let (afunc gfunc)
3638 (if (consp func)
3639 (setq afunc (car func)
3640 gfunc (cdr func))
3641 (setq afunc func
3642 gfunc (intern (format "gnus-%s" func))))
16409b0b 3643 (defalias gfunc
23f87bed 3644 (when (fboundp afunc)
16409b0b
GM
3645 `(lambda (&optional interactive &rest args)
3646 ,(documentation afunc t)
3647 (interactive (list t))
3648 (save-excursion
3649 (set-buffer gnus-article-buffer)
3650 (if interactive
3651 (call-interactively ',afunc)
3652 (apply ',afunc args))))))))
eec82323 3653 '(article-hide-headers
23f87bed
MB
3654 article-verify-x-pgp-sig
3655 article-verify-cancel-lock
eec82323
LMI
3656 article-hide-boring-headers
3657 article-treat-overstrike
16409b0b
GM
3658 article-fill-long-lines
3659 article-capitalize-sentences
eec82323 3660 article-remove-cr
23f87bed 3661 article-remove-leading-whitespace
eec82323 3662 article-display-x-face
23f87bed 3663 article-display-face
eec82323 3664 article-de-quoted-unreadable
16409b0b
GM
3665 article-de-base64-unreadable
3666 article-decode-HZ
3667 article-wash-html
23f87bed 3668 article-unsplit-urls
16409b0b 3669 article-hide-list-identifiers
16409b0b
GM
3670 article-strip-banner
3671 article-babel
eec82323
LMI
3672 article-hide-pem
3673 article-hide-signature
16409b0b 3674 article-strip-headers-in-body
eec82323
LMI
3675 article-remove-trailing-blank-lines
3676 article-strip-leading-blank-lines
3677 article-strip-multiple-blank-lines
3678 article-strip-leading-space
16409b0b 3679 article-strip-trailing-space
eec82323 3680 article-strip-blank-lines
6748645f 3681 article-strip-all-blank-lines
eec82323 3682 article-date-local
23f87bed 3683 article-date-english
6748645f 3684 article-date-iso8601
eec82323
LMI
3685 article-date-original
3686 article-date-ut
16409b0b
GM
3687 article-decode-mime-words
3688 article-decode-charset
3689 article-decode-encoded-words
eec82323
LMI
3690 article-date-user
3691 article-date-lapsed
3692 article-emphasize
6748645f 3693 article-treat-dumbquotes
16409b0b 3694 article-normalize-headers
23f87bed
MB
3695;; (article-show-all . gnus-article-show-all-headers)
3696 )))
eec82323
LMI
3697\f
3698;;;
3699;;; Gnus article mode
3700;;;
3701
3702(put 'gnus-article-mode 'mode-class 'special)
3703
16409b0b
GM
3704(set-keymap-parent gnus-article-mode-map widget-keymap)
3705
a8151ef7
LMI
3706(gnus-define-keys gnus-article-mode-map
3707 " " gnus-article-goto-next-page
3708 "\177" gnus-article-goto-prev-page
3709 [delete] gnus-article-goto-prev-page
16409b0b 3710 [backspace] gnus-article-goto-prev-page
a8151ef7
LMI
3711 "\C-c^" gnus-article-refer-article
3712 "h" gnus-article-show-summary
3713 "s" gnus-article-show-summary
3714 "\C-c\C-m" gnus-article-mail
3715 "?" gnus-article-describe-briefly
16409b0b 3716 "e" gnus-summary-edit-article
a8151ef7
LMI
3717 "<" beginning-of-buffer
3718 ">" end-of-buffer
3719 "\C-c\C-i" gnus-info-find-node
3720 "\C-c\C-b" gnus-bug
23f87bed
MB
3721 "R" gnus-article-reply-with-original
3722 "F" gnus-article-followup-with-original
520aa572
SZ
3723 "\C-hk" gnus-article-describe-key
3724 "\C-hc" gnus-article-describe-key-briefly
a8151ef7
LMI
3725
3726 "\C-d" gnus-article-read-summary-keys
3727 "\M-*" gnus-article-read-summary-keys
3728 "\M-#" gnus-article-read-summary-keys
3729 "\M-^" gnus-article-read-summary-keys
3730 "\M-g" gnus-article-read-summary-keys)
3731
3732(substitute-key-definition
3733 'undefined 'gnus-article-read-summary-keys gnus-article-mode-map)
eec82323
LMI
3734
3735(defun gnus-article-make-menu-bar ()
23f87bed
MB
3736 (unless (boundp 'gnus-article-commands-menu)
3737 (gnus-summary-make-menu-bar))
eec82323
LMI
3738 (gnus-turn-off-edit-menu 'article)
3739 (unless (boundp 'gnus-article-article-menu)
3740 (easy-menu-define
3741 gnus-article-article-menu gnus-article-mode-map ""
3742 '("Article"
3743 ["Scroll forwards" gnus-article-goto-next-page t]
3744 ["Scroll backwards" gnus-article-goto-prev-page t]
3745 ["Show summary" gnus-article-show-summary t]
3746 ["Fetch Message-ID at point" gnus-article-refer-article t]
6748645f
LMI
3747 ["Mail to address at point" gnus-article-mail t]
3748 ["Send a bug report" gnus-bug t]))
eec82323
LMI
3749
3750 (easy-menu-define
3751 gnus-article-treatment-menu gnus-article-mode-map ""
bb367cba 3752 ;; Fixme: this should use :active (and maybe :visible).
eec82323
LMI
3753 '("Treatment"
3754 ["Hide headers" gnus-article-hide-headers t]
3755 ["Hide signature" gnus-article-hide-signature t]
3756 ["Hide citation" gnus-article-hide-citation t]
3757 ["Treat overstrike" gnus-article-treat-overstrike t]
3758 ["Remove carriage return" gnus-article-remove-cr t]
23f87bed 3759 ["Remove leading whitespace" gnus-article-remove-leading-whitespace t]
16409b0b
GM
3760 ["Remove quoted-unreadable" gnus-article-de-quoted-unreadable t]
3761 ["Remove base64" gnus-article-de-base64-unreadable t]
3762 ["Treat html" gnus-article-wash-html t]
23f87bed 3763 ["Remove newlines from within URLs" gnus-article-unsplit-urls t]
16409b0b 3764 ["Decode HZ" gnus-article-decode-HZ t]))
eec82323 3765
6748645f 3766 ;; Note "Commands" menu is defined in gnus-sum.el for consistency
eec82323 3767
23f87bed 3768 ;; Note "Post" menu is defined in gnus-sum.el for consistency
eec82323 3769
6748645f 3770 (gnus-run-hooks 'gnus-article-menu-hook)))
eec82323
LMI
3771
3772(defun gnus-article-mode ()
3773 "Major mode for displaying an article.
3774
3775All normal editing commands are switched off.
3776
3777The following commands are available in addition to all summary mode
3778commands:
3779\\<gnus-article-mode-map>
3780\\[gnus-article-next-page]\t Scroll the article one page forwards
3781\\[gnus-article-prev-page]\t Scroll the article one page backwards
3782\\[gnus-article-refer-article]\t Go to the article referred to by an article id near point
3783\\[gnus-article-show-summary]\t Display the summary buffer
3784\\[gnus-article-mail]\t Send a reply to the address near point
3785\\[gnus-article-describe-briefly]\t Describe the current mode briefly
3786\\[gnus-info-find-node]\t Go to the Gnus info node"
3787 (interactive)
0eb586fc 3788 (kill-all-local-variables)
eec82323
LMI
3789 (gnus-simplify-mode-line)
3790 (setq mode-name "Article")
3791 (setq major-mode 'gnus-article-mode)
3792 (make-local-variable 'minor-mode-alist)
eec82323 3793 (use-local-map gnus-article-mode-map)
1653df0f 3794 (when (gnus-visual-p 'article-menu 'menu)
23f87bed
MB
3795 (gnus-article-make-menu-bar)
3796 (when gnus-summary-tool-bar-map
3797 (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map)))
eec82323
LMI
3798 (gnus-update-format-specifications nil 'article-mode)
3799 (set (make-local-variable 'page-delimiter) gnus-page-delimiter)
23f87bed 3800 (set (make-local-variable 'gnus-page-broken) nil)
6748645f
LMI
3801 (make-local-variable 'gnus-button-marker-list)
3802 (make-local-variable 'gnus-article-current-summary)
16409b0b
GM
3803 (make-local-variable 'gnus-article-mime-handles)
3804 (make-local-variable 'gnus-article-decoded-p)
3805 (make-local-variable 'gnus-article-mime-handle-alist)
3806 (make-local-variable 'gnus-article-wash-types)
23f87bed
MB
3807 (make-local-variable 'gnus-article-image-alist)
3808 (make-local-variable 'gnus-article-charset)
3809 (make-local-variable 'gnus-article-ignored-charsets)
f429956a 3810 ;; Prevent recent Emacsen from displaying non-break space as "\ ".
fc1c32c1 3811 (set (make-local-variable 'nobreak-char-display) nil)
eec82323 3812 (gnus-set-default-directory)
16409b0b 3813 (buffer-disable-undo)
eec82323
LMI
3814 (setq buffer-read-only t)
3815 (set-syntax-table gnus-article-mode-syntax-table)
16409b0b 3816 (mm-enable-multibyte)
cfcd5c91 3817 (gnus-run-mode-hooks 'gnus-article-mode-hook))
eec82323
LMI
3818
3819(defun gnus-article-setup-buffer ()
3820 "Initialize the article buffer."
3821 (let* ((name (if gnus-single-article-buffer "*Article*"
3822 (concat "*Article " gnus-newsgroup-name "*")))
3823 (original
3824 (progn (string-match "\\*Article" name)
3825 (concat " *Original Article"
3826 (substring name (match-end 0))))))
3827 (setq gnus-article-buffer name)
3828 (setq gnus-original-article-buffer original)
16409b0b 3829 (setq gnus-article-mime-handle-alist nil)
eec82323
LMI
3830 ;; This might be a variable local to the summary buffer.
3831 (unless gnus-single-article-buffer
3832 (save-excursion
3833 (set-buffer gnus-summary-buffer)
3834 (setq gnus-article-buffer name)
3835 (setq gnus-original-article-buffer original)
3836 (gnus-set-global-variables)))
16409b0b 3837 (gnus-article-setup-highlight-words)
eec82323
LMI
3838 ;; Init original article buffer.
3839 (save-excursion
6748645f 3840 (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
16409b0b 3841 (mm-enable-multibyte)
eec82323 3842 (setq major-mode 'gnus-original-article-mode)
eec82323 3843 (make-local-variable 'gnus-original-article))
aba1948a
MB
3844 (if (and (get-buffer name)
3845 (with-current-buffer name
3846 (if gnus-article-edit-mode
3847 (if (y-or-n-p "Article mode edit in progress; discard? ")
3848 (progn
3849 (set-buffer-modified-p nil)
3850 (gnus-kill-buffer name)
3851 (message "")
3852 nil)
3853 (error "Action aborted"))
3854 t)))
eec82323
LMI
3855 (save-excursion
3856 (set-buffer name)
23f87bed 3857 (set (make-local-variable 'gnus-article-edit-mode) nil)
16409b0b
GM
3858 (when gnus-article-mime-handles
3859 (mm-destroy-parts gnus-article-mime-handles)
3860 (setq gnus-article-mime-handles nil))
3861 ;; Set it to nil in article-buffer!
a1506d29 3862 (setq gnus-article-mime-handle-alist nil)
16409b0b 3863 (buffer-disable-undo)
eec82323 3864 (setq buffer-read-only t)
23f87bed
MB
3865 ;; This list just keeps growing if we don't reset it.
3866 (setq gnus-button-marker-list nil)
eec82323
LMI
3867 (unless (eq major-mode 'gnus-article-mode)
3868 (gnus-article-mode))
3869 (current-buffer))
3870 (save-excursion
6748645f 3871 (set-buffer (gnus-get-buffer-create name))
eec82323
LMI
3872 (gnus-article-mode)
3873 (make-local-variable 'gnus-summary-buffer)
16409b0b 3874 (gnus-summary-set-local-parameters gnus-newsgroup-name)
eec82323
LMI
3875 (current-buffer)))))
3876
3877;; Set article window start at LINE, where LINE is the number of lines
3878;; from the head of the article.
3879(defun gnus-article-set-window-start (&optional line)
3880 (set-window-start
23f87bed 3881 (gnus-get-buffer-window gnus-article-buffer t)
eec82323
LMI
3882 (save-excursion
3883 (set-buffer gnus-article-buffer)
3884 (goto-char (point-min))
3885 (if (not line)
3886 (point-min)
3887 (gnus-message 6 "Moved to bookmark")
3888 (search-forward "\n\n" nil t)
3889 (forward-line line)
3890 (point)))))
3891
3892(defun gnus-article-prepare (article &optional all-headers header)
3893 "Prepare ARTICLE in article mode buffer.
3894ARTICLE should either be an article number or a Message-ID.
3895If ARTICLE is an id, HEADER should be the article headers.
3896If ALL-HEADERS is non-nil, no headers are hidden."
3897 (save-excursion
3898 ;; Make sure we start in a summary buffer.
3899 (unless (eq major-mode 'gnus-summary-mode)
3900 (set-buffer gnus-summary-buffer))
3901 (setq gnus-summary-buffer (current-buffer))
eec82323
LMI
3902 (let* ((gnus-article (if header (mail-header-number header) article))
3903 (summary-buffer (current-buffer))
6748645f 3904 (gnus-tmp-internal-hook gnus-article-internal-prepare-hook)
eec82323
LMI
3905 (group gnus-newsgroup-name)
3906 result)
3907 (save-excursion
3908 (gnus-article-setup-buffer)
3909 (set-buffer gnus-article-buffer)
3910 ;; Deactivate active regions.
3911 (when (and (boundp 'transient-mark-mode)
3912 transient-mark-mode)
3913 (setq mark-active nil))
4e7d0221 3914 (if (not (setq result (let ((inhibit-read-only t))
eec82323
LMI
3915 (gnus-request-article-this-buffer
3916 article group))))
3917 ;; There is no such article.
3918 (save-excursion
3919 (when (and (numberp article)
3920 (not (memq article gnus-newsgroup-sparse)))
3921 (setq gnus-article-current
3922 (cons gnus-newsgroup-name article))
3923 (set-buffer gnus-summary-buffer)
3924 (setq gnus-current-article article)
23f87bed
MB
3925 (if (and (memq article gnus-newsgroup-undownloaded)
3926 (not (gnus-online (gnus-find-method-for-group
3927 gnus-newsgroup-name))))
6748645f
LMI
3928 (progn
3929 (gnus-summary-set-agent-mark article)
3930 (message "Message marked for downloading"))
3931 (gnus-summary-mark-article article gnus-canceled-mark)
3932 (unless (memq article gnus-newsgroup-sparse)
16409b0b 3933 (gnus-error 1 "No such article (may have expired or been canceled)")))))
6748645f
LMI
3934 (if (or (eq result 'pseudo)
3935 (eq result 'nneething))
eec82323
LMI
3936 (progn
3937 (save-excursion
3938 (set-buffer summary-buffer)
6748645f 3939 (push article gnus-newsgroup-history)
eec82323 3940 (setq gnus-last-article gnus-current-article
eec82323
LMI
3941 gnus-current-article 0
3942 gnus-current-headers nil
3943 gnus-article-current nil)
3944 (if (eq result 'nneething)
3945 (gnus-configure-windows 'summary)
3946 (gnus-configure-windows 'article))
3947 (gnus-set-global-variables))
16409b0b
GM
3948 (let ((gnus-article-mime-handle-alist-1
3949 gnus-article-mime-handle-alist))
3950 (gnus-set-mode-line 'article)))
eec82323
LMI
3951 ;; The result from the `request' was an actual article -
3952 ;; or at least some text that is now displayed in the
3953 ;; article buffer.
3954 (when (and (numberp article)
3955 (not (eq article gnus-current-article)))
3956 ;; Seems like a new article has been selected.
3957 ;; `gnus-current-article' must be an article number.
3958 (save-excursion
3959 (set-buffer summary-buffer)
6748645f 3960 (push article gnus-newsgroup-history)
eec82323 3961 (setq gnus-last-article gnus-current-article
eec82323
LMI
3962 gnus-current-article article
3963 gnus-current-headers
3964 (gnus-summary-article-header gnus-current-article)
3965 gnus-article-current
3966 (cons gnus-newsgroup-name gnus-current-article))
3967 (unless (vectorp gnus-current-headers)
3968 (setq gnus-current-headers nil))
6748645f
LMI
3969 (gnus-summary-goto-subject gnus-current-article)
3970 (when (gnus-summary-show-thread)
3971 ;; If the summary buffer really was folded, the
3972 ;; previous goto may not actually have gone to
3973 ;; the right article, but the thread root instead.
3974 ;; So we go again.
3975 (gnus-summary-goto-subject gnus-current-article))
3976 (gnus-run-hooks 'gnus-mark-article-hook)
eec82323
LMI
3977 (gnus-set-mode-line 'summary)
3978 (when (gnus-visual-p 'article-highlight 'highlight)
6748645f 3979 (gnus-run-hooks 'gnus-visual-mark-article-hook))
eec82323 3980 ;; Set the global newsgroup variables here.
eec82323
LMI
3981 (gnus-set-global-variables)
3982 (setq gnus-have-all-headers
6748645f 3983 (or all-headers gnus-show-all-headers))))
e0bad764
DL
3984 (save-excursion
3985 (gnus-configure-windows 'article))
eec82323
LMI
3986 (when (or (numberp article)
3987 (stringp article))
16409b0b 3988 (gnus-article-prepare-display)
eec82323
LMI
3989 ;; Do page break.
3990 (goto-char (point-min))
23f87bed
MB
3991 (when gnus-break-pages
3992 (gnus-narrow-to-page)))
16409b0b
GM
3993 (let ((gnus-article-mime-handle-alist-1
3994 gnus-article-mime-handle-alist))
3995 (gnus-set-mode-line 'article))
3996 (article-goto-body)
23f87bed
MB
3997 (unless (bobp)
3998 (forward-line -1))
6748645f 3999 (set-window-point (get-buffer-window (current-buffer)) (point))
16409b0b 4000 (gnus-configure-windows 'article)
eec82323
LMI
4001 t))))))
4002
16409b0b
GM
4003;;;###autoload
4004(defun gnus-article-prepare-display ()
4005 "Make the current buffer look like a nice article."
4006 ;; Hooks for getting information from the article.
4007 ;; This hook must be called before being narrowed.
4008 (let ((gnus-article-buffer (current-buffer))
23f87bed
MB
4009 buffer-read-only
4010 (inhibit-read-only t))
16409b0b
GM
4011 (unless (eq major-mode 'gnus-article-mode)
4012 (gnus-article-mode))
4013 (setq buffer-read-only nil
23f87bed
MB
4014 gnus-article-wash-types nil
4015 gnus-article-image-alist nil)
16409b0b
GM
4016 (gnus-run-hooks 'gnus-tmp-internal-hook)
4017 (when gnus-display-mime-function
4018 (funcall gnus-display-mime-function))
4019 (gnus-run-hooks 'gnus-article-prepare-hook)))
4020
4021;;;
4022;;; Gnus MIME viewing functions
4023;;;
4024
4025(defvar gnus-mime-button-line-format "%{%([%p. %d%T]%)%}%e\n"
23f87bed
MB
4026 "Format of the MIME buttons.
4027
4028Valid specifiers include:
16409b0b
GM
4029%t The MIME type
4030%T MIME type, along with additional info
4031%n The `name' parameter
4032%d The description, if any
4033%l The length of the encoded part
4034%p The part identifier number
23f87bed
MB
4035%e Dots if the part isn't displayed
4036
4037General format specifiers can also be used. See Info node
4038`(gnus)Formatting Variables'.")
16409b0b
GM
4039
4040(defvar gnus-mime-button-line-format-alist
4041 '((?t gnus-tmp-type ?s)
4042 (?T gnus-tmp-type-long ?s)
4043 (?n gnus-tmp-name ?s)
4044 (?d gnus-tmp-description ?s)
4045 (?p gnus-tmp-id ?s)
4046 (?l gnus-tmp-length ?d)
4047 (?e gnus-tmp-dots ?s)))
4048
4049(defvar gnus-mime-button-commands
4050 '((gnus-article-press-button "\r" "Toggle Display")
4051 (gnus-mime-view-part "v" "View Interactively...")
4052 (gnus-mime-view-part-as-type "t" "View As Type...")
23f87bed 4053 (gnus-mime-view-part-as-charset "C" "View As charset...")
16409b0b 4054 (gnus-mime-save-part "o" "Save...")
23f87bed
MB
4055 (gnus-mime-save-part-and-strip "\C-o" "Save and Strip")
4056 (gnus-mime-delete-part "d" "Delete part")
16409b0b
GM
4057 (gnus-mime-copy-part "c" "View As Text, In Other Buffer")
4058 (gnus-mime-inline-part "i" "View As Text, In This Buffer")
23f87bed
MB
4059 (gnus-mime-view-part-internally "E" "View Internally")
4060 (gnus-mime-view-part-externally "e" "View Externally")
4061 (gnus-mime-print-part "p" "Print")
e0bad764 4062 (gnus-mime-pipe-part "|" "Pipe To Command...")
23f87bed 4063 (gnus-mime-action-on-part "." "Take action on the part...")))
16409b0b
GM
4064
4065(defun gnus-article-mime-part-status ()
4066 (if gnus-article-mime-handle-alist-1
23f87bed
MB
4067 (if (eq 1 (length gnus-article-mime-handle-alist-1))
4068 " (1 part)"
4069 (format " (%d parts)" (length gnus-article-mime-handle-alist-1)))
16409b0b
GM
4070 ""))
4071
4072(defvar gnus-mime-button-map
4073 (let ((map (make-sparse-keymap)))
23f87bed
MB
4074 (unless (>= (string-to-number emacs-version) 21)
4075 ;; XEmacs doesn't care.
4076 (set-keymap-parent map gnus-article-mode-map))
16409b0b
GM
4077 (define-key map gnus-mouse-2 'gnus-article-push-button)
4078 (define-key map gnus-down-mouse-3 'gnus-mime-button-menu)
4079 (dolist (c gnus-mime-button-commands)
4080 (define-key map (cadr c) (car c)))
4081 map))
4082
23f87bed
MB
4083(easy-menu-define
4084 gnus-mime-button-menu gnus-mime-button-map "MIME button menu."
4085 `("MIME Part"
4086 ,@(mapcar (lambda (c)
4087 (vector (caddr c) (car c) :enable t))
4088 gnus-mime-button-commands)))
4089
4090(eval-when-compile
4091 (define-compiler-macro popup-menu (&whole form
4092 menu &optional position prefix)
4093 (if (and (fboundp 'popup-menu)
4094 (not (memq 'popup-menu (assoc "lmenu" load-history))))
4095 form
4096 ;; Gnus is probably running under Emacs 20.
4097 `(let* ((menu (cdr ,menu))
4098 (response (x-popup-menu
4099 t (list (car menu)
4100 (cons "" (mapcar (lambda (c)
4101 (cons (caddr c) (car c)))
4102 (cdr menu)))))))
4103 (if response
4104 (call-interactively (nth 3 (assq response menu))))))))
4105
4106(defun gnus-mime-button-menu (event prefix)
4107 "Construct a context-sensitive menu of MIME commands."
4108 (interactive "e\nP")
4109 (save-window-excursion
4110 (let ((pos (event-start event)))
4111 (select-window (posn-window pos))
4112 (goto-char (posn-point pos))
4113 (gnus-article-check-buffer)
4114 (popup-menu gnus-mime-button-menu nil prefix))))
16409b0b
GM
4115
4116(defun gnus-mime-view-all-parts (&optional handles)
4117 "View all the MIME parts."
4118 (interactive)
4119 (save-current-buffer
4120 (set-buffer gnus-article-buffer)
4121 (let ((handles (or handles gnus-article-mime-handles))
4122 (mail-parse-charset gnus-newsgroup-charset)
a1506d29 4123 (mail-parse-ignored-charsets
23f87bed
MB
4124 (with-current-buffer gnus-summary-buffer
4125 gnus-newsgroup-ignored-charsets)))
4126 (when handles
4127 (mm-remove-parts handles)
4128 (goto-char (point-min))
4129 (or (search-forward "\n\n") (goto-char (point-max)))
4130 (let ((inhibit-read-only t))
4131 (delete-region (point) (point-max))
4132 (mm-display-parts handles))))))
4133
4134(defun gnus-mime-save-part-and-strip ()
4135 "Save the MIME part under point then replace it with an external body."
4136 (interactive)
4137 (gnus-article-check-buffer)
4138 (when (gnus-group-read-only-p)
4139 (error "The current group does not support deleting of parts"))
4140 (when (mm-complicated-handles gnus-article-mime-handles)
4141 (error "\
4142The current article has a complicated MIME structure, giving up..."))
4143 (when (gnus-yes-or-no-p "\
4144Deleting parts may malfunction or destroy the article; continue? ")
4145 (let* ((data (get-text-property (point) 'gnus-data))
4146 file param
4147 (handles gnus-article-mime-handles))
4148 (setq file (and data (mm-save-part data)))
4149 (when file
4150 (with-current-buffer (mm-handle-buffer data)
4151 (erase-buffer)
4152 (insert "Content-Type: " (mm-handle-media-type data))
4153 (mml-insert-parameter-string (cdr (mm-handle-type data))
4154 '(charset))
531bedc3
MB
4155 ;; Add a filename for the sake of saving the part again.
4156 (mml-insert-parameter
4157 (mail-header-encode-parameter "name" (file-name-nondirectory file)))
23f87bed
MB
4158 (insert "\n")
4159 (insert "Content-ID: " (message-make-message-id) "\n")
4160 (insert "Content-Transfer-Encoding: binary\n")
4161 (insert "\n"))
4162 (setcdr data
4163 (cdr (mm-make-handle nil
4164 `("message/external-body"
4165 (access-type . "LOCAL-FILE")
4166 (name . ,file)))))
4167 (set-buffer gnus-summary-buffer)
4168 (gnus-article-edit-article
4169 `(lambda ()
4170 (erase-buffer)
4171 (let ((mail-parse-charset (or gnus-article-charset
4172 ',gnus-newsgroup-charset))
4173 (mail-parse-ignored-charsets
4174 (or gnus-article-ignored-charsets
4175 ',gnus-newsgroup-ignored-charsets))
4176 (mbl mml-buffer-list))
4177 (setq mml-buffer-list nil)
d8a88581 4178 (insert-buffer-substring gnus-original-article-buffer)
23f87bed
MB
4179 (mime-to-mml ',handles)
4180 (setq gnus-article-mime-handles nil)
4181 (let ((mbl1 mml-buffer-list))
4182 (setq mml-buffer-list mbl)
4183 (set (make-local-variable 'mml-buffer-list) mbl1))
4184 (gnus-make-local-hook 'kill-buffer-hook)
4185 (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
4186 `(lambda (no-highlight)
4187 (let ((mail-parse-charset (or gnus-article-charset
4188 ',gnus-newsgroup-charset))
4189 (message-options message-options)
4190 (message-options-set-recipient)
4191 (mail-parse-ignored-charsets
4192 (or gnus-article-ignored-charsets
4193 ',gnus-newsgroup-ignored-charsets)))
4194 (mml-to-mime)
4195 (mml-destroy-buffers)
4196 (remove-hook 'kill-buffer-hook
4197 'mml-destroy-buffers t)
4198 (kill-local-variable 'mml-buffer-list))
4199 (gnus-summary-edit-article-done
4200 ,(or (mail-header-references gnus-current-headers) "")
4201 ,(gnus-group-read-only-p)
4202 ,gnus-summary-buffer no-highlight)))))))
4203
4204(defun gnus-mime-delete-part ()
4205 "Delete the MIME part under point.
4206Replace it with some information about the removed part."
4207 (interactive)
4208 (gnus-article-check-buffer)
4209 (when (gnus-group-read-only-p)
4210 (error "The current group does not support deleting of parts"))
4211 (when (mm-complicated-handles gnus-article-mime-handles)
4212 (error "\
4213The current article has a complicated MIME structure, giving up..."))
4214 (when (gnus-yes-or-no-p "\
4215Deleting parts may malfunction or destroy the article; continue? ")
4216 (let* ((data (get-text-property (point) 'gnus-data))
4217 (handles gnus-article-mime-handles)
4218 (none "(none)")
4219 (description
4220 (or
4221 (mail-decode-encoded-word-string (or (mm-handle-description data)
4222 none))))
4223 (filename
4224 (or (mail-content-type-get (mm-handle-disposition data) 'filename)
4225 none))
4226 (type (mm-handle-media-type data)))
4227 (unless data
4228 (error "No MIME part under point"))
4229 (with-current-buffer (mm-handle-buffer data)
4230 (let ((bsize (format "%s" (buffer-size))))
4231 (erase-buffer)
4232 (insert
4233 (concat
4234 ",----\n"
4235 "| The following attachment has been deleted:\n"
4236 "|\n"
4237 "| Type: " type "\n"
4238 "| Filename: " filename "\n"
4239 "| Size (encoded): " bsize " Byte\n"
4240 "| Description: " description "\n"
4241 "`----\n"))
4242 (setcdr data
4243 (cdr (mm-make-handle
4244 nil `("text/plain") nil nil
4245 (list "attachment")
4246 (format "Deleted attachment (%s bytes)" bsize))))))
4247 (set-buffer gnus-summary-buffer)
4248 ;; FIXME: maybe some of the following code (borrowed from
4249 ;; `gnus-mime-save-part-and-strip') isn't necessary?
4250 (gnus-article-edit-article
4251 `(lambda ()
4252 (erase-buffer)
4253 (let ((mail-parse-charset (or gnus-article-charset
4254 ',gnus-newsgroup-charset))
4255 (mail-parse-ignored-charsets
4256 (or gnus-article-ignored-charsets
4257 ',gnus-newsgroup-ignored-charsets))
4258 (mbl mml-buffer-list))
4259 (setq mml-buffer-list nil)
d8a88581 4260 (insert-buffer-substring gnus-original-article-buffer)
23f87bed
MB
4261 (mime-to-mml ',handles)
4262 (setq gnus-article-mime-handles nil)
4263 (let ((mbl1 mml-buffer-list))
4264 (setq mml-buffer-list mbl)
4265 (set (make-local-variable 'mml-buffer-list) mbl1))
4266 (gnus-make-local-hook 'kill-buffer-hook)
4267 (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)))
4268 `(lambda (no-highlight)
4269 (let ((mail-parse-charset (or gnus-article-charset
4270 ',gnus-newsgroup-charset))
4271 (message-options message-options)
4272 (message-options-set-recipient)
4273 (mail-parse-ignored-charsets
4274 (or gnus-article-ignored-charsets
4275 ',gnus-newsgroup-ignored-charsets)))
4276 (mml-to-mime)
4277 (mml-destroy-buffers)
4278 (remove-hook 'kill-buffer-hook
4279 'mml-destroy-buffers t)
4280 (kill-local-variable 'mml-buffer-list))
4281 (gnus-summary-edit-article-done
4282 ,(or (mail-header-references gnus-current-headers) "")
4283 ,(gnus-group-read-only-p)
4284 ,gnus-summary-buffer no-highlight)))))
4285 ;; Not in `gnus-mime-save-part-and-strip':
4286 (gnus-article-edit-done)
4287 (gnus-summary-expand-window)
4288 (gnus-summary-show-article))
16409b0b
GM
4289
4290(defun gnus-mime-save-part ()
4291 "Save the MIME part under point."
4292 (interactive)
4293 (gnus-article-check-buffer)
4294 (let ((data (get-text-property (point) 'gnus-data)))
23f87bed
MB
4295 (when data
4296 (mm-save-part data))))
16409b0b
GM
4297
4298(defun gnus-mime-pipe-part ()
4299 "Pipe the MIME part under point to a process."
4300 (interactive)
4301 (gnus-article-check-buffer)
4302 (let ((data (get-text-property (point) 'gnus-data)))
23f87bed
MB
4303 (when data
4304 (mm-pipe-part data))))
16409b0b
GM
4305
4306(defun gnus-mime-view-part ()
4307 "Interactively choose a viewing method for the MIME part under point."
4308 (interactive)
4309 (gnus-article-check-buffer)
4310 (let ((data (get-text-property (point) 'gnus-data)))
23f87bed
MB
4311 (when data
4312 (setq gnus-article-mime-handles
4313 (mm-merge-handles
4314 gnus-article-mime-handles (setq data (copy-sequence data))))
4315 (mm-interactively-view-part data))))
16409b0b
GM
4316
4317(defun gnus-mime-view-part-as-type-internal ()
4318 (gnus-article-check-buffer)
4319 (let* ((name (mail-content-type-get
4320 (mm-handle-type (get-text-property (point) 'gnus-data))
4321 'name))
4322 (def-type (and name (mm-default-file-encoding name))))
4323 (and def-type (cons def-type 0))))
4324
23f87bed 4325(defun gnus-mime-view-part-as-type (&optional mime-type)
16409b0b 4326 "Choose a MIME media type, and view the part as such."
23f87bed
MB
4327 (interactive)
4328 (unless mime-type
4329 (setq mime-type (completing-read
4330 "View as MIME type: "
4331 (mapcar #'list (mailcap-mime-types))
4332 nil nil
4333 (gnus-mime-view-part-as-type-internal))))
16409b0b
GM
4334 (gnus-article-check-buffer)
4335 (let ((handle (get-text-property (point) 'gnus-data)))
23f87bed 4336 (when handle
531bedc3
MB
4337 (when (equal (mm-handle-media-type handle) "message/external-body")
4338 (unless (mm-handle-cache handle)
4339 (mm-extern-cache-contents handle))
4340 (setq handle (mm-handle-cache handle)))
23f87bed
MB
4341 (setq handle
4342 (mm-make-handle (mm-handle-buffer handle)
4343 (cons mime-type (cdr (mm-handle-type handle)))
4344 (mm-handle-encoding handle)
4345 (mm-handle-undisplayer handle)
4346 (mm-handle-disposition handle)
4347 (mm-handle-description handle)
4348 nil
4349 (mm-handle-id handle)))
4350 (setq gnus-article-mime-handles
4351 (mm-merge-handles gnus-article-mime-handles handle))
4352 (gnus-mm-display-part handle))))
4353
4354(eval-when-compile
4355 (require 'jka-compr))
4356
4357;; jka-compr.el uses a "sh -c" to direct stderr to err-file, but these days
4358;; emacs can do that itself.
4359;;
4360(defun gnus-mime-jka-compr-maybe-uncompress ()
4361 "Uncompress the current buffer if `auto-compression-mode' is enabled.
4362The uncompress method used is derived from `buffer-file-name'."
4363 (when (and (fboundp 'jka-compr-installed-p)
4364 (jka-compr-installed-p))
4365 (let ((info (jka-compr-get-compression-info buffer-file-name)))
4366 (when info
4367 (let ((basename (file-name-nondirectory buffer-file-name))
4368 (args (jka-compr-info-uncompress-args info))
4369 (prog (jka-compr-info-uncompress-program info))
4370 (message (jka-compr-info-uncompress-message info))
4371 (err-file (jka-compr-make-temp-name)))
4372 (if message
4373 (message "%s %s..." message basename))
4374 (unwind-protect
4375 (unless (memq (apply 'call-process-region
4376 (point-min) (point-max)
4377 prog
4378 t (list t err-file) nil
4379 args)
4380 jka-compr-acceptable-retval-list)
4381 (jka-compr-error prog args basename message err-file))
4382 (jka-compr-delete-temp-file err-file)))))))
16409b0b
GM
4383
4384(defun gnus-mime-copy-part (&optional handle)
23f87bed
MB
4385 "Put the MIME part under point into a new buffer.
4386If `auto-compression-mode' is enabled, compressed files like .gz and .bz2
4387are decompressed."
16409b0b
GM
4388 (interactive)
4389 (gnus-article-check-buffer)
4390 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
23f87bed
MB
4391 (contents (and handle (mm-get-part handle)))
4392 (base (and handle
4393 (file-name-nondirectory
4394 (or
4395 (mail-content-type-get (mm-handle-type handle) 'name)
4396 (mail-content-type-get (mm-handle-disposition handle)
4397 'filename)
4398 "*decoded*"))))
4399 (buffer (and base (generate-new-buffer base))))
4400 (when contents
4401 (switch-to-buffer buffer)
4402 (insert contents)
4403 ;; We do it this way to make `normal-mode' set the appropriate mode.
4404 (unwind-protect
4405 (progn
4406 (setq buffer-file-name (expand-file-name base))
4407 (gnus-mime-jka-compr-maybe-uncompress)
4408 (normal-mode))
4409 (setq buffer-file-name nil))
4410 (goto-char (point-min)))))
4411
4412(defun gnus-mime-print-part (&optional handle filename)
4413 "Print the MIME part under point."
4414 (interactive (list nil (ps-print-preprint current-prefix-arg)))
4415 (gnus-article-check-buffer)
4416 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4417 (contents (and handle (mm-get-part handle)))
4418 (file (mm-make-temp-file (expand-file-name "mm." mm-tmp-directory)))
4419 (printer (mailcap-mime-info (mm-handle-media-type handle) "print")))
4420 (when contents
4421 (if printer
4422 (unwind-protect
4423 (progn
4424 (mm-save-part-to-file handle file)
4425 (call-process shell-file-name nil
4426 (generate-new-buffer " *mm*")
4427 nil
4428 shell-command-switch
4429 (mm-mailcap-command
4430 printer file (mm-handle-type handle))))
4431 (delete-file file))
4432 (with-temp-buffer
4433 (insert contents)
4434 (gnus-print-buffer))
4435 (ps-despool filename)))))
16409b0b 4436
e0bad764 4437(defun gnus-mime-inline-part (&optional handle arg)
16409b0b 4438 "Insert the MIME part under point into the current buffer."
e0bad764 4439 (interactive (list nil current-prefix-arg))
16409b0b
GM
4440 (gnus-article-check-buffer)
4441 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
e0bad764 4442 contents charset
16409b0b 4443 (b (point))
23f87bed
MB
4444 (inhibit-read-only t))
4445 (when handle
4446 (if (and (not arg) (mm-handle-undisplayer handle))
4447 (mm-remove-part handle)
4448 (setq contents (mm-get-part handle))
4449 (cond
4450 ((not arg)
4451 (setq charset (or (mail-content-type-get
4452 (mm-handle-type handle) 'charset)
4453 gnus-newsgroup-charset)))
4454 ((numberp arg)
4455 (if (mm-handle-undisplayer handle)
4456 (mm-remove-part handle))
4457 (setq charset
4458 (or (cdr (assq arg
4459 gnus-summary-show-article-charset-alist))
56c30d72
MB
4460 (mm-read-coding-system "Charset: "))))
4461 (t
4462 (if (mm-handle-undisplayer handle)
850c333d 4463 (mm-remove-part handle))))
23f87bed 4464 (forward-line 2)
850c333d
MB
4465 (mm-insert-inline
4466 handle
4467 (if (and charset
4468 (setq charset (mm-charset-to-coding-system
4469 charset))
4470 (not (eq charset 'ascii)))
4471 (mm-decode-coding-string contents charset)
4472 (mm-string-to-multibyte contents)))
23f87bed
MB
4473 (goto-char b)))))
4474
4475(defun gnus-mime-view-part-as-charset (&optional handle arg)
4476 "Insert the MIME part under point into the current buffer using the
4477specified charset."
4478 (interactive (list nil current-prefix-arg))
4479 (gnus-article-check-buffer)
4480 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4481 contents charset
4482 (b (point))
4483 (inhibit-read-only t))
4484 (when handle
4485 (if (mm-handle-undisplayer handle)
4486 (mm-remove-part handle))
4487 (let ((gnus-newsgroup-charset
4488 (or (cdr (assq arg
4489 gnus-summary-show-article-charset-alist))
4490 (mm-read-coding-system "Charset: ")))
4491 (gnus-newsgroup-ignored-charsets 'gnus-all))
4492 (gnus-article-press-button)))))
4493
4494(defun gnus-mime-view-part-externally (&optional handle)
16409b0b
GM
4495 "View the MIME part under point with an external viewer."
4496 (interactive)
4497 (gnus-article-check-buffer)
4498 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4499 (mm-user-display-methods nil)
4500 (mm-inlined-types nil)
4501 (mail-parse-charset gnus-newsgroup-charset)
a1506d29 4502 (mail-parse-ignored-charsets
16409b0b
GM
4503 (save-excursion (set-buffer gnus-summary-buffer)
4504 gnus-newsgroup-ignored-charsets)))
23f87bed
MB
4505 (when handle
4506 (if (mm-handle-undisplayer handle)
4507 (mm-remove-part handle)
4508 (mm-display-part handle)))))
16409b0b 4509
23f87bed 4510(defun gnus-mime-view-part-internally (&optional handle)
16409b0b 4511 "View the MIME part under point with an internal viewer.
23f87bed 4512If no internal viewer is available, use an external viewer."
16409b0b
GM
4513 (interactive)
4514 (gnus-article-check-buffer)
4515 (let* ((handle (or handle (get-text-property (point) 'gnus-data)))
4516 (mm-inlined-types '(".*"))
4517 (mm-inline-large-images t)
4518 (mail-parse-charset gnus-newsgroup-charset)
a1506d29 4519 (mail-parse-ignored-charsets
16409b0b 4520 (save-excursion (set-buffer gnus-summary-buffer)
23f87bed
MB
4521 gnus-newsgroup-ignored-charsets))
4522 (inhibit-read-only t))
4523 (when handle
4524 (if (mm-handle-undisplayer handle)
4525 (mm-remove-part handle)
4526 (mm-display-part handle)))))
16409b0b 4527
e0bad764
DL
4528(defun gnus-mime-action-on-part (&optional action)
4529 "Do something with the MIME attachment at \(point\)."
4530 (interactive
23f87bed 4531 (list (completing-read "Action: " gnus-mime-action-alist nil t)))
e0bad764
DL
4532 (gnus-article-check-buffer)
4533 (let ((action-pair (assoc action gnus-mime-action-alist)))
4534 (if action-pair
4535 (funcall (cdr action-pair)))))
4536
16409b0b
GM
4537(defun gnus-article-part-wrapper (n function)
4538 (save-current-buffer
4539 (set-buffer gnus-article-buffer)
4540 (when (> n (length gnus-article-mime-handle-alist))
4541 (error "No such part"))
4542 (gnus-article-goto-part n)
4543 (let ((handle (cdr (assq n gnus-article-mime-handle-alist))))
4544 (funcall function handle))))
4545
4546(defun gnus-article-pipe-part (n)
4547 "Pipe MIME part N, which is the numerical prefix."
4548 (interactive "p")
4549 (gnus-article-part-wrapper n 'mm-pipe-part))
4550
4551(defun gnus-article-save-part (n)
4552 "Save MIME part N, which is the numerical prefix."
4553 (interactive "p")
4554 (gnus-article-part-wrapper n 'mm-save-part))
4555
4556(defun gnus-article-interactively-view-part (n)
4557 "View MIME part N interactively, which is the numerical prefix."
4558 (interactive "p")
4559 (gnus-article-part-wrapper n 'mm-interactively-view-part))
4560
4561(defun gnus-article-copy-part (n)
4562 "Copy MIME part N, which is the numerical prefix."
4563 (interactive "p")
4564 (gnus-article-part-wrapper n 'gnus-mime-copy-part))
4565
23f87bed
MB
4566(defun gnus-article-view-part-as-charset (n)
4567 "View MIME part N using a specified charset.
4568N is the numerical prefix."
4569 (interactive "p")
4570 (gnus-article-part-wrapper n 'gnus-mime-view-part-as-charset))
4571
4572(defun gnus-article-view-part-externally (n)
16409b0b
GM
4573 "View MIME part N externally, which is the numerical prefix."
4574 (interactive "p")
23f87bed 4575 (gnus-article-part-wrapper n 'gnus-mime-view-part-externally))
16409b0b
GM
4576
4577(defun gnus-article-inline-part (n)
4578 "Inline MIME part N, which is the numerical prefix."
4579 (interactive "p")
4580 (gnus-article-part-wrapper n 'gnus-mime-inline-part))
4581
4582(defun gnus-article-mime-match-handle-first (condition)
4583 (if condition
4584 (let ((alist gnus-article-mime-handle-alist) ihandle n)
4585 (while (setq ihandle (pop alist))
a1506d29 4586 (if (and (cond
16409b0b
GM
4587 ((functionp condition)
4588 (funcall condition (cdr ihandle)))
a1506d29 4589 ((eq condition 'undisplayed)
16409b0b
GM
4590 (not (or (mm-handle-undisplayer (cdr ihandle))
4591 (equal (mm-handle-media-type (cdr ihandle))
4592 "multipart/alternative"))))
4593 ((eq condition 'undisplayed-alternative)
4594 (not (mm-handle-undisplayer (cdr ihandle))))
4595 (t t))
4596 (gnus-article-goto-part (car ihandle))
4597 (or (not n) (< (car ihandle) n)))
4598 (setq n (car ihandle))))
4599 (or n 1))
4600 1))
4601
4602(defun gnus-article-view-part (&optional n)
4603 "View MIME part N, which is the numerical prefix."
4604 (interactive "P")
4605 (save-current-buffer
4606 (set-buffer gnus-article-buffer)
a1506d29 4607 (or (numberp n) (setq n (gnus-article-mime-match-handle-first
16409b0b
GM
4608 gnus-article-mime-match-handle-function)))
4609 (when (> n (length gnus-article-mime-handle-alist))
4610 (error "No such part"))
4611 (let ((handle (cdr (assq n gnus-article-mime-handle-alist))))
4612 (when (gnus-article-goto-part n)
4613 (if (equal (car handle) "multipart/alternative")
4614 (gnus-article-press-button)
4615 (when (eq (gnus-mm-display-part handle) 'internal)
4616 (gnus-set-window-start)))))))
4617
e0bad764
DL
4618(defsubst gnus-article-mime-total-parts ()
4619 (if (bufferp (car gnus-article-mime-handles))
4620 1 ;; single part
4621 (1- (length gnus-article-mime-handles))))
4622
16409b0b
GM
4623(defun gnus-mm-display-part (handle)
4624 "Display HANDLE and fix MIME button."
4625 (let ((id (get-text-property (point) 'gnus-part))
4626 (point (point))
23f87bed 4627 (inhibit-read-only t))
16409b0b
GM
4628 (forward-line 1)
4629 (prog1
4630 (let ((window (selected-window))
4631 (mail-parse-charset gnus-newsgroup-charset)
a1506d29 4632 (mail-parse-ignored-charsets
23f87bed
MB
4633 (if (gnus-buffer-live-p gnus-summary-buffer)
4634 (save-excursion
4635 (set-buffer gnus-summary-buffer)
4636 gnus-newsgroup-ignored-charsets)
4637 nil)))
16409b0b
GM
4638 (save-excursion
4639 (unwind-protect
23f87bed 4640 (let ((win (gnus-get-buffer-window (current-buffer) t))
16409b0b
GM
4641 (beg (point)))
4642 (when win
4643 (select-window win))
4644 (goto-char point)
4645 (forward-line)
4646 (if (mm-handle-displayed-p handle)
4647 ;; This will remove the part.
4648 (mm-display-part handle)
4649 (save-restriction
23f87bed
MB
4650 (narrow-to-region (point)
4651 (if (eobp) (point) (1+ (point))))
16409b0b
GM
4652 (mm-display-part handle)
4653 ;; We narrow to the part itself and
4654 ;; then call the treatment functions.
4655 (goto-char (point-min))
4656 (forward-line 1)
4657 (narrow-to-region (point) (point-max))
4658 (gnus-treat-article
4659 nil id
e0bad764 4660 (gnus-article-mime-total-parts)
16409b0b 4661 (mm-handle-media-type handle)))))
23f87bed
MB
4662 (if (window-live-p window)
4663 (select-window window)))))
16409b0b 4664 (goto-char point)
23f87bed 4665 (gnus-delete-line)
16409b0b
GM
4666 (gnus-insert-mime-button
4667 handle id (list (mm-handle-displayed-p handle)))
4668 (goto-char point))))
4669
4670(defun gnus-article-goto-part (n)
4671 "Go to MIME part N."
23f87bed 4672 (gnus-goto-char (text-property-any (point-min) (point-max) 'gnus-part n)))
16409b0b
GM
4673
4674(defun gnus-insert-mime-button (handle gnus-tmp-id &optional displayed)
4675 (let ((gnus-tmp-name
23f87bed
MB
4676 (or (mail-content-type-get (mm-handle-type handle) 'name)
4677 (mail-content-type-get (mm-handle-disposition handle) 'filename)
4678 (mail-content-type-get (mm-handle-type handle) 'url)
16409b0b
GM
4679 ""))
4680 (gnus-tmp-type (mm-handle-media-type handle))
4681 (gnus-tmp-description
4682 (mail-decode-encoded-word-string (or (mm-handle-description handle)
4683 "")))
4684 (gnus-tmp-dots
4685 (if (if displayed (car displayed)
4686 (mm-handle-displayed-p handle))
4687 "" "..."))
4688 (gnus-tmp-length (with-current-buffer (mm-handle-buffer handle)
4689 (buffer-size)))
4690 gnus-tmp-type-long b e)
4691 (when (string-match ".*/" gnus-tmp-name)
4692 (setq gnus-tmp-name (replace-match "" t t gnus-tmp-name)))
4693 (setq gnus-tmp-type-long (concat gnus-tmp-type
4694 (and (not (equal gnus-tmp-name ""))
4695 (concat "; " gnus-tmp-name))))
23f87bed
MB
4696 (unless (equal gnus-tmp-description "")
4697 (setq gnus-tmp-type-long (concat " --- " gnus-tmp-type-long)))
16409b0b
GM
4698 (unless (bolp)
4699 (insert "\n"))
4700 (setq b (point))
4701 (gnus-eval-format
4702 gnus-mime-button-line-format gnus-mime-button-line-format-alist
23f87bed
MB
4703 `(,@(gnus-local-map-property gnus-mime-button-map)
4704 gnus-callback gnus-mm-display-part
4705 gnus-part ,gnus-tmp-id
4706 article-type annotation
4707 gnus-data ,handle))
4708 (setq e (if (bolp)
4709 ;; Exclude a newline.
4710 (1- (point))
4711 (point)))
16409b0b
GM
4712 (widget-convert-button
4713 'link b e
4714 :mime-handle handle
4715 :action 'gnus-widget-press-button
4716 :button-keymap gnus-mime-button-map
4717 :help-echo
4718 (lambda (widget/window &optional overlay pos)
4719 ;; Needed to properly clear the message due to a bug in
4720 ;; wid-edit (XEmacs only).
4721 (if (boundp 'help-echo-owns-message)
4722 (setq help-echo-owns-message t))
4723 (format
4724 "%S: %s the MIME part; %S: more options"
4725 (aref gnus-mouse-2 0)
4726 ;; XEmacs will get a single widget arg; Emacs 21 will get
4727 ;; window, overlay, position.
4728 (if (mm-handle-displayed-p
4729 (if overlay
e0bad764
DL
4730 (with-current-buffer (gnus-overlay-buffer overlay)
4731 (widget-get (widget-at (gnus-overlay-start overlay))
16409b0b
GM
4732 :mime-handle))
4733 (widget-get widget/window :mime-handle)))
4734 "hide" "show")
4735 (aref gnus-down-mouse-3 0))))))
4736
4737(defun gnus-widget-press-button (elems el)
4738 (goto-char (widget-get elems :from))
4739 (gnus-article-press-button))
4740
4741(defvar gnus-displaying-mime nil)
4742
4743(defun gnus-display-mime (&optional ihandles)
4744 "Display the MIME parts."
4745 (save-excursion
4746 (save-selected-window
4747 (let ((window (get-buffer-window gnus-article-buffer))
4748 (point (point)))
4749 (when window
4750 (select-window window)
4751 ;; We have to do this since selecting the window
4752 ;; may change the point. So we set the window point.
4753 (set-window-point window point)))
73043f7d
MB
4754 (let ((handles ihandles)
4755 (inhibit-read-only t)
4756 handle)
4757 (cond (handles)
4758 ((setq handles (mm-dissect-buffer nil gnus-article-loose-mime))
4759 (when gnus-article-emulate-mime
4760 (mm-uu-dissect-text-parts handles)))
4761 (gnus-article-emulate-mime
4762 (setq handles (mm-uu-dissect))))
16409b0b
GM
4763 (when (and (not ihandles)
4764 (not gnus-displaying-mime))
4765 ;; Top-level call; we clean up.
4766 (when gnus-article-mime-handles
4767 (mm-destroy-parts gnus-article-mime-handles)
4768 (setq gnus-article-mime-handle-alist nil));; A trick.
4769 (setq gnus-article-mime-handles handles)
4770 ;; We allow users to glean info from the handles.
4771 (when gnus-article-mime-part-function
4772 (gnus-mime-part-function handles)))
4773 (if (and handles
4774 (or (not (stringp (car handles)))
4775 (cdr handles)))
4776 (progn
4777 (when (and (not ihandles)
4778 (not gnus-displaying-mime))
4779 ;; Clean up for mime parts.
4780 (article-goto-body)
4781 (delete-region (point) (point-max)))
4782 (let ((gnus-displaying-mime t))
4783 (gnus-mime-display-part handles)))
4784 (save-restriction
4785 (article-goto-body)
4786 (narrow-to-region (point) (point-max))
4787 (gnus-treat-article nil 1 1)
4788 (widen)))
4789 (unless ihandles
4790 ;; Highlight the headers.
4791 (save-excursion
4792 (save-restriction
4793 (article-goto-body)
4794 (narrow-to-region (point-min) (point))
31640842
MB
4795 (gnus-article-save-original-date
4796 (gnus-treat-article 'head)))))))))
16409b0b 4797
23f87bed
MB
4798(defcustom gnus-mime-display-multipart-as-mixed nil
4799 "Display \"multipart\" parts as \"multipart/mixed\".
4800
4801If t, it overrides nil values of
4802`gnus-mime-display-multipart-alternative-as-mixed' and
4803`gnus-mime-display-multipart-related-as-mixed'."
4804 :group 'gnus-article-mime
4805 :type 'boolean)
4806
4807(defcustom gnus-mime-display-multipart-alternative-as-mixed nil
4808 "Display \"multipart/alternative\" parts as \"multipart/mixed\"."
bf247b6e 4809 :version "22.1"
23f87bed
MB
4810 :group 'gnus-article-mime
4811 :type 'boolean)
4812
4813(defcustom gnus-mime-display-multipart-related-as-mixed nil
4814 "Display \"multipart/related\" parts as \"multipart/mixed\".
4815
4816If displaying \"text/html\" is discouraged \(see
4817`mm-discouraged-alternatives'\) images or other material inside a
4818\"multipart/related\" part might be overlooked when this variable is nil."
bf247b6e 4819 :version "22.1"
23f87bed
MB
4820 :group 'gnus-article-mime
4821 :type 'boolean)
16409b0b
GM
4822
4823(defun gnus-mime-display-part (handle)
4824 (cond
430d3ed7
MB
4825 ;; Maybe a broken MIME message.
4826 ((null handle))
16409b0b
GM
4827 ;; Single part.
4828 ((not (stringp (car handle)))
4829 (gnus-mime-display-single handle))
4830 ;; User-defined multipart
4831 ((cdr (assoc (car handle) gnus-mime-multipart-functions))
4832 (funcall (cdr (assoc (car handle) gnus-mime-multipart-functions))
4833 handle))
4834 ;; multipart/alternative
4835 ((and (equal (car handle) "multipart/alternative")
23f87bed
MB
4836 (not (or gnus-mime-display-multipart-as-mixed
4837 gnus-mime-display-multipart-alternative-as-mixed)))
16409b0b
GM
4838 (let ((id (1+ (length gnus-article-mime-handle-alist))))
4839 (push (cons id handle) gnus-article-mime-handle-alist)
4840 (gnus-mime-display-alternative (cdr handle) nil nil id)))
4841 ;; multipart/related
4842 ((and (equal (car handle) "multipart/related")
23f87bed
MB
4843 (not (or gnus-mime-display-multipart-as-mixed
4844 gnus-mime-display-multipart-related-as-mixed)))
16409b0b
GM
4845 ;;;!!!We should find the start part, but we just default
4846 ;;;!!!to the first part.
23f87bed
MB
4847 ;;(gnus-mime-display-part (cadr handle))
4848 ;;;!!! Most multipart/related is an HTML message plus images.
4849 ;;;!!! Unfortunately we are unable to let W3 display those
4850 ;;;!!! included images, so we just display it as a mixed multipart.
4851 ;;(gnus-mime-display-mixed (cdr handle))
4852 ;;;!!! No, w3 can display everything just fine.
16409b0b 4853 (gnus-mime-display-part (cadr handle)))
23f87bed
MB
4854 ((equal (car handle) "multipart/signed")
4855 (gnus-add-wash-type 'signed)
4856 (gnus-mime-display-security handle))
4857 ((equal (car handle) "multipart/encrypted")
4858 (gnus-add-wash-type 'encrypted)
4859 (gnus-mime-display-security handle))
16409b0b
GM
4860 ;; Other multiparts are handled like multipart/mixed.
4861 (t
4862 (gnus-mime-display-mixed (cdr handle)))))
4863
4864(defun gnus-mime-part-function (handles)
4865 (if (stringp (car handles))
4866 (mapcar 'gnus-mime-part-function (cdr handles))
4867 (funcall gnus-article-mime-part-function handles)))
4868
4869(defun gnus-mime-display-mixed (handles)
4870 (mapcar 'gnus-mime-display-part handles))
4871
4872(defun gnus-mime-display-single (handle)
4873 (let ((type (mm-handle-media-type handle))
4874 (ignored gnus-ignored-mime-types)
4875 (not-attachment t)
4876 (move nil)
4877 display text)
4878 (catch 'ignored
4879 (progn
4880 (while ignored
4881 (when (string-match (pop ignored) type)
4882 (throw 'ignored nil)))
4883 (if (and (setq not-attachment
4884 (and (not (mm-inline-override-p handle))
4885 (or (not (mm-handle-disposition handle))
4886 (equal (car (mm-handle-disposition handle))
4887 "inline")
4888 (mm-attachment-override-p handle))))
4889 (mm-automatic-display-p handle)
23f87bed
MB
4890 (or (and
4891 (mm-inlinable-p handle)
4892 (mm-inlined-p handle))
16409b0b
GM
4893 (mm-automatic-external-display-p type)))
4894 (setq display t)
4895 (when (equal (mm-handle-media-supertype handle) "text")
4896 (setq text t)))
e0bad764
DL
4897 (let ((id (1+ (length gnus-article-mime-handle-alist)))
4898 beg)
16409b0b 4899 (push (cons id handle) gnus-article-mime-handle-alist)
531bedc3
MB
4900 (when (and display
4901 (equal (mm-handle-media-supertype handle) "message"))
4902 (insert-char
4903 ?\n
4904 (cond ((not (bolp)) 2)
4905 ((or (bobp) (eq (char-before (1- (point))) ?\n)) 0)
4906 (t 1))))
16409b0b
GM
4907 (when (or (not display)
4908 (not (gnus-unbuttonized-mime-type-p type)))
16409b0b
GM
4909 (gnus-insert-mime-button
4910 handle id (list (or display (and not-attachment text))))
4911 (gnus-article-insert-newline)
23f87bed 4912 ;; Remember modify the number of forward lines.
e0bad764
DL
4913 (setq move t))
4914 (setq beg (point))
16409b0b
GM
4915 (cond
4916 (display
4917 (when move
23f87bed 4918 (forward-line -1)
16409b0b
GM
4919 (setq beg (point)))
4920 (let ((mail-parse-charset gnus-newsgroup-charset)
a1506d29 4921 (mail-parse-ignored-charsets
16409b0b
GM
4922 (save-excursion (condition-case ()
4923 (set-buffer gnus-summary-buffer)
4924 (error))
4925 gnus-newsgroup-ignored-charsets)))
4926 (mm-display-part handle t))
4927 (goto-char (point-max)))
4928 ((and text not-attachment)
4929 (when move
23f87bed 4930 (forward-line -1)
16409b0b
GM
4931 (setq beg (point)))
4932 (gnus-article-insert-newline)
53cfefc8
MB
4933 (mm-insert-inline
4934 handle
4935 (let ((charset (mail-content-type-get (mm-handle-type handle)
4936 'charset)))
4937 (cond ((not charset)
4938 (mm-string-as-multibyte (mm-get-part handle)))
4939 ((eq charset 'gnus-decoded)
4940 (with-current-buffer (mm-handle-buffer handle)
4941 (buffer-string)))
4942 (t
4943 (mm-decode-string (mm-get-part handle) charset)))))
16409b0b
GM
4944 (goto-char (point-max))))
4945 ;; Do highlighting.
4946 (save-excursion
4947 (save-restriction
4948 (narrow-to-region beg (point))
4949 (gnus-treat-article
a1506d29 4950 nil id
e0bad764 4951 (gnus-article-mime-total-parts)
16409b0b
GM
4952 (mm-handle-media-type handle)))))))))
4953
4954(defun gnus-unbuttonized-mime-type-p (type)
4955 "Say whether TYPE is to be unbuttonized."
4956 (unless gnus-inhibit-mime-unbuttonizing
23f87bed
MB
4957 (when (catch 'found
4958 (let ((types gnus-unbuttonized-mime-types))
4959 (while types
4960 (when (string-match (pop types) type)
4961 (throw 'found t)))))
4962 (not (catch 'found
4963 (let ((types gnus-buttonized-mime-types))
4964 (while types
4965 (when (string-match (pop types) type)
4966 (throw 'found t)))))))))
16409b0b
GM
4967
4968(defun gnus-article-insert-newline ()
4969 "Insert a newline, but mark it as undeletable."
4970 (gnus-put-text-property
4971 (point) (progn (insert "\n") (point)) 'gnus-undeletable t))
4972
4973(defun gnus-mime-display-alternative (handles &optional preferred ibegend id)
4974 (let* ((preferred (or preferred (mm-preferred-alternative handles)))
4975 (ihandles handles)
4976 (point (point))
23f87bed 4977 handle (inhibit-read-only t) from props begend not-pref)
16409b0b
GM
4978 (save-window-excursion
4979 (save-restriction
4980 (when ibegend
4981 (narrow-to-region (car ibegend)
4982 (or (cdr ibegend)
4983 (progn
4984 (goto-char (car ibegend))
4985 (forward-line 2)
4986 (point))))
4987 (delete-region (point-min) (point-max))
4988 (mm-remove-parts handles))
4989 (setq begend (list (point-marker)))
4990 ;; Do the toggle.
4991 (unless (setq not-pref (cadr (member preferred ihandles)))
4992 (setq not-pref (car ihandles)))
4993 (when (or ibegend
23f87bed 4994 (not preferred)
16409b0b
GM
4995 (not (gnus-unbuttonized-mime-type-p
4996 "multipart/alternative")))
4997 (gnus-add-text-properties
4998 (setq from (point))
4999 (progn
5000 (insert (format "%d. " id))
5001 (point))
5002 `(gnus-callback
5003 (lambda (handles)
5004 (unless ,(not ibegend)
5005 (setq gnus-article-mime-handle-alist
5006 ',gnus-article-mime-handle-alist))
5007 (gnus-mime-display-alternative
5008 ',ihandles ',not-pref ',begend ,id))
23f87bed 5009 ,@(gnus-local-map-property gnus-mime-button-map)
16409b0b
GM
5010 ,gnus-mouse-face-prop ,gnus-article-mouse-face
5011 face ,gnus-article-button-face
16409b0b 5012 gnus-part ,id
7dafe00b 5013 article-type multipart))
16409b0b
GM
5014 (widget-convert-button 'link from (point)
5015 :action 'gnus-widget-press-button
5016 :button-keymap gnus-widget-button-keymap)
5017 ;; Do the handles
5018 (while (setq handle (pop handles))
5019 (gnus-add-text-properties
5020 (setq from (point))
5021 (progn
5022 (insert (format "(%c) %-18s"
5023 (if (equal handle preferred) ?* ? )
5024 (mm-handle-media-type handle)))
5025 (point))
5026 `(gnus-callback
5027 (lambda (handles)
5028 (unless ,(not ibegend)
5029 (setq gnus-article-mime-handle-alist
5030 ',gnus-article-mime-handle-alist))
5031 (gnus-mime-display-alternative
5032 ',ihandles ',handle ',begend ,id))
23f87bed 5033 ,@(gnus-local-map-property gnus-mime-button-map)
16409b0b
GM
5034 ,gnus-mouse-face-prop ,gnus-article-mouse-face
5035 face ,gnus-article-button-face
16409b0b
GM
5036 gnus-part ,id
5037 gnus-data ,handle))
5038 (widget-convert-button 'link from (point)
5039 :action 'gnus-widget-press-button
5040 :button-keymap gnus-widget-button-keymap)
5041 (insert " "))
5042 (insert "\n\n"))
5043 (when preferred
5044 (if (stringp (car preferred))
5045 (gnus-display-mime preferred)
5046 (let ((mail-parse-charset gnus-newsgroup-charset)
a1506d29 5047 (mail-parse-ignored-charsets
16409b0b
GM
5048 (save-excursion (set-buffer gnus-summary-buffer)
5049 gnus-newsgroup-ignored-charsets)))
5050 (mm-display-part preferred)
5051 ;; Do highlighting.
5052 (save-excursion
5053 (save-restriction
5054 (narrow-to-region (car begend) (point-max))
5055 (gnus-treat-article
5056 nil (length gnus-article-mime-handle-alist)
e0bad764 5057 (gnus-article-mime-total-parts)
16409b0b
GM
5058 (mm-handle-media-type handle))))))
5059 (goto-char (point-max))
5060 (setcdr begend (point-marker)))))
5061 (when ibegend
5062 (goto-char point))))
5063
23f87bed
MB
5064(defconst gnus-article-wash-status-strings
5065 (let ((alist '((cite "c" "Possible hidden citation text"
5066 " " "All citation text visible")
5067 (headers "h" "Hidden headers"
5068 " " "All headers visible.")
5069 (pgp "p" "Encrypted or signed message status hidden"
5070 " " "No hidden encryption nor digital signature status")
5071 (signature "s" "Signature has been hidden"
5072 " " "Signature is visible")
5073 (overstrike "o" "Overstrike (^H) characters applied"
5074 " " "No overstrike characters applied")
5075 (emphasis "e" "/*_Emphasis_*/ characters applied"
5076 " " "No /*_emphasis_*/ characters applied")))
5077 result)
5078 (dolist (entry alist result)
5079 (let ((key (nth 0 entry))
5080 (on (copy-sequence (nth 1 entry)))
5081 (on-help (nth 2 entry))
5082 (off (copy-sequence (nth 3 entry)))
5083 (off-help (nth 4 entry)))
5084 (put-text-property 0 1 'help-echo on-help on)
5085 (put-text-property 0 1 'help-echo off-help off)
5086 (push (list key on off) result))))
5087 "Alist of strings describing wash status in the mode line.
5088Each entry has the form (KEY ON OF), where the KEY is a symbol
5089representing the particular washing function, ON is the string to use
5090in the article mode line when the washing function is active, and OFF
5091is the string to use when it is inactive.")
5092
5093(defun gnus-article-wash-status-entry (key value)
5094 (let ((entry (assoc key gnus-article-wash-status-strings)))
5095 (if value (nth 1 entry) (nth 2 entry))))
5096
eec82323
LMI
5097(defun gnus-article-wash-status ()
5098 "Return a string which display status of article washing."
5099 (save-excursion
5100 (set-buffer gnus-article-buffer)
16409b0b
GM
5101 (let ((cite (memq 'cite gnus-article-wash-types))
5102 (headers (memq 'headers gnus-article-wash-types))
5103 (boring (memq 'boring-headers gnus-article-wash-types))
5104 (pgp (memq 'pgp gnus-article-wash-types))
5105 (pem (memq 'pem gnus-article-wash-types))
23f87bed
MB
5106 (signed (memq 'signed gnus-article-wash-types))
5107 (encrypted (memq 'encrypted gnus-article-wash-types))
16409b0b
GM
5108 (signature (memq 'signature gnus-article-wash-types))
5109 (overstrike (memq 'overstrike gnus-article-wash-types))
5110 (emphasis (memq 'emphasis gnus-article-wash-types)))
23f87bed
MB
5111 (concat
5112 (gnus-article-wash-status-entry 'cite cite)
5113 (gnus-article-wash-status-entry 'headers (or headers boring))
5114 (gnus-article-wash-status-entry 'pgp (or pgp pem signed encrypted))
5115 (gnus-article-wash-status-entry 'signature signature)
5116 (gnus-article-wash-status-entry 'overstrike overstrike)
5117 (gnus-article-wash-status-entry 'emphasis emphasis)))))
5118
5119(defun gnus-add-wash-type (type)
5120 "Add a washing of TYPE to the current status."
5121 (add-to-list 'gnus-article-wash-types type))
5122
5123(defun gnus-delete-wash-type (type)
5124 "Add a washing of TYPE to the current status."
5125 (setq gnus-article-wash-types (delq type gnus-article-wash-types)))
5126
5127(defun gnus-add-image (category image)
5128 "Add IMAGE of CATEGORY to the list of displayed images."
5129 (let ((entry (assq category gnus-article-image-alist)))
5130 (unless entry
5131 (setq entry (list category))
5132 (push entry gnus-article-image-alist))
5133 (nconc entry (list image))))
5134
5135(defun gnus-delete-images (category)
5136 "Delete all images in CATEGORY."
5137 (let ((entry (assq category gnus-article-image-alist)))
5138 (dolist (image (cdr entry))
5139 (gnus-remove-image image category))
5140 (setq gnus-article-image-alist (delq entry gnus-article-image-alist))
5141 (gnus-delete-wash-type category)))
eec82323 5142
16409b0b 5143(defalias 'gnus-article-hide-headers-if-wanted 'gnus-article-maybe-hide-headers)
6748645f
LMI
5144
5145(defun gnus-article-maybe-hide-headers ()
eec82323
LMI
5146 "Hide unwanted headers if `gnus-have-all-headers' is nil.
5147Provided for backwards compatibility."
16409b0b
GM
5148 (when (and (or (not (gnus-buffer-live-p gnus-summary-buffer))
5149 (not (save-excursion (set-buffer gnus-summary-buffer)
5150 gnus-have-all-headers)))
5151 (not gnus-inhibit-hiding))
5152 (gnus-article-hide-headers)))
eec82323
LMI
5153
5154;;; Article savers.
5155
5156(defun gnus-output-to-file (file-name)
5157 "Append the current article to a file named FILE-NAME."
5158 (let ((artbuf (current-buffer)))
16409b0b 5159 (with-temp-buffer
eec82323
LMI
5160 (insert-buffer-substring artbuf)
5161 ;; Append newline at end of the buffer as separator, and then
5162 ;; save it to file.
5163 (goto-char (point-max))
5164 (insert "\n")
47e77e9f
SZ
5165 (let ((file-name-coding-system nnmail-pathname-coding-system))
5166 (mm-append-to-file (point-min) (point-max) file-name))
a8151ef7 5167 t)))
eec82323
LMI
5168
5169(defun gnus-narrow-to-page (&optional arg)
5170 "Narrow the article buffer to a page.
5171If given a numerical ARG, move forward ARG pages."
5172 (interactive "P")
5173 (setq arg (if arg (prefix-numeric-value arg) 0))
5174 (save-excursion
5175 (set-buffer gnus-article-buffer)
5176 (goto-char (point-min))
5177 (widen)
5178 ;; Remove any old next/prev buttons.
5179 (when (gnus-visual-p 'page-marker)
4e7d0221 5180 (let ((inhibit-read-only t))
eec82323
LMI
5181 (gnus-remove-text-with-property 'gnus-prev)
5182 (gnus-remove-text-with-property 'gnus-next)))
23f87bed 5183 (if
eec82323
LMI
5184 (cond ((< arg 0)
5185 (re-search-backward page-delimiter nil 'move (1+ (abs arg))))
5186 ((> arg 0)
5187 (re-search-forward page-delimiter nil 'move arg)))
23f87bed 5188 (goto-char (match-end 0))
eec82323
LMI
5189 (save-excursion
5190 (goto-char (point-min))
23f87bed
MB
5191 (setq gnus-page-broken
5192 (and (re-search-forward page-delimiter nil t) t))))
5193 (when gnus-page-broken
5194 (narrow-to-region
5195 (point)
5196 (if (re-search-forward page-delimiter nil 'move)
5197 (match-beginning 0)
5198 (point)))
5199 (when (and (gnus-visual-p 'page-marker)
5200 (> (point-min) (save-restriction (widen) (point-min))))
5201 (save-excursion
5202 (goto-char (point-min))
5203 (gnus-insert-prev-page-button)))
5204 (when (and (gnus-visual-p 'page-marker)
45893b95 5205 (< (point-max) (save-restriction (widen) (point-max))))
23f87bed
MB
5206 (save-excursion
5207 (goto-char (point-max))
5208 (gnus-insert-next-page-button))))))
eec82323
LMI
5209
5210;; Article mode commands
5211
5212(defun gnus-article-goto-next-page ()
5213 "Show the next page of the article."
5214 (interactive)
5215 (when (gnus-article-next-page)
5216 (goto-char (point-min))
5217 (gnus-article-read-summary-keys nil (gnus-character-to-event ?n))))
5218
23f87bed 5219
eec82323 5220(defun gnus-article-goto-prev-page ()
23f87bed 5221 "Show the previous page of the article."
eec82323 5222 (interactive)
23f87bed
MB
5223 (if (bobp)
5224 (gnus-article-read-summary-keys nil (gnus-character-to-event ?p))
eec82323
LMI
5225 (gnus-article-prev-page nil)))
5226
23f87bed
MB
5227;; This is cleaner but currently breaks `gnus-pick-mode':
5228;;
5229;; (defun gnus-article-goto-next-page ()
5230;; "Show the next page of the article."
5231;; (interactive)
5232;; (gnus-eval-in-buffer-window gnus-summary-buffer
5233;; (gnus-summary-next-page)))
5234;;
5235;; (defun gnus-article-goto-prev-page ()
5236;; "Show the next page of the article."
5237;; (interactive)
5238;; (gnus-eval-in-buffer-window gnus-summary-buffer
5239;; (gnus-summary-prev-page)))
5240
eec82323
LMI
5241(defun gnus-article-next-page (&optional lines)
5242 "Show the next page of the current article.
5243If end of article, return non-nil. Otherwise return nil.
5244Argument LINES specifies lines to be scrolled up."
5245 (interactive "p")
d8a88581 5246 (move-to-window-line -1)
eec82323
LMI
5247 (if (save-excursion
5248 (end-of-line)
5249 (and (pos-visible-in-window-p) ;Not continuation line.
23f87bed 5250 (>= (1+ (point)) (point-max)))) ;Allow for trailing newline.
eec82323
LMI
5251 ;; Nothing in this page.
5252 (if (or (not gnus-page-broken)
5253 (save-excursion
5254 (save-restriction
23f87bed
MB
5255 (widen)
5256 (forward-line)
5257 (eobp)))) ;Real end-of-buffer?
5258 (progn
5259 (when gnus-article-over-scroll
5260 (gnus-article-next-page-1 lines))
5261 t) ;Nothing more.
eec82323
LMI
5262 (gnus-narrow-to-page 1) ;Go to next page.
5263 nil)
5264 ;; More in this page.
23f87bed 5265 (gnus-article-next-page-1 lines)
eec82323
LMI
5266 nil))
5267
d8a88581
MB
5268(defmacro gnus-article-beginning-of-window ()
5269 "Move point to the beginning of the window.
5270In Emacs, the point is placed at the line number which `scroll-margin'
5271specifies."
5272 (if (featurep 'xemacs)
5273 '(move-to-window-line 0)
5274 '(move-to-window-line
5275 (min (max 0 scroll-margin)
5276 (max 1 (- (window-height)
5277 (if mode-line-format 1 0)
5278 (if (and (boundp 'header-line-format)
5279 (symbol-value 'header-line-format))
5280 1 0)))))))
5281
23f87bed 5282(defun gnus-article-next-page-1 (lines)
4b91459a
MB
5283 (when (and (not (featurep 'xemacs))
5284 (numberp lines)
5285 (> lines 0)
5286 (numberp (symbol-value 'scroll-margin))
5287 (> (symbol-value 'scroll-margin) 0))
5288 ;; Protect against the bug that Emacs 21.x hangs up when scrolling up for
5289 ;; too many number of lines if `scroll-margin' is set as two or greater.
5290 (setq lines (min lines
5291 (max 0 (- (count-lines (window-start) (point-max))
5292 (symbol-value 'scroll-margin))))))
5293 (condition-case ()
5294 (let ((scroll-in-place nil))
5295 (scroll-up lines))
5296 (end-of-buffer
5297 ;; Long lines may cause an end-of-buffer error.
5298 (goto-char (point-max))))
5299 (gnus-article-beginning-of-window))
23f87bed 5300
eec82323
LMI
5301(defun gnus-article-prev-page (&optional lines)
5302 "Show previous page of current article.
5303Argument LINES specifies lines to be scrolled down."
5304 (interactive "p")
d8a88581 5305 (move-to-window-line 0)
eec82323
LMI
5306 (if (and gnus-page-broken
5307 (bobp)
5308 (not (save-restriction (widen) (bobp)))) ;Real beginning-of-buffer?
5309 (progn
5310 (gnus-narrow-to-page -1) ;Go to previous page.
5311 (goto-char (point-max))
5312 (recenter -1))
c03ac728
MB
5313 (prog1
5314 (condition-case ()
5315 (let ((scroll-in-place nil))
5316 (scroll-down lines))
5317 (beginning-of-buffer
5318 (goto-char (point-min))))
5319 (gnus-article-beginning-of-window))))
eec82323 5320
23f87bed
MB
5321(defun gnus-article-only-boring-p ()
5322 "Decide whether there is only boring text remaining in the article.
5323Something \"interesting\" is a word of at least two letters that does
5324not have a face in `gnus-article-boring-faces'."
5325 (when (and gnus-article-skip-boring
5326 (boundp 'gnus-article-boring-faces)
5327 (symbol-value 'gnus-article-boring-faces))
5328 (save-excursion
531bedc3
MB
5329 (let ((inhibit-point-motion-hooks t))
5330 (catch 'only-boring
5331 (while (re-search-forward "\\b\\w\\w" nil t)
5332 (forward-char -1)
5333 (when (not (gnus-intersection
5334 (gnus-faces-at (point))
5335 (symbol-value 'gnus-article-boring-faces)))
5336 (throw 'only-boring nil)))
5337 (throw 'only-boring t))))))
23f87bed 5338
eec82323
LMI
5339(defun gnus-article-refer-article ()
5340 "Read article specified by message-id around point."
5341 (interactive)
23f87bed
MB
5342 (save-excursion
5343 (re-search-backward "[ \t]\\|^" (gnus-point-at-bol) t)
5344 (re-search-forward "<?news:<?\\|<" (gnus-point-at-eol) t)
5345 (if (re-search-forward "[^@ ]+@[^ \t>]+" (gnus-point-at-eol) t)
5346 (let ((msg-id (concat "<" (match-string 0) ">")))
eec82323 5347 (set-buffer gnus-summary-buffer)
23f87bed 5348 (gnus-summary-refer-article msg-id))
eec82323
LMI
5349 (error "No references around point"))))
5350
5351(defun gnus-article-show-summary ()
5352 "Reconfigure windows to show summary buffer."
5353 (interactive)
5354 (if (not (gnus-buffer-live-p gnus-summary-buffer))
5355 (error "There is no summary buffer for this article buffer")
a8151ef7 5356 (gnus-article-set-globals)
eec82323 5357 (gnus-configure-windows 'article)
6748645f
LMI
5358 (gnus-summary-goto-subject gnus-current-article)
5359 (gnus-summary-position-point)))
eec82323
LMI
5360
5361(defun gnus-article-describe-briefly ()
5362 "Describe article mode commands briefly."
5363 (interactive)
16409b0b 5364 (gnus-message 6 (substitute-command-keys "\\<gnus-article-mode-map>\\[gnus-article-goto-next-page]:Next page \\[gnus-article-goto-prev-page]:Prev page \\[gnus-article-show-summary]:Show summary \\[gnus-info-find-node]:Run Info \\[gnus-article-describe-briefly]:This help")))
eec82323
LMI
5365
5366(defun gnus-article-summary-command ()
5367 "Execute the last keystroke in the summary buffer."
5368 (interactive)
5369 (let ((obuf (current-buffer))
5370 (owin (current-window-configuration))
5371 func)
6748645f 5372 (switch-to-buffer gnus-article-current-summary 'norecord)
eec82323
LMI
5373 (setq func (lookup-key (current-local-map) (this-command-keys)))
5374 (call-interactively func)
5375 (set-buffer obuf)
5376 (set-window-configuration owin)
5377 (set-window-point (get-buffer-window (current-buffer)) (point))))
5378
5379(defun gnus-article-summary-command-nosave ()
5380 "Execute the last keystroke in the summary buffer."
5381 (interactive)
5382 (let (func)
6748645f 5383 (pop-to-buffer gnus-article-current-summary 'norecord)
eec82323
LMI
5384 (setq func (lookup-key (current-local-map) (this-command-keys)))
5385 (call-interactively func)))
5386
16409b0b
GM
5387(defun gnus-article-check-buffer ()
5388 "Beep if not in an article buffer."
5389 (unless (equal major-mode 'gnus-article-mode)
5390 (error "Command invoked outside of a Gnus article buffer")))
5391
eec82323
LMI
5392(defun gnus-article-read-summary-keys (&optional arg key not-restore-window)
5393 "Read a summary buffer key sequence and execute it from the article buffer."
5394 (interactive "P")
16409b0b 5395 (gnus-article-check-buffer)
eec82323 5396 (let ((nosaves
23f87bed
MB
5397 '("q" "Q" "c" "r" "\C-c\C-f" "m" "a" "f"
5398 "Zc" "ZC" "ZE" "ZQ" "ZZ" "Zn" "ZR" "ZG" "ZN" "ZP"
5399 "=" "^" "\M-^" "|"))
5400 (nosave-but-article
5401 '("A\r"))
5402 (nosave-in-article
5403 '("\C-d"))
5404 (up-to-top
5405 '("n" "Gn" "p" "Gp"))
5406 keys new-sum-point)
eec82323 5407 (save-excursion
6748645f 5408 (set-buffer gnus-article-current-summary)
eec82323 5409 (let (gnus-pick-mode)
23f87bed
MB
5410 (push (or key last-command-event) unread-command-events)
5411 (setq keys (if (featurep 'xemacs)
16409b0b
GM
5412 (events-to-keys (read-key-sequence nil))
5413 (read-key-sequence nil)))))
a1506d29 5414
eec82323
LMI
5415 (message "")
5416
5417 (if (or (member keys nosaves)
23f87bed
MB
5418 (member keys nosave-but-article)
5419 (member keys nosave-in-article))
5420 (let (func)
5421 (save-window-excursion
5422 (pop-to-buffer gnus-article-current-summary 'norecord)
5423 ;; We disable the pick minor mode commands.
5424 (let (gnus-pick-mode)
5425 (setq func (lookup-key (current-local-map) keys))))
5426 (if (or (not func)
16409b0b 5427 (numberp func))
23f87bed
MB
5428 (ding)
5429 (unless (member keys nosave-in-article)
5430 (set-buffer gnus-article-current-summary))
5431 (call-interactively func)
5432 (setq new-sum-point (point)))
5433 (when (member keys nosave-but-article)
5434 (pop-to-buffer gnus-article-buffer 'norecord)))
eec82323
LMI
5435 ;; These commands should restore window configuration.
5436 (let ((obuf (current-buffer))
23f87bed
MB
5437 (owin (current-window-configuration))
5438 (opoint (point))
5439 win func in-buffer selected new-sum-start new-sum-hscroll)
5440 (cond (not-restore-window
5441 (pop-to-buffer gnus-article-current-summary 'norecord))
5442 ((setq win (get-buffer-window gnus-article-current-summary))
5443 (select-window win))
5444 (t
5445 (switch-to-buffer gnus-article-current-summary 'norecord)))
5446 (setq in-buffer (current-buffer))
5447 ;; We disable the pick minor mode commands.
5448 (if (and (setq func (let (gnus-pick-mode)
520aa572
SZ
5449 (lookup-key (current-local-map) keys)))
5450 (functionp func))
23f87bed
MB
5451 (progn
5452 (call-interactively func)
5453 (when (eq win (selected-window))
5454 (setq new-sum-point (point)
5455 new-sum-start (window-start win)
9a89f5b0 5456 new-sum-hscroll (window-hscroll win)))
520aa572
SZ
5457 (when (eq in-buffer (current-buffer))
5458 (setq selected (gnus-summary-select-article))
5459 (set-buffer obuf)
5460 (unless not-restore-window
5461 (set-window-configuration owin))
5462 (when (eq selected 'old)
5463 (article-goto-body)
5464 (set-window-start (get-buffer-window (current-buffer))
5465 1)
5466 (set-window-point (get-buffer-window (current-buffer))
5467 (point)))
23f87bed
MB
5468 (when (and (not not-restore-window)
5469 new-sum-point)
5470 (set-window-point win new-sum-point)
5471 (set-window-start win new-sum-start)
9a89f5b0 5472 (set-window-hscroll win new-sum-hscroll))))
23f87bed
MB
5473 (set-window-configuration owin)
5474 (ding))))))
520aa572
SZ
5475
5476(defun gnus-article-describe-key (key)
5477 "Display documentation of the function invoked by KEY. KEY is a string."
5478 (interactive "kDescribe key: ")
5479 (gnus-article-check-buffer)
5480 (if (eq (key-binding key) 'gnus-article-read-summary-keys)
5481 (save-excursion
5482 (set-buffer gnus-article-current-summary)
5483 (let (gnus-pick-mode)
23f87bed
MB
5484 (if (featurep 'xemacs)
5485 (progn
5486 (push (elt key 0) unread-command-events)
5487 (setq key (events-to-keys
5488 (read-key-sequence "Describe key: "))))
5489 (setq unread-command-events
5490 (mapcar
5491 (lambda (x) (if (>= x 128) (list 'meta (- x 128)) x))
5492 (string-to-list key)))
5493 (setq key (read-key-sequence "Describe key: "))))
520aa572
SZ
5494 (describe-key key))
5495 (describe-key key)))
5496
5497(defun gnus-article-describe-key-briefly (key &optional insert)
5498 "Display documentation of the function invoked by KEY. KEY is a string."
5499 (interactive "kDescribe key: \nP")
5500 (gnus-article-check-buffer)
5501 (if (eq (key-binding key) 'gnus-article-read-summary-keys)
5502 (save-excursion
5503 (set-buffer gnus-article-current-summary)
5504 (let (gnus-pick-mode)
23f87bed
MB
5505 (if (featurep 'xemacs)
5506 (progn
5507 (push (elt key 0) unread-command-events)
5508 (setq key (events-to-keys
5509 (read-key-sequence "Describe key: "))))
5510 (setq unread-command-events
5511 (mapcar
5512 (lambda (x) (if (>= x 128) (list 'meta (- x 128)) x))
5513 (string-to-list key)))
5514 (setq key (read-key-sequence "Describe key: "))))
520aa572
SZ
5515 (describe-key-briefly key insert))
5516 (describe-key-briefly key insert)))
eec82323 5517
23f87bed
MB
5518(defun gnus-article-reply-with-original (&optional wide)
5519 "Start composing a reply mail to the current message.
5520The text in the region will be yanked. If the region isn't active,
5521the entire article will be yanked."
5522 (interactive "P")
5523 (let ((article (cdr gnus-article-current))
5524 contents)
5525 (if (not (gnus-mark-active-p))
5526 (with-current-buffer gnus-summary-buffer
5527 (gnus-summary-reply (list (list article)) wide))
5528 (setq contents (buffer-substring (point) (mark t)))
5529 ;; Deactivate active regions.
5530 (when (and (boundp 'transient-mark-mode)
5531 transient-mark-mode)
5532 (setq mark-active nil))
5533 (with-current-buffer gnus-summary-buffer
5534 (gnus-summary-reply
5535 (list (list article contents)) wide)))))
5536
5537(defun gnus-article-followup-with-original ()
5538 "Compose a followup to the current article.
5539The text in the region will be yanked. If the region isn't active,
5540the entire article will be yanked."
5541 (interactive)
5542 (let ((article (cdr gnus-article-current))
5543 contents)
5544 (if (not (gnus-mark-active-p))
5545 (with-current-buffer gnus-summary-buffer
5546 (gnus-summary-followup (list (list article))))
5547 (setq contents (buffer-substring (point) (mark t)))
5548 ;; Deactivate active regions.
5549 (when (and (boundp 'transient-mark-mode)
5550 transient-mark-mode)
5551 (setq mark-active nil))
5552 (with-current-buffer gnus-summary-buffer
5553 (gnus-summary-followup
5554 (list (list article contents)))))))
5555
eec82323
LMI
5556(defun gnus-article-hide (&optional arg force)
5557 "Hide all the gruft in the current article.
23f87bed
MB
5558This means that signatures, cited text and (some) headers will be
5559hidden.
eec82323 5560If given a prefix, show the hidden text instead."
6748645f 5561 (interactive (append (gnus-article-hidden-arg) (list 'force)))
eec82323 5562 (gnus-article-hide-headers arg)
16409b0b 5563 (gnus-article-hide-list-identifiers arg)
eec82323
LMI
5564 (gnus-article-hide-citation-maybe arg force)
5565 (gnus-article-hide-signature arg))
5566
5567(defun gnus-article-maybe-highlight ()
6748645f 5568 "Do some article highlighting if article highlighting is requested."
eec82323
LMI
5569 (when (gnus-visual-p 'article-highlight 'highlight)
5570 (gnus-article-highlight-some)))
5571
6748645f
LMI
5572(defun gnus-check-group-server ()
5573 ;; Make sure the connection to the server is alive.
5574 (unless (gnus-server-opened
5575 (gnus-find-method-for-group gnus-newsgroup-name))
5576 (gnus-check-server (gnus-find-method-for-group gnus-newsgroup-name))
5577 (gnus-request-group gnus-newsgroup-name t)))
5578
23f87bed
MB
5579(eval-when-compile
5580 (autoload 'nneething-get-file-name "nneething"))
5581
eec82323
LMI
5582(defun gnus-request-article-this-buffer (article group)
5583 "Get an article and insert it into this buffer."
6748645f 5584 (let (do-update-line sparse-header)
eec82323
LMI
5585 (prog1
5586 (save-excursion
5587 (erase-buffer)
5588 (gnus-kill-all-overlays)
5589 (setq group (or group gnus-newsgroup-name))
5590
eec82323
LMI
5591 ;; Using `gnus-request-article' directly will insert the article into
5592 ;; `nntp-server-buffer' - so we'll save some time by not having to
5593 ;; copy it from the server buffer into the article buffer.
5594
5595 ;; We only request an article by message-id when we do not have the
5596 ;; headers for it, so we'll have to get those.
5597 (when (stringp article)
16409b0b 5598 (gnus-read-header article))
eec82323
LMI
5599
5600 ;; If the article number is negative, that means that this article
5601 ;; doesn't belong in this newsgroup (possibly), so we find its
5602 ;; message-id and request it by id instead of number.
5603 (when (and (numberp article)
5604 gnus-summary-buffer
5605 (get-buffer gnus-summary-buffer)
6748645f 5606 (gnus-buffer-exists-p gnus-summary-buffer))
eec82323
LMI
5607 (save-excursion
5608 (set-buffer gnus-summary-buffer)
5609 (let ((header (gnus-summary-article-header article)))
5610 (when (< article 0)
5611 (cond
5612 ((memq article gnus-newsgroup-sparse)
5613 ;; This is a sparse gap article.
5614 (setq do-update-line article)
5615 (setq article (mail-header-id header))
16409b0b 5616 (setq sparse-header (gnus-read-header article))
eec82323
LMI
5617 (setq gnus-newsgroup-sparse
5618 (delq article gnus-newsgroup-sparse)))
5619 ((vectorp header)
5620 ;; It's a real article.
5621 (setq article (mail-header-id header)))
5622 (t
5623 ;; It is an extracted pseudo-article.
5624 (setq article 'pseudo)
5625 (gnus-request-pseudo-article header))))
5626
5627 (let ((method (gnus-find-method-for-group
5628 gnus-newsgroup-name)))
6748645f
LMI
5629 (when (and (eq (car method) 'nneething)
5630 (vectorp header))
23f87bed
MB
5631 (let ((dir (nneething-get-file-name
5632 (mail-header-id header))))
5633 (when (and (stringp dir)
5634 (file-directory-p dir))
eec82323
LMI
5635 (setq article 'nneething)
5636 (gnus-group-enter-directory dir))))))))
5637
5638 (cond
5639 ;; Refuse to select canceled articles.
5640 ((and (numberp article)
5641 gnus-summary-buffer
5642 (get-buffer gnus-summary-buffer)
6748645f 5643 (gnus-buffer-exists-p gnus-summary-buffer)
eec82323
LMI
5644 (eq (cdr (save-excursion
5645 (set-buffer gnus-summary-buffer)
5646 (assq article gnus-newsgroup-reads)))
5647 gnus-canceled-mark))
5648 nil)
5649 ;; We first check `gnus-original-article-buffer'.
5650 ((and (get-buffer gnus-original-article-buffer)
5651 (numberp article)
5652 (save-excursion
5653 (set-buffer gnus-original-article-buffer)
5654 (and (equal (car gnus-original-article) group)
5655 (eq (cdr gnus-original-article) article))))
5656 (insert-buffer-substring gnus-original-article-buffer)
5657 'article)
5658 ;; Check the backlog.
5659 ((and gnus-keep-backlog
5660 (gnus-backlog-request-article group article (current-buffer)))
5661 'article)
5662 ;; Check asynchronous pre-fetch.
5663 ((gnus-async-request-fetched-article group article (current-buffer))
5664 (gnus-async-prefetch-next group article gnus-summary-buffer)
6748645f
LMI
5665 (when (and (numberp article) gnus-keep-backlog)
5666 (gnus-backlog-enter-article group article (current-buffer)))
eec82323
LMI
5667 'article)
5668 ;; Check the cache.
5669 ((and gnus-use-cache
5670 (numberp article)
5671 (gnus-cache-request-article article group))
5672 'article)
23f87bed
MB
5673 ;; Check the agent cache.
5674 ((gnus-agent-request-article article group)
5675 'article)
eec82323 5676 ;; Get the article and put into the article buffer.
16409b0b
GM
5677 ((or (stringp article)
5678 (numberp article))
5679 (let ((gnus-override-method gnus-override-method)
a1506d29 5680 (methods (and (stringp article)
16409b0b 5681 gnus-refer-article-method))
23f87bed
MB
5682 (backend (car (gnus-find-method-for-group
5683 gnus-newsgroup-name)))
16409b0b 5684 result
4e7d0221 5685 (inhibit-read-only t))
e0bad764
DL
5686 (if (or (not (listp methods))
5687 (and (symbolp (car methods))
5688 (assq (car methods) nnoo-definition-alist)))
5689 (setq methods (list methods)))
16409b0b
GM
5690 (when (and (null gnus-override-method)
5691 methods)
5692 (setq gnus-override-method (pop methods)))
5693 (while (not result)
5694 (when (eq gnus-override-method 'current)
c55f89a5
GM
5695 (setq gnus-override-method
5696 (with-current-buffer gnus-summary-buffer
5697 gnus-current-select-method)))
16409b0b
GM
5698 (erase-buffer)
5699 (gnus-kill-all-overlays)
5700 (let ((gnus-newsgroup-name group))
5701 (gnus-check-group-server))
23f87bed
MB
5702 (cond
5703 ((gnus-request-article article group (current-buffer))
16409b0b 5704 (when (numberp article)
a1506d29 5705 (gnus-async-prefetch-next group article
16409b0b
GM
5706 gnus-summary-buffer)
5707 (when gnus-keep-backlog
5708 (gnus-backlog-enter-article
5709 group article (current-buffer))))
5710 (setq result 'article))
23f87bed
MB
5711 (methods
5712 (setq gnus-override-method (pop methods)))
5713 ((not (string-match "^400 "
5714 (nnheader-get-report backend)))
5715 ;; If we get 400 server disconnect, reconnect and
5716 ;; retry; otherwise, assume the article has expired.
5717 (setq result 'done))))
16409b0b 5718 (and (eq result 'article) 'article)))
eec82323
LMI
5719 ;; It was a pseudo.
5720 (t article)))
5721
6748645f
LMI
5722 ;; Associate this article with the current summary buffer.
5723 (setq gnus-article-current-summary gnus-summary-buffer)
5724
eec82323
LMI
5725 ;; Take the article from the original article buffer
5726 ;; and place it in the buffer it's supposed to be in.
5727 (when (and (get-buffer gnus-article-buffer)
eec82323
LMI
5728 (equal (buffer-name (current-buffer))
5729 (buffer-name (get-buffer gnus-article-buffer))))
5730 (save-excursion
5731 (if (get-buffer gnus-original-article-buffer)
6748645f
LMI
5732 (set-buffer gnus-original-article-buffer)
5733 (set-buffer (gnus-get-buffer-create gnus-original-article-buffer))
16409b0b 5734 (buffer-disable-undo)
eec82323 5735 (setq major-mode 'gnus-original-article-mode)
6748645f 5736 (setq buffer-read-only t))
23f87bed 5737 (let ((inhibit-read-only t))
eec82323
LMI
5738 (erase-buffer)
5739 (insert-buffer-substring gnus-article-buffer))
16409b0b
GM
5740 (setq gnus-original-article (cons group article)))
5741
5742 ;; Decode charsets.
5743 (run-hooks 'gnus-article-decode-hook)
5744 ;; Mark article as decoded or not.
5745 (setq gnus-article-decoded-p gnus-article-decode-hook))
eec82323
LMI
5746
5747 ;; Update sparse articles.
5748 (when (and do-update-line
5749 (or (numberp article)
5750 (stringp article)))
5751 (let ((buf (current-buffer)))
5752 (set-buffer gnus-summary-buffer)
6748645f 5753 (gnus-summary-update-article do-update-line sparse-header)
eec82323 5754 (gnus-summary-goto-subject do-update-line nil t)
23f87bed 5755 (set-window-point (gnus-get-buffer-window (current-buffer) t)
eec82323
LMI
5756 (point))
5757 (set-buffer buf))))))
5758
5759;;;
5760;;; Article editing
5761;;;
5762
5763(defcustom gnus-article-edit-mode-hook nil
5764 "Hook run in article edit mode buffers."
5765 :group 'gnus-article-various
5766 :type 'hook)
5767
5768(defvar gnus-article-edit-done-function nil)
5769
5770(defvar gnus-article-edit-mode-map nil)
23f87bed 5771(defvar gnus-article-edit-mode nil)
eec82323 5772
16409b0b 5773;; Should we be using derived.el for this?
eec82323 5774(unless gnus-article-edit-mode-map
23f87bed 5775 (setq gnus-article-edit-mode-map (make-keymap))
16409b0b 5776 (set-keymap-parent gnus-article-edit-mode-map text-mode-map)
eec82323
LMI
5777
5778 (gnus-define-keys gnus-article-edit-mode-map
23f87bed 5779 "\C-c?" describe-mode
eec82323 5780 "\C-c\C-c" gnus-article-edit-done
23f87bed
MB
5781 "\C-c\C-k" gnus-article-edit-exit
5782 "\C-c\C-f\C-t" message-goto-to
5783 "\C-c\C-f\C-o" message-goto-from
5784 "\C-c\C-f\C-b" message-goto-bcc
5785 ;;"\C-c\C-f\C-w" message-goto-fcc
5786 "\C-c\C-f\C-c" message-goto-cc
5787 "\C-c\C-f\C-s" message-goto-subject
5788 "\C-c\C-f\C-r" message-goto-reply-to
5789 "\C-c\C-f\C-n" message-goto-newsgroups
5790 "\C-c\C-f\C-d" message-goto-distribution
5791 "\C-c\C-f\C-f" message-goto-followup-to
5792 "\C-c\C-f\C-m" message-goto-mail-followup-to
5793 "\C-c\C-f\C-k" message-goto-keywords
5794 "\C-c\C-f\C-u" message-goto-summary
5795 "\C-c\C-f\C-i" message-insert-or-toggle-importance
5796 "\C-c\C-f\C-a" message-generate-unsubscribed-mail-followup-to
5797 "\C-c\C-b" message-goto-body
5798 "\C-c\C-i" message-goto-signature
5799
5800 "\C-c\C-t" message-insert-to
5801 "\C-c\C-n" message-insert-newsgroups
5802 "\C-c\C-o" message-sort-headers
5803 "\C-c\C-e" message-elide-region
5804 "\C-c\C-v" message-delete-not-region
5805 "\C-c\C-z" message-kill-to-signature
5806 "\M-\r" message-newline-and-reformat
5807 "\C-c\C-a" mml-attach-file
5808 "\C-a" message-beginning-of-line
5809 "\t" message-tab
5810 "\M-;" comment-region)
eec82323
LMI
5811
5812 (gnus-define-keys (gnus-article-edit-wash-map
5813 "\C-c\C-w" gnus-article-edit-mode-map)
5814 "f" gnus-article-edit-full-stops))
5815
23f87bed
MB
5816(easy-menu-define
5817 gnus-article-edit-mode-field-menu gnus-article-edit-mode-map ""
5818 '("Field"
5819 ["Fetch To" message-insert-to t]
5820 ["Fetch Newsgroups" message-insert-newsgroups t]
5821 "----"
5822 ["To" message-goto-to t]
5823 ["From" message-goto-from t]
5824 ["Subject" message-goto-subject t]
5825 ["Cc" message-goto-cc t]
5826 ["Reply-To" message-goto-reply-to t]
5827 ["Summary" message-goto-summary t]
5828 ["Keywords" message-goto-keywords t]
5829 ["Newsgroups" message-goto-newsgroups t]
5830 ["Followup-To" message-goto-followup-to t]
5831 ["Mail-Followup-To" message-goto-mail-followup-to t]
5832 ["Distribution" message-goto-distribution t]
5833 ["Body" message-goto-body t]
5834 ["Signature" message-goto-signature t]))
5835
4e7d0221 5836(define-derived-mode gnus-article-edit-mode message-mode "Article Edit"
eec82323
LMI
5837 "Major mode for editing articles.
5838This is an extended text-mode.
5839
5840\\{gnus-article-edit-mode-map}"
eec82323
LMI
5841 (make-local-variable 'gnus-article-edit-done-function)
5842 (make-local-variable 'gnus-prev-winconf)
80b47379
SZ
5843 (set (make-local-variable 'font-lock-defaults)
5844 '(message-font-lock-keywords t))
23f87bed
MB
5845 (set (make-local-variable 'mail-header-separator) "")
5846 (set (make-local-variable 'gnus-article-edit-mode) t)
5847 (easy-menu-add message-mode-field-menu message-mode-map)
5848 (mml-mode)
eec82323
LMI
5849 (setq buffer-read-only nil)
5850 (buffer-enable-undo)
80b47379 5851 (widen))
eec82323
LMI
5852
5853(defun gnus-article-edit (&optional force)
5854 "Edit the current article.
5855This will have permanent effect only in mail groups.
5856If FORCE is non-nil, allow editing of articles even in read-only
5857groups."
5858 (interactive "P")
5859 (when (and (not force)
5860 (gnus-group-read-only-p))
a8151ef7 5861 (error "The current newsgroup does not support article editing"))
6748645f 5862 (gnus-article-date-original)
eec82323 5863 (gnus-article-edit-article
16409b0b 5864 'ignore
6748645f 5865 `(lambda (no-highlight)
16409b0b 5866 'ignore
eec82323
LMI
5867 (gnus-summary-edit-article-done
5868 ,(or (mail-header-references gnus-current-headers) "")
6748645f 5869 ,(gnus-group-read-only-p) ,gnus-summary-buffer no-highlight))))
eec82323 5870
16409b0b 5871(defun gnus-article-edit-article (start-func exit-func)
eec82323
LMI
5872 "Start editing the contents of the current article buffer."
5873 (let ((winconf (current-window-configuration)))
5874 (set-buffer gnus-article-buffer)
0683d241
MB
5875 (let ((message-auto-save-directory
5876 ;; Don't associate the article buffer with a draft file.
5877 nil))
5878 (gnus-article-edit-mode))
16409b0b 5879 (funcall start-func)
23f87bed 5880 (set-buffer-modified-p nil)
eec82323
LMI
5881 (gnus-configure-windows 'edit-article)
5882 (setq gnus-article-edit-done-function exit-func)
5883 (setq gnus-prev-winconf winconf)
5884 (gnus-message 6 "C-c C-c to end edits")))
5885
6748645f 5886(defun gnus-article-edit-done (&optional arg)
eec82323 5887 "Update the article edits and exit."
6748645f 5888 (interactive "P")
eec82323
LMI
5889 (let ((func gnus-article-edit-done-function)
5890 (buf (current-buffer))
23f87bed
MB
5891 (start (window-start))
5892 (p (point))
5893 (winconf gnus-prev-winconf))
5894 (widen) ;; Widen it in case that users narrowed the buffer.
5895 (funcall func arg)
5896 (set-buffer buf)
5897 ;; The cache and backlog have to be flushed somewhat.
5898 (when gnus-keep-backlog
5899 (gnus-backlog-remove-article
5900 (car gnus-article-current) (cdr gnus-article-current)))
5901 ;; Flush original article as well.
eec82323 5902 (save-excursion
23f87bed
MB
5903 (when (get-buffer gnus-original-article-buffer)
5904 (set-buffer gnus-original-article-buffer)
5905 (setq gnus-original-article nil)))
5906 (when gnus-use-cache
5907 (gnus-cache-update-article
5908 (car gnus-article-current) (cdr gnus-article-current)))
5909 ;; We remove all text props from the article buffer.
5910 (kill-all-local-variables)
5911 (gnus-set-text-properties (point-min) (point-max) nil)
5912 (gnus-article-mode)
5913 (set-window-configuration winconf)
eec82323
LMI
5914 (set-buffer buf)
5915 (set-window-start (get-buffer-window buf) start)
23f87bed
MB
5916 (set-window-point (get-buffer-window buf) (point)))
5917 (gnus-summary-show-article))
eec82323
LMI
5918
5919(defun gnus-article-edit-exit ()
5920 "Exit the article editing without updating."
5921 (interactive)
23f87bed
MB
5922 (when (or (not (buffer-modified-p))
5923 (yes-or-no-p "Article modified; kill anyway? "))
5924 (let ((curbuf (current-buffer))
5925 (p (point))
5926 (window-start (window-start)))
5927 (erase-buffer)
5928 (if (gnus-buffer-live-p gnus-original-article-buffer)
d8a88581 5929 (insert-buffer-substring gnus-original-article-buffer))
23f87bed
MB
5930 (let ((winconf gnus-prev-winconf))
5931 (kill-all-local-variables)
5932 (gnus-article-mode)
5933 (set-window-configuration winconf)
5934 ;; Tippy-toe some to make sure that point remains where it was.
5935 (save-current-buffer
5936 (set-buffer curbuf)
5937 (set-window-start (get-buffer-window (current-buffer)) window-start)
5938 (goto-char p))))
5939 (gnus-summary-show-article)))
eec82323
LMI
5940
5941(defun gnus-article-edit-full-stops ()
5942 "Interactively repair spacing at end of sentences."
5943 (interactive)
5944 (save-excursion
5945 (goto-char (point-min))
5946 (search-forward-regexp "^$" nil t)
5947 (let ((case-fold-search nil))
5948 (query-replace-regexp "\\([.!?][])}]* \\)\\([[({A-Z]\\)" "\\1 \\2"))))
5949
5950;;;
5951;;; Article highlights
5952;;;
5953
5954;; Written by Per Abrahamsen <abraham@iesd.auc.dk>.
5955
5956;;; Internal Variables:
5957
a1506d29 5958(defcustom gnus-button-url-regexp
0076a86e 5959 (if (string-match "[[:digit:]]" "1") ;; support POSIX?
23f87bed
MB
5960 "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?[-a-z0-9_=!?#$@~%&*+\\/:;.,[:word:]]+[-a-z0-9_=#$@~%&*+\\/[:word:]]\\)"
5961 "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)\\(//[-a-z0-9_.]+:[0-9]*\\)?\\([-a-z0-9_=!?#$@~%&*+\\/:;.,]\\|\\w\\)+\\([-a-z0-9_=#$@~%&*+\\/]\\|\\w\\)\\)")
eec82323
LMI
5962 "Regular expression that matches URLs."
5963 :group 'gnus-article-buttons
5964 :type 'regexp)
5965
23f87bed
MB
5966(defcustom gnus-button-valid-fqdn-regexp
5967 message-valid-fqdn-regexp
5968 "Regular expression that matches a valid FQDN."
bf247b6e 5969 :version "22.1"
23f87bed
MB
5970 :group 'gnus-article-buttons
5971 :type 'regexp)
5972
97f78c9b
MB
5973;; Regexp suggested by Felix Wiemann in <87oeuomcz9.fsf@news2.ososo.de>
5974(defcustom gnus-button-valid-localpart-regexp
5975 "[a-z0-9$%(*-=?[_][^<>\")!;:,{}\n\t ]*"
5976 "Regular expression that matches a localpart of mail addresses or MIDs."
5977 :version "22.1"
5978 :group 'gnus-article-buttons
5979 :type 'regexp)
5980
23f87bed
MB
5981(defcustom gnus-button-man-handler 'manual-entry
5982 "Function to use for displaying man pages.
5983The function must take at least one argument with a string naming the
5984man page."
bf247b6e 5985 :version "22.1"
23f87bed
MB
5986 :type '(choice (function-item :tag "Man" manual-entry)
5987 (function-item :tag "Woman" woman)
5988 (function :tag "Other"))
5989 :group 'gnus-article-buttons)
5990
5991(defcustom gnus-ctan-url "http://tug.ctan.org/tex-archive/"
5992 "Top directory of a CTAN \(Comprehensive TeX Archive Network\) archive.
5993If the default site is too slow, try to find a CTAN mirror, see
5994<URL:http://tug.ctan.org/tex-archive/CTAN.sites?action=/index.html>. See also
5995the variable `gnus-button-handle-ctan'."
bf247b6e 5996 :version "22.1"
23f87bed
MB
5997 :group 'gnus-article-buttons
5998 :link '(custom-manual "(gnus)Group Parameters")
5999 :type '(choice (const "http://www.tex.ac.uk/tex-archive/")
6000 (const "http://tug.ctan.org/tex-archive/")
6001 (const "http://www.dante.de/CTAN/")
6002 (string :tag "Other")))
6003
6004(defcustom gnus-button-ctan-handler 'browse-url
6005 "Function to use for displaying CTAN links.
6006The function must take one argument, the string naming the URL."
bf247b6e 6007 :version "22.1"
23f87bed
MB
6008 :type '(choice (function-item :tag "Browse Url" browse-url)
6009 (function :tag "Other"))
6010 :group 'gnus-article-buttons)
6011
6012(defcustom gnus-button-handle-ctan-bogus-regexp "^/?tex-archive/\\|^/"
6013 "Bogus strings removed from CTAN URLs."
bf247b6e 6014 :version "22.1"
23f87bed
MB
6015 :group 'gnus-article-buttons
6016 :type '(choice (const "^/?tex-archive/\\|/")
6017 (regexp :tag "Other")))
6018
6019(defcustom gnus-button-ctan-directory-regexp
97f78c9b
MB
6020 (regexp-opt
6021 (list "archive-tools" "biblio" "bibliography" "digests" "documentation"
6022 "dviware" "fonts" "graphics" "help" "indexing" "info" "language"
6023 "languages" "macros" "nonfree" "obsolete" "support" "systems"
6024 "tds" "tools" "usergrps" "web") t)
23f87bed
MB
6025 "Regular expression for ctan directories.
6026It should match all directories in the top level of `gnus-ctan-url'."
bf247b6e 6027 :version "22.1"
23f87bed
MB
6028 :group 'gnus-article-buttons
6029 :type 'regexp)
6030
6031(defcustom gnus-button-mid-or-mail-regexp
97f78c9b 6032 (concat "\\b\\(<?" gnus-button-valid-localpart-regexp "@"
23f87bed
MB
6033 gnus-button-valid-fqdn-regexp
6034 ">?\\)\\b")
6035 "Regular expression that matches a message ID or a mail address."
bf247b6e 6036 :version "22.1"
23f87bed
MB
6037 :group 'gnus-article-buttons
6038 :type 'regexp)
6039
6040(defcustom gnus-button-prefer-mid-or-mail 'gnus-button-mid-or-mail-heuristic
6041 "What to do when the button on a string as \"foo123@bar.invalid\" is pushed.
6042Strings like this can be either a message ID or a mail address. If it is one
6043of the symbols `mid' or `mail', Gnus will always assume that the string is a
6044message ID or a mail address, respectively. If this variable is set to the
6045symbol `ask', always query the user what do do. If it is a function, this
6046function will be called with the string as it's only argument. The function
6047must return `mid', `mail', `invalid' or `ask'."
bf247b6e 6048 :version "22.1"
23f87bed
MB
6049 :group 'gnus-article-buttons
6050 :type '(choice (function-item :tag "Heuristic function"
6051 gnus-button-mid-or-mail-heuristic)
6052 (const ask)
6053 (const mid)
6054 (const mail)))
6055
6056(defcustom gnus-button-mid-or-mail-heuristic-alist
6057 '((-10.0 . ".+\\$.+@")
6058 (-10.0 . "#")
6059 (-10.0 . "\\*")
6060 (-5.0 . "\\+[^+]*\\+.*@") ;; # two plus signs
6061 (-5.0 . "@[Nn][Ee][Ww][Ss]") ;; /\@news/i
6062 (-5.0 . "@.*[Dd][Ii][Aa][Ll][Uu][Pp]") ;; /\@.*dialup/i;
6063 (-1.0 . "^[^a-z]+@")
6064 ;;
6065 (-5.0 . "\\.[0-9][0-9]+.*@") ;; "\.[0-9]{2,}.*\@"
6066 (-5.0 . "[a-z].*[A-Z].*[a-z].*[A-Z].*@") ;; "([a-z].*[A-Z].*){2,}\@"
6067 (-3.0 . "[A-Z][A-Z][a-z][a-z].*@")
6068 (-5.0 . "\\...?.?@") ;; (-5.0 . "\..{1,3}\@")
6069 ;;
6070 (-2.0 . "^[0-9]")
6071 (-1.0 . "^[0-9][0-9]")
6072 ;;
6073 ;; -3.0 /^[0-9][0-9a-fA-F]{2,2}/;
6074 (-3.0 . "^[0-9][0-9a-fA-F][0-9a-fA-F][^0-9a-fA-F]")
6075 ;; -5.0 /^[0-9][0-9a-fA-F]{3,3}/;
6076 (-5.0 . "^[0-9][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][^0-9a-fA-F]")
6077 ;;
6078 (-3.0 . "[0-9][0-9][0-9][0-9][0-9][^0-9].*@") ;; "[0-9]{5,}.*\@"
6079 (-3.0 . "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][^0-9].*@")
6080 ;; "[0-9]{8,}.*\@"
6081 (-3.0
6082 . "[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].*@")
6083 ;; "[0-9]{12,}.*\@"
6084 ;; compensation for TDMA dated mail addresses:
6085 (25.0 . "-dated-[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]+.*@")
6086 ;;
6087 (-20.0 . "\\.fsf@") ;; Gnus
6088 (-20.0 . "^slrn")
6089 (-20.0 . "^Pine")
6090 (-20.0 . "_-_") ;; Subject change in thread
6091 ;;
6092 (-20.0 . "\\.ln@") ;; leafnode
6093 (-30.0 . "@ID-[0-9]+\\.[a-zA-Z]+\\.dfncis\\.de")
6094 (-30.0 . "@4[Aa][Xx]\\.com") ;; Forte Agent
6095 ;;
6096 ;; (5.0 . "") ;; $local_part_len <= 7
6097 (10.0 . "^[^0-9]+@")
6098 (3.0 . "^[^0-9]+[0-9][0-9]?[0-9]?@")
6099 ;; ^[^0-9]+[0-9]{1,3}\@ digits only at end of local part
6100 (3.0 . "\@stud")
6101 ;;
6102 (2.0 . "[a-z][a-z][._-][A-Z][a-z].*@")
6103 ;;
6104 (0.5 . "^[A-Z][a-z]")
6105 (0.5 . "^[A-Z][a-z][a-z]")
6106 (1.5 . "^[A-Z][a-z][A-Z][a-z][^a-z]") ;; ^[A-Z][a-z]{3,3}
6107 (2.0 . "^[A-Z][a-z][A-Z][a-z][a-z][^a-z]")) ;; ^[A-Z][a-z]{4,4}
6108 "An alist of \(RATE . REGEXP\) pairs for `gnus-button-mid-or-mail-heuristic'.
6109
6110A negative RATE indicates a message IDs, whereas a positive indicates a mail
6111address. The REGEXP is processed with `case-fold-search' set to nil."
bf247b6e 6112 :version "22.1"
23f87bed
MB
6113 :group 'gnus-article-buttons
6114 :type '(repeat (cons (number :tag "Rate")
6115 (regexp :tag "Regexp"))))
6116
6117(defun gnus-button-mid-or-mail-heuristic (mid-or-mail)
6118 "Guess whether MID-OR-MAIL is a message ID or a mail address.
6119Returns `mid' if MID-OR-MAIL is a message IDs, `mail' if it's a mail
6120address, `ask' if unsure and `invalid' if the string is invalid."
6121 (let ((case-fold-search nil)
6122 (list gnus-button-mid-or-mail-heuristic-alist)
6123 (result 0) rate regexp lpartlen elem)
6124 (setq lpartlen
6125 (length (gnus-replace-in-string mid-or-mail "^\\(.*\\)@.*$" "\\1")))
6126 (gnus-message 8 "`%s', length of local part=`%s'." mid-or-mail lpartlen)
6127 ;; Certain special cases...
6128 (when (string-match
6129 (concat
6130 "^0[0-9]+-[0-9][0-9][0-9][0-9]@t-online\\.de$\\|"
6131 "^[0-9]+\\.[0-9]+@compuserve\\|"
6132 "@public\\.gmane\\.org")
6133 mid-or-mail)
6134 (gnus-message 8 "`%s' is a known mail address." mid-or-mail)
6135 (setq result 'mail))
6136 (when (string-match "@.*@\\| " mid-or-mail)
6137 (gnus-message 8 "`%s' is invalid." mid-or-mail)
6138 (setq result 'invalid))
6139 ;; Nothing more to do, if result is not a number here...
6140 (when (numberp result)
6141 (while list
6142 (setq elem (car list)
6143 rate (car elem)
6144 regexp (cdr elem)
6145 list (cdr list))
6146 (when (string-match regexp mid-or-mail)
6147 (setq result (+ result rate))
6148 (gnus-message
6149 9 "`%s' matched `%s', rate `%s', result `%s'."
6150 mid-or-mail regexp rate result)))
6151 (when (<= lpartlen 7)
6152 (setq result (+ result 5.0))
6153 (gnus-message 9 "`%s' matched (<= lpartlen 7), result `%s'."
6154 mid-or-mail result))
6155 (when (>= lpartlen 12)
6156 (gnus-message 9 "`%s' matched (>= lpartlen 12)" mid-or-mail)
6157 (cond
6158 ((string-match "[0-9][^0-9]+[0-9].*@" mid-or-mail)
6159 ;; Long local part should contain realname if e-mail address,
6160 ;; too many digits: message-id.
6161 ;; $score -= 5.0 + 0.1 * $local_part_len;
6162 (setq rate (* -1.0 (+ 5.0 (* 0.1 lpartlen))))
6163 (setq result (+ result rate))
6164 (gnus-message
6165 9 "Many digits in `%s', rate `%s', result `%s'."
6166 mid-or-mail rate result))
6167 ((string-match "[^aeiouy][^aeiouy][^aeiouy][^aeiouy]+.*\@"
6168 mid-or-mail)
6169 ;; Too few vowels [^aeiouy]{4,}.*\@
6170 (setq result (+ result -5.0))
6171 (gnus-message
6172 9 "Few vowels in `%s', rate `%s', result `%s'."
6173 mid-or-mail -5.0 result))
6174 (t
6175 (setq result (+ result 5.0))
6176 (gnus-message
6177 9 "`%s', rate `%s', result `%s'." mid-or-mail 5.0 result)))))
6178 (gnus-message 8 "`%s': Final rate is `%s'." mid-or-mail result)
6179 ;; Maybe we should make this a customizable alist: (condition . 'result)
6180 (cond
6181 ((symbolp result) result)
6182 ;; Now convert number into proper results:
6183 ((< result -10.0) 'mid)
6184 ((> result 10.0) 'mail)
6185 (t 'ask))))
6186
6187(defun gnus-button-handle-mid-or-mail (mid-or-mail)
6188 (let* ((pref gnus-button-prefer-mid-or-mail) guessed
6189 (url-mid (concat "news" ":" mid-or-mail))
6190 (url-mailto (concat "mailto" ":" mid-or-mail)))
6191 (gnus-message 9 "mid-or-mail=%s" mid-or-mail)
6192 (when (fboundp pref)
6193 (setq guessed
6194 ;; get rid of surrounding angles...
6195 (funcall pref
6196 (gnus-replace-in-string mid-or-mail "^<\\|>$" "")))
6197 (if (or (eq 'mid guessed) (eq 'mail guessed))
6198 (setq pref guessed)
6199 (setq pref 'ask)))
6200 (if (eq pref 'ask)
6201 (save-window-excursion
6202 (if (y-or-n-p (concat "Is <" mid-or-mail "> a mail address? "))
6203 (setq pref 'mail)
6204 (setq pref 'mid))))
6205 (cond ((eq pref 'mid)
6206 (gnus-message 8 "calling `gnus-button-handle-news' %s" url-mid)
6207 (gnus-button-handle-news url-mid))
6208 ((eq pref 'mail)
6209 (gnus-message 8 "calling `gnus-url-mailto' %s" url-mailto)
6210 (gnus-url-mailto url-mailto))
6211 (t (gnus-message 3 "Invalid string.")))))
6212
6213(defun gnus-button-handle-custom (url)
6214 "Follow a Custom URL."
6215 (customize-apropos (gnus-url-unhex-string url)))
6216
6217(defvar gnus-button-handle-describe-prefix "^\\(C-h\\|<?[Ff]1>?\\)")
6218
6219;; FIXME: Maybe we should merge some of the functions that do quite similar
6220;; stuff?
6221
6222(defun gnus-button-handle-describe-function (url)
6223 "Call `describe-function' when pushing the corresponding URL button."
6224 (describe-function
6225 (intern
6226 (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))))
6227
6228(defun gnus-button-handle-describe-variable (url)
6229 "Call `describe-variable' when pushing the corresponding URL button."
6230 (describe-variable
6231 (intern
6232 (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))))
6233
6234(defun gnus-button-handle-symbol (url)
6235"Display help on variable or function.
6236Calls `describe-variable' or `describe-function'."
6237 (let ((sym (intern url)))
6238 (cond
6239 ((fboundp sym) (describe-function sym))
6240 ((boundp sym) (describe-variable sym))
6241 (t (gnus-message 3 "`%s' is not a known function of variable." url)))))
6242
6243(defun gnus-button-handle-describe-key (url)
6244 "Call `describe-key' when pushing the corresponding URL button."
6245 (let* ((key-string
6246 (gnus-replace-in-string url gnus-button-handle-describe-prefix ""))
6247 (keys (ignore-errors (eval `(kbd ,key-string)))))
6248 (if keys
6249 (describe-key keys)
6250 (gnus-message 3 "Invalid key sequence in button: %s" key-string))))
6251
6252(defun gnus-button-handle-apropos (url)
6253 "Call `apropos' when pushing the corresponding URL button."
6254 (apropos (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6255
6256(defun gnus-button-handle-apropos-command (url)
6257 "Call `apropos' when pushing the corresponding URL button."
6258 (apropos-command
6259 (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6260
6261(defun gnus-button-handle-apropos-variable (url)
6262 "Call `apropos' when pushing the corresponding URL button."
6263 (funcall
6264 (if (fboundp 'apropos-variable) 'apropos-variable 'apropos)
6265 (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6266
6267(defun gnus-button-handle-apropos-documentation (url)
6268 "Call `apropos' when pushing the corresponding URL button."
6269 (funcall
6270 (if (fboundp 'apropos-documentation) 'apropos-documentation 'apropos)
6271 (gnus-replace-in-string url gnus-button-handle-describe-prefix "")))
6272
6273(defun gnus-button-handle-library (url)
6274 "Call `locate-library' when pushing the corresponding URL button."
6275 (gnus-message 9 "url=`%s'" url)
6276 (let* ((lib (locate-library url))
6277 (file (gnus-replace-in-string (or lib "") "\.elc" ".el")))
6278 (if (not lib)
6279 (gnus-message 1 "Cannot locale library `%s'." url)
6280 (find-file-read-only file))))
6281
6282(defun gnus-button-handle-ctan (url)
6283 "Call `browse-url' when pushing a CTAN URL button."
6284 (funcall
6285 gnus-button-ctan-handler
6286 (concat
6287 gnus-ctan-url
6288 (gnus-replace-in-string url gnus-button-handle-ctan-bogus-regexp ""))))
6289
6290(defcustom gnus-button-tex-level 5
6291 "*Integer that says how many TeX-related buttons Gnus will show.
6292The higher the number, the more buttons will appear and the more false
6293positives are possible. Note that you can set this variable local to
6294specific groups. Setting it higher in TeX groups is probably a good idea.
6295See Info node `(gnus)Group Parameters' and the variable `gnus-parameters' on
6296how to set variables in specific groups."
bf247b6e 6297 :version "22.1"
23f87bed
MB
6298 :group 'gnus-article-buttons
6299 :link '(custom-manual "(gnus)Group Parameters")
6300 :type 'integer)
6301
6302(defcustom gnus-button-man-level 5
6303 "*Integer that says how many man-related buttons Gnus will show.
6304The higher the number, the more buttons will appear and the more false
6305positives are possible. Note that you can set this variable local to
6306specific groups. Setting it higher in Unix groups is probably a good idea.
6307See Info node `(gnus)Group Parameters' and the variable `gnus-parameters' on
6308how to set variables in specific groups."
bf247b6e 6309 :version "22.1"
23f87bed
MB
6310 :group 'gnus-article-buttons
6311 :link '(custom-manual "(gnus)Group Parameters")
6312 :type 'integer)
6313
6314(defcustom gnus-button-emacs-level 5
6315 "*Integer that says how many emacs-related buttons Gnus will show.
6316The higher the number, the more buttons will appear and the more false
6317positives are possible. Note that you can set this variable local to
6318specific groups. Setting it higher in Emacs or Gnus related groups is
6319probably a good idea. See Info node `(gnus)Group Parameters' and the variable
6320`gnus-parameters' on how to set variables in specific groups."
bf247b6e 6321 :version "22.1"
23f87bed
MB
6322 :group 'gnus-article-buttons
6323 :link '(custom-manual "(gnus)Group Parameters")
6324 :type 'integer)
6325
6326(defcustom gnus-button-message-level 5
6327 "*Integer that says how many buttons for news or mail messages will appear.
6328The higher the number, the more buttons will appear and the more false
6329positives are possible."
6330 ;; mail addresses, MIDs, URLs for news, ...
bf247b6e 6331 :version "22.1"
23f87bed
MB
6332 :group 'gnus-article-buttons
6333 :type 'integer)
6334
6335(defcustom gnus-button-browse-level 5
6336 "*Integer that says how many buttons for browsing will appear.
6337The higher the number, the more buttons will appear and the more false
6338positives are possible."
6339 ;; stuff handled by `browse-url' or `gnus-button-embedded-url'
bf247b6e 6340 :version "22.1"
23f87bed
MB
6341 :group 'gnus-article-buttons
6342 :type 'integer)
6343
eec82323 6344(defcustom gnus-button-alist
23f87bed
MB
6345 '(("<\\(url:[>\n\t ]*?\\)?\\(nntp\\|news\\):[>\n\t ]*\\([^>\n\t ]*@[^>\n\t ]*\\)>"
6346 0 (>= gnus-button-message-level 0) gnus-button-handle-news 3)
97f78c9b
MB
6347 ((concat "\\b\\(nntp\\|news\\):\\("
6348 gnus-button-valid-localpart-regexp "@[a-z0-9.-]+[a-z]\\)")
6349 0 t gnus-button-handle-news 2)
23f87bed
MB
6350 ("\\(\\b<\\(url:[>\n\t ]*\\)?\\(nntp\\|news\\):[>\n\t ]*\\(//\\)?\\([^>\n\t ]*\\)>\\)"
6351 1 (>= gnus-button-message-level 0) gnus-button-fetch-group 5)
6352 ("\\b\\(nntp\\|news\\):\\(//\\)?\\([^'\">\n\t ]+\\)"
6353 0 (>= gnus-button-message-level 0) gnus-button-fetch-group 3)
6354 ;; RFC 2392 (Don't allow `/' in domain part --> CID)
6355 ("\\bmid:\\(//\\)?\\([^'\">\n\t ]+@[^'\">\n\t /]+\\)"
6356 0 (>= gnus-button-message-level 0) gnus-button-message-id 2)
6357 ("\\bin\\( +article\\| +message\\)? +\\(<\\([^\n @<>]+@[^\n @<>]+\\)>\\)"
6358 2 (>= gnus-button-message-level 0) gnus-button-message-id 3)
6359 ("\\(<URL: *\\)mailto: *\\([^> \n\t]+\\)>"
6360 0 (>= gnus-button-message-level 0) gnus-url-mailto 2)
6361 ;; RFC 2368 (The mailto URL scheme)
531e5812 6362 ("\\bmailto:\\([-a-z.@_+0-9%=?&/]+\\)"
23f87bed
MB
6363 0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
6364 ("\\bmailto:\\([^ \n\t]+\\)"
6365 0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
6366 ;; CTAN
6367 ((concat "\\bCTAN:[ \t\n]?[^>)!;:,'\n\t ]*\\("
6368 gnus-button-ctan-directory-regexp
6369 "[^][>)!;:,'\n\t ]+\\)")
6370 0 (>= gnus-button-tex-level 1) gnus-button-handle-ctan 1)
6371 ((concat "\\btex-archive/\\("
6372 gnus-button-ctan-directory-regexp
6373 "/[-_.a-z0-9/]+[-_./a-z0-9]+[/a-z0-9]\\)")
6374 1 (>= gnus-button-tex-level 6) gnus-button-handle-ctan 1)
6375 ((concat
6376 "\\b\\("
6377 gnus-button-ctan-directory-regexp
6378 "/[-_.a-z0-9]+/[-_./a-z0-9]+[/a-z0-9]\\)")
6379 1 (>= gnus-button-tex-level 8) gnus-button-handle-ctan 1)
6380 ;; This is info (home-grown style) <info://foo/bar+baz>
6381 ("\\binfo://\\([^'\">\n\t ]+\\)"
6382 0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 1)
6383 ;; Info GNOME style <info:foo#bar_baz>
6384 ("\\binfo:\\([^('\n\t\r \"><][^'\n\t\r \"><]*\\)"
6385 0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url-gnome 1)
6386 ;; Info KDE style <info:(foo)bar baz>
6387 ("<\\(info:\\(([^)]+)[^>\n\r]*\\)\\)>"
6388 1 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url-kde 2)
6389 ("\\((Info-goto-node\\|(info\\)[ \t\n]*\\(\"[^\"]*\"\\))" 0
6390 (>= gnus-button-emacs-level 1) gnus-button-handle-info-url 2)
6391 ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+i[ \t\n]+d?[ \t\n]?m[ \t\n]+\\([^ ]+ ?[^ ]+\\)[ \t\n]+RET"
6392 ;; Info links like `C-h i d m CC Mode RET'
6393 0 (>= gnus-button-emacs-level 1) gnus-button-handle-info-keystrokes 2)
6394 ;; This is custom
6395 ("\\bcustom:\\(//\\)?\\([^'\">\n\t ]+\\)"
6396 0 (>= gnus-button-emacs-level 5) gnus-button-handle-custom 2)
6397 ("M-x[ \t\n]customize-[^ ]+[ \t\n]RET[ \t\n]\\([^ ]+\\)[ \t\n]RET" 0
6398 (>= gnus-button-emacs-level 1) gnus-button-handle-custom 1)
6399 ;; Emacs help commands
6400 ("M-x[ \t\n]+apropos[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6401 ;; regexp doesn't match arguments containing ` '.
6402 0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos 1)
6403 ("M-x[ \t\n]+apropos-command[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6404 0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-command 1)
6405 ("M-x[ \t\n]+apropos-variable[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6406 0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-variable 1)
6407 ("M-x[ \t\n]+apropos-documentation[ \t\n]+RET[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6408 0 (>= gnus-button-emacs-level 1) gnus-button-handle-apropos-documentation 1)
6409 ;; The following entries may lead to many false positives so don't enable
531e5812
MB
6410 ;; them by default (use a high button level).
6411 ("/\\([a-z][-a-z0-9]+\\.el\\)\\>[^.?]"
6412 ;; Exclude [.?] for URLs in gmane.emacs.cvs
23f87bed
MB
6413 1 (>= gnus-button-emacs-level 8) gnus-button-handle-library 1)
6414 ("`\\([a-z][-a-z0-9]+\\.el\\)'"
6415 1 (>= gnus-button-emacs-level 8) gnus-button-handle-library 1)
6416 ("`\\([a-z][a-z0-9]+-[a-z]+-[-a-z]+\\|\\(gnus\\|message\\)-[-a-z]+\\)'"
6417 0 (>= gnus-button-emacs-level 8) gnus-button-handle-symbol 1)
6418 ("`\\([a-z][a-z0-9]+-[a-z]+\\)'"
6419 0 (>= gnus-button-emacs-level 9) gnus-button-handle-symbol 1)
6420 ("(setq[ \t\n]+\\([a-z][a-z0-9]+-[-a-z0-9]+\\)[ \t\n]+.+)"
6421 1 (>= gnus-button-emacs-level 7) gnus-button-handle-describe-variable 1)
6422 ("\\bM-x[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6423 1 (>= gnus-button-emacs-level 7) gnus-button-handle-describe-function 1)
6424 ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+f[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6425 0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-function 2)
6426 ("\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+v[ \t\n]+\\([^ \t\n]+\\)[ \t\n]+RET"
6427 0 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-variable 2)
6428 ("`\\(\\b\\(C-h\\|<?[Ff]1>?\\)[ \t\n]+k[ \t\n]+\\([^']+\\)\\)'"
6429 ;; Unlike the other regexps we really have to require quoting
6430 ;; here to determine where it ends.
6431 1 (>= gnus-button-emacs-level 1) gnus-button-handle-describe-key 3)
6432 ;; This is how URLs _should_ be embedded in text (RFC 1738, RFC 2396)...
6433 ("<URL: *\\([^<>]*\\)>"
6434 1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
6435 ;; RFC 2396 (2.4.3., delims) ...
6436 ("\"URL: *\\([^\"]*\\)\""
6437 1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
6438 ;; RFC 2396 (2.4.3., delims) ...
6439 ("\"URL: *\\([^\"]*\\)\""
6440 1 (>= gnus-button-browse-level 0) gnus-button-embedded-url 1)
eec82323 6441 ;; Raw URLs.
23f87bed
MB
6442 (gnus-button-url-regexp
6443 0 (>= gnus-button-browse-level 0) browse-url 0)
6444 ;; man pages
531e5812 6445 ("\\b\\([a-z][a-z]+([1-9])\\)\\W"
23f87bed
MB
6446 0 (and (>= gnus-button-man-level 1) (< gnus-button-man-level 3))
6447 gnus-button-handle-man 1)
6448 ;; more man pages: resolv.conf(5), iso_8859-1(7), xterm(1x)
531e5812 6449 ("\\b\\([a-z][-_.a-z0-9]+([1-9])\\)\\W"
23f87bed
MB
6450 0 (and (>= gnus-button-man-level 3) (< gnus-button-man-level 5))
6451 gnus-button-handle-man 1)
6452 ;; even more: Apache::PerlRun(3pm), PDL::IO::FastRaw(3pm),
6453 ;; SoWWWAnchor(3iv), XSelectInput(3X11), X(1), X(7)
531e5812 6454 ("\\b\\(\\(?:[a-z][-+_.:a-z0-9]+([1-9][X1a-z]*)\\)\\|\\b\\(?:X([1-9])\\)\\)\\W"
23f87bed
MB
6455 0 (>= gnus-button-man-level 5) gnus-button-handle-man 1)
6456 ;; MID or mail: To avoid too many false positives we don't try to catch
6457 ;; all kind of allowed MIDs or mail addresses. Domain part must contain
6458 ;; at least one dot. TLD must contain two or three chars or be a know TLD
6459 ;; (info|name|...). Put this entry near the _end_ of `gnus-button-alist'
6460 ;; so that non-ambiguous entries (see above) match first.
6461 (gnus-button-mid-or-mail-regexp
6462 0 (>= gnus-button-message-level 5) gnus-button-handle-mid-or-mail 1))
6748645f 6463 "*Alist of regexps matching buttons in article bodies.
eec82323
LMI
6464
6465Each entry has the form (REGEXP BUTTON FORM CALLBACK PAR...), where
23f87bed
MB
6466REGEXP: is the string (case insensitive) matching text around the button (can
6467also be Lisp expression evaluating to a string),
eec82323 6468BUTTON: is the number of the regexp grouping actually matching the button,
4e7d0221 6469FORM: is a Lisp expression which must eval to true for the button to
eec82323
LMI
6470be added,
6471CALLBACK: is the function to call when the user push this button, and each
6472PAR: is a number of a regexp grouping whose text will be passed to CALLBACK.
6473
6474CALLBACK can also be a variable, in that case the value of that
6475variable it the real callback function."
6476 :group 'gnus-article-buttons
23f87bed 6477 :type '(repeat (list (choice regexp variable sexp)
eec82323
LMI
6478 (integer :tag "Button")
6479 (sexp :tag "Form")
6480 (function :tag "Callback")
6481 (repeat :tag "Par"
6482 :inline t
6483 (integer :tag "Regexp group")))))
6484
6485(defcustom gnus-header-button-alist
23f87bed
MB
6486 '(("^\\(References\\|Message-I[Dd]\\|^In-Reply-To\\):" "<[^<>]+>"
6487 0 (>= gnus-button-message-level 0) gnus-button-message-id 0)
6488 ("^\\(From\\|Reply-To\\):" ": *\\(.+\\)$"
6489 1 (>= gnus-button-message-level 0) gnus-button-reply 1)
eec82323 6490 ("^\\(Cc\\|To\\):" "[^ \t\n<>,()\"]+@[^ \t\n<>,()\"]+"
fa9a04e1 6491 0 (>= gnus-button-message-level 0) gnus-msg-mail 0)
23f87bed
MB
6492 ("^X-[Uu][Rr][Ll]:" gnus-button-url-regexp
6493 0 (>= gnus-button-browse-level 0) browse-url 0)
6494 ("^Subject:" gnus-button-url-regexp
6495 0 (>= gnus-button-browse-level 0) browse-url 0)
6496 ("^[^:]+:" gnus-button-url-regexp
6497 0 (>= gnus-button-browse-level 0) browse-url 0)
531e5812 6498 ("^[^:]+:" "\\bmailto:\\([-a-z.@_+0-9%=?&/]+\\)"
23f87bed
MB
6499 0 (>= gnus-button-message-level 0) gnus-url-mailto 1)
6500 ("^[^:]+:" "\\(<\\(url: \\)?\\(nntp\\|news\\):\\([^>\n ]*\\)>\\)"
6501 1 (>= gnus-button-message-level 0) gnus-button-message-id 4))
6748645f 6502 "*Alist of headers and regexps to match buttons in article heads.
eec82323
LMI
6503
6504This alist is very similar to `gnus-button-alist', except that each
6505alist has an additional HEADER element first in each entry:
6506
6507\(HEADER REGEXP BUTTON FORM CALLBACK PAR)
6508
6509HEADER is a regexp to match a header. For a fuller explanation, see
6510`gnus-button-alist'."
6511 :group 'gnus-article-buttons
6512 :group 'gnus-article-headers
6513 :type '(repeat (list (regexp :tag "Header")
23f87bed 6514 (choice regexp variable)
eec82323
LMI
6515 (integer :tag "Button")
6516 (sexp :tag "Form")
6517 (function :tag "Callback")
6518 (repeat :tag "Par"
6519 :inline t
6520 (integer :tag "Regexp group")))))
6521
6522(defvar gnus-button-regexp nil)
6523(defvar gnus-button-marker-list nil)
6524;; Regexp matching any of the regexps from `gnus-button-alist'.
6525
6526(defvar gnus-button-last nil)
6527;; The value of `gnus-button-alist' when `gnus-button-regexp' was build.
6528
6529;;; Commands:
6530
6531(defun gnus-article-push-button (event)
6532 "Check text under the mouse pointer for a callback function.
6533If the text under the mouse pointer has a `gnus-callback' property,
6534call it with the value of the `gnus-data' text property."
6535 (interactive "e")
6536 (set-buffer (window-buffer (posn-window (event-start event))))
6537 (let* ((pos (posn-point (event-start event)))
23f87bed 6538 (data (get-text-property pos 'gnus-data))
eec82323 6539 (fun (get-text-property pos 'gnus-callback)))
6748645f 6540 (goto-char pos)
eec82323
LMI
6541 (when fun
6542 (funcall fun data))))
6543
6544(defun gnus-article-press-button ()
6545 "Check text at point for a callback function.
6546If the text at point has a `gnus-callback' property,
6547call it with the value of the `gnus-data' text property."
6548 (interactive)
23f87bed
MB
6549 (let ((data (get-text-property (point) 'gnus-data))
6550 (fun (get-text-property (point) 'gnus-callback)))
eec82323
LMI
6551 (when fun
6552 (funcall fun data))))
6553
eec82323
LMI
6554(defun gnus-article-highlight (&optional force)
6555 "Highlight current article.
6556This function calls `gnus-article-highlight-headers',
6557`gnus-article-highlight-citation',
6558`gnus-article-highlight-signature', and `gnus-article-add-buttons' to
6559do the highlighting. See the documentation for those functions."
6560 (interactive (list 'force))
6561 (gnus-article-highlight-headers)
6562 (gnus-article-highlight-citation force)
6563 (gnus-article-highlight-signature)
6564 (gnus-article-add-buttons force)
6565 (gnus-article-add-buttons-to-head))
6566
6567(defun gnus-article-highlight-some (&optional force)
6568 "Highlight current article.
6569This function calls `gnus-article-highlight-headers',
6570`gnus-article-highlight-signature', and `gnus-article-add-buttons' to
6571do the highlighting. See the documentation for those functions."
6572 (interactive (list 'force))
6573 (gnus-article-highlight-headers)
6574 (gnus-article-highlight-signature)
6575 (gnus-article-add-buttons))
6576
6577(defun gnus-article-highlight-headers ()
6578 "Highlight article headers as specified by `gnus-header-face-alist'."
6579 (interactive)
6580 (save-excursion
6581 (set-buffer gnus-article-buffer)
6582 (save-restriction
6583 (let ((alist gnus-header-face-alist)
4e7d0221 6584 (inhibit-read-only t)
eec82323
LMI
6585 (case-fold-search t)
6586 (inhibit-point-motion-hooks t)
6587 entry regexp header-face field-face from hpoints fpoints)
16409b0b 6588 (article-narrow-to-head)
eec82323
LMI
6589 (while (setq entry (pop alist))
6590 (goto-char (point-min))
6591 (setq regexp (concat "^\\("
6592 (if (string-equal "" (nth 0 entry))
6593 "[^\t ]"
6594 (nth 0 entry))
6595 "\\)")
6596 header-face (nth 1 entry)
6597 field-face (nth 2 entry))
6598 (while (and (re-search-forward regexp nil t)
6599 (not (eobp)))
6600 (beginning-of-line)
6601 (setq from (point))
6602 (unless (search-forward ":" nil t)
6603 (forward-char 1))
6604 (when (and header-face
6605 (not (memq (point) hpoints)))
6606 (push (point) hpoints)
6607 (gnus-put-text-property from (point) 'face header-face))
6608 (when (and field-face
6609 (not (memq (setq from (point)) fpoints)))
6610 (push from fpoints)
6611 (if (re-search-forward "^[^ \t]" nil t)
6612 (forward-char -2)
6613 (goto-char (point-max)))
6614 (gnus-put-text-property from (point) 'face field-face))))))))
6615
6616(defun gnus-article-highlight-signature ()
6617 "Highlight the signature in an article.
6618It does this by highlighting everything after
0f49874b 6619`gnus-signature-separator' using the face `gnus-signature'."
eec82323
LMI
6620 (interactive)
6621 (save-excursion
6622 (set-buffer gnus-article-buffer)
4e7d0221 6623 (let ((inhibit-read-only t)
eec82323
LMI
6624 (inhibit-point-motion-hooks t))
6625 (save-restriction
6626 (when (and gnus-signature-face
6627 (gnus-article-narrow-to-signature))
6628 (gnus-overlay-put (gnus-make-overlay (point-min) (point-max))
6629 'face gnus-signature-face)
6630 (widen)
6631 (gnus-article-search-signature)
6632 (let ((start (match-beginning 0))
6633 (end (set-marker (make-marker) (1+ (match-end 0)))))
6634 (gnus-article-add-button start (1- end) 'gnus-signature-toggle
6635 end)))))))
6636
6637(defun gnus-button-in-region-p (b e prop)
6638 "Say whether PROP exists in the region."
6639 (text-property-not-all b e prop nil))
6640
6641(defun gnus-article-add-buttons (&optional force)
6642 "Find external references in the article and make buttons of them.
6643\"External references\" are things like Message-IDs and URLs, as
6644specified by `gnus-button-alist'."
6645 (interactive (list 'force))
6646 (save-excursion
6647 (set-buffer gnus-article-buffer)
4e7d0221 6648 (let ((inhibit-read-only t)
eec82323
LMI
6649 (inhibit-point-motion-hooks t)
6650 (case-fold-search t)
6651 (alist gnus-button-alist)
6652 beg entry regexp)
6653 ;; Remove all old markers.
16409b0b 6654 (let (marker entry new-list)
eec82323 6655 (while (setq marker (pop gnus-button-marker-list))
16409b0b
GM
6656 (if (or (< marker (point-min)) (>= marker (point-max)))
6657 (push marker new-list)
6658 (goto-char marker)
6659 (when (setq entry (gnus-button-entry))
6660 (put-text-property (match-beginning (nth 1 entry))
6661 (match-end (nth 1 entry))
6662 'gnus-callback nil))
6663 (set-marker marker nil)))
6664 (setq gnus-button-marker-list new-list))
eec82323 6665 ;; We skip the headers.
16409b0b 6666 (article-goto-body)
eec82323
LMI
6667 (setq beg (point))
6668 (while (setq entry (pop alist))
23f87bed 6669 (setq regexp (eval (car entry)))
eec82323
LMI
6670 (goto-char beg)
6671 (while (re-search-forward regexp nil t)
6672 (let* ((start (and entry (match-beginning (nth 1 entry))))
6673 (end (and entry (match-end (nth 1 entry))))
6674 (from (match-beginning 0)))
6675 (when (and (or (eq t (nth 2 entry))
6676 (eval (nth 2 entry)))
6677 (not (gnus-button-in-region-p
6678 start end 'gnus-callback)))
6679 ;; That optional form returned non-nil, so we add the
6680 ;; button.
6681 (gnus-article-add-button
6682 start end 'gnus-button-push
6683 (car (push (set-marker (make-marker) from)
6684 gnus-button-marker-list))))))))))
6685
6686;; Add buttons to the head of an article.
6687(defun gnus-article-add-buttons-to-head ()
6688 "Add buttons to the head of the article."
6689 (interactive)
6690 (save-excursion
6691 (set-buffer gnus-article-buffer)
16409b0b 6692 (save-restriction
4e7d0221 6693 (let ((inhibit-read-only t)
16409b0b
GM
6694 (inhibit-point-motion-hooks t)
6695 (case-fold-search t)
6696 (alist gnus-header-button-alist)
6697 entry beg end)
6698 (article-narrow-to-head)
6699 (while alist
6700 ;; Each alist entry.
6701 (setq entry (car alist)
6702 alist (cdr alist))
6703 (goto-char (point-min))
6704 (while (re-search-forward (car entry) nil t)
6705 ;; Each header matching the entry.
6706 (setq beg (match-beginning 0))
6707 (setq end (or (and (re-search-forward "^[^ \t]" nil t)
6708 (match-beginning 0))
6709 (point-max)))
6710 (goto-char beg)
23f87bed 6711 (while (re-search-forward (eval (nth 1 entry)) end t)
16409b0b
GM
6712 ;; Each match within a header.
6713 (let* ((entry (cdr entry))
6714 (start (match-beginning (nth 1 entry)))
6715 (end (match-end (nth 1 entry)))
6716 (form (nth 2 entry)))
6717 (goto-char (match-end 0))
6718 (when (eval form)
6719 (gnus-article-add-button
6720 start end (nth 3 entry)
6721 (buffer-substring (match-beginning (nth 4 entry))
6722 (match-end (nth 4 entry)))))))
6723 (goto-char end)))))))
eec82323
LMI
6724
6725;;; External functions:
6726
6727(defun gnus-article-add-button (from to fun &optional data)
6728 "Create a button between FROM and TO with callback FUN and data DATA."
6729 (when gnus-article-button-face
6730 (gnus-overlay-put (gnus-make-overlay from to)
6731 'face gnus-article-button-face))
6732 (gnus-add-text-properties
6733 from to
6734 (nconc (and gnus-article-mouse-face
6735 (list gnus-mouse-face-prop gnus-article-mouse-face))
6736 (list 'gnus-callback fun)
16409b0b
GM
6737 (and data (list 'gnus-data data))))
6738 (widget-convert-button 'link from to :action 'gnus-widget-press-button
6739 :button-keymap gnus-widget-button-keymap))
eec82323
LMI
6740
6741;;; Internal functions:
6742
a8151ef7
LMI
6743(defun gnus-article-set-globals ()
6744 (save-excursion
6745 (set-buffer gnus-summary-buffer)
6746 (gnus-set-global-variables)))
6747
eec82323
LMI
6748(defun gnus-signature-toggle (end)
6749 (save-excursion
6750 (set-buffer gnus-article-buffer)
4e7d0221 6751 (let ((inhibit-read-only t)
eec82323 6752 (inhibit-point-motion-hooks t))
520aa572 6753 (if (text-property-any end (point-max) 'article-type 'signature)
23f87bed
MB
6754 (progn
6755 (gnus-delete-wash-type 'signature)
6756 (gnus-remove-text-properties-when
6757 'article-type 'signature end (point-max)
6758 (cons 'article-type (cons 'signature
6759 gnus-hidden-properties))))
6760 (gnus-add-wash-type 'signature)
520aa572
SZ
6761 (gnus-add-text-properties-when
6762 'article-type nil end (point-max)
6763 (cons 'article-type (cons 'signature
23f87bed
MB
6764 gnus-hidden-properties)))))
6765 (let ((gnus-article-mime-handle-alist-1 gnus-article-mime-handle-alist))
6766 (gnus-set-mode-line 'article))))
eec82323
LMI
6767
6768(defun gnus-button-entry ()
6769 ;; Return the first entry in `gnus-button-alist' matching this place.
6770 (let ((alist gnus-button-alist)
6771 (entry nil))
6772 (while alist
6773 (setq entry (pop alist))
23f87bed 6774 (if (looking-at (eval (car entry)))
eec82323
LMI
6775 (setq alist nil)
6776 (setq entry nil)))
6777 entry))
6778
6779(defun gnus-button-push (marker)
6780 ;; Push button starting at MARKER.
6781 (save-excursion
eec82323
LMI
6782 (goto-char marker)
6783 (let* ((entry (gnus-button-entry))
6784 (inhibit-point-motion-hooks t)
6785 (fun (nth 3 entry))
6786 (args (mapcar (lambda (group)
6787 (let ((string (match-string group)))
6788 (gnus-set-text-properties
6789 0 (length string) nil string)
6790 string))
6791 (nthcdr 4 entry))))
6792 (cond
6793 ((fboundp fun)
6794 (apply fun args))
6795 ((and (boundp fun)
6796 (fboundp (symbol-value fun)))
6797 (apply (symbol-value fun) args))
6798 (t
6799 (gnus-message 1 "You must define `%S' to use this button"
6800 (cons fun args)))))))
6801
23f87bed 6802(defun gnus-parse-news-url (url)
82a8ad04 6803 (let (scheme server port group message-id articles)
23f87bed
MB
6804 (with-temp-buffer
6805 (insert url)
6806 (goto-char (point-min))
6807 (when (looking-at "\\([A-Za-z]+\\):")
6808 (setq scheme (match-string 1))
6809 (goto-char (match-end 0)))
82a8ad04 6810 (when (looking-at "//\\([^:/]+\\)\\(:?\\)\\([0-9]+\\)?/")
23f87bed 6811 (setq server (match-string 1))
82a8ad04
MB
6812 (setq port (if (stringp (match-string 3))
6813 (string-to-number (match-string 3))
6814 (match-string 3)))
23f87bed
MB
6815 (goto-char (match-end 0)))
6816
6817 (cond
6818 ((looking-at "\\(.*@.*\\)")
6819 (setq message-id (match-string 1)))
6820 ((looking-at "\\([^/]+\\)/\\([-0-9]+\\)")
6821 (setq group (match-string 1)
6822 articles (split-string (match-string 2) "-")))
6823 ((looking-at "\\([^/]+\\)/?")
6824 (setq group (match-string 1)))
6825 (t
6826 (error "Unknown news URL syntax"))))
82a8ad04 6827 (list scheme server port group message-id articles)))
23f87bed
MB
6828
6829(defun gnus-button-handle-news (url)
6830 "Fetch a news URL."
82a8ad04 6831 (destructuring-bind (scheme server port group message-id articles)
23f87bed
MB
6832 (gnus-parse-news-url url)
6833 (cond
6834 (message-id
6835 (save-excursion
6836 (set-buffer gnus-summary-buffer)
6837 (if server
ff4d3926
MB
6838 (let ((gnus-refer-article-method
6839 (nconc (list (list 'nntp server))
6840 gnus-refer-article-method))
82a8ad04 6841 (nntp-port-number (or port "nntp")))
ff4d3926
MB
6842 (gnus-message 7 "Fetching %s with %s"
6843 message-id gnus-refer-article-method)
23f87bed
MB
6844 (gnus-summary-refer-article message-id))
6845 (gnus-summary-refer-article message-id))))
6846 (group
6847 (gnus-button-fetch-group url)))))
6848
6849(defun gnus-button-handle-man (url)
6850 "Fetch a man page."
531e5812
MB
6851 (gnus-message 9 "`%s' `%s'" gnus-button-man-handler url)
6852 (when (eq gnus-button-man-handler 'woman)
6853 (setq url (gnus-replace-in-string url "([1-9][X1a-z]*).*\\'" "")))
6854 (gnus-message 9 "`%s' `%s'" gnus-button-man-handler url)
23f87bed
MB
6855 (funcall gnus-button-man-handler url))
6856
6857(defun gnus-button-handle-info-url (url)
6858 "Fetch an info URL."
6859 (setq url (mm-subst-char-in-string ?+ ?\ url))
6860 (cond
6861 ((string-match "^\\([^:/]+\\)?/\\(.*\\)" url)
6862 (gnus-info-find-node
6863 (concat "(" (or (gnus-url-unhex-string (match-string 1 url))
6864 "Gnus")
6865 ")" (gnus-url-unhex-string (match-string 2 url)))))
6866 ((string-match "([^)\"]+)[^\"]+" url)
6867 (setq url
6868 (gnus-replace-in-string
6869 (gnus-replace-in-string url "[\n\t ]+" " ") "\"" ""))
6870 (gnus-info-find-node url))
6871 (t (error "Can't parse %s" url))))
6872
6873(defun gnus-button-handle-info-url-gnome (url)
6874 "Fetch GNOME style info URL."
6875 (setq url (mm-subst-char-in-string ?_ ?\ url))
6876 (if (string-match "\\([^#]+\\)#?\\(.*\\)" url)
6877 (gnus-info-find-node
6878 (concat "("
bf247b6e 6879 (gnus-url-unhex-string
23f87bed
MB
6880 (match-string 1 url))
6881 ")"
bf247b6e 6882 (or (gnus-url-unhex-string
23f87bed
MB
6883 (match-string 2 url))
6884 "Top")))
6885 (error "Can't parse %s" url)))
6886
6887(defun gnus-button-handle-info-url-kde (url)
6888 "Fetch KDE style info URL."
6889 (gnus-info-find-node (gnus-url-unhex-string url)))
6890
6891(defun gnus-button-handle-info-keystrokes (url)
6892 "Call `info' when pushing the corresponding URL button."
6893 ;; For links like `C-h i d m gnus RET', `C-h i d m CC Mode RET'.
6894 (info)
6895 (Info-directory)
6896 (Info-menu url))
6897
eec82323
LMI
6898(defun gnus-button-message-id (message-id)
6899 "Fetch MESSAGE-ID."
6900 (save-excursion
6901 (set-buffer gnus-summary-buffer)
6902 (gnus-summary-refer-article message-id)))
6903
6904(defun gnus-button-fetch-group (address)
6905 "Fetch GROUP specified by ADDRESS."
6906 (if (not (string-match "[:/]" address))
6907 ;; This is just a simple group url.
6908 (gnus-group-read-ephemeral-group address gnus-select-method)
23f87bed
MB
6909 (if (not
6910 (string-match
6911 "^\\([^:/]+\\)\\(:\\([^/]+\\)\\)?/\\([^/]+\\)\\(/\\([0-9]+\\)\\)?"
6912 address))
eec82323
LMI
6913 (error "Can't parse %s" address)
6914 (gnus-group-read-ephemeral-group
6915 (match-string 4 address)
6916 `(nntp ,(match-string 1 address)
6917 (nntp-address ,(match-string 1 address))
6918 (nntp-port-number ,(if (match-end 3)
6919 (match-string 3 address)
23f87bed
MB
6920 "nntp")))
6921 nil nil nil
e9bd5782 6922 (and (match-end 6) (list (string-to-number (match-string 6 address))))))))
eec82323 6923
eec82323
LMI
6924(defun gnus-url-parse-query-string (query &optional downcase)
6925 (let (retval pairs cur key val)
16409b0b 6926 (setq pairs (split-string query "&"))
eec82323
LMI
6927 (while pairs
6928 (setq cur (car pairs)
23f87bed 6929 pairs (cdr pairs))
eec82323 6930 (if (not (string-match "=" cur))
23f87bed
MB
6931 nil ; Grace
6932 (setq key (gnus-url-unhex-string (substring cur 0 (match-beginning 0)))
6933 val (gnus-url-unhex-string (substring cur (match-end 0) nil) t))
6934 (if downcase
6935 (setq key (downcase key)))
6936 (setq cur (assoc key retval))
6937 (if cur
6938 (setcdr cur (cons val (cdr cur)))
6939 (setq retval (cons (list key val) retval)))))
eec82323
LMI
6940 retval))
6941
eec82323
LMI
6942(defun gnus-url-mailto (url)
6943 ;; Send mail to someone
6944 (when (string-match "mailto:/*\\(.*\\)" url)
6945 (setq url (substring url (match-beginning 1) nil)))
6748645f 6946 (let (to args subject func)
23f87bed
MB
6947 (setq args (gnus-url-parse-query-string
6948 (if (string-match "^\\?" url)
6949 (substring url 1)
6950 (if (string-match "^\\([^?]+\\)\\?\\(.*\\)" url)
6951 (concat "to=" (match-string 1 url) "&"
6952 (match-string 2 url))
6953 (concat "to=" url)))
6954 t)
6955 subject (cdr-safe (assoc "subject" args)))
6956 (gnus-msg-mail)
eec82323
LMI
6957 (while args
6958 (setq func (intern-soft (concat "message-goto-" (downcase (caar args)))))
6959 (if (fboundp func)
23f87bed
MB
6960 (funcall func)
6961 (message-position-on-field (caar args)))
6962 (insert (gnus-replace-in-string
6963 (mapconcat 'identity (reverse (cdar args)) ", ")
6964 "\r\n" "\n" t))
eec82323
LMI
6965 (setq args (cdr args)))
6966 (if subject
23f87bed 6967 (message-goto-body)
eec82323
LMI
6968 (message-goto-subject))))
6969
eec82323 6970(defun gnus-button-embedded-url (address)
e0bad764 6971 "Activate ADDRESS with `browse-url'."
1b978bfc 6972 (browse-url (gnus-strip-whitespace address)))
eec82323
LMI
6973
6974;;; Next/prev buttons in the article buffer.
6975
6976(defvar gnus-next-page-line-format "%{%(Next page...%)%}\n")
6977(defvar gnus-prev-page-line-format "%{%(Previous page...%)%}\n")
6978
23f87bed
MB
6979(defvar gnus-prev-page-map
6980 (let ((map (make-sparse-keymap)))
6981 (unless (>= emacs-major-version 21)
6982 ;; XEmacs doesn't care.
6983 (set-keymap-parent map gnus-article-mode-map))
6984 (define-key map gnus-mouse-2 'gnus-button-prev-page)
6985 (define-key map "\r" 'gnus-button-prev-page)
6986 map))
6987
6988(defvar gnus-next-page-map
6989 (let ((map (make-sparse-keymap)))
6990 (unless (>= emacs-major-version 21)
6991 ;; XEmacs doesn't care.
6992 (set-keymap-parent map gnus-article-mode-map))
6993 (define-key map gnus-mouse-2 'gnus-button-next-page)
6994 (define-key map "\r" 'gnus-button-next-page)
6995 map))
eec82323
LMI
6996
6997(defun gnus-insert-prev-page-button ()
23f87bed
MB
6998 (let ((b (point))
6999 (inhibit-read-only t))
eec82323
LMI
7000 (gnus-eval-format
7001 gnus-prev-page-line-format nil
23f87bed
MB
7002 `(,@(gnus-local-map-property gnus-prev-page-map)
7003 gnus-prev t
7004 gnus-callback gnus-article-button-prev-page
7005 article-type annotation))
7006 (widget-convert-button
7007 'link b (if (bolp)
7008 ;; Exclude a newline.
7009 (1- (point))
7010 (point))
7011 :action 'gnus-button-prev-page
7012 :button-keymap gnus-prev-page-map)))
7013
7014(defun gnus-button-next-page (&optional args more-args)
eec82323
LMI
7015 "Go to the next page."
7016 (interactive)
7017 (let ((win (selected-window)))
23f87bed 7018 (select-window (gnus-get-buffer-window gnus-article-buffer t))
eec82323
LMI
7019 (gnus-article-next-page)
7020 (select-window win)))
7021
23f87bed 7022(defun gnus-button-prev-page (&optional args more-args)
eec82323
LMI
7023 "Go to the prev page."
7024 (interactive)
7025 (let ((win (selected-window)))
23f87bed 7026 (select-window (gnus-get-buffer-window gnus-article-buffer t))
eec82323
LMI
7027 (gnus-article-prev-page)
7028 (select-window win)))
7029
7030(defun gnus-insert-next-page-button ()
23f87bed
MB
7031 (let ((b (point))
7032 (inhibit-read-only t))
eec82323 7033 (gnus-eval-format gnus-next-page-line-format nil
23f87bed
MB
7034 `(,@(gnus-local-map-property gnus-next-page-map)
7035 gnus-next t
7036 gnus-callback gnus-article-button-next-page
7037 article-type annotation))
7038 (widget-convert-button
7039 'link b (if (bolp)
7040 ;; Exclude a newline.
7041 (1- (point))
7042 (point))
7043 :action 'gnus-button-next-page
7044 :button-keymap gnus-next-page-map)))
eec82323
LMI
7045
7046(defun gnus-article-button-next-page (arg)
7047 "Go to the next page."
7048 (interactive "P")
7049 (let ((win (selected-window)))
23f87bed 7050 (select-window (gnus-get-buffer-window gnus-article-buffer t))
eec82323
LMI
7051 (gnus-article-next-page)
7052 (select-window win)))
7053
7054(defun gnus-article-button-prev-page (arg)
7055 "Go to the prev page."
7056 (interactive "P")
7057 (let ((win (selected-window)))
23f87bed 7058 (select-window (gnus-get-buffer-window gnus-article-buffer t))
eec82323
LMI
7059 (gnus-article-prev-page)
7060 (select-window win)))
7061
16409b0b
GM
7062(defvar gnus-decode-header-methods
7063 '(mail-decode-encoded-word-region)
7064 "List of methods used to decode headers.
7065
7066This variable is a list of FUNCTION or (REGEXP . FUNCTION). If item
4e7d0221 7067is FUNCTION, FUNCTION will be applied to all newsgroups. If item is a
23f87bed 7068\(REGEXP . FUNCTION), FUNCTION will be only apply to the newsgroups
16409b0b
GM
7069whose names match REGEXP.
7070
7071For example:
8f688cb0 7072\((\"chinese\" . gnus-decode-encoded-word-region-by-guess)
16409b0b
GM
7073 mail-decode-encoded-word-region
7074 (\"chinese\" . rfc1843-decode-region))
7075")
7076
7077(defvar gnus-decode-header-methods-cache nil)
7078
7079(defun gnus-multi-decode-header (start end)
7080 "Apply the functions from `gnus-encoded-word-methods' that match."
7081 (unless (and gnus-decode-header-methods-cache
7082 (eq gnus-newsgroup-name
7083 (car gnus-decode-header-methods-cache)))
7084 (setq gnus-decode-header-methods-cache (list gnus-newsgroup-name))
7085 (mapcar (lambda (x)
7086 (if (symbolp x)
7087 (nconc gnus-decode-header-methods-cache (list x))
7088 (if (and gnus-newsgroup-name
7089 (string-match (car x) gnus-newsgroup-name))
7090 (nconc gnus-decode-header-methods-cache
7091 (list (cdr x))))))
7092 gnus-decode-header-methods))
7093 (let ((xlist gnus-decode-header-methods-cache))
7094 (pop xlist)
7095 (save-restriction
7096 (narrow-to-region start end)
7097 (while xlist
7098 (funcall (pop xlist) (point-min) (point-max))))))
7099
7100;;;
7101;;; Treatment top-level handling.
7102;;;
7103
7104(defun gnus-treat-article (condition &optional part-number total-parts type)
7105 (let ((length (- (point-max) (point-min)))
7106 (alist gnus-treatment-function-alist)
7107 (article-goto-body-goes-to-point-min-p t)
7108 (treated-type
7109 (or (not type)
7110 (catch 'found
7111 (let ((list gnus-article-treat-types))
7112 (while list
7113 (when (string-match (pop list) type)
7114 (throw 'found t)))))))
7115 (highlightp (gnus-visual-p 'article-highlight 'highlight))
7116 val elem)
7117 (gnus-run-hooks 'gnus-part-display-hook)
23f87bed 7118 (dolist (elem alist)
16409b0b
GM
7119 (setq val
7120 (save-excursion
23f87bed
MB
7121 (when (gnus-buffer-live-p gnus-summary-buffer)
7122 (set-buffer gnus-summary-buffer))
16409b0b
GM
7123 (symbol-value (car elem))))
7124 (when (and (or (consp val)
7125 treated-type)
7126 (gnus-treat-predicate val)
7127 (or (not (get (car elem) 'highlight))
7128 highlightp))
7129 (save-restriction
7130 (funcall (cadr elem)))))))
7131
7132;; Dynamic variables.
e0bad764
DL
7133(eval-when-compile
7134 (defvar part-number)
7135 (defvar total-parts)
7136 (defvar type)
7137 (defvar condition)
7138 (defvar length))
7139
16409b0b
GM
7140(defun gnus-treat-predicate (val)
7141 (cond
7142 ((null val)
7143 nil)
23f87bed
MB
7144 (condition
7145 (eq condition val))
16409b0b
GM
7146 ((and (listp val)
7147 (stringp (car val)))
7148 (apply 'gnus-or (mapcar `(lambda (s)
7149 (string-match s ,(or gnus-newsgroup-name "")))
7150 val)))
7151 ((listp val)
7152 (let ((pred (pop val)))
7153 (cond
7154 ((eq pred 'or)
7155 (apply 'gnus-or (mapcar 'gnus-treat-predicate val)))
7156 ((eq pred 'and)
7157 (apply 'gnus-and (mapcar 'gnus-treat-predicate val)))
7158 ((eq pred 'not)
7159 (not (gnus-treat-predicate (car val))))
7160 ((eq pred 'typep)
7161 (equal (car val) type))
7162 (t
7163 (error "%S is not a valid predicate" pred)))))
16409b0b
GM
7164 ((eq val t)
7165 t)
7166 ((eq val 'head)
7167 nil)
7168 ((eq val 'last)
7169 (eq part-number total-parts))
7170 ((numberp val)
7171 (< length val))
7172 (t
7173 (error "%S is not a valid value" val))))
7174
23f87bed
MB
7175(defun gnus-article-encrypt-body (protocol &optional n)
7176 "Encrypt the article body."
7177 (interactive
7178 (list
7179 (or gnus-article-encrypt-protocol
7180 (completing-read "Encrypt protocol: "
7181 gnus-article-encrypt-protocol-alist
7182 nil t))
7183 current-prefix-arg))
7184 (let ((func (cdr (assoc protocol gnus-article-encrypt-protocol-alist))))
7185 (unless func
998a5a95 7186 (error "Can't find the encrypt protocol %s" protocol))
23f87bed
MB
7187 (if (member gnus-newsgroup-name '("nndraft:delayed"
7188 "nndraft:drafts"
7189 "nndraft:queue"))
7190 (error "Can't encrypt the article in group %s"
7191 gnus-newsgroup-name))
7192 (gnus-summary-iterate n
7193 (save-excursion
7194 (set-buffer gnus-summary-buffer)
7195 (let ((mail-parse-charset gnus-newsgroup-charset)
7196 (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
7197 (summary-buffer gnus-summary-buffer)
7198 references point)
7199 (gnus-set-global-variables)
7200 (when (gnus-group-read-only-p)
7201 (error "The current newsgroup does not support article encrypt"))
7202 (gnus-summary-show-article t)
7203 (setq references
7204 (or (mail-header-references gnus-current-headers) ""))
7205 (set-buffer gnus-article-buffer)
7206 (let* ((inhibit-read-only t)
7207 (headers
7208 (mapcar (lambda (field)
7209 (and (save-restriction
7210 (message-narrow-to-head)
7211 (goto-char (point-min))
7212 (search-forward field nil t))
7213 (prog2
7214 (message-narrow-to-field)
7215 (buffer-string)
7216 (delete-region (point-min) (point-max))
7217 (widen))))
7218 '("Content-Type:" "Content-Transfer-Encoding:"
7219 "Content-Disposition:"))))
7220 (message-narrow-to-head)
7221 (message-remove-header "MIME-Version")
7222 (goto-char (point-max))
7223 (setq point (point))
7224 (insert (apply 'concat headers))
7225 (widen)
7226 (narrow-to-region point (point-max))
7227 (let ((message-options message-options))
7228 (message-options-set 'message-sender user-mail-address)
7229 (message-options-set 'message-recipients user-mail-address)
7230 (message-options-set 'message-sign-encrypt 'not)
7231 (funcall func))
7232 (goto-char (point-min))
7233 (insert "MIME-Version: 1.0\n")
7234 (widen)
7235 (gnus-summary-edit-article-done
7236 references nil summary-buffer t))
7237 (when gnus-keep-backlog
7238 (gnus-backlog-remove-article
7239 (car gnus-article-current) (cdr gnus-article-current)))
7240 (save-excursion
7241 (when (get-buffer gnus-original-article-buffer)
7242 (set-buffer gnus-original-article-buffer)
7243 (setq gnus-original-article nil)))
7244 (when gnus-use-cache
7245 (gnus-cache-update-article
7246 (car gnus-article-current) (cdr gnus-article-current))))))))
7247
7248(defvar gnus-mime-security-button-line-format "%{%([[%t:%i]%D]%)%}\n"
7249 "The following specs can be used:
7250%t The security MIME type
7251%i Additional info
7252%d Details
7253%D Details if button is pressed")
7254
7255(defvar gnus-mime-security-button-end-line-format "%{%([[End of %t]%D]%)%}\n"
7256 "The following specs can be used:
7257%t The security MIME type
7258%i Additional info
7259%d Details
7260%D Details if button is pressed")
7261
7262(defvar gnus-mime-security-button-line-format-alist
7263 '((?t gnus-tmp-type ?s)
7264 (?i gnus-tmp-info ?s)
7265 (?d gnus-tmp-details ?s)
7266 (?D gnus-tmp-pressed-details ?s)))
7267
7268(defvar gnus-mime-security-button-map
7269 (let ((map (make-sparse-keymap)))
7270 (unless (>= (string-to-number emacs-version) 21)
7271 (set-keymap-parent map gnus-article-mode-map))
7272 (define-key map gnus-mouse-2 'gnus-article-push-button)
7273 (define-key map "\r" 'gnus-article-press-button)
7274 map))
7275
7276(defvar gnus-mime-security-details-buffer nil)
7277
7278(defvar gnus-mime-security-button-pressed nil)
7279
7280(defvar gnus-mime-security-show-details-inline t
7281 "If non-nil, show details in the article buffer.")
7282
7283(defun gnus-mime-security-verify-or-decrypt (handle)
7284 (mm-remove-parts (cdr handle))
7285 (let ((region (mm-handle-multipart-ctl-parameter handle 'gnus-region))
7286 point (inhibit-read-only t))
7287 (if region
7288 (goto-char (car region)))
7289 (save-restriction
7290 (narrow-to-region (point) (point))
7291 (with-current-buffer (mm-handle-multipart-original-buffer handle)
7292 (let* ((mm-verify-option 'known)
7293 (mm-decrypt-option 'known)
7294 (nparts (mm-possibly-verify-or-decrypt (cdr handle) handle)))
7295 (unless (eq nparts (cdr handle))
7296 (mm-destroy-parts (cdr handle))
7297 (setcdr handle nparts))))
7298 (setq point (point))
7299 (gnus-mime-display-security handle)
7300 (goto-char (point-max)))
7301 (when region
7302 (delete-region (point) (cdr region))
7303 (set-marker (car region) nil)
7304 (set-marker (cdr region) nil))
7305 (goto-char point)))
7306
7307(defun gnus-mime-security-show-details (handle)
7308 (let ((details (mm-handle-multipart-ctl-parameter handle 'gnus-details)))
7309 (if (not details)
7310 (gnus-message 5 "No details.")
7311 (if gnus-mime-security-show-details-inline
7312 (let ((gnus-mime-security-button-pressed
7313 (not (get-text-property (point) 'gnus-mime-details)))
7314 (gnus-mime-security-button-line-format
7315 (get-text-property (point) 'gnus-line-format))
7316 (inhibit-read-only t))
7317 (forward-char -1)
7318 (while (eq (get-text-property (point) 'gnus-line-format)
7319 gnus-mime-security-button-line-format)
7320 (forward-char -1))
7321 (forward-char)
7322 (save-restriction
7323 (narrow-to-region (point) (point))
7324 (gnus-insert-mime-security-button handle))
7325 (delete-region (point)
7326 (or (text-property-not-all
7327 (point) (point-max)
7328 'gnus-line-format
7329 gnus-mime-security-button-line-format)
7330 (point-max))))
7331 ;; Not inlined.
7332 (if (gnus-buffer-live-p gnus-mime-security-details-buffer)
7333 (with-current-buffer gnus-mime-security-details-buffer
7334 (erase-buffer)
7335 t)
7336 (setq gnus-mime-security-details-buffer
7337 (gnus-get-buffer-create "*MIME Security Details*")))
7338 (with-current-buffer gnus-mime-security-details-buffer
7339 (insert details)
7340 (goto-char (point-min)))
7341 (pop-to-buffer gnus-mime-security-details-buffer)))))
7342
7343(defun gnus-mime-security-press-button (handle)
7344 (save-excursion
7345 (if (mm-handle-multipart-ctl-parameter handle 'gnus-info)
7346 (gnus-mime-security-show-details handle)
7347 (gnus-mime-security-verify-or-decrypt handle))))
7348
7349(defun gnus-insert-mime-security-button (handle &optional displayed)
7350 (let* ((protocol (mm-handle-multipart-ctl-parameter handle 'protocol))
7351 (gnus-tmp-type
7352 (concat
7353 (or (nth 2 (assoc protocol mm-verify-function-alist))
7354 (nth 2 (assoc protocol mm-decrypt-function-alist))
7355 "Unknown")
7356 (if (equal (car handle) "multipart/signed")
7357 " Signed" " Encrypted")
7358 " Part"))
7359 (gnus-tmp-info
7360 (or (mm-handle-multipart-ctl-parameter handle 'gnus-info)
7361 "Undecided"))
7362 (gnus-tmp-details
7363 (mm-handle-multipart-ctl-parameter handle 'gnus-details))
7364 gnus-tmp-pressed-details
7365 b e)
7366 (setq gnus-tmp-details
7367 (if gnus-tmp-details
7368 (concat "\n" gnus-tmp-details)
7369 ""))
7370 (setq gnus-tmp-pressed-details
7371 (if gnus-mime-security-button-pressed gnus-tmp-details ""))
7372 (unless (bolp)
7373 (insert "\n"))
7374 (setq b (point))
7375 (gnus-eval-format
7376 gnus-mime-security-button-line-format
7377 gnus-mime-security-button-line-format-alist
7378 `(,@(gnus-local-map-property gnus-mime-security-button-map)
7379 gnus-callback gnus-mime-security-press-button
7380 gnus-line-format ,gnus-mime-security-button-line-format
7381 gnus-mime-details ,gnus-mime-security-button-pressed
7382 article-type annotation
7383 gnus-data ,handle))
7384 (setq e (if (bolp)
7385 ;; Exclude a newline.
7386 (1- (point))
7387 (point)))
7388 (widget-convert-button
7389 'link b e
7390 :mime-handle handle
7391 :action 'gnus-widget-press-button
7392 :button-keymap gnus-mime-security-button-map
7393 :help-echo
7394 (lambda (widget/window &optional overlay pos)
7395 ;; Needed to properly clear the message due to a bug in
7396 ;; wid-edit (XEmacs only).
7397 (when (boundp 'help-echo-owns-message)
7398 (setq help-echo-owns-message t))
7399 (format
7400 "%S: show detail"
7401 (aref gnus-mouse-2 0))))))
7402
7403(defun gnus-mime-display-security (handle)
7404 (save-restriction
7405 (narrow-to-region (point) (point))
7406 (unless (gnus-unbuttonized-mime-type-p (car handle))
7407 (gnus-insert-mime-security-button handle))
7408 (gnus-mime-display-mixed (cdr handle))
7409 (unless (bolp)
7410 (insert "\n"))
7411 (unless (gnus-unbuttonized-mime-type-p (car handle))
7412 (let ((gnus-mime-security-button-line-format
7413 gnus-mime-security-button-end-line-format))
7414 (gnus-insert-mime-security-button handle)))
7415 (mm-set-handle-multipart-parameter
7416 handle 'gnus-region
7417 (cons (set-marker (make-marker) (point-min))
7418 (set-marker (make-marker) (point-max))))))
7419
eec82323
LMI
7420(gnus-ems-redefine)
7421
7422(provide 'gnus-art)
7423
7424(run-hooks 'gnus-art-load-hook)
7425
ab5796a9 7426;;; arch-tag: 2654516f-6279-48f9-a83b-05c1fa450c33
eec82323 7427;;; gnus-art.el ends here