Add a new field `location' to bookmarks for non-file bookmarks.
[bpt/emacs.git] / lisp / gnus / gnus-sum.el
CommitLineData
eec82323 1;;; gnus-sum.el --- summary mode commands for Gnus
e84b4b86
TTN
2
3;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
114f9c96 4;; 2005, 2006, 2007, 2008, 2009, 2010 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
5e809f55 11;; GNU Emacs is free software: you can redistribute it and/or modify
eec82323 12;; it under the terms of the GNU General Public License as published by
5e809f55
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
eec82323
LMI
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
01ccbb85 18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
eec82323
LMI
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
5e809f55 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
eec82323
LMI
23
24;;; Commentary:
25
26;;; Code:
27
d09ae6ca
GM
28;; For Emacs < 22.2.
29(eval-and-compile
30 (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
23f87bed 31(eval-when-compile
9efa445f
DN
32 (require 'cl))
33
34(defvar tool-bar-mode)
35(defvar gnus-tmp-header)
5ab7173c 36
eec82323
LMI
37(require 'gnus)
38(require 'gnus-group)
39(require 'gnus-spec)
40(require 'gnus-range)
41(require 'gnus-int)
42(require 'gnus-undo)
6748645f 43(require 'gnus-util)
18c06a99 44(require 'gmm-utils)
16409b0b 45(require 'mm-decode)
08c9a385 46(require 'nnoo)
23f87bed 47
6748645f 48(autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
d4dfaa19 49(autoload 'gnus-cache-write-active "gnus-cache")
23f87bed
MB
50(autoload 'gnus-mailing-list-insinuate "gnus-ml" nil t)
51(autoload 'turn-on-gnus-mailing-list-mode "gnus-ml" nil t)
531e5812 52(autoload 'gnus-pick-line-number "gnus-salt" nil t)
08c9a385 53(autoload 'mm-uu-dissect "mm-uu")
23f87bed
MB
54(autoload 'gnus-article-outlook-deuglify-article "deuglify"
55 "Deuglify broken Outlook (Express) articles and redisplay."
56 t)
57(autoload 'gnus-article-outlook-unwrap-lines "deuglify" nil t)
58(autoload 'gnus-article-outlook-repair-attribution "deuglify" nil t)
59(autoload 'gnus-article-outlook-rearrange-citation "deuglify" nil t)
eec82323
LMI
60
61(defcustom gnus-kill-summary-on-exit t
62 "*If non-nil, kill the summary buffer when you exit from it.
63If nil, the summary will become a \"*Dead Summary*\" buffer, and
64it will be killed sometime later."
65 :group 'gnus-summary-exit
66 :type 'boolean)
67
01c52d31
MB
68(defcustom gnus-summary-next-group-on-exit t
69 "If non-nil, go to the next unread newsgroup on summary exit.
70See `gnus-group-goto-unread'."
71 :link '(custom-manual "(gnus)Group Maneuvering")
72 :group 'gnus-summary-exit
330f707b 73 :version "23.1" ;; No Gnus
01c52d31
MB
74 :type 'boolean)
75
eec82323
LMI
76(defcustom gnus-fetch-old-headers nil
77 "*Non-nil means that Gnus will try to build threads by grabbing old headers.
01c52d31
MB
78If an unread article in the group refers to an older, already
79read (or just marked as read) article, the old article will not
80normally be displayed in the Summary buffer. If this variable is
81t, Gnus will attempt to grab the headers to the old articles, and
82thereby build complete threads. If it has the value `some', all
83old headers will be fetched but only enough headers to connect
84otherwise loose threads will be displayed. This variable can
85also be a number. In that case, no more than that number of old
86headers will be fetched. If it has the value `invisible', all
6748645f 87old headers will be fetched, but none will be displayed.
eec82323 88
01c52d31
MB
89The server has to support NOV for any of this to work.
90
91This feature can seriously impact performance it ignores all
f394fa25
MB
92locally cached header entries. Setting it to t for groups for a
93server that doesn't expire articles (such as news.gmane.org),
94leads to very slow summary generation."
eec82323
LMI
95 :group 'gnus-thread
96 :type '(choice (const :tag "off" nil)
1232b9cb 97 (const :tag "on" t)
eec82323 98 (const some)
1232b9cb 99 (const invisible)
eec82323
LMI
100 number
101 (sexp :menu-tag "other" t)))
102
01c52d31 103(defcustom gnus-refer-thread-limit 500
6748645f
LMI
104 "*The number of old headers to fetch when doing \\<gnus-summary-mode-map>\\[gnus-summary-refer-thread].
105If t, fetch all the available old headers."
106 :group 'gnus-thread
107 :type '(choice number
108 (sexp :menu-tag "other" t)))
109
eec82323
LMI
110(defcustom gnus-summary-make-false-root 'adopt
111 "*nil means that Gnus won't gather loose threads.
112If the root of a thread has expired or been read in a previous
113session, the information necessary to build a complete thread has been
114lost. Instead of having many small sub-threads from this original thread
115scattered all over the summary buffer, Gnus can gather them.
116
117If non-nil, Gnus will try to gather all loose sub-threads from an
118original thread into one large thread.
119
120If this variable is non-nil, it should be one of `none', `adopt',
121`dummy' or `empty'.
122
123If this variable is `none', Gnus will not make a false root, but just
124present the sub-threads after another.
125If this variable is `dummy', Gnus will create a dummy root that will
126have all the sub-threads as children.
127If this variable is `adopt', Gnus will make one of the \"children\"
128the parent and mark all the step-children as such.
129If this variable is `empty', the \"children\" are printed with empty
01ccbb85 130subject fields. (Or rather, they will be printed with a string
eec82323
LMI
131given by the `gnus-summary-same-subject' variable.)"
132 :group 'gnus-thread
133 :type '(choice (const :tag "off" nil)
134 (const none)
135 (const dummy)
136 (const adopt)
137 (const empty)))
138
23f87bed
MB
139(defcustom gnus-summary-make-false-root-always nil
140 "Always make a false dummy root."
bf247b6e 141 :version "22.1"
23f87bed
MB
142 :group 'gnus-thread
143 :type 'boolean)
144
eec82323
LMI
145(defcustom gnus-summary-gather-exclude-subject "^ *$\\|^(none)$"
146 "*A regexp to match subjects to be excluded from loose thread gathering.
147As loose thread gathering is done on subjects only, that means that
148there can be many false gatherings performed. By rooting out certain
149common subjects, gathering might become saner."
150 :group 'gnus-thread
151 :type 'regexp)
152
153(defcustom gnus-summary-gather-subject-limit nil
154 "*Maximum length of subject comparisons when gathering loose threads.
155Use nil to compare full subjects. Setting this variable to a low
156number will help gather threads that have been corrupted by
157newsreaders chopping off subject lines, but it might also mean that
158unrelated articles that have subject that happen to begin with the
159same few characters will be incorrectly gathered.
160
161If this variable is `fuzzy', Gnus will use a fuzzy algorithm when
162comparing subjects."
163 :group 'gnus-thread
164 :type '(choice (const :tag "off" nil)
165 (const fuzzy)
166 (sexp :menu-tag "on" t)))
167
6748645f
LMI
168(defcustom gnus-simplify-subject-functions nil
169 "List of functions taking a string argument that simplify subjects.
170The functions are applied recursively.
171
23f87bed
MB
172Useful functions to put in this list include:
173`gnus-simplify-subject-re', `gnus-simplify-subject-fuzzy',
174`gnus-simplify-whitespace', and `gnus-simplify-all-whitespace'."
6748645f
LMI
175 :group 'gnus-thread
176 :type '(repeat function))
177
eec82323 178(defcustom gnus-simplify-ignored-prefixes nil
23f87bed 179 "*Remove matches for this regexp from subject lines when simplifying fuzzily."
eec82323
LMI
180 :group 'gnus-thread
181 :type '(choice (const :tag "off" nil)
182 regexp))
183
184(defcustom gnus-build-sparse-threads nil
185 "*If non-nil, fill in the gaps in threads.
186If `some', only fill in the gaps that are needed to tie loose threads
187together. If `more', fill in all leaf nodes that Gnus can find. If
188non-nil and non-`some', fill in all gaps that Gnus manages to guess."
189 :group 'gnus-thread
190 :type '(choice (const :tag "off" nil)
191 (const some)
192 (const more)
193 (sexp :menu-tag "all" t)))
194
195(defcustom gnus-summary-thread-gathering-function
196 'gnus-gather-threads-by-subject
6748645f 197 "*Function used for gathering loose threads.
eec82323
LMI
198There are two pre-defined functions: `gnus-gather-threads-by-subject',
199which only takes Subjects into consideration; and
200`gnus-gather-threads-by-references', which compared the References
201headers of the articles to find matches."
202 :group 'gnus-thread
22115a9e
RS
203 :type '(radio (function-item gnus-gather-threads-by-subject)
204 (function-item gnus-gather-threads-by-references)
205 (function :tag "other")))
eec82323 206
eec82323
LMI
207(defcustom gnus-summary-same-subject ""
208 "*String indicating that the current article has the same subject as the previous.
209This variable will only be used if the value of
210`gnus-summary-make-false-root' is `empty'."
211 :group 'gnus-summary-format
212 :type 'string)
213
214(defcustom gnus-summary-goto-unread t
16409b0b
GM
215 "*If t, many commands will go to the next unread article.
216This applies to marking commands as well as other commands that
217\"naturally\" select the next article, like, for instance, `SPC' at
218the end of an article.
219
220If nil, the marking commands do NOT go to the next unread article
2642ac8f 221\(they go to the next article instead). If `never', commands that
16409b0b
GM
222usually go to the next unread article, will go to the next article,
223whether it is read or not."
eec82323
LMI
224 :group 'gnus-summary-marks
225 :link '(custom-manual "(gnus)Setting Marks")
226 :type '(choice (const :tag "off" nil)
227 (const never)
228 (sexp :menu-tag "on" t)))
229
230(defcustom gnus-summary-default-score 0
231 "*Default article score level.
232All scores generated by the score files will be added to this score.
233If this variable is nil, scoring will be disabled."
234 :group 'gnus-score-default
235 :type '(choice (const :tag "disable")
236 integer))
237
23f87bed
MB
238(defcustom gnus-summary-default-high-score 0
239 "*Default threshold for a high scored article.
240An article will be highlighted as high scored if its score is greater
241than this score."
bf247b6e 242 :version "22.1"
23f87bed
MB
243 :group 'gnus-score-default
244 :type 'integer)
245
246(defcustom gnus-summary-default-low-score 0
247 "*Default threshold for a low scored article.
248An article will be highlighted as low scored if its score is smaller
249than this score."
bf247b6e 250 :version "22.1"
23f87bed
MB
251 :group 'gnus-score-default
252 :type 'integer)
253
eec82323
LMI
254(defcustom gnus-summary-zcore-fuzz 0
255 "*Fuzziness factor for the zcore in the summary buffer.
256Articles with scores closer than this to `gnus-summary-default-score'
257will not be marked."
258 :group 'gnus-summary-format
259 :type 'integer)
260
261(defcustom gnus-simplify-subject-fuzzy-regexp nil
262 "*Strings to be removed when doing fuzzy matches.
263This can either be a regular expression or list of regular expressions
264that will be removed from subject strings if fuzzy subject
265simplification is selected."
266 :group 'gnus-thread
267 :type '(repeat regexp))
268
269(defcustom gnus-show-threads t
270 "*If non-nil, display threads in summary mode."
271 :group 'gnus-thread
272 :type 'boolean)
273
274(defcustom gnus-thread-hide-subtree nil
275 "*If non-nil, hide all threads initially.
23f87bed 276This can be a predicate specifier which says which threads to hide.
eec82323 277If threads are hidden, you have to run the command
4a2358e9 278`gnus-summary-show-thread' by hand or select an article."
eec82323 279 :group 'gnus-thread
23f87bed
MB
280 :type '(radio (sexp :format "Non-nil\n"
281 :match (lambda (widget value)
282 (not (or (consp value) (functionp value))))
283 :value t)
284 (const nil)
ad136a7c 285 (sexp :tag "Predicate specifier")))
eec82323
LMI
286
287(defcustom gnus-thread-hide-killed t
288 "*If non-nil, hide killed threads automatically."
289 :group 'gnus-thread
290 :type 'boolean)
291
6748645f
LMI
292(defcustom gnus-thread-ignore-subject t
293 "*If non-nil, which is the default, ignore subjects and do all threading based on the Reference header.
294If nil, articles that have different subjects from their parents will
295start separate threads."
eec82323
LMI
296 :group 'gnus-thread
297 :type 'boolean)
298
299(defcustom gnus-thread-operation-ignore-subject t
300 "*If non-nil, subjects will be ignored when doing thread commands.
301This affects commands like `gnus-summary-kill-thread' and
302`gnus-summary-lower-thread'.
303
304If this variable is nil, articles in the same thread with different
305subjects will not be included in the operation in question. If this
306variable is `fuzzy', only articles that have subjects that are fuzzily
307equal will be included."
308 :group 'gnus-thread
309 :type '(choice (const :tag "off" nil)
310 (const fuzzy)
311 (sexp :tag "on" t)))
312
313(defcustom gnus-thread-indent-level 4
314 "*Number that says how much each sub-thread should be indented."
315 :group 'gnus-thread
316 :type 'integer)
317
318(defcustom gnus-auto-extend-newsgroup t
319 "*If non-nil, extend newsgroup forward and backward when requested."
320 :group 'gnus-summary-choose
321 :type 'boolean)
322
323(defcustom gnus-auto-select-first t
651408cb
MB
324 "If non-nil, select an article on group entry.
325An article is selected automatically when entering a group
326e.g. with \\<gnus-group-mode-map>\\[gnus-group-read-group], or via `gnus-summary-next-page' or
327`gnus-summary-catchup-and-goto-next-group'.
328
329Which article is selected is controlled by the variable
330`gnus-auto-select-subject'.
23f87bed
MB
331
332If you want to prevent automatic selection of articles in some
333newsgroups, set the variable to nil in `gnus-select-group-hook'."
651408cb
MB
334 ;; Commands include...
335 ;; \\<gnus-group-mode-map>\\[gnus-group-read-group]
336 ;; \\<gnus-summary-mode-map>\\[gnus-summary-next-page]
337 ;; \\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group]
eec82323
LMI
338 :group 'gnus-group-select
339 :type '(choice (const :tag "none" nil)
23f87bed
MB
340 (sexp :menu-tag "first" t)))
341
342(defcustom gnus-auto-select-subject 'unread
343 "*Says what subject to place under point when entering a group.
344
345This variable can either be the symbols `first' (place point on the
346first subject), `unread' (place point on the subject line of the first
347unread article), `best' (place point on the subject line of the
348higest-scored article), `unseen' (place point on the subject line of
99b5aab7 349the first unseen article), `unseen-or-unread' (place point on the subject
23f87bed
MB
350line of the first unseen article or, if all article have been seen, on the
351subject line of the first unread article), or a function to be called to
352place point on some subject line."
bf247b6e 353 :version "22.1"
23f87bed
MB
354 :group 'gnus-group-select
355 :type '(choice (const best)
356 (const unread)
357 (const first)
358 (const unseen)
359 (const unseen-or-unread)))
eec82323
LMI
360
361(defcustom gnus-auto-select-next t
362 "*If non-nil, offer to go to the next group from the end of the previous.
363If the value is t and the next newsgroup is empty, Gnus will exit
23f87bed
MB
364summary mode and go back to group mode. If the value is neither nil
365nor t, Gnus will select the following unread newsgroup. In
eec82323
LMI
366particular, if the value is the symbol `quietly', the next unread
367newsgroup will be selected without any confirmation, and if it is
368`almost-quietly', the next group will be selected without any
369confirmation if you are located on the last article in the group.
23f87bed 370Finally, if this variable is `slightly-quietly', the `\\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group]' command
eec82323
LMI
371will go to the next group without confirmation."
372 :group 'gnus-summary-maneuvering
373 :type '(choice (const :tag "off" nil)
374 (const quietly)
375 (const almost-quietly)
376 (const slightly-quietly)
377 (sexp :menu-tag "on" t)))
378
379(defcustom gnus-auto-select-same nil
6748645f
LMI
380 "*If non-nil, select the next article with the same subject.
381If there are no more articles with the same subject, go to
382the first unread article."
eec82323
LMI
383 :group 'gnus-summary-maneuvering
384 :type 'boolean)
385
01c52d31
MB
386(defcustom gnus-auto-select-on-ephemeral-exit 'next-noselect
387 "What article should be selected after exiting an ephemeral group.
388Valid values include:
389
390`next'
391 Select the next article.
392`next-unread'
393 Select the next unread article.
394`next-noselect'
395 Move the cursor to the next article. This is the default.
396`next-unread-noselect'
397 Move the cursor to the next unread article.
398
399If it has any other value or there is no next (unread) article, the
400article selected before entering to the ephemeral group will appear."
330f707b 401 :version "23.1" ;; No Gnus
01c52d31
MB
402 :group 'gnus-summary-maneuvering
403 :type '(choice :format "%{%t%}:\n %[Value Menu%] %v"
404 (const next) (const next-unread)
405 (const next-noselect) (const next-unread-noselect)
406 (sexp :tag "other" :value nil)))
407
23f87bed
MB
408(defcustom gnus-auto-goto-ignores 'unfetched
409 "*Says how to handle unfetched articles when maneuvering.
410
411This variable can either be the symbols nil (maneuver to any
412article), `undownloaded' (maneuvering while unplugged ignores articles
413that have not been fetched), `always-undownloaded' (maneuvering always
414ignores articles that have not been fetched), `unfetched' (maneuvering
415ignores articles whose headers have not been fetched).
416
417NOTE: The list of unfetched articles will always be nil when plugged
418and, when unplugged, a subset of the undownloaded article list."
bf247b6e 419 :version "22.1"
23f87bed
MB
420 :group 'gnus-summary-maneuvering
421 :type '(choice (const :tag "None" nil)
422 (const :tag "Undownloaded when unplugged" undownloaded)
423 (const :tag "Undownloaded" always-undownloaded)
424 (const :tag "Unfetched" unfetched)))
425
eec82323
LMI
426(defcustom gnus-summary-check-current nil
427 "*If non-nil, consider the current article when moving.
428The \"unread\" movement commands will stay on the same line if the
429current article is unread."
430 :group 'gnus-summary-maneuvering
431 :type 'boolean)
432
01c52d31 433(defcustom gnus-auto-center-summary 2
eec82323
LMI
434 "*If non-nil, always center the current summary buffer.
435In particular, if `vertical' do only vertical recentering. If non-nil
436and non-`vertical', do both horizontal and vertical recentering."
437 :group 'gnus-summary-maneuvering
438 :type '(choice (const :tag "none" nil)
439 (const vertical)
16409b0b 440 (integer :tag "height")
eec82323
LMI
441 (sexp :menu-tag "both" t)))
442
23f87bed
MB
443(defvar gnus-auto-center-group t
444 "*If non-nil, always center the group buffer.")
445
eec82323
LMI
446(defcustom gnus-show-all-headers nil
447 "*If non-nil, don't hide any headers."
448 :group 'gnus-article-hiding
449 :group 'gnus-article-headers
450 :type 'boolean)
451
452(defcustom gnus-summary-ignore-duplicates nil
453 "*If non-nil, ignore articles with identical Message-ID headers."
454 :group 'gnus-summary
455 :type 'boolean)
6748645f 456
eec82323
LMI
457(defcustom gnus-single-article-buffer t
458 "*If non-nil, display all articles in the same buffer.
459If nil, each group will get its own article buffer."
460 :group 'gnus-article-various
461 :type 'boolean)
462
463(defcustom gnus-break-pages t
464 "*If non-nil, do page breaking on articles.
465The page delimiter is specified by the `gnus-page-delimiter'
466variable."
467 :group 'gnus-article-various
468 :type 'boolean)
469
eec82323
LMI
470(defcustom gnus-move-split-methods nil
471 "*Variable used to suggest where articles are to be moved to.
23f87bed
MB
472It uses the same syntax as the `gnus-split-methods' variable.
473However, whereas `gnus-split-methods' specifies file names as targets,
474this variable specifies group names."
eec82323 475 :group 'gnus-summary-mail
6748645f
LMI
476 :type '(repeat (choice (list :value (fun) function)
477 (cons :value ("" "") regexp (repeat string))
478 (sexp :value nil))))
eec82323 479
01c52d31
MB
480(defcustom gnus-move-group-prefix-function 'gnus-group-real-prefix
481 "Function used to compute default prefix for article move/copy/etc prompts.
482The function should take one argument, a group name, and return a
483string with the suggested prefix."
484 :group 'gnus-summary-mail
485 :type 'function)
486
e62e7654
MB
487;; FIXME: Although the custom type is `character' for the following variables,
488;; using multibyte characters (Latin-1, UTF-8) doesn't work. -- rs
489
23f87bed 490(defcustom gnus-unread-mark ? ;Whitespace
eec82323
LMI
491 "*Mark used for unread articles."
492 :group 'gnus-summary-marks
493 :type 'character)
494
495(defcustom gnus-ticked-mark ?!
496 "*Mark used for ticked articles."
497 :group 'gnus-summary-marks
498 :type 'character)
499
500(defcustom gnus-dormant-mark ??
501 "*Mark used for dormant articles."
502 :group 'gnus-summary-marks
503 :type 'character)
504
505(defcustom gnus-del-mark ?r
506 "*Mark used for del'd articles."
507 :group 'gnus-summary-marks
508 :type 'character)
509
510(defcustom gnus-read-mark ?R
511 "*Mark used for read articles."
512 :group 'gnus-summary-marks
513 :type 'character)
514
515(defcustom gnus-expirable-mark ?E
516 "*Mark used for expirable articles."
517 :group 'gnus-summary-marks
518 :type 'character)
519
520(defcustom gnus-killed-mark ?K
521 "*Mark used for killed articles."
522 :group 'gnus-summary-marks
523 :type 'character)
524
23f87bed
MB
525(defcustom gnus-spam-mark ?$
526 "*Mark used for spam articles."
bf247b6e 527 :version "22.1"
23f87bed
MB
528 :group 'gnus-summary-marks
529 :type 'character)
530
eec82323 531(defcustom gnus-souped-mark ?F
23f87bed 532 "*Mark used for souped articles."
eec82323
LMI
533 :group 'gnus-summary-marks
534 :type 'character)
535
536(defcustom gnus-kill-file-mark ?X
537 "*Mark used for articles killed by kill files."
538 :group 'gnus-summary-marks
539 :type 'character)
540
541(defcustom gnus-low-score-mark ?Y
542 "*Mark used for articles with a low score."
543 :group 'gnus-summary-marks
544 :type 'character)
545
546(defcustom gnus-catchup-mark ?C
547 "*Mark used for articles that are caught up."
548 :group 'gnus-summary-marks
549 :type 'character)
550
551(defcustom gnus-replied-mark ?A
552 "*Mark used for articles that have been replied to."
553 :group 'gnus-summary-marks
554 :type 'character)
555
23f87bed
MB
556(defcustom gnus-forwarded-mark ?F
557 "*Mark used for articles that have been forwarded."
bf247b6e 558 :version "22.1"
23f87bed
MB
559 :group 'gnus-summary-marks
560 :type 'character)
561
562(defcustom gnus-recent-mark ?N
563 "*Mark used for articles that are recent."
bf247b6e 564 :version "22.1"
23f87bed
MB
565 :group 'gnus-summary-marks
566 :type 'character)
567
eec82323
LMI
568(defcustom gnus-cached-mark ?*
569 "*Mark used for articles that are in the cache."
570 :group 'gnus-summary-marks
571 :type 'character)
572
573(defcustom gnus-saved-mark ?S
23f87bed
MB
574 "*Mark used for articles that have been saved."
575 :group 'gnus-summary-marks
576 :type 'character)
577
578(defcustom gnus-unseen-mark ?.
579 "*Mark used for articles that haven't been seen."
bf247b6e 580 :version "22.1"
23f87bed
MB
581 :group 'gnus-summary-marks
582 :type 'character)
583
584(defcustom gnus-no-mark ? ;Whitespace
585 "*Mark used for articles that have no other secondary mark."
bf247b6e 586 :version "22.1"
eec82323
LMI
587 :group 'gnus-summary-marks
588 :type 'character)
589
590(defcustom gnus-ancient-mark ?O
591 "*Mark used for ancient articles."
592 :group 'gnus-summary-marks
593 :type 'character)
594
595(defcustom gnus-sparse-mark ?Q
596 "*Mark used for sparsely reffed articles."
597 :group 'gnus-summary-marks
598 :type 'character)
599
600(defcustom gnus-canceled-mark ?G
601 "*Mark used for canceled articles."
602 :group 'gnus-summary-marks
603 :type 'character)
604
605(defcustom gnus-duplicate-mark ?M
606 "*Mark used for duplicate articles."
607 :group 'gnus-summary-marks
608 :type 'character)
609
23f87bed 610(defcustom gnus-undownloaded-mark ?-
6748645f 611 "*Mark used for articles that weren't downloaded."
bf247b6e 612 :version "22.1"
6748645f
LMI
613 :group 'gnus-summary-marks
614 :type 'character)
615
23f87bed
MB
616(defcustom gnus-downloaded-mark ?+
617 "*Mark used for articles that were downloaded."
618 :group 'gnus-summary-marks
619 :type 'character)
620
6748645f
LMI
621(defcustom gnus-downloadable-mark ?%
622 "*Mark used for articles that are to be downloaded."
623 :group 'gnus-summary-marks
624 :type 'character)
625
626(defcustom gnus-unsendable-mark ?=
627 "*Mark used for articles that won't be sent."
628 :group 'gnus-summary-marks
629 :type 'character)
630
eec82323
LMI
631(defcustom gnus-score-over-mark ?+
632 "*Score mark used for articles with high scores."
633 :group 'gnus-summary-marks
634 :type 'character)
635
636(defcustom gnus-score-below-mark ?-
637 "*Score mark used for articles with low scores."
638 :group 'gnus-summary-marks
639 :type 'character)
640
23f87bed 641(defcustom gnus-empty-thread-mark ? ;Whitespace
eec82323
LMI
642 "*There is no thread under the article."
643 :group 'gnus-summary-marks
644 :type 'character)
645
646(defcustom gnus-not-empty-thread-mark ?=
647 "*There is a thread under the article."
648 :group 'gnus-summary-marks
649 :type 'character)
650
651(defcustom gnus-view-pseudo-asynchronously nil
652 "*If non-nil, Gnus will view pseudo-articles asynchronously."
653 :group 'gnus-extract-view
654 :type 'boolean)
655
16409b0b
GM
656(defcustom gnus-auto-expirable-marks
657 (list gnus-killed-mark gnus-del-mark gnus-catchup-mark
658 gnus-low-score-mark gnus-ancient-mark gnus-read-mark
659 gnus-souped-mark gnus-duplicate-mark)
660 "*The list of marks converted into expiration if a group is auto-expirable."
58e39d05 661 :version "21.1"
16409b0b
GM
662 :group 'gnus-summary
663 :type '(repeat character))
664
665(defcustom gnus-inhibit-user-auto-expire t
666 "*If non-nil, user marking commands will not mark an article as expirable, even if the group has auto-expire turned on."
58e39d05 667 :version "21.1"
16409b0b
GM
668 :group 'gnus-summary
669 :type 'boolean)
670
b0b63450
MB
671(defcustom gnus-mark-copied-or-moved-articles-as-expirable nil
672 "If non-nil, mark articles copied or moved to auto-expire group as expirable.
673If nil, the expirable marks will be unchanged except that the marks
674will be removed when copying or moving articles to a group that has
675not turned auto-expire on. If non-nil, articles that have been read
676will be marked as expirable when being copied or moved to a group in
677which auto-expire is turned on."
678 :version "23.2"
679 :type 'boolean
680 :group 'gnus-summary-marks)
681
eec82323
LMI
682(defcustom gnus-view-pseudos nil
683 "*If `automatic', pseudo-articles will be viewed automatically.
684If `not-confirm', pseudos will be viewed automatically, and the user
685will not be asked to confirm the command."
686 :group 'gnus-extract-view
687 :type '(choice (const :tag "off" nil)
688 (const automatic)
689 (const not-confirm)))
690
691(defcustom gnus-view-pseudos-separately t
692 "*If non-nil, one pseudo-article will be created for each file to be viewed.
693If nil, all files that use the same viewing command will be given as a
694list of parameters to that command."
695 :group 'gnus-extract-view
696 :type 'boolean)
697
698(defcustom gnus-insert-pseudo-articles t
699 "*If non-nil, insert pseudo-articles when decoding articles."
700 :group 'gnus-extract-view
701 :type 'boolean)
702
703(defcustom gnus-summary-dummy-line-format
23f87bed 704 " %(: :%) %S\n"
eec82323
LMI
705 "*The format specification for the dummy roots in the summary buffer.
706It works along the same lines as a normal formatting string,
707with some simple extensions.
708
23f87bed
MB
709%S The subject
710
711General format specifiers can also be used.
712See `(gnus)Formatting Variables'."
713 :link '(custom-manual "(gnus)Formatting Variables")
eec82323
LMI
714 :group 'gnus-threading
715 :type 'string)
716
16409b0b 717(defcustom gnus-summary-mode-line-format "Gnus: %g [%A] %Z"
eec82323
LMI
718 "*The format specification for the summary mode line.
719It works along the same lines as a normal formatting string,
720with some simple extensions:
721
722%G Group name
723%p Unprefixed group name
724%A Current article number
6748645f 725%z Current article score
eec82323
LMI
726%V Gnus version
727%U Number of unread articles in the group
728%e Number of unselected articles in the group
729%Z A string with unread/unselected article counts
730%g Shortish group name
731%S Subject of the current article
732%u User-defined spec
733%s Current score file name
734%d Number of dormant articles
735%r Number of articles that have been marked as read in this session
736%E Number of articles expunged by the score files"
737 :group 'gnus-summary-format
738 :type 'string)
739
16409b0b
GM
740(defcustom gnus-list-identifiers nil
741 "Regexp that matches list identifiers to be removed from subject.
742This can also be a list of regexps."
58e39d05 743 :version "21.1"
16409b0b
GM
744 :group 'gnus-summary-format
745 :group 'gnus-article-hiding
746 :type '(choice (const :tag "none" nil)
747 (regexp :value ".*")
748 (repeat :value (".*") regexp)))
749
eec82323
LMI
750(defcustom gnus-summary-mark-below 0
751 "*Mark all articles with a score below this variable as read.
752This variable is local to each summary buffer and usually set by the
753score file."
754 :group 'gnus-score-default
755 :type 'integer)
756
01c52d31
MB
757(defun gnus-widget-reversible-match (widget value)
758 "Ignoring WIDGET, convert VALUE to internal form.
759VALUE should have the form `FOO' or `(not FOO)', where FOO is an symbol."
760 ;; (debug value)
761 (or (symbolp value)
762 (and (listp value)
763 (eq (length value) 2)
764 (eq (nth 0 value) 'not)
765 (symbolp (nth 1 value)))))
766
767(defun gnus-widget-reversible-to-internal (widget value)
768 "Ignoring WIDGET, convert VALUE to internal form.
769VALUE should have the form `FOO' or `(not FOO)', where FOO is an atom.
770FOO is converted to (FOO nil) and (not FOO) is converted to (FOO t)."
771 ;; (debug value)
772 (if (atom value)
773 (list value nil)
774 (list (nth 1 value) t)))
775
776(defun gnus-widget-reversible-to-external (widget value)
777 "Ignoring WIDGET, convert VALUE to external form.
778VALUE should have the form `(FOO nil)' or `(FOO t)', where FOO is an atom.
779\(FOO nil) is converted to FOO and (FOO t) is converted to (not FOO)."
780 ;; (debug value)
781 (if (nth 1 value)
782 (list 'not (nth 0 value))
783 (nth 0 value)))
784
785(define-widget 'gnus-widget-reversible 'group
786 "A `group' that convert values."
787 :match 'gnus-widget-reversible-match
788 :value-to-internal 'gnus-widget-reversible-to-internal
789 :value-to-external 'gnus-widget-reversible-to-external)
790
eec82323
LMI
791(defcustom gnus-article-sort-functions '(gnus-article-sort-by-number)
792 "*List of functions used for sorting articles in the summary buffer.
23f87bed
MB
793
794Each function takes two articles and returns non-nil if the first
795article should be sorted before the other. If you use more than one
796function, the primary sort function should be the last. You should
797probably always include `gnus-article-sort-by-number' in the list of
798sorting functions -- preferably first. Also note that sorting by date
799is often much slower than sorting by number, and the sorting order is
800very similar. (Sorting by date means sorting by the time the message
801was sent, sorting by number means sorting by arrival time.)
802
01c52d31
MB
803Each item can also be a list `(not F)' where F is a function;
804this reverses the sort order.
805
23f87bed
MB
806Ready-made functions include `gnus-article-sort-by-number',
807`gnus-article-sort-by-author', `gnus-article-sort-by-subject',
808`gnus-article-sort-by-date', `gnus-article-sort-by-random'
809and `gnus-article-sort-by-score'.
810
811When threading is turned on, the variable `gnus-thread-sort-functions'
812controls how articles are sorted."
eec82323 813 :group 'gnus-summary-sort
01c52d31
MB
814 :type '(repeat (gnus-widget-reversible
815 (choice (function-item gnus-article-sort-by-number)
816 (function-item gnus-article-sort-by-author)
817 (function-item gnus-article-sort-by-subject)
818 (function-item gnus-article-sort-by-date)
819 (function-item gnus-article-sort-by-score)
820 (function-item gnus-article-sort-by-random)
821 (function :tag "other"))
822 (boolean :tag "Reverse order"))))
823
eec82323
LMI
824
825(defcustom gnus-thread-sort-functions '(gnus-thread-sort-by-number)
826 "*List of functions used for sorting threads in the summary buffer.
827By default, threads are sorted by article number.
828
23f87bed
MB
829Each function takes two threads and returns non-nil if the first
830thread should be sorted before the other. If you use more than one
831function, the primary sort function should be the last. You should
832probably always include `gnus-thread-sort-by-number' in the list of
833sorting functions -- preferably first. Also note that sorting by date
834is often much slower than sorting by number, and the sorting order is
835very similar. (Sorting by date means sorting by the time the message
836was sent, sorting by number means sorting by arrival time.)
eec82323 837
01c52d31
MB
838Each list item can also be a list `(not F)' where F is a
839function; this specifies reversed sort order.
840
eec82323 841Ready-made functions include `gnus-thread-sort-by-number',
01c52d31
MB
842`gnus-thread-sort-by-author', `gnus-thread-sort-by-recipient'
843`gnus-thread-sort-by-subject', `gnus-thread-sort-by-date',
844`gnus-thread-sort-by-score', `gnus-thread-sort-by-most-recent-number',
845`gnus-thread-sort-by-most-recent-date', `gnus-thread-sort-by-random',
846and `gnus-thread-sort-by-total-score' (see
847`gnus-thread-score-function').
23f87bed
MB
848
849When threading is turned off, the variable
850`gnus-article-sort-functions' controls how articles are sorted."
eec82323 851 :group 'gnus-summary-sort
01c52d31
MB
852 :type '(repeat
853 (gnus-widget-reversible
854 (choice (function-item gnus-thread-sort-by-number)
855 (function-item gnus-thread-sort-by-author)
856 (function-item gnus-thread-sort-by-recipient)
857 (function-item gnus-thread-sort-by-subject)
858 (function-item gnus-thread-sort-by-date)
859 (function-item gnus-thread-sort-by-score)
860 (function-item gnus-thread-sort-by-most-recent-number)
861 (function-item gnus-thread-sort-by-most-recent-date)
862 (function-item gnus-thread-sort-by-random)
863 (function-item gnus-thread-sort-by-total-score)
864 (function :tag "other"))
865 (boolean :tag "Reverse order"))))
eec82323
LMI
866
867(defcustom gnus-thread-score-function '+
868 "*Function used for calculating the total score of a thread.
869
870The function is called with the scores of the article and each
871subthread and should then return the score of the thread.
872
873Some functions you can use are `+', `max', or `min'."
874 :group 'gnus-summary-sort
875 :type 'function)
876
877(defcustom gnus-summary-expunge-below nil
6748645f
LMI
878 "All articles that have a score less than this variable will be expunged.
879This variable is local to the summary buffers."
eec82323
LMI
880 :group 'gnus-score-default
881 :type '(choice (const :tag "off" nil)
882 integer))
883
884(defcustom gnus-thread-expunge-below nil
885 "All threads that have a total score less than this variable will be expunged.
886See `gnus-thread-score-function' for en explanation of what a
6748645f
LMI
887\"thread score\" is.
888
889This variable is local to the summary buffers."
16409b0b 890 :group 'gnus-threading
eec82323
LMI
891 :group 'gnus-score-default
892 :type '(choice (const :tag "off" nil)
893 integer))
894
895(defcustom gnus-summary-mode-hook nil
896 "*A hook for Gnus summary mode.
897This hook is run before any variables are set in the summary buffer."
23f87bed 898 :options '(turn-on-gnus-mailing-list-mode gnus-pick-mode)
eec82323
LMI
899 :group 'gnus-summary-various
900 :type 'hook)
901
23f87bed
MB
902;; Extracted from gnus-xmas-redefine in order to preserve user settings
903(when (featurep 'xemacs)
904 (add-hook 'gnus-summary-mode-hook 'gnus-xmas-summary-menu-add)
905 (add-hook 'gnus-summary-mode-hook 'gnus-xmas-setup-summary-toolbar)
906 (add-hook 'gnus-summary-mode-hook
907 'gnus-xmas-switch-horizontal-scrollbar-off))
908
eec82323
LMI
909(defcustom gnus-summary-menu-hook nil
910 "*Hook run after the creation of the summary mode menu."
911 :group 'gnus-summary-visual
912 :type 'hook)
913
914(defcustom gnus-summary-exit-hook nil
915 "*A hook called on exit from the summary buffer.
916It will be called with point in the group buffer."
917 :group 'gnus-summary-exit
918 :type 'hook)
919
920(defcustom gnus-summary-prepare-hook nil
921 "*A hook called after the summary buffer has been generated.
922If you want to modify the summary buffer, you can use this hook."
923 :group 'gnus-summary-various
924 :type 'hook)
925
6748645f
LMI
926(defcustom gnus-summary-prepared-hook nil
927 "*A hook called as the last thing after the summary buffer has been generated."
928 :group 'gnus-summary-various
929 :type 'hook)
930
eec82323
LMI
931(defcustom gnus-summary-generate-hook nil
932 "*A hook run just before generating the summary buffer.
933This hook is commonly used to customize threading variables and the
934like."
935 :group 'gnus-summary-various
936 :type 'hook)
937
938(defcustom gnus-select-group-hook nil
939 "*A hook called when a newsgroup is selected.
940
941If you'd like to simplify subjects like the
942`gnus-summary-next-same-subject' command does, you can use the
943following hook:
944
23f87bed
MB
945 (add-hook gnus-select-group-hook
946 (lambda ()
947 (mapcar (lambda (header)
948 (mail-header-set-subject
949 header
950 (gnus-simplify-subject
951 (mail-header-subject header) 're-only)))
952 gnus-newsgroup-headers)))"
eec82323
LMI
953 :group 'gnus-group-select
954 :type 'hook)
955
956(defcustom gnus-select-article-hook nil
957 "*A hook called when an article is selected."
958 :group 'gnus-summary-choose
23f87bed 959 :options '(gnus-agent-fetch-selected-article)
eec82323
LMI
960 :type 'hook)
961
962(defcustom gnus-visual-mark-article-hook
963 (list 'gnus-highlight-selected-summary)
964 "*Hook run after selecting an article in the summary buffer.
965It is meant to be used for highlighting the article in some way. It
966is not run if `gnus-visual' is nil."
967 :group 'gnus-summary-visual
968 :type 'hook)
969
16409b0b 970(defcustom gnus-parse-headers-hook nil
eec82323
LMI
971 "*A hook called before parsing the headers."
972 :group 'gnus-various
973 :type 'hook)
974
975(defcustom gnus-exit-group-hook nil
16409b0b
GM
976 "*A hook called when exiting summary mode.
977This hook is not called from the non-updating exit commands like `Q'."
eec82323
LMI
978 :group 'gnus-various
979 :type 'hook)
980
981(defcustom gnus-summary-update-hook
982 (list 'gnus-summary-highlight-line)
983 "*A hook called when a summary line is changed.
984The hook will not be called if `gnus-visual' is nil.
985
986The default function `gnus-summary-highlight-line' will
987highlight the line according to the `gnus-summary-highlight'
988variable."
989 :group 'gnus-summary-visual
990 :type 'hook)
991
992(defcustom gnus-mark-article-hook '(gnus-summary-mark-read-and-unread-as-read)
993 "*A hook called when an article is selected for the first time.
994The hook is intended to mark an article as read (or unread)
995automatically when it is selected."
996 :group 'gnus-summary-choose
997 :type 'hook)
998
999(defcustom gnus-group-no-more-groups-hook nil
1000 "*A hook run when returning to group mode having no more (unread) groups."
1001 :group 'gnus-group-select
1002 :type 'hook)
1003
1004(defcustom gnus-ps-print-hook nil
1005 "*A hook run before ps-printing something from Gnus."
1006 :group 'gnus-summary
1007 :type 'hook)
1008
23f87bed
MB
1009(defcustom gnus-summary-article-move-hook nil
1010 "*A hook called after an article is moved, copied, respooled, or crossposted."
bf247b6e 1011 :version "22.1"
23f87bed
MB
1012 :group 'gnus-summary
1013 :type 'hook)
1014
1015(defcustom gnus-summary-article-delete-hook nil
1016 "*A hook called after an article is deleted."
bf247b6e 1017 :version "22.1"
23f87bed
MB
1018 :group 'gnus-summary
1019 :type 'hook)
1020
1021(defcustom gnus-summary-article-expire-hook nil
1022 "*A hook called after an article is expired."
bf247b6e 1023 :version "22.1"
23f87bed
MB
1024 :group 'gnus-summary
1025 :type 'hook)
1026
1027(defcustom gnus-summary-display-arrow
1028 (and (fboundp 'display-graphic-p)
1029 (display-graphic-p))
1030 "*If non-nil, display an arrow highlighting the current article."
bf247b6e 1031 :version "22.1"
23f87bed
MB
1032 :group 'gnus-summary
1033 :type 'boolean)
1034
0f49874b 1035(defcustom gnus-summary-selected-face 'gnus-summary-selected
eec82323
LMI
1036 "Face used for highlighting the current article in the summary buffer."
1037 :group 'gnus-summary-visual
1038 :type 'face)
1039
23f87bed
MB
1040(defvar gnus-tmp-downloaded nil)
1041
eec82323 1042(defcustom gnus-summary-highlight
23f87bed 1043 '(((eq mark gnus-canceled-mark)
0f49874b 1044 . gnus-summary-cancelled)
23f87bed 1045 ((and uncached (> score default-high))
0f49874b 1046 . gnus-summary-high-undownloaded)
23f87bed 1047 ((and uncached (< score default-low))
0f49874b 1048 . gnus-summary-low-undownloaded)
23f87bed 1049 (uncached
0f49874b 1050 . gnus-summary-normal-undownloaded)
23f87bed
MB
1051 ((and (> score default-high)
1052 (or (eq mark gnus-dormant-mark)
1053 (eq mark gnus-ticked-mark)))
0f49874b 1054 . gnus-summary-high-ticked)
23f87bed
MB
1055 ((and (< score default-low)
1056 (or (eq mark gnus-dormant-mark)
1057 (eq mark gnus-ticked-mark)))
0f49874b 1058 . gnus-summary-low-ticked)
23f87bed
MB
1059 ((or (eq mark gnus-dormant-mark)
1060 (eq mark gnus-ticked-mark))
0f49874b 1061 . gnus-summary-normal-ticked)
23f87bed 1062 ((and (> score default-high) (eq mark gnus-ancient-mark))
0f49874b 1063 . gnus-summary-high-ancient)
23f87bed 1064 ((and (< score default-low) (eq mark gnus-ancient-mark))
0f49874b 1065 . gnus-summary-low-ancient)
23f87bed 1066 ((eq mark gnus-ancient-mark)
0f49874b 1067 . gnus-summary-normal-ancient)
23f87bed 1068 ((and (> score default-high) (eq mark gnus-unread-mark))
0f49874b 1069 . gnus-summary-high-unread)
23f87bed 1070 ((and (< score default-low) (eq mark gnus-unread-mark))
0f49874b 1071 . gnus-summary-low-unread)
23f87bed 1072 ((eq mark gnus-unread-mark)
0f49874b 1073 . gnus-summary-normal-unread)
23f87bed 1074 ((> score default-high)
0f49874b 1075 . gnus-summary-high-read)
23f87bed 1076 ((< score default-low)
0f49874b 1077 . gnus-summary-low-read)
eec82323 1078 (t
0f49874b 1079 . gnus-summary-normal-read))
6748645f 1080 "*Controls the highlighting of summary buffer lines.
eec82323 1081
107cf8ec 1082A list of (FORM . FACE) pairs. When deciding how a particular
23f87bed
MB
1083summary line should be displayed, each form is evaluated. The content
1084of the face field after the first true form is used. You can change
1085how those summary lines are displayed, by editing the face field.
eec82323
LMI
1086
1087You can use the following variables in the FORM field.
1088
107cf8ec 1089score: The article's score.
23f87bed
MB
1090default: The default article score.
1091default-high: The default score for high scored articles.
1092default-low: The default score for low scored articles.
1093below: The score below which articles are automatically marked as read.
1094mark: The article's mark.
1095uncached: Non-nil if the article is uncached."
eec82323
LMI
1096 :group 'gnus-summary-visual
1097 :type '(repeat (cons (sexp :tag "Form" nil)
1098 face)))
c12ecb0a 1099(put 'gnus-summary-highlight 'risky-local-variable t)
eec82323 1100
6748645f
LMI
1101(defcustom gnus-alter-header-function nil
1102 "Function called to allow alteration of article header structures.
1103The function is called with one parameter, the article header vector,
0ab0f2d3
SZ
1104which it may alter in any way."
1105 :type '(choice (const :tag "None" nil)
1106 function)
1107 :group 'gnus-summary)
eec82323 1108
16409b0b 1109(defvar gnus-decode-encoded-word-function 'mail-decode-encoded-word-string
343d6628
MB
1110 "Function used to decode a string with encoded words.")
1111
1112(defvar gnus-decode-encoded-address-function
1113 'mail-decode-encoded-address-string
1114 "Function used to decode addresses with encoded words.")
16409b0b 1115
23f87bed 1116(defcustom gnus-extra-headers '(To Newsgroups)
16409b0b 1117 "*Extra headers to parse."
58e39d05 1118 :version "21.1"
16409b0b
GM
1119 :group 'gnus-summary
1120 :type '(repeat symbol))
1121
1122(defcustom gnus-ignored-from-addresses
343d6628 1123 (and user-mail-address
7cd9f860
CY
1124 (not (string= user-mail-address ""))
1125 (regexp-quote user-mail-address))
01c52d31
MB
1126 "*From headers that may be suppressed in favor of To headers.
1127This can be a regexp or a list of regexps."
58e39d05 1128 :version "21.1"
16409b0b 1129 :group 'gnus-summary
01c52d31
MB
1130 :type '(choice regexp
1131 (repeat :tag "Regexp List" regexp)))
1132
1133(defsubst gnus-ignored-from-addresses ()
1134 (gmm-regexp-concat gnus-ignored-from-addresses))
1135
1136(defcustom gnus-summary-to-prefix "-> "
1137 "*String prefixed to the To field in the summary line when
1138using `gnus-ignored-from-addresses'."
1139 :version "22.1"
1140 :group 'gnus-summary
1141 :type 'string)
1142
1143(defcustom gnus-summary-newsgroup-prefix "=> "
1144 "*String prefixed to the Newsgroup field in the summary
1145line when using `gnus-ignored-from-addresses'."
1146 :version "22.1"
1147 :group 'gnus-summary
1148 :type 'string)
16409b0b 1149
16409b0b
GM
1150(defcustom gnus-newsgroup-ignored-charsets '(unknown-8bit x-unknown)
1151 "List of charsets that should be ignored.
1152When these charsets are used in the \"charset\" parameter, the
1153default charset will be used instead."
58e39d05 1154 :version "21.1"
16409b0b
GM
1155 :type '(repeat symbol)
1156 :group 'gnus-charset)
1157
4b70e299
MB
1158(defcustom gnus-newsgroup-maximum-articles nil
1159 "The maximum number of articles a newsgroup.
1160If this is a number, old articles in a newsgroup exceeding this number
1161are silently ignored. If it is nil, no article is ignored. Note that
1162setting this variable to a number might prevent you from reading very
1163old articles."
1164 :group 'gnus-group-select
1165 :version "22.2"
1166 :type '(choice (const :tag "No limit" nil)
1167 integer))
1168
23f87bed
MB
1169(gnus-define-group-parameter
1170 ignored-charsets
1171 :type list
1172 :function-document
1173 "Return the ignored charsets of GROUP."
1174 :variable gnus-group-ignored-charsets-alist
1175 :variable-default
1176 '(("alt\\.chinese\\.text" iso-8859-1))
1177 :variable-document
1178 "Alist of regexps (to match group names) and charsets that should be ignored.
16409b0b
GM
1179When these charsets are used in the \"charset\" parameter, the
1180default charset will be used instead."
23f87bed
MB
1181 :variable-group gnus-charset
1182 :variable-type '(repeat (cons (regexp :tag "Group")
1183 (repeat symbol)))
1184 :parameter-type '(choice :tag "Ignored charsets"
1185 :value nil
1186 (repeat (symbol)))
1187 :parameter-document "\
1188List of charsets that should be ignored.
1189
1190When these charsets are used in the \"charset\" parameter, the
1191default charset will be used instead.")
16409b0b
GM
1192
1193(defcustom gnus-group-highlight-words-alist nil
1194 "Alist of group regexps and highlight regexps.
1195This variable uses the same syntax as `gnus-emphasis-alist'."
58e39d05 1196 :version "21.1"
16409b0b
GM
1197 :type '(repeat (cons (regexp :tag "Group")
1198 (repeat (list (regexp :tag "Highlight regexp")
1199 (number :tag "Group for entire word" 0)
1200 (number :tag "Group for displayed part" 0)
1201 (symbol :tag "Face"
1202 gnus-emphasis-highlight-words)))))
1203 :group 'gnus-summary-visual)
1204
1205(defcustom gnus-summary-show-article-charset-alist
1206 nil
1207 "Alist of number and charset.
1208The article will be shown with the charset corresponding to the
1209numbered argument.
1210For example: ((1 . cn-gb-2312) (2 . big5))."
58e39d05 1211 :version "21.1"
16409b0b
GM
1212 :type '(repeat (cons (number :tag "Argument" 1)
1213 (symbol :tag "Charset")))
1214 :group 'gnus-charset)
1215
1216(defcustom gnus-preserve-marks t
1217 "Whether marks are preserved when moving, copying and respooling messages."
58e39d05 1218 :version "21.1"
16409b0b
GM
1219 :type 'boolean
1220 :group 'gnus-summary-marks)
1221
3a23a519
MB
1222(defcustom gnus-propagate-marks t
1223 "If non-nil, do not propagate marks to the backends."
f8a29505 1224 :version "23.1" ;; No Gnus
3a23a519
MB
1225 :type 'boolean
1226 :group 'gnus-summary-marks)
1227
16409b0b
GM
1228(defcustom gnus-alter-articles-to-read-function nil
1229 "Function to be called to alter the list of articles to be selected."
8fc7a9a1 1230 :type '(choice (const nil) function)
16409b0b
GM
1231 :group 'gnus-summary)
1232
1233(defcustom gnus-orphan-score nil
1234 "*All orphans get this score added. Set in the score file."
1235 :group 'gnus-score-default
1236 :type '(choice (const nil)
1237 integer))
1238
8b93df01 1239(defcustom gnus-summary-save-parts-default-mime "image/.*"
23f87bed
MB
1240 "*A regexp to match MIME parts when saving multiple parts of a
1241message with `gnus-summary-save-parts' (\\<gnus-summary-mode-map>\\[gnus-summary-save-parts]).
1242This regexp will be used by default when prompting the user for which
1243type of files to save."
8b93df01
DL
1244 :group 'gnus-summary
1245 :type 'regexp)
1246
23f87bed
MB
1247(defcustom gnus-read-all-available-headers nil
1248 "Whether Gnus should parse all headers made available to it.
1249This is mostly relevant for slow back ends where the user may
1250wish to widen the summary buffer to include all headers
1251that were fetched. Say, for nnultimate groups."
bf247b6e 1252 :version "22.1"
23f87bed
MB
1253 :group 'gnus-summary
1254 :type '(choice boolean regexp))
1255
89167438
MB
1256(defcustom gnus-summary-pipe-output-default-command nil
1257 "Command (and optional arguments) used to pipe article to subprocess.
1258This will be used as the default command if it is non-nil. The value
1259will be updated if you modify it when executing the command
1260`gnus-summary-pipe-output' or the function `gnus-summary-save-in-pipe'."
1261 :version "23.1" ;; No Gnus
1262 :group 'gnus-summary
1263 :type '(radio (const :tag "None" nil) (string :tag "Command")))
1264
23f87bed 1265(defcustom gnus-summary-muttprint-program "muttprint"
89167438
MB
1266 "Command (and optional arguments) used to run Muttprint.
1267The value will be updated if you modify it when executing the command
1268`gnus-summary-muttprint'."
bf247b6e 1269 :version "22.1"
23f87bed
MB
1270 :group 'gnus-summary
1271 :type 'string)
1272
01c52d31 1273(defcustom gnus-article-loose-mime t
23f87bed
MB
1274 "If non-nil, don't require MIME-Version header.
1275Some brain-damaged MUA/MTA, e.g. Lotus Domino 5.0.6 clients, does not
a08b59c9 1276supply the MIME-Version header or deliberately strip it from the mail.
01c52d31
MB
1277If non-nil (the default), Gnus will treat some articles as MIME
1278even if the MIME-Version header is missing."
bf247b6e 1279 :version "22.1"
23f87bed
MB
1280 :type 'boolean
1281 :group 'gnus-article-mime)
1282
1283(defcustom gnus-article-emulate-mime t
1284 "If non-nil, use MIME emulation for uuencode and the like.
1285This means that Gnus will search message bodies for text that look
1286like uuencoded bits, yEncoded bits, and so on, and present that using
1287the normal Gnus MIME machinery."
bf247b6e 1288 :version "22.1"
23f87bed
MB
1289 :type 'boolean
1290 :group 'gnus-article-mime)
8b93df01 1291
eec82323
LMI
1292;;; Internal variables
1293
23f87bed 1294(defvar gnus-summary-display-cache nil)
16409b0b
GM
1295(defvar gnus-article-mime-handles nil)
1296(defvar gnus-article-decoded-p nil)
23f87bed
MB
1297(defvar gnus-article-charset nil)
1298(defvar gnus-article-ignored-charsets nil)
eec82323
LMI
1299(defvar gnus-scores-exclude-files nil)
1300(defvar gnus-page-broken nil)
1301
1302(defvar gnus-original-article nil)
1303(defvar gnus-article-internal-prepare-hook nil)
1304(defvar gnus-newsgroup-process-stack nil)
1305
1306(defvar gnus-thread-indent-array nil)
1307(defvar gnus-thread-indent-array-level gnus-thread-indent-level)
16409b0b
GM
1308(defvar gnus-sort-gathered-threads-function 'gnus-thread-sort-by-number
1309 "Function called to sort the articles within a thread after it has been gathered together.")
eec82323 1310
8b93df01 1311(defvar gnus-summary-save-parts-type-history nil)
23f87bed 1312(defvar gnus-summary-save-parts-last-directory mm-default-directory)
8b93df01 1313
eec82323
LMI
1314;; Avoid highlighting in kill files.
1315(defvar gnus-summary-inhibit-highlight nil)
1316(defvar gnus-newsgroup-selected-overlay nil)
1317(defvar gnus-inhibit-limiting nil)
1318(defvar gnus-newsgroup-adaptive-score-file nil)
1319(defvar gnus-current-score-file nil)
1320(defvar gnus-current-move-group nil)
1321(defvar gnus-current-copy-group nil)
1322(defvar gnus-current-crosspost-group nil)
23f87bed 1323(defvar gnus-newsgroup-display nil)
eec82323
LMI
1324
1325(defvar gnus-newsgroup-dependencies nil)
1326(defvar gnus-newsgroup-adaptive nil)
1327(defvar gnus-summary-display-article-function nil)
1328(defvar gnus-summary-highlight-line-function nil
1329 "Function called after highlighting a summary line.")
1330
1331(defvar gnus-summary-line-format-alist
1332 `((?N ,(macroexpand '(mail-header-number gnus-tmp-header)) ?d)
1333 (?S ,(macroexpand '(mail-header-subject gnus-tmp-header)) ?s)
1334 (?s gnus-tmp-subject-or-nil ?s)
1335 (?n gnus-tmp-name ?s)
1336 (?A (car (cdr (funcall gnus-extract-address-components gnus-tmp-from)))
1337 ?s)
1338 (?a (or (car (funcall gnus-extract-address-components gnus-tmp-from))
1339 gnus-tmp-from) ?s)
1340 (?F gnus-tmp-from ?s)
1341 (?x ,(macroexpand '(mail-header-xref gnus-tmp-header)) ?s)
1342 (?D ,(macroexpand '(mail-header-date gnus-tmp-header)) ?s)
1343 (?d (gnus-dd-mmm (mail-header-date gnus-tmp-header)) ?s)
6748645f 1344 (?o (gnus-date-iso8601 (mail-header-date gnus-tmp-header)) ?s)
eec82323
LMI
1345 (?M ,(macroexpand '(mail-header-id gnus-tmp-header)) ?s)
1346 (?r ,(macroexpand '(mail-header-references gnus-tmp-header)) ?s)
1347 (?c (or (mail-header-chars gnus-tmp-header) 0) ?d)
23f87bed
MB
1348 (?k (gnus-summary-line-message-size gnus-tmp-header) ?s)
1349 (?L gnus-tmp-lines ?s)
1350 (?O gnus-tmp-downloaded ?c)
eec82323
LMI
1351 (?I gnus-tmp-indentation ?s)
1352 (?T (if (= gnus-tmp-level 0) "" (make-string (frame-width) ? )) ?s)
1353 (?R gnus-tmp-replied ?c)
1354 (?\[ gnus-tmp-opening-bracket ?c)
1355 (?\] gnus-tmp-closing-bracket ?c)
1356 (?\> (make-string gnus-tmp-level ? ) ?s)
1357 (?\< (make-string (max 0 (- 20 gnus-tmp-level)) ? ) ?s)
1358 (?i gnus-tmp-score ?d)
1359 (?z gnus-tmp-score-char ?c)
eec82323
LMI
1360 (?V (gnus-thread-total-score (and (boundp 'thread) (car thread))) ?d)
1361 (?U gnus-tmp-unread ?c)
23f87bed
MB
1362 (?f (gnus-summary-from-or-to-or-newsgroups gnus-tmp-header gnus-tmp-from)
1363 ?s)
eec82323
LMI
1364 (?t (gnus-summary-number-of-articles-in-thread
1365 (and (boundp 'thread) (car thread)) gnus-tmp-level)
1366 ?d)
1367 (?e (gnus-summary-number-of-articles-in-thread
1368 (and (boundp 'thread) (car thread)) gnus-tmp-level t)
1369 ?c)
1370 (?u gnus-tmp-user-defined ?s)
23f87bed
MB
1371 (?P (gnus-pick-line-number) ?d)
1372 (?B gnus-tmp-thread-tree-header-string ?s)
1373 (user-date (gnus-user-date
1374 ,(macroexpand '(mail-header-date gnus-tmp-header))) ?s))
16409b0b
GM
1375 "An alist of format specifications that can appear in summary lines.
1376These are paired with what variables they correspond with, along with
1377the type of the variable (string, integer, character, etc).")
eec82323
LMI
1378
1379(defvar gnus-summary-dummy-line-format-alist
1380 `((?S gnus-tmp-subject ?s)
1381 (?N gnus-tmp-number ?d)
1382 (?u gnus-tmp-user-defined ?s)))
1383
1384(defvar gnus-summary-mode-line-format-alist
1385 `((?G gnus-tmp-group-name ?s)
1386 (?g (gnus-short-group-name gnus-tmp-group-name) ?s)
1387 (?p (gnus-group-real-name gnus-tmp-group-name) ?s)
1388 (?A gnus-tmp-article-number ?d)
1389 (?Z gnus-tmp-unread-and-unselected ?s)
1390 (?V gnus-version ?s)
1391 (?U gnus-tmp-unread-and-unticked ?d)
1392 (?S gnus-tmp-subject ?s)
1393 (?e gnus-tmp-unselected ?d)
1394 (?u gnus-tmp-user-defined ?s)
1395 (?d (length gnus-newsgroup-dormant) ?d)
1396 (?t (length gnus-newsgroup-marked) ?d)
23f87bed 1397 (?h (length gnus-newsgroup-spam-marked) ?d)
eec82323 1398 (?r (length gnus-newsgroup-reads) ?d)
6748645f 1399 (?z (gnus-summary-article-score gnus-tmp-article-number) ?d)
eec82323
LMI
1400 (?E gnus-newsgroup-expunged-tally ?d)
1401 (?s (gnus-current-score-file-nondirectory) ?s)))
1402
c1717fbd
GM
1403;; This is here rather than in gnus-art for compilation reasons.
1404(defvar gnus-article-mode-line-format-alist
1405 (nconc '((?w (gnus-article-wash-status) ?s)
1406 (?m (gnus-article-mime-part-status) ?s))
1407 gnus-summary-mode-line-format-alist))
1408
eec82323
LMI
1409(defvar gnus-last-search-regexp nil
1410 "Default regexp for article search command.")
1411
1412(defvar gnus-last-shell-command nil
1413 "Default shell command on article.")
1414
23f87bed
MB
1415(defvar gnus-newsgroup-agentized nil
1416 "Locally bound in each summary buffer to indicate whether the server has been agentized.")
eec82323
LMI
1417(defvar gnus-newsgroup-begin nil)
1418(defvar gnus-newsgroup-end nil)
1419(defvar gnus-newsgroup-last-rmail nil)
1420(defvar gnus-newsgroup-last-mail nil)
1421(defvar gnus-newsgroup-last-folder nil)
1422(defvar gnus-newsgroup-last-file nil)
26c9afc3 1423(defvar gnus-newsgroup-last-directory nil)
eec82323
LMI
1424(defvar gnus-newsgroup-auto-expire nil)
1425(defvar gnus-newsgroup-active nil)
1426
1427(defvar gnus-newsgroup-data nil)
1428(defvar gnus-newsgroup-data-reverse nil)
1429(defvar gnus-newsgroup-limit nil)
1430(defvar gnus-newsgroup-limits nil)
23f87bed 1431(defvar gnus-summary-use-undownloaded-faces nil)
eec82323
LMI
1432
1433(defvar gnus-newsgroup-unreads nil
23f87bed 1434 "Sorted list of unread articles in the current newsgroup.")
eec82323
LMI
1435
1436(defvar gnus-newsgroup-unselected nil
23f87bed 1437 "Sorted list of unselected unread articles in the current newsgroup.")
eec82323
LMI
1438
1439(defvar gnus-newsgroup-reads nil
1440 "Alist of read articles and article marks in the current newsgroup.")
1441
1442(defvar gnus-newsgroup-expunged-tally nil)
1443
1444(defvar gnus-newsgroup-marked nil
23f87bed
MB
1445 "Sorted list of ticked articles in the current newsgroup (a subset of unread art).")
1446
1447(defvar gnus-newsgroup-spam-marked nil
1448 "List of ranges of articles that have been marked as spam.")
eec82323
LMI
1449
1450(defvar gnus-newsgroup-killed nil
1451 "List of ranges of articles that have been through the scoring process.")
1452
1453(defvar gnus-newsgroup-cached nil
23f87bed 1454 "Sorted list of articles that come from the article cache.")
eec82323
LMI
1455
1456(defvar gnus-newsgroup-saved nil
1457 "List of articles that have been saved.")
1458
1459(defvar gnus-newsgroup-kill-headers nil)
1460
1461(defvar gnus-newsgroup-replied nil
1462 "List of articles that have been replied to in the current newsgroup.")
1463
23f87bed
MB
1464(defvar gnus-newsgroup-forwarded nil
1465 "List of articles that have been forwarded in the current newsgroup.")
1466
1467(defvar gnus-newsgroup-recent nil
1468 "List of articles that have are recent in the current newsgroup.")
1469
eec82323 1470(defvar gnus-newsgroup-expirable nil
23f87bed 1471 "Sorted list of articles in the current newsgroup that can be expired.")
eec82323
LMI
1472
1473(defvar gnus-newsgroup-processable nil
1474 "List of articles in the current newsgroup that can be processed.")
1475
6748645f 1476(defvar gnus-newsgroup-downloadable nil
23f87bed
MB
1477 "Sorted list of articles in the current newsgroup that can be processed.")
1478
1479(defvar gnus-newsgroup-unfetched nil
1480 "Sorted list of articles in the current newsgroup whose headers have
1481not been fetched into the agent.
1482
1483This list will always be a subset of gnus-newsgroup-undownloaded.")
6748645f
LMI
1484
1485(defvar gnus-newsgroup-undownloaded nil
23f87bed 1486 "List of articles in the current newsgroup that haven't been downloaded.")
6748645f
LMI
1487
1488(defvar gnus-newsgroup-unsendable nil
1489 "List of articles in the current newsgroup that won't be sent.")
1490
eec82323
LMI
1491(defvar gnus-newsgroup-bookmarks nil
1492 "List of articles in the current newsgroup that have bookmarks.")
1493
1494(defvar gnus-newsgroup-dormant nil
23f87bed
MB
1495 "Sorted list of dormant articles in the current newsgroup.")
1496
1497(defvar gnus-newsgroup-unseen nil
1498 "List of unseen articles in the current newsgroup.")
1499
1500(defvar gnus-newsgroup-seen nil
1501 "Range of seen articles in the current newsgroup.")
1502
1503(defvar gnus-newsgroup-articles nil
1504 "List of articles in the current newsgroup.")
eec82323
LMI
1505
1506(defvar gnus-newsgroup-scored nil
1507 "List of scored articles in the current newsgroup.")
1508
1509(defvar gnus-newsgroup-headers nil
1510 "List of article headers in the current newsgroup.")
1511
1512(defvar gnus-newsgroup-threads nil)
1513
1514(defvar gnus-newsgroup-prepared nil
1515 "Whether the current group has been prepared properly.")
1516
1517(defvar gnus-newsgroup-ancient nil
1518 "List of `gnus-fetch-old-headers' articles in the current newsgroup.")
1519
1520(defvar gnus-newsgroup-sparse nil)
1521
1522(defvar gnus-current-article nil)
1523(defvar gnus-article-current nil)
1524(defvar gnus-current-headers nil)
1525(defvar gnus-have-all-headers nil)
1526(defvar gnus-last-article nil)
1527(defvar gnus-newsgroup-history nil)
16409b0b
GM
1528(defvar gnus-newsgroup-charset nil)
1529(defvar gnus-newsgroup-ephemeral-charset nil)
1530(defvar gnus-newsgroup-ephemeral-ignored-charsets nil)
eec82323 1531
23f87bed
MB
1532(defvar gnus-article-before-search nil)
1533
1534(defvar gnus-summary-local-variables
eec82323
LMI
1535 '(gnus-newsgroup-name
1536 gnus-newsgroup-begin gnus-newsgroup-end
1537 gnus-newsgroup-last-rmail gnus-newsgroup-last-mail
1538 gnus-newsgroup-last-folder gnus-newsgroup-last-file
26c9afc3 1539 gnus-newsgroup-last-directory
eec82323
LMI
1540 gnus-newsgroup-auto-expire gnus-newsgroup-unreads
1541 gnus-newsgroup-unselected gnus-newsgroup-marked
23f87bed 1542 gnus-newsgroup-spam-marked
eec82323 1543 gnus-newsgroup-reads gnus-newsgroup-saved
23f87bed
MB
1544 gnus-newsgroup-replied gnus-newsgroup-forwarded
1545 gnus-newsgroup-recent
1546 gnus-newsgroup-expirable
eec82323 1547 gnus-newsgroup-processable gnus-newsgroup-killed
6748645f 1548 gnus-newsgroup-downloadable gnus-newsgroup-undownloaded
23f87bed
MB
1549 gnus-newsgroup-unfetched
1550 gnus-newsgroup-unsendable gnus-newsgroup-unseen
1551 gnus-newsgroup-seen gnus-newsgroup-articles
eec82323
LMI
1552 gnus-newsgroup-bookmarks gnus-newsgroup-dormant
1553 gnus-newsgroup-headers gnus-newsgroup-threads
1554 gnus-newsgroup-prepared gnus-summary-highlight-line-function
1555 gnus-current-article gnus-current-headers gnus-have-all-headers
1556 gnus-last-article gnus-article-internal-prepare-hook
1557 gnus-newsgroup-dependencies gnus-newsgroup-selected-overlay
1558 gnus-newsgroup-scored gnus-newsgroup-kill-headers
1559 gnus-thread-expunge-below
16409b0b
GM
1560 gnus-score-alist gnus-current-score-file
1561 (gnus-summary-expunge-below . global)
eec82323 1562 (gnus-summary-mark-below . global)
16409b0b 1563 (gnus-orphan-score . global)
eec82323
LMI
1564 gnus-newsgroup-active gnus-scores-exclude-files
1565 gnus-newsgroup-history gnus-newsgroup-ancient
1566 gnus-newsgroup-sparse gnus-newsgroup-process-stack
1567 (gnus-newsgroup-adaptive . gnus-use-adaptive-scoring)
1568 gnus-newsgroup-adaptive-score-file (gnus-reffed-article-number . -1)
1569 (gnus-newsgroup-expunged-tally . 0)
1570 gnus-cache-removable-articles gnus-newsgroup-cached
1571 gnus-newsgroup-data gnus-newsgroup-data-reverse
16409b0b 1572 gnus-newsgroup-limit gnus-newsgroup-limits
23f87bed
MB
1573 gnus-newsgroup-charset gnus-newsgroup-display
1574 gnus-summary-use-undownloaded-faces)
eec82323
LMI
1575 "Variables that are buffer-local to the summary buffers.")
1576
23f87bed
MB
1577(defvar gnus-newsgroup-variables nil
1578 "A list of variables that have separate values in different newsgroups.
1579A list of newsgroup (summary buffer) local variables, or cons of
1580variables and their default expressions to be evalled (when the default
1581values are not nil), that should be made global while the summary buffer
1582is active.
1583
1584Note: The default expressions will be evaluated (using function `eval')
1585before assignment to the local variable rather than just assigned to it.
1586If the default expression is the symbol `global', that symbol will not
1587be evaluated but the global value of the local variable will be used
1588instead.
1589
1590These variables can be used to set variables in the group parameters
1591while still allowing them to affect operations done in other buffers.
1592For example:
1593
1594\(setq gnus-newsgroup-variables
1595 '(message-use-followup-to
1596 (gnus-visible-headers .
1597 \"^From:\\\\|^Newsgroups:\\\\|^Subject:\\\\|^Date:\\\\|^To:\")))
1598")
1599
23f87bed
MB
1600(eval-when-compile
1601 ;; Bind features so that require will believe that gnus-sum has
1602 ;; already been loaded (avoids infinite recursion)
1603 (let ((features (cons 'gnus-sum features)))
23f87bed 1604 (require 'gnus-art)))
eec82323 1605
16409b0b
GM
1606;; MIME stuff.
1607
1608(defvar gnus-decode-encoded-word-methods
1609 '(mail-decode-encoded-word-string)
1610 "List of methods used to decode encoded words.
1611
23f87bed
MB
1612This variable is a list of FUNCTION or (REGEXP . FUNCTION). If item
1613is FUNCTION, FUNCTION will be apply to all newsgroups. If item is a
1614\(REGEXP . FUNCTION), FUNCTION will be only apply to thes newsgroups
16409b0b
GM
1615whose names match REGEXP.
1616
1617For example:
23f87bed 1618\((\"chinese\" . gnus-decode-encoded-word-string-by-guess)
16409b0b
GM
1619 mail-decode-encoded-word-string
1620 (\"chinese\" . rfc1843-decode-string))")
1621
1622(defvar gnus-decode-encoded-word-methods-cache nil)
1623
1624(defun gnus-multi-decode-encoded-word-string (string)
1625 "Apply the functions from `gnus-encoded-word-methods' that match."
1626 (unless (and gnus-decode-encoded-word-methods-cache
1627 (eq gnus-newsgroup-name
1628 (car gnus-decode-encoded-word-methods-cache)))
1629 (setq gnus-decode-encoded-word-methods-cache (list gnus-newsgroup-name))
01c52d31
MB
1630 (dolist (method gnus-decode-encoded-word-methods)
1631 (if (symbolp method)
1632 (nconc gnus-decode-encoded-word-methods-cache (list method))
1633 (if (and gnus-newsgroup-name
1634 (string-match (car method) gnus-newsgroup-name))
1635 (nconc gnus-decode-encoded-word-methods-cache
1636 (list (cdr method)))))))
1637 (dolist (method (cdr gnus-decode-encoded-word-methods-cache) string)
1638 (setq string (funcall method string))))
16409b0b 1639
eec82323
LMI
1640;; Subject simplification.
1641
6748645f 1642(defun gnus-simplify-whitespace (str)
16409b0b 1643 "Remove excessive whitespace from STR."
23f87bed
MB
1644 ;; Multiple spaces.
1645 (while (string-match "[ \t][ \t]+" str)
1646 (setq str (concat (substring str 0 (match-beginning 0))
1647 " "
1648 (substring str (match-end 0)))))
1649 ;; Leading spaces.
1650 (when (string-match "^[ \t]+" str)
1651 (setq str (substring str (match-end 0))))
1652 ;; Trailing spaces.
1653 (when (string-match "[ \t]+$" str)
1654 (setq str (substring str 0 (match-beginning 0))))
1655 str)
1656
1657(defun gnus-simplify-all-whitespace (str)
1658 "Remove all whitespace from STR."
1659 (while (string-match "[ \t\n]+" str)
1660 (setq str (replace-match "" nil nil str)))
1661 str)
6748645f 1662
eec82323
LMI
1663(defsubst gnus-simplify-subject-re (subject)
1664 "Remove \"Re:\" from subject lines."
23f87bed 1665 (if (string-match message-subject-re-regexp subject)
eec82323
LMI
1666 (substring subject (match-end 0))
1667 subject))
1668
1669(defun gnus-simplify-subject (subject &optional re-only)
1670 "Remove `Re:' and words in parentheses.
1671If RE-ONLY is non-nil, strip leading `Re:'s only."
1672 (let ((case-fold-search t)) ;Ignore case.
1673 ;; Remove `Re:', `Re^N:', `Re(n)', and `Re[n]:'.
1674 (when (string-match "\\`\\(re\\([[(^][0-9]+[])]?\\)?:[ \t]*\\)+" subject)
1675 (setq subject (substring subject (match-end 0))))
1676 ;; Remove uninteresting prefixes.
1677 (when (and (not re-only)
1678 gnus-simplify-ignored-prefixes
1679 (string-match gnus-simplify-ignored-prefixes subject))
1680 (setq subject (substring subject (match-end 0))))
1681 ;; Remove words in parentheses from end.
1682 (unless re-only
1683 (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
1684 (setq subject (substring subject 0 (match-beginning 0)))))
1685 ;; Return subject string.
1686 subject))
1687
1688;; Remove any leading "re:"s, any trailing paren phrases, and simplify
1689;; all whitespace.
1690(defsubst gnus-simplify-buffer-fuzzy-step (regexp &optional newtext)
1691 (goto-char (point-min))
1692 (while (re-search-forward regexp nil t)
16409b0b 1693 (replace-match (or newtext ""))))
eec82323
LMI
1694
1695(defun gnus-simplify-buffer-fuzzy ()
1696 "Simplify string in the buffer fuzzily.
1697The string in the accessible portion of the current buffer is simplified.
1698It is assumed to be a single-line subject.
1699Whitespace is generally cleaned up, and miscellaneous leading/trailing
1700matter is removed. Additional things can be deleted by setting
16409b0b 1701`gnus-simplify-subject-fuzzy-regexp'."
eec82323
LMI
1702 (let ((case-fold-search t)
1703 (modified-tick))
1704 (gnus-simplify-buffer-fuzzy-step "\t" " ")
1705
1706 (while (not (eq modified-tick (buffer-modified-tick)))
1707 (setq modified-tick (buffer-modified-tick))
1708 (cond
1709 ((listp gnus-simplify-subject-fuzzy-regexp)
01c52d31
MB
1710 (mapc 'gnus-simplify-buffer-fuzzy-step
1711 gnus-simplify-subject-fuzzy-regexp))
eec82323
LMI
1712 (gnus-simplify-subject-fuzzy-regexp
1713 (gnus-simplify-buffer-fuzzy-step gnus-simplify-subject-fuzzy-regexp)))
1714 (gnus-simplify-buffer-fuzzy-step "^ *\\[[-+?*!][-+?*!]\\] *")
1715 (gnus-simplify-buffer-fuzzy-step
1716 "^ *\\(re\\|fw\\|fwd\\)[[{(^0-9]*[])}]?[:;] *")
1717 (gnus-simplify-buffer-fuzzy-step "^[[].*:\\( .*\\)[]]$" "\\1"))
1718
1719 (gnus-simplify-buffer-fuzzy-step " *[[{(][^()\n]*[]})] *$")
1720 (gnus-simplify-buffer-fuzzy-step " +" " ")
1721 (gnus-simplify-buffer-fuzzy-step " $")
1722 (gnus-simplify-buffer-fuzzy-step "^ +")))
1723
1724(defun gnus-simplify-subject-fuzzy (subject)
1725 "Simplify a subject string fuzzily.
6748645f 1726See `gnus-simplify-buffer-fuzzy' for details."
eec82323
LMI
1727 (save-excursion
1728 (gnus-set-work-buffer)
1729 (let ((case-fold-search t))
6748645f
LMI
1730 ;; Remove uninteresting prefixes.
1731 (when (and gnus-simplify-ignored-prefixes
1732 (string-match gnus-simplify-ignored-prefixes subject))
1733 (setq subject (substring subject (match-end 0))))
eec82323
LMI
1734 (insert subject)
1735 (inline (gnus-simplify-buffer-fuzzy))
1736 (buffer-string))))
1737
1738(defsubst gnus-simplify-subject-fully (subject)
23f87bed 1739 "Simplify a subject string according to `gnus-summary-gather-subject-limit'."
eec82323 1740 (cond
6748645f
LMI
1741 (gnus-simplify-subject-functions
1742 (gnus-map-function gnus-simplify-subject-functions subject))
eec82323
LMI
1743 ((null gnus-summary-gather-subject-limit)
1744 (gnus-simplify-subject-re subject))
1745 ((eq gnus-summary-gather-subject-limit 'fuzzy)
1746 (gnus-simplify-subject-fuzzy subject))
1747 ((numberp gnus-summary-gather-subject-limit)
01c52d31
MB
1748 (truncate-string-to-width (gnus-simplify-subject-re subject)
1749 gnus-summary-gather-subject-limit))
eec82323
LMI
1750 (t
1751 subject)))
1752
1753(defsubst gnus-subject-equal (s1 s2 &optional simple-first)
6748645f 1754 "Check whether two subjects are equal.
23f87bed 1755If optional argument SIMPLE-FIRST is t, first argument is already
6748645f 1756simplified."
eec82323
LMI
1757 (cond
1758 ((null simple-first)
1759 (equal (gnus-simplify-subject-fully s1)
1760 (gnus-simplify-subject-fully s2)))
1761 (t
1762 (equal s1
1763 (gnus-simplify-subject-fully s2)))))
1764
1765(defun gnus-summary-bubble-group ()
1766 "Increase the score of the current group.
1767This is a handy function to add to `gnus-summary-exit-hook' to
1768increase the score of each group you read."
1769 (gnus-group-add-score gnus-newsgroup-name))
1770
1771\f
1772;;;
1773;;; Gnus summary mode
1774;;;
1775
1776(put 'gnus-summary-mode 'mode-class 'special)
1777
1653df0f
SZ
1778(defvar gnus-article-commands-menu)
1779
23f87bed
MB
1780;; Non-orthogonal keys
1781
1782(gnus-define-keys gnus-summary-mode-map
1783 " " gnus-summary-next-page
1784 "\177" gnus-summary-prev-page
1785 [delete] gnus-summary-prev-page
1786 [backspace] gnus-summary-prev-page
1787 "\r" gnus-summary-scroll-up
1788 "\M-\r" gnus-summary-scroll-down
1789 "n" gnus-summary-next-unread-article
1790 "p" gnus-summary-prev-unread-article
1791 "N" gnus-summary-next-article
1792 "P" gnus-summary-prev-article
1793 "\M-\C-n" gnus-summary-next-same-subject
1794 "\M-\C-p" gnus-summary-prev-same-subject
1795 "\M-n" gnus-summary-next-unread-subject
1796 "\M-p" gnus-summary-prev-unread-subject
1797 "." gnus-summary-first-unread-article
1798 "," gnus-summary-best-unread-article
1799 "\M-s" gnus-summary-search-article-forward
1800 "\M-r" gnus-summary-search-article-backward
01c52d31
MB
1801 "\M-S" gnus-summary-repeat-search-article-forward
1802 "\M-R" gnus-summary-repeat-search-article-backward
23f87bed
MB
1803 "<" gnus-summary-beginning-of-article
1804 ">" gnus-summary-end-of-article
1805 "j" gnus-summary-goto-article
1806 "^" gnus-summary-refer-parent-article
1807 "\M-^" gnus-summary-refer-article
1808 "u" gnus-summary-tick-article-forward
1809 "!" gnus-summary-tick-article-forward
1810 "U" gnus-summary-tick-article-backward
1811 "d" gnus-summary-mark-as-read-forward
1812 "D" gnus-summary-mark-as-read-backward
1813 "E" gnus-summary-mark-as-expirable
1814 "\M-u" gnus-summary-clear-mark-forward
1815 "\M-U" gnus-summary-clear-mark-backward
1816 "k" gnus-summary-kill-same-subject-and-select
1817 "\C-k" gnus-summary-kill-same-subject
1818 "\M-\C-k" gnus-summary-kill-thread
1819 "\M-\C-l" gnus-summary-lower-thread
1820 "e" gnus-summary-edit-article
1821 "#" gnus-summary-mark-as-processable
1822 "\M-#" gnus-summary-unmark-as-processable
1823 "\M-\C-t" gnus-summary-toggle-threads
1824 "\M-\C-s" gnus-summary-show-thread
1825 "\M-\C-h" gnus-summary-hide-thread
1826 "\M-\C-f" gnus-summary-next-thread
1827 "\M-\C-b" gnus-summary-prev-thread
1828 [(meta down)] gnus-summary-next-thread
1829 [(meta up)] gnus-summary-prev-thread
1830 "\M-\C-u" gnus-summary-up-thread
1831 "\M-\C-d" gnus-summary-down-thread
1832 "&" gnus-summary-execute-command
1833 "c" gnus-summary-catchup-and-exit
1834 "\C-w" gnus-summary-mark-region-as-read
1835 "\C-t" gnus-summary-toggle-truncation
1836 "?" gnus-summary-mark-as-dormant
1837 "\C-c\M-\C-s" gnus-summary-limit-include-expunged
1838 "\C-c\C-s\C-n" gnus-summary-sort-by-number
6ecfe5c2 1839 "\C-c\C-s\C-m\C-n" gnus-summary-sort-by-most-recent-number
23f87bed
MB
1840 "\C-c\C-s\C-l" gnus-summary-sort-by-lines
1841 "\C-c\C-s\C-c" gnus-summary-sort-by-chars
1842 "\C-c\C-s\C-a" gnus-summary-sort-by-author
01c52d31 1843 "\C-c\C-s\C-t" gnus-summary-sort-by-recipient
23f87bed
MB
1844 "\C-c\C-s\C-s" gnus-summary-sort-by-subject
1845 "\C-c\C-s\C-d" gnus-summary-sort-by-date
6ecfe5c2 1846 "\C-c\C-s\C-m\C-d" gnus-summary-sort-by-most-recent-date
23f87bed
MB
1847 "\C-c\C-s\C-i" gnus-summary-sort-by-score
1848 "\C-c\C-s\C-o" gnus-summary-sort-by-original
1849 "\C-c\C-s\C-r" gnus-summary-sort-by-random
1850 "=" gnus-summary-expand-window
1851 "\C-x\C-s" gnus-summary-reselect-current-group
1852 "\M-g" gnus-summary-rescan-group
1853 "w" gnus-summary-stop-page-breaking
1854 "\C-c\C-r" gnus-summary-caesar-message
1855 "f" gnus-summary-followup
1856 "F" gnus-summary-followup-with-original
1857 "C" gnus-summary-cancel-article
1858 "r" gnus-summary-reply
1859 "R" gnus-summary-reply-with-original
1860 "\C-c\C-f" gnus-summary-mail-forward
1861 "o" gnus-summary-save-article
1862 "\C-o" gnus-summary-save-article-mail
1863 "|" gnus-summary-pipe-output
1864 "\M-k" gnus-summary-edit-local-kill
1865 "\M-K" gnus-summary-edit-global-kill
1866 ;; "V" gnus-version
1867 "\C-c\C-d" gnus-summary-describe-group
1868 "q" gnus-summary-exit
1869 "Q" gnus-summary-exit-no-update
1870 "\C-c\C-i" gnus-info-find-node
1871 gnus-mouse-2 gnus-mouse-pick-article
132cf96d 1872 [follow-link] mouse-face
23f87bed
MB
1873 "m" gnus-summary-mail-other-window
1874 "a" gnus-summary-post-news
1875 "i" gnus-summary-news-other-window
1876 "x" gnus-summary-limit-to-unread
1877 "s" gnus-summary-isearch-article
1878 "t" gnus-summary-toggle-header
1879 "g" gnus-summary-show-article
1880 "l" gnus-summary-goto-last-article
1881 "\C-c\C-v\C-v" gnus-uu-decode-uu-view
1882 "\C-d" gnus-summary-enter-digest-group
1883 "\M-\C-d" gnus-summary-read-document
1884 "\M-\C-e" gnus-summary-edit-parameters
1885 "\M-\C-a" gnus-summary-customize-parameters
1886 "\C-c\C-b" gnus-bug
1887 "*" gnus-cache-enter-article
1888 "\M-*" gnus-cache-remove-article
1889 "\M-&" gnus-summary-universal-argument
1890 "\C-l" gnus-recenter
1891 "I" gnus-summary-increase-score
1892 "L" gnus-summary-lower-score
1893 "\M-i" gnus-symbolic-argument
1894 "h" gnus-summary-select-article-buffer
1895
1896 "b" gnus-article-view-part
1897 "\M-t" gnus-summary-toggle-display-buttonized
1898
1899 "V" gnus-summary-score-map
1900 "X" gnus-uu-extract-map
1901 "S" gnus-summary-send-map)
1902
1903;; Sort of orthogonal keymap
1904(gnus-define-keys (gnus-summary-mark-map "M" gnus-summary-mode-map)
1905 "t" gnus-summary-tick-article-forward
1906 "!" gnus-summary-tick-article-forward
1907 "d" gnus-summary-mark-as-read-forward
1908 "r" gnus-summary-mark-as-read-forward
1909 "c" gnus-summary-clear-mark-forward
1910 " " gnus-summary-clear-mark-forward
1911 "e" gnus-summary-mark-as-expirable
1912 "x" gnus-summary-mark-as-expirable
1913 "?" gnus-summary-mark-as-dormant
1914 "b" gnus-summary-set-bookmark
1915 "B" gnus-summary-remove-bookmark
1916 "#" gnus-summary-mark-as-processable
1917 "\M-#" gnus-summary-unmark-as-processable
1918 "S" gnus-summary-limit-include-expunged
1919 "C" gnus-summary-catchup
1920 "H" gnus-summary-catchup-to-here
1921 "h" gnus-summary-catchup-from-here
1922 "\C-c" gnus-summary-catchup-all
1923 "k" gnus-summary-kill-same-subject-and-select
1924 "K" gnus-summary-kill-same-subject
1925 "P" gnus-uu-mark-map)
1926
1927(gnus-define-keys (gnus-summary-mscore-map "V" gnus-summary-mark-map)
1928 "c" gnus-summary-clear-above
1929 "u" gnus-summary-tick-above
1930 "m" gnus-summary-mark-above
1931 "k" gnus-summary-kill-below)
1932
1933(gnus-define-keys (gnus-summary-limit-map "/" gnus-summary-mode-map)
1934 "/" gnus-summary-limit-to-subject
1935 "n" gnus-summary-limit-to-articles
01c52d31
MB
1936 "b" gnus-summary-limit-to-bodies
1937 "h" gnus-summary-limit-to-headers
23f87bed
MB
1938 "w" gnus-summary-pop-limit
1939 "s" gnus-summary-limit-to-subject
1940 "a" gnus-summary-limit-to-author
1941 "u" gnus-summary-limit-to-unread
1942 "m" gnus-summary-limit-to-marks
1943 "M" gnus-summary-limit-exclude-marks
1944 "v" gnus-summary-limit-to-score
1945 "*" gnus-summary-limit-include-cached
1946 "D" gnus-summary-limit-include-dormant
1947 "T" gnus-summary-limit-include-thread
1948 "d" gnus-summary-limit-exclude-dormant
1949 "t" gnus-summary-limit-to-age
1950 "." gnus-summary-limit-to-unseen
1951 "x" gnus-summary-limit-to-extra
1952 "p" gnus-summary-limit-to-display-predicate
1953 "E" gnus-summary-limit-include-expunged
1954 "c" gnus-summary-limit-exclude-childless-dormant
1955 "C" gnus-summary-limit-mark-excluded-as-read
1956 "o" gnus-summary-insert-old-articles
01c52d31
MB
1957 "N" gnus-summary-insert-new-articles
1958 "S" gnus-summary-limit-to-singletons
1959 "r" gnus-summary-limit-to-replied
1960 "R" gnus-summary-limit-to-recipient
1961 "A" gnus-summary-limit-to-address)
23f87bed
MB
1962
1963(gnus-define-keys (gnus-summary-goto-map "G" gnus-summary-mode-map)
1964 "n" gnus-summary-next-unread-article
1965 "p" gnus-summary-prev-unread-article
1966 "N" gnus-summary-next-article
1967 "P" gnus-summary-prev-article
1968 "\C-n" gnus-summary-next-same-subject
1969 "\C-p" gnus-summary-prev-same-subject
1970 "\M-n" gnus-summary-next-unread-subject
1971 "\M-p" gnus-summary-prev-unread-subject
1972 "f" gnus-summary-first-unread-article
1973 "b" gnus-summary-best-unread-article
1974 "j" gnus-summary-goto-article
1975 "g" gnus-summary-goto-subject
1976 "l" gnus-summary-goto-last-article
1977 "o" gnus-summary-pop-article)
1978
1979(gnus-define-keys (gnus-summary-thread-map "T" gnus-summary-mode-map)
1980 "k" gnus-summary-kill-thread
01c52d31 1981 "E" gnus-summary-expire-thread
23f87bed
MB
1982 "l" gnus-summary-lower-thread
1983 "i" gnus-summary-raise-thread
1984 "T" gnus-summary-toggle-threads
1985 "t" gnus-summary-rethread-current
1986 "^" gnus-summary-reparent-thread
01c52d31 1987 "\M-^" gnus-summary-reparent-children
23f87bed
MB
1988 "s" gnus-summary-show-thread
1989 "S" gnus-summary-show-all-threads
1990 "h" gnus-summary-hide-thread
1991 "H" gnus-summary-hide-all-threads
1992 "n" gnus-summary-next-thread
1993 "p" gnus-summary-prev-thread
1994 "u" gnus-summary-up-thread
1995 "o" gnus-summary-top-thread
1996 "d" gnus-summary-down-thread
1997 "#" gnus-uu-mark-thread
1998 "\M-#" gnus-uu-unmark-thread)
1999
2000(gnus-define-keys (gnus-summary-buffer-map "Y" gnus-summary-mode-map)
2001 "g" gnus-summary-prepare
2002 "c" gnus-summary-insert-cached-articles
01c52d31
MB
2003 "d" gnus-summary-insert-dormant-articles
2004 "t" gnus-summary-insert-ticked-articles)
23f87bed
MB
2005
2006(gnus-define-keys (gnus-summary-exit-map "Z" gnus-summary-mode-map)
2007 "c" gnus-summary-catchup-and-exit
2008 "C" gnus-summary-catchup-all-and-exit
2009 "E" gnus-summary-exit-no-update
2010 "Q" gnus-summary-exit
2011 "Z" gnus-summary-exit
2012 "n" gnus-summary-catchup-and-goto-next-group
01c52d31 2013 "p" gnus-summary-catchup-and-goto-prev-group
23f87bed
MB
2014 "R" gnus-summary-reselect-current-group
2015 "G" gnus-summary-rescan-group
2016 "N" gnus-summary-next-group
2017 "s" gnus-summary-save-newsrc
2018 "P" gnus-summary-prev-group)
2019
2020(gnus-define-keys (gnus-summary-article-map "A" gnus-summary-mode-map)
2021 " " gnus-summary-next-page
2022 "n" gnus-summary-next-page
2023 "\177" gnus-summary-prev-page
2024 [delete] gnus-summary-prev-page
2025 "p" gnus-summary-prev-page
2026 "\r" gnus-summary-scroll-up
2027 "\M-\r" gnus-summary-scroll-down
2028 "<" gnus-summary-beginning-of-article
2029 ">" gnus-summary-end-of-article
2030 "b" gnus-summary-beginning-of-article
2031 "e" gnus-summary-end-of-article
2032 "^" gnus-summary-refer-parent-article
2033 "r" gnus-summary-refer-parent-article
2034 "D" gnus-summary-enter-digest-group
2035 "R" gnus-summary-refer-references
2036 "T" gnus-summary-refer-thread
2037 "g" gnus-summary-show-article
2038 "s" gnus-summary-isearch-article
2039 "P" gnus-summary-print-article
01c52d31 2040 "S" gnus-sticky-article
23f87bed
MB
2041 "M" gnus-mailing-list-insinuate
2042 "t" gnus-article-babel)
2043
2044(gnus-define-keys (gnus-summary-wash-map "W" gnus-summary-mode-map)
2045 "b" gnus-article-add-buttons
2046 "B" gnus-article-add-buttons-to-head
2047 "o" gnus-article-treat-overstrike
2048 "e" gnus-article-emphasize
2049 "w" gnus-article-fill-cited-article
2050 "Q" gnus-article-fill-long-lines
01c52d31 2051 "L" gnus-article-toggle-truncate-lines
23f87bed
MB
2052 "C" gnus-article-capitalize-sentences
2053 "c" gnus-article-remove-cr
2054 "q" gnus-article-de-quoted-unreadable
2055 "6" gnus-article-de-base64-unreadable
2056 "Z" gnus-article-decode-HZ
01c52d31 2057 "A" gnus-article-treat-ansi-sequences
23f87bed
MB
2058 "h" gnus-article-wash-html
2059 "u" gnus-article-unsplit-urls
2060 "s" gnus-summary-force-verify-and-decrypt
2061 "f" gnus-article-display-x-face
2062 "l" gnus-summary-stop-page-breaking
2063 "r" gnus-summary-caesar-message
2064 "m" gnus-summary-morse-message
2065 "t" gnus-summary-toggle-header
2066 "g" gnus-treat-smiley
2067 "v" gnus-summary-verbose-headers
2068 "a" gnus-article-strip-headers-in-body ;; mnemonic: wash archive
2069 "p" gnus-article-verify-x-pgp-sig
01c52d31
MB
2070 "d" gnus-article-treat-dumbquotes
2071 "i" gnus-summary-idna-message)
23f87bed
MB
2072
2073(gnus-define-keys (gnus-summary-wash-deuglify-map "Y" gnus-summary-wash-map)
2074 ;; mnemonic: deuglif*Y*
2075 "u" gnus-article-outlook-unwrap-lines
2076 "a" gnus-article-outlook-repair-attribution
2077 "c" gnus-article-outlook-rearrange-citation
2078 "f" gnus-article-outlook-deuglify-article) ;; mnemonic: full deuglify
2079
2080(gnus-define-keys (gnus-summary-wash-hide-map "W" gnus-summary-wash-map)
2081 "a" gnus-article-hide
2082 "h" gnus-article-hide-headers
2083 "b" gnus-article-hide-boring-headers
2084 "s" gnus-article-hide-signature
2085 "c" gnus-article-hide-citation
2086 "C" gnus-article-hide-citation-in-followups
2087 "l" gnus-article-hide-list-identifiers
2088 "B" gnus-article-strip-banner
2089 "P" gnus-article-hide-pem
2090 "\C-c" gnus-article-hide-citation-maybe)
2091
2092(gnus-define-keys (gnus-summary-wash-highlight-map "H" gnus-summary-wash-map)
2093 "a" gnus-article-highlight
2094 "h" gnus-article-highlight-headers
2095 "c" gnus-article-highlight-citation
2096 "s" gnus-article-highlight-signature)
2097
2098(gnus-define-keys (gnus-summary-wash-header-map "G" gnus-summary-wash-map)
2099 "f" gnus-article-treat-fold-headers
2100 "u" gnus-article-treat-unfold-headers
2101 "n" gnus-article-treat-fold-newsgroups)
2102
2103(gnus-define-keys (gnus-summary-wash-display-map "D" gnus-summary-wash-map)
2104 "x" gnus-article-display-x-face
2105 "d" gnus-article-display-face
2106 "s" gnus-treat-smiley
2107 "D" gnus-article-remove-images
2108 "f" gnus-treat-from-picon
2109 "m" gnus-treat-mail-picon
2110 "n" gnus-treat-newsgroups-picon)
2111
2112(gnus-define-keys (gnus-summary-wash-mime-map "M" gnus-summary-wash-map)
2113 "w" gnus-article-decode-mime-words
2114 "c" gnus-article-decode-charset
2115 "v" gnus-mime-view-all-parts
2116 "b" gnus-article-view-part)
2117
2118(gnus-define-keys (gnus-summary-wash-time-map "T" gnus-summary-wash-map)
2119 "z" gnus-article-date-ut
2120 "u" gnus-article-date-ut
2121 "l" gnus-article-date-local
2122 "p" gnus-article-date-english
2123 "e" gnus-article-date-lapsed
2124 "o" gnus-article-date-original
2125 "i" gnus-article-date-iso8601
2126 "s" gnus-article-date-user)
2127
2128(gnus-define-keys (gnus-summary-wash-empty-map "E" gnus-summary-wash-map)
2129 "t" gnus-article-remove-trailing-blank-lines
2130 "l" gnus-article-strip-leading-blank-lines
2131 "m" gnus-article-strip-multiple-blank-lines
2132 "a" gnus-article-strip-blank-lines
2133 "A" gnus-article-strip-all-blank-lines
2134 "s" gnus-article-strip-leading-space
2135 "e" gnus-article-strip-trailing-space
2136 "w" gnus-article-remove-leading-whitespace)
2137
2138(gnus-define-keys (gnus-summary-help-map "H" gnus-summary-mode-map)
2139 "v" gnus-version
2140 "f" gnus-summary-fetch-faq
2141 "d" gnus-summary-describe-group
2142 "h" gnus-summary-describe-briefly
2143 "i" gnus-info-find-node
2144 "c" gnus-group-fetch-charter
2145 "C" gnus-group-fetch-control)
2146
2147(gnus-define-keys (gnus-summary-backend-map "B" gnus-summary-mode-map)
2148 "e" gnus-summary-expire-articles
2149 "\M-\C-e" gnus-summary-expire-articles-now
2150 "\177" gnus-summary-delete-article
2151 [delete] gnus-summary-delete-article
2152 [backspace] gnus-summary-delete-article
2153 "m" gnus-summary-move-article
2154 "r" gnus-summary-respool-article
2155 "w" gnus-summary-edit-article
2156 "c" gnus-summary-copy-article
2157 "B" gnus-summary-crosspost-article
2158 "q" gnus-summary-respool-query
2159 "t" gnus-summary-respool-trace
2160 "i" gnus-summary-import-article
2161 "I" gnus-summary-create-article
2162 "p" gnus-summary-article-posted-p)
2163
2164(gnus-define-keys (gnus-summary-save-map "O" gnus-summary-mode-map)
2165 "o" gnus-summary-save-article
2166 "m" gnus-summary-save-article-mail
2167 "F" gnus-summary-write-article-file
2168 "r" gnus-summary-save-article-rmail
2169 "f" gnus-summary-save-article-file
2170 "b" gnus-summary-save-article-body-file
26c9afc3 2171 "B" gnus-summary-write-article-body-file
23f87bed
MB
2172 "h" gnus-summary-save-article-folder
2173 "v" gnus-summary-save-article-vm
2174 "p" gnus-summary-pipe-output
2175 "P" gnus-summary-muttprint
2176 "s" gnus-soup-add-article)
2177
2178(gnus-define-keys (gnus-summary-mime-map "K" gnus-summary-mode-map)
2179 "b" gnus-summary-display-buttonized
2180 "m" gnus-summary-repair-multipart
2181 "v" gnus-article-view-part
2182 "o" gnus-article-save-part
01c52d31
MB
2183 "O" gnus-article-save-part-and-strip
2184 "r" gnus-article-replace-part
2185 "d" gnus-article-delete-part
2186 "t" gnus-article-view-part-as-type
2187 "j" gnus-article-jump-to-part
23f87bed
MB
2188 "c" gnus-article-copy-part
2189 "C" gnus-article-view-part-as-charset
2190 "e" gnus-article-view-part-externally
01c52d31 2191 "H" gnus-article-browse-html-article
23f87bed
MB
2192 "E" gnus-article-encrypt-body
2193 "i" gnus-article-inline-part
2194 "|" gnus-article-pipe-part)
2195
2196(gnus-define-keys (gnus-uu-mark-map "P" gnus-summary-mark-map)
2197 "p" gnus-summary-mark-as-processable
2198 "u" gnus-summary-unmark-as-processable
2199 "U" gnus-summary-unmark-all-processable
2200 "v" gnus-uu-mark-over
2201 "s" gnus-uu-mark-series
2202 "r" gnus-uu-mark-region
2203 "g" gnus-uu-unmark-region
2204 "R" gnus-uu-mark-by-regexp
2205 "G" gnus-uu-unmark-by-regexp
2206 "t" gnus-uu-mark-thread
2207 "T" gnus-uu-unmark-thread
2208 "a" gnus-uu-mark-all
2209 "b" gnus-uu-mark-buffer
2210 "S" gnus-uu-mark-sparse
2211 "k" gnus-summary-kill-process-mark
2212 "y" gnus-summary-yank-process-mark
2213 "w" gnus-summary-save-process-mark
2214 "i" gnus-uu-invert-processable)
2215
2216(gnus-define-keys (gnus-uu-extract-map "X" gnus-summary-mode-map)
2217 ;;"x" gnus-uu-extract-any
2218 "m" gnus-summary-save-parts
2219 "u" gnus-uu-decode-uu
2220 "U" gnus-uu-decode-uu-and-save
2221 "s" gnus-uu-decode-unshar
2222 "S" gnus-uu-decode-unshar-and-save
2223 "o" gnus-uu-decode-save
2224 "O" gnus-uu-decode-save
2225 "b" gnus-uu-decode-binhex
2226 "B" gnus-uu-decode-binhex
b890d447 2227 "Y" gnus-uu-decode-yenc
23f87bed
MB
2228 "p" gnus-uu-decode-postscript
2229 "P" gnus-uu-decode-postscript-and-save)
2230
2231(gnus-define-keys
2232 (gnus-uu-extract-view-map "v" gnus-uu-extract-map)
2233 "u" gnus-uu-decode-uu-view
2234 "U" gnus-uu-decode-uu-and-save-view
2235 "s" gnus-uu-decode-unshar-view
2236 "S" gnus-uu-decode-unshar-and-save-view
2237 "o" gnus-uu-decode-save-view
2238 "O" gnus-uu-decode-save-view
2239 "b" gnus-uu-decode-binhex-view
2240 "B" gnus-uu-decode-binhex-view
2241 "p" gnus-uu-decode-postscript-view
2242 "P" gnus-uu-decode-postscript-and-save-view)
2243
2244(defvar gnus-article-post-menu nil)
2245
2246(defconst gnus-summary-menu-maxlen 20)
2247
2248(defun gnus-summary-menu-split (menu)
2249 ;; If we have lots of elements, divide them into groups of 20
2250 ;; and make a pane (or submenu) for each one.
2251 (if (> (length menu) (/ (* gnus-summary-menu-maxlen 3) 2))
2252 (let ((menu menu) sublists next
2253 (i 1))
2254 (while menu
2255 ;; Pull off the next gnus-summary-menu-maxlen elements
2256 ;; and make them the next element of sublist.
2257 (setq next (nthcdr gnus-summary-menu-maxlen menu))
2258 (if next
2259 (setcdr (nthcdr (1- gnus-summary-menu-maxlen) menu)
2260 nil))
2261 (setq sublists (cons (cons (format "%s ... %s" (aref (car menu) 0)
2262 (aref (car (last menu)) 0)) menu)
2263 sublists))
2264 (setq i (1+ i))
2265 (setq menu next))
2266 (nreverse sublists))
2267 ;; Few elements--put them all in one pane.
2268 menu))
eec82323
LMI
2269
2270(defun gnus-summary-make-menu-bar ()
2271 (gnus-turn-off-edit-menu 'summary)
2272
2273 (unless (boundp 'gnus-summary-misc-menu)
2274
2275 (easy-menu-define
23f87bed
MB
2276 gnus-summary-kill-menu gnus-summary-mode-map ""
2277 (cons
2278 "Score"
2279 (nconc
2280 (list
2281 ["Customize" gnus-score-customize t])
2282 (gnus-make-score-map 'increase)
2283 (gnus-make-score-map 'lower)
2284 '(("Mark"
2285 ["Kill below" gnus-summary-kill-below t]
2286 ["Mark above" gnus-summary-mark-above t]
2287 ["Tick above" gnus-summary-tick-above t]
2288 ["Clear above" gnus-summary-clear-above t])
2289 ["Current score" gnus-summary-current-score t]
2290 ["Set score" gnus-summary-set-score t]
2291 ["Switch current score file..." gnus-score-change-score-file t]
2292 ["Set mark below..." gnus-score-set-mark-below t]
2293 ["Set expunge below..." gnus-score-set-expunge-below t]
2294 ["Edit current score file" gnus-score-edit-current-scores t]
59429511 2295 ["Edit score file..." gnus-score-edit-file t]
23f87bed
MB
2296 ["Trace score" gnus-score-find-trace t]
2297 ["Find words" gnus-score-find-favourite-words t]
2298 ["Rescore buffer" gnus-summary-rescore t]
2299 ["Increase score..." gnus-summary-increase-score t]
2300 ["Lower score..." gnus-summary-lower-score t]))))
2301
2302 ;; Define both the Article menu in the summary buffer and the
2303 ;; equivalent Commands menu in the article buffer here for
2304 ;; consistency.
6748645f 2305 (let ((innards
23f87bed
MB
2306 `(("Hide"
2307 ["All" gnus-article-hide t]
2308 ["Headers" gnus-article-hide-headers t]
2309 ["Signature" gnus-article-hide-signature t]
2310 ["Citation" gnus-article-hide-citation t]
16409b0b 2311 ["List identifiers" gnus-article-hide-list-identifiers t]
16409b0b 2312 ["Banner" gnus-article-strip-banner t]
23f87bed
MB
2313 ["Boring headers" gnus-article-hide-boring-headers t])
2314 ("Highlight"
2315 ["All" gnus-article-highlight t]
2316 ["Headers" gnus-article-highlight-headers t]
2317 ["Signature" gnus-article-highlight-signature t]
2318 ["Citation" gnus-article-highlight-citation t])
16409b0b
GM
2319 ("MIME"
2320 ["Words" gnus-article-decode-mime-words t]
2321 ["Charset" gnus-article-decode-charset t]
2322 ["QP" gnus-article-de-quoted-unreadable t]
2323 ["Base64" gnus-article-de-base64-unreadable t]
23f87bed
MB
2324 ["View MIME buttons" gnus-summary-display-buttonized t]
2325 ["View all" gnus-mime-view-all-parts t]
2326 ["Verify and Decrypt" gnus-summary-force-verify-and-decrypt t]
2327 ["Encrypt body" gnus-article-encrypt-body
2328 :active (not (gnus-group-read-only-p))
2329 ,@(if (featurep 'xemacs) nil
2330 '(:help "Encrypt the message body on disk"))]
2331 ["Extract all parts..." gnus-summary-save-parts t]
2332 ("Multipart"
2333 ["Repair multipart" gnus-summary-repair-multipart t]
2334 ["Pipe part..." gnus-article-pipe-part t]
2335 ["Inline part" gnus-article-inline-part t]
01c52d31 2336 ["View part as type..." gnus-article-view-part-as-type t]
23f87bed
MB
2337 ["Encrypt body" gnus-article-encrypt-body
2338 :active (not (gnus-group-read-only-p))
2339 ,@(if (featurep 'xemacs) nil
2340 '(:help "Encrypt the message body on disk"))]
2341 ["View part externally" gnus-article-view-part-externally t]
01c52d31 2342 ["View HTML parts in browser" gnus-article-browse-html-article t]
23f87bed
MB
2343 ["View part with charset..." gnus-article-view-part-as-charset t]
2344 ["Copy part" gnus-article-copy-part t]
2345 ["Save part..." gnus-article-save-part t]
2346 ["View part" gnus-article-view-part t]))
2347 ("Date"
2348 ["Local" gnus-article-date-local t]
2349 ["ISO8601" gnus-article-date-iso8601 t]
2350 ["UT" gnus-article-date-ut t]
2351 ["Original" gnus-article-date-original t]
2352 ["Lapsed" gnus-article-date-lapsed t]
2353 ["User-defined" gnus-article-date-user t])
2354 ("Display"
2355 ["Remove images" gnus-article-remove-images t]
2356 ["Toggle smiley" gnus-treat-smiley t]
2357 ["Show X-Face" gnus-article-display-x-face t]
2358 ["Show picons in From" gnus-treat-from-picon t]
2359 ["Show picons in mail headers" gnus-treat-mail-picon t]
2360 ["Show picons in news headers" gnus-treat-newsgroups-picon t]
2361 ("View as different encoding"
2362 ,@(gnus-summary-menu-split
2363 (mapcar
2364 (lambda (cs)
2365 ;; Since easymenu under Emacs doesn't allow
2366 ;; lambda forms for menu commands, we should
2367 ;; provide intern'ed function symbols.
2368 (let ((command (intern (format "\
2369gnus-summary-show-article-from-menu-as-charset-%s" cs))))
2370 (fset command
2371 `(lambda ()
2372 (interactive)
2373 (let ((gnus-summary-show-article-charset-alist
2374 '((1 . ,cs))))
2375 (gnus-summary-show-article 1))))
2376 `[,(symbol-name cs) ,command t]))
2377 (sort (if (fboundp 'coding-system-list)
2378 (coding-system-list)
2379 (mapcar 'car mm-mime-mule-charset-alist))
2380 'string<)))))
2381 ("Washing"
2382 ("Remove Blanks"
2383 ["Leading" gnus-article-strip-leading-blank-lines t]
2384 ["Multiple" gnus-article-strip-multiple-blank-lines t]
2385 ["Trailing" gnus-article-remove-trailing-blank-lines t]
2386 ["All of the above" gnus-article-strip-blank-lines t]
2387 ["All" gnus-article-strip-all-blank-lines t]
2388 ["Leading space" gnus-article-strip-leading-space t]
2389 ["Trailing space" gnus-article-strip-trailing-space t]
2390 ["Leading space in headers"
2391 gnus-article-remove-leading-whitespace t])
2392 ["Overstrike" gnus-article-treat-overstrike t]
2393 ["Dumb quotes" gnus-article-treat-dumbquotes t]
2394 ["Emphasis" gnus-article-emphasize t]
2395 ["Word wrap" gnus-article-fill-cited-article t]
16409b0b 2396 ["Fill long lines" gnus-article-fill-long-lines t]
01c52d31 2397 ["Toggle truncate long lines" gnus-article-toggle-truncate-lines t]
16409b0b 2398 ["Capitalize sentences" gnus-article-capitalize-sentences t]
23f87bed
MB
2399 ["Remove CR" gnus-article-remove-cr t]
2400 ["Quoted-Printable" gnus-article-de-quoted-unreadable t]
2401 ["Base64" gnus-article-de-base64-unreadable t]
2402 ["Rot 13" gnus-summary-caesar-message
2403 ,@(if (featurep 'xemacs) '(t)
2404 '(:help "\"Caesar rotate\" article by 13"))]
01c52d31 2405 ["De-IDNA" gnus-summary-idna-message t]
23f87bed
MB
2406 ["Morse decode" gnus-summary-morse-message t]
2407 ["Unix pipe..." gnus-summary-pipe-message t]
2408 ["Add buttons" gnus-article-add-buttons t]
2409 ["Add buttons to head" gnus-article-add-buttons-to-head t]
2410 ["Stop page breaking" gnus-summary-stop-page-breaking t]
2411 ["Verbose header" gnus-summary-verbose-headers t]
2412 ["Toggle header" gnus-summary-toggle-header t]
2413 ["Unfold headers" gnus-article-treat-unfold-headers t]
2414 ["Fold newsgroups" gnus-article-treat-fold-newsgroups t]
16409b0b 2415 ["Html" gnus-article-wash-html t]
23f87bed
MB
2416 ["Unsplit URLs" gnus-article-unsplit-urls t]
2417 ["Verify X-PGP-Sig" gnus-article-verify-x-pgp-sig t]
2418 ["Decode HZ" gnus-article-decode-HZ t]
01c52d31 2419 ["ANSI sequences" gnus-article-treat-ansi-sequences t]
23f87bed
MB
2420 ("(Outlook) Deuglify"
2421 ["Unwrap lines" gnus-article-outlook-unwrap-lines t]
2422 ["Repair attribution" gnus-article-outlook-repair-attribution t]
2423 ["Rearrange citation" gnus-article-outlook-rearrange-citation t]
2424 ["Full (Outlook) deuglify"
2425 gnus-article-outlook-deuglify-article t])
2426 )
2427 ("Output"
2428 ["Save in default format..." gnus-summary-save-article
2429 ,@(if (featurep 'xemacs) '(t)
2430 '(:help "Save article using default method"))]
2431 ["Save in file..." gnus-summary-save-article-file
2432 ,@(if (featurep 'xemacs) '(t)
2433 '(:help "Save article in file"))]
2434 ["Save in Unix mail format..." gnus-summary-save-article-mail t]
2435 ["Save in MH folder..." gnus-summary-save-article-folder t]
2436 ["Save in VM folder..." gnus-summary-save-article-vm t]
2437 ["Save in RMAIL mbox..." gnus-summary-save-article-rmail t]
2438 ["Save body in file..." gnus-summary-save-article-body-file t]
2439 ["Pipe through a filter..." gnus-summary-pipe-output t]
2440 ["Add to SOUP packet" gnus-soup-add-article t]
2441 ["Print with Muttprint..." gnus-summary-muttprint t]
531e5812
MB
2442 ["Print" gnus-summary-print-article
2443 ,@(if (featurep 'xemacs) '(t)
2444 '(:help "Generate and print a PostScript image"))])
2445 ("Copy, move,... (Backend)"
707f2b38 2446 ,@(if (featurep 'xemacs) nil
531e5812 2447 '(:help "Copying, moving, expiring articles..."))
23f87bed
MB
2448 ["Respool article..." gnus-summary-respool-article t]
2449 ["Move article..." gnus-summary-move-article
2450 (gnus-check-backend-function
2451 'request-move-article gnus-newsgroup-name)]
2452 ["Copy article..." gnus-summary-copy-article t]
2453 ["Crosspost article..." gnus-summary-crosspost-article
2454 (gnus-check-backend-function
2455 'request-replace-article gnus-newsgroup-name)]
2456 ["Import file..." gnus-summary-import-article
2457 (gnus-check-backend-function
2458 'request-accept-article gnus-newsgroup-name)]
2459 ["Create article..." gnus-summary-create-article
2460 (gnus-check-backend-function
2461 'request-accept-article gnus-newsgroup-name)]
2462 ["Check if posted" gnus-summary-article-posted-p t]
2463 ["Edit article" gnus-summary-edit-article
2464 (not (gnus-group-read-only-p))]
2465 ["Delete article" gnus-summary-delete-article
2466 (gnus-check-backend-function
2467 'request-expire-articles gnus-newsgroup-name)]
2468 ["Query respool" gnus-summary-respool-query t]
6748645f 2469 ["Trace respool" gnus-summary-respool-trace t]
23f87bed
MB
2470 ["Delete expirable articles" gnus-summary-expire-articles-now
2471 (gnus-check-backend-function
2472 'request-expire-articles gnus-newsgroup-name)])
2473 ("Extract"
2474 ["Uudecode" gnus-uu-decode-uu
2475 ,@(if (featurep 'xemacs) '(t)
2476 '(:help "Decode uuencoded article(s)"))]
2477 ["Uudecode and save" gnus-uu-decode-uu-and-save t]
2478 ["Unshar" gnus-uu-decode-unshar t]
2479 ["Unshar and save" gnus-uu-decode-unshar-and-save t]
2480 ["Save" gnus-uu-decode-save t]
2481 ["Binhex" gnus-uu-decode-binhex t]
2482 ["Postscript" gnus-uu-decode-postscript t]
2483 ["All MIME parts" gnus-summary-save-parts t])
2484 ("Cache"
2485 ["Enter article" gnus-cache-enter-article t]
2486 ["Remove article" gnus-cache-remove-article t])
16409b0b 2487 ["Translate" gnus-article-babel t]
23f87bed 2488 ["Select article buffer" gnus-summary-select-article-buffer t]
01c52d31 2489 ["Make article buffer sticky" gnus-sticky-article t]
23f87bed
MB
2490 ["Enter digest buffer" gnus-summary-enter-digest-group t]
2491 ["Isearch article..." gnus-summary-isearch-article t]
2492 ["Beginning of the article" gnus-summary-beginning-of-article t]
2493 ["End of the article" gnus-summary-end-of-article t]
2494 ["Fetch parent of article" gnus-summary-refer-parent-article t]
2495 ["Fetch referenced articles" gnus-summary-refer-references t]
2496 ["Fetch current thread" gnus-summary-refer-thread t]
2497 ["Fetch article with id..." gnus-summary-refer-article t]
2498 ["Setup Mailing List Params" gnus-mailing-list-insinuate t]
2499 ["Redisplay" gnus-summary-show-article t]
2500 ["Raw article" gnus-summary-show-raw-article :keys "C-u g"])))
6748645f 2501 (easy-menu-define
23f87bed
MB
2502 gnus-summary-article-menu gnus-summary-mode-map ""
2503 (cons "Article" innards))
6748645f 2504
1653df0f
SZ
2505 (if (not (keymapp gnus-summary-article-menu))
2506 (easy-menu-define
2507 gnus-article-commands-menu gnus-article-mode-map ""
2508 (cons "Commands" innards))
2509 ;; in Emacs, don't share menu.
a1506d29 2510 (setq gnus-article-commands-menu
1653df0f
SZ
2511 (copy-keymap gnus-summary-article-menu))
2512 (define-key gnus-article-mode-map [menu-bar commands]
2513 (cons "Commands" gnus-article-commands-menu))))
eec82323
LMI
2514
2515 (easy-menu-define
23f87bed
MB
2516 gnus-summary-thread-menu gnus-summary-mode-map ""
2517 '("Threads"
2518 ["Find all messages in thread" gnus-summary-refer-thread t]
2519 ["Toggle threading" gnus-summary-toggle-threads t]
2520 ["Hide threads" gnus-summary-hide-all-threads t]
2521 ["Show threads" gnus-summary-show-all-threads t]
2522 ["Hide thread" gnus-summary-hide-thread t]
2523 ["Show thread" gnus-summary-show-thread t]
2524 ["Go to next thread" gnus-summary-next-thread t]
2525 ["Go to previous thread" gnus-summary-prev-thread t]
2526 ["Go down thread" gnus-summary-down-thread t]
2527 ["Go up thread" gnus-summary-up-thread t]
2528 ["Top of thread" gnus-summary-top-thread t]
2529 ["Mark thread as read" gnus-summary-kill-thread t]
01c52d31 2530 ["Mark thread as expired" gnus-summary-expire-thread t]
23f87bed
MB
2531 ["Lower thread score" gnus-summary-lower-thread t]
2532 ["Raise thread score" gnus-summary-raise-thread t]
2533 ["Rethread current" gnus-summary-rethread-current t]))
eec82323
LMI
2534
2535 (easy-menu-define
23f87bed
MB
2536 gnus-summary-post-menu gnus-summary-mode-map ""
2537 `("Post"
2538 ["Send a message (mail or news)" gnus-summary-post-news
2539 ,@(if (featurep 'xemacs) '(t)
531e5812 2540 '(:help "Compose a new message (mail or news)"))]
23f87bed
MB
2541 ["Followup" gnus-summary-followup
2542 ,@(if (featurep 'xemacs) '(t)
2543 '(:help "Post followup to this article"))]
2544 ["Followup and yank" gnus-summary-followup-with-original
2545 ,@(if (featurep 'xemacs) '(t)
2546 '(:help "Post followup to this article, quoting its contents"))]
2547 ["Supersede article" gnus-summary-supersede-article t]
2548 ["Cancel article" gnus-summary-cancel-article
2549 ,@(if (featurep 'xemacs) '(t)
2550 '(:help "Cancel an article you posted"))]
2551 ["Reply" gnus-summary-reply t]
2552 ["Reply and yank" gnus-summary-reply-with-original t]
2553 ["Wide reply" gnus-summary-wide-reply t]
2554 ["Wide reply and yank" gnus-summary-wide-reply-with-original
2555 ,@(if (featurep 'xemacs) '(t)
2556 '(:help "Mail a reply, quoting this article"))]
2557 ["Very wide reply" gnus-summary-very-wide-reply t]
2558 ["Very wide reply and yank" gnus-summary-very-wide-reply-with-original
2559 ,@(if (featurep 'xemacs) '(t)
2560 '(:help "Mail a very wide reply, quoting this article"))]
2561 ["Mail forward" gnus-summary-mail-forward t]
2562 ["Post forward" gnus-summary-post-forward t]
2563 ["Digest and mail" gnus-uu-digest-mail-forward t]
2564 ["Digest and post" gnus-uu-digest-post-forward t]
2565 ["Resend message" gnus-summary-resend-message t]
2566 ["Resend message edit" gnus-summary-resend-message-edit t]
2567 ["Send bounced mail" gnus-summary-resend-bounced-mail t]
2568 ["Send a mail" gnus-summary-mail-other-window t]
2569 ["Create a local message" gnus-summary-news-other-window t]
2570 ["Uuencode and post" gnus-uu-post-news
2571 ,@(if (featurep 'xemacs) '(t)
2572 '(:help "Post a uuencoded article"))]
2573 ["Followup via news" gnus-summary-followup-to-mail t]
2574 ["Followup via news and yank"
2575 gnus-summary-followup-to-mail-with-original t]
9b3ebcb6
MB
2576 ["Strip signature on reply"
2577 (lambda ()
2578 (interactive)
2579 (if (not (memq message-cite-function
2580 '(message-cite-original-without-signature
2581 message-cite-original)))
2582 ;; Stupid workaround for XEmacs not honoring :visible.
2583 (message "Can't toggle this value of `message-cite-function'")
2584 (setq message-cite-function
2585 (if (eq message-cite-function
2586 'message-cite-original-without-signature)
2587 'message-cite-original
2588 'message-cite-original-without-signature))))
2589 ;; XEmacs barfs on :visible.
2590 ,@(if (featurep 'xemacs) nil
2591 '(:visible (memq message-cite-function
2592 '(message-cite-original-without-signature
2593 message-cite-original))))
2594 :style toggle
2595 :selected (eq message-cite-function
2596 'message-cite-original-without-signature)
2597 ,@(if (featurep 'xemacs) nil
2598 '(:help "Strip signature from cited article when replying."))]
23f87bed
MB
2599 ;;("Draft"
2600 ;;["Send" gnus-summary-send-draft t]
2601 ;;["Send bounced" gnus-resend-bounced-mail t])
2602 ))
2603
2604 (cond
2605 ((not (keymapp gnus-summary-post-menu))
2606 (setq gnus-article-post-menu gnus-summary-post-menu))
2607 ((not gnus-article-post-menu)
2608 ;; Don't share post menu.
2609 (setq gnus-article-post-menu
2610 (copy-keymap gnus-summary-post-menu))))
2611 (define-key gnus-article-mode-map [menu-bar post]
2612 (cons "Post" gnus-article-post-menu))
eec82323
LMI
2613
2614 (easy-menu-define
23f87bed
MB
2615 gnus-summary-misc-menu gnus-summary-mode-map ""
2616 `("Gnus"
2617 ("Mark Read"
2618 ["Mark as read" gnus-summary-mark-as-read-forward t]
2619 ["Mark same subject and select"
2620 gnus-summary-kill-same-subject-and-select t]
2621 ["Mark same subject" gnus-summary-kill-same-subject t]
2622 ["Catchup" gnus-summary-catchup
2623 ,@(if (featurep 'xemacs) '(t)
2624 '(:help "Mark unread articles in this group as read"))]
2625 ["Catchup all" gnus-summary-catchup-all t]
2626 ["Catchup to here" gnus-summary-catchup-to-here t]
2627 ["Catchup from here" gnus-summary-catchup-from-here t]
2628 ["Catchup region" gnus-summary-mark-region-as-read
2629 (gnus-mark-active-p)]
2630 ["Mark excluded" gnus-summary-limit-mark-excluded-as-read t])
2631 ("Mark Various"
2632 ["Tick" gnus-summary-tick-article-forward t]
2633 ["Mark as dormant" gnus-summary-mark-as-dormant t]
2634 ["Remove marks" gnus-summary-clear-mark-forward t]
2635 ["Set expirable mark" gnus-summary-mark-as-expirable t]
2636 ["Set bookmark" gnus-summary-set-bookmark t]
2637 ["Remove bookmark" gnus-summary-remove-bookmark t])
2638 ("Limit to"
2639 ["Marks..." gnus-summary-limit-to-marks t]
2640 ["Subject..." gnus-summary-limit-to-subject t]
2641 ["Author..." gnus-summary-limit-to-author t]
01c52d31
MB
2642 ["Recipient..." gnus-summary-limit-to-recipient t]
2643 ["Address..." gnus-summary-limit-to-address t]
23f87bed
MB
2644 ["Age..." gnus-summary-limit-to-age t]
2645 ["Extra..." gnus-summary-limit-to-extra t]
2646 ["Score..." gnus-summary-limit-to-score t]
2647 ["Display Predicate" gnus-summary-limit-to-display-predicate t]
2648 ["Unread" gnus-summary-limit-to-unread t]
2649 ["Unseen" gnus-summary-limit-to-unseen t]
01c52d31
MB
2650 ["Singletons" gnus-summary-limit-to-singletons t]
2651 ["Replied" gnus-summary-limit-to-replied t]
23f87bed 2652 ["Non-dormant" gnus-summary-limit-exclude-dormant t]
996aa8c1 2653 ["Next or process marked articles" gnus-summary-limit-to-articles t]
23f87bed
MB
2654 ["Pop limit" gnus-summary-pop-limit t]
2655 ["Show dormant" gnus-summary-limit-include-dormant t]
2656 ["Hide childless dormant"
2657 gnus-summary-limit-exclude-childless-dormant t]
2658 ;;["Hide thread" gnus-summary-limit-exclude-thread t]
2659 ["Hide marked" gnus-summary-limit-exclude-marks t]
2660 ["Show expunged" gnus-summary-limit-include-expunged t])
2661 ("Process Mark"
2662 ["Set mark" gnus-summary-mark-as-processable t]
2663 ["Remove mark" gnus-summary-unmark-as-processable t]
2664 ["Remove all marks" gnus-summary-unmark-all-processable t]
01c52d31 2665 ["Invert marks" gnus-uu-invert-processable t]
23f87bed
MB
2666 ["Mark above" gnus-uu-mark-over t]
2667 ["Mark series" gnus-uu-mark-series t]
2668 ["Mark region" gnus-uu-mark-region (gnus-mark-active-p)]
2669 ["Unmark region" gnus-uu-unmark-region (gnus-mark-active-p)]
2670 ["Mark by regexp..." gnus-uu-mark-by-regexp t]
2671 ["Unmark by regexp..." gnus-uu-unmark-by-regexp t]
2672 ["Mark all" gnus-uu-mark-all t]
2673 ["Mark buffer" gnus-uu-mark-buffer t]
2674 ["Mark sparse" gnus-uu-mark-sparse t]
2675 ["Mark thread" gnus-uu-mark-thread t]
2676 ["Unmark thread" gnus-uu-unmark-thread t]
2677 ("Process Mark Sets"
2678 ["Kill" gnus-summary-kill-process-mark t]
2679 ["Yank" gnus-summary-yank-process-mark
2680 gnus-newsgroup-process-stack]
2681 ["Save" gnus-summary-save-process-mark t]
2682 ["Run command on marked..." gnus-summary-universal-argument t]))
ec7995fa 2683 ("Registry Marks")
23f87bed
MB
2684 ("Scroll article"
2685 ["Page forward" gnus-summary-next-page
2686 ,@(if (featurep 'xemacs) '(t)
2687 '(:help "Show next page of article"))]
2688 ["Page backward" gnus-summary-prev-page
2689 ,@(if (featurep 'xemacs) '(t)
2690 '(:help "Show previous page of article"))]
2691 ["Line forward" gnus-summary-scroll-up t])
2692 ("Move"
2693 ["Next unread article" gnus-summary-next-unread-article t]
2694 ["Previous unread article" gnus-summary-prev-unread-article t]
2695 ["Next article" gnus-summary-next-article t]
2696 ["Previous article" gnus-summary-prev-article t]
2697 ["Next unread subject" gnus-summary-next-unread-subject t]
2698 ["Previous unread subject" gnus-summary-prev-unread-subject t]
2699 ["Next article same subject" gnus-summary-next-same-subject t]
2700 ["Previous article same subject" gnus-summary-prev-same-subject t]
2701 ["First unread article" gnus-summary-first-unread-article t]
2702 ["Best unread article" gnus-summary-best-unread-article t]
2703 ["Go to subject number..." gnus-summary-goto-subject t]
2704 ["Go to article number..." gnus-summary-goto-article t]
2705 ["Go to the last article" gnus-summary-goto-last-article t]
2706 ["Pop article off history" gnus-summary-pop-article t])
2707 ("Sort"
2708 ["Sort by number" gnus-summary-sort-by-number t]
6ecfe5c2 2709 ["Sort by most recent number" gnus-summary-sort-by-most-recent-number t]
23f87bed 2710 ["Sort by author" gnus-summary-sort-by-author t]
01c52d31 2711 ["Sort by recipient" gnus-summary-sort-by-recipient t]
23f87bed
MB
2712 ["Sort by subject" gnus-summary-sort-by-subject t]
2713 ["Sort by date" gnus-summary-sort-by-date t]
6ecfe5c2 2714 ["Sort by most recent date" gnus-summary-sort-by-most-recent-date t]
23f87bed
MB
2715 ["Sort by score" gnus-summary-sort-by-score t]
2716 ["Sort by lines" gnus-summary-sort-by-lines t]
2717 ["Sort by characters" gnus-summary-sort-by-chars t]
2718 ["Randomize" gnus-summary-sort-by-random t]
2719 ["Original sort" gnus-summary-sort-by-original t])
2720 ("Help"
2721 ["Fetch group FAQ" gnus-summary-fetch-faq t]
2722 ["Describe group" gnus-summary-describe-group t]
2723 ["Fetch charter" gnus-group-fetch-charter
2724 ,@(if (featurep 'xemacs) nil
2725 '(:help "Display the charter of the current group"))]
2726 ["Fetch control message" gnus-group-fetch-control
2727 ,@(if (featurep 'xemacs) nil
2728 '(:help "Display the archived control message for the current group"))]
2729 ["Read manual" gnus-info-find-node t])
2730 ("Modes"
2731 ["Pick and read" gnus-pick-mode t]
2732 ["Binary" gnus-binary-mode t])
2733 ("Regeneration"
2734 ["Regenerate" gnus-summary-prepare t]
2735 ["Insert cached articles" gnus-summary-insert-cached-articles t]
2736 ["Insert dormant articles" gnus-summary-insert-dormant-articles t]
01c52d31 2737 ["Insert ticked articles" gnus-summary-insert-ticked-articles t]
23f87bed
MB
2738 ["Toggle threading" gnus-summary-toggle-threads t])
2739 ["See old articles" gnus-summary-insert-old-articles t]
2740 ["See new articles" gnus-summary-insert-new-articles t]
2741 ["Filter articles..." gnus-summary-execute-command t]
2742 ["Run command on articles..." gnus-summary-universal-argument t]
2743 ["Search articles forward..." gnus-summary-search-article-forward t]
2744 ["Search articles backward..." gnus-summary-search-article-backward t]
2745 ["Toggle line truncation" gnus-summary-toggle-truncation t]
2746 ["Expand window" gnus-summary-expand-window t]
2747 ["Expire expirable articles" gnus-summary-expire-articles
2748 (gnus-check-backend-function
2749 'request-expire-articles gnus-newsgroup-name)]
2750 ["Edit local kill file" gnus-summary-edit-local-kill t]
2751 ["Edit main kill file" gnus-summary-edit-global-kill t]
2752 ["Edit group parameters" gnus-summary-edit-parameters t]
2753 ["Customize group parameters" gnus-summary-customize-parameters t]
2754 ["Send a bug report" gnus-bug t]
2755 ("Exit"
2756 ["Catchup and exit" gnus-summary-catchup-and-exit
2757 ,@(if (featurep 'xemacs) '(t)
2758 '(:help "Mark unread articles in this group as read, then exit"))]
2759 ["Catchup all and exit" gnus-summary-catchup-all-and-exit t]
2760 ["Catchup and goto next" gnus-summary-catchup-and-goto-next-group t]
01c52d31 2761 ["Catchup and goto prev" gnus-summary-catchup-and-goto-prev-group t]
23f87bed
MB
2762 ["Exit group" gnus-summary-exit
2763 ,@(if (featurep 'xemacs) '(t)
2764 '(:help "Exit current group, return to group selection mode"))]
2765 ["Exit group without updating" gnus-summary-exit-no-update t]
2766 ["Exit and goto next group" gnus-summary-next-group t]
2767 ["Exit and goto prev group" gnus-summary-prev-group t]
2768 ["Reselect group" gnus-summary-reselect-current-group t]
2769 ["Rescan group" gnus-summary-rescan-group t]
2770 ["Update dribble" gnus-summary-save-newsrc t])))
eec82323 2771
6748645f 2772 (gnus-run-hooks 'gnus-summary-menu-hook)))
eec82323 2773
60bd5589
DL
2774(defvar gnus-summary-tool-bar-map nil)
2775
18c06a99
RS
2776;; Note: The :set function in the `gnus-summary-tool-bar*' variables will only
2777;; affect _new_ message buffers. We might add a function that walks thru all
2778;; summary-mode buffers and force the update.
2779(defun gnus-summary-tool-bar-update (&optional symbol value)
2780 "Update summary mode toolbar.
2781Setter function for custom variables."
2782 (setq-default gnus-summary-tool-bar-map nil)
2783 (when symbol
2784 ;; When used as ":set" function:
2785 (set-default symbol value))
2786 (when (gnus-buffer-live-p gnus-summary-buffer)
2787 (with-current-buffer gnus-summary-buffer
2788 (gnus-summary-make-tool-bar))))
2789
2790(defcustom gnus-summary-tool-bar (if (eq gmm-tool-bar-style 'gnome)
2791 'gnus-summary-tool-bar-gnome
2792 'gnus-summary-tool-bar-retro)
2793 "Specifies the Gnus summary tool bar.
2794
2795It can be either a list or a symbol refering to a list. See
2796`gmm-tool-bar-from-list' for the format of the list. The
2797default key map is `gnus-summary-mode-map'.
2798
2799Pre-defined symbols include `gnus-summary-tool-bar-gnome' and
2800`gnus-summary-tool-bar-retro'."
2801 :type '(choice (const :tag "GNOME style" gnus-summary-tool-bar-gnome)
2802 (const :tag "Retro look" gnus-summary-tool-bar-retro)
2803 (repeat :tag "User defined list" gmm-tool-bar-item)
2804 (symbol))
330f707b 2805 :version "23.1" ;; No Gnus
18c06a99
RS
2806 :initialize 'custom-initialize-default
2807 :set 'gnus-summary-tool-bar-update
2808 :group 'gnus-summary)
2809
2810(defcustom gnus-summary-tool-bar-gnome
2811 '((gnus-summary-post-news "mail/compose" nil)
2812 (gnus-summary-insert-new-articles "mail/inbox" nil
2813 :visible (or (not gnus-agent)
2814 gnus-plugged))
2815 (gnus-summary-reply-with-original "mail/reply")
2816 (gnus-summary-reply "mail/reply" nil :visible nil)
2817 (gnus-summary-followup-with-original "mail/reply-all")
2818 (gnus-summary-followup "mail/reply-all" nil :visible nil)
2819 (gnus-summary-mail-forward "mail/forward")
2820 (gnus-summary-save-article "mail/save")
2821 (gnus-summary-search-article-forward "search" nil :visible nil)
2822 (gnus-summary-print-article "print")
2823 (gnus-summary-tick-article-forward "flag-followup" nil :visible nil)
2824 ;; Some new commands that may need more suitable icons:
2825 (gnus-summary-save-newsrc "save" nil :visible nil)
2826 ;; (gnus-summary-show-article "stock_message-display" nil :visible nil)
2827 (gnus-summary-prev-article "left-arrow")
2828 (gnus-summary-next-article "right-arrow")
2829 (gnus-summary-next-page "next-page")
2830 ;; (gnus-summary-enter-digest-group "right_arrow" nil :visible nil)
2831 ;;
2832 ;; Maybe some sort-by-... could be added:
2833 ;; (gnus-summary-sort-by-author "sort-a-z" nil :visible nil)
2834 ;; (gnus-summary-sort-by-date "sort-1-9" nil :visible nil)
2835 (gnus-summary-mark-as-expirable
2836 "delete" nil
2837 :visible (gnus-check-backend-function 'request-expire-articles
2838 gnus-newsgroup-name))
2839 (gnus-summary-mark-as-spam
2840 "mail/spam" t
2841 :visible (and (fboundp 'spam-group-ham-contents-p)
2842 (spam-group-ham-contents-p gnus-newsgroup-name))
2843 :help "Mark as spam")
2844 (gnus-summary-mark-as-read-forward
2845 "mail/not-spam" nil
2846 :visible (and (fboundp 'spam-group-spam-contents-p)
2847 (spam-group-spam-contents-p gnus-newsgroup-name)))
2848 ;;
2849 (gnus-summary-exit "exit")
2850 (gmm-customize-mode "preferences" t :help "Edit mode preferences")
2851 (gnus-info-find-node "help"))
2852 "List of functions for the summary tool bar (GNOME style).
2853
2854See `gmm-tool-bar-from-list' for the format of the list."
2855 :type '(repeat gmm-tool-bar-item)
330f707b 2856 :version "23.1" ;; No Gnus
18c06a99
RS
2857 :initialize 'custom-initialize-default
2858 :set 'gnus-summary-tool-bar-update
2859 :group 'gnus-summary)
2860
2861(defcustom gnus-summary-tool-bar-retro
2862 '((gnus-summary-prev-unread-article "gnus/prev-ur")
2863 (gnus-summary-next-unread-article "gnus/next-ur")
2864 (gnus-summary-post-news "gnus/post")
2865 (gnus-summary-followup-with-original "gnus/fuwo")
2866 (gnus-summary-followup "gnus/followup")
2867 (gnus-summary-reply-with-original "gnus/reply-wo")
2868 (gnus-summary-reply "gnus/reply")
2869 (gnus-summary-caesar-message "gnus/rot13")
2870 (gnus-uu-decode-uu "gnus/uu-decode")
2871 (gnus-summary-save-article-file "gnus/save-aif")
2872 (gnus-summary-save-article "gnus/save-art")
2873 (gnus-uu-post-news "gnus/uu-post")
2874 (gnus-summary-catchup "gnus/catchup")
2875 (gnus-summary-catchup-and-exit "gnus/cu-exit")
2876 (gnus-summary-exit "gnus/exit-summ")
2877 ;; Some new command that may need more suitable icons:
2878 (gnus-summary-print-article "gnus/print" nil :visible nil)
2879 (gnus-summary-mark-as-expirable "gnus/close" nil :visible nil)
2880 (gnus-summary-save-newsrc "gnus/save" nil :visible nil)
2881 ;; (gnus-summary-enter-digest-group "gnus/right_arrow" nil :visible nil)
2882 (gnus-summary-search-article-forward "gnus/search" nil :visible nil)
2883 ;; (gnus-summary-insert-new-articles "gnus/paste" nil :visible nil)
2884 ;; (gnus-summary-toggle-threads "gnus/open" nil :visible nil)
2885 ;;
2886 (gnus-info-find-node "gnus/help" nil :visible nil))
2887 "List of functions for the summary tool bar (retro look).
2888
2889See `gmm-tool-bar-from-list' for the format of the list."
2890 :type '(repeat gmm-tool-bar-item)
330f707b 2891 :version "23.1" ;; No Gnus
18c06a99
RS
2892 :initialize 'custom-initialize-default
2893 :set 'gnus-summary-tool-bar-update
2894 :group 'gnus-summary)
2895
2896(defcustom gnus-summary-tool-bar-zap-list t
2897 "List of icon items from the global tool bar.
2898These items are not displayed in the Gnus summary mode tool bar.
2899
2900See `gmm-tool-bar-from-list' for the format of the list."
2901 :type 'gmm-tool-bar-zap-list
330f707b 2902 :version "23.1" ;; No Gnus
18c06a99
RS
2903 :initialize 'custom-initialize-default
2904 :set 'gnus-summary-tool-bar-update
2905 :group 'gnus-summary)
2906
2907(defvar image-load-path)
f654fa04 2908(defvar tool-bar-map)
18c06a99
RS
2909
2910(defun gnus-summary-make-tool-bar (&optional force)
2911 "Make a summary mode tool bar from `gnus-summary-tool-bar'.
2912When FORCE, rebuild the tool bar."
2913 (when (and (not (featurep 'xemacs))
2914 (boundp 'tool-bar-mode)
2915 tool-bar-mode
2916 (or (not gnus-summary-tool-bar-map) force))
2917 (let* ((load-path
2918 (gmm-image-load-path-for-library "gnus"
2919 "mail/save.xpm"
2920 nil t))
2921 (image-load-path (cons (car load-path)
2922 (when (boundp 'image-load-path)
2923 image-load-path)))
2924 (map (gmm-tool-bar-from-list gnus-summary-tool-bar
2925 gnus-summary-tool-bar-zap-list
2926 'gnus-summary-mode-map)))
2927 (when map
2928 ;; Need to set `gnus-summary-tool-bar-map' because `gnus-article-mode'
2929 ;; uses it's value.
2930 (setq gnus-summary-tool-bar-map map))))
2931 (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map))
60bd5589 2932
eec82323
LMI
2933(defun gnus-score-set-default (var value)
2934 "A version of set that updates the GNU Emacs menu-bar."
2935 (set var value)
2936 ;; It is the message that forces the active status to be updated.
2937 (message ""))
2938
2939(defun gnus-make-score-map (type)
2940 "Make a summary score map of type TYPE."
2941 (if t
2942 nil
2943 (let ((headers '(("author" "from" string)
2944 ("subject" "subject" string)
2945 ("article body" "body" string)
2946 ("article head" "head" string)
2947 ("xref" "xref" string)
16409b0b 2948 ("extra header" "extra" string)
eec82323
LMI
2949 ("lines" "lines" number)
2950 ("followups to author" "followup" string)))
2951 (types '((number ("less than" <)
2952 ("greater than" >)
2953 ("equal" =))
2954 (string ("substring" s)
2955 ("exact string" e)
2956 ("fuzzy string" f)
2957 ("regexp" r))))
2958 (perms '(("temporary" (current-time-string))
2959 ("permanent" nil)
2960 ("immediate" now)))
2961 header)
2962 (list
2963 (apply
2964 'nconc
2965 (list
2966 (if (eq type 'lower)
2967 "Lower score"
2968 "Increase score"))
2969 (let (outh)
2970 (while headers
2971 (setq header (car headers))
2972 (setq outh
2973 (cons
2974 (apply
2975 'nconc
2976 (list (car header))
2977 (let ((ts (cdr (assoc (nth 2 header) types)))
2978 outt)
2979 (while ts
2980 (setq outt
2981 (cons
2982 (apply
2983 'nconc
2984 (list (caar ts))
2985 (let ((ps perms)
2986 outp)
2987 (while ps
2988 (setq outp
2989 (cons
2990 (vector
2991 (caar ps)
2992 (list
2993 'gnus-summary-score-entry
2994 (nth 1 header)
2995 (if (or (string= (nth 1 header)
2996 "head")
2997 (string= (nth 1 header)
2998 "body"))
2999 ""
3000 (list 'gnus-summary-header
3001 (nth 1 header)))
3002 (list 'quote (nth 1 (car ts)))
16409b0b
GM
3003 (list 'gnus-score-delta-default
3004 nil)
eec82323
LMI
3005 (nth 1 (car ps))
3006 t)
3007 t)
3008 outp))
3009 (setq ps (cdr ps)))
3010 (list (nreverse outp))))
3011 outt))
3012 (setq ts (cdr ts)))
3013 (list (nreverse outt))))
3014 outh))
3015 (setq headers (cdr headers)))
3016 (list (nreverse outh))))))))
3017
704f1663
GM
3018
3019(declare-function turn-on-gnus-mailing-list-mode "gnus-ml" ())
398a825b 3020(defvar bookmark-make-record-function)
eec82323
LMI
3021\f
3022
3023(defun gnus-summary-mode (&optional group)
3024 "Major mode for reading articles.
3025
3026All normal editing commands are switched off.
3027\\<gnus-summary-mode-map>
3028Each line in this buffer represents one article. To read an
3029article, you can, for instance, type `\\[gnus-summary-next-page]'. To move forwards
3030and backwards while displaying articles, type `\\[gnus-summary-next-unread-article]' and `\\[gnus-summary-prev-unread-article]',
3031respectively.
3032
3033You can also post articles and send mail from this buffer. To
23f87bed 3034follow up an article, type `\\[gnus-summary-followup]'. To mail a reply to the author
eec82323
LMI
3035of an article, type `\\[gnus-summary-reply]'.
3036
3037There are approx. one gazillion commands you can execute in this
3038buffer; read the info pages for more information (`\\[gnus-info-find-node]').
3039
3040The following commands are available:
3041
3042\\{gnus-summary-mode-map}"
3043 (interactive)
eec82323 3044 (kill-all-local-variables)
01c52d31
MB
3045 (let ((gnus-summary-local-variables gnus-newsgroup-variables))
3046 (gnus-summary-make-local-variables))
3047 (gnus-summary-make-local-variables)
3048 (setq gnus-newsgroup-name group)
60bd5589
DL
3049 (when (gnus-visual-p 'summary-menu 'menu)
3050 (gnus-summary-make-menu-bar)
3051 (gnus-summary-make-tool-bar))
eec82323
LMI
3052 (gnus-make-thread-indent-array)
3053 (gnus-simplify-mode-line)
3054 (setq major-mode 'gnus-summary-mode)
3055 (setq mode-name "Summary")
3056 (make-local-variable 'minor-mode-alist)
3057 (use-local-map gnus-summary-mode-map)
16409b0b 3058 (buffer-disable-undo)
01c52d31
MB
3059 (setq buffer-read-only t ;Disable modification
3060 show-trailing-whitespace nil)
eec82323 3061 (setq truncate-lines t)
9bfd9a76 3062 (add-to-invisibility-spec '(gnus-sum . t))
eec82323
LMI
3063 (gnus-summary-set-display-table)
3064 (gnus-set-default-directory)
eec82323
LMI
3065 (make-local-variable 'gnus-summary-line-format)
3066 (make-local-variable 'gnus-summary-line-format-spec)
6748645f
LMI
3067 (make-local-variable 'gnus-summary-dummy-line-format)
3068 (make-local-variable 'gnus-summary-dummy-line-format-spec)
eec82323 3069 (make-local-variable 'gnus-summary-mark-positions)
23f87bed 3070 (gnus-make-local-hook 'pre-command-hook)
6748645f 3071 (add-hook 'pre-command-hook 'gnus-set-global-variables nil t)
cfcd5c91 3072 (gnus-run-mode-hooks 'gnus-summary-mode-hook)
23f87bed 3073 (turn-on-gnus-mailing-list-mode)
87545352 3074 (mm-enable-multibyte)
45be326a
TV
3075 (set (make-local-variable 'bookmark-make-record-function)
3076 'gnus-summary-bookmark-make-record)
eec82323
LMI
3077 (gnus-update-format-specifications nil 'summary 'summary-mode 'summary-dummy)
3078 (gnus-update-summary-mark-positions))
3079
3080(defun gnus-summary-make-local-variables ()
3081 "Make all the local summary buffer variables."
16409b0b
GM
3082 (let (global)
3083 (dolist (local gnus-summary-local-variables)
eec82323
LMI
3084 (if (consp local)
3085 (progn
3086 (if (eq (cdr local) 'global)
3087 ;; Copy the global value of the variable.
3088 (setq global (symbol-value (car local)))
3089 ;; Use the value from the list.
3090 (setq global (eval (cdr local))))
16409b0b 3091 (set (make-local-variable (car local)) global))
eec82323 3092 ;; Simple nil-valued local variable.
16409b0b 3093 (set (make-local-variable local) nil)))))
eec82323
LMI
3094
3095(defun gnus-summary-clear-local-variables ()
3096 (let ((locals gnus-summary-local-variables))
3097 (while locals
3098 (if (consp (car locals))
01c52d31 3099 (and (symbolp (caar locals))
eec82323 3100 (set (caar locals) nil))
01c52d31 3101 (and (symbolp (car locals))
eec82323
LMI
3102 (set (car locals) nil)))
3103 (setq locals (cdr locals)))))
3104
3105;; Summary data functions.
3106
3107(defmacro gnus-data-number (data)
3108 `(car ,data))
3109
3110(defmacro gnus-data-set-number (data number)
3111 `(setcar ,data ,number))
3112
3113(defmacro gnus-data-mark (data)
3114 `(nth 1 ,data))
3115
3116(defmacro gnus-data-set-mark (data mark)
3117 `(setcar (nthcdr 1 ,data) ,mark))
3118
3119(defmacro gnus-data-pos (data)
3120 `(nth 2 ,data))
3121
3122(defmacro gnus-data-set-pos (data pos)
3123 `(setcar (nthcdr 2 ,data) ,pos))
3124
3125(defmacro gnus-data-header (data)
3126 `(nth 3 ,data))
3127
3128(defmacro gnus-data-set-header (data header)
3129 `(setf (nth 3 ,data) ,header))
3130
3131(defmacro gnus-data-level (data)
3132 `(nth 4 ,data))
3133
3134(defmacro gnus-data-unread-p (data)
3135 `(= (nth 1 ,data) gnus-unread-mark))
3136
3137(defmacro gnus-data-read-p (data)
3138 `(/= (nth 1 ,data) gnus-unread-mark))
3139
3140(defmacro gnus-data-pseudo-p (data)
3141 `(consp (nth 3 ,data)))
3142
3143(defmacro gnus-data-find (number)
3144 `(assq ,number gnus-newsgroup-data))
3145
3146(defmacro gnus-data-find-list (number &optional data)
3147 `(let ((bdata ,(or data 'gnus-newsgroup-data)))
3148 (memq (assq ,number bdata)
3149 bdata)))
3150
3151(defmacro gnus-data-make (number mark pos header level)
3152 `(list ,number ,mark ,pos ,header ,level))
3153
3154(defun gnus-data-enter (after-article number mark pos header level offset)
3155 (let ((data (gnus-data-find-list after-article)))
3156 (unless data
3157 (error "No such article: %d" after-article))
3158 (setcdr data (cons (gnus-data-make number mark pos header level)
3159 (cdr data)))
3160 (setq gnus-newsgroup-data-reverse nil)
3161 (gnus-data-update-list (cddr data) offset)))
3162
3163(defun gnus-data-enter-list (after-article list &optional offset)
3164 (when list
3165 (let ((data (and after-article (gnus-data-find-list after-article)))
3166 (ilist list))
6748645f
LMI
3167 (if (not (or data
3168 after-article))
3169 (let ((odata gnus-newsgroup-data))
3170 (setq gnus-newsgroup-data (nconc list gnus-newsgroup-data))
eec82323 3171 (when offset
6748645f 3172 (gnus-data-update-list odata offset)))
01c52d31 3173 ;; Find the last element in the list to be spliced into the main
6748645f 3174 ;; list.
01c52d31 3175 (setq list (last list))
6748645f
LMI
3176 (if (not data)
3177 (progn
3178 (setcdr list gnus-newsgroup-data)
3179 (setq gnus-newsgroup-data ilist)
3180 (when offset
3181 (gnus-data-update-list (cdr list) offset)))
3182 (setcdr list (cdr data))
3183 (setcdr data ilist)
3184 (when offset
3185 (gnus-data-update-list (cdr list) offset))))
eec82323
LMI
3186 (setq gnus-newsgroup-data-reverse nil))))
3187
3188(defun gnus-data-remove (article &optional offset)
3189 (let ((data gnus-newsgroup-data))
3190 (if (= (gnus-data-number (car data)) article)
3191 (progn
3192 (setq gnus-newsgroup-data (cdr gnus-newsgroup-data)
3193 gnus-newsgroup-data-reverse nil)
3194 (when offset
3195 (gnus-data-update-list gnus-newsgroup-data offset)))
3196 (while (cdr data)
3197 (when (= (gnus-data-number (cadr data)) article)
3198 (setcdr data (cddr data))
3199 (when offset
3200 (gnus-data-update-list (cdr data) offset))
3201 (setq data nil
3202 gnus-newsgroup-data-reverse nil))
3203 (setq data (cdr data))))))
3204
3205(defmacro gnus-data-list (backward)
3206 `(if ,backward
3207 (or gnus-newsgroup-data-reverse
3208 (setq gnus-newsgroup-data-reverse
3209 (reverse gnus-newsgroup-data)))
3210 gnus-newsgroup-data))
3211
3212(defun gnus-data-update-list (data offset)
3213 "Add OFFSET to the POS of all data entries in DATA."
6748645f 3214 (setq gnus-newsgroup-data-reverse nil)
eec82323
LMI
3215 (while data
3216 (setcar (nthcdr 2 (car data)) (+ offset (nth 2 (car data))))
3217 (setq data (cdr data))))
3218
eec82323
LMI
3219(defun gnus-summary-article-pseudo-p (article)
3220 "Say whether this article is a pseudo article or not."
3221 (not (vectorp (gnus-data-header (gnus-data-find article)))))
3222
3223(defmacro gnus-summary-article-sparse-p (article)
3224 "Say whether this article is a sparse article or not."
a8151ef7 3225 `(memq ,article gnus-newsgroup-sparse))
eec82323
LMI
3226
3227(defmacro gnus-summary-article-ancient-p (article)
3228 "Say whether this article is a sparse article or not."
3229 `(memq ,article gnus-newsgroup-ancient))
3230
3231(defun gnus-article-parent-p (number)
3232 "Say whether this article is a parent or not."
3233 (let ((data (gnus-data-find-list number)))
23f87bed 3234 (and (cdr data) ; There has to be an article after...
eec82323
LMI
3235 (< (gnus-data-level (car data)) ; And it has to have a higher level.
3236 (gnus-data-level (nth 1 data))))))
3237
3238(defun gnus-article-children (number)
3239 "Return a list of all children to NUMBER."
3240 (let* ((data (gnus-data-find-list number))
3241 (level (gnus-data-level (car data)))
3242 children)
3243 (setq data (cdr data))
3244 (while (and data
3245 (= (gnus-data-level (car data)) (1+ level)))
3246 (push (gnus-data-number (car data)) children)
3247 (setq data (cdr data)))
3248 children))
3249
3250(defmacro gnus-summary-skip-intangible ()
3251 "If the current article is intangible, then jump to a different article."
3252 '(let ((to (get-text-property (point) 'gnus-intangible)))
3253 (and to (gnus-summary-goto-subject to))))
3254
3255(defmacro gnus-summary-article-intangible-p ()
3256 "Say whether this article is intangible or not."
3257 '(get-text-property (point) 'gnus-intangible))
3258
3259(defun gnus-article-read-p (article)
3260 "Say whether ARTICLE is read or not."
3261 (not (or (memq article gnus-newsgroup-marked)
23f87bed 3262 (memq article gnus-newsgroup-spam-marked)
eec82323
LMI
3263 (memq article gnus-newsgroup-unreads)
3264 (memq article gnus-newsgroup-unselected)
3265 (memq article gnus-newsgroup-dormant))))
3266
3267;; Some summary mode macros.
3268
3269(defmacro gnus-summary-article-number ()
3270 "The article number of the article on the current line.
8f688cb0 3271If there isn't an article number here, then we return the current
eec82323
LMI
3272article number."
3273 '(progn
3274 (gnus-summary-skip-intangible)
3275 (or (get-text-property (point) 'gnus-number)
3276 (gnus-summary-last-subject))))
3277
3278(defmacro gnus-summary-article-header (&optional number)
6748645f 3279 "Return the header of article NUMBER."
eec82323
LMI
3280 `(gnus-data-header (gnus-data-find
3281 ,(or number '(gnus-summary-article-number)))))
3282
3283(defmacro gnus-summary-thread-level (&optional number)
6748645f 3284 "Return the level of thread that starts with article NUMBER."
eec82323
LMI
3285 `(if (and (eq gnus-summary-make-false-root 'dummy)
3286 (get-text-property (point) 'gnus-intangible))
3287 0
3288 (gnus-data-level (gnus-data-find
3289 ,(or number '(gnus-summary-article-number))))))
3290
3291(defmacro gnus-summary-article-mark (&optional number)
6748645f 3292 "Return the mark of article NUMBER."
eec82323
LMI
3293 `(gnus-data-mark (gnus-data-find
3294 ,(or number '(gnus-summary-article-number)))))
3295
3296(defmacro gnus-summary-article-pos (&optional number)
6748645f 3297 "Return the position of the line of article NUMBER."
eec82323
LMI
3298 `(gnus-data-pos (gnus-data-find
3299 ,(or number '(gnus-summary-article-number)))))
3300
3301(defalias 'gnus-summary-subject-string 'gnus-summary-article-subject)
3302(defmacro gnus-summary-article-subject (&optional number)
3303 "Return current subject string or nil if nothing."
3304 `(let ((headers
3305 ,(if number
3306 `(gnus-data-header (assq ,number gnus-newsgroup-data))
3307 '(gnus-data-header (assq (gnus-summary-article-number)
3308 gnus-newsgroup-data)))))
3309 (and headers
3310 (vectorp headers)
3311 (mail-header-subject headers))))
3312
3313(defmacro gnus-summary-article-score (&optional number)
3314 "Return current article score."
3315 `(or (cdr (assq ,(or number '(gnus-summary-article-number))
3316 gnus-newsgroup-scored))
3317 gnus-summary-default-score 0))
3318
3319(defun gnus-summary-article-children (&optional number)
6748645f 3320 "Return a list of article numbers that are children of article NUMBER."
eec82323
LMI
3321 (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))))
3322 (level (gnus-data-level (car data)))
3323 l children)
3324 (while (and (setq data (cdr data))
3325 (> (setq l (gnus-data-level (car data))) level))
3326 (and (= (1+ level) l)
3327 (push (gnus-data-number (car data))
3328 children)))
3329 (nreverse children)))
3330
3331(defun gnus-summary-article-parent (&optional number)
6748645f 3332 "Return the article number of the parent of article NUMBER."
eec82323
LMI
3333 (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))
3334 (gnus-data-list t)))
3335 (level (gnus-data-level (car data))))
3336 (if (zerop level)
3337 () ; This is a root.
3338 ;; We search until we find an article with a level less than
3339 ;; this one. That function has to be the parent.
3340 (while (and (setq data (cdr data))
3341 (not (< (gnus-data-level (car data)) level))))
3342 (and data (gnus-data-number (car data))))))
3343
3344(defun gnus-unread-mark-p (mark)
3345 "Say whether MARK is the unread mark."
3346 (= mark gnus-unread-mark))
3347
3348(defun gnus-read-mark-p (mark)
3349 "Say whether MARK is one of the marks that mark as read.
3350This is all marks except unread, ticked, dormant, and expirable."
3351 (not (or (= mark gnus-unread-mark)
3352 (= mark gnus-ticked-mark)
23f87bed 3353 (= mark gnus-spam-mark)
eec82323
LMI
3354 (= mark gnus-dormant-mark)
3355 (= mark gnus-expirable-mark))))
3356
3357(defmacro gnus-article-mark (number)
6748645f
LMI
3358 "Return the MARK of article NUMBER.
3359This macro should only be used when computing the mark the \"first\"
3360time; i.e., when generating the summary lines. After that,
3361`gnus-summary-article-mark' should be used to examine the
3362marks of articles."
eec82323 3363 `(cond
6748645f 3364 ((memq ,number gnus-newsgroup-unsendable) gnus-unsendable-mark)
6748645f 3365 ((memq ,number gnus-newsgroup-downloadable) gnus-downloadable-mark)
eec82323
LMI
3366 ((memq ,number gnus-newsgroup-unreads) gnus-unread-mark)
3367 ((memq ,number gnus-newsgroup-marked) gnus-ticked-mark)
23f87bed 3368 ((memq ,number gnus-newsgroup-spam-marked) gnus-spam-mark)
eec82323
LMI
3369 ((memq ,number gnus-newsgroup-dormant) gnus-dormant-mark)
3370 ((memq ,number gnus-newsgroup-expirable) gnus-expirable-mark)
3371 (t (or (cdr (assq ,number gnus-newsgroup-reads))
3372 gnus-ancient-mark))))
3373
3374;; Saving hidden threads.
3375
eec82323
LMI
3376(defmacro gnus-save-hidden-threads (&rest forms)
3377 "Save hidden threads, eval FORMS, and restore the hidden threads."
3378 (let ((config (make-symbol "config")))
3379 `(let ((,config (gnus-hidden-threads-configuration)))
3380 (unwind-protect
3381 (save-excursion
3382 ,@forms)
3383 (gnus-restore-hidden-threads-configuration ,config)))))
23f87bed
MB
3384(put 'gnus-save-hidden-threads 'lisp-indent-function 0)
3385(put 'gnus-save-hidden-threads 'edebug-form-spec '(body))
eec82323 3386
107ecebb
AS
3387(defun gnus-data-compute-positions ()
3388 "Compute the positions of all articles."
3389 (setq gnus-newsgroup-data-reverse nil)
3390 (let ((data gnus-newsgroup-data))
3391 (save-excursion
3392 (gnus-save-hidden-threads
3393 (gnus-summary-show-all-threads)
3394 (goto-char (point-min))
3395 (while data
3396 (while (get-text-property (point) 'gnus-intangible)
3397 (forward-line 1))
3398 (gnus-data-set-pos (car data) (+ (point) 3))
3399 (setq data (cdr data))
3400 (forward-line 1))))))
3401
16409b0b
GM
3402(defun gnus-hidden-threads-configuration ()
3403 "Return the current hidden threads configuration."
3404 (save-excursion
3405 (let (config)
3406 (goto-char (point-min))
3407 (while (search-forward "\r" nil t)
3408 (push (1- (point)) config))
3409 config)))
3410
3411(defun gnus-restore-hidden-threads-configuration (config)
3412 "Restore hidden threads configuration from CONFIG."
3413 (save-excursion
c7a91ce1 3414 (let (point (inhibit-read-only t))
16409b0b
GM
3415 (while (setq point (pop config))
3416 (when (and (< point (point-max))
3417 (goto-char point)
3418 (eq (char-after) ?\n))
3419 (subst-char-in-region point (1+ point) ?\n ?\r))))))
3420
eec82323
LMI
3421;; Various summary mode internalish functions.
3422
3423(defun gnus-mouse-pick-article (e)
3424 (interactive "e")
3425 (mouse-set-point e)
3426 (gnus-summary-next-page nil t))
3427
3428(defun gnus-summary-set-display-table ()
16409b0b
GM
3429 "Change the display table.
3430Odd characters have a tendency to mess
3431up nicely formatted displays - we make all possible glyphs
3432display only a single character."
eec82323
LMI
3433
3434 ;; We start from the standard display table, if any.
3435 (let ((table (or (copy-sequence standard-display-table)
3436 (make-display-table)))
3437 (i 32))
3438 ;; Nix out all the control chars...
3439 (while (>= (setq i (1- i)) 0)
2b968687 3440 (gnus-put-display-table i [??] table))
23f87bed 3441 ;; ... but not newline and cr, of course. (cr is necessary for the
eec82323 3442 ;; selective display).
2b968687
MB
3443 (gnus-put-display-table ?\n nil table)
3444 (gnus-put-display-table ?\r nil table)
6748645f 3445 ;; We keep TAB as well.
2b968687 3446 (gnus-put-display-table ?\t nil table)
719120ef 3447 ;; We nix out any glyphs 127 through 255, or 127 through 159 in
fe62aacc 3448 ;; Emacs 23 (unicode), that are not set already.
719120ef
MB
3449 (let ((i (if (ignore-errors (= (make-char 'latin-iso8859-1 160) 160))
3450 160
3451 256)))
eec82323
LMI
3452 (while (>= (setq i (1- i)) 127)
3453 ;; Only modify if the entry is nil.
2b968687
MB
3454 (unless (gnus-get-display-table i table)
3455 (gnus-put-display-table i [??] table))))
eec82323
LMI
3456 (setq buffer-display-table table)))
3457
23f87bed
MB
3458(defun gnus-summary-set-article-display-arrow (pos)
3459 "Update the overlay arrow to point to line at position POS."
e3e955fe
MB
3460 (when gnus-summary-display-arrow
3461 (make-local-variable 'overlay-arrow-position)
3462 (make-local-variable 'overlay-arrow-string)
23f87bed
MB
3463 (save-excursion
3464 (goto-char pos)
3465 (beginning-of-line)
3466 (unless overlay-arrow-position
3467 (setq overlay-arrow-position (make-marker)))
3468 (setq overlay-arrow-string "=>"
3469 overlay-arrow-position (set-marker overlay-arrow-position
3470 (point)
3471 (current-buffer))))))
3472
eec82323
LMI
3473(defun gnus-summary-setup-buffer (group)
3474 "Initialize summary buffer."
23f87bed
MB
3475 (let ((buffer (gnus-summary-buffer-name group))
3476 (dead-name (concat "*Dead Summary "
3477 (gnus-group-decoded-name group) "*")))
3478 ;; If a dead summary buffer exists, we kill it.
3479 (when (gnus-buffer-live-p dead-name)
3480 (gnus-kill-buffer dead-name))
eec82323
LMI
3481 (if (get-buffer buffer)
3482 (progn
3483 (set-buffer buffer)
3484 (setq gnus-summary-buffer (current-buffer))
3485 (not gnus-newsgroup-prepared))
3486 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
6748645f 3487 (setq gnus-summary-buffer (set-buffer (gnus-get-buffer-create buffer)))
eec82323
LMI
3488 (gnus-summary-mode group)
3489 (when gnus-carpal
3490 (gnus-carpal-setup-buffer 'summary))
01c52d31
MB
3491 (when (gnus-group-quit-config group)
3492 (set (make-local-variable 'gnus-single-article-buffer) nil))
3493 (make-local-variable 'gnus-article-buffer)
3494 (make-local-variable 'gnus-article-current)
3495 (make-local-variable 'gnus-original-article-buffer)
eec82323 3496 (setq gnus-newsgroup-name group)
23f87bed
MB
3497 ;; Set any local variables in the group parameters.
3498 (gnus-summary-set-local-parameters gnus-newsgroup-name)
eec82323
LMI
3499 t)))
3500
3501(defun gnus-set-global-variables ()
16409b0b
GM
3502 "Set the global equivalents of the buffer-local variables.
3503They are set to the latest values they had. These reflect the summary
3504buffer that was in action when the last article was fetched."
eec82323
LMI
3505 (when (eq major-mode 'gnus-summary-mode)
3506 (setq gnus-summary-buffer (current-buffer))
3507 (let ((name gnus-newsgroup-name)
3508 (marked gnus-newsgroup-marked)
23f87bed 3509 (spam gnus-newsgroup-spam-marked)
eec82323
LMI
3510 (unread gnus-newsgroup-unreads)
3511 (headers gnus-current-headers)
3512 (data gnus-newsgroup-data)
3513 (summary gnus-summary-buffer)
3514 (article-buffer gnus-article-buffer)
3515 (original gnus-original-article-buffer)
3516 (gac gnus-article-current)
3517 (reffed gnus-reffed-article-number)
16409b0b 3518 (score-file gnus-current-score-file)
23f87bed
MB
3519 (default-charset gnus-newsgroup-charset)
3520 vlist)
3521 (let ((locals gnus-newsgroup-variables))
3522 (while locals
3523 (if (consp (car locals))
3524 (push (eval (caar locals)) vlist)
3525 (push (eval (car locals)) vlist))
3526 (setq locals (cdr locals)))
3527 (setq vlist (nreverse vlist)))
01c52d31 3528 (with-current-buffer gnus-group-buffer
6748645f
LMI
3529 (setq gnus-newsgroup-name name
3530 gnus-newsgroup-marked marked
23f87bed 3531 gnus-newsgroup-spam-marked spam
6748645f
LMI
3532 gnus-newsgroup-unreads unread
3533 gnus-current-headers headers
3534 gnus-newsgroup-data data
3535 gnus-article-current gac
3536 gnus-summary-buffer summary
3537 gnus-article-buffer article-buffer
3538 gnus-original-article-buffer original
3539 gnus-reffed-article-number reffed
16409b0b
GM
3540 gnus-current-score-file score-file
3541 gnus-newsgroup-charset default-charset)
23f87bed
MB
3542 (let ((locals gnus-newsgroup-variables))
3543 (while locals
3544 (if (consp (car locals))
3545 (set (caar locals) (pop vlist))
3546 (set (car locals) (pop vlist)))
3547 (setq locals (cdr locals))))
eec82323
LMI
3548 ;; The article buffer also has local variables.
3549 (when (gnus-buffer-live-p gnus-article-buffer)
3550 (set-buffer gnus-article-buffer)
3551 (setq gnus-summary-buffer summary))))))
3552
3553(defun gnus-summary-article-unread-p (article)
3554 "Say whether ARTICLE is unread or not."
3555 (memq article gnus-newsgroup-unreads))
3556
3557(defun gnus-summary-first-article-p (&optional article)
3558 "Return whether ARTICLE is the first article in the buffer."
3559 (if (not (setq article (or article (gnus-summary-article-number))))
3560 nil
3561 (eq article (caar gnus-newsgroup-data))))
3562
3563(defun gnus-summary-last-article-p (&optional article)
3564 "Return whether ARTICLE is the last article in the buffer."
3565 (if (not (setq article (or article (gnus-summary-article-number))))
16409b0b
GM
3566 ;; All non-existent numbers are the last article. :-)
3567 t
eec82323
LMI
3568 (not (cdr (gnus-data-find-list article)))))
3569
4921bbdd
CY
3570(defun gnus-make-thread-indent-array (&optional n)
3571 (when (or n
3572 (progn (setq n 200) nil)
3573 (null gnus-thread-indent-array)
3574 (/= gnus-thread-indent-level gnus-thread-indent-array-level))
3575 (setq gnus-thread-indent-array (make-vector (1+ n) "")
3576 gnus-thread-indent-array-level gnus-thread-indent-level)
3577 (while (>= n 0)
3578 (aset gnus-thread-indent-array n
6a30c01d 3579 (make-string (* n gnus-thread-indent-level) ? ))
4921bbdd 3580 (setq n (1- n)))))
eec82323
LMI
3581
3582(defun gnus-update-summary-mark-positions ()
3583 "Compute where the summary marks are to go."
3584 (save-excursion
6748645f 3585 (when (gnus-buffer-exists-p gnus-summary-buffer)
eec82323 3586 (set-buffer gnus-summary-buffer))
5153a47a
MB
3587 (let ((spec gnus-summary-line-format-spec)
3588 pos)
eec82323
LMI
3589 (save-excursion
3590 (gnus-set-work-buffer)
5153a47a
MB
3591 (let ((gnus-tmp-unread ?Z)
3592 (gnus-replied-mark ?Z)
3593 (gnus-score-below-mark ?Z)
3594 (gnus-score-over-mark ?Z)
3595 (gnus-undownloaded-mark ?Z)
3596 (gnus-summary-line-format-spec spec)
54506618 3597 (gnus-newsgroup-downloadable '(0))
5153a47a
MB
3598 (header [0 "" "" "05 Apr 2001 23:33:09 +0400" "" "" 0 0 "" nil])
3599 case-fold-search ignores)
3600 ;; Here, all marks are bound to Z.
3601 (gnus-summary-insert-line header
3602 0 nil t gnus-tmp-unread t nil "" nil 1)
3603 (goto-char (point-min))
3604 ;; Memorize the positions of the same characters as dummy marks.
3605 (while (re-search-forward "[A-D]" nil t)
3606 (push (point) ignores))
54506618 3607 (erase-buffer)
5153a47a
MB
3608 ;; We use A-D as dummy marks in order to know column positions
3609 ;; where marks should be inserted.
3610 (setq gnus-tmp-unread ?A
3611 gnus-replied-mark ?B
3612 gnus-score-below-mark ?C
3613 gnus-score-over-mark ?C
3614 gnus-undownloaded-mark ?D)
3615 (gnus-summary-insert-line header
3616 0 nil t gnus-tmp-unread t nil "" nil 1)
3617 ;; Ignore characters which aren't dummy marks.
3618 (dolist (p ignores)
3619 (delete-region (goto-char (1- p)) p)
3620 (insert ?Z))
eec82323 3621 (goto-char (point-min))
7c3bb5a5 3622 (setq pos (list (cons 'unread
5153a47a 3623 (and (search-forward "A" nil t)
7c3bb5a5 3624 (- (point) (point-min) 1)))))
eec82323 3625 (goto-char (point-min))
5153a47a 3626 (push (cons 'replied (and (search-forward "B" nil t)
667e0ba6 3627 (- (point) (point-min) 1)))
eec82323
LMI
3628 pos)
3629 (goto-char (point-min))
5153a47a 3630 (push (cons 'score (and (search-forward "C" nil t)
667e0ba6 3631 (- (point) (point-min) 1)))
6748645f
LMI
3632 pos)
3633 (goto-char (point-min))
5153a47a 3634 (push (cons 'download (and (search-forward "D" nil t)
7c3bb5a5 3635 (- (point) (point-min) 1)))
eec82323
LMI
3636 pos)))
3637 (setq gnus-summary-mark-positions pos))))
3638
3639(defun gnus-summary-insert-dummy-line (gnus-tmp-subject gnus-tmp-number)
3640 "Insert a dummy root in the summary buffer."
3641 (beginning-of-line)
3642 (gnus-add-text-properties
3643 (point) (progn (eval gnus-summary-dummy-line-format-spec) (point))
3644 (list 'gnus-number gnus-tmp-number 'gnus-intangible gnus-tmp-number)))
3645
23f87bed
MB
3646(defun gnus-summary-extract-address-component (from)
3647 (or (car (funcall gnus-extract-address-components from))
3648 from))
3649
3650(defun gnus-summary-from-or-to-or-newsgroups (header gnus-tmp-from)
3651 (let ((mail-parse-charset gnus-newsgroup-charset)
01c52d31 3652 (ignored-from-addresses (gnus-ignored-from-addresses))
23f87bed
MB
3653 ; Is it really necessary to do this next part for each summary line?
3654 ; Luckily, doesn't seem to slow things down much.
16409b0b 3655 (mail-parse-ignored-charsets
01c52d31
MB
3656 (with-current-buffer gnus-summary-buffer
3657 gnus-newsgroup-ignored-charsets)))
23f87bed 3658 (or
01c52d31
MB
3659 (and ignored-from-addresses
3660 (string-match ignored-from-addresses gnus-tmp-from)
23f87bed
MB
3661 (let ((extra-headers (mail-header-extra header))
3662 to
3663 newsgroups)
3664 (cond
3665 ((setq to (cdr (assq 'To extra-headers)))
01c52d31 3666 (concat gnus-summary-to-prefix
23f87bed
MB
3667 (inline
3668 (gnus-summary-extract-address-component
343d6628 3669 (funcall gnus-decode-encoded-address-function to)))))
01c52d31
MB
3670 ((setq newsgroups
3671 (or
3672 (cdr (assq 'Newsgroups extra-headers))
3673 (and
3674 (memq 'Newsgroups gnus-extra-headers)
3675 (eq (car (gnus-find-method-for-group
3676 gnus-newsgroup-name)) 'nntp)
3677 (gnus-group-real-name gnus-newsgroup-name))))
3678 (concat gnus-summary-newsgroup-prefix newsgroups)))))
23f87bed 3679 (inline (gnus-summary-extract-address-component gnus-tmp-from)))))
16409b0b 3680
eec82323
LMI
3681(defun gnus-summary-insert-line (gnus-tmp-header
3682 gnus-tmp-level gnus-tmp-current
23f87bed 3683 undownloaded gnus-tmp-unread gnus-tmp-replied
eec82323
LMI
3684 gnus-tmp-expirable gnus-tmp-subject-or-nil
3685 &optional gnus-tmp-dummy gnus-tmp-score
3686 gnus-tmp-process)
4921bbdd
CY
3687 (if (>= gnus-tmp-level (length gnus-thread-indent-array))
3688 (gnus-make-thread-indent-array (max (* 2 (length gnus-thread-indent-array))
3689 gnus-tmp-level)))
eec82323
LMI
3690 (let* ((gnus-tmp-indentation (aref gnus-thread-indent-array gnus-tmp-level))
3691 (gnus-tmp-lines (mail-header-lines gnus-tmp-header))
3692 (gnus-tmp-score (or gnus-tmp-score gnus-summary-default-score 0))
3693 (gnus-tmp-score-char
3694 (if (or (null gnus-summary-default-score)
3695 (<= (abs (- gnus-tmp-score gnus-summary-default-score))
3696 gnus-summary-zcore-fuzz))
23f87bed 3697 ? ;Whitespace
eec82323
LMI
3698 (if (< gnus-tmp-score gnus-summary-default-score)
3699 gnus-score-below-mark gnus-score-over-mark)))
23f87bed 3700 (gnus-tmp-number (mail-header-number gnus-tmp-header))
eec82323
LMI
3701 (gnus-tmp-replied
3702 (cond (gnus-tmp-process gnus-process-mark)
3703 ((memq gnus-tmp-current gnus-newsgroup-cached)
3704 gnus-cached-mark)
3705 (gnus-tmp-replied gnus-replied-mark)
23f87bed
MB
3706 ((memq gnus-tmp-current gnus-newsgroup-forwarded)
3707 gnus-forwarded-mark)
eec82323
LMI
3708 ((memq gnus-tmp-current gnus-newsgroup-saved)
3709 gnus-saved-mark)
23f87bed
MB
3710 ((memq gnus-tmp-number gnus-newsgroup-recent)
3711 gnus-recent-mark)
3712 ((memq gnus-tmp-number gnus-newsgroup-unseen)
3713 gnus-unseen-mark)
3714 (t gnus-no-mark)))
3715 (gnus-tmp-downloaded
3716 (cond (undownloaded
3717 gnus-undownloaded-mark)
3718 (gnus-newsgroup-agentized
3719 gnus-downloaded-mark)
3720 (t
3721 gnus-no-mark)))
eec82323
LMI
3722 (gnus-tmp-from (mail-header-from gnus-tmp-header))
3723 (gnus-tmp-name
3724 (cond
3725 ((string-match "<[^>]+> *$" gnus-tmp-from)
3726 (let ((beg (match-beginning 0)))
23f87bed
MB
3727 (or (and (string-match "^\".+\"" gnus-tmp-from)
3728 (substring gnus-tmp-from 1 (1- (match-end 0))))
eec82323
LMI
3729 (substring gnus-tmp-from 0 beg))))
3730 ((string-match "(.+)" gnus-tmp-from)
3731 (substring gnus-tmp-from
3732 (1+ (match-beginning 0)) (1- (match-end 0))))
3733 (t gnus-tmp-from)))
3734 (gnus-tmp-subject (mail-header-subject gnus-tmp-header))
eec82323
LMI
3735 (gnus-tmp-opening-bracket (if gnus-tmp-dummy ?\< ?\[))
3736 (gnus-tmp-closing-bracket (if gnus-tmp-dummy ?\> ?\]))
c7a91ce1 3737 (inhibit-read-only t))
eec82323
LMI
3738 (when (string= gnus-tmp-name "")
3739 (setq gnus-tmp-name gnus-tmp-from))
3740 (unless (numberp gnus-tmp-lines)
23f87bed
MB
3741 (setq gnus-tmp-lines -1))
3742 (if (= gnus-tmp-lines -1)
3743 (setq gnus-tmp-lines "?")
3744 (setq gnus-tmp-lines (number-to-string gnus-tmp-lines)))
64763fe3
MB
3745 (condition-case ()
3746 (gnus-put-text-property
3747 (point)
3748 (progn (eval gnus-summary-line-format-spec) (point))
3749 'gnus-number gnus-tmp-number)
3750 (error (gnus-message 5 "Error updating the summary line")))
eec82323
LMI
3751 (when (gnus-visual-p 'summary-highlight 'highlight)
3752 (forward-line -1)
6748645f 3753 (gnus-run-hooks 'gnus-summary-update-hook)
eec82323
LMI
3754 (forward-line 1))))
3755
3756(defun gnus-summary-update-line (&optional dont-update)
16409b0b 3757 "Update summary line after change."
eec82323
LMI
3758 (when (and gnus-summary-default-score
3759 (not gnus-summary-inhibit-highlight))
3760 (let* ((gnus-summary-inhibit-highlight t) ; Prevent recursion.
3761 (article (gnus-summary-article-number))
3762 (score (gnus-summary-article-score article)))
3763 (unless dont-update
3764 (if (and gnus-summary-mark-below
3765 (< (gnus-summary-article-score)
3766 gnus-summary-mark-below))
3767 ;; This article has a low score, so we mark it as read.
3768 (when (memq article gnus-newsgroup-unreads)
3769 (gnus-summary-mark-article-as-read gnus-low-score-mark))
3770 (when (eq (gnus-summary-article-mark) gnus-low-score-mark)
3771 ;; This article was previously marked as read on account
3772 ;; of a low score, but now it has risen, so we mark it as
3773 ;; unread.
3774 (gnus-summary-mark-article-as-unread gnus-unread-mark)))
3775 (gnus-summary-update-mark
3776 (if (or (null gnus-summary-default-score)
3777 (<= (abs (- score gnus-summary-default-score))
3778 gnus-summary-zcore-fuzz))
23f87bed 3779 ? ;Whitespace
eec82323
LMI
3780 (if (< score gnus-summary-default-score)
3781 gnus-score-below-mark gnus-score-over-mark))
3782 'score))
3783 ;; Do visual highlighting.
3784 (when (gnus-visual-p 'summary-highlight 'highlight)
6748645f 3785 (gnus-run-hooks 'gnus-summary-update-hook)))))
eec82323
LMI
3786
3787(defvar gnus-tmp-new-adopts nil)
3788
3789(defun gnus-summary-number-of-articles-in-thread (thread &optional level char)
3790 "Return the number of articles in THREAD.
3791This may be 0 in some cases -- if none of the articles in
3792the thread are to be displayed."
3793 (let* ((number
23f87bed 3794 ;; Fix by Luc Van Eycken <Luc.VanEycken@esat.kuleuven.ac.be>.
eec82323
LMI
3795 (cond
3796 ((not (listp thread))
3797 1)
3798 ((and (consp thread) (cdr thread))
3799 (apply
3800 '+ 1 (mapcar
3801 'gnus-summary-number-of-articles-in-thread (cdr thread))))
3802 ((null thread)
3803 1)
3804 ((memq (mail-header-number (car thread)) gnus-newsgroup-limit)
3805 1)
3806 (t 0))))
3807 (when (and level (zerop level) gnus-tmp-new-adopts)
3808 (incf number
3809 (apply '+ (mapcar
3810 'gnus-summary-number-of-articles-in-thread
3811 gnus-tmp-new-adopts))))
3812 (if char
3813 (if (> number 1) gnus-not-empty-thread-mark
3814 gnus-empty-thread-mark)
3815 number)))
3816
23f87bed
MB
3817(defsubst gnus-summary-line-message-size (head)
3818 "Return pretty-printed version of message size.
3819This function is intended to be used in
3820`gnus-summary-line-format-alist'."
3821 (let ((c (or (mail-header-chars head) -1)))
3822 (cond ((< c 0) "n/a") ; chars not available
3823 ((< c (* 1000 10)) (format "%1.1fk" (/ c 1024.0)))
3824 ((< c (* 1000 100)) (format "%dk" (/ c 1024.0)))
3825 ((< c (* 1000 10000)) (format "%1.1fM" (/ c (* 1024.0 1024))))
3826 (t (format "%dM" (/ c (* 1024.0 1024)))))))
3827
3828
eec82323
LMI
3829(defun gnus-summary-set-local-parameters (group)
3830 "Go through the local params of GROUP and set all variable specs in that list."
01c52d31
MB
3831 (let ((vars '(quit-config))) ; Ignore quit-config.
3832 (dolist (elem (gnus-group-find-parameter group))
eec82323
LMI
3833 (and (consp elem) ; Has to be a cons.
3834 (consp (cdr elem)) ; The cdr has to be a list.
3835 (symbolp (car elem)) ; Has to be a symbol in there.
23f87bed 3836 (not (memq (car elem) vars))
e3e955fe 3837 (ignore-errors
23f87bed 3838 (push (car elem) vars)
e3e955fe
MB
3839 ;; Variables like `gnus-show-threads' that are globally
3840 ;; bound, if used as group parameters, need to get to be
3841 ;; buffer-local, whereas just parameters like `gcc-self',
3842 ;; `timestamp', etc. should not be bound as variables.
3843 (if (boundp (car elem))
3844 (set (make-local-variable (car elem)) (eval (nth 1 elem)))
3845 (eval (nth 1 elem))))))))
eec82323
LMI
3846
3847(defun gnus-summary-read-group (group &optional show-all no-article
6748645f
LMI
3848 kill-buffer no-display backward
3849 select-articles)
eec82323
LMI
3850 "Start reading news in newsgroup GROUP.
3851If SHOW-ALL is non-nil, already read articles are also listed.
3852If NO-ARTICLE is non-nil, no article is selected initially.
3853If NO-DISPLAY, don't generate a summary buffer."
3854 (let (result)
3855 (while (and group
3856 (null (setq result
3857 (let ((gnus-auto-select-next nil))
6748645f
LMI
3858 (or (gnus-summary-read-group-1
3859 group show-all no-article
3860 kill-buffer no-display
3861 select-articles)
3862 (setq show-all nil
16409b0b 3863 select-articles nil)))))
eec82323
LMI
3864 (eq gnus-auto-select-next 'quietly))
3865 (set-buffer gnus-group-buffer)
6748645f
LMI
3866 ;; The entry function called above goes to the next
3867 ;; group automatically, so we go two groups back
3868 ;; if we are searching for the previous group.
3869 (when backward
3870 (gnus-group-prev-unread-group 2))
eec82323
LMI
3871 (if (not (equal group (gnus-group-group-name)))
3872 (setq group (gnus-group-group-name))
3873 (setq group nil)))
3874 result))
3875
3876(defun gnus-summary-read-group-1 (group show-all no-article
6748645f
LMI
3877 kill-buffer no-display
3878 &optional select-articles)
eec82323 3879 ;; Killed foreign groups can't be entered.
23f87bed
MB
3880 ;; (when (and (not (gnus-group-native-p group))
3881 ;; (not (gnus-gethash group gnus-newsrc-hashtb)))
3882 ;; (error "Dead non-native groups can't be entered"))
3883 (gnus-message 5 "Retrieving newsgroup: %s..."
3884 (gnus-group-decoded-name group))
eec82323
LMI
3885 (let* ((new-group (gnus-summary-setup-buffer group))
3886 (quit-config (gnus-group-quit-config group))
6748645f
LMI
3887 (did-select (and new-group (gnus-select-newsgroup
3888 group show-all select-articles))))
eec82323
LMI
3889 (cond
3890 ;; This summary buffer exists already, so we just select it.
3891 ((not new-group)
3892 (gnus-set-global-variables)
3893 (when kill-buffer
3894 (gnus-kill-or-deaden-summary kill-buffer))
3895 (gnus-configure-windows 'summary 'force)
3896 (gnus-set-mode-line 'summary)
3897 (gnus-summary-position-point)
3898 (message "")
3899 t)
3900 ;; We couldn't select this group.
3901 ((null did-select)
3902 (when (and (eq major-mode 'gnus-summary-mode)
3903 (not (equal (current-buffer) kill-buffer)))
3904 (kill-buffer (current-buffer))
3905 (if (not quit-config)
3906 (progn
6748645f
LMI
3907 ;; Update the info -- marks might need to be removed,
3908 ;; for instance.
3909 (gnus-summary-update-info)
eec82323
LMI
3910 (set-buffer gnus-group-buffer)
3911 (gnus-group-jump-to-group group)
3912 (gnus-group-next-unread-group 1))
3913 (gnus-handle-ephemeral-exit quit-config)))
23f87bed
MB
3914 (let ((grpinfo (gnus-get-info group)))
3915 (if (null (gnus-info-read grpinfo))
3916 (gnus-message 3 "Group %s contains no messages"
3917 (gnus-group-decoded-name group))
3918 (gnus-message 3 "Can't select group")))
eec82323
LMI
3919 nil)
3920 ;; The user did a `C-g' while prompting for number of articles,
3921 ;; so we exit this group.
3922 ((eq did-select 'quit)
3923 (and (eq major-mode 'gnus-summary-mode)
3924 (not (equal (current-buffer) kill-buffer))
3925 (kill-buffer (current-buffer)))
3926 (when kill-buffer
3927 (gnus-kill-or-deaden-summary kill-buffer))
3928 (if (not quit-config)
3929 (progn
3930 (set-buffer gnus-group-buffer)
3931 (gnus-group-jump-to-group group)
3932 (gnus-group-next-unread-group 1)
3933 (gnus-configure-windows 'group 'force))
3934 (gnus-handle-ephemeral-exit quit-config))
3935 ;; Finally signal the quit.
3936 (signal 'quit nil))
3937 ;; The group was successfully selected.
3938 (t
3939 (gnus-set-global-variables)
3940 ;; Save the active value in effect when the group was entered.
3941 (setq gnus-newsgroup-active
3942 (gnus-copy-sequence
3943 (gnus-active gnus-newsgroup-name)))
3944 ;; You can change the summary buffer in some way with this hook.
6748645f 3945 (gnus-run-hooks 'gnus-select-group-hook)
5153a47a
MB
3946 (when (memq 'summary (gnus-update-format-specifications
3947 nil 'summary 'summary-mode 'summary-dummy))
3948 ;; The format specification for the summary line was updated,
3949 ;; so we need to update the mark positions as well.
3950 (gnus-update-summary-mark-positions))
eec82323
LMI
3951 ;; Do score processing.
3952 (when gnus-use-scoring
3953 (gnus-possibly-score-headers))
3954 ;; Check whether to fill in the gaps in the threads.
3955 (when gnus-build-sparse-threads
3956 (gnus-build-sparse-threads))
3957 ;; Find the initial limit.
26c9afc3
MB
3958 (if show-all
3959 (let ((gnus-newsgroup-dormant nil))
eec82323 3960 (gnus-summary-initial-limit show-all))
26c9afc3 3961 (gnus-summary-initial-limit show-all))
eec82323
LMI
3962 ;; Generate the summary buffer.
3963 (unless no-display
3964 (gnus-summary-prepare))
3965 (when gnus-use-trees
3966 (gnus-tree-open group)
3967 (setq gnus-summary-highlight-line-function
3968 'gnus-tree-highlight-article))
3969 ;; If the summary buffer is empty, but there are some low-scored
3970 ;; articles or some excluded dormants, we include these in the
3971 ;; buffer.
3972 (when (and (zerop (buffer-size))
3973 (not no-display))
3974 (cond (gnus-newsgroup-dormant
3975 (gnus-summary-limit-include-dormant))
3976 ((and gnus-newsgroup-scored show-all)
3977 (gnus-summary-limit-include-expunged t))))
3978 ;; Function `gnus-apply-kill-file' must be called in this hook.
6748645f 3979 (gnus-run-hooks 'gnus-apply-kill-hook)
eec82323
LMI
3980 (if (and (zerop (buffer-size))
3981 (not no-display))
3982 (progn
3983 ;; This newsgroup is empty.
3984 (gnus-summary-catchup-and-exit nil t)
3985 (gnus-message 6 "No unread news")
3986 (when kill-buffer
3987 (gnus-kill-or-deaden-summary kill-buffer))
3988 ;; Return nil from this function.
3989 nil)
3990 ;; Hide conversation thread subtrees. We cannot do this in
3991 ;; gnus-summary-prepare-hook since kill processing may not
3992 ;; work with hidden articles.
23f87bed 3993 (gnus-summary-maybe-hide-threads)
6748645f
LMI
3994 (when kill-buffer
3995 (gnus-kill-or-deaden-summary kill-buffer))
23f87bed 3996 (gnus-summary-auto-select-subject)
eec82323
LMI
3997 ;; Show first unread article if requested.
3998 (if (and (not no-article)
3999 (not no-display)
4000 gnus-newsgroup-unreads
4001 gnus-auto-select-first)
16409b0b
GM
4002 (progn
4003 (gnus-configure-windows 'summary)
23f87bed
MB
4004 (let ((art (gnus-summary-article-number)))
4005 (unless (and (not gnus-plugged)
4006 (or (memq art gnus-newsgroup-undownloaded)
4007 (memq art gnus-newsgroup-downloadable)))
4008 (gnus-summary-goto-article art))))
4009 ;; Don't select any articles.
eec82323 4010 (gnus-summary-position-point)
6748645f
LMI
4011 (gnus-configure-windows 'summary 'force)
4012 (gnus-set-mode-line 'summary))
23f87bed
MB
4013 (when (and gnus-auto-center-group
4014 (get-buffer-window gnus-group-buffer t))
eec82323
LMI
4015 ;; Gotta use windows, because recenter does weird stuff if
4016 ;; the current buffer ain't the displayed window.
4017 (let ((owin (selected-window)))
4018 (select-window (get-buffer-window gnus-group-buffer t))
4019 (when (gnus-group-goto-group group)
4020 (recenter))
4021 (select-window owin)))
4022 ;; Mark this buffer as "prepared".
4023 (setq gnus-newsgroup-prepared t)
6748645f 4024 (gnus-run-hooks 'gnus-summary-prepared-hook)
23f87bed
MB
4025 (unless (gnus-ephemeral-group-p group)
4026 (gnus-group-update-group group))
eec82323
LMI
4027 t)))))
4028
23f87bed
MB
4029(defun gnus-summary-auto-select-subject ()
4030 "Select the subject line on initial group entry."
4031 (goto-char (point-min))
4032 (cond
4033 ((eq gnus-auto-select-subject 'best)
4034 (gnus-summary-best-unread-subject))
4035 ((eq gnus-auto-select-subject 'unread)
4036 (gnus-summary-first-unread-subject))
4037 ((eq gnus-auto-select-subject 'unseen)
4038 (gnus-summary-first-unseen-subject))
4039 ((eq gnus-auto-select-subject 'unseen-or-unread)
4040 (gnus-summary-first-unseen-or-unread-subject))
4041 ((eq gnus-auto-select-subject 'first)
4042 ;; Do nothing.
4043 )
4044 ((functionp gnus-auto-select-subject)
4045 (funcall gnus-auto-select-subject))))
4046
eec82323
LMI
4047(defun gnus-summary-prepare ()
4048 "Generate the summary buffer."
4049 (interactive)
c7a91ce1 4050 (let ((inhibit-read-only t))
eec82323
LMI
4051 (erase-buffer)
4052 (setq gnus-newsgroup-data nil
4053 gnus-newsgroup-data-reverse nil)
6748645f 4054 (gnus-run-hooks 'gnus-summary-generate-hook)
eec82323
LMI
4055 ;; Generate the buffer, either with threads or without.
4056 (when gnus-newsgroup-headers
4057 (gnus-summary-prepare-threads
4058 (if gnus-show-threads
4059 (gnus-sort-gathered-threads
4060 (funcall gnus-summary-thread-gathering-function
4061 (gnus-sort-threads
4062 (gnus-cut-threads (gnus-make-threads)))))
4063 ;; Unthreaded display.
4064 (gnus-sort-articles gnus-newsgroup-headers))))
4065 (setq gnus-newsgroup-data (nreverse gnus-newsgroup-data))
4066 ;; Call hooks for modifying summary buffer.
4067 (goto-char (point-min))
6748645f 4068 (gnus-run-hooks 'gnus-summary-prepare-hook)))
eec82323
LMI
4069
4070(defsubst gnus-general-simplify-subject (subject)
23f87bed 4071 "Simplify subject by the same rules as `gnus-gather-threads-by-subject'."
eec82323
LMI
4072 (setq subject
4073 (cond
4074 ;; Truncate the subject.
6748645f
LMI
4075 (gnus-simplify-subject-functions
4076 (gnus-map-function gnus-simplify-subject-functions subject))
eec82323
LMI
4077 ((numberp gnus-summary-gather-subject-limit)
4078 (setq subject (gnus-simplify-subject-re subject))
4079 (if (> (length subject) gnus-summary-gather-subject-limit)
4080 (substring subject 0 gnus-summary-gather-subject-limit)
4081 subject))
4082 ;; Fuzzily simplify it.
4083 ((eq 'fuzzy gnus-summary-gather-subject-limit)
4084 (gnus-simplify-subject-fuzzy subject))
4085 ;; Just remove the leading "Re:".
4086 (t
4087 (gnus-simplify-subject-re subject))))
4088
4089 (if (and gnus-summary-gather-exclude-subject
4090 (string-match gnus-summary-gather-exclude-subject subject))
23f87bed 4091 nil ; This article shouldn't be gathered
eec82323
LMI
4092 subject))
4093
4094(defun gnus-summary-simplify-subject-query ()
4095 "Query where the respool algorithm would put this article."
4096 (interactive)
eec82323 4097 (gnus-summary-select-article)
274f1353 4098 (message "%s" (gnus-general-simplify-subject (gnus-summary-article-subject))))
eec82323
LMI
4099
4100(defun gnus-gather-threads-by-subject (threads)
4101 "Gather threads by looking at Subject headers."
4102 (if (not gnus-summary-make-false-root)
4103 threads
4104 (let ((hashtb (gnus-make-hashtable 1024))
4105 (prev threads)
4106 (result threads)
4107 subject hthread whole-subject)
4108 (while threads
4109 (setq subject (gnus-general-simplify-subject
4110 (setq whole-subject (mail-header-subject
4111 (caar threads)))))
4112 (when subject
4113 (if (setq hthread (gnus-gethash subject hashtb))
4114 (progn
4115 ;; We enter a dummy root into the thread, if we
4116 ;; haven't done that already.
4117 (unless (stringp (caar hthread))
4118 (setcar hthread (list whole-subject (car hthread))))
4119 ;; We add this new gathered thread to this gathered
4120 ;; thread.
4121 (setcdr (car hthread)
4122 (nconc (cdar hthread) (list (car threads))))
4123 ;; Remove it from the list of threads.
4124 (setcdr prev (cdr threads))
4125 (setq threads prev))
4126 ;; Enter this thread into the hash table.
23f87bed
MB
4127 (gnus-sethash subject
4128 (if gnus-summary-make-false-root-always
4129 (progn
4130 ;; If you want a dummy root above all
4131 ;; threads...
4132 (setcar threads (list whole-subject
4133 (car threads)))
4134 threads)
4135 threads)
4136 hashtb)))
eec82323
LMI
4137 (setq prev threads)
4138 (setq threads (cdr threads)))
4139 result)))
4140
4141(defun gnus-gather-threads-by-references (threads)
4142 "Gather threads by looking at References headers."
4143 (let ((idhashtb (gnus-make-hashtable 1024))
4144 (thhashtb (gnus-make-hashtable 1024))
4145 (prev threads)
4146 (result threads)
4147 ids references id gthread gid entered ref)
4148 (while threads
4149 (when (setq references (mail-header-references (caar threads)))
4150 (setq id (mail-header-id (caar threads))
23f87bed 4151 ids (inline (gnus-split-references references))
eec82323
LMI
4152 entered nil)
4153 (while (setq ref (pop ids))
4154 (setq ids (delete ref ids))
4155 (if (not (setq gid (gnus-gethash ref idhashtb)))
4156 (progn
4157 (gnus-sethash ref id idhashtb)
4158 (gnus-sethash id threads thhashtb))
4159 (setq gthread (gnus-gethash gid thhashtb))
4160 (unless entered
4161 ;; We enter a dummy root into the thread, if we
4162 ;; haven't done that already.
4163 (unless (stringp (caar gthread))
4164 (setcar gthread (list (mail-header-subject (caar gthread))
4165 (car gthread))))
4166 ;; We add this new gathered thread to this gathered
4167 ;; thread.
4168 (setcdr (car gthread)
4169 (nconc (cdar gthread) (list (car threads)))))
4170 ;; Add it into the thread hash table.
4171 (gnus-sethash id gthread thhashtb)
4172 (setq entered t)
4173 ;; Remove it from the list of threads.
4174 (setcdr prev (cdr threads))
4175 (setq threads prev))))
4176 (setq prev threads)
4177 (setq threads (cdr threads)))
4178 result))
4179
4180(defun gnus-sort-gathered-threads (threads)
16409b0b 4181 "Sort subtreads inside each gathered thread by `gnus-sort-gathered-threads-function'."
eec82323
LMI
4182 (let ((result threads))
4183 (while threads
4184 (when (stringp (caar threads))
4185 (setcdr (car threads)
16409b0b 4186 (sort (cdar threads) gnus-sort-gathered-threads-function)))
eec82323
LMI
4187 (setq threads (cdr threads)))
4188 result))
4189
4190(defun gnus-thread-loop-p (root thread)
4191 "Say whether ROOT is in THREAD."
4192 (let ((stack (list thread))
4193 (infloop 0)
4194 th)
4195 (while (setq thread (pop stack))
4196 (setq th (cdr thread))
4197 (while (and th
4198 (not (eq (caar th) root)))
4199 (pop th))
4200 (if th
4201 ;; We have found a loop.
4202 (let (ref-dep)
4203 (setcdr thread (delq (car th) (cdr thread)))
4204 (if (boundp (setq ref-dep (intern "none"
4205 gnus-newsgroup-dependencies)))
4206 (setcdr (symbol-value ref-dep)
4207 (nconc (cdr (symbol-value ref-dep))
4208 (list (car th))))
4209 (set ref-dep (list nil (car th))))
4210 (setq infloop 1
4211 stack nil))
4212 ;; Push all the subthreads onto the stack.
4213 (push (cdr thread) stack)))
4214 infloop))
4215
4216(defun gnus-make-threads ()
01ccbb85 4217 "Go through the dependency hashtb and find the roots. Return all threads."
eec82323
LMI
4218 (let (threads)
4219 (while (catch 'infloop
4220 (mapatoms
4221 (lambda (refs)
4222 ;; Deal with self-referencing References loops.
4223 (when (and (car (symbol-value refs))
4224 (not (zerop
4225 (apply
4226 '+
4227 (mapcar
4228 (lambda (thread)
4229 (gnus-thread-loop-p
4230 (car (symbol-value refs)) thread))
4231 (cdr (symbol-value refs)))))))
4232 (setq threads nil)
4233 (throw 'infloop t))
4234 (unless (car (symbol-value refs))
23f87bed
MB
4235 ;; These threads do not refer back to any other
4236 ;; articles, so they're roots.
eec82323
LMI
4237 (setq threads (append (cdr (symbol-value refs)) threads))))
4238 gnus-newsgroup-dependencies)))
4239 threads))
4240
6748645f 4241;; Build the thread tree.
16409b0b 4242(defsubst gnus-dependencies-add-header (header dependencies force-new)
6748645f
LMI
4243 "Enter HEADER into the DEPENDENCIES table if it is not already there.
4244
4245If FORCE-NEW is not nil, enter HEADER into the DEPENDENCIES table even
4246if it was already present.
4247
4248If `gnus-summary-ignore-duplicates' is nil then duplicate Message-IDs
4249will not be entered in the DEPENDENCIES table. Otherwise duplicate
23f87bed
MB
4250Message-IDs will be renamed to a unique Message-ID before being
4251entered.
6748645f
LMI
4252
4253Returns HEADER if it was entered in the DEPENDENCIES. Returns nil otherwise."
4254 (let* ((id (mail-header-id header))
4255 (id-dep (and id (intern id dependencies)))
23f87bed 4256 parent-id ref ref-dep ref-header replaced)
6748645f
LMI
4257 ;; Enter this `header' in the `dependencies' table.
4258 (cond
4259 ((not id-dep)
4260 (setq header nil))
4261 ;; The first two cases do the normal part: enter a new `header'
4262 ;; in the `dependencies' table.
4263 ((not (boundp id-dep))
4264 (set id-dep (list header)))
4265 ((null (car (symbol-value id-dep)))
4266 (setcar (symbol-value id-dep) header))
4267
4268 ;; From here the `header' was already present in the
4269 ;; `dependencies' table.
4270 (force-new
4271 ;; Overrides an existing entry;
4272 ;; just set the header part of the entry.
23f87bed
MB
4273 (setcar (symbol-value id-dep) header)
4274 (setq replaced t))
6748645f
LMI
4275
4276 ;; Renames the existing `header' to a unique Message-ID.
4277 ((not gnus-summary-ignore-duplicates)
4278 ;; An article with this Message-ID has already been seen.
4279 ;; We rename the Message-ID.
4280 (set (setq id-dep (intern (setq id (nnmail-message-id)) dependencies))
4281 (list header))
4282 (mail-header-set-id header id))
4283
4284 ;; The last case ignores an existing entry, except it adds any
4285 ;; additional Xrefs (in case the two articles came from different
4286 ;; servers.
4287 ;; Also sets `header' to `nil' meaning that the `dependencies'
4288 ;; table was *not* modified.
4289 (t
4290 (mail-header-set-xref
4291 (car (symbol-value id-dep))
4292 (concat (or (mail-header-xref (car (symbol-value id-dep)))
4293 "")
4294 (or (mail-header-xref header) "")))
4295 (setq header nil)))
4296
23f87bed
MB
4297 (when (and header (not replaced))
4298 ;; First check that we are not creating a References loop.
4299 (setq parent-id (gnus-parent-id (mail-header-references header)))
4300 (setq ref parent-id)
6748645f
LMI
4301 (while (and ref
4302 (setq ref-dep (intern-soft ref dependencies))
4303 (boundp ref-dep)
4304 (setq ref-header (car (symbol-value ref-dep))))
4305 (if (string= id ref)
4306 ;; Yuk! This is a reference loop. Make the article be a
4307 ;; root article.
4308 (progn
4309 (mail-header-set-references (car (symbol-value id-dep)) "none")
23f87bed
MB
4310 (setq ref nil)
4311 (setq parent-id nil))
6748645f 4312 (setq ref (gnus-parent-id (mail-header-references ref-header)))))
23f87bed 4313 (setq ref-dep (intern (or parent-id "none") dependencies))
6748645f
LMI
4314 (if (boundp ref-dep)
4315 (setcdr (symbol-value ref-dep)
4316 (nconc (cdr (symbol-value ref-dep))
4317 (list (symbol-value id-dep))))
4318 (set ref-dep (list nil (symbol-value id-dep)))))
4319 header))
4320
23f87bed
MB
4321(defun gnus-extract-message-id-from-in-reply-to (string)
4322 (if (string-match "<[^>]+>" string)
4323 (substring string (match-beginning 0) (match-end 0))
4324 nil))
4325
eec82323
LMI
4326(defun gnus-build-sparse-threads ()
4327 (let ((headers gnus-newsgroup-headers)
16409b0b 4328 (mail-parse-charset gnus-newsgroup-charset)
6748645f 4329 (gnus-summary-ignore-duplicates t)
eec82323 4330 header references generation relations
6748645f 4331 subject child end new-child date)
eec82323
LMI
4332 ;; First we create an alist of generations/relations, where
4333 ;; generations is how much we trust the relation, and the relation
4334 ;; is parent/child.
4335 (gnus-message 7 "Making sparse threads...")
4336 (save-excursion
4337 (nnheader-set-temp-buffer " *gnus sparse threads*")
4338 (while (setq header (pop headers))
4339 (when (and (setq references (mail-header-references header))
4340 (not (string= references "")))
4341 (insert references)
4342 (setq child (mail-header-id header)
6748645f
LMI
4343 subject (mail-header-subject header)
4344 date (mail-header-date header)
4345 generation 0)
eec82323
LMI
4346 (while (search-backward ">" nil t)
4347 (setq end (1+ (point)))
4348 (when (search-backward "<" nil t)
6748645f 4349 (setq new-child (buffer-substring (point) end))
eec82323 4350 (push (list (incf generation)
6748645f
LMI
4351 child (setq child new-child)
4352 subject date)
eec82323 4353 relations)))
6748645f
LMI
4354 (when child
4355 (push (list (1+ generation) child nil subject) relations))
eec82323
LMI
4356 (erase-buffer)))
4357 (kill-buffer (current-buffer)))
4358 ;; Sort over trustworthiness.
01c52d31
MB
4359 (dolist (relation (sort relations 'car-less-than-car))
4360 (when (gnus-dependencies-add-header
4361 (make-full-mail-header
4362 gnus-reffed-article-number
4363 (nth 3 relation) "" (or (nth 4 relation) "")
4364 (nth 1 relation)
4365 (or (nth 2 relation) "") 0 0 "")
4366 gnus-newsgroup-dependencies nil)
4367 (push gnus-reffed-article-number gnus-newsgroup-limit)
4368 (push gnus-reffed-article-number gnus-newsgroup-sparse)
4369 (push (cons gnus-reffed-article-number gnus-sparse-mark)
4370 gnus-newsgroup-reads)
4371 (decf gnus-reffed-article-number)))
eec82323
LMI
4372 (gnus-message 7 "Making sparse threads...done")))
4373
4374(defun gnus-build-old-threads ()
4375 ;; Look at all the articles that refer back to old articles, and
4376 ;; fetch the headers for the articles that aren't there. This will
4377 ;; build complete threads - if the roots haven't been expired by the
4378 ;; server, that is.
16409b0b
GM
4379 (let ((mail-parse-charset gnus-newsgroup-charset)
4380 id heads)
eec82323
LMI
4381 (mapatoms
4382 (lambda (refs)
4383 (when (not (car (symbol-value refs)))
4384 (setq heads (cdr (symbol-value refs)))
4385 (while heads
4386 (if (memq (mail-header-number (caar heads))
4387 gnus-newsgroup-dormant)
4388 (setq heads (cdr heads))
4389 (setq id (symbol-name refs))
4390 (while (and (setq id (gnus-build-get-header id))
6748645f 4391 (not (car (gnus-id-to-thread id)))))
eec82323
LMI
4392 (setq heads nil)))))
4393 gnus-newsgroup-dependencies)))
4394
23f87bed
MB
4395(defsubst gnus-remove-odd-characters (string)
4396 "Translate STRING into something that doesn't contain weird characters."
4397 (mm-subst-char-in-string
4398 ?\r ?\-
01c52d31 4399 (mm-subst-char-in-string ?\n ?\- string t) t))
23f87bed 4400
6748645f
LMI
4401;; This function has to be called with point after the article number
4402;; on the beginning of the line.
4403(defsubst gnus-nov-parse-line (number dependencies &optional force-new)
01c52d31 4404 (let ((eol (point-at-eol))
6748645f 4405 (buffer (current-buffer))
23f87bed 4406 header references in-reply-to)
6748645f
LMI
4407
4408 ;; overview: [num subject from date id refs chars lines misc]
4409 (unwind-protect
23f87bed 4410 (let (x)
6748645f
LMI
4411 (narrow-to-region (point) eol)
4412 (unless (eobp)
4413 (forward-char))
4414
4415 (setq header
4416 (make-full-mail-header
4417 number ; number
23f87bed
MB
4418 (condition-case () ; subject
4419 (gnus-remove-odd-characters
4420 (funcall gnus-decode-encoded-word-function
4421 (setq x (nnheader-nov-field))))
4422 (error x))
4423 (condition-case () ; from
4424 (gnus-remove-odd-characters
343d6628 4425 (funcall gnus-decode-encoded-address-function
23f87bed
MB
4426 (setq x (nnheader-nov-field))))
4427 (error x))
16409b0b 4428 (nnheader-nov-field) ; date
01c52d31 4429 (nnheader-nov-read-message-id number) ; id
23f87bed 4430 (setq references (nnheader-nov-field)) ; refs
16409b0b
GM
4431 (nnheader-nov-read-integer) ; chars
4432 (nnheader-nov-read-integer) ; lines
4433 (unless (eobp)
8b93df01
DL
4434 (if (looking-at "Xref: ")
4435 (goto-char (match-end 0)))
4436 (nnheader-nov-field)) ; Xref
16409b0b 4437 (nnheader-nov-parse-extra)))) ; extra
6748645f
LMI
4438
4439 (widen))
4440
23f87bed
MB
4441 (when (and (string= references "")
4442 (setq in-reply-to (mail-header-extra header))
4443 (setq in-reply-to (cdr (assq 'In-Reply-To in-reply-to))))
4444 (mail-header-set-references
4445 header (gnus-extract-message-id-from-in-reply-to in-reply-to)))
4446
6748645f
LMI
4447 (when gnus-alter-header-function
4448 (funcall gnus-alter-header-function header))
4449 (gnus-dependencies-add-header header dependencies force-new)))
4450
eec82323 4451(defun gnus-build-get-header (id)
16409b0b
GM
4452 "Look through the buffer of NOV lines and find the header to ID.
4453Enter this line into the dependencies hash table, and return
4454the id of the parent article (if any)."
eec82323
LMI
4455 (let ((deps gnus-newsgroup-dependencies)
4456 found header)
4457 (prog1
c7a91ce1 4458 (with-current-buffer nntp-server-buffer
eec82323
LMI
4459 (let ((case-fold-search nil))
4460 (goto-char (point-min))
4461 (while (and (not found)
4462 (search-forward id nil t))
4463 (beginning-of-line)
4464 (setq found (looking-at
4465 (format "^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t%s"
4466 (regexp-quote id))))
4467 (or found (beginning-of-line 2)))
4468 (when found
4469 (beginning-of-line)
4470 (and
4471 (setq header (gnus-nov-parse-line
4472 (read (current-buffer)) deps))
4473 (gnus-parent-id (mail-header-references header))))))
4474 (when header
4475 (let ((number (mail-header-number header)))
4476 (push number gnus-newsgroup-limit)
4477 (push header gnus-newsgroup-headers)
4478 (if (memq number gnus-newsgroup-unselected)
4479 (progn
23f87bed
MB
4480 (setq gnus-newsgroup-unreads
4481 (gnus-add-to-sorted-list gnus-newsgroup-unreads
4482 number))
eec82323
LMI
4483 (setq gnus-newsgroup-unselected
4484 (delq number gnus-newsgroup-unselected)))
4485 (push number gnus-newsgroup-ancient)))))))
4486
6748645f
LMI
4487(defun gnus-build-all-threads ()
4488 "Read all the headers."
4489 (let ((gnus-summary-ignore-duplicates t)
16409b0b 4490 (mail-parse-charset gnus-newsgroup-charset)
6748645f
LMI
4491 (dependencies gnus-newsgroup-dependencies)
4492 header article)
c7a91ce1 4493 (with-current-buffer nntp-server-buffer
6748645f
LMI
4494 (let ((case-fold-search nil))
4495 (goto-char (point-min))
4496 (while (not (eobp))
4497 (ignore-errors
4498 (setq article (read (current-buffer))
16409b0b 4499 header (gnus-nov-parse-line article dependencies)))
6748645f 4500 (when header
01c52d31 4501 (with-current-buffer gnus-summary-buffer
6748645f
LMI
4502 (push header gnus-newsgroup-headers)
4503 (if (memq (setq article (mail-header-number header))
4504 gnus-newsgroup-unselected)
4505 (progn
23f87bed
MB
4506 (setq gnus-newsgroup-unreads
4507 (gnus-add-to-sorted-list
4508 gnus-newsgroup-unreads article))
6748645f
LMI
4509 (setq gnus-newsgroup-unselected
4510 (delq article gnus-newsgroup-unselected)))
4511 (push article gnus-newsgroup-ancient)))
4512 (forward-line 1)))))))
4513
eec82323 4514(defun gnus-summary-update-article-line (article header)
23f87bed 4515 "Update the line for ARTICLE using HEADER."
eec82323
LMI
4516 (let* ((id (mail-header-id header))
4517 (thread (gnus-id-to-thread id)))
4518 (unless thread
4519 (error "Article in no thread"))
4520 ;; Update the thread.
4521 (setcar thread header)
4522 (gnus-summary-goto-subject article)
4523 (let* ((datal (gnus-data-find-list article))
4524 (data (car datal))
c7a91ce1 4525 (inhibit-read-only t)
eec82323
LMI
4526 (level (gnus-summary-thread-level)))
4527 (gnus-delete-line)
23f87bed
MB
4528 (let ((inserted (- (point)
4529 (progn
4530 (gnus-summary-insert-line
4531 header level nil
4532 (memq article gnus-newsgroup-undownloaded)
4533 (gnus-article-mark article)
4534 (memq article gnus-newsgroup-replied)
4535 (memq article gnus-newsgroup-expirable)
4536 ;; Only insert the Subject string when it's different
4537 ;; from the previous Subject string.
4538 (if (and
4539 gnus-show-threads
4540 (gnus-subject-equal
4541 (condition-case ()
4542 (mail-header-subject
4543 (gnus-data-header
4544 (cadr
4545 (gnus-data-find-list
4546 article
4547 (gnus-data-list t)))))
4548 ;; Error on the side of excessive subjects.
4549 (error ""))
4550 (mail-header-subject header)))
4551 ""
4552 (mail-header-subject header))
4553 nil (cdr (assq article gnus-newsgroup-scored))
4554 (memq article gnus-newsgroup-processable))
4555 (point)))))
4556 (when (cdr datal)
4557 (gnus-data-update-list
4558 (cdr datal)
4559 (- (gnus-data-pos data) (gnus-data-pos (cadr datal)) inserted)))))))
eec82323
LMI
4560
4561(defun gnus-summary-update-article (article &optional iheader)
4562 "Update ARTICLE in the summary buffer."
4563 (set-buffer gnus-summary-buffer)
6748645f 4564 (let* ((header (gnus-summary-article-header article))
eec82323
LMI
4565 (id (mail-header-id header))
4566 (data (gnus-data-find article))
4567 (thread (gnus-id-to-thread id))
4568 (references (mail-header-references header))
4569 (parent
4570 (gnus-id-to-thread
4571 (or (gnus-parent-id
4572 (when (and references
4573 (not (equal "" references)))
4574 references))
4575 "none")))
c7a91ce1 4576 (inhibit-read-only t)
6748645f 4577 (old (car thread)))
eec82323 4578 (when thread
eec82323 4579 (unless iheader
6748645f
LMI
4580 (setcar thread nil)
4581 (when parent
4582 (delq thread parent)))
4583 (if (gnus-summary-insert-subject id header)
eec82323
LMI
4584 ;; Set the (possibly) new article number in the data structure.
4585 (gnus-data-set-number data (gnus-id-to-article id))
4586 (setcar thread old)
4587 nil))))
4588
6748645f
LMI
4589(defun gnus-rebuild-thread (id &optional line)
4590 "Rebuild the thread containing ID.
4591If LINE, insert the rebuilt thread starting on line LINE."
c7a91ce1 4592 (let ((inhibit-read-only t)
eec82323
LMI
4593 old-pos current thread data)
4594 (if (not gnus-show-threads)
4595 (setq thread (list (car (gnus-id-to-thread id))))
4596 ;; Get the thread this article is part of.
4597 (setq thread (gnus-remove-thread id)))
01c52d31 4598 (setq old-pos (point-at-bol))
eec82323 4599 (setq current (save-excursion
94384150 4600 (and (re-search-backward "[\r\n]" nil t)
eec82323
LMI
4601 (gnus-summary-article-number))))
4602 ;; If this is a gathered thread, we have to go some re-gathering.
4603 (when (stringp (car thread))
4604 (let ((subject (car thread))
4605 roots thr)
4606 (setq thread (cdr thread))
4607 (while thread
4608 (unless (memq (setq thr (gnus-id-to-thread
4609 (gnus-root-id
4610 (mail-header-id (caar thread)))))
4611 roots)
4612 (push thr roots))
4613 (setq thread (cdr thread)))
4614 ;; We now have all (unique) roots.
4615 (if (= (length roots) 1)
4616 ;; All the loose roots are now one solid root.
4617 (setq thread (car roots))
4618 (setq thread (cons subject (gnus-sort-threads roots))))))
4619 (let (threads)
4620 ;; We then insert this thread into the summary buffer.
6748645f
LMI
4621 (when line
4622 (goto-char (point-min))
4623 (forward-line (1- line)))
eec82323
LMI
4624 (let (gnus-newsgroup-data gnus-newsgroup-threads)
4625 (if gnus-show-threads
4626 (gnus-summary-prepare-threads (gnus-cut-threads (list thread)))
4627 (gnus-summary-prepare-unthreaded thread))
4628 (setq data (nreverse gnus-newsgroup-data))
4629 (setq threads gnus-newsgroup-threads))
4630 ;; We splice the new data into the data structure.
6748645f
LMI
4631 ;;!!! This is kinda bogus. We assume that in LINE is non-nil,
4632 ;;!!! then we want to insert at the beginning of the buffer.
4633 ;;!!! That happens to be true with Gnus now, but that may
4634 ;;!!! change in the future. Perhaps.
4635 (gnus-data-enter-list
4636 (if line nil current) data (- (point) old-pos))
4637 (setq gnus-newsgroup-threads
4638 (nconc threads gnus-newsgroup-threads))
4639 (gnus-data-compute-positions))))
eec82323
LMI
4640
4641(defun gnus-number-to-header (number)
4642 "Return the header for article NUMBER."
4643 (let ((headers gnus-newsgroup-headers))
4644 (while (and headers
4645 (not (= number (mail-header-number (car headers)))))
4646 (pop headers))
4647 (when headers
4648 (car headers))))
4649
6748645f 4650(defun gnus-parent-headers (in-headers &optional generation)
eec82323
LMI
4651 "Return the headers of the GENERATIONeth parent of HEADERS."
4652 (unless generation
4653 (setq generation 1))
a8151ef7 4654 (let ((parent t)
6748645f 4655 (headers in-headers)
a8151ef7 4656 references)
6748645f
LMI
4657 (while (and parent
4658 (not (zerop generation))
4659 (setq references (mail-header-references headers)))
4660 (setq headers (if (and references
4661 (setq parent (gnus-parent-id references)))
4662 (car (gnus-id-to-thread parent))
4663 nil))
4664 (decf generation))
4665 (and (not (eq headers in-headers))
4666 headers)))
eec82323
LMI
4667
4668(defun gnus-id-to-thread (id)
4669 "Return the (sub-)thread where ID appears."
4670 (gnus-gethash id gnus-newsgroup-dependencies))
4671
4672(defun gnus-id-to-article (id)
4673 "Return the article number of ID."
4674 (let ((thread (gnus-id-to-thread id)))
4675 (when (and thread
4676 (car thread))
4677 (mail-header-number (car thread)))))
4678
4679(defun gnus-id-to-header (id)
4680 "Return the article headers of ID."
4681 (car (gnus-id-to-thread id)))
4682
4683(defun gnus-article-displayed-root-p (article)
4684 "Say whether ARTICLE is a root(ish) article."
4685 (let ((level (gnus-summary-thread-level article))
4686 (refs (mail-header-references (gnus-summary-article-header article)))
4687 particle)
4688 (cond
4689 ((null level) nil)
4690 ((zerop level) t)
4691 ((null refs) t)
4692 ((null (gnus-parent-id refs)) t)
4693 ((and (= 1 level)
4694 (null (setq particle (gnus-id-to-article
4695 (gnus-parent-id refs))))
4696 (null (gnus-summary-thread-level particle)))))))
4697
4698(defun gnus-root-id (id)
4699 "Return the id of the root of the thread where ID appears."
4700 (let (last-id prev)
6748645f 4701 (while (and id (setq prev (car (gnus-id-to-thread id))))
eec82323
LMI
4702 (setq last-id id
4703 id (gnus-parent-id (mail-header-references prev))))
4704 last-id))
4705
6748645f
LMI
4706(defun gnus-articles-in-thread (thread)
4707 "Return the list of articles in THREAD."
4708 (cons (mail-header-number (car thread))
4709 (apply 'nconc (mapcar 'gnus-articles-in-thread (cdr thread)))))
4710
eec82323
LMI
4711(defun gnus-remove-thread (id &optional dont-remove)
4712 "Remove the thread that has ID in it."
6748645f 4713 (let (headers thread last-id)
eec82323 4714 ;; First go up in this thread until we find the root.
6748645f
LMI
4715 (setq last-id (gnus-root-id id)
4716 headers (message-flatten-list (gnus-id-to-thread last-id)))
01ccbb85 4717 ;; We have now found the real root of this thread. It might have
eec82323
LMI
4718 ;; been gathered into some loose thread, so we have to search
4719 ;; through the threads to find the thread we wanted.
4720 (let ((threads gnus-newsgroup-threads)
4721 sub)
4722 (while threads
4723 (setq sub (car threads))
4724 (if (stringp (car sub))
4725 ;; This is a gathered thread, so we look at the roots
4726 ;; below it to find whether this article is in this
4727 ;; gathered root.
4728 (progn
4729 (setq sub (cdr sub))
4730 (while sub
4731 (when (member (caar sub) headers)
4732 (setq thread (car threads)
4733 threads nil
4734 sub nil))
4735 (setq sub (cdr sub))))
4736 ;; It's an ordinary thread, so we check it.
4737 (when (eq (car sub) (car headers))
4738 (setq thread sub
4739 threads nil)))
4740 (setq threads (cdr threads)))
4741 ;; If this article is in no thread, then it's a root.
4742 (if thread
4743 (unless dont-remove
4744 (setq gnus-newsgroup-threads (delq thread gnus-newsgroup-threads)))
6748645f 4745 (setq thread (gnus-id-to-thread last-id)))
eec82323
LMI
4746 (when thread
4747 (prog1
4748 thread ; We return this thread.
4749 (unless dont-remove
4750 (if (stringp (car thread))
4751 (progn
4752 ;; If we use dummy roots, then we have to remove the
4753 ;; dummy root as well.
4754 (when (eq gnus-summary-make-false-root 'dummy)
6748645f
LMI
4755 ;; We go to the dummy root by going to
4756 ;; the first sub-"thread", and then one line up.
4757 (gnus-summary-goto-article
4758 (mail-header-number (caadr thread)))
4759 (forward-line -1)
eec82323
LMI
4760 (gnus-delete-line)
4761 (gnus-data-compute-positions))
4762 (setq thread (cdr thread))
4763 (while thread
4764 (gnus-remove-thread-1 (car thread))
4765 (setq thread (cdr thread))))
4766 (gnus-remove-thread-1 thread))))))))
4767
4768(defun gnus-remove-thread-1 (thread)
4769 "Remove the thread THREAD recursively."
4770 (let ((number (mail-header-number (pop thread)))
4771 d)
4772 (setq thread (reverse thread))
4773 (while thread
4774 (gnus-remove-thread-1 (pop thread)))
4775 (when (setq d (gnus-data-find number))
4776 (goto-char (gnus-data-pos d))
16409b0b 4777 (gnus-summary-show-thread)
eec82323
LMI
4778 (gnus-data-remove
4779 number
01c52d31 4780 (- (point-at-bol)
eec82323 4781 (prog1
01c52d31 4782 (1+ (point-at-eol))
eec82323
LMI
4783 (gnus-delete-line)))))))
4784
4921bbdd 4785(defun gnus-sort-threads-recursive (threads func)
16409b0b
GM
4786 (sort (mapcar (lambda (thread)
4787 (cons (car thread)
4788 (and (cdr thread)
4921bbdd 4789 (gnus-sort-threads-recursive (cdr thread) func))))
16409b0b
GM
4790 threads) func))
4791
4921bbdd
CY
4792(defun gnus-sort-threads-loop (threads func)
4793 (let* ((superthread (cons nil threads))
4794 (stack (list (cons superthread threads)))
4795 remaining-threads thread)
4796 (while stack
4797 (setq remaining-threads (cdr (car stack)))
4798 (if remaining-threads
4799 (progn (setq thread (car remaining-threads))
4800 (setcdr (car stack) (cdr remaining-threads))
4801 (if (cdr thread)
4802 (push (cons thread (cdr thread)) stack)))
4803 (setq thread (caar stack))
4804 (setcdr thread (sort (cdr thread) func))
4805 (pop stack)))
4806 (cdr superthread)))
4807
eec82323
LMI
4808(defun gnus-sort-threads (threads)
4809 "Sort THREADS."
4810 (if (not gnus-thread-sort-functions)
4811 threads
6748645f 4812 (gnus-message 8 "Sorting threads...")
4921bbdd
CY
4813 (prog1
4814 (condition-case nil
4815 (let ((max-lisp-eval-depth (max max-lisp-eval-depth 5000)))
4816 (gnus-sort-threads-recursive
4817 threads (gnus-make-sort-function gnus-thread-sort-functions)))
4818 ;; Even after binding max-lisp-eval-depth, the recursive
4819 ;; sorter might fail for very long threads. In that case,
4820 ;; try using a (less well-tested) non-recursive sorter.
4821 (error (gnus-sort-threads-loop
4822 threads (gnus-make-sort-function
4823 gnus-thread-sort-functions))))
4824 (gnus-message 8 "Sorting threads...done"))))
eec82323
LMI
4825
4826(defun gnus-sort-articles (articles)
4827 "Sort ARTICLES."
4828 (when gnus-article-sort-functions
4829 (gnus-message 7 "Sorting articles...")
4830 (prog1
4831 (setq gnus-newsgroup-headers
4832 (sort articles (gnus-make-sort-function
4833 gnus-article-sort-functions)))
4834 (gnus-message 7 "Sorting articles...done"))))
4835
4836;; Written by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
4837(defmacro gnus-thread-header (thread)
16409b0b
GM
4838 "Return header of first article in THREAD.
4839Note that THREAD must never, ever be anything else than a variable -
4840using some other form will lead to serious barfage."
eec82323
LMI
4841 (or (symbolp thread) (signal 'wrong-type-argument '(symbolp thread)))
4842 ;; (8% speedup to gnus-summary-prepare, just for fun :-)
16409b0b 4843 (list 'byte-code "\10\211:\203\17\0\211@;\203\16\0A@@\207"
eec82323
LMI
4844 (vector thread) 2))
4845
4846(defsubst gnus-article-sort-by-number (h1 h2)
4847 "Sort articles by article number."
4848 (< (mail-header-number h1)
4849 (mail-header-number h2)))
4850
4851(defun gnus-thread-sort-by-number (h1 h2)
4852 "Sort threads by root article number."
4853 (gnus-article-sort-by-number
4854 (gnus-thread-header h1) (gnus-thread-header h2)))
4855
23f87bed 4856(defsubst gnus-article-sort-by-random (h1 h2)
0b6799c3 4857 "Sort articles randomly."
23f87bed
MB
4858 (zerop (random 2)))
4859
4860(defun gnus-thread-sort-by-random (h1 h2)
0b6799c3 4861 "Sort threads randomly."
23f87bed
MB
4862 (gnus-article-sort-by-random
4863 (gnus-thread-header h1) (gnus-thread-header h2)))
4864
eec82323
LMI
4865(defsubst gnus-article-sort-by-lines (h1 h2)
4866 "Sort articles by article Lines header."
4867 (< (mail-header-lines h1)
4868 (mail-header-lines h2)))
4869
4870(defun gnus-thread-sort-by-lines (h1 h2)
4871 "Sort threads by root article Lines header."
4872 (gnus-article-sort-by-lines
4873 (gnus-thread-header h1) (gnus-thread-header h2)))
4874
16409b0b
GM
4875(defsubst gnus-article-sort-by-chars (h1 h2)
4876 "Sort articles by octet length."
4877 (< (mail-header-chars h1)
4878 (mail-header-chars h2)))
4879
4880(defun gnus-thread-sort-by-chars (h1 h2)
4881 "Sort threads by root article octet length."
4882 (gnus-article-sort-by-chars
4883 (gnus-thread-header h1) (gnus-thread-header h2)))
4884
eec82323
LMI
4885(defsubst gnus-article-sort-by-author (h1 h2)
4886 "Sort articles by root author."
b4fde39f 4887 (gnus-string<
eec82323
LMI
4888 (let ((extract (funcall
4889 gnus-extract-address-components
4890 (mail-header-from h1))))
4891 (or (car extract) (cadr extract) ""))
4892 (let ((extract (funcall
4893 gnus-extract-address-components
4894 (mail-header-from h2))))
4895 (or (car extract) (cadr extract) ""))))
4896
4897(defun gnus-thread-sort-by-author (h1 h2)
4898 "Sort threads by root author."
4899 (gnus-article-sort-by-author
4900 (gnus-thread-header h1) (gnus-thread-header h2)))
4901
01c52d31
MB
4902(defsubst gnus-article-sort-by-recipient (h1 h2)
4903 "Sort articles by recipient."
4904 (gnus-string<
4905 (let ((extract (funcall
4906 gnus-extract-address-components
4907 (or (cdr (assq 'To (mail-header-extra h1))) ""))))
4908 (or (car extract) (cadr extract)))
4909 (let ((extract (funcall
4910 gnus-extract-address-components
4911 (or (cdr (assq 'To (mail-header-extra h2))) ""))))
4912 (or (car extract) (cadr extract)))))
4913
4914(defun gnus-thread-sort-by-recipient (h1 h2)
4915 "Sort threads by root recipient."
4916 (gnus-article-sort-by-recipient
4917 (gnus-thread-header h1) (gnus-thread-header h2)))
4918
eec82323
LMI
4919(defsubst gnus-article-sort-by-subject (h1 h2)
4920 "Sort articles by root subject."
b4fde39f 4921 (gnus-string<
eec82323
LMI
4922 (downcase (gnus-simplify-subject-re (mail-header-subject h1)))
4923 (downcase (gnus-simplify-subject-re (mail-header-subject h2)))))
4924
4925(defun gnus-thread-sort-by-subject (h1 h2)
4926 "Sort threads by root subject."
4927 (gnus-article-sort-by-subject
4928 (gnus-thread-header h1) (gnus-thread-header h2)))
4929
4930(defsubst gnus-article-sort-by-date (h1 h2)
4931 "Sort articles by root article date."
16409b0b 4932 (time-less-p
eec82323
LMI
4933 (gnus-date-get-time (mail-header-date h1))
4934 (gnus-date-get-time (mail-header-date h2))))
4935
4936(defun gnus-thread-sort-by-date (h1 h2)
4937 "Sort threads by root article date."
4938 (gnus-article-sort-by-date
4939 (gnus-thread-header h1) (gnus-thread-header h2)))
4940
4941(defsubst gnus-article-sort-by-score (h1 h2)
4942 "Sort articles by root article score.
4943Unscored articles will be counted as having a score of zero."
4944 (> (or (cdr (assq (mail-header-number h1)
4945 gnus-newsgroup-scored))
4946 gnus-summary-default-score 0)
4947 (or (cdr (assq (mail-header-number h2)
4948 gnus-newsgroup-scored))
4949 gnus-summary-default-score 0)))
4950
4951(defun gnus-thread-sort-by-score (h1 h2)
4952 "Sort threads by root article score."
4953 (gnus-article-sort-by-score
4954 (gnus-thread-header h1) (gnus-thread-header h2)))
4955
4956(defun gnus-thread-sort-by-total-score (h1 h2)
4957 "Sort threads by the sum of all scores in the thread.
4958Unscored articles will be counted as having a score of zero."
4959 (> (gnus-thread-total-score h1) (gnus-thread-total-score h2)))
4960
4961(defun gnus-thread-total-score (thread)
16409b0b 4962 ;; This function find the total score of THREAD.
23f87bed
MB
4963 (cond
4964 ((null thread)
4965 0)
4966 ((consp thread)
4967 (if (stringp (car thread))
4968 (apply gnus-thread-score-function 0
4969 (mapcar 'gnus-thread-total-score-1 (cdr thread)))
4970 (gnus-thread-total-score-1 thread)))
4971 (t
4972 (gnus-thread-total-score-1 (list thread)))))
4973
4974(defun gnus-thread-sort-by-most-recent-number (h1 h2)
4975 "Sort threads such that the thread with the most recently arrived article comes first."
4976 (> (gnus-thread-highest-number h1) (gnus-thread-highest-number h2)))
4977
4978(defun gnus-thread-highest-number (thread)
4979 "Return the highest article number in THREAD."
4980 (apply 'max (mapcar (lambda (header)
4981 (mail-header-number header))
4982 (message-flatten-list thread))))
4983
4984(defun gnus-thread-sort-by-most-recent-date (h1 h2)
4985 "Sort threads such that the thread with the most recently dated article comes first."
4986 (> (gnus-thread-latest-date h1) (gnus-thread-latest-date h2)))
4987
4988(defun gnus-thread-latest-date (thread)
4989 "Return the highest article date in THREAD."
4990 (let ((previous-time 0))
4991 (apply 'max
4992 (mapcar
4993 (lambda (header)
4994 (setq previous-time
4995 (condition-case ()
9693d3c4 4996 (gnus-float-time (mail-header-parse-date
23f87bed
MB
4997 (mail-header-date header)))
4998 (error previous-time))))
4999 (sort
5000 (message-flatten-list thread)
5001 (lambda (h1 h2)
5002 (< (mail-header-number h1)
5003 (mail-header-number h2))))))))
eec82323
LMI
5004
5005(defun gnus-thread-total-score-1 (root)
5006 ;; This function find the total score of the thread below ROOT.
5007 (setq root (car root))
5008 (apply gnus-thread-score-function
5009 (or (append
5010 (mapcar 'gnus-thread-total-score
6748645f 5011 (cdr (gnus-id-to-thread (mail-header-id root))))
eec82323
LMI
5012 (when (> (mail-header-number root) 0)
5013 (list (or (cdr (assq (mail-header-number root)
5014 gnus-newsgroup-scored))
5015 gnus-summary-default-score 0))))
5016 (list gnus-summary-default-score)
5017 '(0))))
5018
5019;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
5020(defvar gnus-tmp-prev-subject nil)
5021(defvar gnus-tmp-false-parent nil)
5022(defvar gnus-tmp-root-expunged nil)
5023(defvar gnus-tmp-dummy-line nil)
5024
16409b0b
GM
5025(defun gnus-extra-header (type &optional header)
5026 "Return the extra header of TYPE."
5027 (or (cdr (assq type (mail-header-extra (or header gnus-tmp-header))))
5028 ""))
5029
23f87bed
MB
5030(defvar gnus-tmp-thread-tree-header-string "")
5031
5032(defcustom gnus-sum-thread-tree-root "> "
5033 "With %B spec, used for the root of a thread.
5034If nil, use subject instead."
bf247b6e 5035 :version "22.1"
ad136a7c 5036 :type '(radio (const :format "%v " nil) string)
23f87bed 5037 :group 'gnus-thread)
01c52d31 5038
23f87bed
MB
5039(defcustom gnus-sum-thread-tree-false-root "> "
5040 "With %B spec, used for a false root of a thread.
5041If nil, use subject instead."
bf247b6e 5042 :version "22.1"
ad136a7c 5043 :type '(radio (const :format "%v " nil) string)
23f87bed 5044 :group 'gnus-thread)
01c52d31 5045
23f87bed
MB
5046(defcustom gnus-sum-thread-tree-single-indent ""
5047 "With %B spec, used for a thread with just one message.
5048If nil, use subject instead."
bf247b6e 5049 :version "22.1"
ad136a7c 5050 :type '(radio (const :format "%v " nil) string)
23f87bed 5051 :group 'gnus-thread)
01c52d31 5052
23f87bed
MB
5053(defcustom gnus-sum-thread-tree-vertical "| "
5054 "With %B spec, used for drawing a vertical line."
bf247b6e 5055 :version "22.1"
23f87bed
MB
5056 :type 'string
5057 :group 'gnus-thread)
01c52d31 5058
23f87bed
MB
5059(defcustom gnus-sum-thread-tree-indent " "
5060 "With %B spec, used for indenting."
bf247b6e 5061 :version "22.1"
23f87bed
MB
5062 :type 'string
5063 :group 'gnus-thread)
01c52d31 5064
23f87bed
MB
5065(defcustom gnus-sum-thread-tree-leaf-with-other "+-> "
5066 "With %B spec, used for a leaf with brothers."
bf247b6e 5067 :version "22.1"
23f87bed
MB
5068 :type 'string
5069 :group 'gnus-thread)
01c52d31 5070
23f87bed
MB
5071(defcustom gnus-sum-thread-tree-single-leaf "\\-> "
5072 "With %B spec, used for a leaf without brothers."
bf247b6e 5073 :version "22.1"
23f87bed
MB
5074 :type 'string
5075 :group 'gnus-thread)
5076
1fc34624
GM
5077(defcustom gnus-summary-display-while-building nil
5078 "If non-nil, show and update the summary buffer as it's being built.
5079If the value is t, update the buffer after every line is inserted. If
5080the value is an integer (N), update the display every N lines."
5081 :version "22.1"
5082 :group 'gnus-thread
5083 :type '(choice (const :tag "off" nil)
5084 number
5085 (const :tag "frequently" t)))
5086
eec82323
LMI
5087(defun gnus-summary-prepare-threads (threads)
5088 "Prepare summary buffer from THREADS and indentation LEVEL.
5089THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])'
5090or a straight list of headers."
5091 (gnus-message 7 "Generating summary...")
5092
5093 (setq gnus-newsgroup-threads threads)
5094 (beginning-of-line)
5095
5096 (let ((gnus-tmp-level 0)
5097 (default-score (or gnus-summary-default-score 0))
5098 (gnus-visual-p (gnus-visual-p 'summary-highlight 'highlight))
23f87bed
MB
5099 (building-line-count gnus-summary-display-while-building)
5100 (building-count (integerp gnus-summary-display-while-building))
eec82323 5101 thread number subject stack state gnus-tmp-gathered beg-match
23f87bed
MB
5102 new-roots gnus-tmp-new-adopts thread-end simp-subject
5103 gnus-tmp-header gnus-tmp-unread gnus-tmp-downloaded
eec82323
LMI
5104 gnus-tmp-replied gnus-tmp-subject-or-nil
5105 gnus-tmp-dummy gnus-tmp-indentation gnus-tmp-lines gnus-tmp-score
5106 gnus-tmp-score-char gnus-tmp-from gnus-tmp-name
23f87bed
MB
5107 gnus-tmp-number gnus-tmp-opening-bracket gnus-tmp-closing-bracket
5108 tree-stack)
eec82323 5109
23f87bed
MB
5110 (setq gnus-tmp-prev-subject nil
5111 gnus-tmp-thread-tree-header-string "")
eec82323
LMI
5112
5113 (if (vectorp (car threads))
5114 ;; If this is a straight (sic) list of headers, then a
5115 ;; threaded summary display isn't required, so we just create
5116 ;; an unthreaded one.
5117 (gnus-summary-prepare-unthreaded threads)
5118
5119 ;; Do the threaded display.
5120
23f87bed
MB
5121 (if gnus-summary-display-while-building
5122 (switch-to-buffer (buffer-name)))
eec82323
LMI
5123 (while (or threads stack gnus-tmp-new-adopts new-roots)
5124
5125 (if (and (= gnus-tmp-level 0)
eec82323
LMI
5126 (or (not stack)
5127 (= (caar stack) 0))
5128 (not gnus-tmp-false-parent)
5129 (or gnus-tmp-new-adopts new-roots))
5130 (if gnus-tmp-new-adopts
5131 (setq gnus-tmp-level (if gnus-tmp-root-expunged 0 1)
5132 thread (list (car gnus-tmp-new-adopts))
5133 gnus-tmp-header (caar thread)
5134 gnus-tmp-new-adopts (cdr gnus-tmp-new-adopts))
5135 (when new-roots
5136 (setq thread (list (car new-roots))
5137 gnus-tmp-header (caar thread)
5138 new-roots (cdr new-roots))))
5139
5140 (if threads
5141 ;; If there are some threads, we do them before the
5142 ;; threads on the stack.
5143 (setq thread threads
5144 gnus-tmp-header (caar thread))
5145 ;; There were no current threads, so we pop something off
5146 ;; the stack.
5147 (setq state (car stack)
5148 gnus-tmp-level (car state)
23f87bed
MB
5149 tree-stack (cadr state)
5150 thread (caddr state)
eec82323
LMI
5151 stack (cdr stack)
5152 gnus-tmp-header (caar thread))))
5153
5154 (setq gnus-tmp-false-parent nil)
5155 (setq gnus-tmp-root-expunged nil)
5156 (setq thread-end nil)
5157
5158 (if (stringp gnus-tmp-header)
5159 ;; The header is a dummy root.
5160 (cond
5161 ((eq gnus-summary-make-false-root 'adopt)
5162 ;; We let the first article adopt the rest.
5163 (setq gnus-tmp-new-adopts (nconc gnus-tmp-new-adopts
5164 (cddar thread)))
5165 (setq gnus-tmp-gathered
5166 (nconc (mapcar
5167 (lambda (h) (mail-header-number (car h)))
5168 (cddar thread))
5169 gnus-tmp-gathered))
5170 (setq thread (cons (list (caar thread)
5171 (cadar thread))
5172 (cdr thread)))
5173 (setq gnus-tmp-level -1
5174 gnus-tmp-false-parent t))
5175 ((eq gnus-summary-make-false-root 'empty)
5176 ;; We print adopted articles with empty subject fields.
5177 (setq gnus-tmp-gathered
5178 (nconc (mapcar
5179 (lambda (h) (mail-header-number (car h)))
5180 (cddar thread))
5181 gnus-tmp-gathered))
5182 (setq gnus-tmp-level -1))
5183 ((eq gnus-summary-make-false-root 'dummy)
5184 ;; We remember that we probably want to output a dummy
5185 ;; root.
5186 (setq gnus-tmp-dummy-line gnus-tmp-header)
5187 (setq gnus-tmp-prev-subject gnus-tmp-header))
5188 (t
5189 ;; We do not make a root for the gathered
5190 ;; sub-threads at all.
5191 (setq gnus-tmp-level -1)))
5192
5193 (setq number (mail-header-number gnus-tmp-header)
23f87bed
MB
5194 subject (mail-header-subject gnus-tmp-header)
5195 simp-subject (gnus-simplify-subject-fully subject))
eec82323
LMI
5196
5197 (cond
5198 ;; If the thread has changed subject, we might want to make
5199 ;; this subthread into a root.
5200 ((and (null gnus-thread-ignore-subject)
5201 (not (zerop gnus-tmp-level))
5202 gnus-tmp-prev-subject
23f87bed 5203 (not (string= gnus-tmp-prev-subject simp-subject)))
eec82323
LMI
5204 (setq new-roots (nconc new-roots (list (car thread)))
5205 thread-end t
5206 gnus-tmp-header nil))
5207 ;; If the article lies outside the current limit,
5208 ;; then we do not display it.
5209 ((not (memq number gnus-newsgroup-limit))
5210 (setq gnus-tmp-gathered
5211 (nconc (mapcar
5212 (lambda (h) (mail-header-number (car h)))
5213 (cdar thread))
5214 gnus-tmp-gathered))
5215 (setq gnus-tmp-new-adopts (if (cdar thread)
5216 (append gnus-tmp-new-adopts
5217 (cdar thread))
5218 gnus-tmp-new-adopts)
5219 thread-end t
5220 gnus-tmp-header nil)
5221 (when (zerop gnus-tmp-level)
5222 (setq gnus-tmp-root-expunged t)))
5223 ;; Perhaps this article is to be marked as read?
5224 ((and gnus-summary-mark-below
5225 (< (or (cdr (assq number gnus-newsgroup-scored))
5226 default-score)
5227 gnus-summary-mark-below)
5228 ;; Don't touch sparse articles.
5229 (not (gnus-summary-article-sparse-p number))
5230 (not (gnus-summary-article-ancient-p number)))
5231 (setq gnus-newsgroup-unreads
5232 (delq number gnus-newsgroup-unreads))
5233 (if gnus-newsgroup-auto-expire
23f87bed
MB
5234 (setq gnus-newsgroup-expirable
5235 (gnus-add-to-sorted-list
5236 gnus-newsgroup-expirable number))
eec82323
LMI
5237 (push (cons number gnus-low-score-mark)
5238 gnus-newsgroup-reads))))
5239
5240 (when gnus-tmp-header
5241 ;; We may have an old dummy line to output before this
5242 ;; article.
6748645f
LMI
5243 (when (and gnus-tmp-dummy-line
5244 (gnus-subject-equal
5245 gnus-tmp-dummy-line
5246 (mail-header-subject gnus-tmp-header)))
eec82323
LMI
5247 (gnus-summary-insert-dummy-line
5248 gnus-tmp-dummy-line (mail-header-number gnus-tmp-header))
5249 (setq gnus-tmp-dummy-line nil))
5250
5251 ;; Compute the mark.
5252 (setq gnus-tmp-unread (gnus-article-mark number))
5253
5254 (push (gnus-data-make number gnus-tmp-unread (1+ (point))
5255 gnus-tmp-header gnus-tmp-level)
5256 gnus-newsgroup-data)
5257
5258 ;; Actually insert the line.
5259 (setq
5260 gnus-tmp-subject-or-nil
5261 (cond
5262 ((and gnus-thread-ignore-subject
5263 gnus-tmp-prev-subject
23f87bed 5264 (not (string= gnus-tmp-prev-subject simp-subject)))
eec82323
LMI
5265 subject)
5266 ((zerop gnus-tmp-level)
5267 (if (and (eq gnus-summary-make-false-root 'empty)
5268 (memq number gnus-tmp-gathered)
5269 gnus-tmp-prev-subject
23f87bed 5270 (string= gnus-tmp-prev-subject simp-subject))
eec82323
LMI
5271 gnus-summary-same-subject
5272 subject))
5273 (t gnus-summary-same-subject)))
5274 (if (and (eq gnus-summary-make-false-root 'adopt)
5275 (= gnus-tmp-level 1)
5276 (memq number gnus-tmp-gathered))
5277 (setq gnus-tmp-opening-bracket ?\<
5278 gnus-tmp-closing-bracket ?\>)
5279 (setq gnus-tmp-opening-bracket ?\[
5280 gnus-tmp-closing-bracket ?\]))
4921bbdd
CY
5281 (if (>= gnus-tmp-level (length gnus-thread-indent-array))
5282 (gnus-make-thread-indent-array
5283 (max (* 2 (length gnus-thread-indent-array))
5284 gnus-tmp-level)))
eec82323
LMI
5285 (setq
5286 gnus-tmp-indentation
5287 (aref gnus-thread-indent-array gnus-tmp-level)
5288 gnus-tmp-lines (mail-header-lines gnus-tmp-header)
5289 gnus-tmp-score (or (cdr (assq number gnus-newsgroup-scored))
5290 gnus-summary-default-score 0)
5291 gnus-tmp-score-char
5292 (if (or (null gnus-summary-default-score)
5293 (<= (abs (- gnus-tmp-score gnus-summary-default-score))
5294 gnus-summary-zcore-fuzz))
23f87bed 5295 ? ;Whitespace
eec82323
LMI
5296 (if (< gnus-tmp-score gnus-summary-default-score)
5297 gnus-score-below-mark gnus-score-over-mark))
5298 gnus-tmp-replied
5299 (cond ((memq number gnus-newsgroup-processable)
5300 gnus-process-mark)
5301 ((memq number gnus-newsgroup-cached)
5302 gnus-cached-mark)
5303 ((memq number gnus-newsgroup-replied)
5304 gnus-replied-mark)
23f87bed
MB
5305 ((memq number gnus-newsgroup-forwarded)
5306 gnus-forwarded-mark)
eec82323
LMI
5307 ((memq number gnus-newsgroup-saved)
5308 gnus-saved-mark)
23f87bed
MB
5309 ((memq number gnus-newsgroup-recent)
5310 gnus-recent-mark)
5311 ((memq number gnus-newsgroup-unseen)
5312 gnus-unseen-mark)
5313 (t gnus-no-mark))
5314 gnus-tmp-downloaded
5315 (cond ((memq number gnus-newsgroup-undownloaded)
5316 gnus-undownloaded-mark)
5317 (gnus-newsgroup-agentized
5318 gnus-downloaded-mark)
5319 (t
5320 gnus-no-mark))
eec82323
LMI
5321 gnus-tmp-from (mail-header-from gnus-tmp-header)
5322 gnus-tmp-name
5323 (cond
5324 ((string-match "<[^>]+> *$" gnus-tmp-from)
5325 (setq beg-match (match-beginning 0))
23f87bed
MB
5326 (or (and (string-match "^\".+\"" gnus-tmp-from)
5327 (substring gnus-tmp-from 1 (1- (match-end 0))))
eec82323
LMI
5328 (substring gnus-tmp-from 0 beg-match)))
5329 ((string-match "(.+)" gnus-tmp-from)
5330 (substring gnus-tmp-from
5331 (1+ (match-beginning 0)) (1- (match-end 0))))
23f87bed
MB
5332 (t gnus-tmp-from))
5333
5334 ;; Do the %B string
5335 gnus-tmp-thread-tree-header-string
5336 (cond
5337 ((not gnus-show-threads) "")
5338 ((zerop gnus-tmp-level)
5339 (cond ((cdar thread)
5340 (or gnus-sum-thread-tree-root subject))
5341 (gnus-tmp-new-adopts
5342 (or gnus-sum-thread-tree-false-root subject))
5343 (t
5344 (or gnus-sum-thread-tree-single-indent subject))))
5345 (t
5346 (concat (apply 'concat
5347 (mapcar (lambda (item)
5348 (if (= item 1)
5349 gnus-sum-thread-tree-vertical
5350 gnus-sum-thread-tree-indent))
5351 (cdr (reverse tree-stack))))
5352 (if (nth 1 thread)
5353 gnus-sum-thread-tree-leaf-with-other
5354 gnus-sum-thread-tree-single-leaf)))))
eec82323
LMI
5355 (when (string= gnus-tmp-name "")
5356 (setq gnus-tmp-name gnus-tmp-from))
5357 (unless (numberp gnus-tmp-lines)
23f87bed
MB
5358 (setq gnus-tmp-lines -1))
5359 (if (= gnus-tmp-lines -1)
5360 (setq gnus-tmp-lines "?")
5361 (setq gnus-tmp-lines (number-to-string gnus-tmp-lines)))
5362 (gnus-put-text-property
64763fe3
MB
5363 (point)
5364 (progn (eval gnus-summary-line-format-spec) (point))
23f87bed 5365 'gnus-number number)
64763fe3
MB
5366 (when gnus-visual-p
5367 (forward-line -1)
5368 (gnus-run-hooks 'gnus-summary-update-hook)
5369 (forward-line 1))
eec82323 5370
64763fe3 5371 (setq gnus-tmp-prev-subject simp-subject)))
eec82323
LMI
5372
5373 (when (nth 1 thread)
23f87bed
MB
5374 (push (list (max 0 gnus-tmp-level)
5375 (copy-sequence tree-stack)
5376 (nthcdr 1 thread))
5377 stack))
5378 (push (if (nth 1 thread) 1 0) tree-stack)
eec82323
LMI
5379 (incf gnus-tmp-level)
5380 (setq threads (if thread-end nil (cdar thread)))
23f87bed
MB
5381 (if gnus-summary-display-while-building
5382 (if building-count
5383 (progn
5384 ;; use a set frequency
5385 (setq building-line-count (1- building-line-count))
5386 (when (= building-line-count 0)
5387 (sit-for 0)
5388 (setq building-line-count
5389 gnus-summary-display-while-building)))
5390 ;; always
5391 (sit-for 0)))
eec82323
LMI
5392 (unless threads
5393 (setq gnus-tmp-level 0)))))
5394 (gnus-message 7 "Generating summary...done"))
5395
5396(defun gnus-summary-prepare-unthreaded (headers)
5397 "Generate an unthreaded summary buffer based on HEADERS."
5398 (let (header number mark)
5399
5400 (beginning-of-line)
5401
5402 (while headers
5403 ;; We may have to root out some bad articles...
5404 (when (memq (setq number (mail-header-number
5405 (setq header (pop headers))))
5406 gnus-newsgroup-limit)
5407 ;; Mark article as read when it has a low score.
5408 (when (and gnus-summary-mark-below
5409 (< (or (cdr (assq number gnus-newsgroup-scored))
5410 gnus-summary-default-score 0)
5411 gnus-summary-mark-below)
5412 (not (gnus-summary-article-ancient-p number)))
5413 (setq gnus-newsgroup-unreads
5414 (delq number gnus-newsgroup-unreads))
5415 (if gnus-newsgroup-auto-expire
5416 (push number gnus-newsgroup-expirable)
5417 (push (cons number gnus-low-score-mark)
5418 gnus-newsgroup-reads)))
5419
5420 (setq mark (gnus-article-mark number))
5421 (push (gnus-data-make number mark (1+ (point)) header 0)
5422 gnus-newsgroup-data)
5423 (gnus-summary-insert-line
5424 header 0 number
23f87bed 5425 (memq number gnus-newsgroup-undownloaded)
eec82323
LMI
5426 mark (memq number gnus-newsgroup-replied)
5427 (memq number gnus-newsgroup-expirable)
5428 (mail-header-subject header) nil
5429 (cdr (assq number gnus-newsgroup-scored))
5430 (memq number gnus-newsgroup-processable))))))
5431
16409b0b
GM
5432(defun gnus-summary-remove-list-identifiers ()
5433 "Remove list identifiers in `gnus-list-identifiers' from articles in the current group."
23f87bed
MB
5434 (let ((regexp (if (consp gnus-list-identifiers)
5435 (mapconcat 'identity gnus-list-identifiers " *\\|")
5436 gnus-list-identifiers))
5437 changed subject)
5438 (when regexp
01c52d31 5439 (setq regexp (concat "^\\(?:R[Ee]: +\\)*\\(" regexp " *\\)"))
23f87bed
MB
5440 (dolist (header gnus-newsgroup-headers)
5441 (setq subject (mail-header-subject header)
5442 changed nil)
01c52d31 5443 (while (string-match regexp subject)
23f87bed 5444 (setq subject
01c52d31 5445 (concat (substring subject 0 (match-beginning 1))
23f87bed
MB
5446 (substring subject (match-end 0)))
5447 changed t))
23f87bed 5448 (when changed
01c52d31
MB
5449 (when (string-match "^\\(\\(?:R[Ee]: +\\)+\\)R[Ee]: +" subject)
5450 (setq subject
5451 (concat (substring subject 0 (match-beginning 1))
5452 (substring subject (match-end 1)))))
23f87bed
MB
5453 (mail-header-set-subject header subject))))))
5454
5455(defun gnus-fetch-headers (articles)
5456 "Fetch headers of ARTICLES."
5457 (let ((name (gnus-group-decoded-name gnus-newsgroup-name)))
5458 (gnus-message 5 "Fetching headers for %s..." name)
5459 (prog1
5460 (if (eq 'nov
5461 (setq gnus-headers-retrieved-by
5462 (gnus-retrieve-headers
5463 articles gnus-newsgroup-name
5464 ;; We might want to fetch old headers, but
5465 ;; not if there is only 1 article.
5466 (and (or (and
5467 (not (eq gnus-fetch-old-headers 'some))
5468 (not (numberp gnus-fetch-old-headers)))
5469 (> (length articles) 1))
5470 gnus-fetch-old-headers))))
5471 (gnus-get-newsgroup-headers-xover
5472 articles nil nil gnus-newsgroup-name t)
5473 (gnus-get-newsgroup-headers))
5474 (gnus-message 5 "Fetching headers for %s...done" name))))
16409b0b 5475
6748645f 5476(defun gnus-select-newsgroup (group &optional read-all select-articles)
eec82323 5477 "Select newsgroup GROUP.
6748645f
LMI
5478If READ-ALL is non-nil, all articles in the group are selected.
5479If SELECT-ARTICLES, only select those articles from GROUP."
01c52d31 5480 (let* ((entry (gnus-group-entry group))
eec82323
LMI
5481 ;;!!! Dirty hack; should be removed.
5482 (gnus-summary-ignore-duplicates
23f87bed 5483 (if (eq (car (gnus-find-method-for-group group)) 'nnvirtual)
eec82323
LMI
5484 t
5485 gnus-summary-ignore-duplicates))
5486 (info (nth 2 entry))
01c52d31 5487 charset articles fetched-articles cached)
eec82323
LMI
5488
5489 (unless (gnus-check-server
475e0e0c
GM
5490 (set (make-local-variable 'gnus-current-select-method)
5491 (gnus-find-method-for-group group)))
eec82323 5492 (error "Couldn't open server"))
01c52d31 5493 (setq charset (gnus-group-name-charset gnus-current-select-method group))
eec82323
LMI
5494
5495 (or (and entry (not (eq (car entry) t))) ; Either it's active...
5496 (gnus-activate-group group) ; Or we can activate it...
5497 (progn ; Or we bug out.
5498 (when (equal major-mode 'gnus-summary-mode)
23f87bed 5499 (gnus-kill-buffer (current-buffer)))
01c52d31
MB
5500 (error
5501 "Couldn't activate group %s: %s"
5502 (mm-decode-coding-string group charset)
5503 (mm-decode-coding-string (gnus-status-message group) charset))))
eec82323
LMI
5504
5505 (unless (gnus-request-group group t)
01c52d31
MB
5506 (when (equal major-mode 'gnus-summary-mode)
5507 (gnus-kill-buffer (current-buffer)))
5508 (error "Couldn't request group %s: %s"
5509 (mm-decode-coding-string group charset)
5510 (mm-decode-coding-string (gnus-status-message group) charset)))
eec82323 5511
23f87bed 5512 (when gnus-agent
54506618 5513 (gnus-agent-possibly-alter-active group (gnus-active group) info)
132cf96d 5514
23f87bed
MB
5515 (setq gnus-summary-use-undownloaded-faces
5516 (gnus-agent-find-parameter
5517 group
5518 'agent-enable-undownloaded-faces)))
5519
5520 (setq gnus-newsgroup-name group
5521 gnus-newsgroup-unselected nil
5522 gnus-newsgroup-unreads (gnus-list-of-unread-articles group))
5523
5524 (let ((display (gnus-group-find-parameter group 'display)))
5525 (setq gnus-newsgroup-display
5526 (cond
5527 ((not (zerop (or (car-safe read-all) 0)))
5528 ;; The user entered the group with C-u SPC/RET, let's show
5529 ;; all articles.
5530 'gnus-not-ignore)
5531 ((eq display 'all)
5532 'gnus-not-ignore)
5533 ((arrayp display)
5534 (gnus-summary-display-make-predicate (mapcar 'identity display)))
5535 ((numberp display)
5536 ;; The following is probably the "correct" solution, but
5537 ;; it makes Gnus fetch all headers and then limit the
5538 ;; articles (which is slow), so instead we hack the
5539 ;; select-articles parameter instead. -- Simon Josefsson
5540 ;; <jas@kth.se>
5541 ;;
5542 ;; (gnus-byte-compile
5543 ;; `(lambda () (> number ,(- (cdr (gnus-active group))
5544 ;; display)))))
5545 (setq select-articles
5546 (gnus-uncompress-range
5547 (cons (let ((tmp (- (cdr (gnus-active group)) display)))
5548 (if (> tmp 0)
5549 tmp
5550 1))
5551 (cdr (gnus-active group)))))
5552 nil)
5553 (t
5554 nil))))
eec82323 5555
23f87bed 5556 (gnus-summary-setup-default-charset)
eec82323
LMI
5557
5558 ;; Kludge to avoid having cached articles nixed out in virtual groups.
5559 (when (gnus-virtual-group-p group)
5560 (setq cached gnus-newsgroup-cached))
5561
5562 (setq gnus-newsgroup-unreads
23f87bed
MB
5563 (gnus-sorted-ndifference
5564 (gnus-sorted-ndifference gnus-newsgroup-unreads
5565 gnus-newsgroup-marked)
eec82323
LMI
5566 gnus-newsgroup-dormant))
5567
5568 (setq gnus-newsgroup-processable nil)
5569
5570 (gnus-update-read-articles group gnus-newsgroup-unreads)
eec82323 5571
23f87bed
MB
5572 ;; Adjust and set lists of article marks.
5573 (when info
5574 (gnus-adjust-marked-articles info))
6748645f
LMI
5575 (if (setq articles select-articles)
5576 (setq gnus-newsgroup-unselected
23f87bed 5577 (gnus-sorted-difference gnus-newsgroup-unreads articles))
6748645f 5578 (setq articles (gnus-articles-to-read group read-all)))
eec82323
LMI
5579
5580 (cond
5581 ((null articles)
5582 ;;(gnus-message 3 "Couldn't select newsgroup -- no articles to display")
5583 'quit)
5584 ((eq articles 0) nil)
5585 (t
5586 ;; Init the dependencies hash table.
5587 (setq gnus-newsgroup-dependencies
5588 (gnus-make-hashtable (length articles)))
16409b0b 5589 (gnus-set-global-variables)
eec82323 5590 ;; Retrieve the headers and read them in.
23f87bed
MB
5591
5592 (setq gnus-newsgroup-headers (gnus-fetch-headers articles))
eec82323
LMI
5593
5594 ;; Kludge to avoid having cached articles nixed out in virtual groups.
5595 (when cached
5596 (setq gnus-newsgroup-cached cached))
5597
5598 ;; Suppress duplicates?
5599 (when gnus-suppress-duplicates
5600 (gnus-dup-suppress-articles))
5601
5602 ;; Set the initial limit.
5603 (setq gnus-newsgroup-limit (copy-sequence articles))
5604 ;; Remove canceled articles from the list of unread articles.
23f87bed
MB
5605 (setq fetched-articles
5606 (mapcar (lambda (headers) (mail-header-number headers))
5607 gnus-newsgroup-headers))
5608 (setq gnus-newsgroup-articles fetched-articles)
eec82323 5609 (setq gnus-newsgroup-unreads
23f87bed
MB
5610 (gnus-sorted-nintersection
5611 gnus-newsgroup-unreads fetched-articles))
5612 (gnus-compute-unseen-list)
5613
eec82323
LMI
5614 ;; Removed marked articles that do not exist.
5615 (gnus-update-missing-marks
23f87bed 5616 (gnus-sorted-difference articles fetched-articles))
eec82323 5617 ;; We might want to build some more threads first.
6748645f
LMI
5618 (when (and gnus-fetch-old-headers
5619 (eq gnus-headers-retrieved-by 'nov))
5620 (if (eq gnus-fetch-old-headers 'invisible)
5621 (gnus-build-all-threads)
5622 (gnus-build-old-threads)))
5623 ;; Let the Gnus agent mark articles as read.
5624 (when gnus-agent
5625 (gnus-agent-get-undownloaded-list))
16409b0b
GM
5626 ;; Remove list identifiers from subject
5627 (when gnus-list-identifiers
5628 (gnus-summary-remove-list-identifiers))
eec82323
LMI
5629 ;; Check whether auto-expire is to be done in this group.
5630 (setq gnus-newsgroup-auto-expire
5631 (gnus-group-auto-expirable-p group))
5632 ;; Set up the article buffer now, if necessary.
01c52d31
MB
5633 (unless (and gnus-single-article-buffer
5634 (equal gnus-article-buffer "*Article*"))
eec82323
LMI
5635 (gnus-article-setup-buffer))
5636 ;; First and last article in this newsgroup.
5637 (when gnus-newsgroup-headers
5638 (setq gnus-newsgroup-begin
5639 (mail-header-number (car gnus-newsgroup-headers))
5640 gnus-newsgroup-end
5641 (mail-header-number
5642 (gnus-last-element gnus-newsgroup-headers))))
5643 ;; GROUP is successfully selected.
5644 (or gnus-newsgroup-headers t)))))
5645
23f87bed
MB
5646(defun gnus-compute-unseen-list ()
5647 ;; The `seen' marks are treated specially.
5648 (if (not gnus-newsgroup-seen)
5649 (setq gnus-newsgroup-unseen gnus-newsgroup-articles)
5650 (setq gnus-newsgroup-unseen
5651 (gnus-inverse-list-range-intersection
5652 gnus-newsgroup-articles gnus-newsgroup-seen))))
5653
d09ae6ca
GM
5654(declare-function gnus-get-predicate "gnus-agent" (predicate))
5655
23f87bed
MB
5656(defun gnus-summary-display-make-predicate (display)
5657 (require 'gnus-agent)
5658 (when (= (length display) 1)
5659 (setq display (car display)))
5660 (unless gnus-summary-display-cache
5661 (dolist (elem (append '((unread . unread)
5662 (read . read)
5663 (unseen . unseen))
5664 gnus-article-mark-lists))
5665 (push (cons (cdr elem)
5666 (gnus-byte-compile
5667 `(lambda () (gnus-article-marked-p ',(cdr elem)))))
5668 gnus-summary-display-cache)))
5669 (let ((gnus-category-predicate-alist gnus-summary-display-cache)
5670 (gnus-category-predicate-cache gnus-summary-display-cache))
5671 (gnus-get-predicate display)))
5672
5673;; Uses the dynamically bound `number' variable.
9efa445f 5674(defvar number)
23f87bed
MB
5675(defun gnus-article-marked-p (type &optional article)
5676 (let ((article (or article number)))
5677 (cond
5678 ((eq type 'tick)
5679 (memq article gnus-newsgroup-marked))
5680 ((eq type 'spam)
5681 (memq article gnus-newsgroup-spam-marked))
5682 ((eq type 'unsend)
5683 (memq article gnus-newsgroup-unsendable))
5684 ((eq type 'undownload)
5685 (memq article gnus-newsgroup-undownloaded))
5686 ((eq type 'download)
5687 (memq article gnus-newsgroup-downloadable))
5688 ((eq type 'unread)
5689 (memq article gnus-newsgroup-unreads))
5690 ((eq type 'read)
5691 (memq article gnus-newsgroup-reads))
5692 ((eq type 'dormant)
5693 (memq article gnus-newsgroup-dormant) )
5694 ((eq type 'expire)
5695 (memq article gnus-newsgroup-expirable))
5696 ((eq type 'reply)
5697 (memq article gnus-newsgroup-replied))
5698 ((eq type 'killed)
5699 (memq article gnus-newsgroup-killed))
5700 ((eq type 'bookmark)
5701 (assq article gnus-newsgroup-bookmarks))
5702 ((eq type 'score)
5703 (assq article gnus-newsgroup-scored))
5704 ((eq type 'save)
5705 (memq article gnus-newsgroup-saved))
5706 ((eq type 'cache)
5707 (memq article gnus-newsgroup-cached))
5708 ((eq type 'forward)
5709 (memq article gnus-newsgroup-forwarded))
5710 ((eq type 'seen)
5711 (not (memq article gnus-newsgroup-unseen)))
5712 ((eq type 'recent)
5713 (memq article gnus-newsgroup-recent))
5714 (t t))))
5715
eec82323 5716(defun gnus-articles-to-read (group &optional read-all)
16409b0b 5717 "Find out what articles the user wants to read."
26c9afc3 5718 (let* ((articles
eec82323
LMI
5719 ;; Select all articles if `read-all' is non-nil, or if there
5720 ;; are no unread articles.
5721 (if (or read-all
5722 (and (zerop (length gnus-newsgroup-marked))
5723 (zerop (length gnus-newsgroup-unreads)))
23f87bed
MB
5724 ;; Fetch all if the predicate is non-nil.
5725 gnus-newsgroup-display)
5726 ;; We want to select the headers for all the articles in
5727 ;; the group, so we select either all the active
5728 ;; articles in the group, or (if that's nil), the
5729 ;; articles in the cache.
16409b0b 5730 (or
4b70e299 5731 (if gnus-newsgroup-maximum-articles
11abff8e
MB
5732 (let ((active (gnus-active group)))
5733 (gnus-uncompress-range
5734 (cons (max (car active)
4b70e299
MB
5735 (- (cdr active)
5736 gnus-newsgroup-maximum-articles
5737 -1))
11abff8e
MB
5738 (cdr active))))
5739 (gnus-uncompress-range (gnus-active group)))
16409b0b 5740 (gnus-cache-articles-in-group group))
23f87bed
MB
5741 ;; Select only the "normal" subset of articles.
5742 (gnus-sorted-nunion
5743 (gnus-sorted-union gnus-newsgroup-dormant gnus-newsgroup-marked)
5744 gnus-newsgroup-unreads)))
eec82323
LMI
5745 (scored-list (gnus-killed-articles gnus-newsgroup-killed articles))
5746 (scored (length scored-list))
5747 (number (length articles))
5748 (marked (+ (length gnus-newsgroup-marked)
5749 (length gnus-newsgroup-dormant)))
5750 (select
5751 (cond
5752 ((numberp read-all)
5753 read-all)
23f87bed
MB
5754 ((numberp gnus-newsgroup-display)
5755 gnus-newsgroup-display)
eec82323
LMI
5756 (t
5757 (condition-case ()
5758 (cond
5759 ((and (or (<= scored marked) (= scored number))
5760 (numberp gnus-large-newsgroup)
5761 (> number gnus-large-newsgroup))
23f87bed
MB
5762 (let* ((cursor-in-echo-area nil)
5763 (initial (gnus-parameter-large-newsgroup-initial
5764 gnus-newsgroup-name))
5765 (input
5766 (read-string
5767 (format
5768 "How many articles from %s (%s %d): "
01c52d31 5769 (gnus-group-decoded-name gnus-newsgroup-name)
23f87bed
MB
5770 (if initial "max" "default")
5771 number)
5772 (if initial
5773 (cons (number-to-string initial)
5774 0)))))
eec82323
LMI
5775 (if (string-match "^[ \t]*$" input) number input)))
5776 ((and (> scored marked) (< scored number)
5777 (> (- scored number) 20))
5778 (let ((input
5779 (read-string
5780 (format "%s %s (%d scored, %d total): "
5781 "How many articles from"
23f87bed
MB
5782 (gnus-group-decoded-name group)
5783 scored number))))
eec82323
LMI
5784 (if (string-match "^[ \t]*$" input)
5785 number input)))
5786 (t number))
d4dfaa19
DL
5787 (quit
5788 (message "Quit getting the articles to read")
5789 nil))))))
eec82323
LMI
5790 (setq select (if (stringp select) (string-to-number select) select))
5791 (if (or (null select) (zerop select))
5792 select
5793 (if (and (not (zerop scored)) (<= (abs select) scored))
5794 (progn
5795 (setq articles (sort scored-list '<))
5796 (setq number (length articles)))
5797 (setq articles (copy-sequence articles)))
5798
5799 (when (< (abs select) number)
5800 (if (< select 0)
5801 ;; Select the N oldest articles.
5802 (setcdr (nthcdr (1- (abs select)) articles) nil)
5803 ;; Select the N most recent articles.
5804 (setq articles (nthcdr (- number select) articles))))
5805 (setq gnus-newsgroup-unselected
23f87bed 5806 (gnus-sorted-difference gnus-newsgroup-unreads articles))
16409b0b 5807 (when gnus-alter-articles-to-read-function
23f87bed 5808 (setq articles
a1506d29 5809 (sort
16409b0b 5810 (funcall gnus-alter-articles-to-read-function
23f87bed 5811 gnus-newsgroup-name articles)
16409b0b 5812 '<)))
eec82323
LMI
5813 articles)))
5814
5815(defun gnus-killed-articles (killed articles)
5816 (let (out)
5817 (while articles
5818 (when (inline (gnus-member-of-range (car articles) killed))
5819 (push (car articles) out))
5820 (setq articles (cdr articles)))
5821 out))
5822
5823(defun gnus-uncompress-marks (marks)
5824 "Uncompress the mark ranges in MARKS."
5825 (let ((uncompressed '(score bookmark))
5826 out)
5827 (while marks
5828 (if (memq (caar marks) uncompressed)
5829 (push (car marks) out)
5830 (push (cons (caar marks) (gnus-uncompress-range (cdar marks))) out))
5831 (setq marks (cdr marks)))
5832 out))
5833
23f87bed
MB
5834(defun gnus-article-mark-to-type (mark)
5835 "Return the type of MARK."
5836 (or (cadr (assq mark gnus-article-special-mark-lists))
5837 'list))
5838
5839(defun gnus-article-unpropagatable-p (mark)
5840 "Return whether MARK should be propagated to back end."
5841 (memq mark gnus-article-unpropagated-mark-lists))
5842
eec82323 5843(defun gnus-adjust-marked-articles (info)
16409b0b 5844 "Set all article lists and remove all marks that are no longer valid."
eec82323
LMI
5845 (let* ((marked-lists (gnus-info-marks info))
5846 (active (gnus-active (gnus-info-group info)))
5847 (min (car active))
5848 (max (cdr active))
5849 (types gnus-article-mark-lists)
54506618
MB
5850 marks var articles article mark mark-type
5851 bgn end)
eec82323 5852
23f87bed
MB
5853 (dolist (marks marked-lists)
5854 (setq mark (car marks)
5855 mark-type (gnus-article-mark-to-type mark)
5856 var (intern (format "gnus-newsgroup-%s" (car (rassq mark types)))))
eec82323 5857
23f87bed
MB
5858 ;; We set the variable according to the type of the marks list,
5859 ;; and then adjust the marks to a subset of the active articles.
eec82323 5860 (cond
54506618 5861 ;; Adjust "simple" lists - compressed yet unsorted
23f87bed 5862 ((eq mark-type 'list)
54506618
MB
5863 ;; Simultaneously uncompress and clip to active range
5864 ;; See gnus-uncompress-range for a description of possible marks
5865 (let (l lh)
5866 (if (not (cadr marks))
5867 (set var nil)
5868 (setq articles (if (numberp (cddr marks))
5869 (list (cdr marks))
5870 (cdr marks))
5871 lh (cons nil nil)
5872 l lh)
5873
5874 (while (setq article (pop articles))
5875 (cond ((consp article)
5876 (setq bgn (max (car article) min)
5877 end (min (cdr article) max))
5878 (while (<= bgn end)
5879 (setq l (setcdr l (cons bgn nil))
5880 bgn (1+ bgn))))
5881 ((and (<= min article)
5882 (>= max article))
5883 (setq l (setcdr l (cons article nil))))))
5884 (set var (cdr lh)))))
eec82323 5885 ;; Adjust assocs.
23f87bed
MB
5886 ((eq mark-type 'tuple)
5887 (set var (setq articles (cdr marks)))
a8151ef7
LMI
5888 (when (not (listp (cdr (symbol-value var))))
5889 (set var (list (symbol-value var))))
5890 (when (not (listp (cdr articles)))
5891 (setq articles (list articles)))
eec82323
LMI
5892 (while articles
5893 (when (or (not (consp (setq article (pop articles))))
5894 (< (car article) min)
5895 (> (car article) max))
23f87bed
MB
5896 (set var (delq article (symbol-value var))))))
5897 ;; Adjust ranges (sloppily).
5898 ((eq mark-type 'range)
5899 (cond
5900 ((eq mark 'seen)
5901 ;; Fix the record for `seen' if it looks like (seen NUM1 . NUM2).
5902 ;; It should be (seen (NUM1 . NUM2)).
5903 (when (numberp (cddr marks))
5904 (setcdr marks (list (cdr marks))))
5905 (setq articles (cdr marks))
5906 (while (and articles
5907 (or (and (consp (car articles))
5908 (> min (cdar articles)))
5909 (and (numberp (car articles))
5910 (> min (car articles)))))
5911 (pop articles))
5912 (set var articles))))))))
eec82323
LMI
5913
5914(defun gnus-update-missing-marks (missing)
6748645f 5915 "Go through the list of MISSING articles and remove them from the mark lists."
eec82323 5916 (when missing
23f87bed 5917 (let (var m)
eec82323 5918 ;; Go through all types.
23f87bed
MB
5919 (dolist (elem gnus-article-mark-lists)
5920 (when (eq (gnus-article-mark-to-type (cdr elem)) 'list)
5921 (setq var (intern (format "gnus-newsgroup-%s" (car elem))))
5922 (when (symbol-value var)
5923 ;; This list has articles. So we delete all missing
5924 ;; articles from it.
5925 (setq m missing)
5926 (while m
5927 (set var (delq (pop m) (symbol-value var))))))))))
eec82323
LMI
5928
5929(defun gnus-update-marks ()
5930 "Enter the various lists of marked articles into the newsgroup info list."
5931 (let ((types gnus-article-mark-lists)
5932 (info (gnus-get-info gnus-newsgroup-name))
16409b0b 5933 type list newmarked symbol delta-marks)
eec82323 5934 (when info
16409b0b 5935 ;; Add all marks lists to the list of marks lists.
eec82323 5936 (while (setq type (pop types))
16409b0b
GM
5937 (setq list (symbol-value
5938 (setq symbol
23f87bed 5939 (intern (format "gnus-newsgroup-%s" (car type))))))
eec82323 5940
16409b0b 5941 (when list
eec82323
LMI
5942 ;; Get rid of the entries of the articles that have the
5943 ;; default score.
5944 (when (and (eq (cdr type) 'score)
5945 gnus-save-score
5946 list)
5947 (let* ((arts list)
5948 (prev (cons nil list))
5949 (all prev))
5950 (while arts
5951 (if (or (not (consp (car arts)))
5952 (= (cdar arts) gnus-summary-default-score))
5953 (setcdr prev (cdr arts))
5954 (setq prev arts))
5955 (setq arts (cdr arts)))
16409b0b
GM
5956 (setq list (cdr all)))))
5957
23f87bed
MB
5958 (when (eq (cdr type) 'seen)
5959 (setq list (gnus-range-add list gnus-newsgroup-unseen)))
5960
5961 (when (eq (gnus-article-mark-to-type (cdr type)) 'list)
16409b0b 5962 (setq list (gnus-compress-sequence (set symbol (sort list '<)) t)))
a1506d29 5963
23f87bed
MB
5964 (when (and (gnus-check-backend-function
5965 'request-set-mark gnus-newsgroup-name)
5966 (not (gnus-article-unpropagatable-p (cdr type))))
5967 (let* ((old (cdr (assq (cdr type) (gnus-info-marks info))))
5968 (del (gnus-remove-from-range (gnus-copy-sequence old) list))
5969 (add (gnus-remove-from-range
5970 (gnus-copy-sequence list) old)))
5971 (when add
5972 (push (list add 'add (list (cdr type))) delta-marks))
5973 (when del
5974 (push (list del 'del (list (cdr type))) delta-marks))))
a1506d29 5975
16409b0b
GM
5976 (when list
5977 (push (cons (cdr type) list) newmarked)))
5978
5979 (when delta-marks
5980 (unless (gnus-check-group gnus-newsgroup-name)
5981 (error "Can't open server for %s" gnus-newsgroup-name))
5982 (gnus-request-set-mark gnus-newsgroup-name delta-marks))
a1506d29 5983
eec82323
LMI
5984 ;; Enter these new marks into the info of the group.
5985 (if (nthcdr 3 info)
5986 (setcar (nthcdr 3 info) newmarked)
5987 ;; Add the marks lists to the end of the info.
5988 (when newmarked
5989 (setcdr (nthcdr 2 info) (list newmarked))))
5990
5991 ;; Cut off the end of the info if there's nothing else there.
5992 (let ((i 5))
5993 (while (and (> i 2)
5994 (not (nth i info)))
5995 (when (nthcdr (decf i) info)
5996 (setcdr (nthcdr i info) nil)))))))
5997
5998(defun gnus-set-mode-line (where)
16409b0b 5999 "Set the mode line of the article or summary buffers.
eec82323
LMI
6000If WHERE is `summary', the summary mode line format will be used."
6001 ;; Is this mode line one we keep updated?
16409b0b
GM
6002 (when (and (memq where gnus-updated-mode-lines)
6003 (symbol-value
6004 (intern (format "gnus-%s-mode-line-format-spec" where))))
eec82323 6005 (let (mode-string)
c7a91ce1
SM
6006 ;; We evaluate this in the summary buffer since these
6007 ;; variables are buffer-local to that buffer.
6008 (with-current-buffer gnus-summary-buffer
6009 ;; We bind all these variables that are used in the `eval' form
eec82323
LMI
6010 ;; below.
6011 (let* ((mformat (symbol-value
6012 (intern
6013 (format "gnus-%s-mode-line-format-spec" where))))
b90a6149
MB
6014 (gnus-tmp-group-name (gnus-mode-string-quote
6015 (gnus-group-decoded-name
6016 gnus-newsgroup-name)))
eec82323
LMI
6017 (gnus-tmp-article-number (or gnus-current-article 0))
6018 (gnus-tmp-unread gnus-newsgroup-unreads)
6019 (gnus-tmp-unread-and-unticked (length gnus-newsgroup-unreads))
6020 (gnus-tmp-unselected (length gnus-newsgroup-unselected))
6021 (gnus-tmp-unread-and-unselected
6022 (cond ((and (zerop gnus-tmp-unread-and-unticked)
6023 (zerop gnus-tmp-unselected))
6024 "")
6025 ((zerop gnus-tmp-unselected)
6026 (format "{%d more}" gnus-tmp-unread-and-unticked))
6027 (t (format "{%d(+%d) more}"
6028 gnus-tmp-unread-and-unticked
6029 gnus-tmp-unselected))))
6030 (gnus-tmp-subject
6031 (if (and gnus-current-headers
6032 (vectorp gnus-current-headers))
6033 (gnus-mode-string-quote
6034 (mail-header-subject gnus-current-headers))
6035 ""))
6036 bufname-length max-len
23f87bed 6037 gnus-tmp-header) ;; passed as argument to any user-format-funcs
eec82323
LMI
6038 (setq mode-string (eval mformat))
6039 (setq bufname-length (if (string-match "%b" mode-string)
6040 (- (length
6041 (buffer-name
6042 (if (eq where 'summary)
6043 nil
6044 (get-buffer gnus-article-buffer))))
6045 2)
6046 0))
6047 (setq max-len (max 4 (if gnus-mode-non-string-length
6048 (- (window-width)
6049 gnus-mode-non-string-length
6050 bufname-length)
6051 (length mode-string))))
6052 ;; We might have to chop a bit of the string off...
6053 (when (> (length mode-string) max-len)
6054 (setq mode-string
16409b0b 6055 (concat (truncate-string-to-width mode-string (- max-len 3))
eec82323
LMI
6056 "...")))
6057 ;; Pad the mode string a bit.
6058 (setq mode-string (format (format "%%-%ds" max-len) mode-string))))
6059 ;; Update the mode line.
6060 (setq mode-line-buffer-identification
6061 (gnus-mode-line-buffer-identification (list mode-string)))
6062 (set-buffer-modified-p t))))
6063
6064(defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
6065 "Go through the HEADERS list and add all Xrefs to a hash table.
6066The resulting hash table is returned, or nil if no Xrefs were found."
6067 (let* ((virtual (gnus-virtual-group-p from-newsgroup))
6068 (prefix (if virtual "" (gnus-group-real-prefix from-newsgroup)))
6069 (xref-hashtb (gnus-make-hashtable))
6070 start group entry number xrefs header)
6071 (while headers
6072 (setq header (pop headers))
6073 (when (and (setq xrefs (mail-header-xref header))
6074 (not (memq (setq number (mail-header-number header))
6075 unreads)))
6076 (setq start 0)
6077 (while (string-match "\\([^ ]+\\)[:/]\\([0-9]+\\)" xrefs start)
6078 (setq start (match-end 0))
6079 (setq group (if prefix
6080 (concat prefix (substring xrefs (match-beginning 1)
6081 (match-end 1)))
6082 (substring xrefs (match-beginning 1) (match-end 1))))
6083 (setq number
e9bd5782 6084 (string-to-number (substring xrefs (match-beginning 2)
eec82323
LMI
6085 (match-end 2))))
6086 (if (setq entry (gnus-gethash group xref-hashtb))
6087 (setcdr entry (cons number (cdr entry)))
6088 (gnus-sethash group (cons number nil) xref-hashtb)))))
6089 (and start xref-hashtb)))
6090
6091(defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads)
6092 "Look through all the headers and mark the Xrefs as read."
6093 (let ((virtual (gnus-virtual-group-p from-newsgroup))
01c52d31 6094 name info xref-hashtb idlist method nth4)
398a825b 6095 (with-current-buffer gnus-group-buffer
eec82323
LMI
6096 (when (setq xref-hashtb
6097 (gnus-create-xref-hashtb from-newsgroup headers unreads))
6098 (mapatoms
6099 (lambda (group)
6100 (unless (string= from-newsgroup (setq name (symbol-name group)))
6101 (setq idlist (symbol-value group))
6102 ;; Dead groups are not updated.
6103 (and (prog1
01c52d31 6104 (setq info (gnus-get-info name))
eec82323
LMI
6105 (when (stringp (setq nth4 (gnus-info-method info)))
6106 (setq nth4 (gnus-server-to-method nth4))))
6107 ;; Only do the xrefs if the group has the same
6108 ;; select method as the group we have just read.
6109 (or (gnus-methods-equal-p
6110 nth4 (gnus-find-method-for-group from-newsgroup))
6111 virtual
6112 (equal nth4 (setq method (gnus-find-method-for-group
6113 from-newsgroup)))
6114 (and (equal (car nth4) (car method))
6115 (equal (nth 1 nth4) (nth 1 method))))
6116 gnus-use-cross-reference
6117 (or (not (eq gnus-use-cross-reference t))
6118 virtual
6119 ;; Only do cross-references on subscribed
6120 ;; groups, if that is what is wanted.
6121 (<= (gnus-info-level info) gnus-level-subscribed))
6122 (gnus-group-make-articles-read name idlist))))
6123 xref-hashtb)))))
6124
6748645f 6125(defun gnus-compute-read-articles (group articles)
01c52d31 6126 (let* ((entry (gnus-group-entry group))
6748645f
LMI
6127 (info (nth 2 entry))
6128 (active (gnus-active group))
6129 ninfo)
6130 (when entry
16409b0b 6131 ;; First peel off all invalid article numbers.
6748645f
LMI
6132 (when active
6133 (let ((ids articles)
6134 id first)
6135 (while (setq id (pop ids))
6136 (when (and first (> id (cdr active)))
6137 ;; We'll end up in this situation in one particular
6138 ;; obscure situation. If you re-scan a group and get
6139 ;; a new article that is cross-posted to a different
6140 ;; group that has not been re-scanned, you might get
6141 ;; crossposted article that has a higher number than
6142 ;; Gnus believes possible. So we re-activate this
6143 ;; group as well. This might mean doing the
6144 ;; crossposting thingy will *increase* the number
6145 ;; of articles in some groups. Tsk, tsk.
6146 (setq active (or (gnus-activate-group group) active)))
6147 (when (or (> id (cdr active))
6148 (< id (car active)))
6149 (setq articles (delq id articles))))))
6150 ;; If the read list is nil, we init it.
6151 (if (and active
6152 (null (gnus-info-read info))
6153 (> (car active) 1))
6154 (setq ninfo (cons 1 (1- (car active))))
6155 (setq ninfo (gnus-info-read info)))
6156 ;; Then we add the read articles to the range.
6157 (gnus-add-to-range
6158 ninfo (setq articles (sort articles '<))))))
6159
eec82323
LMI
6160(defun gnus-group-make-articles-read (group articles)
6161 "Update the info of GROUP to say that ARTICLES are read."
6162 (let* ((num 0)
01c52d31 6163 (entry (gnus-group-entry group))
eec82323
LMI
6164 (info (nth 2 entry))
6165 (active (gnus-active group))
6166 range)
6748645f
LMI
6167 (when entry
6168 (setq range (gnus-compute-read-articles group articles))
01c52d31 6169 (with-current-buffer gnus-group-buffer
6748645f
LMI
6170 (gnus-undo-register
6171 `(progn
6172 (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
6173 (gnus-info-set-read ',info ',(gnus-info-read info))
6174 (gnus-get-unread-articles-in-group ',info (gnus-active ,group))
23f87bed 6175 (gnus-request-set-mark ,group (list (list ',range 'del '(read))))
6748645f
LMI
6176 (gnus-group-update-group ,group t))))
6177 ;; Add the read articles to the range.
6178 (gnus-info-set-read info range)
23f87bed 6179 (gnus-request-set-mark group (list (list range 'add '(read))))
6748645f
LMI
6180 ;; Then we have to re-compute how many unread
6181 ;; articles there are in this group.
6182 (when active
6183 (cond
6184 ((not range)
6185 (setq num (- (1+ (cdr active)) (car active))))
6186 ((not (listp (cdr range)))
6187 (setq num (- (cdr active) (- (1+ (cdr range))
6188 (car range)))))
6189 (t
6190 (while range
6191 (if (numberp (car range))
6192 (setq num (1+ num))
6193 (setq num (+ num (- (1+ (cdar range)) (caar range)))))
6194 (setq range (cdr range)))
6195 (setq num (- (cdr active) num))))
6196 ;; Update the number of unread articles.
6197 (setcar entry num)
6198 ;; Update the group buffer.
23f87bed
MB
6199 (unless (gnus-ephemeral-group-p group)
6200 (gnus-group-update-group group t))))))
eec82323 6201
eec82323
LMI
6202(defvar gnus-newsgroup-none-id 0)
6203
6204(defun gnus-get-newsgroup-headers (&optional dependencies force-new)
6205 (let ((cur nntp-server-buffer)
6206 (dependencies
6207 (or dependencies
01c52d31
MB
6208 (with-current-buffer gnus-summary-buffer
6209 gnus-newsgroup-dependencies)))
6210 headers id end ref number
16409b0b
GM
6211 (mail-parse-charset gnus-newsgroup-charset)
6212 (mail-parse-ignored-charsets
c7a91ce1
SM
6213 (save-current-buffer (condition-case nil
6214 (set-buffer gnus-summary-buffer)
6215 (error))
6216 gnus-newsgroup-ignored-charsets)))
6217 (with-current-buffer nntp-server-buffer
eec82323
LMI
6218 ;; Translate all TAB characters into SPACE characters.
6219 (subst-char-in-region (point-min) (point-max) ?\t ? t)
16409b0b 6220 (subst-char-in-region (point-min) (point-max) ?\r ? t)
23f87bed 6221 (ietf-drums-unfold-fws)
6748645f 6222 (gnus-run-hooks 'gnus-parse-headers-hook)
eec82323 6223 (let ((case-fold-search t)
6748645f 6224 in-reply-to header p lines chars)
eec82323 6225 (goto-char (point-min))
01ccbb85 6226 ;; Search to the beginning of the next header. Error messages
eec82323
LMI
6227 ;; do not begin with 2 or 3.
6228 (while (re-search-forward "^[23][0-9]+ " nil t)
6229 (setq id nil
6230 ref nil)
6231 ;; This implementation of this function, with nine
6232 ;; search-forwards instead of the one re-search-forward and
6233 ;; a case (which basically was the old function) is actually
01ccbb85 6234 ;; about twice as fast, even though it looks messier. You
eec82323
LMI
6235 ;; can't have everything, I guess. Speed and elegance
6236 ;; doesn't always go hand in hand.
6237 (setq
6238 header
6239 (vector
6240 ;; Number.
6241 (prog1
01c52d31 6242 (setq number (read cur))
eec82323
LMI
6243 (end-of-line)
6244 (setq p (point))
6245 (narrow-to-region (point)
6246 (or (and (search-forward "\n.\n" nil t)
6247 (- (point) 2))
6248 (point))))
6249 ;; Subject.
6250 (progn
6251 (goto-char p)
23f87bed 6252 (if (search-forward "\nsubject:" nil t)
16409b0b
GM
6253 (funcall gnus-decode-encoded-word-function
6254 (nnheader-header-value))
2bd3dcae 6255 "(none)"))
eec82323
LMI
6256 ;; From.
6257 (progn
6258 (goto-char p)
23f87bed 6259 (if (search-forward "\nfrom:" nil t)
343d6628 6260 (funcall gnus-decode-encoded-address-function
16409b0b 6261 (nnheader-header-value))
2bd3dcae 6262 "(nobody)"))
eec82323
LMI
6263 ;; Date.
6264 (progn
6265 (goto-char p)
23f87bed 6266 (if (search-forward "\ndate:" nil t)
eec82323
LMI
6267 (nnheader-header-value) ""))
6268 ;; Message-ID.
6269 (progn
6270 (goto-char p)
6748645f
LMI
6271 (setq id (if (re-search-forward
6272 "^message-id: *\\(<[^\n\t> ]+>\\)" nil t)
6273 ;; We do it this way to make sure the Message-ID
6274 ;; is (somewhat) syntactically valid.
6275 (buffer-substring (match-beginning 1)
6276 (match-end 1))
eec82323
LMI
6277 ;; If there was no message-id, we just fake one
6278 ;; to make subsequent routines simpler.
01c52d31 6279 (nnheader-generate-fake-message-id number))))
eec82323
LMI
6280 ;; References.
6281 (progn
6282 (goto-char p)
23f87bed 6283 (if (search-forward "\nreferences:" nil t)
eec82323
LMI
6284 (progn
6285 (setq end (point))
6286 (prog1
6287 (nnheader-header-value)
6288 (setq ref
6289 (buffer-substring
6290 (progn
6291 (end-of-line)
6292 (search-backward ">" end t)
6293 (1+ (point)))
6294 (progn
6295 (search-backward "<" end t)
6296 (point))))))
6297 ;; Get the references from the in-reply-to header if there
6298 ;; were no references and the in-reply-to header looks
6299 ;; promising.
23f87bed 6300 (if (and (search-forward "\nin-reply-to:" nil t)
eec82323
LMI
6301 (setq in-reply-to (nnheader-header-value))
6302 (string-match "<[^>]+>" in-reply-to))
6748645f
LMI
6303 (let (ref2)
6304 (setq ref (substring in-reply-to (match-beginning 0)
6305 (match-end 0)))
6306 (while (string-match "<[^>]+>" in-reply-to (match-end 0))
6307 (setq ref2 (substring in-reply-to (match-beginning 0)
6308 (match-end 0)))
6309 (when (> (length ref2) (length ref))
6310 (setq ref ref2)))
6311 ref)
eec82323
LMI
6312 (setq ref nil))))
6313 ;; Chars.
6748645f
LMI
6314 (progn
6315 (goto-char p)
6316 (if (search-forward "\nchars: " nil t)
6317 (if (numberp (setq chars (ignore-errors (read cur))))
23f87bed
MB
6318 chars -1)
6319 -1))
eec82323
LMI
6320 ;; Lines.
6321 (progn
6322 (goto-char p)
6323 (if (search-forward "\nlines: " nil t)
a8151ef7 6324 (if (numberp (setq lines (ignore-errors (read cur))))
23f87bed
MB
6325 lines -1)
6326 -1))
eec82323
LMI
6327 ;; Xref.
6328 (progn
6329 (goto-char p)
23f87bed 6330 (and (search-forward "\nxref:" nil t)
16409b0b
GM
6331 (nnheader-header-value)))
6332 ;; Extra.
6333 (when gnus-extra-headers
6334 (let ((extra gnus-extra-headers)
6335 out)
6336 (while extra
6337 (goto-char p)
6338 (when (search-forward
23f87bed 6339 (concat "\n" (symbol-name (car extra)) ":") nil t)
16409b0b
GM
6340 (push (cons (car extra) (nnheader-header-value))
6341 out))
6342 (pop extra))
6343 out))))
eec82323
LMI
6344 (when (equal id ref)
6345 (setq ref nil))
6748645f
LMI
6346
6347 (when gnus-alter-header-function
6348 (funcall gnus-alter-header-function header)
6349 (setq id (mail-header-id header)
6350 ref (gnus-parent-id (mail-header-references header))))
6351
6352 (when (setq header
6353 (gnus-dependencies-add-header
6354 header dependencies force-new))
eec82323
LMI
6355 (push header headers))
6356 (goto-char (point-max))
6357 (widen))
6358 (nreverse headers)))))
6359
eec82323
LMI
6360;; Goes through the xover lines and returns a list of vectors
6361(defun gnus-get-newsgroup-headers-xover (sequence &optional
6362 force-new dependencies
6363 group also-fetch-heads)
16409b0b
GM
6364 "Parse the news overview data in the server buffer.
6365Return a list of headers that match SEQUENCE (see
6366`nntp-retrieve-headers')."
eec82323
LMI
6367 ;; Get the Xref when the users reads the articles since most/some
6368 ;; NNTP servers do not include Xrefs when using XOVER.
6369 (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs))
16409b0b
GM
6370 (let ((mail-parse-charset gnus-newsgroup-charset)
6371 (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
6372 (cur nntp-server-buffer)
eec82323 6373 (dependencies (or dependencies gnus-newsgroup-dependencies))
23f87bed
MB
6374 (allp (cond
6375 ((eq gnus-read-all-available-headers t)
6376 t)
14e6dc54
MB
6377 ((and (stringp gnus-read-all-available-headers)
6378 group)
23f87bed
MB
6379 (string-match gnus-read-all-available-headers group))
6380 (t
6381 nil)))
eec82323 6382 number headers header)
c7a91ce1 6383 (with-current-buffer nntp-server-buffer
16409b0b 6384 (subst-char-in-region (point-min) (point-max) ?\r ? t)
eec82323 6385 ;; Allow the user to mangle the headers before parsing them.
6748645f 6386 (gnus-run-hooks 'gnus-parse-headers-hook)
eec82323 6387 (goto-char (point-min))
23f87bed
MB
6388 (gnus-parse-without-error
6389 (while (and (or sequence allp)
6390 (not (eobp)))
6391 (setq number (read cur))
6392 (when (not allp)
6393 (while (and sequence
6394 (< (car sequence) number))
6395 (setq sequence (cdr sequence))))
6396 (when (and (or allp
6397 (and sequence
6398 (eq number (car sequence))))
6399 (progn
6400 (setq sequence (cdr sequence))
6401 (setq header (inline
6402 (gnus-nov-parse-line
6403 number dependencies force-new)))))
6404 (push header headers))
6405 (forward-line 1)))
eec82323
LMI
6406 ;; A common bug in inn is that if you have posted an article and
6407 ;; then retrieves the active file, it will answer correctly --
6408 ;; the new article is included. However, a NOV entry for the
6409 ;; article may not have been generated yet, so this may fail.
6410 ;; We work around this problem by retrieving the last few
6411 ;; headers using HEAD.
6412 (if (or (not also-fetch-heads)
6413 (not sequence))
6414 ;; We (probably) got all the headers.
6415 (nreverse headers)
6416 (let ((gnus-nov-is-evil t))
6417 (nconc
6418 (nreverse headers)
23f87bed 6419 (when (eq (gnus-retrieve-headers sequence group) 'headers)
eec82323
LMI
6420 (gnus-get-newsgroup-headers))))))))
6421
6422(defun gnus-article-get-xrefs ()
6423 "Fill in the Xref value in `gnus-current-headers', if necessary.
6424This is meant to be called in `gnus-article-internal-prepare-hook'."
01c52d31
MB
6425 (let ((headers (with-current-buffer gnus-summary-buffer
6426 gnus-current-headers)))
eec82323
LMI
6427 (or (not gnus-use-cross-reference)
6428 (not headers)
6429 (and (mail-header-xref headers)
6430 (not (string= (mail-header-xref headers) "")))
6431 (let ((case-fold-search t)
6432 xref)
6433 (save-restriction
6434 (nnheader-narrow-to-headers)
6435 (goto-char (point-min))
16409b0b
GM
6436 (when (or (and (not (eobp))
6437 (eq (downcase (char-after)) ?x)
eec82323
LMI
6438 (looking-at "Xref:"))
6439 (search-forward "\nXref:" nil t))
6440 (goto-char (1+ (match-end 0)))
01c52d31 6441 (setq xref (buffer-substring (point) (point-at-eol)))
eec82323
LMI
6442 (mail-header-set-xref headers xref)))))))
6443
6444(defun gnus-summary-insert-subject (id &optional old-header use-old-header)
6748645f
LMI
6445 "Find article ID and insert the summary line for that article.
6446OLD-HEADER can either be a header or a line number to insert
6447the subject line on."
6448 (let* ((line (and (numberp old-header) old-header))
6449 (old-header (and (vectorp old-header) old-header))
6450 (header (cond ((and old-header use-old-header)
16409b0b
GM
6451 old-header)
6452 ((and (numberp id)
6453 (gnus-number-to-header id))
6454 (gnus-number-to-header id))
6455 (t
6456 (gnus-read-header id))))
6457 (number (and (numberp id) id))
6458 d)
eec82323
LMI
6459 (when header
6460 ;; Rebuild the thread that this article is part of and go to the
6461 ;; article we have fetched.
6462 (when (and (not gnus-show-threads)
6463 old-header)
6748645f
LMI
6464 (when (and number
6465 (setq d (gnus-data-find (mail-header-number old-header))))
eec82323
LMI
6466 (goto-char (gnus-data-pos d))
6467 (gnus-data-remove
6468 number
01c52d31 6469 (- (point-at-bol)
eec82323 6470 (prog1
01c52d31 6471 (1+ (point-at-eol))
eec82323 6472 (gnus-delete-line))))))
23f87bed
MB
6473 ;; Remove list identifiers from subject.
6474 (when gnus-list-identifiers
6475 (let ((gnus-newsgroup-headers (list header)))
c3bc41c2 6476 (gnus-summary-remove-list-identifiers)))
eec82323
LMI
6477 (when old-header
6478 (mail-header-set-number header (mail-header-number old-header)))
6479 (setq gnus-newsgroup-sparse
6480 (delq (setq number (mail-header-number header))
6481 gnus-newsgroup-sparse))
6482 (setq gnus-newsgroup-ancient (delq number gnus-newsgroup-ancient))
6748645f
LMI
6483 (push number gnus-newsgroup-limit)
6484 (gnus-rebuild-thread (mail-header-id header) line)
eec82323
LMI
6485 (gnus-summary-goto-subject number nil t))
6486 (when (and (numberp number)
6487 (> number 0))
6488 ;; We have to update the boundaries even if we can't fetch the
6489 ;; article if ID is a number -- so that the next `P' or `N'
6490 ;; command will fetch the previous (or next) article even
6491 ;; if the one we tried to fetch this time has been canceled.
6492 (when (> number gnus-newsgroup-end)
6493 (setq gnus-newsgroup-end number))
6494 (when (< number gnus-newsgroup-begin)
6495 (setq gnus-newsgroup-begin number))
6496 (setq gnus-newsgroup-unselected
6497 (delq number gnus-newsgroup-unselected)))
6498 ;; Report back a success?
6499 (and header (mail-header-number header))))
6500
6501;;; Process/prefix in the summary buffer
6502
6503(defun gnus-summary-work-articles (n)
6748645f
LMI
6504 "Return a list of articles to be worked upon.
6505The prefix argument, the list of process marked articles, and the
6506current article will be taken into consideration."
c7a91ce1 6507 (with-current-buffer gnus-summary-buffer
6748645f
LMI
6508 (cond
6509 (n
6510 ;; A numerical prefix has been given.
6511 (setq n (prefix-numeric-value n))
6512 (let ((backward (< n 0))
6513 (n (abs (prefix-numeric-value n)))
6514 articles article)
6515 (save-excursion
6516 (while
6517 (and (> n 0)
6518 (push (setq article (gnus-summary-article-number))
6519 articles)
6520 (if backward
6521 (gnus-summary-find-prev nil article)
6522 (gnus-summary-find-next nil article)))
6523 (decf n)))
6524 (nreverse articles)))
6525 ((and (gnus-region-active-p) (mark))
6526 (message "region active")
6527 ;; Work on the region between point and mark.
6528 (let ((max (max (point) (mark)))
6529 articles article)
6530 (save-excursion
7dafe00b 6531 (goto-char (min (point) (mark)))
6748645f
LMI
6532 (while
6533 (and
6534 (push (setq article (gnus-summary-article-number)) articles)
6535 (gnus-summary-find-next nil article)
6536 (< (point) max)))
6537 (nreverse articles))))
6538 (gnus-newsgroup-processable
6539 ;; There are process-marked articles present.
6540 ;; Save current state.
6541 (gnus-summary-save-process-mark)
6542 ;; Return the list.
6543 (reverse gnus-newsgroup-processable))
6544 (t
6545 ;; Just return the current article.
6546 (list (gnus-summary-article-number))))))
6547
6548(defmacro gnus-summary-iterate (arg &rest forms)
6549 "Iterate over the process/prefixed articles and do FORMS.
6550ARG is the interactive prefix given to the command. FORMS will be
6551executed with point over the summary line of the articles."
6552 (let ((articles (make-symbol "gnus-summary-iterate-articles")))
6553 `(let ((,articles (gnus-summary-work-articles ,arg)))
6554 (while ,articles
6555 (gnus-summary-goto-subject (car ,articles))
16409b0b
GM
6556 ,@forms
6557 (pop ,articles)))))
6748645f
LMI
6558
6559(put 'gnus-summary-iterate 'lisp-indent-function 1)
6560(put 'gnus-summary-iterate 'edebug-form-spec '(form body))
eec82323
LMI
6561
6562(defun gnus-summary-save-process-mark ()
6563 "Push the current set of process marked articles on the stack."
6564 (interactive)
6565 (push (copy-sequence gnus-newsgroup-processable)
6566 gnus-newsgroup-process-stack))
6567
6568(defun gnus-summary-kill-process-mark ()
6569 "Push the current set of process marked articles on the stack and unmark."
6570 (interactive)
6571 (gnus-summary-save-process-mark)
6572 (gnus-summary-unmark-all-processable))
6573
6574(defun gnus-summary-yank-process-mark ()
6575 "Pop the last process mark state off the stack and restore it."
6576 (interactive)
6577 (unless gnus-newsgroup-process-stack
6578 (error "Empty mark stack"))
6579 (gnus-summary-process-mark-set (pop gnus-newsgroup-process-stack)))
6580
6581(defun gnus-summary-process-mark-set (set)
6582 "Make SET into the current process marked articles."
6583 (gnus-summary-unmark-all-processable)
01c52d31 6584 (mapc 'gnus-summary-set-process-mark set))
eec82323
LMI
6585
6586;;; Searching and stuff
6587
6588(defun gnus-summary-search-group (&optional backward use-level)
6589 "Search for next unread newsgroup.
6590If optional argument BACKWARD is non-nil, search backward instead."
c7a91ce1 6591 (with-current-buffer gnus-group-buffer
eec82323
LMI
6592 (when (gnus-group-search-forward
6593 backward nil (if use-level (gnus-group-group-level) nil))
6594 (gnus-group-group-name))))
6595
6596(defun gnus-summary-best-group (&optional exclude-group)
6597 "Find the name of the best unread group.
6598If EXCLUDE-GROUP, do not go to this group."
01c52d31 6599 (with-current-buffer gnus-group-buffer
eec82323
LMI
6600 (save-excursion
6601 (gnus-group-best-unread-group exclude-group))))
6602
23f87bed
MB
6603(defun gnus-summary-find-next (&optional unread article backward)
6604 (if backward
6605 (gnus-summary-find-prev unread article)
eec82323
LMI
6606 (let* ((dummy (gnus-summary-article-intangible-p))
6607 (article (or article (gnus-summary-article-number)))
23f87bed 6608 (data (gnus-data-find-list article))
eec82323
LMI
6609 result)
6610 (when (and (not dummy)
6611 (or (not gnus-summary-check-current)
6612 (not unread)
23f87bed
MB
6613 (not (gnus-data-unread-p (car data)))))
6614 (setq data (cdr data)))
eec82323
LMI
6615 (when (setq result
6616 (if unread
6617 (progn
23f87bed
MB
6618 (while data
6619 (unless (memq (gnus-data-number (car data))
6620 (cond
6621 ((eq gnus-auto-goto-ignores
6622 'always-undownloaded)
6623 gnus-newsgroup-undownloaded)
6624 (gnus-plugged
6625 nil)
6626 ((eq gnus-auto-goto-ignores
6627 'unfetched)
6628 gnus-newsgroup-unfetched)
6629 ((eq gnus-auto-goto-ignores
6630 'undownloaded)
6631 gnus-newsgroup-undownloaded)))
6632 (when (gnus-data-unread-p (car data))
6633 (setq result (car data)
6634 data nil)))
6635 (setq data (cdr data)))
eec82323 6636 result)
23f87bed 6637 (car data)))
eec82323
LMI
6638 (goto-char (gnus-data-pos result))
6639 (gnus-data-number result)))))
6640
6641(defun gnus-summary-find-prev (&optional unread article)
6642 (let* ((eobp (eobp))
6643 (article (or article (gnus-summary-article-number)))
23f87bed 6644 (data (gnus-data-find-list article (gnus-data-list 'rev)))
eec82323
LMI
6645 result)
6646 (when (and (not eobp)
6647 (or (not gnus-summary-check-current)
6648 (not unread)
23f87bed
MB
6649 (not (gnus-data-unread-p (car data)))))
6650 (setq data (cdr data)))
eec82323
LMI
6651 (when (setq result
6652 (if unread
6653 (progn
23f87bed
MB
6654 (while data
6655 (unless (memq (gnus-data-number (car data))
6656 (cond
6657 ((eq gnus-auto-goto-ignores
6658 'always-undownloaded)
6659 gnus-newsgroup-undownloaded)
6660 (gnus-plugged
6661 nil)
6662 ((eq gnus-auto-goto-ignores
6663 'unfetched)
6664 gnus-newsgroup-unfetched)
6665 ((eq gnus-auto-goto-ignores
6666 'undownloaded)
6667 gnus-newsgroup-undownloaded)))
6668 (when (gnus-data-unread-p (car data))
6669 (setq result (car data)
6670 data nil)))
6671 (setq data (cdr data)))
eec82323 6672 result)
23f87bed 6673 (car data)))
eec82323
LMI
6674 (goto-char (gnus-data-pos result))
6675 (gnus-data-number result))))
6676
6677(defun gnus-summary-find-subject (subject &optional unread backward article)
6678 (let* ((simp-subject (gnus-simplify-subject-fully subject))
6679 (article (or article (gnus-summary-article-number)))
6680 (articles (gnus-data-list backward))
6681 (arts (gnus-data-find-list article articles))
6682 result)
6683 (when (or (not gnus-summary-check-current)
6684 (not unread)
6685 (not (gnus-data-unread-p (car arts))))
6686 (setq arts (cdr arts)))
6687 (while arts
6688 (and (or (not unread)
6689 (gnus-data-unread-p (car arts)))
6690 (vectorp (gnus-data-header (car arts)))
6691 (gnus-subject-equal
6692 simp-subject (mail-header-subject (gnus-data-header (car arts))) t)
6693 (setq result (car arts)
6694 arts nil))
6695 (setq arts (cdr arts)))
6696 (and result
6697 (goto-char (gnus-data-pos result))
6698 (gnus-data-number result))))
6699
6700(defun gnus-summary-search-forward (&optional unread subject backward)
6701 "Search forward for an article.
6702If UNREAD, look for unread articles. If SUBJECT, look for
6703articles with that subject. If BACKWARD, search backward instead."
6704 (cond (subject (gnus-summary-find-subject subject unread backward))
6705 (backward (gnus-summary-find-prev unread))
6706 (t (gnus-summary-find-next unread))))
6707
6708(defun gnus-recenter (&optional n)
6709 "Center point in window and redisplay frame.
6710Also do horizontal recentering."
6711 (interactive "P")
6712 (when (and gnus-auto-center-summary
6713 (not (eq gnus-auto-center-summary 'vertical)))
6714 (gnus-horizontal-recenter))
5aa75bd8
JL
6715 (if (fboundp 'recenter-top-bottom)
6716 (recenter-top-bottom n)
6717 (recenter n)))
6718
6719(put 'gnus-recenter 'isearch-scroll t)
eec82323 6720
7b47345b
AS
6721(defun gnus-forward-line-ignore-invisible (n)
6722 "Move N lines forward (backward if N is negative).
6723Like forward-line, but skip over (and don't count) invisible lines."
6724 (let (done)
6725 (while (and (> n 0) (not done))
6726 ;; If the following character is currently invisible,
6727 ;; skip all characters with that same `invisible' property value.
770d9a1f
KY
6728 (while (gnus-invisible-p (point))
6729 (goto-char (gnus-next-char-property-change (point))))
7b47345b
AS
6730 (forward-line 1)
6731 (if (eobp)
6732 (setq done t)
6733 (setq n (1- n))))
6734 (while (and (< n 0) (not done))
6735 (forward-line -1)
6736 (if (bobp) (setq done t)
6737 (setq n (1+ n))
770d9a1f
KY
6738 (while (and (not (bobp)) (gnus-invisible-p (1- (point))))
6739 (goto-char (gnus-previous-char-property-change (point))))))))
6740
eec82323
LMI
6741(defun gnus-summary-recenter ()
6742 "Center point in the summary window.
6743If `gnus-auto-center-summary' is nil, or the article buffer isn't
6744displayed, no centering will be performed."
6745 ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
6746 ;; Recenter only when requested. Suggested by popovich@park.cs.columbia.edu.
16409b0b 6747 (interactive)
23f87bed
MB
6748 ;; The user has to want it.
6749 (when gnus-auto-center-summary
6750 (let* ((top (cond ((< (window-height) 4) 0)
6751 ((< (window-height) 7) 1)
6752 (t (if (numberp gnus-auto-center-summary)
6753 gnus-auto-center-summary
01c52d31 6754 (/ (1- (window-height)) 2)))))
23f87bed 6755 (height (1- (window-height)))
7b47345b
AS
6756 (bottom (save-excursion
6757 (goto-char (point-max))
6758 (gnus-forward-line-ignore-invisible (- height))
6759 (point)))
23f87bed 6760 (window (get-buffer-window (current-buffer))))
eec82323
LMI
6761 (when (get-buffer-window gnus-article-buffer)
6762 ;; Only do recentering when the article buffer is displayed,
6763 ;; Set the window start to either `bottom', which is the biggest
6764 ;; possible valid number, or the second line from the top,
6765 ;; whichever is the least.
7b47345b
AS
6766 (let ((top-pos (save-excursion
6767 (gnus-forward-line-ignore-invisible (- top))
6768 (point))))
db7ebd73
MB
6769 (if (> bottom top-pos)
6770 ;; Keep the second line from the top visible
01c52d31 6771 (set-window-start window top-pos)
db7ebd73
MB
6772 ;; Try to keep the bottom line visible; if it's partially
6773 ;; obscured, either scroll one more line to make it fully
6774 ;; visible, or revert to using TOP-POS.
6775 (save-excursion
6776 (goto-char (point-max))
7b47345b 6777 (gnus-forward-line-ignore-invisible -1)
db7ebd73
MB
6778 (let ((last-line-start (point)))
6779 (goto-char bottom)
6780 (set-window-start window (point) t)
6781 (when (not (pos-visible-in-window-p last-line-start window))
7b47345b 6782 (gnus-forward-line-ignore-invisible 1)
db7ebd73 6783 (set-window-start window (min (point) top-pos) t)))))))
eec82323
LMI
6784 ;; Do horizontal recentering while we're at it.
6785 (when (and (get-buffer-window (current-buffer) t)
6786 (not (eq gnus-auto-center-summary 'vertical)))
6787 (let ((selected (selected-window)))
6788 (select-window (get-buffer-window (current-buffer) t))
6789 (gnus-summary-position-point)
6790 (gnus-horizontal-recenter)
6791 (select-window selected))))))
6792
6793(defun gnus-summary-jump-to-group (newsgroup)
6794 "Move point to NEWSGROUP in group mode buffer."
6795 ;; Keep update point of group mode buffer if visible.
6796 (if (eq (current-buffer) (get-buffer gnus-group-buffer))
6797 (save-window-excursion
6798 ;; Take care of tree window mode.
6799 (when (get-buffer-window gnus-group-buffer)
6800 (pop-to-buffer gnus-group-buffer))
6801 (gnus-group-jump-to-group newsgroup))
6802 (save-excursion
6803 ;; Take care of tree window mode.
c7a91ce1 6804 (if (get-buffer-window gnus-group-buffer 0)
eec82323
LMI
6805 (pop-to-buffer gnus-group-buffer)
6806 (set-buffer gnus-group-buffer))
6807 (gnus-group-jump-to-group newsgroup))))
6808
6809;; This function returns a list of article numbers based on the
6810;; difference between the ranges of read articles in this group and
6811;; the range of active articles.
6812(defun gnus-list-of-unread-articles (group)
6813 (let* ((read (gnus-info-read (gnus-get-info group)))
6814 (active (or (gnus-active group) (gnus-activate-group group)))
01c52d31
MB
6815 (last (or (cdr active)
6816 (error "Group %s couldn't be activated " group)))
4b70e299
MB
6817 (bottom (if gnus-newsgroup-maximum-articles
6818 (max (car active)
6819 (- last gnus-newsgroup-maximum-articles -1))
11abff8e 6820 (car active)))
eec82323
LMI
6821 first nlast unread)
6822 ;; If none are read, then all are unread.
6823 (if (not read)
11abff8e 6824 (setq first bottom)
eec82323
LMI
6825 ;; If the range of read articles is a single range, then the
6826 ;; first unread article is the article after the last read
6827 ;; article. Sounds logical, doesn't it?
16409b0b 6828 (if (and (not (listp (cdr read)))
11abff8e 6829 (or (< (car read) bottom)
16409b0b
GM
6830 (progn (setq read (list read))
6831 nil)))
11abff8e 6832 (setq first (max bottom (1+ (cdr read))))
eec82323
LMI
6833 ;; `read' is a list of ranges.
6834 (when (/= (setq nlast (or (and (numberp (car read)) (car read))
6835 (caar read)))
6836 1)
11abff8e 6837 (setq first bottom))
eec82323
LMI
6838 (while read
6839 (when first
6840 (while (< first nlast)
54506618
MB
6841 (setq unread (cons first unread)
6842 first (1+ first))))
eec82323
LMI
6843 (setq first (1+ (if (atom (car read)) (car read) (cdar read))))
6844 (setq nlast (if (atom (cadr read)) (cadr read) (caadr read)))
6845 (setq read (cdr read)))))
6846 ;; And add the last unread articles.
6847 (while (<= first last)
54506618
MB
6848 (setq unread (cons first unread)
6849 first (1+ first)))
eec82323 6850 ;; Return the list of unread articles.
6748645f 6851 (delq 0 (nreverse unread))))
eec82323
LMI
6852
6853(defun gnus-list-of-read-articles (group)
6854 "Return a list of unread, unticked and non-dormant articles."
6855 (let* ((info (gnus-get-info group))
6856 (marked (gnus-info-marks info))
6857 (active (gnus-active group)))
6858 (and info active
23f87bed
MB
6859 (gnus-list-range-difference
6860 (gnus-list-range-difference
6861 (gnus-sorted-complement
11abff8e 6862 (gnus-uncompress-range
4b70e299 6863 (if gnus-newsgroup-maximum-articles
11abff8e 6864 (cons (max (car active)
4b70e299
MB
6865 (- (cdr active)
6866 gnus-newsgroup-maximum-articles
6867 -1))
11abff8e
MB
6868 (cdr active))
6869 active))
23f87bed
MB
6870 (gnus-list-of-unread-articles group))
6871 (cdr (assq 'dormant marked)))
6872 (cdr (assq 'tick marked))))))
eec82323 6873
54506618
MB
6874;; This function returns a sequence of article numbers based on the
6875;; difference between the ranges of read articles in this group and
6876;; the range of active articles.
6877(defun gnus-sequence-of-unread-articles (group)
6878 (let* ((read (gnus-info-read (gnus-get-info group)))
6879 (active (or (gnus-active group) (gnus-activate-group group)))
6880 (last (cdr active))
4b70e299
MB
6881 (bottom (if gnus-newsgroup-maximum-articles
6882 (max (car active)
6883 (- last gnus-newsgroup-maximum-articles -1))
11abff8e 6884 (car active)))
54506618
MB
6885 first nlast unread)
6886 ;; If none are read, then all are unread.
6887 (if (not read)
11abff8e 6888 (setq first bottom)
54506618
MB
6889 ;; If the range of read articles is a single range, then the
6890 ;; first unread article is the article after the last read
6891 ;; article. Sounds logical, doesn't it?
6892 (if (and (not (listp (cdr read)))
11abff8e 6893 (or (< (car read) bottom)
54506618
MB
6894 (progn (setq read (list read))
6895 nil)))
11abff8e 6896 (setq first (max bottom (1+ (cdr read))))
54506618
MB
6897 ;; `read' is a list of ranges.
6898 (when (/= (setq nlast (or (and (numberp (car read)) (car read))
6899 (caar read)))
6900 1)
11abff8e 6901 (setq first bottom))
54506618
MB
6902 (while read
6903 (when first
6904 (push (cons first nlast) unread))
6905 (setq first (1+ (if (atom (car read)) (car read) (cdar read))))
6906 (setq nlast (if (atom (cadr read)) (cadr read) (caadr read)))
6907 (setq read (cdr read)))))
6908 ;; And add the last unread articles.
ba0226dd
MB
6909 (cond ((not (and first last))
6910 nil)
6911 ((< first last)
6912 (push (cons first last) unread))
6913 ((= first last)
6914 (push first unread)))
54506618
MB
6915 ;; Return the sequence of unread articles.
6916 (delq 0 (nreverse unread))))
6917
eec82323
LMI
6918;; Various summary commands
6919
6748645f
LMI
6920(defun gnus-summary-select-article-buffer ()
6921 "Reconfigure windows to show article buffer."
6922 (interactive)
6923 (if (not (gnus-buffer-live-p gnus-article-buffer))
6924 (error "There is no article buffer for this summary buffer")
6925 (gnus-configure-windows 'article)
6926 (select-window (get-buffer-window gnus-article-buffer))))
6927
eec82323
LMI
6928(defun gnus-summary-universal-argument (arg)
6929 "Perform any operation on all articles that are process/prefixed."
6930 (interactive "P")
eec82323
LMI
6931 (let ((articles (gnus-summary-work-articles arg))
6932 func article)
6933 (if (eq
6934 (setq
6935 func
6936 (key-binding
6937 (read-key-sequence
6938 (substitute-command-keys
16409b0b 6939 "\\<gnus-summary-mode-map>\\[gnus-summary-universal-argument]"))))
eec82323
LMI
6940 'undefined)
6941 (gnus-error 1 "Undefined key")
6942 (save-excursion
6943 (while articles
6944 (gnus-summary-goto-subject (setq article (pop articles)))
6945 (let (gnus-newsgroup-processable)
6946 (command-execute func))
6947 (gnus-summary-remove-process-mark article)))))
6948 (gnus-summary-position-point))
6949
6950(defun gnus-summary-toggle-truncation (&optional arg)
6951 "Toggle truncation of summary lines.
23f87bed 6952With ARG, turn line truncation on if ARG is positive."
eec82323
LMI
6953 (interactive "P")
6954 (setq truncate-lines
6955 (if (null arg) (not truncate-lines)
6956 (> (prefix-numeric-value arg) 0)))
6957 (redraw-display))
6958
23f87bed
MB
6959(defun gnus-summary-find-for-reselect ()
6960 "Return the number of an article to stay on across a reselect.
6961The current article is considered, then following articles, then previous
6962articles. An article is sought which is not cancelled and isn't a temporary
6963insertion from another group. If there's no such then return a dummy 0."
6964 (let (found)
6965 (dolist (rev '(nil t))
6966 (unless found ; don't demand the reverse list if we don't need it
6967 (let ((data (gnus-data-find-list
6968 (gnus-summary-article-number) (gnus-data-list rev))))
6969 (while (and data (not found))
6970 (if (and (< 0 (gnus-data-number (car data)))
6971 (not (eq gnus-canceled-mark (gnus-data-mark (car data)))))
6972 (setq found (gnus-data-number (car data))))
6973 (setq data (cdr data))))))
6974 (or found 0)))
6975
eec82323
LMI
6976(defun gnus-summary-reselect-current-group (&optional all rescan)
6977 "Exit and then reselect the current newsgroup.
6978The prefix argument ALL means to select all articles."
6979 (interactive "P")
eec82323
LMI
6980 (when (gnus-ephemeral-group-p gnus-newsgroup-name)
6981 (error "Ephemeral groups can't be reselected"))
23f87bed 6982 (let ((current-subject (gnus-summary-find-for-reselect))
eec82323
LMI
6983 (group gnus-newsgroup-name))
6984 (setq gnus-newsgroup-begin nil)
23f87bed 6985 (gnus-summary-exit nil 'leave-hidden)
eec82323
LMI
6986 ;; We have to adjust the point of group mode buffer because
6987 ;; point was moved to the next unread newsgroup by exiting.
6988 (gnus-summary-jump-to-group group)
6989 (when rescan
6990 (save-excursion
6991 (gnus-group-get-new-news-this-group 1)))
6992 (gnus-group-read-group all t)
6993 (gnus-summary-goto-subject current-subject nil t)))
6994
6995(defun gnus-summary-rescan-group (&optional all)
6996 "Exit the newsgroup, ask for new articles, and select the newsgroup."
6997 (interactive "P")
6998 (gnus-summary-reselect-current-group all t))
6999
7000(defun gnus-summary-update-info (&optional non-destructive)
7001 (save-excursion
7002 (let ((group gnus-newsgroup-name))
6748645f
LMI
7003 (when group
7004 (when gnus-newsgroup-kill-headers
7005 (setq gnus-newsgroup-killed
7006 (gnus-compress-sequence
23f87bed
MB
7007 (gnus-sorted-union
7008 (gnus-list-range-intersection
7009 gnus-newsgroup-unselected gnus-newsgroup-killed)
7010 gnus-newsgroup-unreads)
6748645f
LMI
7011 t)))
7012 (unless (listp (cdr gnus-newsgroup-killed))
7013 (setq gnus-newsgroup-killed (list gnus-newsgroup-killed)))
7014 (let ((headers gnus-newsgroup-headers))
7015 ;; Set the new ranges of read articles.
01c52d31 7016 (with-current-buffer gnus-group-buffer
6748645f
LMI
7017 (gnus-undo-force-boundary))
7018 (gnus-update-read-articles
23f87bed
MB
7019 group (gnus-sorted-union
7020 gnus-newsgroup-unreads gnus-newsgroup-unselected))
6748645f
LMI
7021 ;; Set the current article marks.
7022 (let ((gnus-newsgroup-scored
7023 (if (and (not gnus-save-score)
7024 (not non-destructive))
7025 nil
7026 gnus-newsgroup-scored)))
7027 (save-excursion
7028 (gnus-update-marks)))
7029 ;; Do the cross-ref thing.
7030 (when gnus-use-cross-reference
7031 (gnus-mark-xrefs-as-read group headers gnus-newsgroup-unreads))
7032 ;; Do not switch windows but change the buffer to work.
a8151ef7 7033 (set-buffer gnus-group-buffer)
6748645f
LMI
7034 (unless (gnus-ephemeral-group-p group)
7035 (gnus-group-update-group group)))))))
eec82323
LMI
7036
7037(defun gnus-summary-save-newsrc (&optional force)
7038 "Save the current number of read/marked articles in the dribble buffer.
7039The dribble buffer will then be saved.
7040If FORCE (the prefix), also save the .newsrc file(s)."
7041 (interactive "P")
7042 (gnus-summary-update-info t)
7043 (if force
7044 (gnus-save-newsrc-file)
7045 (gnus-dribble-save)))
7046
704f1663
GM
7047(declare-function gnus-cache-write-active "gnus-cache" (&optional force))
7048
23f87bed 7049(defun gnus-summary-exit (&optional temporary leave-hidden)
eec82323 7050 "Exit reading current newsgroup, and then return to group selection mode.
16409b0b 7051`gnus-exit-group-hook' is called with no arguments if that value is non-nil."
eec82323
LMI
7052 (interactive)
7053 (gnus-set-global-variables)
16409b0b 7054 (when (gnus-buffer-live-p gnus-article-buffer)
c7a91ce1 7055 (with-current-buffer gnus-article-buffer
16409b0b
GM
7056 (mm-destroy-parts gnus-article-mime-handles)
7057 ;; Set it to nil for safety reason.
7058 (setq gnus-article-mime-handle-alist nil)
7059 (setq gnus-article-mime-handles nil)))
eec82323 7060 (gnus-kill-save-kill-buffer)
6748645f 7061 (gnus-async-halt-prefetch)
eec82323
LMI
7062 (let* ((group gnus-newsgroup-name)
7063 (quit-config (gnus-group-quit-config gnus-newsgroup-name))
23f87bed 7064 (gnus-group-is-exiting-p t)
eec82323 7065 (mode major-mode)
23f87bed 7066 (group-point nil)
eec82323 7067 (buf (current-buffer)))
16409b0b
GM
7068 (unless quit-config
7069 ;; Do adaptive scoring, and possibly save score files.
7070 (when gnus-newsgroup-adaptive
7071 (gnus-score-adaptive))
7072 (when gnus-use-scoring
7073 (gnus-score-save)))
6748645f 7074 (gnus-run-hooks 'gnus-summary-prepare-exit-hook)
eec82323
LMI
7075 ;; If we have several article buffers, we kill them at exit.
7076 (unless gnus-single-article-buffer
01c52d31
MB
7077 (when (gnus-buffer-live-p gnus-article-buffer)
7078 (with-current-buffer gnus-article-buffer
7079 ;; Don't kill sticky article buffers
7080 (unless (eq major-mode 'gnus-sticky-article-mode)
7081 (gnus-kill-buffer gnus-article-buffer)
7082 (setq gnus-article-current nil))))
7083 (gnus-kill-buffer gnus-original-article-buffer))
eec82323
LMI
7084 (when gnus-use-cache
7085 (gnus-cache-possibly-remove-articles)
7086 (gnus-cache-save-buffers))
7087 (gnus-async-prefetch-remove-group group)
7088 (when gnus-suppress-duplicates
7089 (gnus-dup-enter-articles))
7090 (when gnus-use-trees
7091 (gnus-tree-close group))
16409b0b
GM
7092 (when gnus-use-cache
7093 (gnus-cache-write-active))
6748645f
LMI
7094 ;; Remove entries for this group.
7095 (nnmail-purge-split-history (gnus-group-real-name group))
eec82323
LMI
7096 ;; Make all changes in this group permanent.
7097 (unless quit-config
6748645f 7098 (gnus-run-hooks 'gnus-exit-group-hook)
16409b0b 7099 (gnus-summary-update-info))
eec82323
LMI
7100 (gnus-close-group group)
7101 ;; Make sure where we were, and go to next newsgroup.
7102 (set-buffer gnus-group-buffer)
7103 (unless quit-config
7104 (gnus-group-jump-to-group group))
6748645f
LMI
7105 (gnus-run-hooks 'gnus-summary-exit-hook)
7106 (unless (or quit-config
01c52d31 7107 (not gnus-summary-next-group-on-exit)
6748645f
LMI
7108 ;; If this group has disappeared from the summary
7109 ;; buffer, don't skip forwards.
7110 (not (string= group (gnus-group-group-name))))
eec82323 7111 (gnus-group-next-unread-group 1))
a8151ef7 7112 (setq group-point (point))
eec82323
LMI
7113 (if temporary
7114 nil ;Nothing to do.
eec82323
LMI
7115 (set-buffer buf)
7116 (if (not gnus-kill-summary-on-exit)
23f87bed
MB
7117 (progn
7118 (gnus-deaden-summary)
7119 (setq mode nil))
eec82323
LMI
7120 ;; We set all buffer-local variables to nil. It is unclear why
7121 ;; this is needed, but if we don't, buffer-local variables are
7122 ;; not garbage-collected, it seems. This would the lead to en
7123 ;; ever-growing Emacs.
7124 (gnus-summary-clear-local-variables)
23f87bed
MB
7125 (let ((gnus-summary-local-variables gnus-newsgroup-variables))
7126 (gnus-summary-clear-local-variables))
eec82323
LMI
7127 (when (get-buffer gnus-article-buffer)
7128 (bury-buffer gnus-article-buffer))
eec82323
LMI
7129 ;; Return to group mode buffer.
7130 (when (eq mode 'gnus-summary-mode)
7131 (gnus-kill-buffer buf)))
7132 (setq gnus-current-select-method gnus-select-method)
d61c212b
SM
7133 (set-buffer gnus-group-buffer)
7134 (if quit-config
7135 (gnus-handle-ephemeral-exit quit-config)
4e90f2b9
SM
7136 (goto-char group-point)
7137 ;; If gnus-group-buffer is already displayed, make sure we also move
7138 ;; the cursor in the window that displays it.
7139 (let ((win (get-buffer-window (current-buffer) 0)))
7140 (if win (set-window-point win (point))))
d61c212b 7141 (unless leave-hidden
4e90f2b9 7142 (gnus-configure-windows 'group 'force)))
6748645f 7143 ;; Clear the current group name.
eec82323
LMI
7144 (unless quit-config
7145 (setq gnus-newsgroup-name nil)))))
7146
7147(defalias 'gnus-summary-quit 'gnus-summary-exit-no-update)
7148(defun gnus-summary-exit-no-update (&optional no-questions)
7149 "Quit reading current newsgroup without updating read article info."
7150 (interactive)
eec82323 7151 (let* ((group gnus-newsgroup-name)
23f87bed
MB
7152 (gnus-group-is-exiting-p t)
7153 (gnus-group-is-exiting-without-update-p t)
eec82323
LMI
7154 (quit-config (gnus-group-quit-config group)))
7155 (when (or no-questions
7156 gnus-expert-user
7157 (gnus-y-or-n-p "Discard changes to this group and exit? "))
6748645f 7158 (gnus-async-halt-prefetch)
23f87bed 7159 (run-hooks 'gnus-summary-prepare-exit-hook)
16409b0b 7160 (when (gnus-buffer-live-p gnus-article-buffer)
c7a91ce1 7161 (with-current-buffer gnus-article-buffer
16409b0b
GM
7162 (mm-destroy-parts gnus-article-mime-handles)
7163 ;; Set it to nil for safety reason.
7164 (setq gnus-article-mime-handle-alist nil)
7165 (setq gnus-article-mime-handles nil)))
eec82323
LMI
7166 ;; If we have several article buffers, we kill them at exit.
7167 (unless gnus-single-article-buffer
7168 (gnus-kill-buffer gnus-article-buffer)
7169 (gnus-kill-buffer gnus-original-article-buffer)
7170 (setq gnus-article-current nil))
7171 (if (not gnus-kill-summary-on-exit)
7172 (gnus-deaden-summary)
7173 (gnus-close-group group)
7174 (gnus-summary-clear-local-variables)
23f87bed
MB
7175 (let ((gnus-summary-local-variables gnus-newsgroup-variables))
7176 (gnus-summary-clear-local-variables))
7177 (gnus-kill-buffer gnus-summary-buffer))
eec82323
LMI
7178 (unless gnus-single-article-buffer
7179 (setq gnus-article-current nil))
7180 (when gnus-use-trees
7181 (gnus-tree-close group))
7182 (gnus-async-prefetch-remove-group group)
7183 (when (get-buffer gnus-article-buffer)
7184 (bury-buffer gnus-article-buffer))
7185 ;; Return to the group buffer.
7186 (gnus-configure-windows 'group 'force)
7187 ;; Clear the current group name.
7188 (setq gnus-newsgroup-name nil)
23f87bed
MB
7189 (unless (gnus-ephemeral-group-p group)
7190 (gnus-group-update-group group))
eec82323
LMI
7191 (when (equal (gnus-group-group-name) group)
7192 (gnus-group-next-unread-group 1))
7193 (when quit-config
23f87bed 7194 (gnus-handle-ephemeral-exit quit-config)))))
eec82323
LMI
7195
7196(defun gnus-handle-ephemeral-exit (quit-config)
6748645f
LMI
7197 "Handle movement when leaving an ephemeral group.
7198The state which existed when entering the ephemeral is reset."
eec82323
LMI
7199 (if (not (buffer-name (car quit-config)))
7200 (gnus-configure-windows 'group 'force)
7201 (set-buffer (car quit-config))
7202 (cond ((eq major-mode 'gnus-summary-mode)
23f87bed
MB
7203 (gnus-set-global-variables))
7204 ((eq major-mode 'gnus-article-mode)
c7a91ce1 7205 (save-current-buffer
23f87bed
MB
7206 ;; The `gnus-summary-buffer' variable may point
7207 ;; to the old summary buffer when using a single
7208 ;; article buffer.
7209 (unless (gnus-buffer-live-p gnus-summary-buffer)
7210 (set-buffer gnus-group-buffer))
7211 (set-buffer gnus-summary-buffer)
7212 (gnus-set-global-variables))))
eec82323 7213 (if (or (eq (cdr quit-config) 'article)
23f87bed 7214 (eq (cdr quit-config) 'pick))
01c52d31
MB
7215 (if (and (boundp 'gnus-pick-mode) (symbol-value 'gnus-pick-mode))
7216 (gnus-configure-windows 'pick 'force)
7217 (gnus-configure-windows (cdr quit-config) 'force))
eec82323
LMI
7218 (gnus-configure-windows (cdr quit-config) 'force))
7219 (when (eq major-mode 'gnus-summary-mode)
01c52d31
MB
7220 (if (memq gnus-auto-select-on-ephemeral-exit '(next-noselect
7221 next-unread-noselect))
7222 (when (zerop (cond ((eq gnus-auto-select-on-ephemeral-exit
7223 'next-noselect)
7224 (gnus-summary-next-subject 1 nil t))
7225 ((eq gnus-auto-select-on-ephemeral-exit
7226 'next-unread-noselect)
7227 (gnus-summary-next-subject 1 t t))))
7228 ;; Hide the article buffer which displays the article different
7229 ;; from the one that the cursor points to in the summary buffer.
7230 (gnus-configure-windows 'summary 'force))
7231 (cond ((eq gnus-auto-select-on-ephemeral-exit 'next)
7232 (gnus-summary-next-subject 1))
7233 ((eq gnus-auto-select-on-ephemeral-exit 'next-unread)
7234 (gnus-summary-next-subject 1 t))))
eec82323
LMI
7235 (gnus-summary-recenter)
7236 (gnus-summary-position-point))))
7237
7238;;; Dead summaries.
7239
7240(defvar gnus-dead-summary-mode-map nil)
7241
7242(unless gnus-dead-summary-mode-map
7243 (setq gnus-dead-summary-mode-map (make-keymap))
7244 (suppress-keymap gnus-dead-summary-mode-map)
7245 (substitute-key-definition
7246 'undefined 'gnus-summary-wake-up-the-dead gnus-dead-summary-mode-map)
23f87bed
MB
7247 (dolist (key '("\C-d" "\r" "\177" [delete]))
7248 (define-key gnus-dead-summary-mode-map
7249 key 'gnus-summary-wake-up-the-dead))
7250 (dolist (key '("q" "Q"))
7251 (define-key gnus-dead-summary-mode-map key 'bury-buffer)))
eec82323
LMI
7252
7253(defvar gnus-dead-summary-mode nil
7254 "Minor mode for Gnus summary buffers.")
7255
7256(defun gnus-dead-summary-mode (&optional arg)
7257 "Minor mode for Gnus summary buffers."
7258 (interactive "P")
7259 (when (eq major-mode 'gnus-summary-mode)
7260 (make-local-variable 'gnus-dead-summary-mode)
7261 (setq gnus-dead-summary-mode
7262 (if (null arg) (not gnus-dead-summary-mode)
7263 (> (prefix-numeric-value arg) 0)))
7264 (when gnus-dead-summary-mode
01c52d31 7265 (add-minor-mode
a8151ef7 7266 'gnus-dead-summary-mode " Dead" gnus-dead-summary-mode-map))))
eec82323
LMI
7267
7268(defun gnus-deaden-summary ()
7269 "Make the current summary buffer into a dead summary buffer."
7270 ;; Kill any previous dead summary buffer.
7271 (when (and gnus-dead-summary
7272 (buffer-name gnus-dead-summary))
01c52d31 7273 (with-current-buffer gnus-dead-summary
eec82323
LMI
7274 (when gnus-dead-summary-mode
7275 (kill-buffer (current-buffer)))))
7276 ;; Make this the current dead summary.
7277 (setq gnus-dead-summary (current-buffer))
7278 (gnus-dead-summary-mode 1)
7279 (let ((name (buffer-name)))
7280 (when (string-match "Summary" name)
7281 (rename-buffer
7282 (concat (substring name 0 (match-beginning 0)) "Dead "
7283 (substring name (match-beginning 0)))
16409b0b
GM
7284 t)
7285 (bury-buffer))))
eec82323
LMI
7286
7287(defun gnus-kill-or-deaden-summary (buffer)
7288 "Kill or deaden the summary BUFFER."
6748645f
LMI
7289 (save-excursion
7290 (when (and (buffer-name buffer)
7291 (not gnus-single-article-buffer))
01c52d31 7292 (with-current-buffer buffer
6748645f
LMI
7293 (gnus-kill-buffer gnus-article-buffer)
7294 (gnus-kill-buffer gnus-original-article-buffer)))
23f87bed
MB
7295 (cond
7296 ;; Kill the buffer.
7297 (gnus-kill-summary-on-exit
7298 (when (and gnus-use-trees
7299 (gnus-buffer-exists-p buffer))
c7a91ce1 7300 (with-current-buffer buffer
23f87bed
MB
7301 (gnus-tree-close gnus-newsgroup-name)))
7302 (gnus-kill-buffer buffer))
7303 ;; Deaden the buffer.
7304 ((gnus-buffer-exists-p buffer)
c7a91ce1 7305 (with-current-buffer buffer
23f87bed 7306 (gnus-deaden-summary))))))
eec82323
LMI
7307
7308(defun gnus-summary-wake-up-the-dead (&rest args)
7309 "Wake up the dead summary buffer."
7310 (interactive)
7311 (gnus-dead-summary-mode -1)
7312 (let ((name (buffer-name)))
7313 (when (string-match "Dead " name)
7314 (rename-buffer
7315 (concat (substring name 0 (match-beginning 0))
7316 (substring name (match-end 0)))
7317 t)))
7318 (gnus-message 3 "This dead summary is now alive again"))
7319
7320;; Suggested by Andrew Eskilsson <pi92ae@pt.hk-r.se>.
7321(defun gnus-summary-fetch-faq (&optional faq-dir)
7322 "Fetch the FAQ for the current group.
7323If FAQ-DIR (the prefix), prompt for a directory to search for the faq
7324in."
7325 (interactive
7326 (list
7327 (when current-prefix-arg
7328 (completing-read
8f688cb0 7329 "FAQ dir: " (and (listp gnus-group-faq-directory)
01c52d31 7330 (mapcar 'list
a8151ef7 7331 gnus-group-faq-directory))))))
eec82323
LMI
7332 (let (gnus-faq-buffer)
7333 (when (setq gnus-faq-buffer
7334 (gnus-group-fetch-faq gnus-newsgroup-name faq-dir))
7335 (gnus-configure-windows 'summary-faq))))
7336
7337;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
7338(defun gnus-summary-describe-group (&optional force)
7339 "Describe the current newsgroup."
7340 (interactive "P")
7341 (gnus-group-describe-group force gnus-newsgroup-name))
7342
7343(defun gnus-summary-describe-briefly ()
7344 "Describe summary mode commands briefly."
7345 (interactive)
16409b0b 7346 (gnus-message 6 (substitute-command-keys "\\<gnus-summary-mode-map>\\[gnus-summary-next-page]:Select \\[gnus-summary-next-unread-article]:Forward \\[gnus-summary-prev-unread-article]:Backward \\[gnus-summary-exit]:Exit \\[gnus-info-find-node]:Run Info \\[gnus-summary-describe-briefly]:This help")))
eec82323
LMI
7347
7348;; Walking around group mode buffer from summary mode.
7349
7350(defun gnus-summary-next-group (&optional no-article target-group backward)
7351 "Exit current newsgroup and then select next unread newsgroup.
7352If prefix argument NO-ARTICLE is non-nil, no article is selected
23f87bed 7353initially. If TARGET-GROUP, go to this group. If BACKWARD, go to
eec82323
LMI
7354previous group instead."
7355 (interactive "P")
eec82323
LMI
7356 ;; Stop pre-fetching.
7357 (gnus-async-halt-prefetch)
7358 (let ((current-group gnus-newsgroup-name)
7359 (current-buffer (current-buffer))
7360 entered)
7361 ;; First we semi-exit this group to update Xrefs and all variables.
7362 ;; We can't do a real exit, because the window conf must remain
7363 ;; the same in case the user is prompted for info, and we don't
7364 ;; want the window conf to change before that...
7365 (gnus-summary-exit t)
7366 (while (not entered)
7367 ;; Then we find what group we are supposed to enter.
7368 (set-buffer gnus-group-buffer)
7369 (gnus-group-jump-to-group current-group)
7370 (setq target-group
7371 (or target-group
7372 (if (eq gnus-keep-same-level 'best)
7373 (gnus-summary-best-group gnus-newsgroup-name)
7374 (gnus-summary-search-group backward gnus-keep-same-level))))
7375 (if (not target-group)
7376 ;; There are no further groups, so we return to the group
7377 ;; buffer.
7378 (progn
7379 (gnus-message 5 "Returning to the group buffer")
7380 (setq entered t)
7381 (when (gnus-buffer-live-p current-buffer)
7382 (set-buffer current-buffer)
7383 (gnus-summary-exit))
6748645f 7384 (gnus-run-hooks 'gnus-group-no-more-groups-hook))
eec82323
LMI
7385 ;; We try to enter the target group.
7386 (gnus-group-jump-to-group target-group)
7387 (let ((unreads (gnus-group-group-unread)))
7388 (if (and (or (eq t unreads)
7389 (and unreads (not (zerop unreads))))
23f87bed
MB
7390 (gnus-summary-read-group
7391 target-group nil no-article
7392 (and (buffer-name current-buffer) current-buffer)
7393 nil backward))
eec82323
LMI
7394 (setq entered t)
7395 (setq current-group target-group
7396 target-group nil)))))))
7397
7398(defun gnus-summary-prev-group (&optional no-article)
7399 "Exit current newsgroup and then select previous unread newsgroup.
7400If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
7401 (interactive "P")
7402 (gnus-summary-next-group no-article nil t))
7403
7404;; Walking around summary lines.
7405
23f87bed
MB
7406(defun gnus-summary-first-subject (&optional unread undownloaded unseen)
7407 "Go to the first subject satisfying any non-nil constraint.
7408If UNREAD is non-nil, the article should be unread.
7409If UNDOWNLOADED is non-nil, the article should be undownloaded.
7410If UNSEEN is non-nil, the article should be unseen.
7411Returns the article selected or nil if there are no matching articles."
eec82323 7412 (interactive "P")
23f87bed
MB
7413 (cond
7414 ;; Empty summary.
7415 ((null gnus-newsgroup-data)
7416 (gnus-message 3 "No articles in the group")
7417 nil)
7418 ;; Pick the first article.
7419 ((not (or unread undownloaded unseen))
7420 (goto-char (gnus-data-pos (car gnus-newsgroup-data)))
7421 (gnus-data-number (car gnus-newsgroup-data)))
7422 ;; Find the first unread article.
7423 (t
7424 (let ((data gnus-newsgroup-data))
7425 (while (and data
7426 (let ((num (gnus-data-number (car data))))
7427 (or (memq num gnus-newsgroup-unfetched)
7428 (not (or (and unread
7429 (memq num gnus-newsgroup-unreads))
7430 (and undownloaded
7431 (memq num gnus-newsgroup-undownloaded))
7432 (and unseen
7433 (memq num gnus-newsgroup-unseen)))))))
7434 (setq data (cdr data)))
7435 (prog1
7436 (if data
7437 (progn
7438 (goto-char (gnus-data-pos (car data)))
7439 (gnus-data-number (car data)))
7440 (gnus-message 3 "No more%s articles"
7441 (let* ((r (when unread " unread"))
7442 (d (when undownloaded " undownloaded"))
7443 (s (when unseen " unseen"))
7444 (l (delq nil (list r d s))))
7445 (cond ((= 3 (length l))
7446 (concat r "," d ", or" s))
7447 ((= 2 (length l))
7448 (concat (car l) ", or" (cadr l)))
7449 ((= 1 (length l))
7450 (car l))
7451 (t
7452 ""))))
7453 nil
7454 )
7455 (gnus-summary-position-point))))))
eec82323
LMI
7456
7457(defun gnus-summary-next-subject (n &optional unread dont-display)
7458 "Go to next N'th summary line.
7459If N is negative, go to the previous N'th subject line.
7460If UNREAD is non-nil, only unread articles are selected.
7461The difference between N and the actual number of steps taken is
7462returned."
7463 (interactive "p")
7464 (let ((backward (< n 0))
7465 (n (abs n)))
7466 (while (and (> n 0)
7467 (if backward
7468 (gnus-summary-find-prev unread)
7469 (gnus-summary-find-next unread)))
16409b0b
GM
7470 (unless (zerop (setq n (1- n)))
7471 (gnus-summary-show-thread)))
eec82323
LMI
7472 (when (/= 0 n)
7473 (gnus-message 7 "No more%s articles"
7474 (if unread " unread" "")))
7475 (unless dont-display
7476 (gnus-summary-recenter)
7477 (gnus-summary-position-point))
7478 n))
7479
7480(defun gnus-summary-next-unread-subject (n)
7481 "Go to next N'th unread summary line."
7482 (interactive "p")
7483 (gnus-summary-next-subject n t))
7484
7485(defun gnus-summary-prev-subject (n &optional unread)
7486 "Go to previous N'th summary line.
7487If optional argument UNREAD is non-nil, only unread article is selected."
7488 (interactive "p")
7489 (gnus-summary-next-subject (- n) unread))
7490
7491(defun gnus-summary-prev-unread-subject (n)
7492 "Go to previous N'th unread summary line."
7493 (interactive "p")
7494 (gnus-summary-next-subject (- n) t))
7495
23f87bed
MB
7496(defun gnus-summary-goto-subjects (articles)
7497 "Insert the subject header for ARTICLES in the current buffer."
7498 (save-excursion
7499 (dolist (article articles)
7500 (gnus-summary-goto-subject article t)))
7501 (gnus-summary-limit (append articles gnus-newsgroup-limit))
7502 (gnus-summary-position-point))
132cf96d 7503
eec82323 7504(defun gnus-summary-goto-subject (article &optional force silent)
d55fe5bb 7505 "Go to the subject line of ARTICLE.
eec82323
LMI
7506If FORCE, also allow jumping to articles not currently shown."
7507 (interactive "nArticle number: ")
23f87bed
MB
7508 (unless (numberp article)
7509 (error "Article %s is not a number" article))
eec82323
LMI
7510 (let ((b (point))
7511 (data (gnus-data-find article)))
7512 ;; We read in the article if we have to.
7513 (and (not data)
7514 force
6748645f
LMI
7515 (gnus-summary-insert-subject
7516 article
7517 (if (or (numberp force) (vectorp force)) force)
7518 t)
eec82323
LMI
7519 (setq data (gnus-data-find article)))
7520 (goto-char b)
7521 (if (not data)
7522 (progn
7523 (unless silent
7524 (gnus-message 3 "Can't find article %d" article))
7525 nil)
23f87bed
MB
7526 (let ((pt (gnus-data-pos data)))
7527 (goto-char pt)
7528 (gnus-summary-set-article-display-arrow pt))
6748645f 7529 (gnus-summary-position-point)
eec82323
LMI
7530 article)))
7531
7532;; Walking around summary lines with displaying articles.
7533
7534(defun gnus-summary-expand-window (&optional arg)
7535 "Make the summary buffer take up the entire Emacs frame.
7536Given a prefix, will force an `article' buffer configuration."
7537 (interactive "P")
eec82323
LMI
7538 (if arg
7539 (gnus-configure-windows 'article 'force)
7540 (gnus-configure-windows 'summary 'force)))
7541
7542(defun gnus-summary-display-article (article &optional all-header)
7543 "Display ARTICLE in article buffer."
01c52d31
MB
7544 (unless (and (gnus-buffer-live-p gnus-article-buffer)
7545 (with-current-buffer gnus-article-buffer
7546 (eq major-mode 'gnus-article-mode)))
7547 (gnus-article-setup-buffer))
eec82323 7548 (gnus-set-global-variables)
01c52d31
MB
7549 (with-current-buffer gnus-article-buffer
7550 (setq gnus-article-charset gnus-newsgroup-charset)
7551 (setq gnus-article-ignored-charsets gnus-newsgroup-ignored-charsets)
7552 (mm-enable-multibyte))
eec82323
LMI
7553 (if (null article)
7554 nil
7555 (prog1
7556 (if gnus-summary-display-article-function
7557 (funcall gnus-summary-display-article-function article all-header)
7558 (gnus-article-prepare article all-header))
6748645f 7559 (gnus-run-hooks 'gnus-select-article-hook)
eec82323
LMI
7560 (when (and gnus-current-article
7561 (not (zerop gnus-current-article)))
7562 (gnus-summary-goto-subject gnus-current-article))
7563 (gnus-summary-recenter)
7564 (when (and gnus-use-trees gnus-show-threads)
7565 (gnus-possibly-generate-tree article)
7566 (gnus-highlight-selected-tree article))
7567 ;; Successfully display article.
7568 (gnus-article-set-window-start
7569 (cdr (assq article gnus-newsgroup-bookmarks))))))
7570
7571(defun gnus-summary-select-article (&optional all-headers force pseudo article)
7572 "Select the current article.
7573If ALL-HEADERS is non-nil, show all header fields. If FORCE is
7574non-nil, the article will be re-fetched even if it already present in
7575the article buffer. If PSEUDO is non-nil, pseudo-articles will also
7576be displayed."
7577 ;; Make sure we are in the summary buffer to work around bbdb bug.
7578 (unless (eq major-mode 'gnus-summary-mode)
7579 (set-buffer gnus-summary-buffer))
7580 (let ((article (or article (gnus-summary-article-number)))
f0529b5b 7581 (all-headers (not (not all-headers))) ;Must be t or nil.
16409b0b 7582 gnus-summary-display-article-function)
eec82323
LMI
7583 (and (not pseudo)
7584 (gnus-summary-article-pseudo-p article)
a8151ef7 7585 (error "This is a pseudo-article"))
c7a91ce1 7586 (with-current-buffer gnus-summary-buffer
16409b0b
GM
7587 (if (or (and gnus-single-article-buffer
7588 (or (null gnus-current-article)
7589 (null gnus-article-current)
7590 (null (get-buffer gnus-article-buffer))
7591 (not (eq article (cdr gnus-article-current)))
7592 (not (equal (car gnus-article-current)
7593 gnus-newsgroup-name))))
7594 (and (not gnus-single-article-buffer)
7595 (or (null gnus-current-article)
7596 (not (eq gnus-current-article article))))
7597 force)
7598 ;; The requested article is different from the current article.
7599 (progn
16409b0b
GM
7600 (gnus-summary-display-article article all-headers)
7601 (when (gnus-buffer-live-p gnus-article-buffer)
23f87bed 7602 (with-current-buffer gnus-article-buffer
16409b0b 7603 (if (not gnus-article-decoded-p) ;; a local variable
87545352 7604 (mm-disable-multibyte))))
16409b0b
GM
7605 (gnus-article-set-window-start
7606 (cdr (assq article gnus-newsgroup-bookmarks)))
7607 article)
16409b0b 7608 'old))))
eec82323 7609
23f87bed
MB
7610(defun gnus-summary-force-verify-and-decrypt ()
7611 "Display buttons for signed/encrypted parts and verify/decrypt them."
7612 (interactive)
7613 (let ((mm-verify-option 'known)
7614 (mm-decrypt-option 'known)
7615 (gnus-article-emulate-mime t)
7616 (gnus-buttonized-mime-types (append (list "multipart/signed"
7617 "multipart/encrypted")
7618 gnus-buttonized-mime-types)))
7619 (gnus-summary-select-article nil 'force)))
7620
eec82323
LMI
7621(defun gnus-summary-set-current-mark (&optional current-mark)
7622 "Obsolete function."
7623 nil)
7624
7625(defun gnus-summary-next-article (&optional unread subject backward push)
7626 "Select the next article.
7627If UNREAD, only unread articles are selected.
7628If SUBJECT, only articles with SUBJECT are selected.
7629If BACKWARD, the previous article is selected instead of the next."
7630 (interactive "P")
11e95b02
MB
7631 ;; Make sure we are in the summary buffer.
7632 (unless (eq major-mode 'gnus-summary-mode)
7633 (set-buffer gnus-summary-buffer))
eec82323
LMI
7634 (cond
7635 ;; Is there such an article?
7636 ((and (gnus-summary-search-forward unread subject backward)
7637 (or (gnus-summary-display-article (gnus-summary-article-number))
7638 (eq (gnus-summary-article-mark) gnus-canceled-mark)))
7639 (gnus-summary-position-point))
7640 ;; If not, we try the first unread, if that is wanted.
7641 ((and subject
7642 gnus-auto-select-same
7643 (gnus-summary-first-unread-article))
7644 (gnus-summary-position-point)
7645 (gnus-message 6 "Wrapped"))
7646 ;; Try to get next/previous article not displayed in this group.
7647 ((and gnus-auto-extend-newsgroup
7648 (not unread) (not subject))
7649 (gnus-summary-goto-article
7650 (if backward (1- gnus-newsgroup-begin) (1+ gnus-newsgroup-end))
6748645f 7651 nil (count-lines (point-min) (point))))
eec82323
LMI
7652 ;; Go to next/previous group.
7653 (t
7654 (unless (gnus-ephemeral-group-p gnus-newsgroup-name)
7655 (gnus-summary-jump-to-group gnus-newsgroup-name))
49e787c9
GM
7656 (let ((cmd (if (featurep 'xemacs)
7657 last-command-char
7658 last-command-event))
eec82323 7659 (point
01c52d31 7660 (with-current-buffer gnus-group-buffer
eec82323
LMI
7661 (point)))
7662 (group
7663 (if (eq gnus-keep-same-level 'best)
7664 (gnus-summary-best-group gnus-newsgroup-name)
7665 (gnus-summary-search-group backward gnus-keep-same-level))))
7666 ;; For some reason, the group window gets selected. We change
7667 ;; it back.
7668 (select-window (get-buffer-window (current-buffer)))
7669 ;; Select next unread newsgroup automagically.
7670 (cond
7671 ((or (not gnus-auto-select-next)
7672 (not cmd))
7673 (gnus-message 7 "No more%s articles" (if unread " unread" "")))
7674 ((or (eq gnus-auto-select-next 'quietly)
7675 (and (eq gnus-auto-select-next 'slightly-quietly)
7676 push)
7677 (and (eq gnus-auto-select-next 'almost-quietly)
7678 (gnus-summary-last-article-p)))
7679 ;; Select quietly.
7680 (if (gnus-ephemeral-group-p gnus-newsgroup-name)
7681 (gnus-summary-exit)
7682 (gnus-message 7 "No more%s articles (%s)..."
7683 (if unread " unread" "")
7684 (if group (concat "selecting " group)
7685 "exiting"))
7686 (gnus-summary-next-group nil group backward)))
7687 (t
7688 (when (gnus-key-press-event-p last-input-event)
7689 (gnus-summary-walk-group-buffer
7690 gnus-newsgroup-name cmd unread backward point))))))))
7691
7692(defun gnus-summary-walk-group-buffer (from-group cmd unread backward start)
7693 (let ((keystrokes '((?\C-n (gnus-group-next-unread-group 1))
7694 (?\C-p (gnus-group-prev-unread-group 1))))
7695 (cursor-in-echo-area t)
23f87bed 7696 keve key group ended prompt)
c7a91ce1 7697 (with-current-buffer gnus-group-buffer
eec82323
LMI
7698 (goto-char start)
7699 (setq group
7700 (if (eq gnus-keep-same-level 'best)
7701 (gnus-summary-best-group gnus-newsgroup-name)
7702 (gnus-summary-search-group backward gnus-keep-same-level))))
7703 (while (not ended)
23f87bed
MB
7704 (setq prompt
7705 (format
7706 "No more%s articles%s " (if unread " unread" "")
7707 (if (and group
7708 (not (gnus-ephemeral-group-p gnus-newsgroup-name)))
7709 (format " (Type %s for %s [%s])"
91472578
MB
7710 (single-key-description cmd)
7711 (gnus-group-decoded-name group)
01c52d31 7712 (gnus-group-unread group))
23f87bed
MB
7713 (format " (Type %s to exit %s)"
7714 (single-key-description cmd)
91472578 7715 (gnus-group-decoded-name gnus-newsgroup-name)))))
eec82323 7716 ;; Confirm auto selection.
23f87bed
MB
7717 (setq key (car (setq keve (gnus-read-event-char prompt)))
7718 ended t)
eec82323
LMI
7719 (cond
7720 ((assq key keystrokes)
7721 (let ((obuf (current-buffer)))
7722 (switch-to-buffer gnus-group-buffer)
7723 (when group
7724 (gnus-group-jump-to-group group))
7725 (eval (cadr (assq key keystrokes)))
7726 (setq group (gnus-group-group-name))
7727 (switch-to-buffer obuf))
7728 (setq ended nil))
7729 ((equal key cmd)
7730 (if (or (not group)
7731 (gnus-ephemeral-group-p gnus-newsgroup-name))
7732 (gnus-summary-exit)
7733 (gnus-summary-next-group nil group backward)))
7734 (t
7735 (push (cdr keve) unread-command-events))))))
7736
7737(defun gnus-summary-next-unread-article ()
7738 "Select unread article after current one."
7739 (interactive)
7740 (gnus-summary-next-article
7741 (or (not (eq gnus-summary-goto-unread 'never))
7742 (gnus-summary-last-article-p (gnus-summary-article-number)))
7743 (and gnus-auto-select-same
7744 (gnus-summary-article-subject))))
7745
7746(defun gnus-summary-prev-article (&optional unread subject)
bbbe940b 7747 "Select the article before the current one.
eec82323
LMI
7748If UNREAD is non-nil, only unread articles are selected."
7749 (interactive "P")
7750 (gnus-summary-next-article unread subject t))
7751
7752(defun gnus-summary-prev-unread-article ()
7753 "Select unread article before current one."
7754 (interactive)
7755 (gnus-summary-prev-article
7756 (or (not (eq gnus-summary-goto-unread 'never))
7757 (gnus-summary-first-article-p (gnus-summary-article-number)))
7758 (and gnus-auto-select-same
7759 (gnus-summary-article-subject))))
7760
23f87bed 7761(defun gnus-summary-next-page (&optional lines circular stop)
eec82323
LMI
7762 "Show next page of the selected article.
7763If at the end of the current article, select the next article.
7764LINES says how many lines should be scrolled up.
7765
7766If CIRCULAR is non-nil, go to the start of the article instead of
7767selecting the next article when reaching the end of the current
23f87bed
MB
7768article.
7769
7770If STOP is non-nil, just stop when reaching the end of the message.
7771
7772Also see the variable `gnus-article-skip-boring'."
eec82323
LMI
7773 (interactive "P")
7774 (setq gnus-summary-buffer (current-buffer))
7775 (gnus-set-global-variables)
7776 (let ((article (gnus-summary-article-number))
7777 (article-window (get-buffer-window gnus-article-buffer t))
7778 endp)
6748645f
LMI
7779 ;; If the buffer is empty, we have no article.
7780 (unless article
7781 (error "No article to select"))
eec82323
LMI
7782 (gnus-configure-windows 'article)
7783 (if (eq (cdr (assq article gnus-newsgroup-reads)) gnus-canceled-mark)
7784 (if (and (eq gnus-summary-goto-unread 'never)
7785 (not (gnus-summary-last-article-p article)))
7786 (gnus-summary-next-article)
7787 (gnus-summary-next-unread-article))
7788 (if (or (null gnus-current-article)
7789 (null gnus-article-current)
7790 (/= article (cdr gnus-article-current))
7791 (not (equal (car gnus-article-current) gnus-newsgroup-name)))
7792 ;; Selected subject is different from current article's.
7793 (gnus-summary-display-article article)
7794 (when article-window
7795 (gnus-eval-in-buffer-window gnus-article-buffer
23f87bed
MB
7796 (setq endp (or (gnus-article-next-page lines)
7797 (gnus-article-only-boring-p))))
eec82323 7798 (when endp
23f87bed
MB
7799 (cond (stop
7800 (gnus-message 3 "End of message"))
7801 (circular
eec82323
LMI
7802 (gnus-summary-beginning-of-article))
7803 (lines
7804 (gnus-message 3 "End of message"))
7805 ((null lines)
7806 (if (and (eq gnus-summary-goto-unread 'never)
7807 (not (gnus-summary-last-article-p article)))
7808 (gnus-summary-next-article)
7809 (gnus-summary-next-unread-article))))))))
7810 (gnus-summary-recenter)
7811 (gnus-summary-position-point)))
7812
7813(defun gnus-summary-prev-page (&optional lines move)
7814 "Show previous page of selected article.
7815Argument LINES specifies lines to be scrolled down.
7816If MOVE, move to the previous unread article if point is at
7817the beginning of the buffer."
7818 (interactive "P")
eec82323
LMI
7819 (let ((article (gnus-summary-article-number))
7820 (article-window (get-buffer-window gnus-article-buffer t))
7821 endp)
7822 (gnus-configure-windows 'article)
7823 (if (or (null gnus-current-article)
7824 (null gnus-article-current)
7825 (/= article (cdr gnus-article-current))
7826 (not (equal (car gnus-article-current) gnus-newsgroup-name)))
7827 ;; Selected subject is different from current article's.
7828 (gnus-summary-display-article article)
7829 (gnus-summary-recenter)
7830 (when article-window
7831 (gnus-eval-in-buffer-window gnus-article-buffer
7832 (setq endp (gnus-article-prev-page lines)))
7833 (when (and move endp)
7834 (cond (lines
7835 (gnus-message 3 "Beginning of message"))
7836 ((null lines)
7837 (if (and (eq gnus-summary-goto-unread 'never)
7838 (not (gnus-summary-first-article-p article)))
7839 (gnus-summary-prev-article)
7840 (gnus-summary-prev-unread-article))))))))
7841 (gnus-summary-position-point))
7842
7843(defun gnus-summary-prev-page-or-article (&optional lines)
7844 "Show previous page of selected article.
7845Argument LINES specifies lines to be scrolled down.
7846If at the beginning of the article, go to the next article."
7847 (interactive "P")
7848 (gnus-summary-prev-page lines t))
7849
7850(defun gnus-summary-scroll-up (lines)
7851 "Scroll up (or down) one line current article.
7852Argument LINES specifies lines to be scrolled up (or down if negative)."
7853 (interactive "p")
eec82323
LMI
7854 (gnus-configure-windows 'article)
7855 (gnus-summary-show-thread)
7856 (when (eq (gnus-summary-select-article nil nil 'pseudo) 'old)
7857 (gnus-eval-in-buffer-window gnus-article-buffer
7858 (cond ((> lines 0)
7859 (when (gnus-article-next-page lines)
7860 (gnus-message 3 "End of message")))
7861 ((< lines 0)
7862 (gnus-article-prev-page (- lines))))))
7863 (gnus-summary-recenter)
7864 (gnus-summary-position-point))
7865
6748645f
LMI
7866(defun gnus-summary-scroll-down (lines)
7867 "Scroll down (or up) one line current article.
7868Argument LINES specifies lines to be scrolled down (or up if negative)."
7869 (interactive "p")
7870 (gnus-summary-scroll-up (- lines)))
7871
eec82323
LMI
7872(defun gnus-summary-next-same-subject ()
7873 "Select next article which has the same subject as current one."
7874 (interactive)
eec82323
LMI
7875 (gnus-summary-next-article nil (gnus-summary-article-subject)))
7876
7877(defun gnus-summary-prev-same-subject ()
7878 "Select previous article which has the same subject as current one."
7879 (interactive)
eec82323
LMI
7880 (gnus-summary-prev-article nil (gnus-summary-article-subject)))
7881
7882(defun gnus-summary-next-unread-same-subject ()
7883 "Select next unread article which has the same subject as current one."
7884 (interactive)
eec82323
LMI
7885 (gnus-summary-next-article t (gnus-summary-article-subject)))
7886
7887(defun gnus-summary-prev-unread-same-subject ()
7888 "Select previous unread article which has the same subject as current one."
7889 (interactive)
eec82323
LMI
7890 (gnus-summary-prev-article t (gnus-summary-article-subject)))
7891
7892(defun gnus-summary-first-unread-article ()
7893 "Select the first unread article.
7894Return nil if there are no unread articles."
7895 (interactive)
eec82323
LMI
7896 (prog1
7897 (when (gnus-summary-first-subject t)
7898 (gnus-summary-show-thread)
7899 (gnus-summary-first-subject t)
7900 (gnus-summary-display-article (gnus-summary-article-number)))
7901 (gnus-summary-position-point)))
7902
16409b0b
GM
7903(defun gnus-summary-first-unread-subject ()
7904 "Place the point on the subject line of the first unread article.
7905Return nil if there are no unread articles."
7906 (interactive)
7907 (prog1
7908 (when (gnus-summary-first-subject t)
7909 (gnus-summary-show-thread)
7910 (gnus-summary-first-subject t))
7911 (gnus-summary-position-point)))
7912
23f87bed
MB
7913(defun gnus-summary-first-unseen-subject ()
7914 "Place the point on the subject line of the first unseen article.
7915Return nil if there are no unseen articles."
7916 (interactive)
7917 (prog1
7918 (when (gnus-summary-first-subject nil nil t)
7919 (gnus-summary-show-thread)
7920 (gnus-summary-first-subject nil nil t))
7921 (gnus-summary-position-point)))
7922
7923(defun gnus-summary-first-unseen-or-unread-subject ()
7924 "Place the point on the subject line of the first unseen article or,
7925if all article have been seen, on the subject line of the first unread
7926article."
7927 (interactive)
7928 (prog1
7929 (unless (when (gnus-summary-first-subject nil nil t)
7930 (gnus-summary-show-thread)
7931 (gnus-summary-first-subject nil nil t))
7932 (when (gnus-summary-first-subject t)
7933 (gnus-summary-show-thread)
7934 (gnus-summary-first-subject t)))
7935 (gnus-summary-position-point)))
7936
eec82323
LMI
7937(defun gnus-summary-first-article ()
7938 "Select the first article.
7939Return nil if there are no articles."
7940 (interactive)
eec82323
LMI
7941 (prog1
7942 (when (gnus-summary-first-subject)
16409b0b
GM
7943 (gnus-summary-show-thread)
7944 (gnus-summary-first-subject)
7945 (gnus-summary-display-article (gnus-summary-article-number)))
eec82323
LMI
7946 (gnus-summary-position-point)))
7947
23f87bed
MB
7948(defun gnus-summary-best-unread-article (&optional arg)
7949 "Select the unread article with the highest score.
7950If given a prefix argument, select the next unread article that has a
7951score higher than the default score."
7952 (interactive "P")
7953 (let ((article (if arg
7954 (gnus-summary-better-unread-subject)
7955 (gnus-summary-best-unread-subject))))
7956 (if article
7957 (gnus-summary-goto-article article)
7958 (error "No unread articles"))))
7959
7960(defun gnus-summary-best-unread-subject ()
7961 "Select the unread subject with the highest score."
eec82323 7962 (interactive)
eec82323
LMI
7963 (let ((best -1000000)
7964 (data gnus-newsgroup-data)
7965 article score)
7966 (while data
7967 (and (gnus-data-unread-p (car data))
7968 (> (setq score
7969 (gnus-summary-article-score (gnus-data-number (car data))))
7970 best)
7971 (setq best score
7972 article (gnus-data-number (car data))))
7973 (setq data (cdr data)))
23f87bed
MB
7974 (when article
7975 (gnus-summary-goto-subject article))
7976 (gnus-summary-position-point)
7977 article))
7978
7979(defun gnus-summary-better-unread-subject ()
7980 "Select the first unread subject that has a score over the default score."
7981 (interactive)
7982 (let ((data gnus-newsgroup-data)
7983 article score)
7984 (while (and (setq article (gnus-data-number (car data)))
7985 (or (gnus-data-read-p (car data))
7986 (not (> (gnus-summary-article-score article)
7987 gnus-summary-default-score))))
7988 (setq data (cdr data)))
7989 (when article
7990 (gnus-summary-goto-subject article))
7991 (gnus-summary-position-point)
7992 article))
eec82323
LMI
7993
7994(defun gnus-summary-last-subject ()
7995 "Go to the last displayed subject line in the group."
7996 (let ((article (gnus-data-number (car (gnus-data-list t)))))
7997 (when article
7998 (gnus-summary-goto-subject article))))
7999
8000(defun gnus-summary-goto-article (article &optional all-headers force)
6748645f
LMI
8001 "Fetch ARTICLE (article number or Message-ID) and display it if it exists.
8002If ALL-HEADERS is non-nil, no header lines are hidden.
8003If FORCE, go to the article even if it isn't displayed. If FORCE
8004is a number, it is the line the article is to be displayed on."
eec82323
LMI
8005 (interactive
8006 (list
6748645f
LMI
8007 (completing-read
8008 "Article number or Message-ID: "
8009 (mapcar (lambda (number) (list (int-to-string number)))
8010 gnus-newsgroup-limit))
eec82323
LMI
8011 current-prefix-arg
8012 t))
8013 (prog1
6748645f 8014 (if (and (stringp article)
23f87bed 8015 (string-match "@\\|%40" article))
6748645f
LMI
8016 (gnus-summary-refer-article article)
8017 (when (stringp article)
8018 (setq article (string-to-number article)))
8019 (if (gnus-summary-goto-subject article force)
8020 (gnus-summary-display-article article all-headers)
8021 (gnus-message 4 "Couldn't go to article %s" article) nil))
eec82323
LMI
8022 (gnus-summary-position-point)))
8023
8024(defun gnus-summary-goto-last-article ()
8025 "Go to the previously read article."
8026 (interactive)
8027 (prog1
8028 (when gnus-last-article
6748645f 8029 (gnus-summary-goto-article gnus-last-article nil t))
eec82323
LMI
8030 (gnus-summary-position-point)))
8031
8032(defun gnus-summary-pop-article (number)
8033 "Pop one article off the history and go to the previous.
8034NUMBER articles will be popped off."
8035 (interactive "p")
8036 (let (to)
8037 (setq gnus-newsgroup-history
8038 (cdr (setq to (nthcdr number gnus-newsgroup-history))))
8039 (if to
6748645f 8040 (gnus-summary-goto-article (car to) nil t)
eec82323
LMI
8041 (error "Article history empty")))
8042 (gnus-summary-position-point))
8043
8044;; Summary commands and functions for limiting the summary buffer.
8045
8046(defun gnus-summary-limit-to-articles (n)
8047 "Limit the summary buffer to the next N articles.
8048If not given a prefix, use the process marked articles instead."
8049 (interactive "P")
eec82323
LMI
8050 (prog1
8051 (let ((articles (gnus-summary-work-articles n)))
8052 (setq gnus-newsgroup-processable nil)
8053 (gnus-summary-limit articles))
8054 (gnus-summary-position-point)))
8055
8056(defun gnus-summary-pop-limit (&optional total)
8057 "Restore the previous limit.
8058If given a prefix, remove all limits."
8059 (interactive "P")
eec82323
LMI
8060 (when total
8061 (setq gnus-newsgroup-limits
8062 (list (mapcar (lambda (h) (mail-header-number h))
8063 gnus-newsgroup-headers))))
8064 (unless gnus-newsgroup-limits
8065 (error "No limit to pop"))
8066 (prog1
8067 (gnus-summary-limit nil 'pop)
8068 (gnus-summary-position-point)))
8069
47b63dfa
SZ
8070(defun gnus-summary-limit-to-subject (subject &optional header not-matching)
8071 "Limit the summary buffer to articles that have subjects that match a regexp.
8072If NOT-MATCHING, excluding articles that have subjects that match a regexp."
a1506d29 8073 (interactive
47b63dfa
SZ
8074 (list (read-string (if current-prefix-arg
8075 "Exclude subject (regexp): "
a1506d29 8076 "Limit to subject (regexp): "))
47b63dfa 8077 nil current-prefix-arg))
eec82323
LMI
8078 (unless header
8079 (setq header "subject"))
8080 (when (not (equal "" subject))
8081 (prog1
8082 (let ((articles (gnus-summary-find-matching
a1506d29 8083 (or header "subject") subject 'all nil nil
47b63dfa 8084 not-matching)))
eec82323
LMI
8085 (unless articles
8086 (error "Found no matches for \"%s\"" subject))
8087 (gnus-summary-limit articles))
8088 (gnus-summary-position-point))))
8089
ef6e0ec7 8090(defun gnus-summary-limit-to-author (from &optional not-matching)
47b63dfa
SZ
8091 "Limit the summary buffer to articles that have authors that match a regexp.
8092If NOT-MATCHING, excluding articles that have authors that match a regexp."
a1506d29 8093 (interactive
47b63dfa
SZ
8094 (list (read-string (if current-prefix-arg
8095 "Exclude author (regexp): "
a1506d29 8096 "Limit to author (regexp): "))
ef6e0ec7
SZ
8097 current-prefix-arg))
8098 (gnus-summary-limit-to-subject from "from" not-matching))
eec82323 8099
01c52d31
MB
8100(defun gnus-summary-limit-to-recipient (recipient &optional not-matching)
8101 "Limit the summary buffer to articles with the given RECIPIENT.
8102
8103If NOT-MATCHING, exclude RECIPIENT.
8104
8105To and Cc headers are checked. You need to include them in
8106`nnmail-extra-headers'."
8107 ;; Unlike `rmail-summary-by-recipients', doesn't include From.
8108 (interactive
8109 (list (read-string (format "%s recipient (regexp): "
8110 (if current-prefix-arg "Exclude" "Limit to")))
8111 current-prefix-arg))
8112 (when (not (equal "" recipient))
8113 (prog1 (let* ((to
8114 (if (memq 'To nnmail-extra-headers)
8115 (gnus-summary-find-matching
8116 (cons 'extra 'To) recipient 'all nil nil
8117 not-matching)
8118 (gnus-message
8119 1 "`To' isn't present in `nnmail-extra-headers'")
8120 (sit-for 1)
8121 nil))
8122 (cc
8123 (if (memq 'Cc nnmail-extra-headers)
8124 (gnus-summary-find-matching
8125 (cons 'extra 'Cc) recipient 'all nil nil
8126 not-matching)
8127 (gnus-message
8128 1 "`Cc' isn't present in `nnmail-extra-headers'")
8129 (sit-for 1)
8130 nil))
8131 (articles
8132 (if not-matching
8133 ;; We need the numbers that are in both lists:
8134 (mapcar (lambda (a)
8135 (and (memq a to) a))
8136 cc)
8137 (nconc to cc))))
8138 (unless articles
8139 (error "Found no matches for \"%s\"" recipient))
8140 (gnus-summary-limit articles))
8141 (gnus-summary-position-point))))
8142
8143(defun gnus-summary-limit-to-address (address &optional not-matching)
8144 "Limit the summary buffer to articles with the given ADDRESS.
8145
8146If NOT-MATCHING, exclude ADDRESS.
8147
8148To, Cc and From headers are checked. You need to include `To' and `Cc'
8149in `nnmail-extra-headers'."
8150 (interactive
8151 (list (read-string (format "%s address (regexp): "
8152 (if current-prefix-arg "Exclude" "Limit to")))
8153 current-prefix-arg))
8154 (when (not (equal "" address))
8155 (prog1 (let* ((to
8156 (if (memq 'To nnmail-extra-headers)
8157 (gnus-summary-find-matching
8158 (cons 'extra 'To) address 'all nil nil
8159 not-matching)
8160 (gnus-message
8161 1 "`To' isn't present in `nnmail-extra-headers'")
8162 (sit-for 1)
8163 t))
8164 (cc
8165 (if (memq 'Cc nnmail-extra-headers)
8166 (gnus-summary-find-matching
8167 (cons 'extra 'Cc) address 'all nil nil
8168 not-matching)
8169 (gnus-message
8170 1 "`Cc' isn't present in `nnmail-extra-headers'")
8171 (sit-for 1)
8172 t))
8173 (from
8174 (gnus-summary-find-matching "from" address
8175 'all nil nil not-matching))
8176 (articles
8177 (if not-matching
8178 ;; We need the numbers that are in all lists:
8179 (if (eq cc t)
8180 (if (eq to t)
8181 from
8182 (mapcar (lambda (a) (car (memq a from))) to))
8183 (if (eq to t)
8184 (mapcar (lambda (a) (car (memq a from))) cc)
8185 (mapcar (lambda (a) (car (memq a from)))
8186 (mapcar (lambda (a) (car (memq a to)))
8187 cc))))
8188 (nconc (if (eq to t) nil to)
8189 (if (eq cc t) nil cc)
8190 from))))
8191 (unless articles
8192 (error "Found no matches for \"%s\"" address))
8193 (gnus-summary-limit articles))
8194 (gnus-summary-position-point))))
8195
8196(defun gnus-summary-limit-strange-charsets-predicate (header)
8197 (let ((string (concat (mail-header-subject header)
8198 (mail-header-from header)))
8199 charset found)
8200 (dotimes (i (1- (length string)))
8201 (setq charset (format "%s" (char-charset (aref string (1+ i)))))
8202 (when (string-match "unicode\\|big\\|japanese" charset)
8203 (setq found t)))
8204 found))
8205
8206(defun gnus-summary-limit-to-predicate (predicate)
8207 "Limit to articles where PREDICATE returns non-nil.
8208PREDICATE will be called with the header structures of the
8209articles."
8210 (let ((articles nil)
8211 (case-fold-search t))
8212 (dolist (header gnus-newsgroup-headers)
8213 (when (funcall predicate header)
8214 (push (mail-header-number header) articles)))
8215 (gnus-summary-limit (nreverse articles))))
8216
eec82323
LMI
8217(defun gnus-summary-limit-to-age (age &optional younger-p)
8218 "Limit the summary buffer to articles that are older than (or equal) AGE days.
8219If YOUNGER-P (the prefix) is non-nil, limit the summary buffer to
8220articles that are younger than AGE days."
16409b0b
GM
8221 (interactive
8222 (let ((younger current-prefix-arg)
8223 (days-got nil)
8224 days)
8225 (while (not days-got)
8226 (setq days (if younger
23f87bed
MB
8227 (read-string "Limit to articles younger than (in days, older when negative): ")
8228 (read-string
8229 "Limit to articles older than (in days, younger when negative): ")))
16409b0b
GM
8230 (when (> (length days) 0)
8231 (setq days (read days)))
8232 (if (numberp days)
23f87bed
MB
8233 (progn
8234 (setq days-got t)
01c52d31
MB
8235 (when (< days 0)
8236 (setq younger (not younger))
8237 (setq days (* days -1))))
16409b0b
GM
8238 (message "Please enter a number.")
8239 (sleep-for 1)))
8240 (list days younger)))
eec82323
LMI
8241 (prog1
8242 (let ((data gnus-newsgroup-data)
16409b0b 8243 (cutoff (days-to-time age))
eec82323
LMI
8244 articles d date is-younger)
8245 (while (setq d (pop data))
8246 (when (and (vectorp (gnus-data-header d))
8247 (setq date (mail-header-date (gnus-data-header d))))
16409b0b
GM
8248 (setq is-younger (time-less-p
8249 (time-since (condition-case ()
8250 (date-to-time date)
8251 (error '(0 0))))
eec82323 8252 cutoff))
6748645f
LMI
8253 (when (if younger-p
8254 is-younger
8255 (not is-younger))
eec82323
LMI
8256 (push (gnus-data-number d) articles))))
8257 (gnus-summary-limit (nreverse articles)))
8258 (gnus-summary-position-point)))
8259
47b63dfa 8260(defun gnus-summary-limit-to-extra (header regexp &optional not-matching)
16409b0b
GM
8261 "Limit the summary buffer to articles that match an 'extra' header."
8262 (interactive
8263 (let ((header
8264 (intern
23f87bed 8265 (gnus-completing-read-with-default
16409b0b 8266 (symbol-name (car gnus-extra-headers))
47b63dfa 8267 (if current-prefix-arg
81df110a
RF
8268 "Exclude extra header"
8269 "Limit extra header")
16409b0b
GM
8270 (mapcar (lambda (x)
8271 (cons (symbol-name x) x))
8272 gnus-extra-headers)
8273 nil
8274 t))))
8275 (list header
a1506d29 8276 (read-string (format "%s header %s (regexp): "
47b63dfa
SZ
8277 (if current-prefix-arg "Exclude" "Limit to")
8278 header))
8279 current-prefix-arg)))
16409b0b
GM
8280 (when (not (equal "" regexp))
8281 (prog1
8282 (let ((articles (gnus-summary-find-matching
a1506d29 8283 (cons 'extra header) regexp 'all nil nil
47b63dfa 8284 not-matching)))
16409b0b
GM
8285 (unless articles
8286 (error "Found no matches for \"%s\"" regexp))
8287 (gnus-summary-limit articles))
8288 (gnus-summary-position-point))))
8289
23f87bed
MB
8290(defun gnus-summary-limit-to-display-predicate ()
8291 "Limit the summary buffer to the predicated in the `display' group parameter."
8292 (interactive)
8293 (unless gnus-newsgroup-display
8294 (error "There is no `display' group parameter"))
8295 (let (articles)
8296 (dolist (number gnus-newsgroup-articles)
8297 (when (funcall gnus-newsgroup-display)
8298 (push number articles)))
8299 (gnus-summary-limit articles))
8300 (gnus-summary-position-point))
8301
eec82323
LMI
8302(defalias 'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
8303(make-obsolete
265ac10b 8304 'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread "Emacs 20.4")
eec82323
LMI
8305
8306(defun gnus-summary-limit-to-unread (&optional all)
8307 "Limit the summary buffer to articles that are not marked as read.
8308If ALL is non-nil, limit strictly to unread articles."
8309 (interactive "P")
8310 (if all
8311 (gnus-summary-limit-to-marks (char-to-string gnus-unread-mark))
8312 (gnus-summary-limit-to-marks
8313 ;; Concat all the marks that say that an article is read and have
8314 ;; those removed.
8315 (list gnus-del-mark gnus-read-mark gnus-ancient-mark
23f87bed 8316 gnus-killed-mark gnus-spam-mark gnus-kill-file-mark
eec82323
LMI
8317 gnus-low-score-mark gnus-expirable-mark
8318 gnus-canceled-mark gnus-catchup-mark gnus-sparse-mark
8319 gnus-duplicate-mark gnus-souped-mark)
8320 'reverse)))
8321
01c52d31
MB
8322(defun gnus-summary-limit-to-headers (match &optional reverse)
8323 "Limit the summary buffer to articles that have headers that match MATCH.
8324If REVERSE (the prefix), limit to articles that don't match."
8325 (interactive "sMatch headers (regexp): \nP")
8326 (gnus-summary-limit-to-bodies match reverse t))
8327
8328(defun gnus-summary-limit-to-bodies (match &optional reverse headersp)
8329 "Limit the summary buffer to articles that have bodies that match MATCH.
8330If REVERSE (the prefix), limit to articles that don't match."
8331 (interactive "sMatch body (regexp): \nP")
8332 (let ((articles nil)
8333 (gnus-select-article-hook nil) ;Disable hook.
8334 (gnus-article-prepare-hook nil)
8335 (gnus-use-article-prefetch nil)
8336 (gnus-keep-backlog nil)
8337 (gnus-break-pages nil)
8338 (gnus-summary-display-arrow nil)
8339 (gnus-updated-mode-lines nil)
8340 (gnus-auto-center-summary nil)
8341 (gnus-display-mime-function nil))
8342 (dolist (data gnus-newsgroup-data)
8343 (let (gnus-mark-article-hook)
8344 (gnus-summary-select-article t t nil (gnus-data-number data)))
398a825b 8345 (with-current-buffer gnus-article-buffer
01c52d31
MB
8346 (article-goto-body)
8347 (let* ((case-fold-search t)
8348 (found (if headersp
8349 (re-search-backward match nil t)
8350 (re-search-forward match nil t))))
8351 (when (or (and found
8352 (not reverse))
8353 (and (not found)
8354 reverse))
8355 (push (gnus-data-number data) articles)))))
8356 (if (not articles)
8357 (message "No messages matched")
8358 (gnus-summary-limit articles)))
8359 (gnus-summary-position-point))
8360
8361(defun gnus-summary-limit-to-singletons (&optional threadsp)
8362 "Limit the summary buffer to articles that aren't part on any thread.
8363If THREADSP (the prefix), limit to articles that are in threads."
8364 (interactive "P")
8365 (let ((articles nil)
8366 thread-articles
8367 threads)
8368 (dolist (thread gnus-newsgroup-threads)
8369 (if (stringp (car thread))
8370 (dolist (thread (cdr thread))
8371 (push thread threads))
8372 (push thread threads)))
8373 (dolist (thread threads)
8374 (setq thread-articles (gnus-articles-in-thread thread))
8375 (when (or (and threadsp
8376 (> (length thread-articles) 1))
8377 (and (not threadsp)
8378 (= (length thread-articles) 1)))
8379 (setq articles (nconc thread-articles articles))))
8380 (if (not articles)
8381 (message "No messages matched")
8382 (gnus-summary-limit articles))
8383 (gnus-summary-position-point)))
8384
8385(defun gnus-summary-limit-to-replied (&optional unreplied)
8386 "Limit the summary buffer to replied articles.
8387If UNREPLIED (the prefix), limit to unreplied articles."
8388 (interactive "P")
8389 (if unreplied
8390 (gnus-summary-limit
8391 (gnus-set-difference gnus-newsgroup-articles
8392 gnus-newsgroup-replied))
8393 (gnus-summary-limit gnus-newsgroup-replied))
8394 (gnus-summary-position-point))
8395
eec82323
LMI
8396(defalias 'gnus-summary-delete-marked-with 'gnus-summary-limit-exclude-marks)
8397(make-obsolete 'gnus-summary-delete-marked-with
265ac10b 8398 'gnus-summary-limit-exclude-marks "Emacs 20.4")
eec82323
LMI
8399
8400(defun gnus-summary-limit-exclude-marks (marks &optional reverse)
8401 "Exclude articles that are marked with MARKS (e.g. \"DK\").
8402If REVERSE, limit the summary buffer to articles that are marked
8403with MARKS. MARKS can either be a string of marks or a list of marks.
8404Returns how many articles were removed."
8405 (interactive "sMarks: ")
8406 (gnus-summary-limit-to-marks marks t))
8407
8408(defun gnus-summary-limit-to-marks (marks &optional reverse)
8409 "Limit the summary buffer to articles that are marked with MARKS (e.g. \"DK\").
8410If REVERSE (the prefix), limit the summary buffer to articles that are
8411not marked with MARKS. MARKS can either be a string of marks or a
8412list of marks.
8413Returns how many articles were removed."
6748645f 8414 (interactive "sMarks: \nP")
eec82323
LMI
8415 (prog1
8416 (let ((data gnus-newsgroup-data)
8417 (marks (if (listp marks) marks
8418 (append marks nil))) ; Transform to list.
8419 articles)
8420 (while data
8421 (when (if reverse (not (memq (gnus-data-mark (car data)) marks))
8422 (memq (gnus-data-mark (car data)) marks))
8423 (push (gnus-data-number (car data)) articles))
8424 (setq data (cdr data)))
8425 (gnus-summary-limit articles))
8426 (gnus-summary-position-point)))
8427
23f87bed 8428(defun gnus-summary-limit-to-score (score)
eec82323 8429 "Limit to articles with score at or above SCORE."
23f87bed 8430 (interactive "NLimit to articles with score of at least: ")
eec82323
LMI
8431 (let ((data gnus-newsgroup-data)
8432 articles)
8433 (while data
8434 (when (>= (gnus-summary-article-score (gnus-data-number (car data)))
8435 score)
8436 (push (gnus-data-number (car data)) articles))
8437 (setq data (cdr data)))
8438 (prog1
8439 (gnus-summary-limit articles)
8440 (gnus-summary-position-point))))
8441
23f87bed
MB
8442(defun gnus-summary-limit-to-unseen ()
8443 "Limit to unseen articles."
8444 (interactive)
8445 (prog1
8446 (gnus-summary-limit gnus-newsgroup-unseen)
8447 (gnus-summary-position-point)))
8448
6748645f 8449(defun gnus-summary-limit-include-thread (id)
23f87bed
MB
8450 "Display all the hidden articles that is in the thread with ID in it.
8451When called interactively, ID is the Message-ID of the current
8452article."
6748645f
LMI
8453 (interactive (list (mail-header-id (gnus-summary-article-header))))
8454 (let ((articles (gnus-articles-in-thread
8455 (gnus-id-to-thread (gnus-root-id id)))))
8456 (prog1
8457 (gnus-summary-limit (nconc articles gnus-newsgroup-limit))
23f87bed
MB
8458 (gnus-summary-limit-include-matching-articles
8459 "subject"
8460 (regexp-quote (gnus-simplify-subject-re
8461 (mail-header-subject (gnus-id-to-header id)))))
6748645f
LMI
8462 (gnus-summary-position-point))))
8463
23f87bed
MB
8464(defun gnus-summary-limit-include-matching-articles (header regexp)
8465 "Display all the hidden articles that have HEADERs that match REGEXP."
8466 (interactive (list (read-string "Match on header: ")
8467 (read-string "Regexp: ")))
8468 (let ((articles (gnus-find-matching-articles header regexp)))
8469 (prog1
8470 (gnus-summary-limit (nconc articles gnus-newsgroup-limit))
8471 (gnus-summary-position-point))))
8472
8473(defun gnus-summary-insert-dormant-articles ()
8474 "Insert all the dormant articles for this group into the current buffer."
8475 (interactive)
8476 (let ((gnus-verbose (max 6 gnus-verbose)))
8477 (if (not gnus-newsgroup-dormant)
db629244 8478 (gnus-message 3 "No dormant articles for this group")
23f87bed
MB
8479 (gnus-summary-goto-subjects gnus-newsgroup-dormant))))
8480
01c52d31
MB
8481(defun gnus-summary-insert-ticked-articles ()
8482 "Insert ticked articles for this group into the current buffer."
8483 (interactive)
8484 (let ((gnus-verbose (max 6 gnus-verbose)))
8485 (if (not gnus-newsgroup-marked)
8486 (gnus-message 3 "No ticked articles for this group")
8487 (gnus-summary-goto-subjects gnus-newsgroup-marked))))
8488
eec82323 8489(defun gnus-summary-limit-include-dormant ()
6748645f
LMI
8490 "Display all the hidden articles that are marked as dormant.
8491Note that this command only works on a subset of the articles currently
8492fetched for this group."
eec82323 8493 (interactive)
eec82323
LMI
8494 (unless gnus-newsgroup-dormant
8495 (error "There are no dormant articles in this group"))
8496 (prog1
8497 (gnus-summary-limit (append gnus-newsgroup-dormant gnus-newsgroup-limit))
8498 (gnus-summary-position-point)))
8499
8500(defun gnus-summary-limit-exclude-dormant ()
8501 "Hide all dormant articles."
8502 (interactive)
eec82323
LMI
8503 (prog1
8504 (gnus-summary-limit-to-marks (list gnus-dormant-mark) 'reverse)
8505 (gnus-summary-position-point)))
8506
8507(defun gnus-summary-limit-exclude-childless-dormant ()
8508 "Hide all dormant articles that have no children."
8509 (interactive)
eec82323
LMI
8510 (let ((data (gnus-data-list t))
8511 articles d children)
8512 ;; Find all articles that are either not dormant or have
8513 ;; children.
8514 (while (setq d (pop data))
8515 (when (or (not (= (gnus-data-mark d) gnus-dormant-mark))
8516 (and (setq children
8517 (gnus-article-children (gnus-data-number d)))
8518 (let (found)
8519 (while children
8520 (when (memq (car children) articles)
8521 (setq children nil
8522 found t))
8523 (pop children))
8524 found)))
8525 (push (gnus-data-number d) articles)))
8526 ;; Do the limiting.
8527 (prog1
8528 (gnus-summary-limit articles)
8529 (gnus-summary-position-point))))
8530
8531(defun gnus-summary-limit-mark-excluded-as-read (&optional all)
8532 "Mark all unread excluded articles as read.
8533If ALL, mark even excluded ticked and dormants as read."
8534 (interactive "P")
23f87bed
MB
8535 (setq gnus-newsgroup-limit (sort gnus-newsgroup-limit '<))
8536 (let ((articles (gnus-sorted-ndifference
eec82323
LMI
8537 (sort
8538 (mapcar (lambda (h) (mail-header-number h))
8539 gnus-newsgroup-headers)
8540 '<)
23f87bed 8541 gnus-newsgroup-limit))
eec82323 8542 article)
6748645f 8543 (setq gnus-newsgroup-unreads
23f87bed
MB
8544 (gnus-sorted-intersection gnus-newsgroup-unreads
8545 gnus-newsgroup-limit))
eec82323
LMI
8546 (if all
8547 (setq gnus-newsgroup-dormant nil
8548 gnus-newsgroup-marked nil
8549 gnus-newsgroup-reads
8550 (nconc
8551 (mapcar (lambda (n) (cons n gnus-catchup-mark)) articles)
8552 gnus-newsgroup-reads))
8553 (while (setq article (pop articles))
8554 (unless (or (memq article gnus-newsgroup-dormant)
8555 (memq article gnus-newsgroup-marked))
8556 (push (cons article gnus-catchup-mark) gnus-newsgroup-reads))))))
8557
8558(defun gnus-summary-limit (articles &optional pop)
8559 (if pop
8560 ;; We pop the previous limit off the stack and use that.
8561 (setq articles (car gnus-newsgroup-limits)
8562 gnus-newsgroup-limits (cdr gnus-newsgroup-limits))
8563 ;; We use the new limit, so we push the old limit on the stack.
8564 (push gnus-newsgroup-limit gnus-newsgroup-limits))
8565 ;; Set the limit.
8566 (setq gnus-newsgroup-limit articles)
8567 (let ((total (length gnus-newsgroup-data))
8568 (data (gnus-data-find-list (gnus-summary-article-number)))
8569 (gnus-summary-mark-below nil) ; Inhibit this.
8570 found)
8571 ;; This will do all the work of generating the new summary buffer
8572 ;; according to the new limit.
8573 (gnus-summary-prepare)
8574 ;; Hide any threads, possibly.
23f87bed 8575 (gnus-summary-maybe-hide-threads)
eec82323
LMI
8576 ;; Try to return to the article you were at, or one in the
8577 ;; neighborhood.
8578 (when data
8579 ;; We try to find some article after the current one.
8580 (while data
8581 (when (gnus-summary-goto-subject (gnus-data-number (car data)) nil t)
8582 (setq data nil
8583 found t))
8584 (setq data (cdr data))))
8585 (unless found
8586 ;; If there is no data, that means that we were after the last
8587 ;; article. The same goes when we can't find any articles
8588 ;; after the current one.
8589 (goto-char (point-max))
8590 (gnus-summary-find-prev))
6748645f 8591 (gnus-set-mode-line 'summary)
eec82323
LMI
8592 ;; We return how many articles were removed from the summary
8593 ;; buffer as a result of the new limit.
8594 (- total (length gnus-newsgroup-data))))
8595
8596(defsubst gnus-invisible-cut-children (threads)
8597 (let ((num 0))
8598 (while threads
8599 (when (memq (mail-header-number (caar threads)) gnus-newsgroup-limit)
8600 (incf num))
8601 (pop threads))
8602 (< num 2)))
8603
8604(defsubst gnus-cut-thread (thread)
8605 "Go forwards in the thread until we find an article that we want to display."
8606 (when (or (eq gnus-fetch-old-headers 'some)
6748645f 8607 (eq gnus-fetch-old-headers 'invisible)
16409b0b 8608 (numberp gnus-fetch-old-headers)
eec82323
LMI
8609 (eq gnus-build-sparse-threads 'some)
8610 (eq gnus-build-sparse-threads 'more))
8611 ;; Deal with old-fetched headers and sparse threads.
8612 (while (and
8613 thread
8614 (or
8615 (gnus-summary-article-sparse-p (mail-header-number (car thread)))
8616 (gnus-summary-article-ancient-p
8617 (mail-header-number (car thread))))
6748645f
LMI
8618 (if (or (<= (length (cdr thread)) 1)
8619 (eq gnus-fetch-old-headers 'invisible))
8620 (setq gnus-newsgroup-limit
8621 (delq (mail-header-number (car thread))
8622 gnus-newsgroup-limit)
8623 thread (cadr thread))
8624 (when (gnus-invisible-cut-children (cdr thread))
8625 (let ((th (cdr thread)))
8626 (while th
8627 (if (memq (mail-header-number (caar th))
a8151ef7 8628 gnus-newsgroup-limit)
6748645f
LMI
8629 (setq thread (car th)
8630 th nil)
8631 (setq th (cdr th))))))))))
eec82323
LMI
8632 thread)
8633
8634(defun gnus-cut-threads (threads)
23f87bed 8635 "Cut off all uninteresting articles from the beginning of THREADS."
eec82323 8636 (when (or (eq gnus-fetch-old-headers 'some)
6748645f 8637 (eq gnus-fetch-old-headers 'invisible)
16409b0b 8638 (numberp gnus-fetch-old-headers)
eec82323
LMI
8639 (eq gnus-build-sparse-threads 'some)
8640 (eq gnus-build-sparse-threads 'more))
8641 (let ((th threads))
8642 (while th
8643 (setcar th (gnus-cut-thread (car th)))
8644 (setq th (cdr th)))))
8645 ;; Remove nixed out threads.
8646 (delq nil threads))
8647
8648(defun gnus-summary-initial-limit (&optional show-if-empty)
8649 "Figure out what the initial limit is supposed to be on group entry.
8650This entails weeding out unwanted dormants, low-scored articles,
8651fetch-old-headers verbiage, and so on."
8652 ;; Most groups have nothing to remove.
f394fa25
MB
8653 (unless (or gnus-inhibit-limiting
8654 (and (null gnus-newsgroup-dormant)
8655 (eq gnus-newsgroup-display 'gnus-not-ignore)
8656 (not (eq gnus-fetch-old-headers 'some))
8657 (not (numberp gnus-fetch-old-headers))
8658 (not (eq gnus-fetch-old-headers 'invisible))
8659 (null gnus-summary-expunge-below)
8660 (not (eq gnus-build-sparse-threads 'some))
8661 (not (eq gnus-build-sparse-threads 'more))
8662 (null gnus-thread-expunge-below)
8663 (not gnus-use-nocem)))
eec82323
LMI
8664 (push gnus-newsgroup-limit gnus-newsgroup-limits)
8665 (setq gnus-newsgroup-limit nil)
8666 (mapatoms
8667 (lambda (node)
8668 (unless (car (symbol-value node))
8669 ;; These threads have no parents -- they are roots.
8670 (let ((nodes (cdr (symbol-value node)))
8671 thread)
8672 (while nodes
8673 (if (and gnus-thread-expunge-below
8674 (< (gnus-thread-total-score (car nodes))
8675 gnus-thread-expunge-below))
8676 (gnus-expunge-thread (pop nodes))
8677 (setq thread (pop nodes))
8678 (gnus-summary-limit-children thread))))))
8679 gnus-newsgroup-dependencies)
8680 ;; If this limitation resulted in an empty group, we might
8681 ;; pop the previous limit and use it instead.
8682 (when (and (not gnus-newsgroup-limit)
8683 show-if-empty)
8684 (setq gnus-newsgroup-limit (pop gnus-newsgroup-limits)))
8685 gnus-newsgroup-limit))
8686
8687(defun gnus-summary-limit-children (thread)
8688 "Return 1 if this subthread is visible and 0 if it is not."
8689 ;; First we get the number of visible children to this thread. This
8690 ;; is done by recursing down the thread using this function, so this
8691 ;; will really go down to a leaf article first, before slowly
8692 ;; working its way up towards the root.
8693 (when thread
04b61ae9 8694 (let* ((max-lisp-eval-depth (max 5000 max-lisp-eval-depth))
23f87bed 8695 (children
eec82323
LMI
8696 (if (cdr thread)
8697 (apply '+ (mapcar 'gnus-summary-limit-children
8698 (cdr thread)))
8699 0))
8700 (number (mail-header-number (car thread)))
8701 score)
8702 (if (and
8703 (not (memq number gnus-newsgroup-marked))
8704 (or
8705 ;; If this article is dormant and has absolutely no visible
8706 ;; children, then this article isn't visible.
8707 (and (memq number gnus-newsgroup-dormant)
8708 (zerop children))
8709 ;; If this is "fetch-old-headered" and there is no
8710 ;; visible children, then we don't want this article.
16409b0b
GM
8711 (and (or (eq gnus-fetch-old-headers 'some)
8712 (numberp gnus-fetch-old-headers))
eec82323
LMI
8713 (gnus-summary-article-ancient-p number)
8714 (zerop children))
6748645f
LMI
8715 ;; If this is "fetch-old-headered" and `invisible', then
8716 ;; we don't want this article.
8717 (and (eq gnus-fetch-old-headers 'invisible)
8718 (gnus-summary-article-ancient-p number))
eec82323
LMI
8719 ;; If this is a sparsely inserted article with no children,
8720 ;; we don't want it.
8721 (and (eq gnus-build-sparse-threads 'some)
8722 (gnus-summary-article-sparse-p number)
8723 (zerop children))
8724 ;; If we use expunging, and this article is really
8725 ;; low-scored, then we don't want this article.
8726 (when (and gnus-summary-expunge-below
8727 (< (setq score
8728 (or (cdr (assq number gnus-newsgroup-scored))
8729 gnus-summary-default-score))
8730 gnus-summary-expunge-below))
8731 ;; We increase the expunge-tally here, but that has
8732 ;; nothing to do with the limits, really.
8733 (incf gnus-newsgroup-expunged-tally)
8734 ;; We also mark as read here, if that's wanted.
8735 (when (and gnus-summary-mark-below
8736 (< score gnus-summary-mark-below))
8737 (setq gnus-newsgroup-unreads
8738 (delq number gnus-newsgroup-unreads))
8739 (if gnus-newsgroup-auto-expire
8740 (push number gnus-newsgroup-expirable)
8741 (push (cons number gnus-low-score-mark)
8742 gnus-newsgroup-reads)))
8743 t)
23f87bed
MB
8744 ;; Do the `display' group parameter.
8745 (and gnus-newsgroup-display
8746 (not (funcall gnus-newsgroup-display)))
eec82323 8747 ;; Check NoCeM things.
01c52d31
MB
8748 (when (and gnus-use-nocem
8749 (gnus-nocem-unwanted-article-p
8750 (mail-header-id (car thread))))
8751 (setq gnus-newsgroup-unreads
8752 (delq number gnus-newsgroup-unreads))
8753 t)))
eec82323
LMI
8754 ;; Nope, invisible article.
8755 0
8756 ;; Ok, this article is to be visible, so we add it to the limit
8757 ;; and return 1.
8758 (push number gnus-newsgroup-limit)
8759 1))))
8760
8761(defun gnus-expunge-thread (thread)
8762 "Mark all articles in THREAD as read."
8763 (let* ((number (mail-header-number (car thread))))
8764 (incf gnus-newsgroup-expunged-tally)
8765 ;; We also mark as read here, if that's wanted.
8766 (setq gnus-newsgroup-unreads
8767 (delq number gnus-newsgroup-unreads))
8768 (if gnus-newsgroup-auto-expire
8769 (push number gnus-newsgroup-expirable)
8770 (push (cons number gnus-low-score-mark)
8771 gnus-newsgroup-reads)))
8772 ;; Go recursively through all subthreads.
8773 (mapcar 'gnus-expunge-thread (cdr thread)))
8774
8775;; Summary article oriented commands
8776
8777(defun gnus-summary-refer-parent-article (n)
8778 "Refer parent article N times.
8779If N is negative, go to ancestor -N instead.
8780The difference between N and the number of articles fetched is returned."
8781 (interactive "p")
eec82323
LMI
8782 (let ((skip 1)
8783 error header ref)
8784 (when (not (natnump n))
8785 (setq skip (abs n)
8786 n 1))
8787 (while (and (> n 0)
8788 (not error))
8789 (setq header (gnus-summary-article-header))
8790 (if (and (eq (mail-header-number header)
8791 (cdr gnus-article-current))
8792 (equal gnus-newsgroup-name
8793 (car gnus-article-current)))
8794 ;; If we try to find the parent of the currently
8795 ;; displayed article, then we take a look at the actual
8796 ;; References header, since this is slightly more
8797 ;; reliable than the References field we got from the
8798 ;; server.
c7a91ce1 8799 (with-current-buffer gnus-original-article-buffer
eec82323
LMI
8800 (nnheader-narrow-to-headers)
8801 (unless (setq ref (message-fetch-field "references"))
23f87bed
MB
8802 (when (setq ref (message-fetch-field "in-reply-to"))
8803 (setq ref (gnus-extract-message-id-from-in-reply-to ref))))
eec82323
LMI
8804 (widen))
8805 (setq ref
8806 ;; It's not the current article, so we take a bet on
8807 ;; the value we got from the server.
8808 (mail-header-references header)))
8809 (if (and ref
8810 (not (equal ref "")))
8811 (unless (gnus-summary-refer-article (gnus-parent-id ref skip))
8812 (gnus-message 1 "Couldn't find parent"))
8813 (gnus-message 1 "No references in article %d"
8814 (gnus-summary-article-number))
8815 (setq error t))
8816 (decf n))
8817 (gnus-summary-position-point)
8818 n))
8819
8820(defun gnus-summary-refer-references ()
8821 "Fetch all articles mentioned in the References header.
6748645f 8822Return the number of articles fetched."
eec82323 8823 (interactive)
eec82323
LMI
8824 (let ((ref (mail-header-references (gnus-summary-article-header)))
8825 (current (gnus-summary-article-number))
8826 (n 0))
8827 (if (or (not ref)
8828 (equal ref ""))
8829 (error "No References in the current article")
8830 ;; For each Message-ID in the References header...
8831 (while (string-match "<[^>]*>" ref)
8832 (incf n)
8833 ;; ... fetch that article.
8834 (gnus-summary-refer-article
8835 (prog1 (match-string 0 ref)
8836 (setq ref (substring ref (match-end 0))))))
8837 (gnus-summary-goto-subject current)
8838 (gnus-summary-position-point)
8839 n)))
8840
6748645f
LMI
8841(defun gnus-summary-refer-thread (&optional limit)
8842 "Fetch all articles in the current thread.
8843If LIMIT (the numerical prefix), fetch that many old headers instead
8844of what's specified by the `gnus-refer-thread-limit' variable."
8845 (interactive "P")
8846 (let ((id (mail-header-id (gnus-summary-article-header)))
8847 (limit (if limit (prefix-numeric-value limit)
8848 gnus-refer-thread-limit)))
6748645f
LMI
8849 (unless (eq gnus-fetch-old-headers 'invisible)
8850 (gnus-message 5 "Fetching headers for %s..." gnus-newsgroup-name)
8851 ;; Retrieve the headers and read them in.
23f87bed
MB
8852 (if (eq (if (numberp limit)
8853 (gnus-retrieve-headers
8854 (list (min
8855 (+ (mail-header-number
8856 (gnus-summary-article-header))
8857 limit)
8858 gnus-newsgroup-end))
8859 gnus-newsgroup-name (* limit 2))
8860 ;; gnus-refer-thread-limit is t, i.e. fetch _all_
8861 ;; headers.
8862 (gnus-retrieve-headers (list gnus-newsgroup-end)
8863 gnus-newsgroup-name limit))
6748645f
LMI
8864 'nov)
8865 (gnus-build-all-threads)
23f87bed 8866 (error "Can't fetch thread from back ends that don't support NOV"))
6748645f
LMI
8867 (gnus-message 5 "Fetching headers for %s...done" gnus-newsgroup-name))
8868 (gnus-summary-limit-include-thread id)))
8869
16409b0b
GM
8870(defun gnus-summary-refer-article (message-id)
8871 "Fetch an article specified by MESSAGE-ID."
8872 (interactive "sMessage-ID: ")
eec82323
LMI
8873 (when (and (stringp message-id)
8874 (not (zerop (length message-id))))
23f87bed 8875 (setq message-id (gnus-replace-in-string message-id " " ""))
eec82323
LMI
8876 ;; Construct the correct Message-ID if necessary.
8877 ;; Suggested by tale@pawl.rpi.edu.
8878 (unless (string-match "^<" message-id)
8879 (setq message-id (concat "<" message-id)))
8880 (unless (string-match ">$" message-id)
8881 (setq message-id (concat message-id ">")))
23f87bed
MB
8882 ;; People often post MIDs from URLs, so unhex it:
8883 (unless (string-match "@" message-id)
8884 (setq message-id (gnus-url-unhex-string message-id)))
eec82323
LMI
8885 (let* ((header (gnus-id-to-header message-id))
8886 (sparse (and header
8887 (gnus-summary-article-sparse-p
a8151ef7
LMI
8888 (mail-header-number header))
8889 (memq (mail-header-number header)
16409b0b
GM
8890 gnus-newsgroup-limit)))
8891 number)
6748645f
LMI
8892 (cond
8893 ;; If the article is present in the buffer we just go to it.
8894 ((and header
8895 (or (not (gnus-summary-article-sparse-p
8896 (mail-header-number header)))
8897 sparse))
8898 (prog1
8899 (gnus-summary-goto-article
8900 (mail-header-number header) nil t)
8901 (when sparse
8902 (gnus-summary-update-article (mail-header-number header)))))
8903 (t
16409b0b
GM
8904 ;; We fetch the article.
8905 (catch 'found
8906 (dolist (gnus-override-method (gnus-refer-article-methods))
23f87bed
MB
8907 (when (and (gnus-check-server gnus-override-method)
8908 ;; Fetch the header,
8909 (setq number (gnus-summary-insert-subject message-id)))
8910 ;; and display the article.
eec82323 8911 (gnus-summary-select-article nil nil nil number)
16409b0b
GM
8912 (throw 'found t)))
8913 (gnus-message 3 "Couldn't fetch article %s" message-id)))))))
8914
8915(defun gnus-refer-article-methods ()
8f688cb0 8916 "Return a list of referable methods."
16409b0b
GM
8917 (cond
8918 ;; No method, so we default to current and native.
8919 ((null gnus-refer-article-method)
8920 (list gnus-current-select-method gnus-select-method))
8921 ;; Current.
8922 ((eq 'current gnus-refer-article-method)
8923 (list gnus-current-select-method))
8924 ;; List of select methods.
d4dfaa19
DL
8925 ((not (and (symbolp (car gnus-refer-article-method))
8926 (assq (car gnus-refer-article-method) nnoo-definition-alist)))
16409b0b
GM
8927 (let (out)
8928 (dolist (method gnus-refer-article-method)
8929 (push (if (eq 'current method)
8930 gnus-current-select-method
8931 method)
8932 out))
8933 (nreverse out)))
8934 ;; One single select method.
8935 (t
8936 (list gnus-refer-article-method))))
6748645f
LMI
8937
8938(defun gnus-summary-edit-parameters ()
8939 "Edit the group parameters of the current group."
8940 (interactive)
8941 (gnus-group-edit-group gnus-newsgroup-name 'params))
eec82323 8942
16409b0b
GM
8943(defun gnus-summary-customize-parameters ()
8944 "Customize the group parameters of the current group."
8945 (interactive)
8946 (gnus-group-customize gnus-newsgroup-name))
8947
eec82323
LMI
8948(defun gnus-summary-enter-digest-group (&optional force)
8949 "Enter an nndoc group based on the current article.
8950If FORCE, force a digest interpretation. If not, try
8951to guess what the document format is."
8952 (interactive "P")
eec82323 8953 (let ((conf gnus-current-window-configuration))
23f87bed
MB
8954 (save-window-excursion
8955 (save-excursion
8956 (let (gnus-article-prepare-hook
8957 gnus-display-mime-function
8958 gnus-break-pages)
8959 (gnus-summary-select-article))))
eec82323
LMI
8960 (setq gnus-current-window-configuration conf)
8961 (let* ((name (format "%s-%d"
8962 (gnus-group-prefixed-name
8963 gnus-newsgroup-name (list 'nndoc ""))
01c52d31 8964 (with-current-buffer gnus-summary-buffer
eec82323
LMI
8965 gnus-current-article)))
8966 (ogroup gnus-newsgroup-name)
8967 (params (append (gnus-info-params (gnus-get-info ogroup))
8968 (list (cons 'to-group ogroup))
23f87bed 8969 (list (cons 'parent-group ogroup))
eec82323
LMI
8970 (list (cons 'save-article-group ogroup))))
8971 (case-fold-search t)
8972 (buf (current-buffer))
16409b0b 8973 dig to-address)
c7a91ce1 8974 (with-current-buffer gnus-original-article-buffer
16409b0b
GM
8975 ;; Have the digest group inherit the main mail address of
8976 ;; the parent article.
23f87bed
MB
8977 (when (setq to-address (or (gnus-fetch-field "reply-to")
8978 (gnus-fetch-field "from")))
343d6628
MB
8979 (setq params
8980 (append
8981 (list (cons 'to-address
8982 (funcall gnus-decode-encoded-address-function
8983 to-address))))))
eec82323
LMI
8984 (setq dig (nnheader-set-temp-buffer " *gnus digest buffer*"))
8985 (insert-buffer-substring gnus-original-article-buffer)
8986 ;; Remove lines that may lead nndoc to misinterpret the
8987 ;; document type.
8988 (narrow-to-region
8989 (goto-char (point-min))
8990 (or (search-forward "\n\n" nil t) (point)))
8991 (goto-char (point-min))
16409b0b 8992 (delete-matching-lines "^Path:\\|^From ")
eec82323
LMI
8993 (widen))
8994 (unwind-protect
23f87bed 8995 (if (let ((gnus-newsgroup-ephemeral-charset gnus-newsgroup-charset)
16409b0b
GM
8996 (gnus-newsgroup-ephemeral-ignored-charsets
8997 gnus-newsgroup-ignored-charsets))
8998 (gnus-group-read-ephemeral-group
8999 name `(nndoc ,name (nndoc-address ,(get-buffer dig))
9000 (nndoc-article-type
23f87bed
MB
9001 ,(if force 'mbox 'guess)))
9002 t nil nil nil
9003 `((adapt-file . ,(gnus-score-file-name gnus-newsgroup-name
9004 "ADAPT")))))
16409b0b 9005 ;; Make all postings to this group go to the parent group.
23f87bed
MB
9006 (nconc (gnus-info-params (gnus-get-info name))
9007 params)
9008 ;; Couldn't select this doc group.
9009 (switch-to-buffer buf)
9010 (gnus-set-global-variables)
9011 (gnus-configure-windows 'summary)
9012 (gnus-message 3 "Article couldn't be entered?"))
eec82323
LMI
9013 (kill-buffer dig)))))
9014
9015(defun gnus-summary-read-document (n)
9016 "Open a new group based on the current article(s).
9017This will allow you to read digests and other similar
9018documents as newsgroups.
9019Obeys the standard process/prefix convention."
9020 (interactive "P")
01c52d31 9021 (let* ((ogroup gnus-newsgroup-name)
eec82323
LMI
9022 (params (append (gnus-info-params (gnus-get-info ogroup))
9023 (list (cons 'to-group ogroup))))
01c52d31
MB
9024 group egroup groups vgroup)
9025 (dolist (article (gnus-summary-work-articles n))
eec82323
LMI
9026 (setq group (format "%s-%d" gnus-newsgroup-name article))
9027 (gnus-summary-remove-process-mark article)
9028 (when (gnus-summary-display-article article)
398a825b 9029 (save-excursion ;;What for?
16409b0b 9030 (with-temp-buffer
eec82323
LMI
9031 (insert-buffer-substring gnus-original-article-buffer)
9032 ;; Remove some headers that may lead nndoc to make
9033 ;; the wrong guess.
9034 (message-narrow-to-head)
9035 (goto-char (point-min))
01c52d31 9036 (delete-matching-lines "^Path:\\|^From ")
eec82323
LMI
9037 (widen)
9038 (if (setq egroup
9039 (gnus-group-read-ephemeral-group
9040 group `(nndoc ,group (nndoc-address ,(current-buffer))
9041 (nndoc-article-type guess))
9042 t nil t))
9043 (progn
c7a91ce1 9044 ;; Make all postings to this group go to the parent group.
eec82323
LMI
9045 (nconc (gnus-info-params (gnus-get-info egroup))
9046 params)
9047 (push egroup groups))
9048 ;; Couldn't select this doc group.
9049 (gnus-error 3 "Article couldn't be entered"))))))
9050 ;; Now we have selected all the documents.
9051 (cond
9052 ((not groups)
9053 (error "None of the articles could be interpreted as documents"))
9054 ((gnus-group-read-ephemeral-group
9055 (setq vgroup (format
9056 "nnvirtual:%s-%s" gnus-newsgroup-name
9057 (format-time-string "%Y%m%dT%H%M%S" (current-time))))
9058 `(nnvirtual ,vgroup (nnvirtual-component-groups ,groups))
9059 t
9060 (cons (current-buffer) 'summary)))
9061 (t
9062 (error "Couldn't select virtual nndoc group")))))
9063
9064(defun gnus-summary-isearch-article (&optional regexp-p)
9065 "Do incremental search forward on the current article.
9066If REGEXP-P (the prefix) is non-nil, do regexp isearch."
9067 (interactive "P")
eec82323
LMI
9068 (gnus-summary-select-article)
9069 (gnus-configure-windows 'article)
9070 (gnus-eval-in-buffer-window gnus-article-buffer
6748645f
LMI
9071 (save-restriction
9072 (widen)
9073 (isearch-forward regexp-p))))
eec82323 9074
01c52d31
MB
9075(defun gnus-summary-repeat-search-article-forward ()
9076 "Repeat the previous search forwards."
9077 (interactive)
9078 (unless gnus-last-search-regexp
9079 (error "No previous search"))
9080 (gnus-summary-search-article-forward gnus-last-search-regexp))
9081
9082(defun gnus-summary-repeat-search-article-backward ()
9083 "Repeat the previous search backwards."
9084 (interactive)
9085 (unless gnus-last-search-regexp
9086 (error "No previous search"))
9087 (gnus-summary-search-article-forward gnus-last-search-regexp t))
9088
eec82323
LMI
9089(defun gnus-summary-search-article-forward (regexp &optional backward)
9090 "Search for an article containing REGEXP forward.
9091If BACKWARD, search backward instead."
9092 (interactive
9093 (list (read-string
9094 (format "Search article %s (regexp%s): "
9095 (if current-prefix-arg "backward" "forward")
9096 (if gnus-last-search-regexp
9097 (concat ", default " gnus-last-search-regexp)
9098 "")))
9099 current-prefix-arg))
eec82323
LMI
9100 (if (string-equal regexp "")
9101 (setq regexp (or gnus-last-search-regexp ""))
23f87bed
MB
9102 (setq gnus-last-search-regexp regexp)
9103 (setq gnus-article-before-search gnus-current-article))
9104 ;; Intentionally set gnus-last-article.
9105 (setq gnus-last-article gnus-article-before-search)
9106 (let ((gnus-last-article gnus-last-article))
9107 (if (gnus-summary-search-article regexp backward)
9108 (gnus-summary-show-thread)
abc40aab 9109 (signal 'search-failed (list regexp)))))
eec82323
LMI
9110
9111(defun gnus-summary-search-article-backward (regexp)
9112 "Search for an article containing REGEXP backward."
9113 (interactive
9114 (list (read-string
9115 (format "Search article backward (regexp%s): "
9116 (if gnus-last-search-regexp
9117 (concat ", default " gnus-last-search-regexp)
9118 "")))))
9119 (gnus-summary-search-article-forward regexp 'backward))
9120
9121(defun gnus-summary-search-article (regexp &optional backward)
9122 "Search for an article containing REGEXP.
9123Optional argument BACKWARD means do search for backward.
9124`gnus-select-article-hook' is not called during the search."
a8151ef7
LMI
9125 ;; We have to require this here to make sure that the following
9126 ;; dynamic binding isn't shadowed by autoloading.
9127 (require 'gnus-async)
16409b0b 9128 (require 'gnus-art)
eec82323 9129 (let ((gnus-select-article-hook nil) ;Disable hook.
16409b0b 9130 (gnus-article-prepare-hook nil)
eec82323
LMI
9131 (gnus-mark-article-hook nil) ;Inhibit marking as read.
9132 (gnus-use-article-prefetch nil)
9133 (gnus-xmas-force-redisplay nil) ;Inhibit XEmacs redisplay.
a8151ef7 9134 (gnus-use-trees nil) ;Inhibit updating tree buffer.
23f87bed
MB
9135 (gnus-visual nil)
9136 (gnus-keep-backlog nil)
9137 (gnus-break-pages nil)
9138 (gnus-summary-display-arrow nil)
9139 (gnus-updated-mode-lines nil)
9140 (gnus-auto-center-summary nil)
eec82323 9141 (sum (current-buffer))
16409b0b 9142 (gnus-display-mime-function nil)
eec82323
LMI
9143 (found nil)
9144 point)
9145 (gnus-save-hidden-threads
9146 (gnus-summary-select-article)
9147 (set-buffer gnus-article-buffer)
16409b0b 9148 (goto-char (window-point (get-buffer-window (current-buffer))))
eec82323
LMI
9149 (when backward
9150 (forward-line -1))
9151 (while (not found)
9152 (gnus-message 7 "Searching article: %d..." (cdr gnus-article-current))
9153 (if (if backward
9154 (re-search-backward regexp nil t)
9155 (re-search-forward regexp nil t))
9156 ;; We found the regexp.
9157 (progn
9158 (setq found 'found)
9159 (beginning-of-line)
9160 (set-window-start
9161 (get-buffer-window (current-buffer))
9162 (point))
9163 (forward-line 1)
16409b0b
GM
9164 (set-window-point
9165 (get-buffer-window (current-buffer))
9166 (point))
eec82323
LMI
9167 (set-buffer sum)
9168 (setq point (point)))
9169 ;; We didn't find it, so we go to the next article.
9170 (set-buffer sum)
9171 (setq found 'not)
9172 (while (eq found 'not)
9173 (if (not (if backward (gnus-summary-find-prev)
9174 (gnus-summary-find-next)))
9175 ;; No more articles.
9176 (setq found t)
9177 ;; Select the next article and adjust point.
9178 (unless (gnus-summary-article-sparse-p
9179 (gnus-summary-article-number))
9180 (setq found nil)
9181 (gnus-summary-select-article)
9182 (set-buffer gnus-article-buffer)
9183 (widen)
9184 (goto-char (if backward (point-max) (point-min))))))))
9185 (gnus-message 7 ""))
9186 ;; Return whether we found the regexp.
9187 (when (eq found 'found)
9188 (goto-char point)
9189 (gnus-summary-show-thread)
9190 (gnus-summary-goto-subject gnus-current-article)
9191 (gnus-summary-position-point)
9192 t)))
9193
23f87bed
MB
9194(defun gnus-find-matching-articles (header regexp)
9195 "Return a list of all articles that match REGEXP on HEADER.
9196This search includes all articles in the current group that Gnus has
9197fetched headers for, whether they are displayed or not."
9198 (let ((articles nil)
c7a91ce1 9199 ;; Can't eta-reduce because it's a macro.
23f87bed
MB
9200 (func `(lambda (h) (,(intern (concat "mail-header-" header)) h)))
9201 (case-fold-search t))
9202 (dolist (header gnus-newsgroup-headers)
9203 (when (string-match regexp (funcall func header))
9204 (push (mail-header-number header) articles)))
9205 (nreverse articles)))
9206
eec82323 9207(defun gnus-summary-find-matching (header regexp &optional backward unread
47b63dfa 9208 not-case-fold not-matching)
eec82323
LMI
9209 "Return a list of all articles that match REGEXP on HEADER.
9210The search stars on the current article and goes forwards unless
9211BACKWARD is non-nil. If BACKWARD is `all', do all articles.
9212If UNREAD is non-nil, only unread articles will
9213be taken into consideration. If NOT-CASE-FOLD, case won't be folded
a1506d29 9214in the comparisons. If NOT-MATCHING, return a list of all articles that
47b63dfa
SZ
9215not match REGEXP on HEADER."
9216 (let ((case-fold-search (not not-case-fold))
16409b0b
GM
9217 articles d func)
9218 (if (consp header)
9219 (if (eq (car header) 'extra)
9220 (setq func
9221 `(lambda (h)
9222 (or (cdr (assq ',(cdr header) (mail-header-extra h)))
9223 "")))
9224 (error "%s is an invalid header" header))
9225 (unless (fboundp (intern (concat "mail-header-" header)))
9226 (error "%s is not a valid header" header))
9227 (setq func `(lambda (h) (,(intern (concat "mail-header-" header)) h))))
47b63dfa
SZ
9228 (dolist (d (if (eq backward 'all)
9229 gnus-newsgroup-data
9230 (gnus-data-find-list
9231 (gnus-summary-article-number)
9232 (gnus-data-list backward))))
9233 (when (and (or (not unread) ; We want all articles...
9234 (gnus-data-unread-p d)) ; Or just unreads.
9235 (vectorp (gnus-data-header d)) ; It's not a pseudo.
9236 (if not-matching
a1506d29 9237 (not (string-match
47b63dfa
SZ
9238 regexp
9239 (funcall func (gnus-data-header d))))
9240 (string-match regexp
9241 (funcall func (gnus-data-header d)))))
9242 (push (gnus-data-number d) articles))) ; Success!
eec82323
LMI
9243 (nreverse articles)))
9244
9245(defun gnus-summary-execute-command (header regexp command &optional backward)
9246 "Search forward for an article whose HEADER matches REGEXP and execute COMMAND.
9247If HEADER is an empty string (or nil), the match is done on the entire
9248article. If BACKWARD (the prefix) is non-nil, search backward instead."
9249 (interactive
9250 (list (let ((completion-ignore-case t))
9251 (completing-read
9252 "Header name: "
23f87bed
MB
9253 (mapcar (lambda (header) (list (format "%s" header)))
9254 (append
9255 '("Number" "Subject" "From" "Lines" "Date"
9256 "Message-ID" "Xref" "References" "Body")
9257 gnus-extra-headers))
eec82323
LMI
9258 nil 'require-match))
9259 (read-string "Regexp: ")
9260 (read-key-sequence "Command: ")
9261 current-prefix-arg))
9262 (when (equal header "Body")
9263 (setq header ""))
eec82323
LMI
9264 ;; Hidden thread subtrees must be searched as well.
9265 (gnus-summary-show-all-threads)
9266 ;; We don't want to change current point nor window configuration.
9267 (save-excursion
9268 (save-window-excursion
23f87bed
MB
9269 (let (gnus-visual
9270 gnus-treat-strip-trailing-blank-lines
9271 gnus-treat-strip-leading-blank-lines
9272 gnus-treat-strip-multiple-blank-lines
9273 gnus-treat-hide-boring-headers
9274 gnus-treat-fold-newsgroups
9275 gnus-article-prepare-hook)
9276 (gnus-message 6 "Executing %s..." (key-description command))
9277 ;; We'd like to execute COMMAND interactively so as to give arguments.
9278 (gnus-execute header regexp
9279 `(call-interactively ',(key-binding command))
9280 backward)
9281 (gnus-message 6 "Executing %s...done" (key-description command))))))
eec82323
LMI
9282
9283(defun gnus-summary-beginning-of-article ()
9284 "Scroll the article back to the beginning."
9285 (interactive)
eec82323
LMI
9286 (gnus-summary-select-article)
9287 (gnus-configure-windows 'article)
9288 (gnus-eval-in-buffer-window gnus-article-buffer
9289 (widen)
9290 (goto-char (point-min))
23f87bed 9291 (when gnus-break-pages
eec82323
LMI
9292 (gnus-narrow-to-page))))
9293
9294(defun gnus-summary-end-of-article ()
9295 "Scroll to the end of the article."
9296 (interactive)
eec82323
LMI
9297 (gnus-summary-select-article)
9298 (gnus-configure-windows 'article)
9299 (gnus-eval-in-buffer-window gnus-article-buffer
9300 (widen)
9301 (goto-char (point-max))
9302 (recenter -3)
23f87bed 9303 (when gnus-break-pages
eec82323
LMI
9304 (gnus-narrow-to-page))))
9305
23f87bed
MB
9306(defun gnus-summary-print-truncate-and-quote (string &optional len)
9307 "Truncate to LEN and quote all \"(\"'s in STRING."
9308 (gnus-replace-in-string (if (and len (> (length string) len))
9309 (substring string 0 len)
9310 string)
9311 "[()]" "\\\\\\&"))
9312
6748645f 9313(defun gnus-summary-print-article (&optional filename n)
23f87bed
MB
9314 "Generate and print a PostScript image of the process-marked (mail) articles.
9315
9316If used interactively, print the current article if none are
9317process-marked. With prefix arg, prompt the user for the name of the
9318file to save in.
6748645f 9319
23f87bed
MB
9320When used from Lisp, accept two optional args FILENAME and N. N means
9321to print the next N articles. If N is negative, print the N previous
9322articles. If N is nil and articles have been marked with the process
9323mark, print these instead.
eec82323 9324
16409b0b 9325If the optional first argument FILENAME is nil, send the image to the
6748645f
LMI
9326printer. If FILENAME is a string, save the PostScript image in a file with
9327that name. If FILENAME is a number, prompt the user for the name of the file
eec82323 9328to save in."
676a7cc9 9329 (interactive (list (ps-print-preprint current-prefix-arg)))
6748645f
LMI
9330 (dolist (article (gnus-summary-work-articles n))
9331 (gnus-summary-select-article nil nil 'pseudo article)
9332 (gnus-eval-in-buffer-window gnus-article-buffer
23f87bed 9333 (gnus-print-buffer))
676a7cc9
SZ
9334 (gnus-summary-remove-process-mark article))
9335 (ps-despool filename))
eec82323 9336
23f87bed
MB
9337(defun gnus-print-buffer ()
9338 (let ((buffer (generate-new-buffer " *print*")))
9339 (unwind-protect
9340 (progn
9341 (copy-to-buffer buffer (point-min) (point-max))
9342 (set-buffer buffer)
9343 (gnus-remove-text-with-property 'gnus-decoration)
9344 (when (gnus-visual-p 'article-highlight 'highlight)
9345 ;; Copy-to-buffer doesn't copy overlay. So redo
9346 ;; highlight.
9347 (let ((gnus-article-buffer buffer))
9348 (gnus-article-highlight-citation t)
9349 (gnus-article-highlight-signature)
9350 (gnus-article-emphasize)
9351 (gnus-article-delete-invisible-text)))
9352 (let ((ps-left-header
9353 (list
9354 (concat "("
9355 (gnus-summary-print-truncate-and-quote
9356 (mail-header-subject gnus-current-headers)
9357 66) ")")
9358 (concat "("
9359 (gnus-summary-print-truncate-and-quote
9360 (mail-header-from gnus-current-headers)
9361 45) ")")))
9362 (ps-right-header
9363 (list
9364 "/pagenumberstring load"
9365 (concat "("
9366 (mail-header-date gnus-current-headers) ")"))))
9367 (gnus-run-hooks 'gnus-ps-print-hook)
9368 (save-excursion
a7b50e1c 9369 (if ps-print-color-p
23f87bed
MB
9370 (ps-spool-buffer-with-faces)
9371 (ps-spool-buffer)))))
9372 (kill-buffer buffer))))
9373
eec82323 9374(defun gnus-summary-show-article (&optional arg)
23f87bed 9375 "Force redisplaying of the current article.
16409b0b
GM
9376If ARG (the prefix) is a number, show the article with the charset
9377defined in `gnus-summary-show-article-charset-alist', or the charset
23f87bed 9378input.
16409b0b 9379If ARG (the prefix) is non-nil and not a number, show the raw article
23f87bed
MB
9380without any article massaging functions being run. Normally, the key
9381strokes are `C-u g'."
eec82323 9382 (interactive "P")
16409b0b
GM
9383 (cond
9384 ((numberp arg)
23f87bed 9385 (gnus-summary-show-article t)
16409b0b
GM
9386 (let ((gnus-newsgroup-charset
9387 (or (cdr (assq arg gnus-summary-show-article-charset-alist))
23f87bed
MB
9388 (mm-read-coding-system
9389 "View as charset: " ;; actually it is coding system.
01c52d31 9390 (with-current-buffer gnus-article-buffer
23f87bed 9391 (mm-detect-coding-region (point) (point-max))))))
16409b0b 9392 (gnus-newsgroup-ignored-charsets 'gnus-all))
23f87bed
MB
9393 (gnus-summary-select-article nil 'force)
9394 (let ((deps gnus-newsgroup-dependencies)
9395 head header lines)
c7a91ce1 9396 (with-current-buffer gnus-original-article-buffer
23f87bed
MB
9397 (save-restriction
9398 (message-narrow-to-head)
9399 (setq head (buffer-string))
9400 (goto-char (point-min))
9401 (unless (re-search-forward "^lines:[ \t]\\([0-9]+\\)" nil t)
9402 (goto-char (point-max))
9403 (widen)
9404 (setq lines (1- (count-lines (point) (point-max))))))
9405 (with-temp-buffer
9406 (insert (format "211 %d Article retrieved.\n"
9407 (cdr gnus-article-current)))
9408 (insert head)
9409 (if lines (insert (format "Lines: %d\n" lines)))
9410 (insert ".\n")
9411 (let ((nntp-server-buffer (current-buffer)))
9412 (setq header (car (gnus-get-newsgroup-headers deps t))))))
9413 (gnus-data-set-header
9414 (gnus-data-find (cdr gnus-article-current))
9415 header)
9416 (gnus-summary-update-article-line
9417 (cdr gnus-article-current) header)
9418 (when (gnus-summary-goto-subject (cdr gnus-article-current) nil t)
9419 (gnus-summary-update-secondary-mark (cdr gnus-article-current))))))
16409b0b
GM
9420 ((not arg)
9421 ;; Select the article the normal way.
9422 (gnus-summary-select-article nil 'force))
9423 (t
9424 ;; We have to require this here to make sure that the following
9425 ;; dynamic binding isn't shadowed by autoloading.
9426 (require 'gnus-async)
9427 (require 'gnus-art)
eec82323
LMI
9428 ;; Bind the article treatment functions to nil.
9429 (let ((gnus-have-all-headers t)
eec82323 9430 gnus-article-prepare-hook
16409b0b
GM
9431 gnus-article-decode-hook
9432 gnus-display-mime-function
9433 gnus-break-pages)
9434 ;; Destroy any MIME parts.
9435 (when (gnus-buffer-live-p gnus-article-buffer)
c7a91ce1 9436 (with-current-buffer gnus-article-buffer
16409b0b
GM
9437 (mm-destroy-parts gnus-article-mime-handles)
9438 ;; Set it to nil for safety reason.
9439 (setq gnus-article-mime-handle-alist nil)
9440 (setq gnus-article-mime-handles nil)))
9441 (gnus-summary-select-article nil 'force))))
eec82323
LMI
9442 (gnus-summary-goto-subject gnus-current-article)
9443 (gnus-summary-position-point))
9444
23f87bed
MB
9445(defun gnus-summary-show-raw-article ()
9446 "Show the raw article without any article massaging functions being run."
9447 (interactive)
9448 (gnus-summary-show-article t))
9449
eec82323
LMI
9450(defun gnus-summary-verbose-headers (&optional arg)
9451 "Toggle permanent full header display.
9452If ARG is a positive number, turn header display on.
9453If ARG is a negative number, turn header display off."
9454 (interactive "P")
eec82323
LMI
9455 (setq gnus-show-all-headers
9456 (cond ((or (not (numberp arg))
9457 (zerop arg))
9458 (not gnus-show-all-headers))
9459 ((natnump arg)
9460 t)))
9461 (gnus-summary-show-article))
9462
9463(defun gnus-summary-toggle-header (&optional arg)
9464 "Show the headers if they are hidden, or hide them if they are shown.
9465If ARG is a positive number, show the entire header.
9466If ARG is a negative number, hide the unwanted header lines."
9467 (interactive "P")
23f87bed
MB
9468 (let ((window (and (gnus-buffer-live-p gnus-article-buffer)
9469 (get-buffer-window gnus-article-buffer t))))
9470 (with-current-buffer gnus-article-buffer
9471 (widen)
9472 (article-narrow-to-head)
c7a91ce1 9473 (let* ((inhibit-read-only t)
16409b0b 9474 (inhibit-point-motion-hooks t)
23f87bed
MB
9475 (hidden (if (numberp arg)
9476 (>= arg 0)
f0096211
MB
9477 (or (not (looking-at "[^ \t\n]+:"))
9478 (gnus-article-hidden-text-p 'headers))))
23f87bed
MB
9479 s e)
9480 (delete-region (point-min) (point-max))
667e0ba6
SM
9481 (with-current-buffer gnus-original-article-buffer
9482 (goto-char (setq s (point-min)))
23f87bed
MB
9483 (setq e (if (search-forward "\n\n" nil t)
9484 (1- (point))
9485 (point-max))))
667e0ba6 9486 (insert-buffer-substring gnus-original-article-buffer s e)
23f87bed
MB
9487 (run-hooks 'gnus-article-decode-hook)
9488 (if hidden
9489 (let ((gnus-treat-hide-headers nil)
9490 (gnus-treat-hide-boring-headers nil))
9491 (gnus-delete-wash-type 'headers)
9492 (gnus-treat-article 'head))
9493 (gnus-treat-article 'head))
9494 (widen)
9495 (if window
9496 (set-window-start window (goto-char (point-min))))
9497 (if gnus-break-pages
9498 (gnus-narrow-to-page)
9499 (when (gnus-visual-p 'page-marker)
c7a91ce1 9500 (let ((inhibit-read-only t))
23f87bed
MB
9501 (gnus-remove-text-with-property 'gnus-prev)
9502 (gnus-remove-text-with-property 'gnus-next))))
16409b0b 9503 (gnus-set-mode-line 'article)))))
eec82323
LMI
9504
9505(defun gnus-summary-show-all-headers ()
9506 "Make all header lines visible."
9507 (interactive)
23f87bed 9508 (gnus-summary-toggle-header 1))
eec82323 9509
eec82323
LMI
9510(defun gnus-summary-caesar-message (&optional arg)
9511 "Caesar rotate the current article by 13.
01c52d31
MB
9512With a non-numerical prefix, also rotate headers. A numerical
9513prefix specifies how many places to rotate each letter forward."
eec82323 9514 (interactive "P")
eec82323
LMI
9515 (gnus-summary-select-article)
9516 (let ((mail-header-separator ""))
9517 (gnus-eval-in-buffer-window gnus-article-buffer
9518 (save-restriction
9519 (widen)
9520 (let ((start (window-start))
c7a91ce1 9521 (inhibit-read-only t))
01c52d31
MB
9522 (if (equal arg '(4))
9523 (message-caesar-buffer-body nil t)
9524 (message-caesar-buffer-body arg))
ff4d3926
MB
9525 (set-window-start (get-buffer-window (current-buffer)) start)))))
9526 ;; Create buttons and stuff...
9527 (gnus-treat-article nil))
eec82323 9528
704f1663
GM
9529(declare-function idna-to-unicode "ext:idna" (str))
9530
01c52d31
MB
9531(defun gnus-summary-idna-message (&optional arg)
9532 "Decode IDNA encoded domain names in the current articles.
9533IDNA encoded domain names looks like `xn--bar'. If a string
9534remain unencoded after running this function, it is likely an
9535invalid IDNA string (`xn--bar' is invalid).
9536
9537You must have GNU Libidn (`http://www.gnu.org/software/libidn/')
9538installed for this command to work."
9539 (interactive "P")
9540 (if (not (and (condition-case nil (require 'idna)
9541 (file-error))
9542 (mm-coding-system-p 'utf-8)
9543 (executable-find (symbol-value 'idna-program))))
9544 (gnus-message
9545 5 "GNU Libidn not installed properly (`idn' or `idna.el' missing)")
9546 (gnus-summary-select-article)
9547 (let ((mail-header-separator ""))
9548 (gnus-eval-in-buffer-window gnus-article-buffer
9549 (save-restriction
9550 (widen)
9551 (let ((start (window-start))
9552 buffer-read-only)
9553 (while (re-search-forward "\\(xn--[-0-9a-z]+\\)" nil t)
9554 (replace-match (idna-to-unicode (match-string 1))))
9555 (set-window-start (get-buffer-window (current-buffer)) start)))))))
23f87bed
MB
9556
9557(defun gnus-summary-morse-message (&optional arg)
9558 "Morse decode the current article."
9559 (interactive "P")
9560 (gnus-summary-select-article)
9561 (let ((mail-header-separator ""))
9562 (gnus-eval-in-buffer-window gnus-article-buffer
9563 (save-excursion
9564 (save-restriction
9565 (widen)
9566 (let ((pos (window-start))
c7a91ce1 9567 (inhibit-read-only t))
23f87bed
MB
9568 (goto-char (point-min))
9569 (when (message-goto-body)
9570 (gnus-narrow-to-body))
9571 (goto-char (point-min))
01c52d31 9572