* gnus-fun.el (gnus-display-x-face-in-from): Fake it.
[gnus] / lisp / gnus-sum.el
1 ;;; gnus-sum.el --- summary mode commands for Gnus
2 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002
3 ;;        Free Software Foundation, Inc.
4
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
6 ;; Keywords: news
7
8 ;; This file is part of GNU Emacs.
9
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 2, or (at your option)
13 ;; any later version.
14
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.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24
25 ;;; Commentary:
26
27 ;;; Code:
28
29 (eval-when-compile (require 'cl))
30
31 (require 'gnus)
32 (require 'gnus-group)
33 (require 'gnus-spec)
34 (require 'gnus-range)
35 (require 'gnus-int)
36 (require 'gnus-undo)
37 (require 'gnus-util)
38 (require 'mm-decode)
39 (require 'nnoo)
40
41 (autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
42 (autoload 'gnus-cache-write-active "gnus-cache")
43 (autoload 'gnus-mailing-list-insinuate "gnus-ml" nil t)
44 (autoload 'turn-on-gnus-mailing-list-mode "gnus-ml" nil t)
45 (autoload 'mm-uu-dissect "mm-uu")
46
47 (defcustom gnus-kill-summary-on-exit t
48   "*If non-nil, kill the summary buffer when you exit from it.
49 If nil, the summary will become a \"*Dead Summary*\" buffer, and
50 it will be killed sometime later."
51   :group 'gnus-summary-exit
52   :type 'boolean)
53
54 (defcustom gnus-fetch-old-headers nil
55   "*Non-nil means that Gnus will try to build threads by grabbing old headers.
56 If an unread article in the group refers to an older, already read (or
57 just marked as read) article, the old article will not normally be
58 displayed in the Summary buffer.  If this variable is non-nil, Gnus
59 will attempt to grab the headers to the old articles, and thereby
60 build complete threads.  If it has the value `some', only enough
61 headers to connect otherwise loose threads will be displayed.  This
62 variable can also be a number.  In that case, no more than that number
63 of old headers will be fetched.  If it has the value `invisible', all
64 old headers will be fetched, but none will be displayed.
65
66 The server has to support NOV for any of this to work."
67   :group 'gnus-thread
68   :type '(choice (const :tag "off" nil)
69                  (const some)
70                  number
71                  (sexp :menu-tag "other" t)))
72
73 (defcustom gnus-refer-thread-limit 200
74   "*The number of old headers to fetch when doing \\<gnus-summary-mode-map>\\[gnus-summary-refer-thread].
75 If t, fetch all the available old headers."
76   :group 'gnus-thread
77   :type '(choice number
78                  (sexp :menu-tag "other" t)))
79
80 (defcustom gnus-summary-make-false-root 'adopt
81   "*nil means that Gnus won't gather loose threads.
82 If the root of a thread has expired or been read in a previous
83 session, the information necessary to build a complete thread has been
84 lost.  Instead of having many small sub-threads from this original thread
85 scattered all over the summary buffer, Gnus can gather them.
86
87 If non-nil, Gnus will try to gather all loose sub-threads from an
88 original thread into one large thread.
89
90 If this variable is non-nil, it should be one of `none', `adopt',
91 `dummy' or `empty'.
92
93 If this variable is `none', Gnus will not make a false root, but just
94 present the sub-threads after another.
95 If this variable is `dummy', Gnus will create a dummy root that will
96 have all the sub-threads as children.
97 If this variable is `adopt', Gnus will make one of the \"children\"
98 the parent and mark all the step-children as such.
99 If this variable is `empty', the \"children\" are printed with empty
100 subject fields.  (Or rather, they will be printed with a string
101 given by the `gnus-summary-same-subject' variable.)"
102   :group 'gnus-thread
103   :type '(choice (const :tag "off" nil)
104                  (const none)
105                  (const dummy)
106                  (const adopt)
107                  (const empty)))
108
109 (defcustom gnus-summary-gather-exclude-subject "^ *$\\|^(none)$"
110   "*A regexp to match subjects to be excluded from loose thread gathering.
111 As loose thread gathering is done on subjects only, that means that
112 there can be many false gatherings performed.  By rooting out certain
113 common subjects, gathering might become saner."
114   :group 'gnus-thread
115   :type 'regexp)
116
117 (defcustom gnus-summary-gather-subject-limit nil
118   "*Maximum length of subject comparisons when gathering loose threads.
119 Use nil to compare full subjects.  Setting this variable to a low
120 number will help gather threads that have been corrupted by
121 newsreaders chopping off subject lines, but it might also mean that
122 unrelated articles that have subject that happen to begin with the
123 same few characters will be incorrectly gathered.
124
125 If this variable is `fuzzy', Gnus will use a fuzzy algorithm when
126 comparing subjects."
127   :group 'gnus-thread
128   :type '(choice (const :tag "off" nil)
129                  (const fuzzy)
130                  (sexp :menu-tag "on" t)))
131
132 (defcustom gnus-simplify-subject-functions nil
133   "List of functions taking a string argument that simplify subjects.
134 The functions are applied recursively.
135
136 Useful functions to put in this list include: `gnus-simplify-subject-re',
137 `gnus-simplify-subject-fuzzy' and `gnus-simplify-whitespace'."
138   :group 'gnus-thread
139   :type '(repeat function))
140
141 (defcustom gnus-simplify-ignored-prefixes nil
142   "*Regexp, matches for which are removed from subject lines when simplifying fuzzily."
143   :group 'gnus-thread
144   :type '(choice (const :tag "off" nil)
145                  regexp))
146
147 (defcustom gnus-build-sparse-threads nil
148   "*If non-nil, fill in the gaps in threads.
149 If `some', only fill in the gaps that are needed to tie loose threads
150 together.  If `more', fill in all leaf nodes that Gnus can find.  If
151 non-nil and non-`some', fill in all gaps that Gnus manages to guess."
152   :group 'gnus-thread
153   :type '(choice (const :tag "off" nil)
154                  (const some)
155                  (const more)
156                  (sexp :menu-tag "all" t)))
157
158 (defcustom gnus-summary-thread-gathering-function
159   'gnus-gather-threads-by-subject
160   "*Function used for gathering loose threads.
161 There are two pre-defined functions: `gnus-gather-threads-by-subject',
162 which only takes Subjects into consideration; and
163 `gnus-gather-threads-by-references', which compared the References
164 headers of the articles to find matches."
165   :group 'gnus-thread
166   :type '(radio (function-item gnus-gather-threads-by-subject)
167                 (function-item gnus-gather-threads-by-references)
168                 (function :tag "other")))
169
170 (defcustom gnus-summary-same-subject ""
171   "*String indicating that the current article has the same subject as the previous.
172 This variable will only be used if the value of
173 `gnus-summary-make-false-root' is `empty'."
174   :group 'gnus-summary-format
175   :type 'string)
176
177 (defcustom gnus-summary-goto-unread t
178   "*If t, many commands will go to the next unread article.
179 This applies to marking commands as well as other commands that
180 \"naturally\" select the next article, like, for instance, `SPC' at
181 the end of an article.
182
183 If nil, the marking commands do NOT go to the next unread article
184 (they go to the next article instead).  If `never', commands that
185 usually go to the next unread article, will go to the next article,
186 whether it is read or not."
187   :group 'gnus-summary-marks
188   :link '(custom-manual "(gnus)Setting Marks")
189   :type '(choice (const :tag "off" nil)
190                  (const never)
191                  (sexp :menu-tag "on" t)))
192
193 (defcustom gnus-summary-default-score 0
194   "*Default article score level.
195 All scores generated by the score files will be added to this score.
196 If this variable is nil, scoring will be disabled."
197   :group 'gnus-score-default
198   :type '(choice (const :tag "disable")
199                  integer))
200
201 (defcustom gnus-summary-default-high-score 0
202   "*Default threshold for a high scored article.
203 An article will be highlighted as high scored if its score is greater
204 than this score."
205   :group 'gnus-score-default
206   :type 'integer)
207
208 (defcustom gnus-summary-default-low-score 0
209   "*Default threshold for a low scored article.
210 An article will be highlighted as low scored if its score is smaller
211 than this score."
212   :group 'gnus-score-default
213   :type 'integer)
214
215 (defcustom gnus-summary-zcore-fuzz 0
216   "*Fuzziness factor for the zcore in the summary buffer.
217 Articles with scores closer than this to `gnus-summary-default-score'
218 will not be marked."
219   :group 'gnus-summary-format
220   :type 'integer)
221
222 (defcustom gnus-simplify-subject-fuzzy-regexp nil
223   "*Strings to be removed when doing fuzzy matches.
224 This can either be a regular expression or list of regular expressions
225 that will be removed from subject strings if fuzzy subject
226 simplification is selected."
227   :group 'gnus-thread
228   :type '(repeat regexp))
229
230 (defcustom gnus-show-threads t
231   "*If non-nil, display threads in summary mode."
232   :group 'gnus-thread
233   :type 'boolean)
234
235 (defcustom gnus-thread-hide-subtree nil
236   "*If non-nil, hide all threads initially.
237 This can be a predicate specifier which says which threads to hide.
238 If threads are hidden, you have to run the command
239 `gnus-summary-show-thread' by hand or use `gnus-select-article-hook'
240 to expose hidden threads."
241   :group 'gnus-thread
242   :type 'boolean)
243
244 (defcustom gnus-thread-hide-killed t
245   "*If non-nil, hide killed threads automatically."
246   :group 'gnus-thread
247   :type 'boolean)
248
249 (defcustom gnus-thread-ignore-subject t
250   "*If non-nil, which is the default, ignore subjects and do all threading based on the Reference header.
251 If nil, articles that have different subjects from their parents will
252 start separate threads."
253   :group 'gnus-thread
254   :type 'boolean)
255
256 (defcustom gnus-thread-operation-ignore-subject t
257   "*If non-nil, subjects will be ignored when doing thread commands.
258 This affects commands like `gnus-summary-kill-thread' and
259 `gnus-summary-lower-thread'.
260
261 If this variable is nil, articles in the same thread with different
262 subjects will not be included in the operation in question.  If this
263 variable is `fuzzy', only articles that have subjects that are fuzzily
264 equal will be included."
265   :group 'gnus-thread
266   :type '(choice (const :tag "off" nil)
267                  (const fuzzy)
268                  (sexp :tag "on" t)))
269
270 (defcustom gnus-thread-indent-level 4
271   "*Number that says how much each sub-thread should be indented."
272   :group 'gnus-thread
273   :type 'integer)
274
275 (defcustom gnus-auto-extend-newsgroup t
276   "*If non-nil, extend newsgroup forward and backward when requested."
277   :group 'gnus-summary-choose
278   :type 'boolean)
279
280 (defcustom gnus-auto-select-first t
281   "*If non-nil, select the article under point.
282 Which article this is is controlled by the `gnus-auto-select-subject'
283 variable.
284
285 If you want to prevent automatic selection of articles in some
286 newsgroups, set the variable to nil in `gnus-select-group-hook'."
287   :group 'gnus-group-select
288   :type '(choice (const :tag "none" nil)
289                  (sexp :menu-tag "first" t)))
290
291 (defcustom gnus-auto-select-subject 'unread
292   "*Says what subject to place under point when entering a group.
293
294 This variable can either be the symbols `first' (place point on the
295 first subject), `unread' (place point on the subject line of the first
296 unread article), `best' (place point on the subject line of the
297 higest-scored article), `unseen' (place point on the subject line of
298 the first unseen article), or a function to be called to place point on
299 some subject line.."
300   :group 'gnus-group-select
301   :type '(choice (const best)
302                  (const unread)
303                  (const first)
304                  (const unseen)))
305
306 (defcustom gnus-auto-select-next t
307   "*If non-nil, offer to go to the next group from the end of the previous.
308 If the value is t and the next newsgroup is empty, Gnus will exit
309 summary mode and go back to group mode.  If the value is neither nil
310 nor t, Gnus will select the following unread newsgroup.  In
311 particular, if the value is the symbol `quietly', the next unread
312 newsgroup will be selected without any confirmation, and if it is
313 `almost-quietly', the next group will be selected without any
314 confirmation if you are located on the last article in the group.
315 Finally, if this variable is `slightly-quietly', the `Z n' command
316 will go to the next group without confirmation."
317   :group 'gnus-summary-maneuvering
318   :type '(choice (const :tag "off" nil)
319                  (const quietly)
320                  (const almost-quietly)
321                  (const slightly-quietly)
322                  (sexp :menu-tag "on" t)))
323
324 (defcustom gnus-auto-select-same nil
325   "*If non-nil, select the next article with the same subject.
326 If there are no more articles with the same subject, go to
327 the first unread article."
328   :group 'gnus-summary-maneuvering
329   :type 'boolean)
330
331 (defcustom gnus-summary-check-current nil
332   "*If non-nil, consider the current article when moving.
333 The \"unread\" movement commands will stay on the same line if the
334 current article is unread."
335   :group 'gnus-summary-maneuvering
336   :type 'boolean)
337
338 (defcustom gnus-auto-center-summary t
339   "*If non-nil, always center the current summary buffer.
340 In particular, if `vertical' do only vertical recentering.  If non-nil
341 and non-`vertical', do both horizontal and vertical recentering."
342   :group 'gnus-summary-maneuvering
343   :type '(choice (const :tag "none" nil)
344                  (const vertical)
345                  (integer :tag "height")
346                  (sexp :menu-tag "both" t)))
347
348 (defcustom gnus-show-all-headers nil
349   "*If non-nil, don't hide any headers."
350   :group 'gnus-article-hiding
351   :group 'gnus-article-headers
352   :type 'boolean)
353
354 (defcustom gnus-summary-ignore-duplicates nil
355   "*If non-nil, ignore articles with identical Message-ID headers."
356   :group 'gnus-summary
357   :type 'boolean)
358
359 (defcustom gnus-single-article-buffer t
360   "*If non-nil, display all articles in the same buffer.
361 If nil, each group will get its own article buffer."
362   :group 'gnus-article-various
363   :type 'boolean)
364
365 (defcustom gnus-break-pages t
366   "*If non-nil, do page breaking on articles.
367 The page delimiter is specified by the `gnus-page-delimiter'
368 variable."
369   :group 'gnus-article-various
370   :type 'boolean)
371
372 (defcustom gnus-move-split-methods nil
373   "*Variable used to suggest where articles are to be moved to.
374 It uses the same syntax as the `gnus-split-methods' variable.
375 However, whereas `gnus-split-methods' specifies file names as targets,
376 this variable specifies group names."
377   :group 'gnus-summary-mail
378   :type '(repeat (choice (list :value (fun) function)
379                          (cons :value ("" "") regexp (repeat string))
380                          (sexp :value nil))))
381
382 (defcustom gnus-unread-mark ?           ;Whitespace
383   "*Mark used for unread articles."
384   :group 'gnus-summary-marks
385   :type 'character)
386
387 (defcustom gnus-ticked-mark ?!
388   "*Mark used for ticked articles."
389   :group 'gnus-summary-marks
390   :type 'character)
391
392 (defcustom gnus-dormant-mark ??
393   "*Mark used for dormant articles."
394   :group 'gnus-summary-marks
395   :type 'character)
396
397 (defcustom gnus-del-mark ?r
398   "*Mark used for del'd articles."
399   :group 'gnus-summary-marks
400   :type 'character)
401
402 (defcustom gnus-read-mark ?R
403   "*Mark used for read articles."
404   :group 'gnus-summary-marks
405   :type 'character)
406
407 (defcustom gnus-expirable-mark ?E
408   "*Mark used for expirable articles."
409   :group 'gnus-summary-marks
410   :type 'character)
411
412 (defcustom gnus-killed-mark ?K
413   "*Mark used for killed articles."
414   :group 'gnus-summary-marks
415   :type 'character)
416
417 (defcustom gnus-souped-mark ?F
418   "*Mark used for souped articles."
419   :group 'gnus-summary-marks
420   :type 'character)
421
422 (defcustom gnus-kill-file-mark ?X
423   "*Mark used for articles killed by kill files."
424   :group 'gnus-summary-marks
425   :type 'character)
426
427 (defcustom gnus-low-score-mark ?Y
428   "*Mark used for articles with a low score."
429   :group 'gnus-summary-marks
430   :type 'character)
431
432 (defcustom gnus-catchup-mark ?C
433   "*Mark used for articles that are caught up."
434   :group 'gnus-summary-marks
435   :type 'character)
436
437 (defcustom gnus-replied-mark ?A
438   "*Mark used for articles that have been replied to."
439   :group 'gnus-summary-marks
440   :type 'character)
441
442 (defcustom gnus-forwarded-mark ?F
443   "*Mark used for articles that have been forwarded."
444   :group 'gnus-summary-marks
445   :type 'character)
446
447 (defcustom gnus-recent-mark ?N
448   "*Mark used for articles that are recent."
449   :group 'gnus-summary-marks
450   :type 'character)
451
452 (defcustom gnus-cached-mark ?*
453   "*Mark used for articles that are in the cache."
454   :group 'gnus-summary-marks
455   :type 'character)
456
457 (defcustom gnus-saved-mark ?S
458   "*Mark used for articles that have been saved."
459   :group 'gnus-summary-marks
460   :type 'character)
461
462 (defcustom gnus-unseen-mark ?.
463   "*Mark used for articles that haven't been seen."
464   :group 'gnus-summary-marks
465   :type 'character)
466
467 (defcustom gnus-no-mark ?               ;Whitespace
468   "*Mark used for articles that have no other secondary mark."
469   :group 'gnus-summary-marks
470   :type 'character)
471
472 (defcustom gnus-ancient-mark ?O
473   "*Mark used for ancient articles."
474   :group 'gnus-summary-marks
475   :type 'character)
476
477 (defcustom gnus-sparse-mark ?Q
478   "*Mark used for sparsely reffed articles."
479   :group 'gnus-summary-marks
480   :type 'character)
481
482 (defcustom gnus-canceled-mark ?G
483   "*Mark used for canceled articles."
484   :group 'gnus-summary-marks
485   :type 'character)
486
487 (defcustom gnus-duplicate-mark ?M
488   "*Mark used for duplicate articles."
489   :group 'gnus-summary-marks
490   :type 'character)
491
492 (defcustom gnus-undownloaded-mark ?@
493   "*Mark used for articles that weren't downloaded."
494   :group 'gnus-summary-marks
495   :type 'character)
496
497 (defcustom gnus-downloadable-mark ?%
498   "*Mark used for articles that are to be downloaded."
499   :group 'gnus-summary-marks
500   :type 'character)
501
502 (defcustom gnus-unsendable-mark ?=
503   "*Mark used for articles that won't be sent."
504   :group 'gnus-summary-marks
505   :type 'character)
506
507 (defcustom gnus-score-over-mark ?+
508   "*Score mark used for articles with high scores."
509   :group 'gnus-summary-marks
510   :type 'character)
511
512 (defcustom gnus-score-below-mark ?-
513   "*Score mark used for articles with low scores."
514   :group 'gnus-summary-marks
515   :type 'character)
516
517 (defcustom gnus-empty-thread-mark ?     ;Whitespace
518   "*There is no thread under the article."
519   :group 'gnus-summary-marks
520   :type 'character)
521
522 (defcustom gnus-not-empty-thread-mark ?=
523   "*There is a thread under the article."
524   :group 'gnus-summary-marks
525   :type 'character)
526
527 (defcustom gnus-view-pseudo-asynchronously nil
528   "*If non-nil, Gnus will view pseudo-articles asynchronously."
529   :group 'gnus-extract-view
530   :type 'boolean)
531
532 (defcustom gnus-auto-expirable-marks
533   (list gnus-killed-mark gnus-del-mark gnus-catchup-mark
534         gnus-low-score-mark gnus-ancient-mark gnus-read-mark
535         gnus-souped-mark gnus-duplicate-mark)
536   "*The list of marks converted into expiration if a group is auto-expirable."
537   :version "21.1"
538   :group 'gnus-summary
539   :type '(repeat character))
540
541 (defcustom gnus-inhibit-user-auto-expire t
542   "*If non-nil, user marking commands will not mark an article as expirable, even if the group has auto-expire turned on."
543   :version "21.1"
544   :group 'gnus-summary
545   :type 'boolean)
546
547 (defcustom gnus-view-pseudos nil
548   "*If `automatic', pseudo-articles will be viewed automatically.
549 If `not-confirm', pseudos will be viewed automatically, and the user
550 will not be asked to confirm the command."
551   :group 'gnus-extract-view
552   :type '(choice (const :tag "off" nil)
553                  (const automatic)
554                  (const not-confirm)))
555
556 (defcustom gnus-view-pseudos-separately t
557   "*If non-nil, one pseudo-article will be created for each file to be viewed.
558 If nil, all files that use the same viewing command will be given as a
559 list of parameters to that command."
560   :group 'gnus-extract-view
561   :type 'boolean)
562
563 (defcustom gnus-insert-pseudo-articles t
564   "*If non-nil, insert pseudo-articles when decoding articles."
565   :group 'gnus-extract-view
566   :type 'boolean)
567
568 (defcustom gnus-summary-dummy-line-format
569   "  %(:                          :%) %S\n"
570   "*The format specification for the dummy roots in the summary buffer.
571 It works along the same lines as a normal formatting string,
572 with some simple extensions.
573
574 %S  The subject
575
576 General format specifiers can also be used.  
577 See (gnus)Formatting Variables."
578   :link '(custom-manual "(gnus)Formatting Variables")
579   :group 'gnus-threading
580   :type 'string)
581
582 (defcustom gnus-summary-mode-line-format "Gnus: %g [%A] %Z"
583   "*The format specification for the summary mode line.
584 It works along the same lines as a normal formatting string,
585 with some simple extensions:
586
587 %G  Group name
588 %p  Unprefixed group name
589 %A  Current article number
590 %z  Current article score
591 %V  Gnus version
592 %U  Number of unread articles in the group
593 %e  Number of unselected articles in the group
594 %Z  A string with unread/unselected article counts
595 %g  Shortish group name
596 %S  Subject of the current article
597 %u  User-defined spec
598 %s  Current score file name
599 %d  Number of dormant articles
600 %r  Number of articles that have been marked as read in this session
601 %E  Number of articles expunged by the score files"
602   :group 'gnus-summary-format
603   :type 'string)
604
605 (defcustom gnus-list-identifiers nil
606   "Regexp that matches list identifiers to be removed from subject.
607 This can also be a list of regexps."
608   :version "21.1"
609   :group 'gnus-summary-format
610   :group 'gnus-article-hiding
611   :type '(choice (const :tag "none" nil)
612                  (regexp :value ".*")
613                  (repeat :value (".*") regexp)))
614
615 (defcustom gnus-summary-mark-below 0
616   "*Mark all articles with a score below this variable as read.
617 This variable is local to each summary buffer and usually set by the
618 score file."
619   :group 'gnus-score-default
620   :type 'integer)
621
622 (defcustom gnus-article-sort-functions '(gnus-article-sort-by-number)
623   "*List of functions used for sorting articles in the summary buffer.
624
625 Each function takes two articles and returns non-nil if the first
626 article should be sorted before the other.  If you use more than one
627 function, the primary sort function should be the last.  You should
628 probably always include `gnus-article-sort-by-number' in the list of
629 sorting functions -- preferably first.  Also note that sorting by date
630 is often much slower than sorting by number, and the sorting order is
631 very similar.  (Sorting by date means sorting by the time the message
632 was sent, sorting by number means sorting by arrival time.)
633
634 Ready-made functions include `gnus-article-sort-by-number',
635 `gnus-article-sort-by-author', `gnus-article-sort-by-subject',
636 `gnus-article-sort-by-date' and `gnus-article-sort-by-score'.
637
638 When threading is turned on, the variable `gnus-thread-sort-functions'
639 controls how articles are sorted."
640   :group 'gnus-summary-sort
641   :type '(repeat (choice (function-item gnus-article-sort-by-number)
642                          (function-item gnus-article-sort-by-author)
643                          (function-item gnus-article-sort-by-subject)
644                          (function-item gnus-article-sort-by-date)
645                          (function-item gnus-article-sort-by-score)
646                          (function :tag "other"))))
647
648 (defcustom gnus-thread-sort-functions '(gnus-thread-sort-by-number)
649   "*List of functions used for sorting threads in the summary buffer.
650 By default, threads are sorted by article number.
651
652 Each function takes two threads and returns non-nil if the first
653 thread should be sorted before the other.  If you use more than one
654 function, the primary sort function should be the last.  You should
655 probably always include `gnus-thread-sort-by-number' in the list of
656 sorting functions -- preferably first.  Also note that sorting by date
657 is often much slower than sorting by number, and the sorting order is
658 very similar.  (Sorting by date means sorting by the time the message
659 was sent, sorting by number means sorting by arrival time.)
660
661 Ready-made functions include `gnus-thread-sort-by-number',
662 `gnus-thread-sort-by-author', `gnus-thread-sort-by-subject',
663 `gnus-thread-sort-by-date', `gnus-thread-sort-by-score',
664 `gnus-thread-sort-by-most-recent-number',
665 `gnus-thread-sort-by-most-recent-date', and
666 `gnus-thread-sort-by-total-score' (see `gnus-thread-score-function').
667
668 When threading is turned off, the variable
669 `gnus-article-sort-functions' controls how articles are sorted."
670   :group 'gnus-summary-sort
671   :type '(repeat (choice (function-item gnus-thread-sort-by-number)
672                          (function-item gnus-thread-sort-by-author)
673                          (function-item gnus-thread-sort-by-subject)
674                          (function-item gnus-thread-sort-by-date)
675                          (function-item gnus-thread-sort-by-score)
676                          (function-item gnus-thread-sort-by-total-score)
677                          (function :tag "other"))))
678
679 (defcustom gnus-thread-score-function '+
680   "*Function used for calculating the total score of a thread.
681
682 The function is called with the scores of the article and each
683 subthread and should then return the score of the thread.
684
685 Some functions you can use are `+', `max', or `min'."
686   :group 'gnus-summary-sort
687   :type 'function)
688
689 (defcustom gnus-summary-expunge-below nil
690   "All articles that have a score less than this variable will be expunged.
691 This variable is local to the summary buffers."
692   :group 'gnus-score-default
693   :type '(choice (const :tag "off" nil)
694                  integer))
695
696 (defcustom gnus-thread-expunge-below nil
697   "All threads that have a total score less than this variable will be expunged.
698 See `gnus-thread-score-function' for en explanation of what a
699 \"thread score\" is.
700
701 This variable is local to the summary buffers."
702   :group 'gnus-threading
703   :group 'gnus-score-default
704   :type '(choice (const :tag "off" nil)
705                  integer))
706
707 (defcustom gnus-summary-mode-hook nil
708   "*A hook for Gnus summary mode.
709 This hook is run before any variables are set in the summary buffer."
710   :options '(turn-on-gnus-mailing-list-mode gnus-pick-mode)
711   :group 'gnus-summary-various
712   :type 'hook)
713
714 ;; Extracted from gnus-xmas-redefine in order to preserve user settings
715 (when (featurep 'xemacs)
716   (add-hook 'gnus-summary-mode-hook 'gnus-xmas-summary-menu-add)
717   (add-hook 'gnus-summary-mode-hook 'gnus-xmas-setup-summary-toolbar)
718   (add-hook 'gnus-summary-mode-hook
719             'gnus-xmas-switch-horizontal-scrollbar-off))
720
721 (defcustom gnus-summary-menu-hook nil
722   "*Hook run after the creation of the summary mode menu."
723   :group 'gnus-summary-visual
724   :type 'hook)
725
726 (defcustom gnus-summary-exit-hook nil
727   "*A hook called on exit from the summary buffer.
728 It will be called with point in the group buffer."
729   :group 'gnus-summary-exit
730   :type 'hook)
731
732 (defcustom gnus-summary-prepare-hook nil
733   "*A hook called after the summary buffer has been generated.
734 If you want to modify the summary buffer, you can use this hook."
735   :group 'gnus-summary-various
736   :type 'hook)
737
738 (defcustom gnus-summary-prepared-hook nil
739   "*A hook called as the last thing after the summary buffer has been generated."
740   :group 'gnus-summary-various
741   :type 'hook)
742
743 (defcustom gnus-summary-generate-hook nil
744   "*A hook run just before generating the summary buffer.
745 This hook is commonly used to customize threading variables and the
746 like."
747   :group 'gnus-summary-various
748   :type 'hook)
749
750 (defcustom gnus-select-group-hook nil
751   "*A hook called when a newsgroup is selected.
752
753 If you'd like to simplify subjects like the
754 `gnus-summary-next-same-subject' command does, you can use the
755 following hook:
756
757  (add-hook gnus-select-group-hook
758            (lambda ()
759              (mapcar (lambda (header)
760                        (mail-header-set-subject
761                         header
762                         (gnus-simplify-subject
763                          (mail-header-subject header) 're-only)))
764                      gnus-newsgroup-headers)))"
765   :group 'gnus-group-select
766   :type 'hook)
767
768 (defcustom gnus-select-article-hook nil
769   "*A hook called when an article is selected."
770   :group 'gnus-summary-choose
771   :type 'hook)
772
773 (defcustom gnus-visual-mark-article-hook
774   (list 'gnus-highlight-selected-summary)
775   "*Hook run after selecting an article in the summary buffer.
776 It is meant to be used for highlighting the article in some way.  It
777 is not run if `gnus-visual' is nil."
778   :group 'gnus-summary-visual
779   :type 'hook)
780
781 (defcustom gnus-parse-headers-hook nil
782   "*A hook called before parsing the headers."
783   :group 'gnus-various
784   :type 'hook)
785
786 (defcustom gnus-exit-group-hook nil
787   "*A hook called when exiting summary mode.
788 This hook is not called from the non-updating exit commands like `Q'."
789   :group 'gnus-various
790   :type 'hook)
791
792 (defcustom gnus-summary-update-hook
793   (list 'gnus-summary-highlight-line)
794   "*A hook called when a summary line is changed.
795 The hook will not be called if `gnus-visual' is nil.
796
797 The default function `gnus-summary-highlight-line' will
798 highlight the line according to the `gnus-summary-highlight'
799 variable."
800   :group 'gnus-summary-visual
801   :type 'hook)
802
803 (defcustom gnus-mark-article-hook '(gnus-summary-mark-read-and-unread-as-read)
804   "*A hook called when an article is selected for the first time.
805 The hook is intended to mark an article as read (or unread)
806 automatically when it is selected."
807   :group 'gnus-summary-choose
808   :type 'hook)
809
810 (defcustom gnus-group-no-more-groups-hook nil
811   "*A hook run when returning to group mode having no more (unread) groups."
812   :group 'gnus-group-select
813   :type 'hook)
814
815 (defcustom gnus-ps-print-hook nil
816   "*A hook run before ps-printing something from Gnus."
817   :group 'gnus-summary
818   :type 'hook)
819
820 (defcustom gnus-summary-display-arrow
821   (and (fboundp 'display-graphic-p)
822        (display-graphic-p))
823   "*If non-nil, display an arrow highlighting the current article."
824   :version "21.1"
825   :group 'gnus-summary
826   :type 'boolean)
827
828 (defcustom gnus-summary-selected-face 'gnus-summary-selected-face
829   "Face used for highlighting the current article in the summary buffer."
830   :group 'gnus-summary-visual
831   :type 'face)
832
833 (defcustom gnus-summary-highlight
834   '(((= mark gnus-canceled-mark)
835      . gnus-summary-cancelled-face)
836     ((and (> score default-high)
837           (or (= mark gnus-dormant-mark)
838               (= mark gnus-ticked-mark)))
839      . gnus-summary-high-ticked-face)
840     ((and (< score default-low)
841           (or (= mark gnus-dormant-mark)
842               (= mark gnus-ticked-mark)))
843      . gnus-summary-low-ticked-face)
844     ((or (= mark gnus-dormant-mark)
845          (= mark gnus-ticked-mark))
846      . gnus-summary-normal-ticked-face)
847     ((and (> score default-high) (= mark gnus-ancient-mark))
848      . gnus-summary-high-ancient-face)
849     ((and (< score default-low) (= mark gnus-ancient-mark))
850      . gnus-summary-low-ancient-face)
851     ((= mark gnus-ancient-mark)