1 ;;; gnus-sum.el --- summary mode commands for Gnus
3 ;; Copyright (C) 1996-2011 Free Software Foundation, Inc.
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
27 ;; For Emacs <22.2 and XEmacs.
29 (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
33 (when (featurep 'xemacs)
34 (require 'easy-mmode))) ; for `define-minor-mode'
36 (defvar tool-bar-mode)
37 (defvar gnus-tmp-header)
50 (autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
51 (autoload 'gnus-cache-write-active "gnus-cache")
52 (autoload 'gnus-mailing-list-insinuate "gnus-ml" nil t)
53 (autoload 'turn-on-gnus-mailing-list-mode "gnus-ml" nil t)
54 (autoload 'gnus-pick-line-number "gnus-salt" nil t)
55 (autoload 'mm-uu-dissect "mm-uu")
56 (autoload 'gnus-article-outlook-deuglify-article "deuglify"
57 "Deuglify broken Outlook (Express) articles and redisplay."
59 (autoload 'gnus-article-outlook-unwrap-lines "deuglify" nil t)
60 (autoload 'gnus-article-outlook-repair-attribution "deuglify" nil t)
61 (autoload 'gnus-article-outlook-rearrange-citation "deuglify" nil t)
62 (autoload 'nnir-article-rsv "nnir" nil nil 'macro)
63 (autoload 'nnir-article-group "nnir" nil nil 'macro)
65 (defcustom gnus-kill-summary-on-exit t
66 "*If non-nil, kill the summary buffer when you exit from it.
67 If nil, the summary will become a \"*Dead Summary*\" buffer, and
68 it will be killed sometime later."
69 :group 'gnus-summary-exit
72 (defcustom gnus-summary-next-group-on-exit t
73 "If non-nil, go to the next unread newsgroup on summary exit.
74 See `gnus-group-goto-unread'."
75 :link '(custom-manual "(gnus)Group Maneuvering")
76 :group 'gnus-summary-exit
77 :version "23.1" ;; No Gnus
80 (defcustom gnus-summary-stop-at-end-of-message nil
81 "If non-nil, don't select the next message when using `SPC'."
82 :link '(custom-manual "(gnus)Group Maneuvering")
83 :group 'gnus-summary-maneuvering
87 (defcustom gnus-fetch-old-headers nil
88 "*Non-nil means that Gnus will try to build threads by grabbing old headers.
89 If an unread article in the group refers to an older, already
90 read (or just marked as read) article, the old article will not
91 normally be displayed in the Summary buffer. If this variable is
92 t, Gnus will attempt to grab the headers to the old articles, and
93 thereby build complete threads. If it has the value `some', all
94 old headers will be fetched but only enough headers to connect
95 otherwise loose threads will be displayed. This variable can
96 also be a number. In that case, no more than that number of old
97 headers will be fetched. If it has the value `invisible', all
98 old headers will be fetched, but none will be displayed.
100 The server has to support NOV for any of this to work.
102 This feature can seriously impact performance it ignores all
103 locally cached header entries. Setting it to t for groups for a
104 server that doesn't expire articles (such as news.gmane.org),
105 leads to very slow summary generation."
107 :type '(choice (const :tag "off" nil)
112 (sexp :menu-tag "other" t)))
114 (defcustom gnus-refer-thread-limit 500
115 "*The number of old headers to fetch when doing \\<gnus-summary-mode-map>\\[gnus-summary-refer-thread].
116 If t, fetch all the available old headers."
118 :type '(choice number
119 (sexp :menu-tag "other" t)))
121 (defcustom gnus-summary-make-false-root 'adopt
122 "*nil means that Gnus won't gather loose threads.
123 If the root of a thread has expired or been read in a previous
124 session, the information necessary to build a complete thread has been
125 lost. Instead of having many small sub-threads from this original thread
126 scattered all over the summary buffer, Gnus can gather them.
128 If non-nil, Gnus will try to gather all loose sub-threads from an
129 original thread into one large thread.
131 If this variable is non-nil, it should be one of `none', `adopt',
134 If this variable is `none', Gnus will not make a false root, but just
135 present the sub-threads after another.
136 If this variable is `dummy', Gnus will create a dummy root that will
137 have all the sub-threads as children.
138 If this variable is `adopt', Gnus will make one of the \"children\"
139 the parent and mark all the step-children as such.
140 If this variable is `empty', the \"children\" are printed with empty
141 subject fields. (Or rather, they will be printed with a string
142 given by the `gnus-summary-same-subject' variable.)"
144 :type '(choice (const :tag "off" nil)
150 (defcustom gnus-summary-make-false-root-always nil
151 "Always make a false dummy root."
156 (defcustom gnus-summary-gather-exclude-subject "^ *$\\|^(none)$"
157 "*A regexp to match subjects to be excluded from loose thread gathering.
158 As loose thread gathering is done on subjects only, that means that
159 there can be many false gatherings performed. By rooting out certain
160 common subjects, gathering might become saner."
164 (defcustom gnus-summary-gather-subject-limit nil
165 "*Maximum length of subject comparisons when gathering loose threads.
166 Use nil to compare full subjects. Setting this variable to a low
167 number will help gather threads that have been corrupted by
168 newsreaders chopping off subject lines, but it might also mean that
169 unrelated articles that have subject that happen to begin with the
170 same few characters will be incorrectly gathered.
172 If this variable is `fuzzy', Gnus will use a fuzzy algorithm when
175 :type '(choice (const :tag "off" nil)
177 (sexp :menu-tag "on" t)))
179 (defcustom gnus-simplify-subject-functions nil
180 "List of functions taking a string argument that simplify subjects.
181 The functions are applied recursively.
183 Useful functions to put in this list include:
184 `gnus-simplify-subject-re', `gnus-simplify-subject-fuzzy',
185 `gnus-simplify-whitespace', and `gnus-simplify-all-whitespace'."
187 :type '(repeat function))
189 (defcustom gnus-simplify-ignored-prefixes nil
190 "*Remove matches for this regexp from subject lines when simplifying fuzzily."
192 :type '(choice (const :tag "off" nil)
195 (defcustom gnus-build-sparse-threads nil
196 "*If non-nil, fill in the gaps in threads.
197 If `some', only fill in the gaps that are needed to tie loose threads
198 together. If `more', fill in all leaf nodes that Gnus can find. If
199 non-nil and non-`some', fill in all gaps that Gnus manages to guess."
201 :type '(choice (const :tag "off" nil)
204 (sexp :menu-tag "all" t)))
206 (defcustom gnus-summary-thread-gathering-function
207 'gnus-gather-threads-by-subject
208 "*Function used for gathering loose threads.
209 There are two pre-defined functions: `gnus-gather-threads-by-subject',
210 which only takes Subjects into consideration; and
211 `gnus-gather-threads-by-references', which compared the References
212 headers of the articles to find matches."
214 :type '(radio (function-item gnus-gather-threads-by-subject)
215 (function-item gnus-gather-threads-by-references)
216 (function :tag "other")))
218 (defcustom gnus-summary-same-subject ""
219 "*String indicating that the current article has the same subject as the previous.
220 This variable will only be used if the value of
221 `gnus-summary-make-false-root' is `empty'."
222 :group 'gnus-summary-format
225 (defcustom gnus-summary-goto-unread nil
226 "*If t, many commands will go to the next unread article.
227 This applies to marking commands as well as other commands that
228 \"naturally\" select the next article, like, for instance, `SPC' at
229 the end of an article.
231 If nil, the marking commands do NOT go to the next unread article
232 \(they go to the next article instead). If `never', commands that
233 usually go to the next unread article, will go to the next article,
234 whether it is read or not."
236 :group 'gnus-summary-marks
237 :link '(custom-manual "(gnus)Setting Marks")
238 :type '(choice (const :tag "off" nil)
240 (sexp :menu-tag "on" t)))
242 (defcustom gnus-summary-default-score 0
243 "*Default article score level.
244 All scores generated by the score files will be added to this score.
245 If this variable is nil, scoring will be disabled."
246 :group 'gnus-score-default
247 :type '(choice (const :tag "disable")
250 (defcustom gnus-summary-default-high-score 0
251 "*Default threshold for a high scored article.
252 An article will be highlighted as high scored if its score is greater
255 :group 'gnus-score-default
258 (defcustom gnus-summary-default-low-score 0
259 "*Default threshold for a low scored article.
260 An article will be highlighted as low scored if its score is smaller
263 :group 'gnus-score-default
266 (defcustom gnus-summary-zcore-fuzz 0
267 "*Fuzziness factor for the zcore in the summary buffer.
268 Articles with scores closer than this to `gnus-summary-default-score'
270 :group 'gnus-summary-format
273 (defcustom gnus-simplify-subject-fuzzy-regexp nil
274 "*Strings to be removed when doing fuzzy matches.
275 This can either be a regular expression or list of regular expressions
276 that will be removed from subject strings if fuzzy subject
277 simplification is selected."
279 :type '(repeat regexp))
281 (defcustom gnus-show-threads t
282 "*If non-nil, display threads in summary mode."
286 (defcustom gnus-thread-hide-subtree nil
287 "*If non-nil, hide all threads initially.
288 This can be a predicate specifier which says which threads to hide.
289 If threads are hidden, you have to run the command
290 `gnus-summary-show-thread' by hand or select an article."
292 :type '(radio (sexp :format "Non-nil\n"
293 :match (lambda (widget value)
294 (not (or (consp value) (functionp value))))
297 (sexp :tag "Predicate specifier")))
299 (defcustom gnus-thread-hide-killed t
300 "*If non-nil, hide killed threads automatically."
304 (defcustom gnus-thread-ignore-subject t
305 "*If non-nil, which is the default, ignore subjects and do all threading based on the Reference header.
306 If nil, articles that have different subjects from their parents will
307 start separate threads."
311 (defcustom gnus-thread-operation-ignore-subject t
312 "*If non-nil, subjects will be ignored when doing thread commands.
313 This affects commands like `gnus-summary-kill-thread' and
314 `gnus-summary-lower-thread'.
316 If this variable is nil, articles in the same thread with different
317 subjects will not be included in the operation in question. If this
318 variable is `fuzzy', only articles that have subjects that are fuzzily
319 equal will be included."
321 :type '(choice (const :tag "off" nil)
325 (defcustom gnus-thread-indent-level 4
326 "*Number that says how much each sub-thread should be indented."
330 (defcustom gnus-auto-extend-newsgroup t
331 "*If non-nil, extend newsgroup forward and backward when requested."
332 :group 'gnus-summary-choose
335 (defcustom gnus-auto-select-first t
336 "If non-nil, select an article on group entry.
337 An article is selected automatically when entering a group
338 e.g. with \\<gnus-group-mode-map>\\[gnus-group-read-group], or via `gnus-summary-next-page' or
339 `gnus-summary-catchup-and-goto-next-group'.
341 Which article is selected is controlled by the variable
342 `gnus-auto-select-subject'.
344 If you want to prevent automatic selection of articles in some
345 newsgroups, set the variable to nil in `gnus-select-group-hook'."
346 ;; Commands include...
347 ;; \\<gnus-group-mode-map>\\[gnus-group-read-group]
348 ;; \\<gnus-summary-mode-map>\\[gnus-summary-next-page]
349 ;; \\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group]
350 :group 'gnus-group-select
351 :type '(choice (const :tag "none" nil)
352 (sexp :menu-tag "first" t)))
354 (defcustom gnus-auto-select-subject 'unseen-or-unread
355 "*Says what subject to place under point when entering a group.
357 This variable can either be the symbols `first' (place point on the
358 first subject), `unread' (place point on the subject line of the first
359 unread article), `best' (place point on the subject line of the
360 higest-scored article), `unseen' (place point on the subject line of
361 the first unseen article), `unseen-or-unread' (place point on the subject
362 line of the first unseen article or, if all articles have been seen, on the
363 subject line of the first unread article), or a function to be called to
364 place point on some subject line."
366 :group 'gnus-group-select
367 :type '(choice (const best)
371 (const unseen-or-unread)))
373 (defcustom gnus-auto-select-next t
374 "*If non-nil, offer to go to the next group from the end of the previous.
375 If the value is t and the next newsgroup is empty, Gnus will exit
376 summary mode and go back to group mode. If the value is neither nil
377 nor t, Gnus will select the following unread newsgroup. In
378 particular, if the value is the symbol `quietly', the next unread
379 newsgroup will be selected without any confirmation, and if it is
380 `almost-quietly', the next group will be selected without any
381 confirmation if you are located on the last article in the group.
382 Finally, if this variable is `slightly-quietly', the `\\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group]' command
383 will go to the next group without confirmation."
384 :group 'gnus-summary-maneuvering
385 :type '(choice (const :tag "off" nil)
387 (const almost-quietly)
388 (const slightly-quietly)
389 (sexp :menu-tag "on" t)))
391 (defcustom gnus-auto-select-same nil
392 "*If non-nil, select the next article with the same subject.
393 If there are no more articles with the same subject, go to
394 the first unread article."
395 :group 'gnus-summary-maneuvering
398 (defcustom gnus-auto-select-on-ephemeral-exit 'next-noselect
399 "What article should be selected after exiting an ephemeral group.
400 Valid values include:
403 Select the next article.
405 Select the next unread article.
407 Move the cursor to the next article. This is the default.
408 `next-unread-noselect'
409 Move the cursor to the next unread article.
411 If it has any other value or there is no next (unread) article, the
412 article selected before entering to the ephemeral group will appear."
413 :version "23.1" ;; No Gnus
414 :group 'gnus-summary-maneuvering
415 :type '(choice :format "%{%t%}:\n %[Value Menu%] %v"
416 (const next) (const next-unread)
417 (const next-noselect) (const next-unread-noselect)
418 (sexp :tag "other" :value nil)))
420 (defcustom gnus-auto-goto-ignores 'unfetched
421 "*Says how to handle unfetched articles when maneuvering.
423 This variable can either be the symbols nil (maneuver to any
424 article), `undownloaded' (maneuvering while unplugged ignores articles
425 that have not been fetched), `always-undownloaded' (maneuvering always
426 ignores articles that have not been fetched), `unfetched' (maneuvering
427 ignores articles whose headers have not been fetched).
429 NOTE: The list of unfetched articles will always be nil when plugged
430 and, when unplugged, a subset of the undownloaded article list."
432 :group 'gnus-summary-maneuvering
433 :type '(choice (const :tag "None" nil)
434 (const :tag "Undownloaded when unplugged" undownloaded)
435 (const :tag "Undownloaded" always-undownloaded)
436 (const :tag "Unfetched" unfetched)))
438 (defcustom gnus-summary-check-current nil
439 "*If non-nil, consider the current article when moving.
440 The \"unread\" movement commands will stay on the same line if the
441 current article is unread."
442 :group 'gnus-summary-maneuvering
445 (defcustom gnus-auto-center-summary 2
446 "*If non-nil, always center the current summary buffer.
447 In particular, if `vertical' do only vertical recentering. If non-nil
448 and non-`vertical', do both horizontal and vertical recentering."
449 :group 'gnus-summary-maneuvering
450 :type '(choice (const :tag "none" nil)
452 (integer :tag "height")
453 (sexp :menu-tag "both" t)))
455 (defcustom gnus-auto-center-group t
456 "If non-nil, always center the group buffer."
457 :group 'gnus-summary-maneuvering
460 (defcustom gnus-show-all-headers nil
461 "*If non-nil, don't hide any headers."
462 :group 'gnus-article-hiding
463 :group 'gnus-article-headers
466 (defcustom gnus-summary-ignore-duplicates nil
467 "*If non-nil, ignore articles with identical Message-ID headers."
471 (defcustom gnus-single-article-buffer nil
472 "*If non-nil, display all articles in the same buffer.
473 If nil, each group will get its own article buffer."
475 :group 'gnus-article-various
478 (defcustom gnus-widen-article-window nil
479 "If non-nil, selecting the article buffer will display only the article buffer."
481 :group 'gnus-article-various
484 (defcustom gnus-break-pages t
485 "*If non-nil, do page breaking on articles.
486 The page delimiter is specified by the `gnus-page-delimiter'
488 :group 'gnus-article-various
491 (defcustom gnus-move-split-methods nil
492 "*Variable used to suggest where articles are to be moved to.
493 It uses the same syntax as the `gnus-split-methods' variable.
494 However, whereas `gnus-split-methods' specifies file names as targets,
495 this variable specifies group names."
496 :group 'gnus-summary-mail
497 :type '(repeat (choice (list :value (fun) function)
498 (cons :value ("" "") regexp (repeat string))
501 (defcustom gnus-move-group-prefix-function 'gnus-group-real-prefix
502 "Function used to compute default prefix for article move/copy/etc prompts.
503 The function should take one argument, a group name, and return a
504 string with the suggested prefix."
505 :group 'gnus-summary-mail
508 ;; FIXME: Although the custom type is `character' for the following variables,
509 ;; using multibyte characters (Latin-1, UTF-8) doesn't work. -- rs
511 (defcustom gnus-unread-mark ? ;Whitespace
512 "*Mark used for unread articles."
513 :group 'gnus-summary-marks
516 (defcustom gnus-ticked-mark ?!
517 "*Mark used for ticked articles."
518 :group 'gnus-summary-marks
521 (defcustom gnus-dormant-mark ??
522 "*Mark used for dormant articles."
523 :group 'gnus-summary-marks
526 (defcustom gnus-del-mark ?r
527 "*Mark used for del'd articles."
528 :group 'gnus-summary-marks
531 (defcustom gnus-read-mark ?R
532 "*Mark used for read articles."
533 :group 'gnus-summary-marks
536 (defcustom gnus-expirable-mark ?E
537 "*Mark used for expirable articles."
538 :group 'gnus-summary-marks
541 (defcustom gnus-killed-mark ?K
542 "*Mark used for killed articles."
543 :group 'gnus-summary-marks
546 (defcustom gnus-spam-mark ?$
547 "*Mark used for spam articles."
549 :group 'gnus-summary-marks
552 (defcustom gnus-kill-file-mark ?X
553 "*Mark used for articles killed by kill files."
554 :group 'gnus-summary-marks
557 (defcustom gnus-low-score-mark ?Y
558 "*Mark used for articles with a low score."
559 :group 'gnus-summary-marks
562 (defcustom gnus-catchup-mark ?C
563 "*Mark used for articles that are caught up."
564 :group 'gnus-summary-marks
567 (defcustom gnus-replied-mark ?A
568 "*Mark used for articles that have been replied to."
569 :group 'gnus-summary-marks
572 (defcustom gnus-forwarded-mark ?F
573 "*Mark used for articles that have been forwarded."
575 :group 'gnus-summary-marks
578 (defcustom gnus-recent-mark ?N
579 "*Mark used for articles that are recent."
581 :group 'gnus-summary-marks
584 (defcustom gnus-cached-mark ?*
585 "*Mark used for articles that are in the cache."