Fix possibly buggy calls to `message'.
[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,
d7a0267c 4;; 2005, 2006, 2007 Free Software Foundation, Inc.
eec82323 5
6748645f 6;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
eec82323
LMI
7;; Keywords: news
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software; you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
5a9dffec 13;; the Free Software Foundation; either version 3, or (at your option)
eec82323
LMI
14;; any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
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
22;; along with GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24;; Boston, MA 02110-1301, USA.
eec82323
LMI
25
26;;; Commentary:
27
28;;; Code:
29
23f87bed 30(eval-when-compile
9efa445f
DN
31 (require 'cl))
32
33(defvar tool-bar-mode)
34(defvar gnus-tmp-header)
5ab7173c 35
eec82323
LMI
36(require 'gnus)
37(require 'gnus-group)
38(require 'gnus-spec)
39(require 'gnus-range)
40(require 'gnus-int)
41(require 'gnus-undo)
6748645f 42(require 'gnus-util)
18c06a99 43(require 'gmm-utils)
16409b0b 44(require 'mm-decode)
08c9a385 45(require 'nnoo)
23f87bed 46
6748645f 47(autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
d4dfaa19 48(autoload 'gnus-cache-write-active "gnus-cache")
23f87bed
MB
49(autoload 'gnus-mailing-list-insinuate "gnus-ml" nil t)
50(autoload 'turn-on-gnus-mailing-list-mode "gnus-ml" nil t)
531e5812 51(autoload 'gnus-pick-line-number "gnus-salt" nil t)
08c9a385 52(autoload 'mm-uu-dissect "mm-uu")
23f87bed
MB
53(autoload 'gnus-article-outlook-deuglify-article "deuglify"
54 "Deuglify broken Outlook (Express) articles and redisplay."
55 t)
56(autoload 'gnus-article-outlook-unwrap-lines "deuglify" nil t)
57(autoload 'gnus-article-outlook-repair-attribution "deuglify" nil t)
58(autoload 'gnus-article-outlook-rearrange-citation "deuglify" nil t)
eec82323
LMI
59
60(defcustom gnus-kill-summary-on-exit t
61 "*If non-nil, kill the summary buffer when you exit from it.
62If nil, the summary will become a \"*Dead Summary*\" buffer, and
63it will be killed sometime later."
64 :group 'gnus-summary-exit
65 :type 'boolean)
66
01c52d31
MB
67(defcustom gnus-summary-next-group-on-exit t
68 "If non-nil, go to the next unread newsgroup on summary exit.
69See `gnus-group-goto-unread'."
70 :link '(custom-manual "(gnus)Group Maneuvering")
71 :group 'gnus-summary-exit
72 :version "23.0" ;; No Gnus
73 :type 'boolean)
74
eec82323
LMI
75(defcustom gnus-fetch-old-headers nil
76 "*Non-nil means that Gnus will try to build threads by grabbing old headers.
01c52d31
MB
77If an unread article in the group refers to an older, already
78read (or just marked as read) article, the old article will not
79normally be displayed in the Summary buffer. If this variable is
80t, Gnus will attempt to grab the headers to the old articles, and
81thereby build complete threads. If it has the value `some', all
82old headers will be fetched but only enough headers to connect
83otherwise loose threads will be displayed. This variable can
84also be a number. In that case, no more than that number of old
85headers will be fetched. If it has the value `invisible', all
6748645f 86old headers will be fetched, but none will be displayed.
eec82323 87
01c52d31
MB
88The server has to support NOV for any of this to work.
89
90This feature can seriously impact performance it ignores all
91locally cached header entries."
eec82323
LMI
92 :group 'gnus-thread
93 :type '(choice (const :tag "off" nil)
1232b9cb 94 (const :tag "on" t)
eec82323 95 (const some)
1232b9cb 96 (const invisible)
eec82323
LMI
97 number
98 (sexp :menu-tag "other" t)))
99
01c52d31 100(defcustom gnus-refer-thread-limit 500
6748645f
LMI
101 "*The number of old headers to fetch when doing \\<gnus-summary-mode-map>\\[gnus-summary-refer-thread].
102If t, fetch all the available old headers."
103 :group 'gnus-thread
104 :type '(choice number
105 (sexp :menu-tag "other" t)))
106
eec82323
LMI
107(defcustom gnus-summary-make-false-root 'adopt
108 "*nil means that Gnus won't gather loose threads.
109If the root of a thread has expired or been read in a previous
110session, the information necessary to build a complete thread has been
111lost. Instead of having many small sub-threads from this original thread
112scattered all over the summary buffer, Gnus can gather them.
113
114If non-nil, Gnus will try to gather all loose sub-threads from an
115original thread into one large thread.
116
117If this variable is non-nil, it should be one of `none', `adopt',
118`dummy' or `empty'.
119
120If this variable is `none', Gnus will not make a false root, but just
121present the sub-threads after another.
122If this variable is `dummy', Gnus will create a dummy root that will
123have all the sub-threads as children.
124If this variable is `adopt', Gnus will make one of the \"children\"
125the parent and mark all the step-children as such.
126If this variable is `empty', the \"children\" are printed with empty
01ccbb85 127subject fields. (Or rather, they will be printed with a string
eec82323
LMI
128given by the `gnus-summary-same-subject' variable.)"
129 :group 'gnus-thread
130 :type '(choice (const :tag "off" nil)
131 (const none)
132 (const dummy)
133 (const adopt)
134 (const empty)))
135
23f87bed
MB
136(defcustom gnus-summary-make-false-root-always nil
137 "Always make a false dummy root."
bf247b6e 138 :version "22.1"
23f87bed
MB
139 :group 'gnus-thread
140 :type 'boolean)
141
eec82323
LMI
142(defcustom gnus-summary-gather-exclude-subject "^ *$\\|^(none)$"
143 "*A regexp to match subjects to be excluded from loose thread gathering.
144As loose thread gathering is done on subjects only, that means that
145there can be many false gatherings performed. By rooting out certain
146common subjects, gathering might become saner."
147 :group 'gnus-thread
148 :type 'regexp)
149
150(defcustom gnus-summary-gather-subject-limit nil
151 "*Maximum length of subject comparisons when gathering loose threads.
152Use nil to compare full subjects. Setting this variable to a low
153number will help gather threads that have been corrupted by
154newsreaders chopping off subject lines, but it might also mean that
155unrelated articles that have subject that happen to begin with the
156same few characters will be incorrectly gathered.
157
158If this variable is `fuzzy', Gnus will use a fuzzy algorithm when
159comparing subjects."
160 :group 'gnus-thread
161 :type '(choice (const :tag "off" nil)
162 (const fuzzy)
163 (sexp :menu-tag "on" t)))
164
6748645f
LMI
165(defcustom gnus-simplify-subject-functions nil
166 "List of functions taking a string argument that simplify subjects.
167The functions are applied recursively.
168
23f87bed
MB
169Useful functions to put in this list include:
170`gnus-simplify-subject-re', `gnus-simplify-subject-fuzzy',
171`gnus-simplify-whitespace', and `gnus-simplify-all-whitespace'."
6748645f
LMI
172 :group 'gnus-thread
173 :type '(repeat function))
174
eec82323 175(defcustom gnus-simplify-ignored-prefixes nil
23f87bed 176 "*Remove matches for this regexp from subject lines when simplifying fuzzily."
eec82323
LMI
177 :group 'gnus-thread
178 :type '(choice (const :tag "off" nil)
179 regexp))
180
181(defcustom gnus-build-sparse-threads nil
182 "*If non-nil, fill in the gaps in threads.
183If `some', only fill in the gaps that are needed to tie loose threads
184together. If `more', fill in all leaf nodes that Gnus can find. If
185non-nil and non-`some', fill in all gaps that Gnus manages to guess."
186 :group 'gnus-thread
187 :type '(choice (const :tag "off" nil)
188 (const some)
189 (const more)
190 (sexp :menu-tag "all" t)))
191
192(defcustom gnus-summary-thread-gathering-function
193 'gnus-gather-threads-by-subject
6748645f 194 "*Function used for gathering loose threads.
eec82323
LMI
195There are two pre-defined functions: `gnus-gather-threads-by-subject',
196which only takes Subjects into consideration; and
197`gnus-gather-threads-by-references', which compared the References
198headers of the articles to find matches."
199 :group 'gnus-thread
22115a9e
RS
200 :type '(radio (function-item gnus-gather-threads-by-subject)
201 (function-item gnus-gather-threads-by-references)
202 (function :tag "other")))
eec82323 203
eec82323
LMI
204(defcustom gnus-summary-same-subject ""
205 "*String indicating that the current article has the same subject as the previous.
206This variable will only be used if the value of
207`gnus-summary-make-false-root' is `empty'."
208 :group 'gnus-summary-format
209 :type 'string)
210
211(defcustom gnus-summary-goto-unread t
16409b0b
GM
212 "*If t, many commands will go to the next unread article.
213This applies to marking commands as well as other commands that
214\"naturally\" select the next article, like, for instance, `SPC' at
215the end of an article.
216
217If nil, the marking commands do NOT go to the next unread article
2642ac8f 218\(they go to the next article instead). If `never', commands that
16409b0b
GM
219usually go to the next unread article, will go to the next article,
220whether it is read or not."
eec82323
LMI
221 :group 'gnus-summary-marks
222 :link '(custom-manual "(gnus)Setting Marks")
223 :type '(choice (const :tag "off" nil)
224 (const never)
225 (sexp :menu-tag "on" t)))
226
227(defcustom gnus-summary-default-score 0
228 "*Default article score level.
229All scores generated by the score files will be added to this score.
230If this variable is nil, scoring will be disabled."
231 :group 'gnus-score-default
232 :type '(choice (const :tag "disable")
233 integer))
234
23f87bed
MB
235(defcustom gnus-summary-default-high-score 0
236 "*Default threshold for a high scored article.
237An article will be highlighted as high scored if its score is greater
238than this score."
bf247b6e 239 :version "22.1"
23f87bed
MB
240 :group 'gnus-score-default
241 :type 'integer)
242
243(defcustom gnus-summary-default-low-score 0
244 "*Default threshold for a low scored article.
245An article will be highlighted as low scored if its score is smaller
246than this score."
bf247b6e 247 :version "22.1"
23f87bed
MB
248 :group 'gnus-score-default
249 :type 'integer)
250
eec82323
LMI
251(defcustom gnus-summary-zcore-fuzz 0
252 "*Fuzziness factor for the zcore in the summary buffer.
253Articles with scores closer than this to `gnus-summary-default-score'
254will not be marked."
255 :group 'gnus-summary-format
256 :type 'integer)
257
258(defcustom gnus-simplify-subject-fuzzy-regexp nil
259 "*Strings to be removed when doing fuzzy matches.
260This can either be a regular expression or list of regular expressions
261that will be removed from subject strings if fuzzy subject
262simplification is selected."
263 :group 'gnus-thread
264 :type '(repeat regexp))
265
266(defcustom gnus-show-threads t
267 "*If non-nil, display threads in summary mode."
268 :group 'gnus-thread
269 :type 'boolean)
270
271(defcustom gnus-thread-hide-subtree nil
272 "*If non-nil, hide all threads initially.
23f87bed 273This can be a predicate specifier which says which threads to hide.
eec82323 274If threads are hidden, you have to run the command
4a2358e9 275`gnus-summary-show-thread' by hand or select an article."
eec82323 276 :group 'gnus-thread
23f87bed
MB
277 :type '(radio (sexp :format "Non-nil\n"
278 :match (lambda (widget value)
279 (not (or (consp value) (functionp value))))
280 :value t)
281 (const nil)
ad136a7c 282 (sexp :tag "Predicate specifier")))
eec82323
LMI
283
284(defcustom gnus-thread-hide-killed t
285 "*If non-nil, hide killed threads automatically."
286 :group 'gnus-thread
287 :type 'boolean)
288
6748645f
LMI
289(defcustom gnus-thread-ignore-subject t
290 "*If non-nil, which is the default, ignore subjects and do all threading based on the Reference header.
291If nil, articles that have different subjects from their parents will
292start separate threads."
eec82323
LMI
293 :group 'gnus-thread
294 :type 'boolean)
295
296(defcustom gnus-thread-operation-ignore-subject t
297 "*If non-nil, subjects will be ignored when doing thread commands.
298This affects commands like `gnus-summary-kill-thread' and
299`gnus-summary-lower-thread'.
300
301If this variable is nil, articles in the same thread with different
302subjects will not be included in the operation in question. If this
303variable is `fuzzy', only articles that have subjects that are fuzzily
304equal will be included."
305 :group 'gnus-thread
306 :type '(choice (const :tag "off" nil)
307 (const fuzzy)
308 (sexp :tag "on" t)))
309
310(defcustom gnus-thread-indent-level 4
311 "*Number that says how much each sub-thread should be indented."
312 :group 'gnus-thread
313 :type 'integer)
314
315(defcustom gnus-auto-extend-newsgroup t
316 "*If non-nil, extend newsgroup forward and backward when requested."
317 :group 'gnus-summary-choose
318 :type 'boolean)
319
320(defcustom gnus-auto-select-first t
651408cb
MB
321 "If non-nil, select an article on group entry.
322An article is selected automatically when entering a group
323e.g. with \\<gnus-group-mode-map>\\[gnus-group-read-group], or via `gnus-summary-next-page' or
324`gnus-summary-catchup-and-goto-next-group'.
325
326Which article is selected is controlled by the variable
327`gnus-auto-select-subject'.
23f87bed
MB
328
329If you want to prevent automatic selection of articles in some
330newsgroups, set the variable to nil in `gnus-select-group-hook'."
651408cb
MB
331 ;; Commands include...
332 ;; \\<gnus-group-mode-map>\\[gnus-group-read-group]
333 ;; \\<gnus-summary-mode-map>\\[gnus-summary-next-page]
334 ;; \\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group]
eec82323
LMI
335 :group 'gnus-group-select
336 :type '(choice (const :tag "none" nil)
23f87bed
MB
337 (sexp :menu-tag "first" t)))
338
339(defcustom gnus-auto-select-subject 'unread
340 "*Says what subject to place under point when entering a group.
341
342This variable can either be the symbols `first' (place point on the
343first subject), `unread' (place point on the subject line of the first
344unread article), `best' (place point on the subject line of the
345higest-scored article), `unseen' (place point on the subject line of
99b5aab7 346the first unseen article), `unseen-or-unread' (place point on the subject
23f87bed
MB
347line of the first unseen article or, if all article have been seen, on the
348subject line of the first unread article), or a function to be called to
349place point on some subject line."
bf247b6e 350 :version "22.1"
23f87bed
MB
351 :group 'gnus-group-select
352 :type '(choice (const best)
353 (const unread)
354 (const first)
355 (const unseen)
356 (const unseen-or-unread)))
eec82323
LMI
357
358(defcustom gnus-auto-select-next t
359 "*If non-nil, offer to go to the next group from the end of the previous.
360If the value is t and the next newsgroup is empty, Gnus will exit
23f87bed
MB
361summary mode and go back to group mode. If the value is neither nil
362nor t, Gnus will select the following unread newsgroup. In
eec82323
LMI
363particular, if the value is the symbol `quietly', the next unread
364newsgroup will be selected without any confirmation, and if it is
365`almost-quietly', the next group will be selected without any
366confirmation if you are located on the last article in the group.
23f87bed 367Finally, if this variable is `slightly-quietly', the `\\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group]' command
eec82323
LMI
368will go to the next group without confirmation."
369 :group 'gnus-summary-maneuvering
370 :type '(choice (const :tag "off" nil)
371 (const quietly)
372 (const almost-quietly)
373 (const slightly-quietly)
374 (sexp :menu-tag "on" t)))
375
376(defcustom gnus-auto-select-same nil
6748645f
LMI
377 "*If non-nil, select the next article with the same subject.
378If there are no more articles with the same subject, go to
379the first unread article."
eec82323
LMI
380 :group 'gnus-summary-maneuvering
381 :type 'boolean)
382
01c52d31
MB
383(defcustom gnus-auto-select-on-ephemeral-exit 'next-noselect
384 "What article should be selected after exiting an ephemeral group.
385Valid values include:
386
387`next'
388 Select the next article.
389`next-unread'
390 Select the next unread article.
391`next-noselect'
392 Move the cursor to the next article. This is the default.
393`next-unread-noselect'
394 Move the cursor to the next unread article.
395
396If it has any other value or there is no next (unread) article, the
397article selected before entering to the ephemeral group will appear."
398 :version "23.0" ;; No Gnus
399 :group 'gnus-summary-maneuvering
400 :type '(choice :format "%{%t%}:\n %[Value Menu%] %v"
401 (const next) (const next-unread)
402 (const next-noselect) (const next-unread-noselect)
403 (sexp :tag "other" :value nil)))
404
23f87bed
MB
405(defcustom gnus-auto-goto-ignores 'unfetched
406 "*Says how to handle unfetched articles when maneuvering.
407
408This variable can either be the symbols nil (maneuver to any
409article), `undownloaded' (maneuvering while unplugged ignores articles
410that have not been fetched), `always-undownloaded' (maneuvering always
411ignores articles that have not been fetched), `unfetched' (maneuvering
412ignores articles whose headers have not been fetched).
413
414NOTE: The list of unfetched articles will always be nil when plugged
415and, when unplugged, a subset of the undownloaded article list."
bf247b6e 416 :version "22.1"
23f87bed
MB
417 :group 'gnus-summary-maneuvering
418 :type '(choice (const :tag "None" nil)
419 (const :tag "Undownloaded when unplugged" undownloaded)
420 (const :tag "Undownloaded" always-undownloaded)
421 (const :tag "Unfetched" unfetched)))
422
eec82323
LMI
423(defcustom gnus-summary-check-current nil
424 "*If non-nil, consider the current article when moving.
425The \"unread\" movement commands will stay on the same line if the
426current article is unread."
427 :group 'gnus-summary-maneuvering
428 :type 'boolean)
429
01c52d31 430(defcustom gnus-auto-center-summary 2
eec82323
LMI
431 "*If non-nil, always center the current summary buffer.
432In particular, if `vertical' do only vertical recentering. If non-nil
433and non-`vertical', do both horizontal and vertical recentering."
434 :group 'gnus-summary-maneuvering
435 :type '(choice (const :tag "none" nil)
436 (const vertical)
16409b0b 437 (integer :tag "height")
eec82323
LMI
438 (sexp :menu-tag "both" t)))
439
23f87bed
MB
440(defvar gnus-auto-center-group t
441 "*If non-nil, always center the group buffer.")
442
eec82323
LMI
443(defcustom gnus-show-all-headers nil
444 "*If non-nil, don't hide any headers."
445 :group 'gnus-article-hiding
446 :group 'gnus-article-headers
447 :type 'boolean)
448
449(defcustom gnus-summary-ignore-duplicates nil
450 "*If non-nil, ignore articles with identical Message-ID headers."
451 :group 'gnus-summary
452 :type 'boolean)
6748645f 453
eec82323
LMI
454(defcustom gnus-single-article-buffer t
455 "*If non-nil, display all articles in the same buffer.
456If nil, each group will get its own article buffer."
457 :group 'gnus-article-various
458 :type 'boolean)
459
460(defcustom gnus-break-pages t
461 "*If non-nil, do page breaking on articles.
462The page delimiter is specified by the `gnus-page-delimiter'
463variable."
464 :group 'gnus-article-various
465 :type 'boolean)
466
eec82323
LMI
467(defcustom gnus-move-split-methods nil
468 "*Variable used to suggest where articles are to be moved to.
23f87bed
MB
469It uses the same syntax as the `gnus-split-methods' variable.
470However, whereas `gnus-split-methods' specifies file names as targets,
471this variable specifies group names."
eec82323 472 :group 'gnus-summary-mail
6748645f
LMI
473 :type '(repeat (choice (list :value (fun) function)
474 (cons :value ("" "") regexp (repeat string))
475 (sexp :value nil))))
eec82323 476
01c52d31
MB
477(defcustom gnus-move-group-prefix-function 'gnus-group-real-prefix
478 "Function used to compute default prefix for article move/copy/etc prompts.
479The function should take one argument, a group name, and return a
480string with the suggested prefix."
481 :group 'gnus-summary-mail
482 :type 'function)
483
e62e7654
MB
484;; FIXME: Although the custom type is `character' for the following variables,
485;; using multibyte characters (Latin-1, UTF-8) doesn't work. -- rs
486
23f87bed 487(defcustom gnus-unread-mark ? ;Whitespace
eec82323
LMI
488 "*Mark used for unread articles."
489 :group 'gnus-summary-marks
490 :type 'character)
491
492(defcustom gnus-ticked-mark ?!
493 "*Mark used for ticked articles."
494 :group 'gnus-summary-marks
495 :type 'character)
496
497(defcustom gnus-dormant-mark ??
498 "*Mark used for dormant articles."
499 :group 'gnus-summary-marks
500 :type 'character)
501
502(defcustom gnus-del-mark ?r
503 "*Mark used for del'd articles."
504 :group 'gnus-summary-marks
505 :type 'character)
506
507(defcustom gnus-read-mark ?R
508 "*Mark used for read articles."
509 :group 'gnus-summary-marks
510 :type 'character)
511
512(defcustom gnus-expirable-mark ?E
513 "*Mark used for expirable articles."
514 :group 'gnus-summary-marks
515 :type 'character)
516
517(defcustom gnus-killed-mark ?K
518 "*Mark used for killed articles."
519 :group 'gnus-summary-marks
520 :type 'character)
521
23f87bed
MB
522(defcustom gnus-spam-mark ?$
523 "*Mark used for spam articles."
bf247b6e 524 :version "22.1"
23f87bed
MB
525 :group 'gnus-summary-marks
526 :type 'character)
527
eec82323 528(defcustom gnus-souped-mark ?F
23f87bed 529 "*Mark used for souped articles."
eec82323
LMI
530 :group 'gnus-summary-marks
531 :type 'character)
532
533(defcustom gnus-kill-file-mark ?X
534 "*Mark used for articles killed by kill files."
535 :group 'gnus-summary-marks
536 :type 'character)
537
538(defcustom gnus-low-score-mark ?Y
539 "*Mark used for articles with a low score."
540 :group 'gnus-summary-marks
541 :type 'character)
542
543(defcustom gnus-catchup-mark ?C
544 "*Mark used for articles that are caught up."
545 :group 'gnus-summary-marks
546 :type 'character)
547
548(defcustom gnus-replied-mark ?A
549 "*Mark used for articles that have been replied to."
550 :group 'gnus-summary-marks
551 :type 'character)
552
23f87bed
MB
553(defcustom gnus-forwarded-mark ?F
554 "*Mark used for articles that have been forwarded."
bf247b6e 555 :version "22.1"
23f87bed
MB
556 :group 'gnus-summary-marks
557 :type 'character)
558
559(defcustom gnus-recent-mark ?N
560 "*Mark used for articles that are recent."
bf247b6e 561 :version "22.1"
23f87bed
MB
562 :group 'gnus-summary-marks
563 :type 'character)
564
eec82323
LMI
565(defcustom gnus-cached-mark ?*
566 "*Mark used for articles that are in the cache."
567 :group 'gnus-summary-marks
568 :type 'character)
569
570(defcustom gnus-saved-mark ?S
23f87bed
MB
571 "*Mark used for articles that have been saved."
572 :group 'gnus-summary-marks
573 :type 'character)
574
575(defcustom gnus-unseen-mark ?.
576 "*Mark used for articles that haven't been seen."
bf247b6e 577 :version "22.1"
23f87bed
MB
578 :group 'gnus-summary-marks
579 :type 'character)
580
581(defcustom gnus-no-mark ? ;Whitespace
582 "*Mark used for articles that have no other secondary mark."
bf247b6e 583 :version "22.1"
eec82323
LMI
584 :group 'gnus-summary-marks
585 :type 'character)
586
587(defcustom gnus-ancient-mark ?O
588 "*Mark used for ancient articles."
589 :group 'gnus-summary-marks
590 :type 'character)
591
592(defcustom gnus-sparse-mark ?Q
593 "*Mark used for sparsely reffed articles."
594 :group 'gnus-summary-marks
595 :type 'character)
596
597(defcustom gnus-canceled-mark ?G
598 "*Mark used for canceled articles."
599 :group 'gnus-summary-marks
600 :type 'character)
601
602(defcustom gnus-duplicate-mark ?M
603 "*Mark used for duplicate articles."
604 :group 'gnus-summary-marks
605 :type 'character)
606
23f87bed 607(defcustom gnus-undownloaded-mark ?-
6748645f 608 "*Mark used for articles that weren't downloaded."
bf247b6e 609 :version "22.1"
6748645f
LMI
610 :group 'gnus-summary-marks
611 :type 'character)
612
23f87bed
MB
613(defcustom gnus-downloaded-mark ?+
614 "*Mark used for articles that were downloaded."
615 :group 'gnus-summary-marks
616 :type 'character)
617
6748645f
LMI
618(defcustom gnus-downloadable-mark ?%
619 "*Mark used for articles that are to be downloaded."
620 :group 'gnus-summary-marks
621 :type 'character)
622
623(defcustom gnus-unsendable-mark ?=
624 "*Mark used for articles that won't be sent."
625 :group 'gnus-summary-marks
626 :type 'character)
627
eec82323
LMI
628(defcustom gnus-score-over-mark ?+
629 "*Score mark used for articles with high scores."
630 :group 'gnus-summary-marks
631 :type 'character)
632
633(defcustom gnus-score-below-mark ?-
634 "*Score mark used for articles with low scores."
635 :group 'gnus-summary-marks
636 :type 'character)
637
23f87bed 638(defcustom gnus-empty-thread-mark ? ;Whitespace
eec82323
LMI
639 "*There is no thread under the article."
640 :group 'gnus-summary-marks
641 :type 'character)
642
643(defcustom gnus-not-empty-thread-mark ?=
644 "*There is a thread under the article."
645 :group 'gnus-summary-marks
646 :type 'character)
647
648(defcustom gnus-view-pseudo-asynchronously nil
649 "*If non-nil, Gnus will view pseudo-articles asynchronously."
650 :group 'gnus-extract-view
651 :type 'boolean)
652
16409b0b
GM
653(defcustom gnus-auto-expirable-marks
654 (list gnus-killed-mark gnus-del-mark gnus-catchup-mark
655 gnus-low-score-mark gnus-ancient-mark gnus-read-mark
656 gnus-souped-mark gnus-duplicate-mark)
657 "*The list of marks converted into expiration if a group is auto-expirable."
58e39d05 658 :version "21.1"
16409b0b
GM
659 :group 'gnus-summary
660 :type '(repeat character))
661
662(defcustom gnus-inhibit-user-auto-expire t
663 "*If non-nil, user marking commands will not mark an article as expirable, even if the group has auto-expire turned on."
58e39d05 664 :version "21.1"
16409b0b
GM
665 :group 'gnus-summary
666 :type 'boolean)
667
eec82323
LMI
668(defcustom gnus-view-pseudos nil
669 "*If `automatic', pseudo-articles will be viewed automatically.
670If `not-confirm', pseudos will be viewed automatically, and the user
671will not be asked to confirm the command."
672 :group 'gnus-extract-view
673 :type '(choice (const :tag "off" nil)
674 (const automatic)
675 (const not-confirm)))
676
677(defcustom gnus-view-pseudos-separately t
678 "*If non-nil, one pseudo-article will be created for each file to be viewed.
679If nil, all files that use the same viewing command will be given as a
680list of parameters to that command."
681 :group 'gnus-extract-view
682 :type 'boolean)
683
684(defcustom gnus-insert-pseudo-articles t
685 "*If non-nil, insert pseudo-articles when decoding articles."
686 :group 'gnus-extract-view
687 :type 'boolean)
688
689(defcustom gnus-summary-dummy-line-format
23f87bed 690 " %(: :%) %S\n"
eec82323
LMI
691 "*The format specification for the dummy roots in the summary buffer.
692It works along the same lines as a normal formatting string,
693with some simple extensions.
694
23f87bed
MB
695%S The subject
696
697General format specifiers can also be used.
698See `(gnus)Formatting Variables'."
699 :link '(custom-manual "(gnus)Formatting Variables")
eec82323
LMI
700 :group 'gnus-threading
701 :type 'string)
702
16409b0b 703(defcustom gnus-summary-mode-line-format "Gnus: %g [%A] %Z"
eec82323
LMI
704 "*The format specification for the summary mode line.
705It works along the same lines as a normal formatting string,
706with some simple extensions:
707
708%G Group name
709%p Unprefixed group name
710%A Current article number
6748645f 711%z Current article score
eec82323
LMI
712%V Gnus version
713%U Number of unread articles in the group
714%e Number of unselected articles in the group
715%Z A string with unread/unselected article counts
716%g Shortish group name
717%S Subject of the current article
718%u User-defined spec
719%s Current score file name
720%d Number of dormant articles
721%r Number of articles that have been marked as read in this session
722%E Number of articles expunged by the score files"
723 :group 'gnus-summary-format
724 :type 'string)
725
16409b0b
GM
726(defcustom gnus-list-identifiers nil
727 "Regexp that matches list identifiers to be removed from subject.
728This can also be a list of regexps."
58e39d05 729 :version "21.1"
16409b0b
GM
730 :group 'gnus-summary-format
731 :group 'gnus-article-hiding
732 :type '(choice (const :tag "none" nil)
733 (regexp :value ".*")
734 (repeat :value (".*") regexp)))
735
eec82323
LMI
736(defcustom gnus-summary-mark-below 0
737 "*Mark all articles with a score below this variable as read.
738This variable is local to each summary buffer and usually set by the
739score file."
740 :group 'gnus-score-default
741 :type 'integer)
742
01c52d31
MB
743(defun gnus-widget-reversible-match (widget value)
744 "Ignoring WIDGET, convert VALUE to internal form.
745VALUE should have the form `FOO' or `(not FOO)', where FOO is an symbol."
746 ;; (debug value)
747 (or (symbolp value)
748 (and (listp value)
749 (eq (length value) 2)
750 (eq (nth 0 value) 'not)
751 (symbolp (nth 1 value)))))
752
753(defun gnus-widget-reversible-to-internal (widget value)
754 "Ignoring WIDGET, convert VALUE to internal form.
755VALUE should have the form `FOO' or `(not FOO)', where FOO is an atom.
756FOO is converted to (FOO nil) and (not FOO) is converted to (FOO t)."
757 ;; (debug value)
758 (if (atom value)
759 (list value nil)
760 (list (nth 1 value) t)))
761
762(defun gnus-widget-reversible-to-external (widget value)
763 "Ignoring WIDGET, convert VALUE to external form.
764VALUE should have the form `(FOO nil)' or `(FOO t)', where FOO is an atom.
765\(FOO nil) is converted to FOO and (FOO t) is converted to (not FOO)."
766 ;; (debug value)
767 (if (nth 1 value)
768 (list 'not (nth 0 value))
769 (nth 0 value)))
770
771(define-widget 'gnus-widget-reversible 'group
772 "A `group' that convert values."
773 :match 'gnus-widget-reversible-match
774 :value-to-internal 'gnus-widget-reversible-to-internal
775 :value-to-external 'gnus-widget-reversible-to-external)
776
eec82323
LMI
777(defcustom gnus-article-sort-functions '(gnus-article-sort-by-number)
778 "*List of functions used for sorting articles in the summary buffer.
23f87bed
MB
779
780Each function takes two articles and returns non-nil if the first
781article should be sorted before the other. If you use more than one
782function, the primary sort function should be the last. You should
783probably always include `gnus-article-sort-by-number' in the list of
784sorting functions -- preferably first. Also note that sorting by date
785is often much slower than sorting by number, and the sorting order is
786very similar. (Sorting by date means sorting by the time the message
787was sent, sorting by number means sorting by arrival time.)
788
01c52d31
MB
789Each item can also be a list `(not F)' where F is a function;
790this reverses the sort order.
791
23f87bed
MB
792Ready-made functions include `gnus-article-sort-by-number',
793`gnus-article-sort-by-author', `gnus-article-sort-by-subject',
794`gnus-article-sort-by-date', `gnus-article-sort-by-random'
795and `gnus-article-sort-by-score'.
796
797When threading is turned on, the variable `gnus-thread-sort-functions'
798controls how articles are sorted."
eec82323 799 :group 'gnus-summary-sort
01c52d31
MB
800 :type '(repeat (gnus-widget-reversible
801 (choice (function-item gnus-article-sort-by-number)
802 (function-item gnus-article-sort-by-author)
803 (function-item gnus-article-sort-by-subject)
804 (function-item gnus-article-sort-by-date)
805 (function-item gnus-article-sort-by-score)
806 (function-item gnus-article-sort-by-random)
807 (function :tag "other"))
808 (boolean :tag "Reverse order"))))
809
eec82323
LMI
810
811(defcustom gnus-thread-sort-functions '(gnus-thread-sort-by-number)
812 "*List of functions used for sorting threads in the summary buffer.
813By default, threads are sorted by article number.
814
23f87bed
MB
815Each function takes two threads and returns non-nil if the first
816thread should be sorted before the other. If you use more than one
817function, the primary sort function should be the last. You should
818probably always include `gnus-thread-sort-by-number' in the list of
819sorting functions -- preferably first. Also note that sorting by date
820is often much slower than sorting by number, and the sorting order is
821very similar. (Sorting by date means sorting by the time the message
822was sent, sorting by number means sorting by arrival time.)
eec82323 823
01c52d31
MB
824Each list item can also be a list `(not F)' where F is a
825function; this specifies reversed sort order.
826
eec82323 827Ready-made functions include `gnus-thread-sort-by-number',
01c52d31
MB
828`gnus-thread-sort-by-author', `gnus-thread-sort-by-recipient'
829`gnus-thread-sort-by-subject', `gnus-thread-sort-by-date',
830`gnus-thread-sort-by-score', `gnus-thread-sort-by-most-recent-number',
831`gnus-thread-sort-by-most-recent-date', `gnus-thread-sort-by-random',
832and `gnus-thread-sort-by-total-score' (see
833`gnus-thread-score-function').
23f87bed
MB
834
835When threading is turned off, the variable
836`gnus-article-sort-functions' controls how articles are sorted."
eec82323 837 :group 'gnus-summary-sort
01c52d31
MB
838 :type '(repeat
839 (gnus-widget-reversible
840 (choice (function-item gnus-thread-sort-by-number)
841 (function-item gnus-thread-sort-by-author)
842 (function-item gnus-thread-sort-by-recipient)
843 (function-item gnus-thread-sort-by-subject)
844 (function-item gnus-thread-sort-by-date)
845 (function-item gnus-thread-sort-by-score)
846 (function-item gnus-thread-sort-by-most-recent-number)
847 (function-item gnus-thread-sort-by-most-recent-date)
848 (function-item gnus-thread-sort-by-random)
849 (function-item gnus-thread-sort-by-total-score)
850 (function :tag "other"))
851 (boolean :tag "Reverse order"))))
eec82323
LMI
852
853(defcustom gnus-thread-score-function '+
854 "*Function used for calculating the total score of a thread.
855
856The function is called with the scores of the article and each
857subthread and should then return the score of the thread.
858
859Some functions you can use are `+', `max', or `min'."
860 :group 'gnus-summary-sort
861 :type 'function)
862
863(defcustom gnus-summary-expunge-below nil
6748645f
LMI
864 "All articles that have a score less than this variable will be expunged.
865This variable is local to the summary buffers."
eec82323
LMI
866 :group 'gnus-score-default
867 :type '(choice (const :tag "off" nil)
868 integer))
869
870(defcustom gnus-thread-expunge-below nil
871 "All threads that have a total score less than this variable will be expunged.
872See `gnus-thread-score-function' for en explanation of what a
6748645f
LMI
873\"thread score\" is.
874
875This variable is local to the summary buffers."
16409b0b 876 :group 'gnus-threading
eec82323
LMI
877 :group 'gnus-score-default
878 :type '(choice (const :tag "off" nil)
879 integer))
880
881(defcustom gnus-summary-mode-hook nil
882 "*A hook for Gnus summary mode.
883This hook is run before any variables are set in the summary buffer."
23f87bed 884 :options '(turn-on-gnus-mailing-list-mode gnus-pick-mode)
eec82323
LMI
885 :group 'gnus-summary-various
886 :type 'hook)
887
23f87bed
MB
888;; Extracted from gnus-xmas-redefine in order to preserve user settings
889(when (featurep 'xemacs)
890 (add-hook 'gnus-summary-mode-hook 'gnus-xmas-summary-menu-add)
891 (add-hook 'gnus-summary-mode-hook 'gnus-xmas-setup-summary-toolbar)
892 (add-hook 'gnus-summary-mode-hook
893 'gnus-xmas-switch-horizontal-scrollbar-off))
894
eec82323
LMI
895(defcustom gnus-summary-menu-hook nil
896 "*Hook run after the creation of the summary mode menu."
897 :group 'gnus-summary-visual
898 :type 'hook)
899
900(defcustom gnus-summary-exit-hook nil
901 "*A hook called on exit from the summary buffer.
902It will be called with point in the group buffer."
903 :group 'gnus-summary-exit
904 :type 'hook)
905
906(defcustom gnus-summary-prepare-hook nil
907 "*A hook called after the summary buffer has been generated.
908If you want to modify the summary buffer, you can use this hook."
909 :group 'gnus-summary-various
910 :type 'hook)
911
6748645f
LMI
912(defcustom gnus-summary-prepared-hook nil
913 "*A hook called as the last thing after the summary buffer has been generated."
914 :group 'gnus-summary-various
915 :type 'hook)
916
eec82323
LMI
917(defcustom gnus-summary-generate-hook nil
918 "*A hook run just before generating the summary buffer.
919This hook is commonly used to customize threading variables and the
920like."
921 :group 'gnus-summary-various
922 :type 'hook)
923
924(defcustom gnus-select-group-hook nil
925 "*A hook called when a newsgroup is selected.
926
927If you'd like to simplify subjects like the
928`gnus-summary-next-same-subject' command does, you can use the
929following hook:
930
23f87bed
MB
931 (add-hook gnus-select-group-hook
932 (lambda ()
933 (mapcar (lambda (header)
934 (mail-header-set-subject
935 header
936 (gnus-simplify-subject
937 (mail-header-subject header) 're-only)))
938 gnus-newsgroup-headers)))"
eec82323
LMI
939 :group 'gnus-group-select
940 :type 'hook)
941
942(defcustom gnus-select-article-hook nil
943 "*A hook called when an article is selected."
944 :group 'gnus-summary-choose
23f87bed 945 :options '(gnus-agent-fetch-selected-article)
eec82323
LMI
946 :type 'hook)
947
948(defcustom gnus-visual-mark-article-hook
949 (list 'gnus-highlight-selected-summary)
950 "*Hook run after selecting an article in the summary buffer.
951It is meant to be used for highlighting the article in some way. It
952is not run if `gnus-visual' is nil."
953 :group 'gnus-summary-visual
954 :type 'hook)
955
16409b0b 956(defcustom gnus-parse-headers-hook nil
eec82323
LMI
957 "*A hook called before parsing the headers."
958 :group 'gnus-various
959 :type 'hook)
960
961(defcustom gnus-exit-group-hook nil
16409b0b
GM
962 "*A hook called when exiting summary mode.
963This hook is not called from the non-updating exit commands like `Q'."
eec82323
LMI
964 :group 'gnus-various
965 :type 'hook)
966
967(defcustom gnus-summary-update-hook
968 (list 'gnus-summary-highlight-line)
969 "*A hook called when a summary line is changed.
970The hook will not be called if `gnus-visual' is nil.
971
972The default function `gnus-summary-highlight-line' will
973highlight the line according to the `gnus-summary-highlight'
974variable."
975 :group 'gnus-summary-visual
976 :type 'hook)
977
978(defcustom gnus-mark-article-hook '(gnus-summary-mark-read-and-unread-as-read)
979 "*A hook called when an article is selected for the first time.
980The hook is intended to mark an article as read (or unread)
981automatically when it is selected."
982 :group 'gnus-summary-choose
983 :type 'hook)
984
985(defcustom gnus-group-no-more-groups-hook nil
986 "*A hook run when returning to group mode having no more (unread) groups."
987 :group 'gnus-group-select
988 :type 'hook)
989
990(defcustom gnus-ps-print-hook nil
991 "*A hook run before ps-printing something from Gnus."
992 :group 'gnus-summary
993 :type 'hook)
994
23f87bed
MB
995(defcustom gnus-summary-article-move-hook nil
996 "*A hook called after an article is moved, copied, respooled, or crossposted."
bf247b6e 997 :version "22.1"
23f87bed
MB
998 :group 'gnus-summary
999 :type 'hook)
1000
1001(defcustom gnus-summary-article-delete-hook nil
1002 "*A hook called after an article is deleted."
bf247b6e 1003 :version "22.1"
23f87bed
MB
1004 :group 'gnus-summary
1005 :type 'hook)
1006
1007(defcustom gnus-summary-article-expire-hook nil
1008 "*A hook called after an article is expired."
bf247b6e 1009 :version "22.1"
23f87bed
MB
1010 :group 'gnus-summary
1011 :type 'hook)
1012
1013(defcustom gnus-summary-display-arrow
1014 (and (fboundp 'display-graphic-p)
1015 (display-graphic-p))
1016 "*If non-nil, display an arrow highlighting the current article."
bf247b6e 1017 :version "22.1"
23f87bed
MB
1018 :group 'gnus-summary
1019 :type 'boolean)
1020
0f49874b 1021(defcustom gnus-summary-selected-face 'gnus-summary-selected
eec82323
LMI
1022 "Face used for highlighting the current article in the summary buffer."
1023 :group 'gnus-summary-visual
1024 :type 'face)
1025
23f87bed
MB
1026(defvar gnus-tmp-downloaded nil)
1027
eec82323 1028(defcustom gnus-summary-highlight
23f87bed 1029 '(((eq mark gnus-canceled-mark)
0f49874b 1030 . gnus-summary-cancelled)
23f87bed 1031 ((and uncached (> score default-high))
0f49874b 1032 . gnus-summary-high-undownloaded)
23f87bed 1033 ((and uncached (< score default-low))
0f49874b 1034 . gnus-summary-low-undownloaded)
23f87bed 1035 (uncached
0f49874b 1036 . gnus-summary-normal-undownloaded)
23f87bed
MB
1037 ((and (> score default-high)
1038 (or (eq mark gnus-dormant-mark)
1039 (eq mark gnus-ticked-mark)))
0f49874b 1040 . gnus-summary-high-ticked)
23f87bed
MB
1041 ((and (< score default-low)
1042 (or (eq mark gnus-dormant-mark)
1043 (eq mark gnus-ticked-mark)))
0f49874b 1044 . gnus-summary-low-ticked)
23f87bed
MB
1045 ((or (eq mark gnus-dormant-mark)
1046 (eq mark gnus-ticked-mark))
0f49874b 1047 . gnus-summary-normal-ticked)
23f87bed 1048 ((and (> score default-high) (eq mark gnus-ancient-mark))
0f49874b 1049 . gnus-summary-high-ancient)
23f87bed 1050 ((and (< score default-low) (eq mark gnus-ancient-mark))
0f49874b 1051 . gnus-summary-low-ancient)
23f87bed 1052 ((eq mark gnus-ancient-mark)
0f49874b 1053 . gnus-summary-normal-ancient)
23f87bed 1054 ((and (> score default-high) (eq mark gnus-unread-mark))
0f49874b 1055 . gnus-summary-high-unread)
23f87bed 1056 ((and (< score default-low) (eq mark gnus-unread-mark))
0f49874b 1057 . gnus-summary-low-unread)
23f87bed 1058 ((eq mark gnus-unread-mark)
0f49874b 1059 . gnus-summary-normal-unread)
23f87bed 1060 ((> score default-high)
0f49874b 1061 . gnus-summary-high-read)
23f87bed 1062 ((< score default-low)
0f49874b 1063 . gnus-summary-low-read)
eec82323 1064 (t
0f49874b 1065 . gnus-summary-normal-read))
6748645f 1066 "*Controls the highlighting of summary buffer lines.
eec82323 1067
107cf8ec 1068A list of (FORM . FACE) pairs. When deciding how a particular
23f87bed
MB
1069summary line should be displayed, each form is evaluated. The content
1070of the face field after the first true form is used. You can change
1071how those summary lines are displayed, by editing the face field.
eec82323
LMI
1072
1073You can use the following variables in the FORM field.
1074
107cf8ec 1075score: The article's score.
23f87bed
MB
1076default: The default article score.
1077default-high: The default score for high scored articles.
1078default-low: The default score for low scored articles.
1079below: The score below which articles are automatically marked as read.
1080mark: The article's mark.
1081uncached: Non-nil if the article is uncached."
eec82323
LMI
1082 :group 'gnus-summary-visual
1083 :type '(repeat (cons (sexp :tag "Form" nil)
1084 face)))
c12ecb0a 1085(put 'gnus-summary-highlight 'risky-local-variable t)
eec82323 1086
6748645f
LMI
1087(defcustom gnus-alter-header-function nil
1088 "Function called to allow alteration of article header structures.
1089The function is called with one parameter, the article header vector,
0ab0f2d3
SZ
1090which it may alter in any way."
1091 :type '(choice (const :tag "None" nil)
1092 function)
1093 :group 'gnus-summary)
eec82323 1094
16409b0b 1095(defvar gnus-decode-encoded-word-function 'mail-decode-encoded-word-string
343d6628
MB
1096 "Function used to decode a string with encoded words.")
1097
1098(defvar gnus-decode-encoded-address-function
1099 'mail-decode-encoded-address-string
1100 "Function used to decode addresses with encoded words.")
16409b0b 1101
23f87bed 1102(defcustom gnus-extra-headers '(To Newsgroups)
16409b0b 1103 "*Extra headers to parse."
58e39d05 1104 :version "21.1"
16409b0b
GM
1105 :group 'gnus-summary
1106 :type '(repeat symbol))
1107
1108(defcustom gnus-ignored-from-addresses
343d6628 1109 (and user-mail-address
7cd9f860
CY
1110 (not (string= user-mail-address ""))
1111 (regexp-quote user-mail-address))
01c52d31
MB
1112 "*From headers that may be suppressed in favor of To headers.
1113This can be a regexp or a list of regexps."
58e39d05 1114 :version "21.1"
16409b0b 1115 :group 'gnus-summary
01c52d31
MB
1116 :type '(choice regexp
1117 (repeat :tag "Regexp List" regexp)))
1118
1119(defsubst gnus-ignored-from-addresses ()
1120 (gmm-regexp-concat gnus-ignored-from-addresses))
1121
1122(defcustom gnus-summary-to-prefix "-> "
1123 "*String prefixed to the To field in the summary line when
1124using `gnus-ignored-from-addresses'."
1125 :version "22.1"
1126 :group 'gnus-summary
1127 :type 'string)
1128
1129(defcustom gnus-summary-newsgroup-prefix "=> "
1130 "*String prefixed to the Newsgroup field in the summary
1131line when using `gnus-ignored-from-addresses'."
1132 :version "22.1"
1133 :group 'gnus-summary
1134 :type 'string)
16409b0b 1135
16409b0b
GM
1136(defcustom gnus-newsgroup-ignored-charsets '(unknown-8bit x-unknown)
1137 "List of charsets that should be ignored.
1138When these charsets are used in the \"charset\" parameter, the
1139default charset will be used instead."
58e39d05 1140 :version "21.1"
16409b0b
GM
1141 :type '(repeat symbol)
1142 :group 'gnus-charset)
1143
4b70e299
MB
1144(defcustom gnus-newsgroup-maximum-articles nil
1145 "The maximum number of articles a newsgroup.
1146If this is a number, old articles in a newsgroup exceeding this number
1147are silently ignored. If it is nil, no article is ignored. Note that
1148setting this variable to a number might prevent you from reading very
1149old articles."
1150 :group 'gnus-group-select
1151 :version "22.2"
1152 :type '(choice (const :tag "No limit" nil)
1153 integer))
1154
23f87bed
MB
1155(gnus-define-group-parameter
1156 ignored-charsets
1157 :type list
1158 :function-document
1159 "Return the ignored charsets of GROUP."
1160 :variable gnus-group-ignored-charsets-alist
1161 :variable-default
1162 '(("alt\\.chinese\\.text" iso-8859-1))
1163 :variable-document
1164 "Alist of regexps (to match group names) and charsets that should be ignored.
16409b0b
GM
1165When these charsets are used in the \"charset\" parameter, the
1166default charset will be used instead."
23f87bed
MB
1167 :variable-group gnus-charset
1168 :variable-type '(repeat (cons (regexp :tag "Group")
1169 (repeat symbol)))
1170 :parameter-type '(choice :tag "Ignored charsets"
1171 :value nil
1172 (repeat (symbol)))
1173 :parameter-document "\
1174List of charsets that should be ignored.
1175
1176When these charsets are used in the \"charset\" parameter, the
1177default charset will be used instead.")
16409b0b
GM
1178
1179(defcustom gnus-group-highlight-words-alist nil
1180 "Alist of group regexps and highlight regexps.
1181This variable uses the same syntax as `gnus-emphasis-alist'."
58e39d05 1182 :version "21.1"
16409b0b
GM
1183 :type '(repeat (cons (regexp :tag "Group")
1184 (repeat (list (regexp :tag "Highlight regexp")
1185 (number :tag "Group for entire word" 0)
1186 (number :tag "Group for displayed part" 0)
1187 (symbol :tag "Face"
1188 gnus-emphasis-highlight-words)))))
1189 :group 'gnus-summary-visual)
1190
1191(defcustom gnus-summary-show-article-charset-alist
1192 nil
1193 "Alist of number and charset.
1194The article will be shown with the charset corresponding to the
1195numbered argument.
1196For example: ((1 . cn-gb-2312) (2 . big5))."
58e39d05 1197 :version "21.1"
16409b0b
GM
1198 :type '(repeat (cons (number :tag "Argument" 1)
1199 (symbol :tag "Charset")))
1200 :group 'gnus-charset)
1201
1202(defcustom gnus-preserve-marks t
1203 "Whether marks are preserved when moving, copying and respooling messages."
58e39d05 1204 :version "21.1"
16409b0b
GM
1205 :type 'boolean
1206 :group 'gnus-summary-marks)
1207
1208(defcustom gnus-alter-articles-to-read-function nil
1209 "Function to be called to alter the list of articles to be selected."
8fc7a9a1 1210 :type '(choice (const nil) function)
16409b0b
GM
1211 :group 'gnus-summary)
1212
1213(defcustom gnus-orphan-score nil
1214 "*All orphans get this score added. Set in the score file."
1215 :group 'gnus-score-default
1216 :type '(choice (const nil)
1217 integer))
1218
8b93df01 1219(defcustom gnus-summary-save-parts-default-mime "image/.*"
23f87bed
MB
1220 "*A regexp to match MIME parts when saving multiple parts of a
1221message with `gnus-summary-save-parts' (\\<gnus-summary-mode-map>\\[gnus-summary-save-parts]).
1222This regexp will be used by default when prompting the user for which
1223type of files to save."
8b93df01
DL
1224 :group 'gnus-summary
1225 :type 'regexp)
1226
23f87bed
MB
1227(defcustom gnus-read-all-available-headers nil
1228 "Whether Gnus should parse all headers made available to it.
1229This is mostly relevant for slow back ends where the user may
1230wish to widen the summary buffer to include all headers
1231that were fetched. Say, for nnultimate groups."
bf247b6e 1232 :version "22.1"
23f87bed
MB
1233 :group 'gnus-summary
1234 :type '(choice boolean regexp))
1235
1236(defcustom gnus-summary-muttprint-program "muttprint"
1237 "Command (and optional arguments) used to run Muttprint."
bf247b6e 1238 :version "22.1"
23f87bed
MB
1239 :group 'gnus-summary
1240 :type 'string)
1241
01c52d31 1242(defcustom gnus-article-loose-mime t
23f87bed
MB
1243 "If non-nil, don't require MIME-Version header.
1244Some brain-damaged MUA/MTA, e.g. Lotus Domino 5.0.6 clients, does not
a08b59c9 1245supply the MIME-Version header or deliberately strip it from the mail.
01c52d31
MB
1246If non-nil (the default), Gnus will treat some articles as MIME
1247even if the MIME-Version header is missing."
bf247b6e 1248 :version "22.1"
23f87bed
MB
1249 :type 'boolean
1250 :group 'gnus-article-mime)
1251
1252(defcustom gnus-article-emulate-mime t
1253 "If non-nil, use MIME emulation for uuencode and the like.
1254This means that Gnus will search message bodies for text that look
1255like uuencoded bits, yEncoded bits, and so on, and present that using
1256the normal Gnus MIME machinery."
bf247b6e 1257 :version "22.1"
23f87bed
MB
1258 :type 'boolean
1259 :group 'gnus-article-mime)
8b93df01 1260
eec82323
LMI
1261;;; Internal variables
1262
23f87bed 1263(defvar gnus-summary-display-cache nil)
16409b0b
GM
1264(defvar gnus-article-mime-handles nil)
1265(defvar gnus-article-decoded-p nil)
23f87bed
MB
1266(defvar gnus-article-charset nil)
1267(defvar gnus-article-ignored-charsets nil)
eec82323
LMI
1268(defvar gnus-scores-exclude-files nil)
1269(defvar gnus-page-broken nil)
1270
1271(defvar gnus-original-article nil)
1272(defvar gnus-article-internal-prepare-hook nil)
1273(defvar gnus-newsgroup-process-stack nil)
1274
1275(defvar gnus-thread-indent-array nil)
1276(defvar gnus-thread-indent-array-level gnus-thread-indent-level)
16409b0b
GM
1277(defvar gnus-sort-gathered-threads-function 'gnus-thread-sort-by-number
1278 "Function called to sort the articles within a thread after it has been gathered together.")
eec82323 1279
8b93df01 1280(defvar gnus-summary-save-parts-type-history nil)
23f87bed 1281(defvar gnus-summary-save-parts-last-directory mm-default-directory)
8b93df01 1282
eec82323
LMI
1283;; Avoid highlighting in kill files.
1284(defvar gnus-summary-inhibit-highlight nil)
1285(defvar gnus-newsgroup-selected-overlay nil)
1286(defvar gnus-inhibit-limiting nil)
1287(defvar gnus-newsgroup-adaptive-score-file nil)
1288(defvar gnus-current-score-file nil)
1289(defvar gnus-current-move-group nil)
1290(defvar gnus-current-copy-group nil)
1291(defvar gnus-current-crosspost-group nil)
23f87bed 1292(defvar gnus-newsgroup-display nil)
eec82323
LMI
1293
1294(defvar gnus-newsgroup-dependencies nil)
1295(defvar gnus-newsgroup-adaptive nil)
1296(defvar gnus-summary-display-article-function nil)
1297(defvar gnus-summary-highlight-line-function nil
1298 "Function called after highlighting a summary line.")
1299
1300(defvar gnus-summary-line-format-alist
1301 `((?N ,(macroexpand '(mail-header-number gnus-tmp-header)) ?d)
1302 (?S ,(macroexpand '(mail-header-subject gnus-tmp-header)) ?s)
1303 (?s gnus-tmp-subject-or-nil ?s)
1304 (?n gnus-tmp-name ?s)
1305 (?A (car (cdr (funcall gnus-extract-address-components gnus-tmp-from)))
1306 ?s)
1307 (?a (or (car (funcall gnus-extract-address-components gnus-tmp-from))
1308 gnus-tmp-from) ?s)
1309 (?F gnus-tmp-from ?s)
1310 (?x ,(macroexpand '(mail-header-xref gnus-tmp-header)) ?s)
1311 (?D ,(macroexpand '(mail-header-date gnus-tmp-header)) ?s)
1312 (?d (gnus-dd-mmm (mail-header-date gnus-tmp-header)) ?s)
6748645f 1313 (?o (gnus-date-iso8601 (mail-header-date gnus-tmp-header)) ?s)
eec82323
LMI
1314 (?M ,(macroexpand '(mail-header-id gnus-tmp-header)) ?s)
1315 (?r ,(macroexpand '(mail-header-references gnus-tmp-header)) ?s)
1316 (?c (or (mail-header-chars gnus-tmp-header) 0) ?d)
23f87bed
MB
1317 (?k (gnus-summary-line-message-size gnus-tmp-header) ?s)
1318 (?L gnus-tmp-lines ?s)
1319 (?O gnus-tmp-downloaded ?c)
eec82323
LMI
1320 (?I gnus-tmp-indentation ?s)
1321 (?T (if (= gnus-tmp-level 0) "" (make-string (frame-width) ? )) ?s)
1322 (?R gnus-tmp-replied ?c)
1323 (?\[ gnus-tmp-opening-bracket ?c)
1324 (?\] gnus-tmp-closing-bracket ?c)
1325 (?\> (make-string gnus-tmp-level ? ) ?s)
1326 (?\< (make-string (max 0 (- 20 gnus-tmp-level)) ? ) ?s)
1327 (?i gnus-tmp-score ?d)
1328 (?z gnus-tmp-score-char ?c)
eec82323
LMI
1329 (?V (gnus-thread-total-score (and (boundp 'thread) (car thread))) ?d)
1330 (?U gnus-tmp-unread ?c)
23f87bed
MB
1331 (?f (gnus-summary-from-or-to-or-newsgroups gnus-tmp-header gnus-tmp-from)
1332 ?s)
eec82323
LMI
1333 (?t (gnus-summary-number-of-articles-in-thread
1334 (and (boundp 'thread) (car thread)) gnus-tmp-level)
1335 ?d)
1336 (?e (gnus-summary-number-of-articles-in-thread
1337 (and (boundp 'thread) (car thread)) gnus-tmp-level t)
1338 ?c)
1339 (?u gnus-tmp-user-defined ?s)
23f87bed
MB
1340 (?P (gnus-pick-line-number) ?d)
1341 (?B gnus-tmp-thread-tree-header-string ?s)
1342 (user-date (gnus-user-date
1343 ,(macroexpand '(mail-header-date gnus-tmp-header))) ?s))
16409b0b
GM
1344 "An alist of format specifications that can appear in summary lines.
1345These are paired with what variables they correspond with, along with
1346the type of the variable (string, integer, character, etc).")
eec82323
LMI
1347
1348(defvar gnus-summary-dummy-line-format-alist
1349 `((?S gnus-tmp-subject ?s)
1350 (?N gnus-tmp-number ?d)
1351 (?u gnus-tmp-user-defined ?s)))
1352
1353(defvar gnus-summary-mode-line-format-alist
1354 `((?G gnus-tmp-group-name ?s)
1355 (?g (gnus-short-group-name gnus-tmp-group-name) ?s)
1356 (?p (gnus-group-real-name gnus-tmp-group-name) ?s)
1357 (?A gnus-tmp-article-number ?d)
1358 (?Z gnus-tmp-unread-and-unselected ?s)
1359 (?V gnus-version ?s)
1360 (?U gnus-tmp-unread-and-unticked ?d)
1361 (?S gnus-tmp-subject ?s)
1362 (?e gnus-tmp-unselected ?d)
1363 (?u gnus-tmp-user-defined ?s)
1364 (?d (length gnus-newsgroup-dormant) ?d)
1365 (?t (length gnus-newsgroup-marked) ?d)
23f87bed 1366 (?h (length gnus-newsgroup-spam-marked) ?d)
eec82323 1367 (?r (length gnus-newsgroup-reads) ?d)
6748645f 1368 (?z (gnus-summary-article-score gnus-tmp-article-number) ?d)
eec82323
LMI
1369 (?E gnus-newsgroup-expunged-tally ?d)
1370 (?s (gnus-current-score-file-nondirectory) ?s)))
1371
1372(defvar gnus-last-search-regexp nil
1373 "Default regexp for article search command.")
1374
1375(defvar gnus-last-shell-command nil
1376 "Default shell command on article.")
1377
23f87bed
MB
1378(defvar gnus-newsgroup-agentized nil
1379 "Locally bound in each summary buffer to indicate whether the server has been agentized.")
eec82323
LMI
1380(defvar gnus-newsgroup-begin nil)
1381(defvar gnus-newsgroup-end nil)
1382(defvar gnus-newsgroup-last-rmail nil)
1383(defvar gnus-newsgroup-last-mail nil)
1384(defvar gnus-newsgroup-last-folder nil)
1385(defvar gnus-newsgroup-last-file nil)
26c9afc3 1386(defvar gnus-newsgroup-last-directory nil)
eec82323
LMI
1387(defvar gnus-newsgroup-auto-expire nil)
1388(defvar gnus-newsgroup-active nil)
1389
1390(defvar gnus-newsgroup-data nil)
1391(defvar gnus-newsgroup-data-reverse nil)
1392(defvar gnus-newsgroup-limit nil)
1393(defvar gnus-newsgroup-limits nil)
23f87bed 1394(defvar gnus-summary-use-undownloaded-faces nil)
eec82323
LMI
1395
1396(defvar gnus-newsgroup-unreads nil
23f87bed 1397 "Sorted list of unread articles in the current newsgroup.")
eec82323
LMI
1398
1399(defvar gnus-newsgroup-unselected nil
23f87bed 1400 "Sorted list of unselected unread articles in the current newsgroup.")
eec82323
LMI
1401
1402(defvar gnus-newsgroup-reads nil
1403 "Alist of read articles and article marks in the current newsgroup.")
1404
1405(defvar gnus-newsgroup-expunged-tally nil)
1406
1407(defvar gnus-newsgroup-marked nil
23f87bed
MB
1408 "Sorted list of ticked articles in the current newsgroup (a subset of unread art).")
1409
1410(defvar gnus-newsgroup-spam-marked nil
1411 "List of ranges of articles that have been marked as spam.")
eec82323
LMI
1412
1413(defvar gnus-newsgroup-killed nil
1414 "List of ranges of articles that have been through the scoring process.")
1415
1416(defvar gnus-newsgroup-cached nil
23f87bed 1417 "Sorted list of articles that come from the article cache.")
eec82323
LMI
1418
1419(defvar gnus-newsgroup-saved nil
1420 "List of articles that have been saved.")
1421
1422(defvar gnus-newsgroup-kill-headers nil)
1423
1424(defvar gnus-newsgroup-replied nil
1425 "List of articles that have been replied to in the current newsgroup.")
1426
23f87bed
MB
1427(defvar gnus-newsgroup-forwarded nil
1428 "List of articles that have been forwarded in the current newsgroup.")
1429
1430(defvar gnus-newsgroup-recent nil
1431 "List of articles that have are recent in the current newsgroup.")
1432
eec82323 1433(defvar gnus-newsgroup-expirable nil
23f87bed 1434 "Sorted list of articles in the current newsgroup that can be expired.")
eec82323
LMI
1435
1436(defvar gnus-newsgroup-processable nil
1437 "List of articles in the current newsgroup that can be processed.")
1438
6748645f 1439(defvar gnus-newsgroup-downloadable nil
23f87bed
MB
1440 "Sorted list of articles in the current newsgroup that can be processed.")
1441
1442(defvar gnus-newsgroup-unfetched nil
1443 "Sorted list of articles in the current newsgroup whose headers have
1444not been fetched into the agent.
1445
1446This list will always be a subset of gnus-newsgroup-undownloaded.")
6748645f
LMI
1447
1448(defvar gnus-newsgroup-undownloaded nil
23f87bed 1449 "List of articles in the current newsgroup that haven't been downloaded.")
6748645f
LMI
1450
1451(defvar gnus-newsgroup-unsendable nil
1452 "List of articles in the current newsgroup that won't be sent.")
1453
eec82323
LMI
1454(defvar gnus-newsgroup-bookmarks nil
1455 "List of articles in the current newsgroup that have bookmarks.")
1456
1457(defvar gnus-newsgroup-dormant nil
23f87bed
MB
1458 "Sorted list of dormant articles in the current newsgroup.")
1459
1460(defvar gnus-newsgroup-unseen nil
1461 "List of unseen articles in the current newsgroup.")
1462
1463(defvar gnus-newsgroup-seen nil
1464 "Range of seen articles in the current newsgroup.")
1465
1466(defvar gnus-newsgroup-articles nil
1467 "List of articles in the current newsgroup.")
eec82323
LMI
1468
1469(defvar gnus-newsgroup-scored nil
1470 "List of scored articles in the current newsgroup.")
1471
1472(defvar gnus-newsgroup-headers nil
1473 "List of article headers in the current newsgroup.")
1474
1475(defvar gnus-newsgroup-threads nil)
1476
1477(defvar gnus-newsgroup-prepared nil
1478 "Whether the current group has been prepared properly.")
1479
1480(defvar gnus-newsgroup-ancient nil
1481 "List of `gnus-fetch-old-headers' articles in the current newsgroup.")
1482
1483(defvar gnus-newsgroup-sparse nil)
1484
1485(defvar gnus-current-article nil)
1486(defvar gnus-article-current nil)
1487(defvar gnus-current-headers nil)
1488(defvar gnus-have-all-headers nil)
1489(defvar gnus-last-article nil)
1490(defvar gnus-newsgroup-history nil)
16409b0b
GM
1491(defvar gnus-newsgroup-charset nil)
1492(defvar gnus-newsgroup-ephemeral-charset nil)
1493(defvar gnus-newsgroup-ephemeral-ignored-charsets nil)
eec82323 1494
23f87bed
MB
1495(defvar gnus-article-before-search nil)
1496
1497(defvar gnus-summary-local-variables
eec82323
LMI
1498 '(gnus-newsgroup-name
1499 gnus-newsgroup-begin gnus-newsgroup-end
1500 gnus-newsgroup-last-rmail gnus-newsgroup-last-mail
1501 gnus-newsgroup-last-folder gnus-newsgroup-last-file
26c9afc3 1502 gnus-newsgroup-last-directory
eec82323
LMI
1503 gnus-newsgroup-auto-expire gnus-newsgroup-unreads
1504 gnus-newsgroup-unselected gnus-newsgroup-marked
23f87bed 1505 gnus-newsgroup-spam-marked
eec82323 1506 gnus-newsgroup-reads gnus-newsgroup-saved
23f87bed
MB
1507 gnus-newsgroup-replied gnus-newsgroup-forwarded
1508 gnus-newsgroup-recent
1509 gnus-newsgroup-expirable
eec82323 1510 gnus-newsgroup-processable gnus-newsgroup-killed
6748645f 1511 gnus-newsgroup-downloadable gnus-newsgroup-undownloaded
23f87bed
MB
1512 gnus-newsgroup-unfetched
1513 gnus-newsgroup-unsendable gnus-newsgroup-unseen
1514 gnus-newsgroup-seen gnus-newsgroup-articles
eec82323
LMI
1515 gnus-newsgroup-bookmarks gnus-newsgroup-dormant
1516 gnus-newsgroup-headers gnus-newsgroup-threads
1517 gnus-newsgroup-prepared gnus-summary-highlight-line-function
1518 gnus-current-article gnus-current-headers gnus-have-all-headers
1519 gnus-last-article gnus-article-internal-prepare-hook
1520 gnus-newsgroup-dependencies gnus-newsgroup-selected-overlay
1521 gnus-newsgroup-scored gnus-newsgroup-kill-headers
1522 gnus-thread-expunge-below
16409b0b
GM
1523 gnus-score-alist gnus-current-score-file
1524 (gnus-summary-expunge-below . global)
eec82323 1525 (gnus-summary-mark-below . global)
16409b0b 1526 (gnus-orphan-score . global)
eec82323
LMI
1527 gnus-newsgroup-active gnus-scores-exclude-files
1528 gnus-newsgroup-history gnus-newsgroup-ancient
1529 gnus-newsgroup-sparse gnus-newsgroup-process-stack
1530 (gnus-newsgroup-adaptive . gnus-use-adaptive-scoring)
1531 gnus-newsgroup-adaptive-score-file (gnus-reffed-article-number . -1)
1532 (gnus-newsgroup-expunged-tally . 0)
1533 gnus-cache-removable-articles gnus-newsgroup-cached
1534 gnus-newsgroup-data gnus-newsgroup-data-reverse
16409b0b 1535 gnus-newsgroup-limit gnus-newsgroup-limits
23f87bed
MB
1536 gnus-newsgroup-charset gnus-newsgroup-display
1537 gnus-summary-use-undownloaded-faces)
eec82323
LMI
1538 "Variables that are buffer-local to the summary buffers.")
1539
23f87bed
MB
1540(defvar gnus-newsgroup-variables nil
1541 "A list of variables that have separate values in different newsgroups.
1542A list of newsgroup (summary buffer) local variables, or cons of
1543variables and their default expressions to be evalled (when the default
1544values are not nil), that should be made global while the summary buffer
1545is active.
1546
1547Note: The default expressions will be evaluated (using function `eval')
1548before assignment to the local variable rather than just assigned to it.
1549If the default expression is the symbol `global', that symbol will not
1550be evaluated but the global value of the local variable will be used
1551instead.
1552
1553These variables can be used to set variables in the group parameters
1554while still allowing them to affect operations done in other buffers.
1555For example:
1556
1557\(setq gnus-newsgroup-variables
1558 '(message-use-followup-to
1559 (gnus-visible-headers .
1560 \"^From:\\\\|^Newsgroups:\\\\|^Subject:\\\\|^Date:\\\\|^To:\")))
1561")
1562
eec82323 1563;; Byte-compiler warning.
23f87bed
MB
1564(eval-when-compile
1565 ;; Bind features so that require will believe that gnus-sum has
1566 ;; already been loaded (avoids infinite recursion)
1567 (let ((features (cons 'gnus-sum features)))
1568 ;; Several of the declarations in gnus-sum are needed to load the
1569 ;; following files. Right now, these definitions have been
1570 ;; compiled but not defined (evaluated). We could either do a
1571 ;; eval-and-compile about all of the declarations or evaluate the
1572 ;; source file.
1573 (if (boundp 'gnus-newsgroup-variables)
1574 nil
1575 (load "gnus-sum.el" t t t))
1576 (require 'gnus)
23f87bed 1577 (require 'gnus-art)))
eec82323 1578
16409b0b
GM
1579;; MIME stuff.
1580
1581(defvar gnus-decode-encoded-word-methods
1582 '(mail-decode-encoded-word-string)
1583 "List of methods used to decode encoded words.
1584
23f87bed
MB
1585This variable is a list of FUNCTION or (REGEXP . FUNCTION). If item
1586is FUNCTION, FUNCTION will be apply to all newsgroups. If item is a
1587\(REGEXP . FUNCTION), FUNCTION will be only apply to thes newsgroups
16409b0b
GM
1588whose names match REGEXP.
1589
1590For example:
23f87bed 1591\((\"chinese\" . gnus-decode-encoded-word-string-by-guess)
16409b0b
GM
1592 mail-decode-encoded-word-string
1593 (\"chinese\" . rfc1843-decode-string))")
1594
1595(defvar gnus-decode-encoded-word-methods-cache nil)
1596
1597(defun gnus-multi-decode-encoded-word-string (string)
1598 "Apply the functions from `gnus-encoded-word-methods' that match."
1599 (unless (and gnus-decode-encoded-word-methods-cache
1600 (eq gnus-newsgroup-name
1601 (car gnus-decode-encoded-word-methods-cache)))
1602 (setq gnus-decode-encoded-word-methods-cache (list gnus-newsgroup-name))
01c52d31
MB
1603 (dolist (method gnus-decode-encoded-word-methods)
1604 (if (symbolp method)
1605 (nconc gnus-decode-encoded-word-methods-cache (list method))
1606 (if (and gnus-newsgroup-name
1607 (string-match (car method) gnus-newsgroup-name))
1608 (nconc gnus-decode-encoded-word-methods-cache
1609 (list (cdr method)))))))
1610 (dolist (method (cdr gnus-decode-encoded-word-methods-cache) string)
1611 (setq string (funcall method string))))
16409b0b 1612
eec82323
LMI
1613;; Subject simplification.
1614
6748645f 1615(defun gnus-simplify-whitespace (str)
16409b0b 1616 "Remove excessive whitespace from STR."
23f87bed
MB
1617 ;; Multiple spaces.
1618 (while (string-match "[ \t][ \t]+" str)
1619 (setq str (concat (substring str 0 (match-beginning 0))
1620 " "
1621 (substring str (match-end 0)))))
1622 ;; Leading spaces.
1623 (when (string-match "^[ \t]+" str)
1624 (setq str (substring str (match-end 0))))
1625 ;; Trailing spaces.
1626 (when (string-match "[ \t]+$" str)
1627 (setq str (substring str 0 (match-beginning 0))))
1628 str)
1629
1630(defun gnus-simplify-all-whitespace (str)
1631 "Remove all whitespace from STR."
1632 (while (string-match "[ \t\n]+" str)
1633 (setq str (replace-match "" nil nil str)))
1634 str)
6748645f 1635
eec82323
LMI
1636(defsubst gnus-simplify-subject-re (subject)
1637 "Remove \"Re:\" from subject lines."
23f87bed 1638 (if (string-match message-subject-re-regexp subject)
eec82323
LMI
1639 (substring subject (match-end 0))
1640 subject))
1641
1642(defun gnus-simplify-subject (subject &optional re-only)
1643 "Remove `Re:' and words in parentheses.
1644If RE-ONLY is non-nil, strip leading `Re:'s only."
1645 (let ((case-fold-search t)) ;Ignore case.
1646 ;; Remove `Re:', `Re^N:', `Re(n)', and `Re[n]:'.
1647 (when (string-match "\\`\\(re\\([[(^][0-9]+[])]?\\)?:[ \t]*\\)+" subject)
1648 (setq subject (substring subject (match-end 0))))
1649 ;; Remove uninteresting prefixes.
1650 (when (and (not re-only)
1651 gnus-simplify-ignored-prefixes
1652 (string-match gnus-simplify-ignored-prefixes subject))
1653 (setq subject (substring subject (match-end 0))))
1654 ;; Remove words in parentheses from end.
1655 (unless re-only
1656 (while (string-match "[ \t\n]*([^()]*)[ \t\n]*\\'" subject)
1657 (setq subject (substring subject 0 (match-beginning 0)))))
1658 ;; Return subject string.
1659 subject))
1660
1661;; Remove any leading "re:"s, any trailing paren phrases, and simplify
1662;; all whitespace.
1663(defsubst gnus-simplify-buffer-fuzzy-step (regexp &optional newtext)
1664 (goto-char (point-min))
1665 (while (re-search-forward regexp nil t)
16409b0b 1666 (replace-match (or newtext ""))))
eec82323
LMI
1667
1668(defun gnus-simplify-buffer-fuzzy ()
1669 "Simplify string in the buffer fuzzily.
1670The string in the accessible portion of the current buffer is simplified.
1671It is assumed to be a single-line subject.
1672Whitespace is generally cleaned up, and miscellaneous leading/trailing
1673matter is removed. Additional things can be deleted by setting
16409b0b 1674`gnus-simplify-subject-fuzzy-regexp'."
eec82323
LMI
1675 (let ((case-fold-search t)
1676 (modified-tick))
1677 (gnus-simplify-buffer-fuzzy-step "\t" " ")
1678
1679 (while (not (eq modified-tick (buffer-modified-tick)))
1680 (setq modified-tick (buffer-modified-tick))
1681 (cond
1682 ((listp gnus-simplify-subject-fuzzy-regexp)
01c52d31
MB
1683 (mapc 'gnus-simplify-buffer-fuzzy-step
1684 gnus-simplify-subject-fuzzy-regexp))
eec82323
LMI
1685 (gnus-simplify-subject-fuzzy-regexp
1686 (gnus-simplify-buffer-fuzzy-step gnus-simplify-subject-fuzzy-regexp)))
1687 (gnus-simplify-buffer-fuzzy-step "^ *\\[[-+?*!][-+?*!]\\] *")
1688 (gnus-simplify-buffer-fuzzy-step
1689 "^ *\\(re\\|fw\\|fwd\\)[[{(^0-9]*[])}]?[:;] *")
1690 (gnus-simplify-buffer-fuzzy-step "^[[].*:\\( .*\\)[]]$" "\\1"))
1691
1692 (gnus-simplify-buffer-fuzzy-step " *[[{(][^()\n]*[]})] *$")
1693 (gnus-simplify-buffer-fuzzy-step " +" " ")
1694 (gnus-simplify-buffer-fuzzy-step " $")
1695 (gnus-simplify-buffer-fuzzy-step "^ +")))
1696
1697(defun gnus-simplify-subject-fuzzy (subject)
1698 "Simplify a subject string fuzzily.
6748645f 1699See `gnus-simplify-buffer-fuzzy' for details."
eec82323
LMI
1700 (save-excursion
1701 (gnus-set-work-buffer)
1702 (let ((case-fold-search t))
6748645f
LMI
1703 ;; Remove uninteresting prefixes.
1704 (when (and gnus-simplify-ignored-prefixes
1705 (string-match gnus-simplify-ignored-prefixes subject))
1706 (setq subject (substring subject (match-end 0))))
eec82323
LMI
1707 (insert subject)
1708 (inline (gnus-simplify-buffer-fuzzy))
1709 (buffer-string))))
1710
1711(defsubst gnus-simplify-subject-fully (subject)
23f87bed 1712 "Simplify a subject string according to `gnus-summary-gather-subject-limit'."
eec82323 1713 (cond
6748645f
LMI
1714 (gnus-simplify-subject-functions
1715 (gnus-map-function gnus-simplify-subject-functions subject))
eec82323
LMI
1716 ((null gnus-summary-gather-subject-limit)
1717 (gnus-simplify-subject-re subject))
1718 ((eq gnus-summary-gather-subject-limit 'fuzzy)
1719 (gnus-simplify-subject-fuzzy subject))
1720 ((numberp gnus-summary-gather-subject-limit)
01c52d31
MB
1721 (truncate-string-to-width (gnus-simplify-subject-re subject)
1722 gnus-summary-gather-subject-limit))
eec82323
LMI
1723 (t
1724 subject)))
1725
1726(defsubst gnus-subject-equal (s1 s2 &optional simple-first)
6748645f 1727 "Check whether two subjects are equal.
23f87bed 1728If optional argument SIMPLE-FIRST is t, first argument is already
6748645f 1729simplified."
eec82323
LMI
1730 (cond
1731 ((null simple-first)
1732 (equal (gnus-simplify-subject-fully s1)
1733 (gnus-simplify-subject-fully s2)))
1734 (t
1735 (equal s1
1736 (gnus-simplify-subject-fully s2)))))
1737
1738(defun gnus-summary-bubble-group ()
1739 "Increase the score of the current group.
1740This is a handy function to add to `gnus-summary-exit-hook' to
1741increase the score of each group you read."
1742 (gnus-group-add-score gnus-newsgroup-name))
1743
1744\f
1745;;;
1746;;; Gnus summary mode
1747;;;
1748
1749(put 'gnus-summary-mode 'mode-class 'special)
1750
1653df0f
SZ
1751(defvar gnus-article-commands-menu)
1752
23f87bed
MB
1753;; Non-orthogonal keys
1754
1755(gnus-define-keys gnus-summary-mode-map
1756 " " gnus-summary-next-page
1757 "\177" gnus-summary-prev-page
1758 [delete] gnus-summary-prev-page
1759 [backspace] gnus-summary-prev-page
1760 "\r" gnus-summary-scroll-up
1761 "\M-\r" gnus-summary-scroll-down
1762 "n" gnus-summary-next-unread-article
1763 "p" gnus-summary-prev-unread-article
1764 "N" gnus-summary-next-article
1765 "P" gnus-summary-prev-article
1766 "\M-\C-n" gnus-summary-next-same-subject
1767 "\M-\C-p" gnus-summary-prev-same-subject
1768 "\M-n" gnus-summary-next-unread-subject
1769 "\M-p" gnus-summary-prev-unread-subject
1770 "." gnus-summary-first-unread-article
1771 "," gnus-summary-best-unread-article
1772 "\M-s" gnus-summary-search-article-forward
1773 "\M-r" gnus-summary-search-article-backward
01c52d31
MB
1774 "\M-S" gnus-summary-repeat-search-article-forward
1775 "\M-R" gnus-summary-repeat-search-article-backward
23f87bed
MB
1776 "<" gnus-summary-beginning-of-article
1777 ">" gnus-summary-end-of-article
1778 "j" gnus-summary-goto-article
1779 "^" gnus-summary-refer-parent-article
1780 "\M-^" gnus-summary-refer-article
1781 "u" gnus-summary-tick-article-forward
1782 "!" gnus-summary-tick-article-forward
1783 "U" gnus-summary-tick-article-backward
1784 "d" gnus-summary-mark-as-read-forward
1785 "D" gnus-summary-mark-as-read-backward
1786 "E" gnus-summary-mark-as-expirable
1787 "\M-u" gnus-summary-clear-mark-forward
1788 "\M-U" gnus-summary-clear-mark-backward
1789 "k" gnus-summary-kill-same-subject-and-select
1790 "\C-k" gnus-summary-kill-same-subject
1791 "\M-\C-k" gnus-summary-kill-thread
1792 "\M-\C-l" gnus-summary-lower-thread
1793 "e" gnus-summary-edit-article
1794 "#" gnus-summary-mark-as-processable
1795 "\M-#" gnus-summary-unmark-as-processable
1796 "\M-\C-t" gnus-summary-toggle-threads
1797 "\M-\C-s" gnus-summary-show-thread
1798 "\M-\C-h" gnus-summary-hide-thread
1799 "\M-\C-f" gnus-summary-next-thread
1800 "\M-\C-b" gnus-summary-prev-thread
1801 [(meta down)] gnus-summary-next-thread
1802 [(meta up)] gnus-summary-prev-thread
1803 "\M-\C-u" gnus-summary-up-thread
1804 "\M-\C-d" gnus-summary-down-thread
1805 "&" gnus-summary-execute-command
1806 "c" gnus-summary-catchup-and-exit
1807 "\C-w" gnus-summary-mark-region-as-read
1808 "\C-t" gnus-summary-toggle-truncation
1809 "?" gnus-summary-mark-as-dormant
1810 "\C-c\M-\C-s" gnus-summary-limit-include-expunged
1811 "\C-c\C-s\C-n" gnus-summary-sort-by-number
1812 "\C-c\C-s\C-l" gnus-summary-sort-by-lines
1813 "\C-c\C-s\C-c" gnus-summary-sort-by-chars
1814 "\C-c\C-s\C-a" gnus-summary-sort-by-author
01c52d31 1815 "\C-c\C-s\C-t" gnus-summary-sort-by-recipient
23f87bed
MB
1816 "\C-c\C-s\C-s" gnus-summary-sort-by-subject
1817 "\C-c\C-s\C-d" gnus-summary-sort-by-date
1818 "\C-c\C-s\C-i" gnus-summary-sort-by-score
1819 "\C-c\C-s\C-o" gnus-summary-sort-by-original
1820 "\C-c\C-s\C-r" gnus-summary-sort-by-random
1821 "=" gnus-summary-expand-window
1822 "\C-x\C-s" gnus-summary-reselect-current-group
1823 "\M-g" gnus-summary-rescan-group
1824 "w" gnus-summary-stop-page-breaking
1825 "\C-c\C-r" gnus-summary-caesar-message
1826 "f" gnus-summary-followup
1827 "F" gnus-summary-followup-with-original
1828 "C" gnus-summary-cancel-article
1829 "r" gnus-summary-reply
1830 "R" gnus-summary-reply-with-original
1831 "\C-c\C-f" gnus-summary-mail-forward
1832 "o" gnus-summary-save-article
1833 "\C-o" gnus-summary-save-article-mail
1834 "|" gnus-summary-pipe-output
1835 "\M-k" gnus-summary-edit-local-kill
1836 "\M-K" gnus-summary-edit-global-kill
1837 ;; "V" gnus-version
1838 "\C-c\C-d" gnus-summary-describe-group
1839 "q" gnus-summary-exit
1840 "Q" gnus-summary-exit-no-update
1841 "\C-c\C-i" gnus-info-find-node
1842 gnus-mouse-2 gnus-mouse-pick-article
132cf96d 1843 [follow-link] mouse-face
23f87bed
MB
1844 "m" gnus-summary-mail-other-window
1845 "a" gnus-summary-post-news
1846 "i" gnus-summary-news-other-window
1847 "x" gnus-summary-limit-to-unread
1848 "s" gnus-summary-isearch-article
1849 "t" gnus-summary-toggle-header
1850 "g" gnus-summary-show-article
1851 "l" gnus-summary-goto-last-article
1852 "\C-c\C-v\C-v" gnus-uu-decode-uu-view
1853 "\C-d" gnus-summary-enter-digest-group
1854 "\M-\C-d" gnus-summary-read-document
1855 "\M-\C-e" gnus-summary-edit-parameters
1856 "\M-\C-a" gnus-summary-customize-parameters
1857 "\C-c\C-b" gnus-bug
1858 "*" gnus-cache-enter-article
1859 "\M-*" gnus-cache-remove-article
1860 "\M-&" gnus-summary-universal-argument
1861 "\C-l" gnus-recenter
1862 "I" gnus-summary-increase-score
1863 "L" gnus-summary-lower-score
1864 "\M-i" gnus-symbolic-argument
1865 "h" gnus-summary-select-article-buffer
1866
1867 "b" gnus-article-view-part
1868 "\M-t" gnus-summary-toggle-display-buttonized
1869
1870 "V" gnus-summary-score-map
1871 "X" gnus-uu-extract-map
1872 "S" gnus-summary-send-map)
1873
1874;; Sort of orthogonal keymap
1875(gnus-define-keys (gnus-summary-mark-map "M" gnus-summary-mode-map)
1876 "t" gnus-summary-tick-article-forward
1877 "!" gnus-summary-tick-article-forward
1878 "d" gnus-summary-mark-as-read-forward
1879 "r" gnus-summary-mark-as-read-forward
1880 "c" gnus-summary-clear-mark-forward
1881 " " gnus-summary-clear-mark-forward
1882 "e" gnus-summary-mark-as-expirable
1883 "x" gnus-summary-mark-as-expirable
1884 "?" gnus-summary-mark-as-dormant
1885 "b" gnus-summary-set-bookmark
1886 "B" gnus-summary-remove-bookmark
1887 "#" gnus-summary-mark-as-processable
1888 "\M-#" gnus-summary-unmark-as-processable
1889 "S" gnus-summary-limit-include-expunged
1890 "C" gnus-summary-catchup
1891 "H" gnus-summary-catchup-to-here
1892 "h" gnus-summary-catchup-from-here
1893 "\C-c" gnus-summary-catchup-all
1894 "k" gnus-summary-kill-same-subject-and-select
1895 "K" gnus-summary-kill-same-subject
1896 "P" gnus-uu-mark-map)
1897
1898(gnus-define-keys (gnus-summary-mscore-map "V" gnus-summary-mark-map)
1899 "c" gnus-summary-clear-above
1900 "u" gnus-summary-tick-above
1901 "m" gnus-summary-mark-above
1902 "k" gnus-summary-kill-below)
1903
1904(gnus-define-keys (gnus-summary-limit-map "/" gnus-summary-mode-map)
1905 "/" gnus-summary-limit-to-subject
1906 "n" gnus-summary-limit-to-articles
01c52d31
MB
1907 "b" gnus-summary-limit-to-bodies
1908 "h" gnus-summary-limit-to-headers
23f87bed
MB
1909 "w" gnus-summary-pop-limit
1910 "s" gnus-summary-limit-to-subject
1911 "a" gnus-summary-limit-to-author
1912 "u" gnus-summary-limit-to-unread
1913 "m" gnus-summary-limit-to-marks
1914 "M" gnus-summary-limit-exclude-marks
1915 "v" gnus-summary-limit-to-score
1916 "*" gnus-summary-limit-include-cached
1917 "D" gnus-summary-limit-include-dormant
1918 "T" gnus-summary-limit-include-thread
1919 "d" gnus-summary-limit-exclude-dormant
1920 "t" gnus-summary-limit-to-age
1921 "." gnus-summary-limit-to-unseen
1922 "x" gnus-summary-limit-to-extra
1923 "p" gnus-summary-limit-to-display-predicate
1924 "E" gnus-summary-limit-include-expunged
1925 "c" gnus-summary-limit-exclude-childless-dormant
1926 "C" gnus-summary-limit-mark-excluded-as-read
1927 "o" gnus-summary-insert-old-articles
01c52d31
MB
1928 "N" gnus-summary-insert-new-articles
1929 "S" gnus-summary-limit-to-singletons
1930 "r" gnus-summary-limit-to-replied
1931 "R" gnus-summary-limit-to-recipient
1932 "A" gnus-summary-limit-to-address)
23f87bed
MB
1933
1934(gnus-define-keys (gnus-summary-goto-map "G" gnus-summary-mode-map)
1935 "n" gnus-summary-next-unread-article
1936 "p" gnus-summary-prev-unread-article
1937 "N" gnus-summary-next-article
1938 "P" gnus-summary-prev-article
1939 "\C-n" gnus-summary-next-same-subject
1940 "\C-p" gnus-summary-prev-same-subject
1941 "\M-n" gnus-summary-next-unread-subject
1942 "\M-p" gnus-summary-prev-unread-subject
1943 "f" gnus-summary-first-unread-article
1944 "b" gnus-summary-best-unread-article
1945 "j" gnus-summary-goto-article
1946 "g" gnus-summary-goto-subject
1947 "l" gnus-summary-goto-last-article
1948 "o" gnus-summary-pop-article)
1949
1950(gnus-define-keys (gnus-summary-thread-map "T" gnus-summary-mode-map)
1951 "k" gnus-summary-kill-thread
01c52d31 1952 "E" gnus-summary-expire-thread
23f87bed
MB
1953 "l" gnus-summary-lower-thread
1954 "i" gnus-summary-raise-thread
1955 "T" gnus-summary-toggle-threads
1956 "t" gnus-summary-rethread-current
1957 "^" gnus-summary-reparent-thread
01c52d31 1958 "\M-^" gnus-summary-reparent-children
23f87bed
MB
1959 "s" gnus-summary-show-thread
1960 "S" gnus-summary-show-all-threads
1961 "h" gnus-summary-hide-thread
1962 "H" gnus-summary-hide-all-threads
1963 "n" gnus-summary-next-thread
1964 "p" gnus-summary-prev-thread
1965 "u" gnus-summary-up-thread
1966 "o" gnus-summary-top-thread
1967 "d" gnus-summary-down-thread
1968 "#" gnus-uu-mark-thread
1969 "\M-#" gnus-uu-unmark-thread)
1970
1971(gnus-define-keys (gnus-summary-buffer-map "Y" gnus-summary-mode-map)
1972 "g" gnus-summary-prepare
1973 "c" gnus-summary-insert-cached-articles
01c52d31
MB
1974 "d" gnus-summary-insert-dormant-articles
1975 "t" gnus-summary-insert-ticked-articles)
23f87bed
MB
1976
1977(gnus-define-keys (gnus-summary-exit-map "Z" gnus-summary-mode-map)
1978 "c" gnus-summary-catchup-and-exit
1979 "C" gnus-summary-catchup-all-and-exit
1980 "E" gnus-summary-exit-no-update
1981 "Q" gnus-summary-exit
1982 "Z" gnus-summary-exit
1983 "n" gnus-summary-catchup-and-goto-next-group
01c52d31 1984 "p" gnus-summary-catchup-and-goto-prev-group
23f87bed
MB
1985 "R" gnus-summary-reselect-current-group
1986 "G" gnus-summary-rescan-group
1987 "N" gnus-summary-next-group
1988 "s" gnus-summary-save-newsrc
1989 "P" gnus-summary-prev-group)
1990
1991(gnus-define-keys (gnus-summary-article-map "A" gnus-summary-mode-map)
1992 " " gnus-summary-next-page
1993 "n" gnus-summary-next-page
1994 "\177" gnus-summary-prev-page
1995 [delete] gnus-summary-prev-page
1996 "p" gnus-summary-prev-page
1997 "\r" gnus-summary-scroll-up
1998 "\M-\r" gnus-summary-scroll-down
1999 "<" gnus-summary-beginning-of-article
2000 ">" gnus-summary-end-of-article
2001 "b" gnus-summary-beginning-of-article
2002 "e" gnus-summary-end-of-article
2003 "^" gnus-summary-refer-parent-article
2004 "r" gnus-summary-refer-parent-article
2005 "D" gnus-summary-enter-digest-group
2006 "R" gnus-summary-refer-references
2007 "T" gnus-summary-refer-thread
2008 "g" gnus-summary-show-article
2009 "s" gnus-summary-isearch-article
2010 "P" gnus-summary-print-article
01c52d31 2011 "S" gnus-sticky-article
23f87bed
MB
2012 "M" gnus-mailing-list-insinuate
2013 "t" gnus-article-babel)
2014
2015(gnus-define-keys (gnus-summary-wash-map "W" gnus-summary-mode-map)
2016 "b" gnus-article-add-buttons
2017 "B" gnus-article-add-buttons-to-head
2018 "o" gnus-article-treat-overstrike
2019 "e" gnus-article-emphasize
2020 "w" gnus-article-fill-cited-article
2021 "Q" gnus-article-fill-long-lines
01c52d31 2022 "L" gnus-article-toggle-truncate-lines
23f87bed
MB
2023 "C" gnus-article-capitalize-sentences
2024 "c" gnus-article-remove-cr
2025 "q" gnus-article-de-quoted-unreadable
2026 "6" gnus-article-de-base64-unreadable
2027 "Z" gnus-article-decode-HZ
01c52d31 2028 "A" gnus-article-treat-ansi-sequences
23f87bed
MB
2029 "h" gnus-article-wash-html
2030 "u" gnus-article-unsplit-urls
2031 "s" gnus-summary-force-verify-and-decrypt
2032 "f" gnus-article-display-x-face
2033 "l" gnus-summary-stop-page-breaking
2034 "r" gnus-summary-caesar-message
2035 "m" gnus-summary-morse-message
2036 "t" gnus-summary-toggle-header
2037 "g" gnus-treat-smiley
2038 "v" gnus-summary-verbose-headers
2039 "a" gnus-article-strip-headers-in-body ;; mnemonic: wash archive
2040 "p" gnus-article-verify-x-pgp-sig
01c52d31
MB
2041 "d" gnus-article-treat-dumbquotes
2042 "i" gnus-summary-idna-message)
23f87bed
MB
2043
2044(gnus-define-keys (gnus-summary-wash-deuglify-map "Y" gnus-summary-wash-map)
2045 ;; mnemonic: deuglif*Y*
2046 "u" gnus-article-outlook-unwrap-lines
2047 "a" gnus-article-outlook-repair-attribution
2048 "c" gnus-article-outlook-rearrange-citation
2049 "f" gnus-article-outlook-deuglify-article) ;; mnemonic: full deuglify
2050
2051(gnus-define-keys (gnus-summary-wash-hide-map "W" gnus-summary-wash-map)
2052 "a" gnus-article-hide
2053 "h" gnus-article-hide-headers
2054 "b" gnus-article-hide-boring-headers
2055 "s" gnus-article-hide-signature
2056 "c" gnus-article-hide-citation
2057 "C" gnus-article-hide-citation-in-followups
2058 "l" gnus-article-hide-list-identifiers
2059 "B" gnus-article-strip-banner
2060 "P" gnus-article-hide-pem
2061 "\C-c" gnus-article-hide-citation-maybe)
2062
2063(gnus-define-keys (gnus-summary-wash-highlight-map "H" gnus-summary-wash-map)
2064 "a" gnus-article-highlight
2065 "h" gnus-article-highlight-headers
2066 "c" gnus-article-highlight-citation
2067 "s" gnus-article-highlight-signature)
2068
2069(gnus-define-keys (gnus-summary-wash-header-map "G" gnus-summary-wash-map)
2070 "f" gnus-article-treat-fold-headers
2071 "u" gnus-article-treat-unfold-headers
2072 "n" gnus-article-treat-fold-newsgroups)
2073
2074(gnus-define-keys (gnus-summary-wash-display-map "D" gnus-summary-wash-map)
2075 "x" gnus-article-display-x-face
2076 "d" gnus-article-display-face
2077 "s" gnus-treat-smiley
2078 "D" gnus-article-remove-images
2079 "f" gnus-treat-from-picon
2080 "m" gnus-treat-mail-picon
2081 "n" gnus-treat-newsgroups-picon)
2082
2083(gnus-define-keys (gnus-summary-wash-mime-map "M" gnus-summary-wash-map)
2084 "w" gnus-article-decode-mime-words
2085 "c" gnus-article-decode-charset
2086 "v" gnus-mime-view-all-parts
2087 "b" gnus-article-view-part)
2088
2089(gnus-define-keys (gnus-summary-wash-time-map "T" gnus-summary-wash-map)
2090 "z" gnus-article-date-ut
2091 "u" gnus-article-date-ut
2092 "l" gnus-article-date-local
2093 "p" gnus-article-date-english
2094 "e" gnus-article-date-lapsed
2095 "o" gnus-article-date-original
2096 "i" gnus-article-date-iso8601
2097 "s" gnus-article-date-user)
2098
2099(gnus-define-keys (gnus-summary-wash-empty-map "E" gnus-summary-wash-map)
2100 "t" gnus-article-remove-trailing-blank-lines
2101 "l" gnus-article-strip-leading-blank-lines
2102 "m" gnus-article-strip-multiple-blank-lines
2103 "a" gnus-article-strip-blank-lines
2104 "A" gnus-article-strip-all-blank-lines
2105 "s" gnus-article-strip-leading-space
2106 "e" gnus-article-strip-trailing-space
2107 "w" gnus-article-remove-leading-whitespace)
2108
2109(gnus-define-keys (gnus-summary-help-map "H" gnus-summary-mode-map)
2110 "v" gnus-version
2111 "f" gnus-summary-fetch-faq
2112 "d" gnus-summary-describe-group
2113 "h" gnus-summary-describe-briefly
2114 "i" gnus-info-find-node
2115 "c" gnus-group-fetch-charter
2116 "C" gnus-group-fetch-control)
2117
2118(gnus-define-keys (gnus-summary-backend-map "B" gnus-summary-mode-map)
2119 "e" gnus-summary-expire-articles
2120 "\M-\C-e" gnus-summary-expire-articles-now
2121 "\177" gnus-summary-delete-article
2122 [delete] gnus-summary-delete-article
2123 [backspace] gnus-summary-delete-article
2124 "m" gnus-summary-move-article
2125 "r" gnus-summary-respool-article
2126 "w" gnus-summary-edit-article
2127 "c" gnus-summary-copy-article
2128 "B" gnus-summary-crosspost-article
2129 "q" gnus-summary-respool-query
2130 "t" gnus-summary-respool-trace
2131 "i" gnus-summary-import-article
2132 "I" gnus-summary-create-article
2133 "p" gnus-summary-article-posted-p)
2134
2135(gnus-define-keys (gnus-summary-save-map "O" gnus-summary-mode-map)
2136 "o" gnus-summary-save-article
2137 "m" gnus-summary-save-article-mail
2138 "F" gnus-summary-write-article-file
2139 "r" gnus-summary-save-article-rmail
2140 "f" gnus-summary-save-article-file
2141 "b" gnus-summary-save-article-body-file
26c9afc3 2142 "B" gnus-summary-write-article-body-file
23f87bed
MB
2143 "h" gnus-summary-save-article-folder
2144 "v" gnus-summary-save-article-vm
2145 "p" gnus-summary-pipe-output
2146 "P" gnus-summary-muttprint
2147 "s" gnus-soup-add-article)
2148
2149(gnus-define-keys (gnus-summary-mime-map "K" gnus-summary-mode-map)
2150 "b" gnus-summary-display-buttonized
2151 "m" gnus-summary-repair-multipart
2152 "v" gnus-article-view-part
2153 "o" gnus-article-save-part
01c52d31
MB
2154 "O" gnus-article-save-part-and-strip
2155 "r" gnus-article-replace-part
2156 "d" gnus-article-delete-part
2157 "t" gnus-article-view-part-as-type
2158 "j" gnus-article-jump-to-part
23f87bed
MB
2159 "c" gnus-article-copy-part
2160 "C" gnus-article-view-part-as-charset
2161 "e" gnus-article-view-part-externally
01c52d31 2162 "H" gnus-article-browse-html-article
23f87bed
MB
2163 "E" gnus-article-encrypt-body
2164 "i" gnus-article-inline-part
2165 "|" gnus-article-pipe-part)
2166
2167(gnus-define-keys (gnus-uu-mark-map "P" gnus-summary-mark-map)
2168 "p" gnus-summary-mark-as-processable
2169 "u" gnus-summary-unmark-as-processable
2170 "U" gnus-summary-unmark-all-processable
2171 "v" gnus-uu-mark-over
2172 "s" gnus-uu-mark-series
2173 "r" gnus-uu-mark-region
2174 "g" gnus-uu-unmark-region
2175 "R" gnus-uu-mark-by-regexp
2176 "G" gnus-uu-unmark-by-regexp
2177 "t" gnus-uu-mark-thread
2178 "T" gnus-uu-unmark-thread
2179 "a" gnus-uu-mark-all
2180 "b" gnus-uu-mark-buffer
2181 "S" gnus-uu-mark-sparse
2182 "k" gnus-summary-kill-process-mark
2183 "y" gnus-summary-yank-process-mark
2184 "w" gnus-summary-save-process-mark
2185 "i" gnus-uu-invert-processable)
2186
2187(gnus-define-keys (gnus-uu-extract-map "X" gnus-summary-mode-map)
2188 ;;"x" gnus-uu-extract-any
2189 "m" gnus-summary-save-parts
2190 "u" gnus-uu-decode-uu
2191 "U" gnus-uu-decode-uu-and-save
2192 "s" gnus-uu-decode-unshar
2193 "S" gnus-uu-decode-unshar-and-save
2194 "o" gnus-uu-decode-save
2195 "O" gnus-uu-decode-save
2196 "b" gnus-uu-decode-binhex
2197 "B" gnus-uu-decode-binhex
2198 "p" gnus-uu-decode-postscript
2199 "P" gnus-uu-decode-postscript-and-save)
2200
2201(gnus-define-keys
2202 (gnus-uu-extract-view-map "v" gnus-uu-extract-map)
2203 "u" gnus-uu-decode-uu-view
2204 "U" gnus-uu-decode-uu-and-save-view
2205 "s" gnus-uu-decode-unshar-view
2206 "S" gnus-uu-decode-unshar-and-save-view
2207 "o" gnus-uu-decode-save-view
2208 "O" gnus-uu-decode-save-view
2209 "b" gnus-uu-decode-binhex-view
2210 "B" gnus-uu-decode-binhex-view
2211 "p" gnus-uu-decode-postscript-view
2212 "P" gnus-uu-decode-postscript-and-save-view)
2213
2214(defvar gnus-article-post-menu nil)
2215
2216(defconst gnus-summary-menu-maxlen 20)
2217
2218(defun gnus-summary-menu-split (menu)
2219 ;; If we have lots of elements, divide them into groups of 20
2220 ;; and make a pane (or submenu) for each one.
2221 (if (> (length menu) (/ (* gnus-summary-menu-maxlen 3) 2))
2222 (let ((menu menu) sublists next
2223 (i 1))
2224 (while menu
2225 ;; Pull off the next gnus-summary-menu-maxlen elements
2226 ;; and make them the next element of sublist.
2227 (setq next (nthcdr gnus-summary-menu-maxlen menu))
2228 (if next
2229 (setcdr (nthcdr (1- gnus-summary-menu-maxlen) menu)
2230 nil))
2231 (setq sublists (cons (cons (format "%s ... %s" (aref (car menu) 0)
2232 (aref (car (last menu)) 0)) menu)
2233 sublists))
2234 (setq i (1+ i))
2235 (setq menu next))
2236 (nreverse sublists))
2237 ;; Few elements--put them all in one pane.
2238 menu))
eec82323
LMI
2239
2240(defun gnus-summary-make-menu-bar ()
2241 (gnus-turn-off-edit-menu 'summary)
2242
2243 (unless (boundp 'gnus-summary-misc-menu)
2244
2245 (easy-menu-define
23f87bed
MB
2246 gnus-summary-kill-menu gnus-summary-mode-map ""
2247 (cons
2248 "Score"
2249 (nconc
2250 (list
2251 ["Customize" gnus-score-customize t])
2252 (gnus-make-score-map 'increase)
2253 (gnus-make-score-map 'lower)
2254 '(("Mark"
2255 ["Kill below" gnus-summary-kill-below t]
2256 ["Mark above" gnus-summary-mark-above t]
2257 ["Tick above" gnus-summary-tick-above t]
2258 ["Clear above" gnus-summary-clear-above t])
2259 ["Current score" gnus-summary-current-score t]
2260 ["Set score" gnus-summary-set-score t]
2261 ["Switch current score file..." gnus-score-change-score-file t]
2262 ["Set mark below..." gnus-score-set-mark-below t]
2263 ["Set expunge below..." gnus-score-set-expunge-below t]
2264 ["Edit current score file" gnus-score-edit-current-scores t]
2265 ["Edit score file" gnus-score-edit-file t]
2266 ["Trace score" gnus-score-find-trace t]
2267 ["Find words" gnus-score-find-favourite-words t]
2268 ["Rescore buffer" gnus-summary-rescore t]
2269 ["Increase score..." gnus-summary-increase-score t]
2270 ["Lower score..." gnus-summary-lower-score t]))))
2271
2272 ;; Define both the Article menu in the summary buffer and the
2273 ;; equivalent Commands menu in the article buffer here for
2274 ;; consistency.
6748645f 2275 (let ((innards
23f87bed
MB
2276 `(("Hide"
2277 ["All" gnus-article-hide t]
2278 ["Headers" gnus-article-hide-headers t]
2279 ["Signature" gnus-article-hide-signature t]
2280 ["Citation" gnus-article-hide-citation t]
16409b0b 2281 ["List identifiers" gnus-article-hide-list-identifiers t]
16409b0b 2282 ["Banner" gnus-article-strip-banner t]
23f87bed
MB
2283 ["Boring headers" gnus-article-hide-boring-headers t])
2284 ("Highlight"
2285 ["All" gnus-article-highlight t]
2286 ["Headers" gnus-article-highlight-headers t]
2287 ["Signature" gnus-article-highlight-signature t]
2288 ["Citation" gnus-article-highlight-citation t])
16409b0b
GM
2289 ("MIME"
2290 ["Words" gnus-article-decode-mime-words t]
2291 ["Charset" gnus-article-decode-charset t]
2292 ["QP" gnus-article-de-quoted-unreadable t]
2293 ["Base64" gnus-article-de-base64-unreadable t]
23f87bed
MB
2294 ["View MIME buttons" gnus-summary-display-buttonized t]
2295 ["View all" gnus-mime-view-all-parts t]
2296 ["Verify and Decrypt" gnus-summary-force-verify-and-decrypt t]
2297 ["Encrypt body" gnus-article-encrypt-body
2298 :active (not (gnus-group-read-only-p))
2299 ,@(if (featurep 'xemacs) nil
2300 '(:help "Encrypt the message body on disk"))]
2301 ["Extract all parts..." gnus-summary-save-parts t]
2302 ("Multipart"
2303 ["Repair multipart" gnus-summary-repair-multipart t]
2304 ["Pipe part..." gnus-article-pipe-part t]
2305 ["Inline part" gnus-article-inline-part t]
01c52d31 2306 ["View part as type..." gnus-article-view-part-as-type t]
23f87bed
MB
2307 ["Encrypt body" gnus-article-encrypt-body
2308 :active (not (gnus-group-read-only-p))
2309 ,@(if (featurep 'xemacs) nil
2310 '(:help "Encrypt the message body on disk"))]
2311 ["View part externally" gnus-article-view-part-externally t]
01c52d31 2312 ["View HTML parts in browser" gnus-article-browse-html-article t]
23f87bed
MB
2313 ["View part with charset..." gnus-article-view-part-as-charset t]
2314 ["Copy part" gnus-article-copy-part t]
2315 ["Save part..." gnus-article-save-part t]
2316 ["View part" gnus-article-view-part t]))
2317 ("Date"
2318 ["Local" gnus-article-date-local t]
2319 ["ISO8601" gnus-article-date-iso8601 t]
2320 ["UT" gnus-article-date-ut t]
2321 ["Original" gnus-article-date-original t]
2322 ["Lapsed" gnus-article-date-lapsed t]
2323 ["User-defined" gnus-article-date-user t])
2324 ("Display"
2325 ["Remove images" gnus-article-remove-images t]
2326 ["Toggle smiley" gnus-treat-smiley t]
2327 ["Show X-Face" gnus-article-display-x-face t]
2328 ["Show picons in From" gnus-treat-from-picon t]
2329 ["Show picons in mail headers" gnus-treat-mail-picon t]
2330 ["Show picons in news headers" gnus-treat-newsgroups-picon t]
2331 ("View as different encoding"
2332 ,@(gnus-summary-menu-split
2333 (mapcar
2334 (lambda (cs)
2335 ;; Since easymenu under Emacs doesn't allow
2336 ;; lambda forms for menu commands, we should
2337 ;; provide intern'ed function symbols.
2338 (let ((command (intern (format "\
2339gnus-summary-show-article-from-menu-as-charset-%s" cs))))
2340 (fset command
2341 `(lambda ()
2342 (interactive)
2343 (let ((gnus-summary-show-article-charset-alist
2344 '((1 . ,cs))))
2345 (gnus-summary-show-article 1))))
2346 `[,(symbol-name cs) ,command t]))
2347 (sort (if (fboundp 'coding-system-list)
2348 (coding-system-list)
2349 (mapcar 'car mm-mime-mule-charset-alist))
2350 'string<)))))
2351 ("Washing"
2352 ("Remove Blanks"
2353 ["Leading" gnus-article-strip-leading-blank-lines t]
2354 ["Multiple" gnus-article-strip-multiple-blank-lines t]
2355 ["Trailing" gnus-article-remove-trailing-blank-lines t]
2356 ["All of the above" gnus-article-strip-blank-lines t]
2357 ["All" gnus-article-strip-all-blank-lines t]
2358 ["Leading space" gnus-article-strip-leading-space t]
2359 ["Trailing space" gnus-article-strip-trailing-space t]
2360 ["Leading space in headers"
2361 gnus-article-remove-leading-whitespace t])
2362 ["Overstrike" gnus-article-treat-overstrike t]
2363 ["Dumb quotes" gnus-article-treat-dumbquotes t]
2364 ["Emphasis" gnus-article-emphasize t]
2365 ["Word wrap" gnus-article-fill-cited-article t]
16409b0b 2366 ["Fill long lines" gnus-article-fill-long-lines t]
01c52d31 2367 ["Toggle truncate long lines" gnus-article-toggle-truncate-lines t]
16409b0b 2368 ["Capitalize sentences" gnus-article-capitalize-sentences t]
23f87bed
MB
2369 ["Remove CR" gnus-article-remove-cr t]
2370 ["Quoted-Printable" gnus-article-de-quoted-unreadable t]
2371 ["Base64" gnus-article-de-base64-unreadable t]
2372 ["Rot 13" gnus-summary-caesar-message
2373 ,@(if (featurep 'xemacs) '(t)
2374 '(:help "\"Caesar rotate\" article by 13"))]
01c52d31 2375 ["De-IDNA" gnus-summary-idna-message t]
23f87bed
MB
2376 ["Morse decode" gnus-summary-morse-message t]
2377 ["Unix pipe..." gnus-summary-pipe-message t]
2378 ["Add buttons" gnus-article-add-buttons t]
2379 ["Add buttons to head" gnus-article-add-buttons-to-head t]
2380 ["Stop page breaking" gnus-summary-stop-page-breaking t]
2381 ["Verbose header" gnus-summary-verbose-headers t]
2382 ["Toggle header" gnus-summary-toggle-header t]
2383 ["Unfold headers" gnus-article-treat-unfold-headers t]
2384 ["Fold newsgroups" gnus-article-treat-fold-newsgroups t]
16409b0b 2385 ["Html" gnus-article-wash-html t]
23f87bed
MB
2386 ["Unsplit URLs" gnus-article-unsplit-urls t]
2387 ["Verify X-PGP-Sig" gnus-article-verify-x-pgp-sig t]
2388 ["Decode HZ" gnus-article-decode-HZ t]
01c52d31 2389 ["ANSI sequences" gnus-article-treat-ansi-sequences t]
23f87bed
MB
2390 ("(Outlook) Deuglify"
2391 ["Unwrap lines" gnus-article-outlook-unwrap-lines t]
2392 ["Repair attribution" gnus-article-outlook-repair-attribution t]
2393 ["Rearrange citation" gnus-article-outlook-rearrange-citation t]
2394 ["Full (Outlook) deuglify"
2395 gnus-article-outlook-deuglify-article t])
2396 )
2397 ("Output"
2398 ["Save in default format..." gnus-summary-save-article
2399 ,@(if (featurep 'xemacs) '(t)
2400 '(:help "Save article using default method"))]
2401 ["Save in file..." gnus-summary-save-article-file
2402 ,@(if (featurep 'xemacs) '(t)
2403 '(:help "Save article in file"))]
2404 ["Save in Unix mail format..." gnus-summary-save-article-mail t]
2405 ["Save in MH folder..." gnus-summary-save-article-folder t]
2406 ["Save in VM folder..." gnus-summary-save-article-vm t]
2407 ["Save in RMAIL mbox..." gnus-summary-save-article-rmail t]
2408 ["Save body in file..." gnus-summary-save-article-body-file t]
2409 ["Pipe through a filter..." gnus-summary-pipe-output t]
2410 ["Add to SOUP packet" gnus-soup-add-article t]
2411 ["Print with Muttprint..." gnus-summary-muttprint t]
531e5812
MB
2412 ["Print" gnus-summary-print-article
2413 ,@(if (featurep 'xemacs) '(t)
2414 '(:help "Generate and print a PostScript image"))])
2415 ("Copy, move,... (Backend)"
707f2b38 2416 ,@(if (featurep 'xemacs) nil
531e5812 2417 '(:help "Copying, moving, expiring articles..."))
23f87bed
MB
2418 ["Respool article..." gnus-summary-respool-article t]
2419 ["Move article..." gnus-summary-move-article
2420 (gnus-check-backend-function
2421 'request-move-article gnus-newsgroup-name)]
2422 ["Copy article..." gnus-summary-copy-article t]
2423 ["Crosspost article..." gnus-summary-crosspost-article
2424 (gnus-check-backend-function
2425 'request-replace-article gnus-newsgroup-name)]
2426 ["Import file..." gnus-summary-import-article
2427 (gnus-check-backend-function
2428 'request-accept-article gnus-newsgroup-name)]
2429 ["Create article..." gnus-summary-create-article
2430 (gnus-check-backend-function
2431 'request-accept-article gnus-newsgroup-name)]
2432 ["Check if posted" gnus-summary-article-posted-p t]
2433 ["Edit article" gnus-summary-edit-article
2434 (not (gnus-group-read-only-p))]
2435 ["Delete article" gnus-summary-delete-article
2436 (gnus-check-backend-function
2437 'request-expire-articles gnus-newsgroup-name)]
2438 ["Query respool" gnus-summary-respool-query t]
6748645f 2439 ["Trace respool" gnus-summary-respool-trace t]
23f87bed
MB
2440 ["Delete expirable articles" gnus-summary-expire-articles-now
2441 (gnus-check-backend-function
2442 'request-expire-articles gnus-newsgroup-name)])
2443 ("Extract"
2444 ["Uudecode" gnus-uu-decode-uu
2445 ,@(if (featurep 'xemacs) '(t)
2446 '(:help "Decode uuencoded article(s)"))]
2447 ["Uudecode and save" gnus-uu-decode-uu-and-save t]
2448 ["Unshar" gnus-uu-decode-unshar t]
2449 ["Unshar and save" gnus-uu-decode-unshar-and-save t]
2450 ["Save" gnus-uu-decode-save t]
2451 ["Binhex" gnus-uu-decode-binhex t]
2452 ["Postscript" gnus-uu-decode-postscript t]
2453 ["All MIME parts" gnus-summary-save-parts t])
2454 ("Cache"
2455 ["Enter article" gnus-cache-enter-article t]
2456 ["Remove article" gnus-cache-remove-article t])
16409b0b 2457 ["Translate" gnus-article-babel t]
23f87bed 2458 ["Select article buffer" gnus-summary-select-article-buffer t]
01c52d31 2459 ["Make article buffer sticky" gnus-sticky-article t]
23f87bed
MB
2460 ["Enter digest buffer" gnus-summary-enter-digest-group t]
2461 ["Isearch article..." gnus-summary-isearch-article t]
2462 ["Beginning of the article" gnus-summary-beginning-of-article t]
2463 ["End of the article" gnus-summary-end-of-article t]
2464 ["Fetch parent of article" gnus-summary-refer-parent-article t]
2465 ["Fetch referenced articles" gnus-summary-refer-references t]
2466 ["Fetch current thread" gnus-summary-refer-thread t]
2467 ["Fetch article with id..." gnus-summary-refer-article t]
2468 ["Setup Mailing List Params" gnus-mailing-list-insinuate t]
2469 ["Redisplay" gnus-summary-show-article t]
2470 ["Raw article" gnus-summary-show-raw-article :keys "C-u g"])))
6748645f 2471 (easy-menu-define
23f87bed
MB
2472 gnus-summary-article-menu gnus-summary-mode-map ""
2473 (cons "Article" innards))
6748645f 2474
1653df0f
SZ
2475 (if (not (keymapp gnus-summary-article-menu))
2476 (easy-menu-define
2477 gnus-article-commands-menu gnus-article-mode-map ""
2478 (cons "Commands" innards))
2479 ;; in Emacs, don't share menu.
a1506d29 2480 (setq gnus-article-commands-menu
1653df0f
SZ
2481 (copy-keymap gnus-summary-article-menu))
2482 (define-key gnus-article-mode-map [menu-bar commands]
2483 (cons "Commands" gnus-article-commands-menu))))
eec82323
LMI
2484
2485 (easy-menu-define
23f87bed
MB
2486 gnus-summary-thread-menu gnus-summary-mode-map ""
2487 '("Threads"
2488 ["Find all messages in thread" gnus-summary-refer-thread t]
2489 ["Toggle threading" gnus-summary-toggle-threads t]
2490 ["Hide threads" gnus-summary-hide-all-threads t]
2491 ["Show threads" gnus-summary-show-all-threads t]
2492 ["Hide thread" gnus-summary-hide-thread t]
2493 ["Show thread" gnus-summary-show-thread t]
2494 ["Go to next thread" gnus-summary-next-thread t]
2495 ["Go to previous thread" gnus-summary-prev-thread t]
2496 ["Go down thread" gnus-summary-down-thread t]
2497 ["Go up thread" gnus-summary-up-thread t]
2498 ["Top of thread" gnus-summary-top-thread t]
2499 ["Mark thread as read" gnus-summary-kill-thread t]
01c52d31 2500 ["Mark thread as expired" gnus-summary-expire-thread t]
23f87bed
MB
2501 ["Lower thread score" gnus-summary-lower-thread t]
2502 ["Raise thread score" gnus-summary-raise-thread t]
2503 ["Rethread current" gnus-summary-rethread-current t]))
eec82323
LMI
2504
2505 (easy-menu-define
23f87bed
MB
2506 gnus-summary-post-menu gnus-summary-mode-map ""
2507 `("Post"
2508 ["Send a message (mail or news)" gnus-summary-post-news
2509 ,@(if (featurep 'xemacs) '(t)
531e5812 2510 '(:help "Compose a new message (mail or news)"))]
23f87bed
MB
2511 ["Followup" gnus-summary-followup
2512 ,@(if (featurep 'xemacs) '(t)
2513 '(:help "Post followup to this article"))]
2514 ["Followup and yank" gnus-summary-followup-with-original
2515 ,@(if (featurep 'xemacs) '(t)
2516 '(:help "Post followup to this article, quoting its contents"))]
2517 ["Supersede article" gnus-summary-supersede-article t]
2518 ["Cancel article" gnus-summary-cancel-article
2519 ,@(if (featurep 'xemacs) '(t)
2520 '(:help "Cancel an article you posted"))]
2521 ["Reply" gnus-summary-reply t]
2522 ["Reply and yank" gnus-summary-reply-with-original t]
2523 ["Wide reply" gnus-summary-wide-reply t]
2524 ["Wide reply and yank" gnus-summary-wide-reply-with-original
2525 ,@(if (featurep 'xemacs) '(t)
2526 '(:help "Mail a reply, quoting this article"))]
2527 ["Very wide reply" gnus-summary-very-wide-reply t]
2528 ["Very wide reply and yank" gnus-summary-very-wide-reply-with-original
2529 ,@(if (featurep 'xemacs) '(t)
2530 '(:help "Mail a very wide reply, quoting this article"))]
2531 ["Mail forward" gnus-summary-mail-forward t]
2532 ["Post forward" gnus-summary-post-forward t]
2533 ["Digest and mail" gnus-uu-digest-mail-forward t]
2534 ["Digest and post" gnus-uu-digest-post-forward t]
2535 ["Resend message" gnus-summary-resend-message t]
2536 ["Resend message edit" gnus-summary-resend-message-edit t]
2537 ["Send bounced mail" gnus-summary-resend-bounced-mail t]
2538 ["Send a mail" gnus-summary-mail-other-window t]
2539 ["Create a local message" gnus-summary-news-other-window t]
2540 ["Uuencode and post" gnus-uu-post-news
2541 ,@(if (featurep 'xemacs) '(t)
2542 '(:help "Post a uuencoded article"))]
2543 ["Followup via news" gnus-summary-followup-to-mail t]
2544 ["Followup via news and yank"
2545 gnus-summary-followup-to-mail-with-original t]
2546 ;;("Draft"
2547 ;;["Send" gnus-summary-send-draft t]
2548 ;;["Send bounced" gnus-resend-bounced-mail t])
2549 ))
2550
2551 (cond
2552 ((not (keymapp gnus-summary-post-menu))
2553 (setq gnus-article-post-menu gnus-summary-post-menu))
2554 ((not gnus-article-post-menu)
2555 ;; Don't share post menu.
2556 (setq gnus-article-post-menu
2557 (copy-keymap gnus-summary-post-menu))))
2558 (define-key gnus-article-mode-map [menu-bar post]
2559 (cons "Post" gnus-article-post-menu))
eec82323
LMI
2560
2561 (easy-menu-define
23f87bed
MB
2562 gnus-summary-misc-menu gnus-summary-mode-map ""
2563 `("Gnus"
2564 ("Mark Read"
2565 ["Mark as read" gnus-summary-mark-as-read-forward t]
2566 ["Mark same subject and select"
2567 gnus-summary-kill-same-subject-and-select t]
2568 ["Mark same subject" gnus-summary-kill-same-subject t]
2569 ["Catchup" gnus-summary-catchup
2570 ,@(if (featurep 'xemacs) '(t)
2571 '(:help "Mark unread articles in this group as read"))]
2572 ["Catchup all" gnus-summary-catchup-all t]
2573 ["Catchup to here" gnus-summary-catchup-to-here t]
2574 ["Catchup from here" gnus-summary-catchup-from-here t]
2575 ["Catchup region" gnus-summary-mark-region-as-read
2576 (gnus-mark-active-p)]
2577 ["Mark excluded" gnus-summary-limit-mark-excluded-as-read t])
2578 ("Mark Various"
2579 ["Tick" gnus-summary-tick-article-forward t]
2580 ["Mark as dormant" gnus-summary-mark-as-dormant t]
2581 ["Remove marks" gnus-summary-clear-mark-forward t]
2582 ["Set expirable mark" gnus-summary-mark-as-expirable t]
2583 ["Set bookmark" gnus-summary-set-bookmark t]
2584 ["Remove bookmark" gnus-summary-remove-bookmark t])
2585 ("Limit to"
2586 ["Marks..." gnus-summary-limit-to-marks t]
2587 ["Subject..." gnus-summary-limit-to-subject t]
2588 ["Author..." gnus-summary-limit-to-author t]
01c52d31
MB
2589 ["Recipient..." gnus-summary-limit-to-recipient t]
2590 ["Address..." gnus-summary-limit-to-address t]
23f87bed
MB
2591 ["Age..." gnus-summary-limit-to-age t]
2592 ["Extra..." gnus-summary-limit-to-extra t]
2593 ["Score..." gnus-summary-limit-to-score t]
2594 ["Display Predicate" gnus-summary-limit-to-display-predicate t]
2595 ["Unread" gnus-summary-limit-to-unread t]
2596 ["Unseen" gnus-summary-limit-to-unseen t]
01c52d31
MB
2597 ["Singletons" gnus-summary-limit-to-singletons t]
2598 ["Replied" gnus-summary-limit-to-replied t]
23f87bed 2599 ["Non-dormant" gnus-summary-limit-exclude-dormant t]
996aa8c1 2600 ["Next or process marked articles" gnus-summary-limit-to-articles t]
23f87bed
MB
2601 ["Pop limit" gnus-summary-pop-limit t]
2602 ["Show dormant" gnus-summary-limit-include-dormant t]
2603 ["Hide childless dormant"
2604 gnus-summary-limit-exclude-childless-dormant t]
2605 ;;["Hide thread" gnus-summary-limit-exclude-thread t]
2606 ["Hide marked" gnus-summary-limit-exclude-marks t]
2607 ["Show expunged" gnus-summary-limit-include-expunged t])
2608 ("Process Mark"
2609 ["Set mark" gnus-summary-mark-as-processable t]
2610 ["Remove mark" gnus-summary-unmark-as-processable t]
2611 ["Remove all marks" gnus-summary-unmark-all-processable t]
01c52d31 2612 ["Invert marks" gnus-uu-invert-processable t]
23f87bed
MB
2613 ["Mark above" gnus-uu-mark-over t]
2614 ["Mark series" gnus-uu-mark-series t]
2615 ["Mark region" gnus-uu-mark-region (gnus-mark-active-p)]
2616 ["Unmark region" gnus-uu-unmark-region (gnus-mark-active-p)]
2617 ["Mark by regexp..." gnus-uu-mark-by-regexp t]
2618 ["Unmark by regexp..." gnus-uu-unmark-by-regexp t]
2619 ["Mark all" gnus-uu-mark-all t]
2620 ["Mark buffer" gnus-uu-mark-buffer t]
2621 ["Mark sparse" gnus-uu-mark-sparse t]
2622 ["Mark thread" gnus-uu-mark-thread t]
2623 ["Unmark thread" gnus-uu-unmark-thread t]
2624 ("Process Mark Sets"
2625 ["Kill" gnus-summary-kill-process-mark t]
2626 ["Yank" gnus-summary-yank-process-mark
2627 gnus-newsgroup-process-stack]
2628 ["Save" gnus-summary-save-process-mark t]
2629 ["Run command on marked..." gnus-summary-universal-argument t]))
2630 ("Scroll article"
2631 ["Page forward" gnus-summary-next-page
2632 ,@(if (featurep 'xemacs) '(t)
2633 '(:help "Show next page of article"))]
2634 ["Page backward" gnus-summary-prev-page
2635 ,@(if (featurep 'xemacs) '(t)
2636 '(:help "Show previous page of article"))]
2637 ["Line forward" gnus-summary-scroll-up t])
2638 ("Move"
2639 ["Next unread article" gnus-summary-next-unread-article t]
2640 ["Previous unread article" gnus-summary-prev-unread-article t]
2641 ["Next article" gnus-summary-next-article t]
2642 ["Previous article" gnus-summary-prev-article t]
2643 ["Next unread subject" gnus-summary-next-unread-subject t]
2644 ["Previous unread subject" gnus-summary-prev-unread-subject t]
2645 ["Next article same subject" gnus-summary-next-same-subject t]
2646 ["Previous article same subject" gnus-summary-prev-same-subject t]
2647 ["First unread article" gnus-summary-first-unread-article t]
2648 ["Best unread article" gnus-summary-best-unread-article t]
2649 ["Go to subject number..." gnus-summary-goto-subject t]
2650 ["Go to article number..." gnus-summary-goto-article t]
2651 ["Go to the last article" gnus-summary-goto-last-article t]
2652 ["Pop article off history" gnus-summary-pop-article t])
2653 ("Sort"
2654 ["Sort by number" gnus-summary-sort-by-number t]
2655 ["Sort by author" gnus-summary-sort-by-author t]
01c52d31 2656 ["Sort by recipient" gnus-summary-sort-by-recipient t]
23f87bed
MB
2657 ["Sort by subject" gnus-summary-sort-by-subject t]
2658 ["Sort by date" gnus-summary-sort-by-date t]
2659 ["Sort by score" gnus-summary-sort-by-score t]
2660 ["Sort by lines" gnus-summary-sort-by-lines t]
2661 ["Sort by characters" gnus-summary-sort-by-chars t]
2662 ["Randomize" gnus-summary-sort-by-random t]
2663 ["Original sort" gnus-summary-sort-by-original t])
2664 ("Help"
2665 ["Fetch group FAQ" gnus-summary-fetch-faq t]
2666 ["Describe group" gnus-summary-describe-group t]
2667 ["Fetch charter" gnus-group-fetch-charter
2668 ,@(if (featurep 'xemacs) nil
2669 '(:help "Display the charter of the current group"))]
2670 ["Fetch control message" gnus-group-fetch-control
2671 ,@(if (featurep 'xemacs) nil
2672 '(:help "Display the archived control message for the current group"))]
2673 ["Read manual" gnus-info-find-node t])
2674 ("Modes"
2675 ["Pick and read" gnus-pick-mode t]
2676 ["Binary" gnus-binary-mode t])
2677 ("Regeneration"
2678 ["Regenerate" gnus-summary-prepare t]
2679 ["Insert cached articles" gnus-summary-insert-cached-articles t]
2680 ["Insert dormant articles" gnus-summary-insert-dormant-articles t]
01c52d31 2681 ["Insert ticked articles" gnus-summary-insert-ticked-articles t]
23f87bed
MB
2682 ["Toggle threading" gnus-summary-toggle-threads t])
2683 ["See old articles" gnus-summary-insert-old-articles t]
2684 ["See new articles" gnus-summary-insert-new-articles t]
2685 ["Filter articles..." gnus-summary-execute-command t]
2686 ["Run command on articles..." gnus-summary-universal-argument t]
2687 ["Search articles forward..." gnus-summary-search-article-forward t]
2688 ["Search articles backward..." gnus-summary-search-article-backward t]
2689 ["Toggle line truncation" gnus-summary-toggle-truncation t]
2690 ["Expand window" gnus-summary-expand-window t]
2691 ["Expire expirable articles" gnus-summary-expire-articles
2692 (gnus-check-backend-function
2693 'request-expire-articles gnus-newsgroup-name)]
2694 ["Edit local kill file" gnus-summary-edit-local-kill t]
2695 ["Edit main kill file" gnus-summary-edit-global-kill t]
2696 ["Edit group parameters" gnus-summary-edit-parameters t]
2697 ["Customize group parameters" gnus-summary-customize-parameters t]
2698 ["Send a bug report" gnus-bug t]
2699 ("Exit"
2700 ["Catchup and exit" gnus-summary-catchup-and-exit
2701 ,@(if (featurep 'xemacs) '(t)
2702 '(:help "Mark unread articles in this group as read, then exit"))]
2703 ["Catchup all and exit" gnus-summary-catchup-all-and-exit t]
2704 ["Catchup and goto next" gnus-summary-catchup-and-goto-next-group t]
01c52d31 2705 ["Catchup and goto prev" gnus-summary-catchup-and-goto-prev-group t]
23f87bed
MB
2706 ["Exit group" gnus-summary-exit
2707 ,@(if (featurep 'xemacs) '(t)
2708 '(:help "Exit current group, return to group selection mode"))]
2709 ["Exit group without updating" gnus-summary-exit-no-update t]
2710 ["Exit and goto next group" gnus-summary-next-group t]
2711 ["Exit and goto prev group" gnus-summary-prev-group t]
2712 ["Reselect group" gnus-summary-reselect-current-group t]
2713 ["Rescan group" gnus-summary-rescan-group t]
2714 ["Update dribble" gnus-summary-save-newsrc t])))
eec82323 2715
6748645f 2716 (gnus-run-hooks 'gnus-summary-menu-hook)))
eec82323 2717
60bd5589
DL
2718(defvar gnus-summary-tool-bar-map nil)
2719
18c06a99
RS
2720;; Note: The :set function in the `gnus-summary-tool-bar*' variables will only
2721;; affect _new_ message buffers. We might add a function that walks thru all
2722;; summary-mode buffers and force the update.
2723(defun gnus-summary-tool-bar-update (&optional symbol value)
2724 "Update summary mode toolbar.
2725Setter function for custom variables."
2726 (setq-default gnus-summary-tool-bar-map nil)
2727 (when symbol
2728 ;; When used as ":set" function:
2729 (set-default symbol value))
2730 (when (gnus-buffer-live-p gnus-summary-buffer)
2731 (with-current-buffer gnus-summary-buffer
2732 (gnus-summary-make-tool-bar))))
2733
2734(defcustom gnus-summary-tool-bar (if (eq gmm-tool-bar-style 'gnome)
2735 'gnus-summary-tool-bar-gnome
2736 'gnus-summary-tool-bar-retro)
2737 "Specifies the Gnus summary tool bar.
2738
2739It can be either a list or a symbol refering to a list. See
2740`gmm-tool-bar-from-list' for the format of the list. The
2741default key map is `gnus-summary-mode-map'.
2742
2743Pre-defined symbols include `gnus-summary-tool-bar-gnome' and
2744`gnus-summary-tool-bar-retro'."
2745 :type '(choice (const :tag "GNOME style" gnus-summary-tool-bar-gnome)
2746 (const :tag "Retro look" gnus-summary-tool-bar-retro)
2747 (repeat :tag "User defined list" gmm-tool-bar-item)
2748 (symbol))
01c52d31 2749 :version "23.0" ;; No Gnus
18c06a99
RS
2750 :initialize 'custom-initialize-default
2751 :set 'gnus-summary-tool-bar-update
2752 :group 'gnus-summary)
2753
2754(defcustom gnus-summary-tool-bar-gnome
2755 '((gnus-summary-post-news "mail/compose" nil)
2756 (gnus-summary-insert-new-articles "mail/inbox" nil
2757 :visible (or (not gnus-agent)
2758 gnus-plugged))
2759 (gnus-summary-reply-with-original "mail/reply")
2760 (gnus-summary-reply "mail/reply" nil :visible nil)
2761 (gnus-summary-followup-with-original "mail/reply-all")
2762 (gnus-summary-followup "mail/reply-all" nil :visible nil)
2763 (gnus-summary-mail-forward "mail/forward")
2764 (gnus-summary-save-article "mail/save")
2765 (gnus-summary-search-article-forward "search" nil :visible nil)
2766 (gnus-summary-print-article "print")
2767 (gnus-summary-tick-article-forward "flag-followup" nil :visible nil)
2768 ;; Some new commands that may need more suitable icons:
2769 (gnus-summary-save-newsrc "save" nil :visible nil)
2770 ;; (gnus-summary-show-article "stock_message-display" nil :visible nil)
2771 (gnus-summary-prev-article "left-arrow")
2772 (gnus-summary-next-article "right-arrow")
2773 (gnus-summary-next-page "next-page")
2774 ;; (gnus-summary-enter-digest-group "right_arrow" nil :visible nil)
2775 ;;
2776 ;; Maybe some sort-by-... could be added:
2777 ;; (gnus-summary-sort-by-author "sort-a-z" nil :visible nil)
2778 ;; (gnus-summary-sort-by-date "sort-1-9" nil :visible nil)
2779 (gnus-summary-mark-as-expirable
2780 "delete" nil
2781 :visible (gnus-check-backend-function 'request-expire-articles
2782 gnus-newsgroup-name))
2783 (gnus-summary-mark-as-spam
2784 "mail/spam" t
2785 :visible (and (fboundp 'spam-group-ham-contents-p)
2786 (spam-group-ham-contents-p gnus-newsgroup-name))
2787 :help "Mark as spam")
2788 (gnus-summary-mark-as-read-forward
2789 "mail/not-spam" nil
2790 :visible (and (fboundp 'spam-group-spam-contents-p)
2791 (spam-group-spam-contents-p gnus-newsgroup-name)))
2792 ;;
2793 (gnus-summary-exit "exit")
2794 (gmm-customize-mode "preferences" t :help "Edit mode preferences")
2795 (gnus-info-find-node "help"))
2796 "List of functions for the summary tool bar (GNOME style).
2797
2798See `gmm-tool-bar-from-list' for the format of the list."
2799 :type '(repeat gmm-tool-bar-item)
01c52d31 2800 :version "23.0" ;; No Gnus
18c06a99
RS
2801 :initialize 'custom-initialize-default
2802 :set 'gnus-summary-tool-bar-update
2803 :group 'gnus-summary)
2804
2805(defcustom gnus-summary-tool-bar-retro
2806 '((gnus-summary-prev-unread-article "gnus/prev-ur")
2807 (gnus-summary-next-unread-article "gnus/next-ur")
2808 (gnus-summary-post-news "gnus/post")
2809 (gnus-summary-followup-with-original "gnus/fuwo")
2810 (gnus-summary-followup "gnus/followup")
2811 (gnus-summary-reply-with-original "gnus/reply-wo")
2812 (gnus-summary-reply "gnus/reply")
2813 (gnus-summary-caesar-message "gnus/rot13")
2814 (gnus-uu-decode-uu "gnus/uu-decode")
2815 (gnus-summary-save-article-file "gnus/save-aif")
2816 (gnus-summary-save-article "gnus/save-art")
2817 (gnus-uu-post-news "gnus/uu-post")
2818 (gnus-summary-catchup "gnus/catchup")
2819 (gnus-summary-catchup-and-exit "gnus/cu-exit")
2820 (gnus-summary-exit "gnus/exit-summ")
2821 ;; Some new command that may need more suitable icons:
2822 (gnus-summary-print-article "gnus/print" nil :visible nil)
2823 (gnus-summary-mark-as-expirable "gnus/close" nil :visible nil)
2824 (gnus-summary-save-newsrc "gnus/save" nil :visible nil)
2825 ;; (gnus-summary-enter-digest-group "gnus/right_arrow" nil :visible nil)
2826 (gnus-summary-search-article-forward "gnus/search" nil :visible nil)
2827 ;; (gnus-summary-insert-new-articles "gnus/paste" nil :visible nil)
2828 ;; (gnus-summary-toggle-threads "gnus/open" nil :visible nil)
2829 ;;
2830 (gnus-info-find-node "gnus/help" nil :visible nil))
2831 "List of functions for the summary tool bar (retro look).
2832
2833See `gmm-tool-bar-from-list' for the format of the list."
2834 :type '(repeat gmm-tool-bar-item)
01c52d31 2835 :version "23.0" ;; No Gnus
18c06a99
RS
2836 :initialize 'custom-initialize-default
2837 :set 'gnus-summary-tool-bar-update
2838 :group 'gnus-summary)
2839
2840(defcustom gnus-summary-tool-bar-zap-list t
2841 "List of icon items from the global tool bar.
2842These items are not displayed in the Gnus summary mode tool bar.
2843
2844See `gmm-tool-bar-from-list' for the format of the list."
2845 :type 'gmm-tool-bar-zap-list
01c52d31 2846 :version "23.0" ;; No Gnus
18c06a99
RS
2847 :initialize 'custom-initialize-default
2848 :set 'gnus-summary-tool-bar-update
2849 :group 'gnus-summary)
2850
2851(defvar image-load-path)
2852
2853(defun gnus-summary-make-tool-bar (&optional force)
2854 "Make a summary mode tool bar from `gnus-summary-tool-bar'.
2855When FORCE, rebuild the tool bar."
2856 (when (and (not (featurep 'xemacs))
2857 (boundp 'tool-bar-mode)
2858 tool-bar-mode
2859 (or (not gnus-summary-tool-bar-map) force))
2860 (let* ((load-path
2861 (gmm-image-load-path-for-library "gnus"
2862 "mail/save.xpm"
2863 nil t))
2864 (image-load-path (cons (car load-path)
2865 (when (boundp 'image-load-path)
2866 image-load-path)))
2867 (map (gmm-tool-bar-from-list gnus-summary-tool-bar
2868 gnus-summary-tool-bar-zap-list
2869 'gnus-summary-mode-map)))
2870 (when map
2871 ;; Need to set `gnus-summary-tool-bar-map' because `gnus-article-mode'
2872 ;; uses it's value.
2873 (setq gnus-summary-tool-bar-map map))))
2874 (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map))
60bd5589 2875
eec82323
LMI
2876(defun gnus-score-set-default (var value)
2877 "A version of set that updates the GNU Emacs menu-bar."
2878 (set var value)
2879 ;; It is the message that forces the active status to be updated.
2880 (message ""))
2881
2882(defun gnus-make-score-map (type)
2883 "Make a summary score map of type TYPE."
2884 (if t
2885 nil
2886 (let ((headers '(("author" "from" string)
2887 ("subject" "subject" string)
2888 ("article body" "body" string)
2889 ("article head" "head" string)
2890 ("xref" "xref" string)
16409b0b 2891 ("extra header" "extra" string)
eec82323
LMI
2892 ("lines" "lines" number)
2893 ("followups to author" "followup" string)))
2894 (types '((number ("less than" <)
2895 ("greater than" >)
2896 ("equal" =))
2897 (string ("substring" s)
2898 ("exact string" e)
2899 ("fuzzy string" f)
2900 ("regexp" r))))
2901 (perms '(("temporary" (current-time-string))
2902 ("permanent" nil)
2903 ("immediate" now)))
2904 header)
2905 (list
2906 (apply
2907 'nconc
2908 (list
2909 (if (eq type 'lower)
2910 "Lower score"
2911 "Increase score"))
2912 (let (outh)
2913 (while headers
2914 (setq header (car headers))
2915 (setq outh
2916 (cons
2917 (apply
2918 'nconc
2919 (list (car header))
2920 (let ((ts (cdr (assoc (nth 2 header) types)))
2921 outt)
2922 (while ts
2923 (setq outt
2924 (cons
2925 (apply
2926 'nconc
2927 (list (caar ts))
2928 (let ((ps perms)
2929 outp)
2930 (while ps
2931 (setq outp
2932 (cons
2933 (vector
2934 (caar ps)
2935 (list
2936 'gnus-summary-score-entry
2937 (nth 1 header)
2938 (if (or (string= (nth 1 header)
2939 "head")
2940 (string= (nth 1 header)
2941 "body"))
2942 ""
2943 (list 'gnus-summary-header
2944 (nth 1 header)))
2945 (list 'quote (nth 1 (car ts)))
16409b0b
GM
2946 (list 'gnus-score-delta-default
2947 nil)
eec82323
LMI
2948 (nth 1 (car ps))
2949 t)
2950 t)
2951 outp))
2952 (setq ps (cdr ps)))
2953 (list (nreverse outp))))
2954 outt))
2955 (setq ts (cdr ts)))
2956 (list (nreverse outt))))
2957 outh))
2958 (setq headers (cdr headers)))
2959 (list (nreverse outh))))))))
2960
2961\f
2962
2963(defun gnus-summary-mode (&optional group)
2964 "Major mode for reading articles.
2965
2966All normal editing commands are switched off.
2967\\<gnus-summary-mode-map>
2968Each line in this buffer represents one article. To read an
2969article, you can, for instance, type `\\[gnus-summary-next-page]'. To move forwards
2970and backwards while displaying articles, type `\\[gnus-summary-next-unread-article]' and `\\[gnus-summary-prev-unread-article]',
2971respectively.
2972
2973You can also post articles and send mail from this buffer. To
23f87bed 2974follow up an article, type `\\[gnus-summary-followup]'. To mail a reply to the author
eec82323
LMI
2975of an article, type `\\[gnus-summary-reply]'.
2976
2977There are approx. one gazillion commands you can execute in this
2978buffer; read the info pages for more information (`\\[gnus-info-find-node]').
2979
2980The following commands are available:
2981
2982\\{gnus-summary-mode-map}"
2983 (interactive)
eec82323 2984 (kill-all-local-variables)
01c52d31
MB
2985 (let ((gnus-summary-local-variables gnus-newsgroup-variables))
2986 (gnus-summary-make-local-variables))
2987 (gnus-summary-make-local-variables)
2988 (setq gnus-newsgroup-name group)
60bd5589
DL
2989 (when (gnus-visual-p 'summary-menu 'menu)
2990 (gnus-summary-make-menu-bar)
2991 (gnus-summary-make-tool-bar))
eec82323
LMI
2992 (gnus-make-thread-indent-array)
2993 (gnus-simplify-mode-line)
2994 (setq major-mode 'gnus-summary-mode)
2995 (setq mode-name "Summary")
2996 (make-local-variable 'minor-mode-alist)
2997 (use-local-map gnus-summary-mode-map)
16409b0b 2998 (buffer-disable-undo)
01c52d31
MB
2999 (setq buffer-read-only t ;Disable modification
3000 show-trailing-whitespace nil)
eec82323
LMI
3001 (setq truncate-lines t)
3002 (setq selective-display t)
3003 (setq selective-display-ellipses t) ;Display `...'
3004 (gnus-summary-set-display-table)
3005 (gnus-set-default-directory)
eec82323
LMI
3006 (make-local-variable 'gnus-summary-line-format)
3007 (make-local-variable 'gnus-summary-line-format-spec)
6748645f
LMI
3008 (make-local-variable 'gnus-summary-dummy-line-format)
3009 (make-local-variable 'gnus-summary-dummy-line-format-spec)
eec82323 3010 (make-local-variable 'gnus-summary-mark-positions)
23f87bed 3011 (gnus-make-local-hook 'pre-command-hook)
6748645f 3012 (add-hook 'pre-command-hook 'gnus-set-global-variables nil t)
cfcd5c91 3013 (gnus-run-mode-hooks 'gnus-summary-mode-hook)
23f87bed 3014 (turn-on-gnus-mailing-list-mode)
87545352 3015 (mm-enable-multibyte)
eec82323
LMI
3016 (gnus-update-format-specifications nil 'summary 'summary-mode 'summary-dummy)
3017 (gnus-update-summary-mark-positions))
3018
3019(defun gnus-summary-make-local-variables ()
3020 "Make all the local summary buffer variables."
16409b0b
GM
3021 (let (global)
3022 (dolist (local gnus-summary-local-variables)
eec82323
LMI
3023 (if (consp local)
3024 (progn
3025 (if (eq (cdr local) 'global)
3026 ;; Copy the global value of the variable.
3027 (setq global (symbol-value (car local)))
3028 ;; Use the value from the list.
3029 (setq global (eval (cdr local))))
16409b0b 3030 (set (make-local-variable (car local)) global))
eec82323 3031 ;; Simple nil-valued local variable.
16409b0b 3032 (set (make-local-variable local) nil)))))
eec82323
LMI
3033
3034(defun gnus-summary-clear-local-variables ()
3035 (let ((locals gnus-summary-local-variables))
3036 (while locals
3037 (if (consp (car locals))
01c52d31 3038 (and (symbolp (caar locals))
eec82323 3039 (set (caar locals) nil))
01c52d31 3040 (and (symbolp (car locals))
eec82323
LMI
3041 (set (car locals) nil)))
3042 (setq locals (cdr locals)))))
3043
3044;; Summary data functions.
3045
3046(defmacro gnus-data-number (data)
3047 `(car ,data))
3048
3049(defmacro gnus-data-set-number (data number)
3050 `(setcar ,data ,number))
3051
3052(defmacro gnus-data-mark (data)
3053 `(nth 1 ,data))
3054
3055(defmacro gnus-data-set-mark (data mark)
3056 `(setcar (nthcdr 1 ,data) ,mark))
3057
3058(defmacro gnus-data-pos (data)
3059 `(nth 2 ,data))
3060
3061(defmacro gnus-data-set-pos (data pos)
3062 `(setcar (nthcdr 2 ,data) ,pos))
3063
3064(defmacro gnus-data-header (data)
3065 `(nth 3 ,data))
3066
3067(defmacro gnus-data-set-header (data header)
3068 `(setf (nth 3 ,data) ,header))
3069
3070(defmacro gnus-data-level (data)
3071 `(nth 4 ,data))
3072
3073(defmacro gnus-data-unread-p (data)
3074 `(= (nth 1 ,data) gnus-unread-mark))
3075
3076(defmacro gnus-data-read-p (data)
3077 `(/= (nth 1 ,data) gnus-unread-mark))
3078
3079(defmacro gnus-data-pseudo-p (data)
3080 `(consp (nth 3 ,data)))
3081
3082(defmacro gnus-data-find (number)
3083 `(assq ,number gnus-newsgroup-data))
3084
3085(defmacro gnus-data-find-list (number &optional data)
3086 `(let ((bdata ,(or data 'gnus-newsgroup-data)))
3087 (memq (assq ,number bdata)
3088 bdata)))
3089
3090(defmacro gnus-data-make (number mark pos header level)
3091 `(list ,number ,mark ,pos ,header ,level))
3092
3093(defun gnus-data-enter (after-article number mark pos header level offset)
3094 (let ((data (gnus-data-find-list after-article)))
3095 (unless data
3096 (error "No such article: %d" after-article))
3097 (setcdr data (cons (gnus-data-make number mark pos header level)
3098 (cdr data)))
3099 (setq gnus-newsgroup-data-reverse nil)
3100 (gnus-data-update-list (cddr data) offset)))
3101
3102(defun gnus-data-enter-list (after-article list &optional offset)
3103 (when list
3104 (let ((data (and after-article (gnus-data-find-list after-article)))
3105 (ilist list))
6748645f
LMI
3106 (if (not (or data
3107 after-article))
3108 (let ((odata gnus-newsgroup-data))
3109 (setq gnus-newsgroup-data (nconc list gnus-newsgroup-data))
eec82323 3110 (when offset
6748645f 3111 (gnus-data-update-list odata offset)))
01c52d31 3112 ;; Find the last element in the list to be spliced into the main
6748645f 3113 ;; list.
01c52d31 3114 (setq list (last list))
6748645f
LMI
3115 (if (not data)
3116 (progn
3117 (setcdr list gnus-newsgroup-data)
3118 (setq gnus-newsgroup-data ilist)
3119 (when offset
3120 (gnus-data-update-list (cdr list) offset)))
3121 (setcdr list (cdr data))
3122 (setcdr data ilist)
3123 (when offset
3124 (gnus-data-update-list (cdr list) offset))))
eec82323
LMI
3125 (setq gnus-newsgroup-data-reverse nil))))
3126
3127(defun gnus-data-remove (article &optional offset)
3128 (let ((data gnus-newsgroup-data))
3129 (if (= (gnus-data-number (car data)) article)
3130 (progn
3131 (setq gnus-newsgroup-data (cdr gnus-newsgroup-data)
3132 gnus-newsgroup-data-reverse nil)
3133 (when offset
3134 (gnus-data-update-list gnus-newsgroup-data offset)))
3135 (while (cdr data)
3136 (when (= (gnus-data-number (cadr data)) article)
3137 (setcdr data (cddr data))
3138 (when offset
3139 (gnus-data-update-list (cdr data) offset))
3140 (setq data nil
3141 gnus-newsgroup-data-reverse nil))
3142 (setq data (cdr data))))))
3143
3144(defmacro gnus-data-list (backward)
3145 `(if ,backward
3146 (or gnus-newsgroup-data-reverse
3147 (setq gnus-newsgroup-data-reverse
3148 (reverse gnus-newsgroup-data)))
3149 gnus-newsgroup-data))
3150
3151(defun gnus-data-update-list (data offset)
3152 "Add OFFSET to the POS of all data entries in DATA."
6748645f 3153 (setq gnus-newsgroup-data-reverse nil)
eec82323
LMI
3154 (while data
3155 (setcar (nthcdr 2 (car data)) (+ offset (nth 2 (car data))))
3156 (setq data (cdr data))))
3157
eec82323
LMI
3158(defun gnus-summary-article-pseudo-p (article)
3159 "Say whether this article is a pseudo article or not."
3160 (not (vectorp (gnus-data-header (gnus-data-find article)))))
3161
3162(defmacro gnus-summary-article-sparse-p (article)
3163 "Say whether this article is a sparse article or not."
a8151ef7 3164 `(memq ,article gnus-newsgroup-sparse))
eec82323
LMI
3165
3166(defmacro gnus-summary-article-ancient-p (article)
3167 "Say whether this article is a sparse article or not."
3168 `(memq ,article gnus-newsgroup-ancient))
3169
3170(defun gnus-article-parent-p (number)
3171 "Say whether this article is a parent or not."
3172 (let ((data (gnus-data-find-list number)))
23f87bed 3173 (and (cdr data) ; There has to be an article after...
eec82323
LMI
3174 (< (gnus-data-level (car data)) ; And it has to have a higher level.
3175 (gnus-data-level (nth 1 data))))))
3176
3177(defun gnus-article-children (number)
3178 "Return a list of all children to NUMBER."
3179 (let* ((data (gnus-data-find-list number))
3180 (level (gnus-data-level (car data)))
3181 children)
3182 (setq data (cdr data))
3183 (while (and data
3184 (= (gnus-data-level (car data)) (1+ level)))
3185 (push (gnus-data-number (car data)) children)
3186 (setq data (cdr data)))
3187 children))
3188
3189(defmacro gnus-summary-skip-intangible ()
3190 "If the current article is intangible, then jump to a different article."
3191 '(let ((to (get-text-property (point) 'gnus-intangible)))
3192 (and to (gnus-summary-goto-subject to))))
3193
3194(defmacro gnus-summary-article-intangible-p ()
3195 "Say whether this article is intangible or not."
3196 '(get-text-property (point) 'gnus-intangible))
3197
3198(defun gnus-article-read-p (article)
3199 "Say whether ARTICLE is read or not."
3200 (not (or (memq article gnus-newsgroup-marked)
23f87bed 3201 (memq article gnus-newsgroup-spam-marked)
eec82323
LMI
3202 (memq article gnus-newsgroup-unreads)
3203 (memq article gnus-newsgroup-unselected)
3204 (memq article gnus-newsgroup-dormant))))
3205
3206;; Some summary mode macros.
3207
3208(defmacro gnus-summary-article-number ()
3209 "The article number of the article on the current line.
8f688cb0 3210If there isn't an article number here, then we return the current
eec82323
LMI
3211article number."
3212 '(progn
3213 (gnus-summary-skip-intangible)
3214 (or (get-text-property (point) 'gnus-number)
3215 (gnus-summary-last-subject))))
3216
3217(defmacro gnus-summary-article-header (&optional number)
6748645f 3218 "Return the header of article NUMBER."
eec82323
LMI
3219 `(gnus-data-header (gnus-data-find
3220 ,(or number '(gnus-summary-article-number)))))
3221
3222(defmacro gnus-summary-thread-level (&optional number)
6748645f 3223 "Return the level of thread that starts with article NUMBER."
eec82323
LMI
3224 `(if (and (eq gnus-summary-make-false-root 'dummy)
3225 (get-text-property (point) 'gnus-intangible))
3226 0
3227 (gnus-data-level (gnus-data-find
3228 ,(or number '(gnus-summary-article-number))))))
3229
3230(defmacro gnus-summary-article-mark (&optional number)
6748645f 3231 "Return the mark of article NUMBER."
eec82323
LMI
3232 `(gnus-data-mark (gnus-data-find
3233 ,(or number '(gnus-summary-article-number)))))
3234
3235(defmacro gnus-summary-article-pos (&optional number)
6748645f 3236 "Return the position of the line of article NUMBER."
eec82323
LMI
3237 `(gnus-data-pos (gnus-data-find
3238 ,(or number '(gnus-summary-article-number)))))
3239
3240(defalias 'gnus-summary-subject-string 'gnus-summary-article-subject)
3241(defmacro gnus-summary-article-subject (&optional number)
3242 "Return current subject string or nil if nothing."
3243 `(let ((headers
3244 ,(if number
3245 `(gnus-data-header (assq ,number gnus-newsgroup-data))
3246 '(gnus-data-header (assq (gnus-summary-article-number)
3247 gnus-newsgroup-data)))))
3248 (and headers
3249 (vectorp headers)
3250 (mail-header-subject headers))))
3251
3252(defmacro gnus-summary-article-score (&optional number)
3253 "Return current article score."
3254 `(or (cdr (assq ,(or number '(gnus-summary-article-number))
3255 gnus-newsgroup-scored))
3256 gnus-summary-default-score 0))
3257
3258(defun gnus-summary-article-children (&optional number)
6748645f 3259 "Return a list of article numbers that are children of article NUMBER."
eec82323
LMI
3260 (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))))
3261 (level (gnus-data-level (car data)))
3262 l children)
3263 (while (and (setq data (cdr data))
3264 (> (setq l (gnus-data-level (car data))) level))
3265 (and (= (1+ level) l)
3266 (push (gnus-data-number (car data))
3267 children)))
3268 (nreverse children)))
3269
3270(defun gnus-summary-article-parent (&optional number)
6748645f 3271 "Return the article number of the parent of article NUMBER."
eec82323
LMI
3272 (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))
3273 (gnus-data-list t)))
3274 (level (gnus-data-level (car data))))
3275 (if (zerop level)
3276 () ; This is a root.
3277 ;; We search until we find an article with a level less than
3278 ;; this one. That function has to be the parent.
3279 (while (and (setq data (cdr data))
3280 (not (< (gnus-data-level (car data)) level))))
3281 (and data (gnus-data-number (car data))))))
3282
3283(defun gnus-unread-mark-p (mark)
3284 "Say whether MARK is the unread mark."
3285 (= mark gnus-unread-mark))
3286
3287(defun gnus-read-mark-p (mark)
3288 "Say whether MARK is one of the marks that mark as read.
3289This is all marks except unread, ticked, dormant, and expirable."
3290 (not (or (= mark gnus-unread-mark)
3291 (= mark gnus-ticked-mark)
23f87bed 3292 (= mark gnus-spam-mark)
eec82323
LMI
3293 (= mark gnus-dormant-mark)
3294 (= mark gnus-expirable-mark))))
3295
3296(defmacro gnus-article-mark (number)
6748645f
LMI
3297 "Return the MARK of article NUMBER.
3298This macro should only be used when computing the mark the \"first\"
3299time; i.e., when generating the summary lines. After that,
3300`gnus-summary-article-mark' should be used to examine the
3301marks of articles."
eec82323 3302 `(cond
6748645f 3303 ((memq ,number gnus-newsgroup-unsendable) gnus-unsendable-mark)
6748645f 3304 ((memq ,number gnus-newsgroup-downloadable) gnus-downloadable-mark)
eec82323
LMI
3305 ((memq ,number gnus-newsgroup-unreads) gnus-unread-mark)
3306 ((memq ,number gnus-newsgroup-marked) gnus-ticked-mark)
23f87bed 3307 ((memq ,number gnus-newsgroup-spam-marked) gnus-spam-mark)
eec82323
LMI
3308 ((memq ,number gnus-newsgroup-dormant) gnus-dormant-mark)
3309 ((memq ,number gnus-newsgroup-expirable) gnus-expirable-mark)
3310 (t (or (cdr (assq ,number gnus-newsgroup-reads))
3311 gnus-ancient-mark))))
3312
3313;; Saving hidden threads.
3314
eec82323
LMI
3315(defmacro gnus-save-hidden-threads (&rest forms)
3316 "Save hidden threads, eval FORMS, and restore the hidden threads."
3317 (let ((config (make-symbol "config")))
3318 `(let ((,config (gnus-hidden-threads-configuration)))
3319 (unwind-protect
3320 (save-excursion
3321 ,@forms)
3322 (gnus-restore-hidden-threads-configuration ,config)))))
23f87bed
MB
3323(put 'gnus-save-hidden-threads 'lisp-indent-function 0)
3324(put 'gnus-save-hidden-threads 'edebug-form-spec '(body))
eec82323 3325
107ecebb
AS
3326(defun gnus-data-compute-positions ()
3327 "Compute the positions of all articles."
3328 (setq gnus-newsgroup-data-reverse nil)
3329 (let ((data gnus-newsgroup-data))
3330 (save-excursion
3331 (gnus-save-hidden-threads
3332 (gnus-summary-show-all-threads)
3333 (goto-char (point-min))
3334 (while data
3335 (while (get-text-property (point) 'gnus-intangible)
3336 (forward-line 1))
3337 (gnus-data-set-pos (car data) (+ (point) 3))
3338 (setq data (cdr data))
3339 (forward-line 1))))))
3340
16409b0b
GM
3341(defun gnus-hidden-threads-configuration ()
3342 "Return the current hidden threads configuration."
3343 (save-excursion
3344 (let (config)
3345 (goto-char (point-min))
3346 (while (search-forward "\r" nil t)
3347 (push (1- (point)) config))
3348 config)))
3349
3350(defun gnus-restore-hidden-threads-configuration (config)
3351 "Restore hidden threads configuration from CONFIG."
3352 (save-excursion
3353 (let (point buffer-read-only)
3354 (while (setq point (pop config))
3355 (when (and (< point (point-max))
3356 (goto-char point)
3357 (eq (char-after) ?\n))
3358 (subst-char-in-region point (1+ point) ?\n ?\r))))))
3359
eec82323
LMI
3360;; Various summary mode internalish functions.
3361
3362(defun gnus-mouse-pick-article (e)
3363 (interactive "e")
3364 (mouse-set-point e)
3365 (gnus-summary-next-page nil t))
3366
3367(defun gnus-summary-set-display-table ()
16409b0b
GM
3368 "Change the display table.
3369Odd characters have a tendency to mess
3370up nicely formatted displays - we make all possible glyphs
3371display only a single character."
eec82323
LMI
3372
3373 ;; We start from the standard display table, if any.
3374 (let ((table (or (copy-sequence standard-display-table)
3375 (make-display-table)))
3376 (i 32))
3377 ;; Nix out all the control chars...
3378 (while (>= (setq i (1- i)) 0)
3379 (aset table i [??]))
23f87bed 3380 ;; ... but not newline and cr, of course. (cr is necessary for the
eec82323
LMI
3381 ;; selective display).
3382 (aset table ?\n nil)
3383 (aset table ?\r nil)
6748645f
LMI
3384 ;; We keep TAB as well.
3385 (aset table ?\t nil)
719120ef 3386 ;; We nix out any glyphs 127 through 255, or 127 through 159 in
fe62aacc 3387 ;; Emacs 23 (unicode), that are not set already.
719120ef
MB
3388 (let ((i (if (ignore-errors (= (make-char 'latin-iso8859-1 160) 160))
3389 160
3390 256)))
eec82323
LMI
3391 (while (>= (setq i (1- i)) 127)
3392 ;; Only modify if the entry is nil.
3393 (unless (aref table i)
3394 (aset table i [??]))))
3395 (setq buffer-display-table table)))
3396
23f87bed
MB
3397(defun gnus-summary-set-article-display-arrow (pos)
3398 "Update the overlay arrow to point to line at position POS."
3399 (when (and gnus-summary-display-arrow
3400 (boundp 'overlay-arrow-position)
3401 (boundp 'overlay-arrow-string))
3402 (save-excursion
3403 (goto-char pos)
3404 (beginning-of-line)
3405 (unless overlay-arrow-position
3406 (setq overlay-arrow-position (make-marker)))
3407 (setq overlay-arrow-string "=>"
3408 overlay-arrow-position (set-marker overlay-arrow-position
3409 (point)
3410 (current-buffer))))))
3411
eec82323
LMI
3412(defun gnus-summary-setup-buffer (group)
3413 "Initialize summary buffer."
23f87bed
MB
3414 (let ((buffer (gnus-summary-buffer-name group))
3415 (dead-name (concat "*Dead Summary "
3416 (gnus-group-decoded-name group) "*")))
3417 ;; If a dead summary buffer exists, we kill it.
3418 (when (gnus-buffer-live-p dead-name)
3419 (gnus-kill-buffer dead-name))
eec82323
LMI
3420 (if (get-buffer buffer)
3421 (progn
3422 (set-buffer buffer)
3423 (setq gnus-summary-buffer (current-buffer))
3424 (not gnus-newsgroup-prepared))
3425 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
6748645f 3426 (setq gnus-summary-buffer (set-buffer (gnus-get-buffer-create buffer)))
eec82323
LMI
3427 (gnus-summary-mode group)
3428 (when gnus-carpal
3429 (gnus-carpal-setup-buffer 'summary))
01c52d31
MB
3430 (when (gnus-group-quit-config group)
3431 (set (make-local-variable 'gnus-single-article-buffer) nil))
3432 (make-local-variable 'gnus-article-buffer)
3433 (make-local-variable 'gnus-article-current)
3434 (make-local-variable 'gnus-original-article-buffer)
eec82323 3435 (setq gnus-newsgroup-name group)
23f87bed
MB
3436 ;; Set any local variables in the group parameters.
3437 (gnus-summary-set-local-parameters gnus-newsgroup-name)
eec82323
LMI
3438 t)))
3439
3440(defun gnus-set-global-variables ()
16409b0b
GM
3441 "Set the global equivalents of the buffer-local variables.
3442They are set to the latest values they had. These reflect the summary
3443buffer that was in action when the last article was fetched."
eec82323
LMI
3444 (when (eq major-mode 'gnus-summary-mode)
3445 (setq gnus-summary-buffer (current-buffer))
3446 (let ((name gnus-newsgroup-name)
3447 (marked gnus-newsgroup-marked)
23f87bed 3448 (spam gnus-newsgroup-spam-marked)
eec82323
LMI
3449 (unread gnus-newsgroup-unreads)
3450 (headers gnus-current-headers)
3451 (data gnus-newsgroup-data)
3452 (summary gnus-summary-buffer)
3453 (article-buffer gnus-article-buffer)
3454 (original gnus-original-article-buffer)
3455 (gac gnus-article-current)
3456 (reffed gnus-reffed-article-number)
16409b0b 3457 (score-file gnus-current-score-file)
23f87bed
MB
3458 (default-charset gnus-newsgroup-charset)
3459 vlist)
3460 (let ((locals gnus-newsgroup-variables))
3461 (while locals
3462 (if (consp (car locals))
3463 (push (eval (caar locals)) vlist)
3464 (push (eval (car locals)) vlist))
3465 (setq locals (cdr locals)))
3466 (setq vlist (nreverse vlist)))
01c52d31 3467 (with-current-buffer gnus-group-buffer
6748645f
LMI
3468 (setq gnus-newsgroup-name name
3469 gnus-newsgroup-marked marked
23f87bed 3470 gnus-newsgroup-spam-marked spam
6748645f
LMI
3471 gnus-newsgroup-unreads unread
3472 gnus-current-headers headers
3473 gnus-newsgroup-data data
3474 gnus-article-current gac
3475 gnus-summary-buffer summary
3476 gnus-article-buffer article-buffer
3477 gnus-original-article-buffer original
3478 gnus-reffed-article-number reffed
16409b0b
GM
3479 gnus-current-score-file score-file
3480 gnus-newsgroup-charset default-charset)
23f87bed
MB
3481 (let ((locals gnus-newsgroup-variables))
3482 (while locals
3483 (if (consp (car locals))
3484 (set (caar locals) (pop vlist))
3485 (set (car locals) (pop vlist)))
3486 (setq locals (cdr locals))))
eec82323
LMI
3487 ;; The article buffer also has local variables.
3488 (when (gnus-buffer-live-p gnus-article-buffer)
3489 (set-buffer gnus-article-buffer)
3490 (setq gnus-summary-buffer summary))))))
3491
3492(defun gnus-summary-article-unread-p (article)
3493 "Say whether ARTICLE is unread or not."
3494 (memq article gnus-newsgroup-unreads))
3495
3496(defun gnus-summary-first-article-p (&optional article)
3497 "Return whether ARTICLE is the first article in the buffer."
3498 (if (not (setq article (or article (gnus-summary-article-number))))
3499 nil
3500 (eq article (caar gnus-newsgroup-data))))
3501
3502(defun gnus-summary-last-article-p (&optional article)
3503 "Return whether ARTICLE is the last article in the buffer."
3504 (if (not (setq article (or article (gnus-summary-article-number))))
16409b0b
GM
3505 ;; All non-existent numbers are the last article. :-)
3506 t
eec82323
LMI
3507 (not (cdr (gnus-data-find-list article)))))
3508
4921bbdd
CY
3509(defun gnus-make-thread-indent-array (&optional n)
3510 (when (or n
3511 (progn (setq n 200) nil)
3512 (null gnus-thread-indent-array)
3513 (/= gnus-thread-indent-level gnus-thread-indent-array-level))
3514 (setq gnus-thread-indent-array (make-vector (1+ n) "")
3515 gnus-thread-indent-array-level gnus-thread-indent-level)
3516 (while (>= n 0)
3517 (aset gnus-thread-indent-array n
6a30c01d 3518 (make-string (* n gnus-thread-indent-level) ? ))
4921bbdd 3519 (setq n (1- n)))))
eec82323
LMI
3520
3521(defun gnus-update-summary-mark-positions ()
3522 "Compute where the summary marks are to go."
3523 (save-excursion
6748645f 3524 (when (gnus-buffer-exists-p gnus-summary-buffer)
eec82323 3525 (set-buffer gnus-summary-buffer))
5153a47a
MB
3526 (let ((spec gnus-summary-line-format-spec)
3527 pos)
eec82323
LMI
3528 (save-excursion
3529 (gnus-set-work-buffer)
5153a47a
MB
3530 (let ((gnus-tmp-unread ?Z)
3531 (gnus-replied-mark ?Z)
3532 (gnus-score-below-mark ?Z)
3533 (gnus-score-over-mark ?Z)
3534 (gnus-undownloaded-mark ?Z)
3535 (gnus-summary-line-format-spec spec)
54506618 3536 (gnus-newsgroup-downloadable '(0))
5153a47a
MB
3537 (header [0 "" "" "05 Apr 2001 23:33:09 +0400" "" "" 0 0 "" nil])
3538 case-fold-search ignores)
3539 ;; Here, all marks are bound to Z.
3540 (gnus-summary-insert-line header
3541 0 nil t gnus-tmp-unread t nil "" nil 1)
3542 (goto-char (point-min))
3543 ;; Memorize the positions of the same characters as dummy marks.
3544 (while (re-search-forward "[A-D]" nil t)
3545 (push (point) ignores))
54506618 3546 (erase-buffer)
5153a47a
MB
3547 ;; We use A-D as dummy marks in order to know column positions
3548 ;; where marks should be inserted.
3549 (setq gnus-tmp-unread ?A
3550 gnus-replied-mark ?B
3551 gnus-score-below-mark ?C
3552 gnus-score-over-mark ?C
3553 gnus-undownloaded-mark ?D)
3554 (gnus-summary-insert-line header
3555 0 nil t gnus-tmp-unread t nil "" nil 1)
3556 ;; Ignore characters which aren't dummy marks.
3557 (dolist (p ignores)
3558 (delete-region (goto-char (1- p)) p)
3559 (insert ?Z))
eec82323 3560 (goto-char (point-min))
7c3bb5a5 3561 (setq pos (list (cons 'unread
5153a47a 3562 (and (search-forward "A" nil t)
7c3bb5a5 3563 (- (point) (point-min) 1)))))
eec82323 3564 (goto-char (point-min))
5153a47a 3565 (push (cons 'replied (and (search-forward "B" nil t)
667e0ba6 3566 (- (point) (point-min) 1)))
eec82323
LMI
3567 pos)
3568 (goto-char (point-min))
5153a47a 3569 (push (cons 'score (and (search-forward "C" nil t)
667e0ba6 3570 (- (point) (point-min) 1)))
6748645f
LMI
3571 pos)
3572 (goto-char (point-min))
5153a47a 3573 (push (cons 'download (and (search-forward "D" nil t)
7c3bb5a5 3574 (- (point) (point-min) 1)))
eec82323
LMI
3575 pos)))
3576 (setq gnus-summary-mark-positions pos))))
3577
3578(defun gnus-summary-insert-dummy-line (gnus-tmp-subject gnus-tmp-number)
3579 "Insert a dummy root in the summary buffer."
3580 (beginning-of-line)
3581 (gnus-add-text-properties
3582 (point) (progn (eval gnus-summary-dummy-line-format-spec) (point))
3583 (list 'gnus-number gnus-tmp-number 'gnus-intangible gnus-tmp-number)))
3584
23f87bed
MB
3585(defun gnus-summary-extract-address-component (from)
3586 (or (car (funcall gnus-extract-address-components from))
3587 from))
3588
3589(defun gnus-summary-from-or-to-or-newsgroups (header gnus-tmp-from)
3590 (let ((mail-parse-charset gnus-newsgroup-charset)
01c52d31 3591 (ignored-from-addresses (gnus-ignored-from-addresses))
23f87bed
MB
3592 ; Is it really necessary to do this next part for each summary line?
3593 ; Luckily, doesn't seem to slow things down much.
16409b0b 3594 (mail-parse-ignored-charsets
01c52d31
MB
3595 (with-current-buffer gnus-summary-buffer
3596 gnus-newsgroup-ignored-charsets)))
23f87bed 3597 (or
01c52d31
MB
3598 (and ignored-from-addresses
3599 (string-match ignored-from-addresses gnus-tmp-from)
23f87bed
MB
3600 (let ((extra-headers (mail-header-extra header))
3601 to
3602 newsgroups)
3603 (cond
3604 ((setq to (cdr (assq 'To extra-headers)))
01c52d31 3605 (concat gnus-summary-to-prefix
23f87bed
MB
3606 (inline
3607 (gnus-summary-extract-address-component
343d6628 3608 (funcall gnus-decode-encoded-address-function to)))))
01c52d31
MB
3609 ((setq newsgroups
3610 (or
3611 (cdr (assq 'Newsgroups extra-headers))
3612 (and
3613 (memq 'Newsgroups gnus-extra-headers)
3614 (eq (car (gnus-find-method-for-group
3615 gnus-newsgroup-name)) 'nntp)
3616 (gnus-group-real-name gnus-newsgroup-name))))
3617 (concat gnus-summary-newsgroup-prefix newsgroups)))))
23f87bed 3618 (inline (gnus-summary-extract-address-component gnus-tmp-from)))))
16409b0b 3619
eec82323
LMI
3620(defun gnus-summary-insert-line (gnus-tmp-header
3621 gnus-tmp-level gnus-tmp-current
23f87bed 3622 undownloaded gnus-tmp-unread gnus-tmp-replied
eec82323
LMI
3623 gnus-tmp-expirable gnus-tmp-subject-or-nil
3624 &optional gnus-tmp-dummy gnus-tmp-score
3625 gnus-tmp-process)
4921bbdd
CY
3626 (if (>= gnus-tmp-level (length gnus-thread-indent-array))
3627 (gnus-make-thread-indent-array (max (* 2 (length gnus-thread-indent-array))
3628 gnus-tmp-level)))
eec82323
LMI
3629 (let* ((gnus-tmp-indentation (aref gnus-thread-indent-array gnus-tmp-level))
3630 (gnus-tmp-lines (mail-header-lines gnus-tmp-header))
3631 (gnus-tmp-score (or gnus-tmp-score gnus-summary-default-score 0))
3632 (gnus-tmp-score-char
3633 (if (or (null gnus-summary-default-score)
3634 (<= (abs (- gnus-tmp-score gnus-summary-default-score))
3635 gnus-summary-zcore-fuzz))
23f87bed 3636 ? ;Whitespace
eec82323
LMI
3637 (if (< gnus-tmp-score gnus-summary-default-score)
3638 gnus-score-below-mark gnus-score-over-mark)))
23f87bed 3639 (gnus-tmp-number (mail-header-number gnus-tmp-header))
eec82323
LMI
3640 (gnus-tmp-replied
3641 (cond (gnus-tmp-process gnus-process-mark)
3642 ((memq gnus-tmp-current gnus-newsgroup-cached)
3643 gnus-cached-mark)
3644 (gnus-tmp-replied gnus-replied-mark)
23f87bed
MB
3645 ((memq gnus-tmp-current gnus-newsgroup-forwarded)
3646 gnus-forwarded-mark)
eec82323
LMI
3647 ((memq gnus-tmp-current gnus-newsgroup-saved)
3648 gnus-saved-mark)
23f87bed
MB
3649 ((memq gnus-tmp-number gnus-newsgroup-recent)
3650 gnus-recent-mark)
3651 ((memq gnus-tmp-number gnus-newsgroup-unseen)
3652 gnus-unseen-mark)
3653 (t gnus-no-mark)))
3654 (gnus-tmp-downloaded
3655 (cond (undownloaded
3656 gnus-undownloaded-mark)
3657 (gnus-newsgroup-agentized
3658 gnus-downloaded-mark)
3659 (t
3660 gnus-no-mark)))
eec82323
LMI
3661 (gnus-tmp-from (mail-header-from gnus-tmp-header))
3662 (gnus-tmp-name
3663 (cond
3664 ((string-match "<[^>]+> *$" gnus-tmp-from)
3665 (let ((beg (match-beginning 0)))
23f87bed
MB
3666 (or (and (string-match "^\".+\"" gnus-tmp-from)
3667 (substring gnus-tmp-from 1 (1- (match-end 0))))
eec82323
LMI
3668 (substring gnus-tmp-from 0 beg))))
3669 ((string-match "(.+)" gnus-tmp-from)
3670 (substring gnus-tmp-from
3671 (1+ (match-beginning 0)) (1- (match-end 0))))
3672 (t gnus-tmp-from)))
3673 (gnus-tmp-subject (mail-header-subject gnus-tmp-header))
eec82323
LMI
3674 (gnus-tmp-opening-bracket (if gnus-tmp-dummy ?\< ?\[))
3675 (gnus-tmp-closing-bracket (if gnus-tmp-dummy ?\> ?\]))
3676 (buffer-read-only nil))
3677 (when (string= gnus-tmp-name "")
3678 (setq gnus-tmp-name gnus-tmp-from))
3679 (unless (numberp gnus-tmp-lines)
23f87bed
MB
3680 (setq gnus-tmp-lines -1))
3681 (if (= gnus-tmp-lines -1)
3682 (setq gnus-tmp-lines "?")
3683 (setq gnus-tmp-lines (number-to-string gnus-tmp-lines)))
3684 (gnus-put-text-property
eec82323
LMI
3685 (point)
3686 (progn (eval gnus-summary-line-format-spec) (point))
23f87bed 3687 'gnus-number gnus-tmp-number)
eec82323
LMI
3688 (when (gnus-visual-p 'summary-highlight 'highlight)
3689 (forward-line -1)
6748645f 3690 (gnus-run-hooks 'gnus-summary-update-hook)
eec82323
LMI
3691 (forward-line 1))))
3692
3693(defun gnus-summary-update-line (&optional dont-update)
16409b0b 3694 "Update summary line after change."
eec82323
LMI
3695 (when (and gnus-summary-default-score
3696 (not gnus-summary-inhibit-highlight))
3697 (let* ((gnus-summary-inhibit-highlight t) ; Prevent recursion.
3698 (article (gnus-summary-article-number))
3699 (score (gnus-summary-article-score article)))
3700 (unless dont-update
3701 (if (and gnus-summary-mark-below
3702 (< (gnus-summary-article-score)
3703 gnus-summary-mark-below))
3704 ;; This article has a low score, so we mark it as read.
3705 (when (memq article gnus-newsgroup-unreads)
3706 (gnus-summary-mark-article-as-read gnus-low-score-mark))
3707 (when (eq (gnus-summary-article-mark) gnus-low-score-mark)
3708 ;; This article was previously marked as read on account
3709 ;; of a low score, but now it has risen, so we mark it as
3710 ;; unread.
3711 (gnus-summary-mark-article-as-unread gnus-unread-mark)))
3712 (gnus-summary-update-mark
3713 (if (or (null gnus-summary-default-score)
3714 (<= (abs (- score gnus-summary-default-score))
3715 gnus-summary-zcore-fuzz))
23f87bed 3716 ? ;Whitespace
eec82323
LMI
3717 (if (< score gnus-summary-default-score)
3718 gnus-score-below-mark gnus-score-over-mark))
3719 'score))
3720 ;; Do visual highlighting.
3721 (when (gnus-visual-p 'summary-highlight 'highlight)
6748645f 3722 (gnus-run-hooks 'gnus-summary-update-hook)))))
eec82323
LMI
3723
3724(defvar gnus-tmp-new-adopts nil)
3725
3726(defun gnus-summary-number-of-articles-in-thread (thread &optional level char)
3727 "Return the number of articles in THREAD.
3728This may be 0 in some cases -- if none of the articles in
3729the thread are to be displayed."
3730 (let* ((number
23f87bed 3731 ;; Fix by Luc Van Eycken <Luc.VanEycken@esat.kuleuven.ac.be>.
eec82323
LMI
3732 (cond
3733 ((not (listp thread))
3734 1)
3735 ((and (consp thread) (cdr thread))
3736 (apply
3737 '+ 1 (mapcar
3738 'gnus-summary-number-of-articles-in-thread (cdr thread))))
3739 ((null thread)
3740 1)
3741 ((memq (mail-header-number (car thread)) gnus-newsgroup-limit)
3742 1)
3743 (t 0))))
3744 (when (and level (zerop level) gnus-tmp-new-adopts)
3745 (incf number
3746 (apply '+ (mapcar
3747 'gnus-summary-number-of-articles-in-thread
3748 gnus-tmp-new-adopts))))
3749 (if char
3750 (if (> number 1) gnus-not-empty-thread-mark
3751 gnus-empty-thread-mark)
3752 number)))
3753
23f87bed
MB
3754(defsubst gnus-summary-line-message-size (head)
3755 "Return pretty-printed version of message size.
3756This function is intended to be used in
3757`gnus-summary-line-format-alist'."
3758 (let ((c (or (mail-header-chars head) -1)))
3759 (cond ((< c 0) "n/a") ; chars not available
3760 ((< c (* 1000 10)) (format "%1.1fk" (/ c 1024.0)))
3761 ((< c (* 1000 100)) (format "%dk" (/ c 1024.0)))
3762 ((< c (* 1000 10000)) (format "%1.1fM" (/ c (* 1024.0 1024))))
3763 (t (format "%dM" (/ c (* 1024.0 1024)))))))
3764
3765
eec82323
LMI
3766(defun gnus-summary-set-local-parameters (group)
3767 "Go through the local params of GROUP and set all variable specs in that list."
01c52d31
MB
3768 (let ((vars '(quit-config))) ; Ignore quit-config.
3769 (dolist (elem (gnus-group-find-parameter group))
eec82323
LMI
3770 (and (consp elem) ; Has to be a cons.
3771 (consp (cdr elem)) ; The cdr has to be a list.
3772 (symbolp (car elem)) ; Has to be a symbol in there.
23f87bed 3773 (not (memq (car elem) vars))
eec82323 3774 (ignore-errors ; So we set it.
23f87bed 3775 (push (car elem) vars)
eec82323
LMI
3776 (make-local-variable (car elem))
3777 (set (car elem) (eval (nth 1 elem))))))))
3778
3779(defun gnus-summary-read-group (group &optional show-all no-article
6748645f
LMI
3780 kill-buffer no-display backward
3781 select-articles)
eec82323
LMI
3782 "Start reading news in newsgroup GROUP.
3783If SHOW-ALL is non-nil, already read articles are also listed.
3784If NO-ARTICLE is non-nil, no article is selected initially.
3785If NO-DISPLAY, don't generate a summary buffer."
3786 (let (result)
3787 (while (and group
3788 (null (setq result
3789 (let ((gnus-auto-select-next nil))
6748645f
LMI
3790 (or (gnus-summary-read-group-1
3791 group show-all no-article
3792 kill-buffer no-display
3793 select-articles)
3794 (setq show-all nil
16409b0b 3795 select-articles nil)))))
eec82323
LMI
3796 (eq gnus-auto-select-next 'quietly))
3797 (set-buffer gnus-group-buffer)
6748645f
LMI
3798 ;; The entry function called above goes to the next
3799 ;; group automatically, so we go two groups back
3800 ;; if we are searching for the previous group.
3801 (when backward
3802 (gnus-group-prev-unread-group 2))
eec82323
LMI
3803 (if (not (equal group (gnus-group-group-name)))
3804 (setq group (gnus-group-group-name))
3805 (setq group nil)))
3806 result))
3807
3808(defun gnus-summary-read-group-1 (group show-all no-article
6748645f
LMI
3809 kill-buffer no-display
3810 &optional select-articles)
eec82323 3811 ;; Killed foreign groups can't be entered.
23f87bed
MB
3812 ;; (when (and (not (gnus-group-native-p group))
3813 ;; (not (gnus-gethash group gnus-newsrc-hashtb)))
3814 ;; (error "Dead non-native groups can't be entered"))
3815 (gnus-message 5 "Retrieving newsgroup: %s..."
3816 (gnus-group-decoded-name group))
eec82323
LMI
3817 (let* ((new-group (gnus-summary-setup-buffer group))
3818 (quit-config (gnus-group-quit-config group))
6748645f
LMI
3819 (did-select (and new-group (gnus-select-newsgroup
3820 group show-all select-articles))))
eec82323
LMI
3821 (cond
3822 ;; This summary buffer exists already, so we just select it.
3823 ((not new-group)
3824 (gnus-set-global-variables)
3825 (when kill-buffer
3826 (gnus-kill-or-deaden-summary kill-buffer))
3827 (gnus-configure-windows 'summary 'force)
3828 (gnus-set-mode-line 'summary)
3829 (gnus-summary-position-point)
3830 (message "")
3831 t)
3832 ;; We couldn't select this group.
3833 ((null did-select)
3834 (when (and (eq major-mode 'gnus-summary-mode)
3835 (not (equal (current-buffer) kill-buffer)))
3836 (kill-buffer (current-buffer))
3837 (if (not quit-config)
3838 (progn
6748645f
LMI
3839 ;; Update the info -- marks might need to be removed,
3840 ;; for instance.
3841 (gnus-summary-update-info)
eec82323
LMI
3842 (set-buffer gnus-group-buffer)
3843 (gnus-group-jump-to-group group)
3844 (gnus-group-next-unread-group 1))
3845 (gnus-handle-ephemeral-exit quit-config)))
23f87bed
MB
3846 (let ((grpinfo (gnus-get-info group)))
3847 (if (null (gnus-info-read grpinfo))
3848 (gnus-message 3 "Group %s contains no messages"
3849 (gnus-group-decoded-name group))
3850 (gnus-message 3 "Can't select group")))
eec82323
LMI
3851 nil)
3852 ;; The user did a `C-g' while prompting for number of articles,
3853 ;; so we exit this group.
3854 ((eq did-select 'quit)
3855 (and (eq major-mode 'gnus-summary-mode)
3856 (not (equal (current-buffer) kill-buffer))
3857 (kill-buffer (current-buffer)))
3858 (when kill-buffer
3859 (gnus-kill-or-deaden-summary kill-buffer))
3860 (if (not quit-config)
3861 (progn
3862 (set-buffer gnus-group-buffer)
3863 (gnus-group-jump-to-group group)
3864 (gnus-group-next-unread-group 1)
3865 (gnus-configure-windows 'group 'force))
3866 (gnus-handle-ephemeral-exit quit-config))
3867 ;; Finally signal the quit.
3868 (signal 'quit nil))
3869 ;; The group was successfully selected.
3870 (t
3871 (gnus-set-global-variables)
3872 ;; Save the active value in effect when the group was entered.
3873 (setq gnus-newsgroup-active
3874 (gnus-copy-sequence
3875 (gnus-active gnus-newsgroup-name)))
3876 ;; You can change the summary buffer in some way with this hook.
6748645f 3877 (gnus-run-hooks 'gnus-select-group-hook)
5153a47a
MB
3878 (when (memq 'summary (gnus-update-format-specifications
3879 nil 'summary 'summary-mode 'summary-dummy))
3880 ;; The format specification for the summary line was updated,
3881 ;; so we need to update the mark positions as well.
3882 (gnus-update-summary-mark-positions))
eec82323
LMI
3883 ;; Do score processing.
3884 (when gnus-use-scoring
3885 (gnus-possibly-score-headers))
3886 ;; Check whether to fill in the gaps in the threads.
3887 (when gnus-build-sparse-threads
3888 (gnus-build-sparse-threads))
3889 ;; Find the initial limit.
26c9afc3
MB
3890 (if show-all
3891 (let ((gnus-newsgroup-dormant nil))
eec82323 3892 (gnus-summary-initial-limit show-all))
26c9afc3 3893 (gnus-summary-initial-limit show-all))
eec82323
LMI
3894 ;; Generate the summary buffer.
3895 (unless no-display
3896 (gnus-summary-prepare))
3897 (when gnus-use-trees
3898 (gnus-tree-open group)
3899 (setq gnus-summary-highlight-line-function
3900 'gnus-tree-highlight-article))
3901 ;; If the summary buffer is empty, but there are some low-scored
3902 ;; articles or some excluded dormants, we include these in the
3903 ;; buffer.
3904 (when (and (zerop (buffer-size))
3905 (not no-display))
3906 (cond (gnus-newsgroup-dormant
3907 (gnus-summary-limit-include-dormant))
3908 ((and gnus-newsgroup-scored show-all)
3909 (gnus-summary-limit-include-expunged t))))
3910 ;; Function `gnus-apply-kill-file' must be called in this hook.
6748645f 3911 (gnus-run-hooks 'gnus-apply-kill-hook)
eec82323
LMI
3912 (if (and (zerop (buffer-size))
3913 (not no-display))
3914 (progn
3915 ;; This newsgroup is empty.
3916 (gnus-summary-catchup-and-exit nil t)
3917 (gnus-message 6 "No unread news")
3918 (when kill-buffer
3919 (gnus-kill-or-deaden-summary kill-buffer))
3920 ;; Return nil from this function.
3921 nil)
3922 ;; Hide conversation thread subtrees. We cannot do this in
3923 ;; gnus-summary-prepare-hook since kill processing may not
3924 ;; work with hidden articles.
23f87bed 3925 (gnus-summary-maybe-hide-threads)
6748645f
LMI
3926 (when kill-buffer
3927 (gnus-kill-or-deaden-summary kill-buffer))
23f87bed 3928 (gnus-summary-auto-select-subject)
eec82323
LMI
3929 ;; Show first unread article if requested.
3930 (if (and (not no-article)
3931 (not no-display)
3932 gnus-newsgroup-unreads
3933 gnus-auto-select-first)
16409b0b
GM
3934 (progn
3935 (gnus-configure-windows 'summary)
23f87bed
MB
3936 (let ((art (gnus-summary-article-number)))
3937 (unless (and (not gnus-plugged)
3938 (or (memq art gnus-newsgroup-undownloaded)
3939 (memq art gnus-newsgroup-downloadable)))
3940 (gnus-summary-goto-article art))))
3941 ;; Don't select any articles.
eec82323 3942 (gnus-summary-position-point)
6748645f
LMI
3943 (gnus-configure-windows 'summary 'force)
3944 (gnus-set-mode-line 'summary))
23f87bed
MB
3945 (when (and gnus-auto-center-group
3946 (get-buffer-window gnus-group-buffer t))
eec82323
LMI
3947 ;; Gotta use windows, because recenter does weird stuff if
3948 ;; the current buffer ain't the displayed window.
3949 (let ((owin (selected-window)))
3950 (select-window (get-buffer-window gnus-group-buffer t))
3951 (when (gnus-group-goto-group group)
3952 (recenter))
3953 (select-window owin)))
3954 ;; Mark this buffer as "prepared".
3955 (setq gnus-newsgroup-prepared t)
6748645f 3956 (gnus-run-hooks 'gnus-summary-prepared-hook)
23f87bed
MB
3957 (unless (gnus-ephemeral-group-p group)
3958 (gnus-group-update-group group))
eec82323
LMI
3959 t)))))
3960
23f87bed
MB
3961(defun gnus-summary-auto-select-subject ()
3962 "Select the subject line on initial group entry."
3963 (goto-char (point-min))
3964 (cond
3965 ((eq gnus-auto-select-subject 'best)
3966 (gnus-summary-best-unread-subject))
3967 ((eq gnus-auto-select-subject 'unread)
3968 (gnus-summary-first-unread-subject))
3969 ((eq gnus-auto-select-subject 'unseen)
3970 (gnus-summary-first-unseen-subject))
3971 ((eq gnus-auto-select-subject 'unseen-or-unread)
3972 (gnus-summary-first-unseen-or-unread-subject))
3973 ((eq gnus-auto-select-subject 'first)
3974 ;; Do nothing.
3975 )
3976 ((functionp gnus-auto-select-subject)
3977 (funcall gnus-auto-select-subject))))
3978
eec82323
LMI
3979(defun gnus-summary-prepare ()
3980 "Generate the summary buffer."
3981 (interactive)
3982 (let ((buffer-read-only nil))
3983 (erase-buffer)
3984 (setq gnus-newsgroup-data nil
3985 gnus-newsgroup-data-reverse nil)
6748645f 3986 (gnus-run-hooks 'gnus-summary-generate-hook)
eec82323
LMI
3987 ;; Generate the buffer, either with threads or without.
3988 (when gnus-newsgroup-headers
3989 (gnus-summary-prepare-threads
3990 (if gnus-show-threads
3991 (gnus-sort-gathered-threads
3992 (funcall gnus-summary-thread-gathering-function
3993 (gnus-sort-threads
3994 (gnus-cut-threads (gnus-make-threads)))))
3995 ;; Unthreaded display.
3996 (gnus-sort-articles gnus-newsgroup-headers))))
3997 (setq gnus-newsgroup-data (nreverse gnus-newsgroup-data))
3998 ;; Call hooks for modifying summary buffer.
3999 (goto-char (point-min))
6748645f 4000 (gnus-run-hooks 'gnus-summary-prepare-hook)))
eec82323
LMI
4001
4002(defsubst gnus-general-simplify-subject (subject)
23f87bed 4003 "Simplify subject by the same rules as `gnus-gather-threads-by-subject'."
eec82323
LMI
4004 (setq subject
4005 (cond
4006 ;; Truncate the subject.
6748645f
LMI
4007 (gnus-simplify-subject-functions
4008 (gnus-map-function gnus-simplify-subject-functions subject))
eec82323
LMI
4009 ((numberp gnus-summary-gather-subject-limit)
4010 (setq subject (gnus-simplify-subject-re subject))
4011 (if (> (length subject) gnus-summary-gather-subject-limit)
4012 (substring subject 0 gnus-summary-gather-subject-limit)
4013 subject))
4014 ;; Fuzzily simplify it.
4015 ((eq 'fuzzy gnus-summary-gather-subject-limit)
4016 (gnus-simplify-subject-fuzzy subject))
4017 ;; Just remove the leading "Re:".
4018 (t
4019 (gnus-simplify-subject-re subject))))
4020
4021 (if (and gnus-summary-gather-exclude-subject
4022 (string-match gnus-summary-gather-exclude-subject subject))
23f87bed 4023 nil ; This article shouldn't be gathered
eec82323
LMI
4024 subject))
4025
4026(defun gnus-summary-simplify-subject-query ()
4027 "Query where the respool algorithm would put this article."
4028 (interactive)
eec82323
LMI
4029 (gnus-summary-select-article)
4030 (message (gnus-general-simplify-subject (gnus-summary-article-subject))))
4031
4032(defun gnus-gather-threads-by-subject (threads)
4033 "Gather threads by looking at Subject headers."
4034 (if (not gnus-summary-make-false-root)
4035 threads
4036 (let ((hashtb (gnus-make-hashtable 1024))
4037 (prev threads)
4038 (result threads)
4039 subject hthread whole-subject)
4040 (while threads
4041 (setq subject (gnus-general-simplify-subject
4042 (setq whole-subject (mail-header-subject
4043 (caar threads)))))
4044 (when subject
4045 (if (setq hthread (gnus-gethash subject hashtb))
4046 (progn
4047 ;; We enter a dummy root into the thread, if we
4048 ;; haven't done that already.
4049 (unless (stringp (caar hthread))
4050 (setcar hthread (list whole-subject (car hthread))))
4051 ;; We add this new gathered thread to this gathered
4052 ;; thread.
4053 (setcdr (car hthread)
4054 (nconc (cdar hthread) (list (car threads))))
4055 ;; Remove it from the list of threads.
4056 (setcdr prev (cdr threads))
4057 (setq threads prev))
4058 ;; Enter this thread into the hash table.
23f87bed
MB
4059 (gnus-sethash subject
4060 (if gnus-summary-make-false-root-always
4061 (progn
4062 ;; If you want a dummy root above all
4063 ;; threads...
4064 (setcar threads (list whole-subject
4065 (car threads)))
4066 threads)
4067 threads)
4068 hashtb)))
eec82323
LMI
4069 (setq prev threads)
4070 (setq threads (cdr threads)))
4071 result)))
4072
4073(defun gnus-gather-threads-by-references (threads)
4074 "Gather threads by looking at References headers."
4075 (let ((idhashtb (gnus-make-hashtable 1024))
4076 (thhashtb (gnus-make-hashtable 1024))
4077 (prev threads)
4078 (result threads)
4079 ids references id gthread gid entered ref)
4080 (while threads
4081 (when (setq references (mail-header-references (caar threads)))
4082 (setq id (mail-header-id (caar threads))
23f87bed 4083 ids (inline (gnus-split-references references))
eec82323
LMI
4084 entered nil)
4085 (while (setq ref (pop ids))
4086 (setq ids (delete ref ids))
4087 (if (not (setq gid (gnus-gethash ref idhashtb)))
4088 (progn
4089 (gnus-sethash ref id idhashtb)
4090 (gnus-sethash id threads thhashtb))
4091 (setq gthread (gnus-gethash gid thhashtb))
4092 (unless entered
4093 ;; We enter a dummy root into the thread, if we
4094 ;; haven't done that already.
4095 (unless (stringp (caar gthread))
4096 (setcar gthread (list (mail-header-subject (caar gthread))
4097 (car gthread))))
4098 ;; We add this new gathered thread to this gathered
4099 ;; thread.
4100 (setcdr (car gthread)
4101 (nconc (cdar gthread) (list (car threads)))))
4102 ;; Add it into the thread hash table.
4103 (gnus-sethash id gthread thhashtb)
4104 (setq entered t)
4105 ;; Remove it from the list of threads.
4106 (setcdr prev (cdr threads))
4107 (setq threads prev))))
4108 (setq prev threads)
4109 (setq threads (cdr threads)))
4110 result))
4111
4112(defun gnus-sort-gathered-threads (threads)
16409b0b 4113 "Sort subtreads inside each gathered thread by `gnus-sort-gathered-threads-function'."
eec82323
LMI
4114 (let ((result threads))
4115 (while threads
4116 (when (stringp (caar threads))
4117 (setcdr (car threads)
16409b0b 4118 (sort (cdar threads) gnus-sort-gathered-threads-function)))
eec82323
LMI
4119 (setq threads (cdr threads)))
4120 result))
4121
4122(defun gnus-thread-loop-p (root thread)
4123 "Say whether ROOT is in THREAD."
4124 (let ((stack (list thread))
4125 (infloop 0)
4126 th)
4127 (while (setq thread (pop stack))
4128 (setq th (cdr thread))
4129 (while (and th
4130 (not (eq (caar th) root)))
4131 (pop th))
4132 (if th
4133 ;; We have found a loop.
4134 (let (ref-dep)
4135 (setcdr thread (delq (car th) (cdr thread)))
4136 (if (boundp (setq ref-dep (intern "none"
4137 gnus-newsgroup-dependencies)))
4138 (setcdr (symbol-value ref-dep)
4139 (nconc (cdr (symbol-value ref-dep))
4140 (list (car th))))
4141 (set ref-dep (list nil (car th))))
4142 (setq infloop 1
4143 stack nil))
4144 ;; Push all the subthreads onto the stack.
4145 (push (cdr thread) stack)))
4146 infloop))
4147
4148(defun gnus-make-threads ()
01ccbb85 4149 "Go through the dependency hashtb and find the roots. Return all threads."
eec82323
LMI
4150 (let (threads)
4151 (while (catch 'infloop
4152 (mapatoms
4153 (lambda (refs)
4154 ;; Deal with self-referencing References loops.
4155 (when (and (car (symbol-value refs))
4156 (not (zerop
4157 (apply
4158 '+
4159 (mapcar
4160 (lambda (thread)
4161 (gnus-thread-loop-p
4162 (car (symbol-value refs)) thread))
4163 (cdr (symbol-value refs)))))))
4164 (setq threads nil)
4165 (throw 'infloop t))
4166 (unless (car (symbol-value refs))
23f87bed
MB
4167 ;; These threads do not refer back to any other
4168 ;; articles, so they're roots.
eec82323
LMI
4169 (setq threads (append (cdr (symbol-value refs)) threads))))
4170 gnus-newsgroup-dependencies)))
4171 threads))
4172
6748645f 4173;; Build the thread tree.
16409b0b 4174(defsubst gnus-dependencies-add-header (header dependencies force-new)
6748645f
LMI
4175 "Enter HEADER into the DEPENDENCIES table if it is not already there.
4176
4177If FORCE-NEW is not nil, enter HEADER into the DEPENDENCIES table even
4178if it was already present.
4179
4180If `gnus-summary-ignore-duplicates' is nil then duplicate Message-IDs
4181will not be entered in the DEPENDENCIES table. Otherwise duplicate
23f87bed
MB
4182Message-IDs will be renamed to a unique Message-ID before being
4183entered.
6748645f
LMI
4184
4185Returns HEADER if it was entered in the DEPENDENCIES. Returns nil otherwise."
4186 (let* ((id (mail-header-id header))
4187 (id-dep (and id (intern id dependencies)))
23f87bed 4188 parent-id ref ref-dep ref-header replaced)
6748645f
LMI
4189 ;; Enter this `header' in the `dependencies' table.
4190 (cond
4191 ((not id-dep)
4192 (setq header nil))
4193 ;; The first two cases do the normal part: enter a new `header'
4194 ;; in the `dependencies' table.
4195 ((not (boundp id-dep))
4196 (set id-dep (list header)))
4197 ((null (car (symbol-value id-dep)))
4198 (setcar (symbol-value id-dep) header))
4199
4200 ;; From here the `header' was already present in the
4201 ;; `dependencies' table.
4202 (force-new
4203 ;; Overrides an existing entry;
4204 ;; just set the header part of the entry.
23f87bed
MB
4205 (setcar (symbol-value id-dep) header)
4206 (setq replaced t))
6748645f
LMI
4207
4208 ;; Renames the existing `header' to a unique Message-ID.
4209 ((not gnus-summary-ignore-duplicates)
4210 ;; An article with this Message-ID has already been seen.
4211 ;; We rename the Message-ID.
4212 (set (setq id-dep (intern (setq id (nnmail-message-id)) dependencies))
4213 (list header))
4214 (mail-header-set-id header id))
4215
4216 ;; The last case ignores an existing entry, except it adds any
4217 ;; additional Xrefs (in case the two articles came from different
4218 ;; servers.
4219 ;; Also sets `header' to `nil' meaning that the `dependencies'
4220 ;; table was *not* modified.
4221 (t
4222 (mail-header-set-xref
4223 (car (symbol-value id-dep))
4224 (concat (or (mail-header-xref (car (symbol-value id-dep)))
4225 "")
4226 (or (mail-header-xref header) "")))
4227 (setq header nil)))
4228
23f87bed
MB
4229 (when (and header (not replaced))
4230 ;; First check that we are not creating a References loop.
4231 (setq parent-id (gnus-parent-id (mail-header-references header)))
4232 (setq ref parent-id)
6748645f
LMI
4233 (while (and ref
4234 (setq ref-dep (intern-soft ref dependencies))
4235 (boundp ref-dep)
4236 (setq ref-header (car (symbol-value ref-dep))))
4237 (if (string= id ref)
4238 ;; Yuk! This is a reference loop. Make the article be a
4239 ;; root article.
4240 (progn
4241 (mail-header-set-references (car (symbol-value id-dep)) "none")
23f87bed
MB
4242 (setq ref nil)
4243 (setq parent-id nil))
6748645f 4244 (setq ref (gnus-parent-id (mail-header-references ref-header)))))
23f87bed 4245 (setq ref-dep (intern (or parent-id "none") dependencies))
6748645f
LMI
4246 (if (boundp ref-dep)
4247 (setcdr (symbol-value ref-dep)
4248 (nconc (cdr (symbol-value ref-dep))
4249 (list (symbol-value id-dep))))
4250 (set ref-dep (list nil (symbol-value id-dep)))))
4251 header))
4252
23f87bed
MB
4253(defun gnus-extract-message-id-from-in-reply-to (string)
4254 (if (string-match "<[^>]+>" string)
4255 (substring string (match-beginning 0) (match-end 0))
4256 nil))
4257
eec82323
LMI
4258(defun gnus-build-sparse-threads ()
4259 (let ((headers gnus-newsgroup-headers)
16409b0b 4260 (mail-parse-charset gnus-newsgroup-charset)
6748645f 4261 (gnus-summary-ignore-duplicates t)
eec82323 4262 header references generation relations
6748645f 4263 subject child end new-child date)
eec82323
LMI
4264 ;; First we create an alist of generations/relations, where
4265 ;; generations is how much we trust the relation, and the relation
4266 ;; is parent/child.
4267 (gnus-message 7 "Making sparse threads...")
4268 (save-excursion
4269 (nnheader-set-temp-buffer " *gnus sparse threads*")
4270 (while (setq header (pop headers))
4271 (when (and (setq references (mail-header-references header))
4272 (not (string= references "")))
4273 (insert references)
4274 (setq child (mail-header-id header)
6748645f
LMI
4275 subject (mail-header-subject header)
4276 date (mail-header-date header)
4277 generation 0)
eec82323
LMI
4278 (while (search-backward ">" nil t)
4279 (setq end (1+ (point)))
4280 (when (search-backward "<" nil t)
6748645f 4281 (setq new-child (buffer-substring (point) end))
eec82323 4282 (push (list (incf generation)
6748645f
LMI
4283 child (setq child new-child)
4284 subject date)
eec82323 4285 relations)))
6748645f
LMI
4286 (when child
4287 (push (list (1+ generation) child nil subject) relations))
eec82323
LMI
4288 (erase-buffer)))
4289 (kill-buffer (current-buffer)))
4290 ;; Sort over trustworthiness.
01c52d31
MB
4291 (dolist (relation (sort relations 'car-less-than-car))
4292 (when (gnus-dependencies-add-header
4293 (make-full-mail-header
4294 gnus-reffed-article-number
4295 (nth 3 relation) "" (or (nth 4 relation) "")
4296 (nth 1 relation)
4297 (or (nth 2 relation) "") 0 0 "")
4298 gnus-newsgroup-dependencies nil)
4299 (push gnus-reffed-article-number gnus-newsgroup-limit)
4300 (push gnus-reffed-article-number gnus-newsgroup-sparse)
4301 (push (cons gnus-reffed-article-number gnus-sparse-mark)
4302 gnus-newsgroup-reads)
4303 (decf gnus-reffed-article-number)))
eec82323
LMI
4304 (gnus-message 7 "Making sparse threads...done")))
4305
4306(defun gnus-build-old-threads ()
4307 ;; Look at all the articles that refer back to old articles, and
4308 ;; fetch the headers for the articles that aren't there. This will
4309 ;; build complete threads - if the roots haven't been expired by the
4310 ;; server, that is.
16409b0b
GM
4311 (let ((mail-parse-charset gnus-newsgroup-charset)
4312 id heads)
eec82323
LMI
4313 (mapatoms
4314 (lambda (refs)
4315 (when (not (car (symbol-value refs)))
4316 (setq heads (cdr (symbol-value refs)))
4317 (while heads
4318 (if (memq (mail-header-number (caar heads))
4319 gnus-newsgroup-dormant)
4320 (setq heads (cdr heads))
4321 (setq id (symbol-name refs))
4322 (while (and (setq id (gnus-build-get-header id))
6748645f 4323 (not (car (gnus-id-to-thread id)))))
eec82323
LMI
4324 (setq heads nil)))))
4325 gnus-newsgroup-dependencies)))
4326
23f87bed
MB
4327(defsubst gnus-remove-odd-characters (string)
4328 "Translate STRING into something that doesn't contain weird characters."
4329 (mm-subst-char-in-string
4330 ?\r ?\-
01c52d31 4331 (mm-subst-char-in-string ?\n ?\- string t) t))
23f87bed 4332
6748645f
LMI
4333;; This function has to be called with point after the article number
4334;; on the beginning of the line.
4335(defsubst gnus-nov-parse-line (number dependencies &optional force-new)
01c52d31 4336 (let ((eol (point-at-eol))
6748645f 4337 (buffer (current-buffer))
23f87bed 4338 header references in-reply-to)
6748645f
LMI
4339
4340 ;; overview: [num subject from date id refs chars lines misc]
4341 (unwind-protect
23f87bed 4342 (let (x)
6748645f
LMI
4343 (narrow-to-region (point) eol)
4344 (unless (eobp)
4345 (forward-char))
4346
4347 (setq header
4348 (make-full-mail-header
4349 number ; number
23f87bed
MB
4350 (condition-case () ; subject
4351 (gnus-remove-odd-characters
4352 (funcall gnus-decode-encoded-word-function
4353 (setq x (nnheader-nov-field))))
4354 (error x))
4355 (condition-case () ; from
4356 (gnus-remove-odd-characters
343d6628 4357 (funcall gnus-decode-encoded-address-function
23f87bed
MB
4358 (setq x (nnheader-nov-field))))
4359 (error x))
16409b0b 4360 (nnheader-nov-field) ; date
01c52d31 4361 (nnheader-nov-read-message-id number) ; id
23f87bed 4362 (setq references (nnheader-nov-field)) ; refs
16409b0b
GM
4363 (nnheader-nov-read-integer) ; chars
4364 (nnheader-nov-read-integer) ; lines
4365 (unless (eobp)
8b93df01
DL
4366 (if (looking-at "Xref: ")
4367 (goto-char (match-end 0)))
4368 (nnheader-nov-field)) ; Xref
16409b0b 4369 (nnheader-nov-parse-extra)))) ; extra
6748645f
LMI
4370
4371 (widen))
4372
23f87bed
MB
4373 (when (and (string= references "")
4374 (setq in-reply-to (mail-header-extra header))
4375 (setq in-reply-to (cdr (assq 'In-Reply-To in-reply-to))))
4376 (mail-header-set-references
4377 header (gnus-extract-message-id-from-in-reply-to in-reply-to)))
4378
6748645f
LMI
4379 (when gnus-alter-header-function
4380 (funcall gnus-alter-header-function header))
4381 (gnus-dependencies-add-header header dependencies force-new)))
4382
eec82323 4383(defun gnus-build-get-header (id)
16409b0b
GM
4384 "Look through the buffer of NOV lines and find the header to ID.
4385Enter this line into the dependencies hash table, and return
4386the id of the parent article (if any)."
eec82323
LMI
4387 (let ((deps gnus-newsgroup-dependencies)
4388 found header)
4389 (prog1
4390 (save-excursion
4391 (set-buffer nntp-server-buffer)
4392 (let ((case-fold-search nil))
4393 (goto-char (point-min))
4394 (while (and (not found)
4395 (search-forward id nil t))
4396 (beginning-of-line)
4397 (setq found (looking-at
4398 (format "^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t%s"
4399 (regexp-quote id))))
4400 (or found (beginning-of-line 2)))
4401 (when found
4402 (beginning-of-line)
4403 (and
4404 (setq header (gnus-nov-parse-line
4405 (read (current-buffer)) deps))
4406 (gnus-parent-id (mail-header-references header))))))
4407 (when header
4408 (let ((number (mail-header-number header)))
4409 (push number gnus-newsgroup-limit)
4410 (push header gnus-newsgroup-headers)
4411 (if (memq number gnus-newsgroup-unselected)
4412 (progn
23f87bed
MB
4413 (setq gnus-newsgroup-unreads
4414 (gnus-add-to-sorted-list gnus-newsgroup-unreads
4415 number))
eec82323
LMI
4416 (setq gnus-newsgroup-unselected
4417 (delq number gnus-newsgroup-unselected)))
4418 (push number gnus-newsgroup-ancient)))))))
4419
6748645f
LMI
4420(defun gnus-build-all-threads ()
4421 "Read all the headers."
4422 (let ((gnus-summary-ignore-duplicates t)
16409b0b 4423 (mail-parse-charset gnus-newsgroup-charset)
6748645f
LMI
4424 (dependencies gnus-newsgroup-dependencies)
4425 header article)
4426 (save-excursion
4427 (set-buffer nntp-server-buffer)
4428 (let ((case-fold-search nil))
4429 (goto-char (point-min))
4430 (while (not (eobp))
4431 (ignore-errors
4432 (setq article (read (current-buffer))
16409b0b 4433 header (gnus-nov-parse-line article dependencies)))
6748645f 4434 (when header
01c52d31 4435 (with-current-buffer gnus-summary-buffer
6748645f
LMI
4436 (push header gnus-newsgroup-headers)
4437 (if (memq (setq article (mail-header-number header))
4438 gnus-newsgroup-unselected)
4439 (progn
23f87bed
MB
4440 (setq gnus-newsgroup-unreads
4441 (gnus-add-to-sorted-list
4442 gnus-newsgroup-unreads article))
6748645f
LMI
4443 (setq gnus-newsgroup-unselected
4444 (delq article gnus-newsgroup-unselected)))
4445 (push article gnus-newsgroup-ancient)))
4446 (forward-line 1)))))))
4447
eec82323 4448(defun gnus-summary-update-article-line (article header)
23f87bed 4449 "Update the line for ARTICLE using HEADER."
eec82323
LMI
4450 (let* ((id (mail-header-id header))
4451 (thread (gnus-id-to-thread id)))
4452 (unless thread
4453 (error "Article in no thread"))
4454 ;; Update the thread.
4455 (setcar thread header)
4456 (gnus-summary-goto-subject article)
4457 (let* ((datal (gnus-data-find-list article))
4458 (data (car datal))
eec82323
LMI
4459 (buffer-read-only nil)
4460 (level (gnus-summary-thread-level)))
4461 (gnus-delete-line)
23f87bed
MB
4462 (let ((inserted (- (point)
4463 (progn
4464 (gnus-summary-insert-line
4465 header level nil
4466 (memq article gnus-newsgroup-undownloaded)
4467 (gnus-article-mark article)
4468 (memq article gnus-newsgroup-replied)
4469 (memq article gnus-newsgroup-expirable)
4470 ;; Only insert the Subject string when it's different
4471 ;; from the previous Subject string.
4472 (if (and
4473 gnus-show-threads
4474 (gnus-subject-equal
4475 (condition-case ()
4476 (mail-header-subject
4477 (gnus-data-header
4478 (cadr
4479 (gnus-data-find-list
4480 article
4481 (gnus-data-list t)))))
4482 ;; Error on the side of excessive subjects.
4483 (error ""))
4484 (mail-header-subject header)))
4485 ""
4486 (mail-header-subject header))
4487 nil (cdr (assq article gnus-newsgroup-scored))
4488 (memq article gnus-newsgroup-processable))
4489 (point)))))
4490 (when (cdr datal)
4491 (gnus-data-update-list
4492 (cdr datal)
4493 (- (gnus-data-pos data) (gnus-data-pos (cadr datal)) inserted)))))))
eec82323
LMI
4494
4495(defun gnus-summary-update-article (article &optional iheader)
4496 "Update ARTICLE in the summary buffer."
4497 (set-buffer gnus-summary-buffer)
6748645f 4498 (let* ((header (gnus-summary-article-header article))
eec82323
LMI
4499 (id (mail-header-id header))
4500 (data (gnus-data-find article))
4501 (thread (gnus-id-to-thread id))
4502 (references (mail-header-references header))
4503 (parent
4504 (gnus-id-to-thread
4505 (or (gnus-parent-id
4506 (when (and references
4507 (not (equal "" references)))
4508 references))
4509 "none")))
4510 (buffer-read-only nil)
6748645f 4511 (old (car thread)))
eec82323 4512 (when thread
eec82323 4513 (unless iheader
6748645f
LMI
4514 (setcar thread nil)
4515 (when parent
4516 (delq thread parent)))
4517 (if (gnus-summary-insert-subject id header)
eec82323
LMI
4518 ;; Set the (possibly) new article number in the data structure.
4519 (gnus-data-set-number data (gnus-id-to-article id))
4520 (setcar thread old)
4521 nil))))
4522
6748645f
LMI
4523(defun gnus-rebuild-thread (id &optional line)
4524 "Rebuild the thread containing ID.
4525If LINE, insert the rebuilt thread starting on line LINE."
eec82323
LMI
4526 (let ((buffer-read-only nil)
4527 old-pos current thread data)
4528 (if (not gnus-show-threads)
4529 (setq thread (list (car (gnus-id-to-thread id))))
4530 ;; Get the thread this article is part of.
4531 (setq thread (gnus-remove-thread id)))
01c52d31 4532 (setq old-pos (point-at-bol))
eec82323 4533 (setq current (save-excursion
94384150 4534 (and (re-search-backward "[\r\n]" nil t)
eec82323
LMI
4535 (gnus-summary-article-number))))
4536 ;; If this is a gathered thread, we have to go some re-gathering.
4537 (when (stringp (car thread))
4538 (let ((subject (car thread))
4539 roots thr)
4540 (setq thread (cdr thread))
4541 (while thread
4542 (unless (memq (setq thr (gnus-id-to-thread
4543 (gnus-root-id
4544 (mail-header-id (caar thread)))))
4545 roots)
4546 (push thr roots))
4547 (setq thread (cdr thread)))
4548 ;; We now have all (unique) roots.
4549 (if (= (length roots) 1)
4550 ;; All the loose roots are now one solid root.
4551 (setq thread (car roots))
4552 (setq thread (cons subject (gnus-sort-threads roots))))))
4553 (let (threads)
4554 ;; We then insert this thread into the summary buffer.
6748645f
LMI
4555 (when line
4556 (goto-char (point-min))
4557 (forward-line (1- line)))
eec82323
LMI
4558 (let (gnus-newsgroup-data gnus-newsgroup-threads)
4559 (if gnus-show-threads
4560 (gnus-summary-prepare-threads (gnus-cut-threads (list thread)))
4561 (gnus-summary-prepare-unthreaded thread))
4562 (setq data (nreverse gnus-newsgroup-data))
4563 (setq threads gnus-newsgroup-threads))
4564 ;; We splice the new data into the data structure.
6748645f
LMI
4565 ;;!!! This is kinda bogus. We assume that in LINE is non-nil,
4566 ;;!!! then we want to insert at the beginning of the buffer.
4567 ;;!!! That happens to be true with Gnus now, but that may
4568 ;;!!! change in the future. Perhaps.
4569 (gnus-data-enter-list
4570 (if line nil current) data (- (point) old-pos))
4571 (setq gnus-newsgroup-threads
4572 (nconc threads gnus-newsgroup-threads))
4573 (gnus-data-compute-positions))))
eec82323
LMI
4574
4575(defun gnus-number-to-header (number)
4576 "Return the header for article NUMBER."
4577 (let ((headers gnus-newsgroup-headers))
4578 (while (and headers
4579 (not (= number (mail-header-number (car headers)))))
4580 (pop headers))
4581 (when headers
4582 (car headers))))
4583
6748645f 4584(defun gnus-parent-headers (in-headers &optional generation)
eec82323
LMI
4585 "Return the headers of the GENERATIONeth parent of HEADERS."
4586 (unless generation
4587 (setq generation 1))
a8151ef7 4588 (let ((parent t)
6748645f 4589 (headers in-headers)
a8151ef7 4590 references)
6748645f
LMI
4591 (while (and parent
4592 (not (zerop generation))
4593 (setq references (mail-header-references headers)))
4594 (setq headers (if (and references
4595 (setq parent (gnus-parent-id references)))
4596 (car (gnus-id-to-thread parent))
4597 nil))
4598 (decf generation))
4599 (and (not (eq headers in-headers))
4600 headers)))
eec82323
LMI
4601
4602(defun gnus-id-to-thread (id)
4603 "Return the (sub-)thread where ID appears."
4604 (gnus-gethash id gnus-newsgroup-dependencies))
4605
4606(defun gnus-id-to-article (id)
4607 "Return the article number of ID."
4608 (let ((thread (gnus-id-to-thread id)))
4609 (when (and thread
4610 (car thread))
4611 (mail-header-number (car thread)))))
4612
4613(defun gnus-id-to-header (id)
4614 "Return the article headers of ID."
4615 (car (gnus-id-to-thread id)))
4616
4617(defun gnus-article-displayed-root-p (article)
4618 "Say whether ARTICLE is a root(ish) article."
4619 (let ((level (gnus-summary-thread-level article))
4620 (refs (mail-header-references (gnus-summary-article-header article)))
4621 particle)
4622 (cond
4623 ((null level) nil)
4624 ((zerop level) t)
4625 ((null refs) t)
4626 ((null (gnus-parent-id refs)) t)
4627 ((and (= 1 level)
4628 (null (setq particle (gnus-id-to-article
4629 (gnus-parent-id refs))))
4630 (null (gnus-summary-thread-level particle)))))))
4631
4632(defun gnus-root-id (id)
4633 "Return the id of the root of the thread where ID appears."
4634 (let (last-id prev)
6748645f 4635 (while (and id (setq prev (car (gnus-id-to-thread id))))
eec82323
LMI
4636 (setq last-id id
4637 id (gnus-parent-id (mail-header-references prev))))
4638 last-id))
4639
6748645f
LMI
4640(defun gnus-articles-in-thread (thread)
4641 "Return the list of articles in THREAD."
4642 (cons (mail-header-number (car thread))
4643 (apply 'nconc (mapcar 'gnus-articles-in-thread (cdr thread)))))
4644
eec82323
LMI
4645(defun gnus-remove-thread (id &optional dont-remove)
4646 "Remove the thread that has ID in it."
6748645f 4647 (let (headers thread last-id)
eec82323 4648 ;; First go up in this thread until we find the root.
6748645f
LMI
4649 (setq last-id (gnus-root-id id)
4650 headers (message-flatten-list (gnus-id-to-thread last-id)))
01ccbb85 4651 ;; We have now found the real root of this thread. It might have
eec82323
LMI
4652 ;; been gathered into some loose thread, so we have to search
4653 ;; through the threads to find the thread we wanted.
4654 (let ((threads gnus-newsgroup-threads)
4655 sub)
4656 (while threads
4657 (setq sub (car threads))
4658 (if (stringp (car sub))
4659 ;; This is a gathered thread, so we look at the roots
4660 ;; below it to find whether this article is in this
4661 ;; gathered root.
4662 (progn
4663 (setq sub (cdr sub))
4664 (while sub
4665 (when (member (caar sub) headers)
4666 (setq thread (car threads)
4667 threads nil
4668 sub nil))
4669 (setq sub (cdr sub))))
4670 ;; It's an ordinary thread, so we check it.
4671 (when (eq (car sub) (car headers))
4672 (setq thread sub
4673 threads nil)))
4674 (setq threads (cdr threads)))
4675 ;; If this article is in no thread, then it's a root.
4676 (if thread
4677 (unless dont-remove
4678 (setq gnus-newsgroup-threads (delq thread gnus-newsgroup-threads)))
6748645f 4679 (setq thread (gnus-id-to-thread last-id)))
eec82323
LMI
4680 (when thread
4681 (prog1
4682 thread ; We return this thread.
4683 (unless dont-remove
4684 (if (stringp (car thread))
4685 (progn
4686 ;; If we use dummy roots, then we have to remove the
4687 ;; dummy root as well.
4688 (when (eq gnus-summary-make-false-root 'dummy)
6748645f
LMI
4689 ;; We go to the dummy root by going to
4690 ;; the first sub-"thread", and then one line up.
4691 (gnus-summary-goto-article
4692 (mail-header-number (caadr thread)))
4693 (forward-line -1)
eec82323
LMI
4694 (gnus-delete-line)
4695 (gnus-data-compute-positions))
4696 (setq thread (cdr thread))
4697 (while thread
4698 (gnus-remove-thread-1 (car thread))
4699 (setq thread (cdr thread))))
4700 (gnus-remove-thread-1 thread))))))))
4701
4702(defun gnus-remove-thread-1 (thread)
4703 "Remove the thread THREAD recursively."
4704 (let ((number (mail-header-number (pop thread)))
4705 d)
4706 (setq thread (reverse thread))
4707 (while thread
4708 (gnus-remove-thread-1 (pop thread)))
4709 (when (setq d (gnus-data-find number))
4710 (goto-char (gnus-data-pos d))
16409b0b 4711 (gnus-summary-show-thread)
eec82323
LMI
4712 (gnus-data-remove
4713 number
01c52d31 4714 (- (point-at-bol)
eec82323 4715 (prog1
01c52d31 4716 (1+ (point-at-eol))
eec82323
LMI
4717 (gnus-delete-line)))))))
4718
4921bbdd 4719(defun gnus-sort-threads-recursive (threads func)
16409b0b
GM
4720 (sort (mapcar (lambda (thread)
4721 (cons (car thread)
4722 (and (cdr thread)
4921bbdd 4723 (gnus-sort-threads-recursive (cdr thread) func))))
16409b0b
GM
4724 threads) func))
4725
4921bbdd
CY
4726(defun gnus-sort-threads-loop (threads func)
4727 (let* ((superthread (cons nil threads))
4728 (stack (list (cons superthread threads)))
4729 remaining-threads thread)
4730 (while stack
4731 (setq remaining-threads (cdr (car stack)))
4732 (if remaining-threads
4733 (progn (setq thread (car remaining-threads))
4734 (setcdr (car stack) (cdr remaining-threads))
4735 (if (cdr thread)
4736 (push (cons thread (cdr thread)) stack)))
4737 (setq thread (caar stack))
4738 (setcdr thread (sort (cdr thread) func))
4739 (pop stack)))
4740 (cdr superthread)))
4741
eec82323
LMI
4742(defun gnus-sort-threads (threads)
4743 "Sort THREADS."
4744 (if (not gnus-thread-sort-functions)
4745 threads
6748645f 4746 (gnus-message 8 "Sorting threads...")
4921bbdd
CY
4747 (prog1
4748 (condition-case nil
4749 (let ((max-lisp-eval-depth (max max-lisp-eval-depth 5000)))
4750 (gnus-sort-threads-recursive
4751 threads (gnus-make-sort-function gnus-thread-sort-functions)))
4752 ;; Even after binding max-lisp-eval-depth, the recursive
4753 ;; sorter might fail for very long threads. In that case,
4754 ;; try using a (less well-tested) non-recursive sorter.
4755 (error (gnus-sort-threads-loop
4756 threads (gnus-make-sort-function
4757 gnus-thread-sort-functions))))
4758 (gnus-message 8 "Sorting threads...done"))))
eec82323
LMI
4759
4760(defun gnus-sort-articles (articles)
4761 "Sort ARTICLES."
4762 (when gnus-article-sort-functions
4763 (gnus-message 7 "Sorting articles...")
4764 (prog1
4765 (setq gnus-newsgroup-headers
4766 (sort articles (gnus-make-sort-function
4767 gnus-article-sort-functions)))
4768 (gnus-message 7 "Sorting articles...done"))))
4769
4770;; Written by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
4771(defmacro gnus-thread-header (thread)
16409b0b
GM
4772 "Return header of first article in THREAD.
4773Note that THREAD must never, ever be anything else than a variable -
4774using some other form will lead to serious barfage."
eec82323
LMI
4775 (or (symbolp thread) (signal 'wrong-type-argument '(symbolp thread)))
4776 ;; (8% speedup to gnus-summary-prepare, just for fun :-)
16409b0b 4777 (list 'byte-code "\10\211:\203\17\0\211@;\203\16\0A@@\207"
eec82323
LMI
4778 (vector thread) 2))
4779
4780(defsubst gnus-article-sort-by-number (h1 h2)
4781 "Sort articles by article number."
4782 (< (mail-header-number h1)
4783 (mail-header-number h2)))
4784
4785(defun gnus-thread-sort-by-number (h1 h2)
4786 "Sort threads by root article number."
4787 (gnus-article-sort-by-number
4788 (gnus-thread-header h1) (gnus-thread-header h2)))
4789
23f87bed
MB
4790(defsubst gnus-article-sort-by-random (h1 h2)
4791 "Sort articles by article number."
4792 (zerop (random 2)))
4793
4794(defun gnus-thread-sort-by-random (h1 h2)
4795 "Sort threads by root article number."
4796 (gnus-article-sort-by-random
4797 (gnus-thread-header h1) (gnus-thread-header h2)))
4798
eec82323
LMI
4799(defsubst gnus-article-sort-by-lines (h1 h2)
4800 "Sort articles by article Lines header."
4801 (< (mail-header-lines h1)
4802 (mail-header-lines h2)))
4803
4804(defun gnus-thread-sort-by-lines (h1 h2)
4805 "Sort threads by root article Lines header."
4806 (gnus-article-sort-by-lines
4807 (gnus-thread-header h1) (gnus-thread-header h2)))
4808
16409b0b
GM
4809(defsubst gnus-article-sort-by-chars (h1 h2)
4810 "Sort articles by octet length."
4811 (< (mail-header-chars h1)
4812 (mail-header-chars h2)))
4813
4814(defun gnus-thread-sort-by-chars (h1 h2)
4815 "Sort threads by root article octet length."
4816 (gnus-article-sort-by-chars
4817 (gnus-thread-header h1) (gnus-thread-header h2)))
4818
eec82323
LMI
4819(defsubst gnus-article-sort-by-author (h1 h2)
4820 "Sort articles by root author."
b4fde39f 4821 (gnus-string<
eec82323
LMI
4822 (let ((extract (funcall
4823 gnus-extract-address-components
4824 (mail-header-from h1))))
4825 (or (car extract) (cadr extract) ""))
4826 (let ((extract (funcall
4827 gnus-extract-address-components
4828 (mail-header-from h2))))
4829 (or (car extract) (cadr extract) ""))))
4830
4831(defun gnus-thread-sort-by-author (h1 h2)
4832 "Sort threads by root author."
4833 (gnus-article-sort-by-author
4834 (gnus-thread-header h1) (gnus-thread-header h2)))
4835
01c52d31
MB
4836(defsubst gnus-article-sort-by-recipient (h1 h2)
4837 "Sort articles by recipient."
4838 (gnus-string<
4839 (let ((extract (funcall
4840 gnus-extract-address-components
4841 (or (cdr (assq 'To (mail-header-extra h1))) ""))))
4842 (or (car extract) (cadr extract)))
4843 (let ((extract (funcall
4844 gnus-extract-address-components
4845 (or (cdr (assq 'To (mail-header-extra h2))) ""))))
4846 (or (car extract) (cadr extract)))))
4847
4848(defun gnus-thread-sort-by-recipient (h1 h2)
4849 "Sort threads by root recipient."
4850 (gnus-article-sort-by-recipient
4851 (gnus-thread-header h1) (gnus-thread-header h2)))
4852
eec82323
LMI
4853(defsubst gnus-article-sort-by-subject (h1 h2)
4854 "Sort articles by root subject."
b4fde39f 4855 (gnus-string<
eec82323
LMI
4856 (downcase (gnus-simplify-subject-re (mail-header-subject h1)))
4857 (downcase (gnus-simplify-subject-re (mail-header-subject h2)))))
4858
4859(defun gnus-thread-sort-by-subject (h1 h2)
4860 "Sort threads by root subject."
4861 (gnus-article-sort-by-subject
4862 (gnus-thread-header h1) (gnus-thread-header h2)))
4863
4864(defsubst gnus-article-sort-by-date (h1 h2)
4865 "Sort articles by root article date."
16409b0b 4866 (time-less-p
eec82323
LMI
4867 (gnus-date-get-time (mail-header-date h1))
4868 (gnus-date-get-time (mail-header-date h2))))
4869
4870(defun gnus-thread-sort-by-date (h1 h2)
4871 "Sort threads by root article date."
4872 (gnus-article-sort-by-date
4873 (gnus-thread-header h1) (gnus-thread-header h2)))
4874
4875(defsubst gnus-article-sort-by-score (h1 h2)
4876 "Sort articles by root article score.
4877Unscored articles will be counted as having a score of zero."
4878 (> (or (cdr (assq (mail-header-number h1)
4879 gnus-newsgroup-scored))
4880 gnus-summary-default-score 0)
4881 (or (cdr (assq (mail-header-number h2)
4882 gnus-newsgroup-scored))
4883 gnus-summary-default-score 0)))
4884
4885(defun gnus-thread-sort-by-score (h1 h2)
4886 "Sort threads by root article score."
4887 (gnus-article-sort-by-score
4888 (gnus-thread-header h1) (gnus-thread-header h2)))
4889
4890(defun gnus-thread-sort-by-total-score (h1 h2)
4891 "Sort threads by the sum of all scores in the thread.
4892Unscored articles will be counted as having a score of zero."
4893 (> (gnus-thread-total-score h1) (gnus-thread-total-score h2)))
4894
4895(defun gnus-thread-total-score (thread)
16409b0b 4896 ;; This function find the total score of THREAD.
23f87bed
MB
4897 (cond
4898 ((null thread)
4899 0)
4900 ((consp thread)
4901 (if (stringp (car thread))
4902 (apply gnus-thread-score-function 0
4903 (mapcar 'gnus-thread-total-score-1 (cdr thread)))
4904 (gnus-thread-total-score-1 thread)))
4905 (t
4906 (gnus-thread-total-score-1 (list thread)))))
4907
4908(defun gnus-thread-sort-by-most-recent-number (h1 h2)
4909 "Sort threads such that the thread with the most recently arrived article comes first."
4910 (> (gnus-thread-highest-number h1) (gnus-thread-highest-number h2)))
4911
4912(defun gnus-thread-highest-number (thread)
4913 "Return the highest article number in THREAD."
4914 (apply 'max (mapcar (lambda (header)
4915 (mail-header-number header))
4916 (message-flatten-list thread))))
4917
4918(defun gnus-thread-sort-by-most-recent-date (h1 h2)
4919 "Sort threads such that the thread with the most recently dated article comes first."
4920 (> (gnus-thread-latest-date h1) (gnus-thread-latest-date h2)))
4921
4922(defun gnus-thread-latest-date (thread)
4923 "Return the highest article date in THREAD."
4924 (let ((previous-time 0))
4925 (apply 'max
4926 (mapcar
4927 (lambda (header)
4928 (setq previous-time
4929 (condition-case ()
4930 (time-to-seconds (mail-header-parse-date
4931 (mail-header-date header)))
4932 (error previous-time))))
4933 (sort
4934 (message-flatten-list thread)
4935 (lambda (h1 h2)
4936 (< (mail-header-number h1)
4937 (mail-header-number h2))))))))
eec82323
LMI
4938
4939(defun gnus-thread-total-score-1 (root)
4940 ;; This function find the total score of the thread below ROOT.
4941 (setq root (car root))
4942 (apply gnus-thread-score-function
4943 (or (append
4944 (mapcar 'gnus-thread-total-score
6748645f 4945 (cdr (gnus-id-to-thread (mail-header-id root))))
eec82323
LMI
4946 (when (> (mail-header-number root) 0)
4947 (list (or (cdr (assq (mail-header-number root)
4948 gnus-newsgroup-scored))
4949 gnus-summary-default-score 0))))
4950 (list gnus-summary-default-score)
4951 '(0))))
4952
4953;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
4954(defvar gnus-tmp-prev-subject nil)
4955(defvar gnus-tmp-false-parent nil)
4956(defvar gnus-tmp-root-expunged nil)
4957(defvar gnus-tmp-dummy-line nil)
4958
16409b0b
GM
4959(defun gnus-extra-header (type &optional header)
4960 "Return the extra header of TYPE."
4961 (or (cdr (assq type (mail-header-extra (or header gnus-tmp-header))))
4962 ""))
4963
23f87bed
MB
4964(defvar gnus-tmp-thread-tree-header-string "")
4965
4966(defcustom gnus-sum-thread-tree-root "> "
4967 "With %B spec, used for the root of a thread.
4968If nil, use subject instead."
bf247b6e 4969 :version "22.1"
ad136a7c 4970 :type '(radio (const :format "%v " nil) string)
23f87bed 4971 :group 'gnus-thread)
01c52d31 4972
23f87bed
MB
4973(defcustom gnus-sum-thread-tree-false-root "> "
4974 "With %B spec, used for a false root of a thread.
4975If nil, use subject instead."
bf247b6e 4976 :version "22.1"
ad136a7c 4977 :type '(radio (const :format "%v " nil) string)
23f87bed 4978 :group 'gnus-thread)
01c52d31 4979
23f87bed
MB
4980(defcustom gnus-sum-thread-tree-single-indent ""
4981 "With %B spec, used for a thread with just one message.
4982If nil, use subject instead."
bf247b6e 4983 :version "22.1"
ad136a7c 4984 :type '(radio (const :format "%v " nil) string)
23f87bed 4985 :group 'gnus-thread)
01c52d31 4986
23f87bed
MB
4987(defcustom gnus-sum-thread-tree-vertical "| "
4988 "With %B spec, used for drawing a vertical line."
bf247b6e 4989 :version "22.1"
23f87bed
MB
4990 :type 'string
4991 :group 'gnus-thread)
01c52d31 4992
23f87bed
MB
4993(defcustom gnus-sum-thread-tree-indent " "
4994 "With %B spec, used for indenting."
bf247b6e 4995 :version "22.1"
23f87bed
MB
4996 :type 'string
4997 :group 'gnus-thread)
01c52d31 4998
23f87bed
MB
4999(defcustom gnus-sum-thread-tree-leaf-with-other "+-> "
5000 "With %B spec, used for a leaf with brothers."
bf247b6e 5001 :version "22.1"
23f87bed
MB
5002 :type 'string
5003 :group 'gnus-thread)
01c52d31 5004
23f87bed
MB
5005(defcustom gnus-sum-thread-tree-single-leaf "\\-> "
5006 "With %B spec, used for a leaf without brothers."
bf247b6e 5007 :version "22.1"
23f87bed
MB
5008 :type 'string
5009 :group 'gnus-thread)
5010
eec82323
LMI
5011(defun gnus-summary-prepare-threads (threads)
5012 "Prepare summary buffer from THREADS and indentation LEVEL.
5013THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])'
5014or a straight list of headers."
5015 (gnus-message 7 "Generating summary...")
5016
5017 (setq gnus-newsgroup-threads threads)
5018 (beginning-of-line)
5019
5020 (let ((gnus-tmp-level 0)
5021 (default-score (or gnus-summary-default-score 0))
5022 (gnus-visual-p (gnus-visual-p 'summary-highlight 'highlight))
23f87bed
MB
5023 (building-line-count gnus-summary-display-while-building)
5024 (building-count (integerp gnus-summary-display-while-building))
eec82323 5025 thread number subject stack state gnus-tmp-gathered beg-match
23f87bed
MB
5026 new-roots gnus-tmp-new-adopts thread-end simp-subject
5027 gnus-tmp-header gnus-tmp-unread gnus-tmp-downloaded
eec82323
LMI
5028 gnus-tmp-replied gnus-tmp-subject-or-nil
5029 gnus-tmp-dummy gnus-tmp-indentation gnus-tmp-lines gnus-tmp-score
5030 gnus-tmp-score-char gnus-tmp-from gnus-tmp-name
23f87bed
MB
5031 gnus-tmp-number gnus-tmp-opening-bracket gnus-tmp-closing-bracket
5032 tree-stack)
eec82323 5033
23f87bed
MB
5034 (setq gnus-tmp-prev-subject nil
5035 gnus-tmp-thread-tree-header-string "")
eec82323
LMI
5036
5037 (if (vectorp (car threads))
5038 ;; If this is a straight (sic) list of headers, then a
5039 ;; threaded summary display isn't required, so we just create
5040 ;; an unthreaded one.
5041 (gnus-summary-prepare-unthreaded threads)
5042
5043 ;; Do the threaded display.
5044
23f87bed
MB
5045 (if gnus-summary-display-while-building
5046 (switch-to-buffer (buffer-name)))
eec82323
LMI
5047 (while (or threads stack gnus-tmp-new-adopts new-roots)
5048
5049 (if (and (= gnus-tmp-level 0)
eec82323
LMI
5050 (or (not stack)
5051 (= (caar stack) 0))
5052 (not gnus-tmp-false-parent)
5053 (or gnus-tmp-new-adopts new-roots))
5054 (if gnus-tmp-new-adopts
5055 (setq gnus-tmp-level (if gnus-tmp-root-expunged 0 1)
5056 thread (list (car gnus-tmp-new-adopts))
5057 gnus-tmp-header (caar thread)
5058 gnus-tmp-new-adopts (cdr gnus-tmp-new-adopts))
5059 (when new-roots
5060 (setq thread (list (car new-roots))
5061 gnus-tmp-header (caar thread)
5062 new-roots (cdr new-roots))))
5063
5064 (if threads
5065 ;; If there are some threads, we do them before the
5066 ;; threads on the stack.
5067 (setq thread threads
5068 gnus-tmp-header (caar thread))
5069 ;; There were no current threads, so we pop something off
5070 ;; the stack.
5071 (setq state (car stack)
5072 gnus-tmp-level (car state)
23f87bed
MB
5073 tree-stack (cadr state)
5074 thread (caddr state)
eec82323
LMI
5075 stack (cdr stack)
5076 gnus-tmp-header (caar thread))))
5077
5078 (setq gnus-tmp-false-parent nil)
5079 (setq gnus-tmp-root-expunged nil)
5080 (setq thread-end nil)
5081
5082 (if (stringp gnus-tmp-header)
5083 ;; The header is a dummy root.
5084 (cond
5085 ((eq gnus-summary-make-false-root 'adopt)
5086 ;; We let the first article adopt the rest.
5087 (setq gnus-tmp-new-adopts (nconc gnus-tmp-new-adopts
5088 (cddar thread)))
5089 (setq gnus-tmp-gathered
5090 (nconc (mapcar
5091 (lambda (h) (mail-header-number (car h)))
5092 (cddar thread))
5093 gnus-tmp-gathered))
5094 (setq thread (cons (list (caar thread)
5095 (cadar thread))
5096 (cdr thread)))
5097 (setq gnus-tmp-level -1
5098 gnus-tmp-false-parent t))
5099 ((eq gnus-summary-make-false-root 'empty)
5100 ;; We print adopted articles with empty subject fields.
5101 (setq gnus-tmp-gathered
5102 (nconc (mapcar
5103 (lambda (h) (mail-header-number (car h)))
5104 (cddar thread))
5105 gnus-tmp-gathered))
5106 (setq gnus-tmp-level -1))
5107 ((eq gnus-summary-make-false-root 'dummy)
5108 ;; We remember that we probably want to output a dummy
5109 ;; root.
5110 (setq gnus-tmp-dummy-line gnus-tmp-header)
5111 (setq gnus-tmp-prev-subject gnus-tmp-header))
5112 (t
5113 ;; We do not make a root for the gathered
5114 ;; sub-threads at all.
5115 (setq gnus-tmp-level -1)))
5116
5117 (setq number (mail-header-number gnus-tmp-header)
23f87bed
MB
5118 subject (mail-header-subject gnus-tmp-header)
5119 simp-subject (gnus-simplify-subject-fully subject))
eec82323
LMI
5120
5121 (cond
5122 ;; If the thread has changed subject, we might want to make
5123 ;; this subthread into a root.
5124 ((and (null gnus-thread-ignore-subject)
5125 (not (zerop gnus-tmp-level))
5126 gnus-tmp-prev-subject
23f87bed 5127 (not (string= gnus-tmp-prev-subject simp-subject)))
eec82323
LMI
5128 (setq new-roots (nconc new-roots (list (car thread)))
5129 thread-end t
5130 gnus-tmp-header nil))
5131 ;; If the article lies outside the current limit,
5132 ;; then we do not display it.
5133 ((not (memq number gnus-newsgroup-limit))
5134 (setq gnus-tmp-gathered
5135 (nconc (mapcar
5136 (lambda (h) (mail-header-number (car h)))
5137 (cdar thread))
5138 gnus-tmp-gathered))
5139 (setq gnus-tmp-new-adopts (if (cdar thread)
5140 (append gnus-tmp-new-adopts
5141 (cdar thread))
5142 gnus-tmp-new-adopts)
5143 thread-end t
5144 gnus-tmp-header nil)
5145 (when (zerop gnus-tmp-level)
5146 (setq gnus-tmp-root-expunged t)))
5147 ;; Perhaps this article is to be marked as read?
5148 ((and gnus-summary-mark-below
5149 (< (or (cdr (assq number gnus-newsgroup-scored))
5150 default-score)
5151 gnus-summary-mark-below)
5152 ;; Don't touch sparse articles.
5153 (not (gnus-summary-article-sparse-p number))
5154 (not (gnus-summary-article-ancient-p number)))
5155 (setq gnus-newsgroup-unreads
5156 (delq number gnus-newsgroup-unreads))
5157 (if gnus-newsgroup-auto-expire
23f87bed
MB
5158 (setq gnus-newsgroup-expirable
5159 (gnus-add-to-sorted-list
5160 gnus-newsgroup-expirable number))
eec82323
LMI
5161 (push (cons number gnus-low-score-mark)
5162 gnus-newsgroup-reads))))
5163
5164 (when gnus-tmp-header
5165 ;; We may have an old dummy line to output before this
5166 ;; article.
6748645f
LMI
5167 (when (and gnus-tmp-dummy-line
5168 (gnus-subject-equal
5169 gnus-tmp-dummy-line
5170 (mail-header-subject gnus-tmp-header)))
eec82323
LMI
5171 (gnus-summary-insert-dummy-line
5172 gnus-tmp-dummy-line (mail-header-number gnus-tmp-header))
5173 (setq gnus-tmp-dummy-line nil))
5174
5175 ;; Compute the mark.
5176 (setq gnus-tmp-unread (gnus-article-mark number))
5177
5178 (push (gnus-data-make number gnus-tmp-unread (1+ (point))
5179 gnus-tmp-header gnus-tmp-level)
5180 gnus-newsgroup-data)
5181
5182 ;; Actually insert the line.
5183 (setq
5184 gnus-tmp-subject-or-nil
5185 (cond
5186 ((and gnus-thread-ignore-subject
5187 gnus-tmp-prev-subject
23f87bed 5188 (not (string= gnus-tmp-prev-subject simp-subject)))
eec82323
LMI
5189 subject)
5190 ((zerop gnus-tmp-level)
5191 (if (and (eq gnus-summary-make-false-root 'empty)
5192 (memq number gnus-tmp-gathered)
5193 gnus-tmp-prev-subject
23f87bed 5194 (string= gnus-tmp-prev-subject simp-subject))
eec82323
LMI
5195 gnus-summary-same-subject
5196 subject))
5197 (t gnus-summary-same-subject)))
5198 (if (and (eq gnus-summary-make-false-root 'adopt)
5199 (= gnus-tmp-level 1)
5200 (memq number gnus-tmp-gathered))
5201 (setq gnus-tmp-opening-bracket ?\<
5202 gnus-tmp-closing-bracket ?\>)
5203 (setq gnus-tmp-opening-bracket ?\[
5204 gnus-tmp-closing-bracket ?\]))
4921bbdd
CY
5205 (if (>= gnus-tmp-level (length gnus-thread-indent-array))
5206 (gnus-make-thread-indent-array
5207 (max (* 2 (length gnus-thread-indent-array))
5208 gnus-tmp-level)))
eec82323
LMI
5209 (setq
5210 gnus-tmp-indentation
5211 (aref gnus-thread-indent-array gnus-tmp-level)
5212 gnus-tmp-lines (mail-header-lines gnus-tmp-header)
5213 gnus-tmp-score (or (cdr (assq number gnus-newsgroup-scored))
5214 gnus-summary-default-score 0)
5215 gnus-tmp-score-char
5216 (if (or (null gnus-summary-default-score)
5217 (<= (abs (- gnus-tmp-score gnus-summary-default-score))
5218 gnus-summary-zcore-fuzz))
23f87bed 5219 ? ;Whitespace
eec82323
LMI
5220 (if (< gnus-tmp-score gnus-summary-default-score)
5221 gnus-score-below-mark gnus-score-over-mark))
5222 gnus-tmp-replied
5223 (cond ((memq number gnus-newsgroup-processable)
5224 gnus-process-mark)
5225 ((memq number gnus-newsgroup-cached)
5226 gnus-cached-mark)
5227 ((memq number gnus-newsgroup-replied)
5228 gnus-replied-mark)
23f87bed
MB
5229 ((memq number gnus-newsgroup-forwarded)
5230 gnus-forwarded-mark)
eec82323
LMI
5231 ((memq number gnus-newsgroup-saved)
5232 gnus-saved-mark)
23f87bed
MB
5233 ((memq number gnus-newsgroup-recent)
5234 gnus-recent-mark)
5235 ((memq number gnus-newsgroup-unseen)
5236 gnus-unseen-mark)
5237 (t gnus-no-mark))
5238 gnus-tmp-downloaded
5239 (cond ((memq number gnus-newsgroup-undownloaded)
5240 gnus-undownloaded-mark)
5241 (gnus-newsgroup-agentized
5242 gnus-downloaded-mark)
5243 (t
5244 gnus-no-mark))
eec82323
LMI
5245 gnus-tmp-from (mail-header-from gnus-tmp-header)
5246 gnus-tmp-name
5247 (cond
5248 ((string-match "<[^>]+> *$" gnus-tmp-from)
5249 (setq beg-match (match-beginning 0))
23f87bed
MB
5250 (or (and (string-match "^\".+\"" gnus-tmp-from)
5251 (substring gnus-tmp-from 1 (1- (match-end 0))))
eec82323
LMI
5252 (substring gnus-tmp-from 0 beg-match)))
5253 ((string-match "(.+)" gnus-tmp-from)
5254 (substring gnus-tmp-from
5255 (1+ (match-beginning 0)) (1- (match-end 0))))
23f87bed
MB
5256 (t gnus-tmp-from))
5257
5258 ;; Do the %B string
5259 gnus-tmp-thread-tree-header-string
5260 (cond
5261 ((not gnus-show-threads) "")
5262 ((zerop gnus-tmp-level)
5263 (cond ((cdar thread)
5264 (or gnus-sum-thread-tree-root subject))
5265 (gnus-tmp-new-adopts
5266 (or gnus-sum-thread-tree-false-root subject))
5267 (t
5268 (or gnus-sum-thread-tree-single-indent subject))))
5269 (t
5270 (concat (apply 'concat
5271 (mapcar (lambda (item)
5272 (if (= item 1)
5273 gnus-sum-thread-tree-vertical
5274 gnus-sum-thread-tree-indent))
5275 (cdr (reverse tree-stack))))
5276 (if (nth 1 thread)
5277 gnus-sum-thread-tree-leaf-with-other
5278 gnus-sum-thread-tree-single-leaf)))))
eec82323
LMI
5279 (when (string= gnus-tmp-name "")
5280 (setq gnus-tmp-name gnus-tmp-from))
5281 (unless (numberp gnus-tmp-lines)
23f87bed
MB
5282 (setq gnus-tmp-lines -1))
5283 (if (= gnus-tmp-lines -1)
5284 (setq gnus-tmp-lines "?")
5285 (setq gnus-tmp-lines (number-to-string gnus-tmp-lines)))
5286 (gnus-put-text-property
eec82323
LMI
5287 (point)
5288 (progn (eval gnus-summary-line-format-spec) (point))
23f87bed 5289 'gnus-number number)
eec82323
LMI
5290 (when gnus-visual-p
5291 (forward-line -1)
6748645f 5292 (gnus-run-hooks 'gnus-summary-update-hook)
eec82323
LMI
5293 (forward-line 1))
5294
23f87bed 5295 (setq gnus-tmp-prev-subject simp-subject)))
eec82323
LMI
5296
5297 (when (nth 1 thread)
23f87bed
MB
5298 (push (list (max 0 gnus-tmp-level)
5299 (copy-sequence tree-stack)
5300 (nthcdr 1 thread))
5301 stack))
5302 (push (if (nth 1 thread) 1 0) tree-stack)
eec82323
LMI
5303 (incf gnus-tmp-level)
5304 (setq threads (if thread-end nil (cdar thread)))
23f87bed
MB
5305 (if gnus-summary-display-while-building
5306 (if building-count
5307 (progn
5308 ;; use a set frequency
5309 (setq building-line-count (1- building-line-count))
5310 (when (= building-line-count 0)
5311 (sit-for 0)
5312 (setq building-line-count
5313 gnus-summary-display-while-building)))
5314 ;; always
5315 (sit-for 0)))
eec82323
LMI
5316 (unless threads
5317 (setq gnus-tmp-level 0)))))
5318 (gnus-message 7 "Generating summary...done"))
5319
5320(defun gnus-summary-prepare-unthreaded (headers)
5321 "Generate an unthreaded summary buffer based on HEADERS."
5322 (let (header number mark)
5323
5324 (beginning-of-line)
5325
5326 (while headers
5327 ;; We may have to root out some bad articles...
5328 (when (memq (setq number (mail-header-number
5329 (setq header (pop headers))))
5330 gnus-newsgroup-limit)
5331 ;; Mark article as read when it has a low score.
5332 (when (and gnus-summary-mark-below
5333 (< (or (cdr (assq number gnus-newsgroup-scored))
5334 gnus-summary-default-score 0)
5335 gnus-summary-mark-below)
5336 (not (gnus-summary-article-ancient-p number)))
5337 (setq gnus-newsgroup-unreads
5338 (delq number gnus-newsgroup-unreads))
5339 (if gnus-newsgroup-auto-expire
5340 (push number gnus-newsgroup-expirable)
5341 (push (cons number gnus-low-score-mark)
5342 gnus-newsgroup-reads)))
5343
5344 (setq mark (gnus-article-mark number))
5345 (push (gnus-data-make number mark (1+ (point)) header 0)
5346 gnus-newsgroup-data)
5347 (gnus-summary-insert-line
5348 header 0 number
23f87bed 5349 (memq number gnus-newsgroup-undownloaded)
eec82323
LMI
5350 mark (memq number gnus-newsgroup-replied)
5351 (memq number gnus-newsgroup-expirable)
5352 (mail-header-subject header) nil
5353 (cdr (assq number gnus-newsgroup-scored))
5354 (memq number gnus-newsgroup-processable))))))
5355
16409b0b
GM
5356(defun gnus-summary-remove-list-identifiers ()
5357 "Remove list identifiers in `gnus-list-identifiers' from articles in the current group."
23f87bed
MB
5358 (let ((regexp (if (consp gnus-list-identifiers)
5359 (mapconcat 'identity gnus-list-identifiers " *\\|")
5360 gnus-list-identifiers))
5361 changed subject)
5362 (when regexp
01c52d31 5363 (setq regexp (concat "^\\(?:R[Ee]: +\\)*\\(" regexp " *\\)"))
23f87bed
MB
5364 (dolist (header gnus-newsgroup-headers)
5365 (setq subject (mail-header-subject header)
5366 changed nil)
01c52d31 5367 (while (string-match regexp subject)
23f87bed 5368 (setq subject
01c52d31 5369 (concat (substring subject 0 (match-beginning 1))
23f87bed
MB
5370 (substring subject (match-end 0)))
5371 changed t))
23f87bed 5372 (when changed
01c52d31
MB
5373 (when (string-match "^\\(\\(?:R[Ee]: +\\)+\\)R[Ee]: +" subject)
5374 (setq subject
5375 (concat (substring subject 0 (match-beginning 1))
5376 (substring subject (match-end 1)))))
23f87bed
MB
5377 (mail-header-set-subject header subject))))))
5378
5379(defun gnus-fetch-headers (articles)
5380 "Fetch headers of ARTICLES."
5381 (let ((name (gnus-group-decoded-name gnus-newsgroup-name)))
5382 (gnus-message 5 "Fetching headers for %s..." name)
5383 (prog1
5384 (if (eq 'nov
5385 (setq gnus-headers-retrieved-by
5386 (gnus-retrieve-headers
5387 articles gnus-newsgroup-name
5388 ;; We might want to fetch old headers, but
5389 ;; not if there is only 1 article.
5390 (and (or (and
5391 (not (eq gnus-fetch-old-headers 'some))
5392 (not (numberp gnus-fetch-old-headers)))
5393 (> (length articles) 1))
5394 gnus-fetch-old-headers))))
5395 (gnus-get-newsgroup-headers-xover
5396 articles nil nil gnus-newsgroup-name t)
5397 (gnus-get-newsgroup-headers))
5398 (gnus-message 5 "Fetching headers for %s...done" name))))
16409b0b 5399
6748645f 5400(defun gnus-select-newsgroup (group &optional read-all select-articles)
eec82323 5401 "Select newsgroup GROUP.
6748645f
LMI
5402If READ-ALL is non-nil, all articles in the group are selected.
5403If SELECT-ARTICLES, only select those articles from GROUP."
01c52d31 5404 (let* ((entry (gnus-group-entry group))
eec82323
LMI
5405 ;;!!! Dirty hack; should be removed.
5406 (gnus-summary-ignore-duplicates
23f87bed 5407 (if (eq (car (gnus-find-method-for-group group)) 'nnvirtual)
eec82323
LMI
5408 t
5409 gnus-summary-ignore-duplicates))
5410 (info (nth 2 entry))
01c52d31 5411 charset articles fetched-articles cached)
eec82323
LMI
5412
5413 (unless (gnus-check-server
475e0e0c
GM
5414 (set (make-local-variable 'gnus-current-select-method)
5415 (gnus-find-method-for-group group)))
eec82323 5416 (error "Couldn't open server"))
01c52d31 5417 (setq charset (gnus-group-name-charset gnus-current-select-method group))
eec82323
LMI
5418
5419 (or (and entry (not (eq (car entry) t))) ; Either it's active...
5420 (gnus-activate-group group) ; Or we can activate it...
5421 (progn ; Or we bug out.
5422 (when (equal major-mode 'gnus-summary-mode)
23f87bed 5423 (gnus-kill-buffer (current-buffer)))
01c52d31
MB
5424 (error
5425 "Couldn't activate group %s: %s"
5426 (mm-decode-coding-string group charset)
5427 (mm-decode-coding-string (gnus-status-message group) charset))))
eec82323
LMI
5428
5429 (unless (gnus-request-group group t)
01c52d31
MB
5430 (when (equal major-mode 'gnus-summary-mode)
5431 (gnus-kill-buffer (current-buffer)))
5432 (error "Couldn't request group %s: %s"
5433 (mm-decode-coding-string group charset)
5434 (mm-decode-coding-string (gnus-status-message group) charset)))
eec82323 5435
23f87bed 5436 (when gnus-agent
54506618 5437 (gnus-agent-possibly-alter-active group (gnus-active group) info)
132cf96d 5438
23f87bed
MB
5439 (setq gnus-summary-use-undownloaded-faces
5440 (gnus-agent-find-parameter
5441 group
5442 'agent-enable-undownloaded-faces)))
5443
5444 (setq gnus-newsgroup-name group
5445 gnus-newsgroup-unselected nil
5446 gnus-newsgroup-unreads (gnus-list-of-unread-articles group))
5447
5448 (let ((display (gnus-group-find-parameter group 'display)))
5449 (setq gnus-newsgroup-display
5450 (cond
5451 ((not (zerop (or (car-safe read-all) 0)))
5452 ;; The user entered the group with C-u SPC/RET, let's show
5453 ;; all articles.
5454 'gnus-not-ignore)
5455 ((eq display 'all)
5456 'gnus-not-ignore)
5457 ((arrayp display)
5458 (gnus-summary-display-make-predicate (mapcar 'identity display)))
5459 ((numberp display)
5460 ;; The following is probably the "correct" solution, but
5461 ;; it makes Gnus fetch all headers and then limit the
5462 ;; articles (which is slow), so instead we hack the
5463 ;; select-articles parameter instead. -- Simon Josefsson
5464 ;; <jas@kth.se>
5465 ;;
5466 ;; (gnus-byte-compile
5467 ;; `(lambda () (> number ,(- (cdr (gnus-active group))
5468 ;; display)))))
5469 (setq select-articles
5470 (gnus-uncompress-range
5471 (cons (let ((tmp (- (cdr (gnus-active group)) display)))
5472 (if (> tmp 0)
5473 tmp
5474 1))
5475 (cdr (gnus-active group)))))
5476 nil)
5477 (t
5478 nil))))
eec82323 5479
23f87bed 5480 (gnus-summary-setup-default-charset)
eec82323
LMI
5481
5482 ;; Kludge to avoid having cached articles nixed out in virtual groups.
5483 (when (gnus-virtual-group-p group)
5484 (setq cached gnus-newsgroup-cached))
5485
5486 (setq gnus-newsgroup-unreads
23f87bed
MB
5487 (gnus-sorted-ndifference
5488 (gnus-sorted-ndifference gnus-newsgroup-unreads
5489 gnus-newsgroup-marked)
eec82323
LMI
5490 gnus-newsgroup-dormant))
5491
5492 (setq gnus-newsgroup-processable nil)
5493
5494 (gnus-update-read-articles group gnus-newsgroup-unreads)
eec82323 5495
23f87bed
MB
5496 ;; Adjust and set lists of article marks.
5497 (when info
5498 (gnus-adjust-marked-articles info))
6748645f
LMI
5499 (if (setq articles select-articles)
5500 (setq gnus-newsgroup-unselected
23f87bed 5501 (gnus-sorted-difference gnus-newsgroup-unreads articles))
6748645f 5502 (setq articles (gnus-articles-to-read group read-all)))
eec82323
LMI
5503
5504 (cond
5505 ((null articles)
5506 ;;(gnus-message 3 "Couldn't select newsgroup -- no articles to display")
5507 'quit)
5508 ((eq articles 0) nil)
5509 (t
5510 ;; Init the dependencies hash table.
5511 (setq gnus-newsgroup-dependencies
5512 (gnus-make-hashtable (length articles)))
16409b0b 5513 (gnus-set-global-variables)
eec82323 5514 ;; Retrieve the headers and read them in.
23f87bed
MB
5515
5516 (setq gnus-newsgroup-headers (gnus-fetch-headers articles))
eec82323
LMI
5517
5518 ;; Kludge to avoid having cached articles nixed out in virtual groups.
5519 (when cached
5520 (setq gnus-newsgroup-cached cached))
5521
5522 ;; Suppress duplicates?
5523 (when gnus-suppress-duplicates
5524 (gnus-dup-suppress-articles))
5525
5526 ;; Set the initial limit.
5527 (setq gnus-newsgroup-limit (copy-sequence articles))
5528 ;; Remove canceled articles from the list of unread articles.
23f87bed
MB
5529 (setq fetched-articles
5530 (mapcar (lambda (headers) (mail-header-number headers))
5531 gnus-newsgroup-headers))
5532 (setq gnus-newsgroup-articles fetched-articles)
eec82323 5533 (setq gnus-newsgroup-unreads
23f87bed
MB
5534 (gnus-sorted-nintersection
5535 gnus-newsgroup-unreads fetched-articles))
5536 (gnus-compute-unseen-list)
5537
eec82323
LMI
5538 ;; Removed marked articles that do not exist.
5539 (gnus-update-missing-marks
23f87bed 5540 (gnus-sorted-difference articles fetched-articles))
eec82323 5541 ;; We might want to build some more threads first.
6748645f
LMI
5542 (when (and gnus-fetch-old-headers
5543 (eq gnus-headers-retrieved-by 'nov))
5544 (if (eq gnus-fetch-old-headers 'invisible)
5545 (gnus-build-all-threads)
5546 (gnus-build-old-threads)))
5547 ;; Let the Gnus agent mark articles as read.
5548 (when gnus-agent
5549 (gnus-agent-get-undownloaded-list))
16409b0b
GM
5550 ;; Remove list identifiers from subject
5551 (when gnus-list-identifiers
5552 (gnus-summary-remove-list-identifiers))
eec82323
LMI
5553 ;; Check whether auto-expire is to be done in this group.
5554 (setq gnus-newsgroup-auto-expire
5555 (gnus-group-auto-expirable-p group))
5556 ;; Set up the article buffer now, if necessary.
01c52d31
MB
5557 (unless (and gnus-single-article-buffer
5558 (equal gnus-article-buffer "*Article*"))
eec82323
LMI
5559 (gnus-article-setup-buffer))
5560 ;; First and last article in this newsgroup.
5561 (when gnus-newsgroup-headers
5562 (setq gnus-newsgroup-begin
5563 (mail-header-number (car gnus-newsgroup-headers))
5564 gnus-newsgroup-end
5565 (mail-header-number
5566 (gnus-last-element gnus-newsgroup-headers))))
5567 ;; GROUP is successfully selected.
5568 (or gnus-newsgroup-headers t)))))
5569
23f87bed
MB
5570(defun gnus-compute-unseen-list ()
5571 ;; The `seen' marks are treated specially.
5572 (if (not gnus-newsgroup-seen)
5573 (setq gnus-newsgroup-unseen gnus-newsgroup-articles)
5574 (setq gnus-newsgroup-unseen
5575 (gnus-inverse-list-range-intersection
5576 gnus-newsgroup-articles gnus-newsgroup-seen))))
5577
5578(defun gnus-summary-display-make-predicate (display)
5579 (require 'gnus-agent)
5580 (when (= (length display) 1)
5581 (setq display (car display)))
5582 (unless gnus-summary-display-cache
5583 (dolist (elem (append '((unread . unread)
5584 (read . read)
5585 (unseen . unseen))
5586 gnus-article-mark-lists))
5587 (push (cons (cdr elem)
5588 (gnus-byte-compile
5589 `(lambda () (gnus-article-marked-p ',(cdr elem)))))
5590 gnus-summary-display-cache)))
5591 (let ((gnus-category-predicate-alist gnus-summary-display-cache)
5592 (gnus-category-predicate-cache gnus-summary-display-cache))
5593 (gnus-get-predicate display)))
5594
5595;; Uses the dynamically bound `number' variable.
9efa445f 5596(defvar number)
23f87bed
MB
5597(defun gnus-article-marked-p (type &optional article)
5598 (let ((article (or article number)))
5599 (cond
5600 ((eq type 'tick)
5601 (memq article gnus-newsgroup-marked))
5602 ((eq type 'spam)
5603 (memq article gnus-newsgroup-spam-marked))
5604 ((eq type 'unsend)
5605 (memq article gnus-newsgroup-unsendable))
5606 ((eq type 'undownload)
5607 (memq article gnus-newsgroup-undownloaded))
5608 ((eq type 'download)
5609 (memq article gnus-newsgroup-downloadable))
5610 ((eq type 'unread)
5611 (memq article gnus-newsgroup-unreads))
5612 ((eq type 'read)
5613 (memq article gnus-newsgroup-reads))
5614 ((eq type 'dormant)
5615 (memq article gnus-newsgroup-dormant) )
5616 ((eq type 'expire)
5617 (memq article gnus-newsgroup-expirable))
5618 ((eq type 'reply)
5619 (memq article gnus-newsgroup-replied))
5620 ((eq type 'killed)
5621 (memq article gnus-newsgroup-killed))
5622 ((eq type 'bookmark)
5623 (assq article gnus-newsgroup-bookmarks))
5624 ((eq type 'score)
5625 (assq article gnus-newsgroup-scored))
5626 ((eq type 'save)
5627 (memq article gnus-newsgroup-saved))
5628 ((eq type 'cache)
5629 (memq article gnus-newsgroup-cached))
5630 ((eq type 'forward)
5631 (memq article gnus-newsgroup-forwarded))
5632 ((eq type 'seen)
5633 (not (memq article gnus-newsgroup-unseen)))
5634 ((eq type 'recent)
5635 (memq article gnus-newsgroup-recent))
5636 (t t))))
5637
eec82323 5638(defun gnus-articles-to-read (group &optional read-all)
16409b0b 5639 "Find out what articles the user wants to read."
26c9afc3 5640 (let* ((articles
eec82323
LMI
5641 ;; Select all articles if `read-all' is non-nil, or if there
5642 ;; are no unread articles.
5643 (if (or read-all
5644 (and (zerop (length gnus-newsgroup-marked))
5645 (zerop (length gnus-newsgroup-unreads)))
23f87bed
MB
5646 ;; Fetch all if the predicate is non-nil.
5647 gnus-newsgroup-display)
5648 ;; We want to select the headers for all the articles in
5649 ;; the group, so we select either all the active
5650 ;; articles in the group, or (if that's nil), the
5651 ;; articles in the cache.
16409b0b 5652 (or
4b70e299 5653 (if gnus-newsgroup-maximum-articles
11abff8e
MB
5654 (let ((active (gnus-active group)))
5655 (gnus-uncompress-range
5656 (cons (max (car active)
4b70e299
MB
5657 (- (cdr active)
5658 gnus-newsgroup-maximum-articles
5659 -1))
11abff8e
MB
5660 (cdr active))))
5661 (gnus-uncompress-range (gnus-active group)))
16409b0b 5662 (gnus-cache-articles-in-group group))
23f87bed
MB
5663 ;; Select only the "normal" subset of articles.
5664 (gnus-sorted-nunion
5665 (gnus-sorted-union gnus-newsgroup-dormant gnus-newsgroup-marked)
5666 gnus-newsgroup-unreads)))
eec82323
LMI
5667 (scored-list (gnus-killed-articles gnus-newsgroup-killed articles))
5668 (scored (length scored-list))
5669 (number (length articles))
5670 (marked (+ (length gnus-newsgroup-marked)
5671 (length gnus-newsgroup-dormant)))
5672 (select
5673 (cond
5674 ((numberp read-all)
5675 read-all)
23f87bed
MB
5676 ((numberp gnus-newsgroup-display)
5677 gnus-newsgroup-display)
eec82323
LMI
5678 (t
5679 (condition-case ()
5680 (cond
5681 ((and (or (<= scored marked) (= scored number))
5682 (numberp gnus-large-newsgroup)
5683 (> number gnus-large-newsgroup))
23f87bed
MB
5684 (let* ((cursor-in-echo-area nil)
5685 (initial (gnus-parameter-large-newsgroup-initial
5686 gnus-newsgroup-name))
5687 (input
5688 (read-string
5689 (format
5690 "How many articles from %s (%s %d): "
01c52d31 5691 (gnus-group-decoded-name gnus-newsgroup-name)
23f87bed
MB
5692 (if initial "max" "default")
5693 number)
5694 (if initial
5695 (cons (number-to-string initial)
5696 0)))))
eec82323
LMI
5697 (if (string-match "^[ \t]*$" input) number input)))
5698 ((and (> scored marked) (< scored number)
5699 (> (- scored number) 20))
5700 (let ((input
5701 (read-string
5702 (format "%s %s (%d scored, %d total): "
5703 "How many articles from"
23f87bed
MB
5704 (gnus-group-decoded-name group)
5705 scored number))))
eec82323
LMI
5706 (if (string-match "^[ \t]*$" input)
5707 number input)))
5708 (t number))
d4dfaa19
DL
5709 (quit
5710 (message "Quit getting the articles to read")
5711 nil))))))
eec82323
LMI
5712 (setq select (if (stringp select) (string-to-number select) select))
5713 (if (or (null select) (zerop select))
5714 select
5715 (if (and (not (zerop scored)) (<= (abs select) scored))
5716 (progn
5717 (setq articles (sort scored-list '<))
5718 (setq number (length articles)))
5719 (setq articles (copy-sequence articles)))
5720
5721 (when (< (abs select) number)
5722 (if (< select 0)
5723 ;; Select the N oldest articles.
5724 (setcdr (nthcdr (1- (abs select)) articles) nil)
5725 ;; Select the N most recent articles.
5726 (setq articles (nthcdr (- number select) articles))))
5727 (setq gnus-newsgroup-unselected
23f87bed 5728 (gnus-sorted-difference gnus-newsgroup-unreads articles))
16409b0b 5729 (when gnus-alter-articles-to-read-function
23f87bed 5730 (setq articles
a1506d29 5731 (sort
16409b0b 5732 (funcall gnus-alter-articles-to-read-function
23f87bed 5733 gnus-newsgroup-name articles)
16409b0b 5734 '<)))
eec82323
LMI
5735 articles)))
5736
5737(defun gnus-killed-articles (killed articles)
5738 (let (out)
5739 (while articles
5740 (when (inline (gnus-member-of-range (car articles) killed))
5741 (push (car articles) out))
5742 (setq articles (cdr articles)))
5743 out))
5744
5745(defun gnus-uncompress-marks (marks)
5746 "Uncompress the mark ranges in MARKS."
5747 (let ((uncompressed '(score bookmark))
5748 out)
5749 (while marks
5750 (if (memq (caar marks) uncompressed)
5751 (push (car marks) out)
5752 (push (cons (caar marks) (gnus-uncompress-range (cdar marks))) out))
5753 (setq marks (cdr marks)))
5754 out))
5755
23f87bed
MB
5756(defun gnus-article-mark-to-type (mark)
5757 "Return the type of MARK."
5758 (or (cadr (assq mark gnus-article-special-mark-lists))
5759 'list))
5760
5761(defun gnus-article-unpropagatable-p (mark)
5762 "Return whether MARK should be propagated to back end."
5763 (memq mark gnus-article-unpropagated-mark-lists))
5764
eec82323 5765(defun gnus-adjust-marked-articles (info)
16409b0b 5766 "Set all article lists and remove all marks that are no longer valid."
eec82323
LMI
5767 (let* ((marked-lists (gnus-info-marks info))
5768 (active (gnus-active (gnus-info-group info)))
5769 (min (car active))
5770 (max (cdr active))
5771 (types gnus-article-mark-lists)
54506618
MB
5772 marks var articles article mark mark-type
5773 bgn end)
eec82323 5774
23f87bed
MB
5775 (dolist (marks marked-lists)
5776 (setq mark (car marks)
5777 mark-type (gnus-article-mark-to-type mark)
5778 var (intern (format "gnus-newsgroup-%s" (car (rassq mark types)))))
eec82323 5779
23f87bed
MB
5780 ;; We set the variable according to the type of the marks list,
5781 ;; and then adjust the marks to a subset of the active articles.
eec82323 5782 (cond
54506618 5783 ;; Adjust "simple" lists - compressed yet unsorted
23f87bed 5784 ((eq mark-type 'list)
54506618
MB
5785 ;; Simultaneously uncompress and clip to active range
5786 ;; See gnus-uncompress-range for a description of possible marks
5787 (let (l lh)
5788 (if (not (cadr marks))
5789 (set var nil)
5790 (setq articles (if (numberp (cddr marks))
5791 (list (cdr marks))
5792 (cdr marks))
5793 lh (cons nil nil)
5794 l lh)
5795
5796 (while (setq article (pop articles))
5797 (cond ((consp article)
5798 (setq bgn (max (car article) min)
5799 end (min (cdr article) max))
5800 (while (<= bgn end)
5801 (setq l (setcdr l (cons bgn nil))
5802 bgn (1+ bgn))))
5803 ((and (<= min article)
5804 (>= max article))
5805 (setq l (setcdr l (cons article nil))))))
5806 (set var (cdr lh)))))
eec82323 5807 ;; Adjust assocs.
23f87bed
MB
5808 ((eq mark-type 'tuple)
5809 (set var (setq articles (cdr marks)))
a8151ef7
LMI
5810 (when (not (listp (cdr (symbol-value var))))
5811 (set var (list (symbol-value var))))
5812 (when (not (listp (cdr articles)))
5813 (setq articles (list articles)))
eec82323
LMI
5814 (while articles
5815 (when (or (not (consp (setq article (pop articles))))
5816 (< (car article) min)
5817 (> (car article) max))
23f87bed
MB
5818 (set var (delq article (symbol-value var))))))
5819 ;; Adjust ranges (sloppily).
5820 ((eq mark-type 'range)
5821 (cond
5822 ((eq mark 'seen)
5823 ;; Fix the record for `seen' if it looks like (seen NUM1 . NUM2).
5824 ;; It should be (seen (NUM1 . NUM2)).
5825 (when (numberp (cddr marks))
5826 (setcdr marks (list (cdr marks))))
5827 (setq articles (cdr marks))
5828 (while (and articles
5829 (or (and (consp (car articles))
5830 (> min (cdar articles)))
5831 (and (numberp (car articles))
5832 (> min (car articles)))))
5833 (pop articles))
5834 (set var articles))))))))
eec82323
LMI
5835
5836(defun gnus-update-missing-marks (missing)
6748645f 5837 "Go through the list of MISSING articles and remove them from the mark lists."
eec82323 5838 (when missing
23f87bed 5839 (let (var m)
eec82323 5840 ;; Go through all types.
23f87bed
MB
5841 (dolist (elem gnus-article-mark-lists)
5842 (when (eq (gnus-article-mark-to-type (cdr elem)) 'list)
5843 (setq var (intern (format "gnus-newsgroup-%s" (car elem))))
5844 (when (symbol-value var)
5845 ;; This list has articles. So we delete all missing
5846 ;; articles from it.
5847 (setq m missing)
5848 (while m
5849 (set var (delq (pop m) (symbol-value var))))))))))
eec82323
LMI
5850
5851(defun gnus-update-marks ()
5852 "Enter the various lists of marked articles into the newsgroup info list."
5853 (let ((types gnus-article-mark-lists)
5854 (info (gnus-get-info gnus-newsgroup-name))
16409b0b 5855 type list newmarked symbol delta-marks)
eec82323 5856 (when info
16409b0b 5857 ;; Add all marks lists to the list of marks lists.
eec82323 5858 (while (setq type (pop types))
16409b0b
GM
5859 (setq list (symbol-value
5860 (setq symbol
23f87bed 5861 (intern (format "gnus-newsgroup-%s" (car type))))))
eec82323 5862
16409b0b 5863 (when list
eec82323
LMI
5864 ;; Get rid of the entries of the articles that have the
5865 ;; default score.
5866 (when (and (eq (cdr type) 'score)
5867 gnus-save-score
5868 list)
5869 (let* ((arts list)
5870 (prev (cons nil list))
5871 (all prev))
5872 (while arts
5873 (if (or (not (consp (car arts)))
5874 (= (cdar arts) gnus-summary-default-score))
5875 (setcdr prev (cdr arts))
5876 (setq prev arts))
5877 (setq arts (cdr arts)))
16409b0b
GM
5878 (setq list (cdr all)))))
5879
23f87bed
MB
5880 (when (eq (cdr type) 'seen)
5881 (setq list (gnus-range-add list gnus-newsgroup-unseen)))
5882
5883 (when (eq (gnus-article-mark-to-type (cdr type)) 'list)
16409b0b 5884 (setq list (gnus-compress-sequence (set symbol (sort list '<)) t)))
a1506d29 5885
23f87bed
MB
5886 (when (and (gnus-check-backend-function
5887 'request-set-mark gnus-newsgroup-name)
5888 (not (gnus-article-unpropagatable-p (cdr type))))
5889 (let* ((old (cdr (assq (cdr type) (gnus-info-marks info))))
5890 (del (gnus-remove-from-range (gnus-copy-sequence old) list))
5891 (add (gnus-remove-from-range
5892 (gnus-copy-sequence list) old)))
5893 (when add
5894 (push (list add 'add (list (cdr type))) delta-marks))
5895 (when del
5896 (push (list del 'del (list (cdr type))) delta-marks))))
a1506d29 5897
16409b0b
GM
5898 (when list
5899 (push (cons (cdr type) list) newmarked)))
5900
5901 (when delta-marks
5902 (unless (gnus-check-group gnus-newsgroup-name)
5903 (error "Can't open server for %s" gnus-newsgroup-name))
5904 (gnus-request-set-mark gnus-newsgroup-name delta-marks))
a1506d29 5905
eec82323
LMI
5906 ;; Enter these new marks into the info of the group.
5907 (if (nthcdr 3 info)
5908 (setcar (nthcdr 3 info) newmarked)
5909 ;; Add the marks lists to the end of the info.
5910 (when newmarked
5911 (setcdr (nthcdr 2 info) (list newmarked))))
5912
5913 ;; Cut off the end of the info if there's nothing else there.
5914 (let ((i 5))
5915 (while (and (> i 2)
5916 (not (nth i info)))
5917 (when (nthcdr (decf i) info)
5918 (setcdr (nthcdr i info) nil)))))))
5919
5920(defun gnus-set-mode-line (where)
16409b0b 5921 "Set the mode line of the article or summary buffers.
eec82323
LMI
5922If WHERE is `summary', the summary mode line format will be used."
5923 ;; Is this mode line one we keep updated?
16409b0b
GM
5924 (when (and (memq where gnus-updated-mode-lines)
5925 (symbol-value
5926 (intern (format "gnus-%s-mode-line-format-spec" where))))
eec82323
LMI
5927 (let (mode-string)
5928 (save-excursion
5929 ;; We evaluate this in the summary buffer since these
5930 ;; variables are buffer-local to that buffer.
5931 (set-buffer gnus-summary-buffer)
23f87bed 5932 ;; We bind all these variables that are used in the `eval' form
eec82323
LMI
5933 ;; below.
5934 (let* ((mformat (symbol-value
5935 (intern
5936 (format "gnus-%s-mode-line-format-spec" where))))
b90a6149
MB
5937 (gnus-tmp-group-name (gnus-mode-string-quote
5938 (gnus-group-decoded-name
5939 gnus-newsgroup-name)))
eec82323
LMI
5940 (gnus-tmp-article-number (or gnus-current-article 0))
5941 (gnus-tmp-unread gnus-newsgroup-unreads)
5942 (gnus-tmp-unread-and-unticked (length gnus-newsgroup-unreads))
5943 (gnus-tmp-unselected (length gnus-newsgroup-unselected))
5944 (gnus-tmp-unread-and-unselected
5945 (cond ((and (zerop gnus-tmp-unread-and-unticked)
5946 (zerop gnus-tmp-unselected))
5947 "")
5948 ((zerop gnus-tmp-unselected)
5949 (format "{%d more}" gnus-tmp-unread-and-unticked))
5950 (t (format "{%d(+%d) more}"
5951 gnus-tmp-unread-and-unticked
5952 gnus-tmp-unselected))))
5953 (gnus-tmp-subject
5954 (if (and gnus-current-headers
5955 (vectorp gnus-current-headers))
5956 (gnus-mode-string-quote
5957 (mail-header-subject gnus-current-headers))
5958 ""))
5959 bufname-length max-len
23f87bed 5960 gnus-tmp-header) ;; passed as argument to any user-format-funcs
eec82323
LMI
5961 (setq mode-string (eval mformat))
5962 (setq bufname-length (if (string-match "%b" mode-string)
5963 (- (length
5964 (buffer-name
5965 (if (eq where 'summary)
5966 nil
5967 (get-buffer gnus-article-buffer))))
5968 2)
5969 0))
5970 (setq max-len (max 4 (if gnus-mode-non-string-length
5971 (- (window-width)
5972 gnus-mode-non-string-length
5973 bufname-length)
5974 (length mode-string))))
5975 ;; We might have to chop a bit of the string off...
5976 (when (> (length mode-string) max-len)
5977 (setq mode-string
16409b0b 5978 (concat (truncate-string-to-width mode-string (- max-len 3))
eec82323
LMI
5979 "...")))
5980 ;; Pad the mode string a bit.
5981 (setq mode-string (format (format "%%-%ds" max-len) mode-string))))
5982 ;; Update the mode line.
5983 (setq mode-line-buffer-identification
5984 (gnus-mode-line-buffer-identification (list mode-string)))
5985 (set-buffer-modified-p t))))
5986
5987(defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
5988 "Go through the HEADERS list and add all Xrefs to a hash table.
5989The resulting hash table is returned, or nil if no Xrefs were found."
5990 (let* ((virtual (gnus-virtual-group-p from-newsgroup))
5991 (prefix (if virtual "" (gnus-group-real-prefix from-newsgroup)))
5992 (xref-hashtb (gnus-make-hashtable))
5993 start group entry number xrefs header)
5994 (while headers
5995 (setq header (pop headers))
5996 (when (and (setq xrefs (mail-header-xref header))
5997 (not (memq (setq number (mail-header-number header))
5998 unreads)))
5999 (setq start 0)
6000 (while (string-match "\\([^ ]+\\)[:/]\\([0-9]+\\)" xrefs start)
6001 (setq start (match-end 0))
6002 (setq group (if prefix
6003 (concat prefix (substring xrefs (match-beginning 1)
6004 (match-end 1)))
6005 (substring xrefs (match-beginning 1) (match-end 1))))
6006 (setq number
e9bd5782 6007 (string-to-number (substring xrefs (match-beginning 2)
eec82323
LMI
6008 (match-end 2))))
6009 (if (setq entry (gnus-gethash group xref-hashtb))
6010 (setcdr entry (cons number (cdr entry)))
6011 (gnus-sethash group (cons number nil) xref-hashtb)))))
6012 (and start xref-hashtb)))
6013
6014(defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads)
6015 "Look through all the headers and mark the Xrefs as read."
6016 (let ((virtual (gnus-virtual-group-p from-newsgroup))
01c52d31 6017 name info xref-hashtb idlist method nth4)
eec82323
LMI
6018 (save-excursion
6019 (set-buffer gnus-group-buffer)
6020 (when (setq xref-hashtb
6021 (gnus-create-xref-hashtb from-newsgroup headers unreads))
6022 (mapatoms
6023 (lambda (group)
6024 (unless (string= from-newsgroup (setq name (symbol-name group)))
6025 (setq idlist (symbol-value group))
6026 ;; Dead groups are not updated.
6027 (and (prog1
01c52d31 6028 (setq info (gnus-get-info name))
eec82323
LMI
6029 (when (stringp (setq nth4 (gnus-info-method info)))
6030 (setq nth4 (gnus-server-to-method nth4))))
6031 ;; Only do the xrefs if the group has the same
6032 ;; select method as the group we have just read.
6033 (or (gnus-methods-equal-p
6034 nth4 (gnus-find-method-for-group from-newsgroup))
6035 virtual
6036 (equal nth4 (setq method (gnus-find-method-for-group
6037 from-newsgroup)))
6038 (and (equal (car nth4) (car method))
6039 (equal (nth 1 nth4) (nth 1 method))))
6040 gnus-use-cross-reference
6041 (or (not (eq gnus-use-cross-reference t))
6042 virtual
6043 ;; Only do cross-references on subscribed
6044 ;; groups, if that is what is wanted.
6045 (<= (gnus-info-level info) gnus-level-subscribed))
6046 (gnus-group-make-articles-read name idlist))))
6047 xref-hashtb)))))
6048
6748645f 6049(defun gnus-compute-read-articles (group articles)
01c52d31 6050 (let* ((entry (gnus-group-entry group))
6748645f
LMI
6051 (info (nth 2 entry))
6052 (active (gnus-active group))
6053 ninfo)
6054 (when entry
16409b0b 6055 ;; First peel off all invalid article numbers.
6748645f
LMI
6056 (when active
6057 (let ((ids articles)
6058 id first)
6059 (while (setq id (pop ids))
6060 (when (and first (> id (cdr active)))
6061 ;; We'll end up in this situation in one particular
6062 ;; obscure situation. If you re-scan a group and get
6063 ;; a new article that is cross-posted to a different
6064 ;; group that has not been re-scanned, you might get
6065 ;; crossposted article that has a higher number than
6066 ;; Gnus believes possible. So we re-activate this
6067 ;; group as well. This might mean doing the
6068 ;; crossposting thingy will *increase* the number
6069 ;; of articles in some groups. Tsk, tsk.
6070 (setq active (or (gnus-activate-group group) active)))
6071 (when (or (> id (cdr active))
6072 (< id (car active)))
6073 (setq articles (delq id articles))))))
6074 ;; If the read list is nil, we init it.
6075 (if (and active
6076 (null (gnus-info-read info))
6077 (> (car active) 1))
6078 (setq ninfo (cons 1 (1- (car active))))
6079 (setq ninfo (gnus-info-read info)))
6080 ;; Then we add the read articles to the range.
6081 (gnus-add-to-range
6082 ninfo (setq articles (sort articles '<))))))
6083
eec82323
LMI
6084(defun gnus-group-make-articles-read (group articles)
6085 "Update the info of GROUP to say that ARTICLES are read."
6086 (let* ((num 0)
01c52d31 6087 (entry (gnus-group-entry group))
eec82323
LMI
6088 (info (nth 2 entry))
6089 (active (gnus-active group))
6090 range)
6748645f
LMI
6091 (when entry
6092 (setq range (gnus-compute-read-articles group articles))
01c52d31 6093 (with-current-buffer gnus-group-buffer
6748645f
LMI
6094 (gnus-undo-register
6095 `(progn
6096 (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
6097 (gnus-info-set-read ',info ',(gnus-info-read info))
6098 (gnus-get-unread-articles-in-group ',info (gnus-active ,group))
23f87bed 6099 (gnus-request-set-mark ,group (list (list ',range 'del '(read))))
6748645f
LMI
6100 (gnus-group-update-group ,group t))))
6101 ;; Add the read articles to the range.
6102 (gnus-info-set-read info range)
23f87bed 6103 (gnus-request-set-mark group (list (list range 'add '(read))))
6748645f
LMI
6104 ;; Then we have to re-compute how many unread
6105 ;; articles there are in this group.
6106 (when active
6107 (cond
6108 ((not range)
6109 (setq num (- (1+ (cdr active)) (car active))))
6110 ((not (listp (cdr range)))
6111 (setq num (- (cdr active) (- (1+ (cdr range))
6112 (car range)))))
6113 (t
6114 (while range
6115 (if (numberp (car range))
6116 (setq num (1+ num))
6117 (setq num (+ num (- (1+ (cdar range)) (caar range)))))
6118 (setq range (cdr range)))
6119 (setq num (- (cdr active) num))))
6120 ;; Update the number of unread articles.
6121 (setcar entry num)
6122 ;; Update the group buffer.
23f87bed
MB
6123 (unless (gnus-ephemeral-group-p group)
6124 (gnus-group-update-group group t))))))
eec82323 6125
eec82323
LMI
6126(defvar gnus-newsgroup-none-id 0)
6127
6128(defun gnus-get-newsgroup-headers (&optional dependencies force-new)
6129 (let ((cur nntp-server-buffer)
6130 (dependencies
6131 (or dependencies
01c52d31
MB
6132 (with-current-buffer gnus-summary-buffer
6133 gnus-newsgroup-dependencies)))
6134 headers id end ref number
16409b0b
GM
6135 (mail-parse-charset gnus-newsgroup-charset)
6136 (mail-parse-ignored-charsets
6137 (save-excursion (condition-case nil
6138 (set-buffer gnus-summary-buffer)
6139 (error))
6140 gnus-newsgroup-ignored-charsets)))
eec82323
LMI
6141 (save-excursion
6142 (set-buffer nntp-server-buffer)
6143 ;; Translate all TAB characters into SPACE characters.
6144 (subst-char-in-region (point-min) (point-max) ?\t ? t)
16409b0b 6145 (subst-char-in-region (point-min) (point-max) ?\r ? t)
23f87bed 6146 (ietf-drums-unfold-fws)
6748645f 6147 (gnus-run-hooks 'gnus-parse-headers-hook)
eec82323 6148 (let ((case-fold-search t)
6748645f 6149 in-reply-to header p lines chars)
eec82323 6150 (goto-char (point-min))
01ccbb85 6151 ;; Search to the beginning of the next header. Error messages
eec82323
LMI
6152 ;; do not begin with 2 or 3.
6153 (while (re-search-forward "^[23][0-9]+ " nil t)
6154 (setq id nil
6155 ref nil)
6156 ;; This implementation of this function, with nine
6157 ;; search-forwards instead of the one re-search-forward and
6158 ;; a case (which basically was the old function) is actually
01ccbb85 6159 ;; about twice as fast, even though it looks messier. You
eec82323
LMI
6160 ;; can't have everything, I guess. Speed and elegance
6161 ;; doesn't always go hand in hand.
6162 (setq
6163 header
6164 (vector
6165 ;; Number.
6166 (prog1
01c52d31 6167 (setq number (read cur))
eec82323
LMI
6168 (end-of-line)
6169 (setq p (point))
6170 (narrow-to-region (point)
6171 (or (and (search-forward "\n.\n" nil t)
6172 (- (point) 2))
6173 (point))))
6174 ;; Subject.
6175 (progn
6176 (goto-char p)
23f87bed 6177 (if (search-forward "\nsubject:" nil t)
16409b0b
GM
6178 (funcall gnus-decode-encoded-word-function
6179 (nnheader-header-value))
2bd3dcae 6180 "(none)"))
eec82323
LMI
6181 ;; From.
6182 (progn
6183 (goto-char p)
23f87bed 6184 (if (search-forward "\nfrom:" nil t)
343d6628 6185 (funcall gnus-decode-encoded-address-function
16409b0b 6186 (nnheader-header-value))
2bd3dcae 6187 "(nobody)"))
eec82323
LMI
6188 ;; Date.
6189 (progn
6190 (goto-char p)
23f87bed 6191 (if (search-forward "\ndate:" nil t)
eec82323
LMI
6192 (nnheader-header-value) ""))
6193 ;; Message-ID.
6194 (progn
6195 (goto-char p)
6748645f
LMI
6196 (setq id (if (re-search-forward
6197 "^message-id: *\\(<[^\n\t> ]+>\\)" nil t)
6198 ;; We do it this way to make sure the Message-ID
6199 ;; is (somewhat) syntactically valid.
6200 (buffer-substring (match-beginning 1)
6201 (match-end 1))
eec82323
LMI
6202 ;; If there was no message-id, we just fake one
6203 ;; to make subsequent routines simpler.
01c52d31 6204 (nnheader-generate-fake-message-id number))))
eec82323
LMI
6205 ;; References.
6206 (progn
6207 (goto-char p)
23f87bed 6208 (if (search-forward "\nreferences:" nil t)
eec82323
LMI
6209 (progn
6210 (setq end (point))
6211 (prog1
6212 (nnheader-header-value)
6213 (setq ref
6214 (buffer-substring
6215 (progn
6216 (end-of-line)
6217 (search-backward ">" end t)
6218 (1+ (point)))
6219 (progn
6220 (search-backward "<" end t)
6221 (point))))))
6222 ;; Get the references from the in-reply-to header if there
6223 ;; were no references and the in-reply-to header looks
6224 ;; promising.
23f87bed 6225 (if (and (search-forward "\nin-reply-to:" nil t)
eec82323
LMI
6226 (setq in-reply-to (nnheader-header-value))
6227 (string-match "<[^>]+>" in-reply-to))
6748645f
LMI
6228 (let (ref2)
6229 (setq ref (substring in-reply-to (match-beginning 0)
6230 (match-end 0)))
6231 (while (string-match "<[^>]+>" in-reply-to (match-end 0))
6232 (setq ref2 (substring in-reply-to (match-beginning 0)
6233 (match-end 0)))
6234 (when (> (length ref2) (length ref))
6235 (setq ref ref2)))
6236 ref)
eec82323
LMI
6237 (setq ref nil))))
6238 ;; Chars.
6748645f
LMI
6239 (progn
6240 (goto-char p)
6241 (if (search-forward "\nchars: " nil t)
6242 (if (numberp (setq chars (ignore-errors (read cur))))
23f87bed
MB
6243 chars -1)
6244 -1))
eec82323
LMI
6245 ;; Lines.
6246 (progn
6247 (goto-char p)
6248 (if (search-forward "\nlines: " nil t)
a8151ef7 6249 (if (numberp (setq lines (ignore-errors (read cur))))
23f87bed
MB
6250 lines -1)
6251 -1))
eec82323
LMI
6252 ;; Xref.
6253 (progn
6254 (goto-char p)
23f87bed 6255 (and (search-forward "\nxref:" nil t)
16409b0b
GM
6256 (nnheader-header-value)))
6257 ;; Extra.
6258 (when gnus-extra-headers
6259 (let ((extra gnus-extra-headers)
6260 out)
6261 (while extra
6262 (goto-char p)
6263 (when (search-forward
23f87bed 6264 (concat "\n" (symbol-name (car extra)) ":") nil t)
16409b0b
GM
6265 (push (cons (car extra) (nnheader-header-value))
6266 out))
6267 (pop extra))
6268 out))))
eec82323
LMI
6269 (when (equal id ref)
6270 (setq ref nil))
6748645f
LMI
6271
6272 (when gnus-alter-header-function
6273 (funcall gnus-alter-header-function header)
6274 (setq id (mail-header-id header)
6275 ref (gnus-parent-id (mail-header-references header))))
6276
6277 (when (setq header
6278 (gnus-dependencies-add-header
6279 header dependencies force-new))
eec82323
LMI
6280 (push header headers))
6281 (goto-char (point-max))
6282 (widen))
6283 (nreverse headers)))))
6284
eec82323
LMI
6285;; Goes through the xover lines and returns a list of vectors
6286(defun gnus-get-newsgroup-headers-xover (sequence &optional
6287 force-new dependencies
6288 group also-fetch-heads)
16409b0b
GM
6289 "Parse the news overview data in the server buffer.
6290Return a list of headers that match SEQUENCE (see
6291`nntp-retrieve-headers')."
eec82323
LMI
6292 ;; Get the Xref when the users reads the articles since most/some
6293 ;; NNTP servers do not include Xrefs when using XOVER.
6294 (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs))
16409b0b
GM
6295 (let ((mail-parse-charset gnus-newsgroup-charset)
6296 (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
6297 (cur nntp-server-buffer)
eec82323 6298 (dependencies (or dependencies gnus-newsgroup-dependencies))
23f87bed
MB
6299 (allp (cond
6300 ((eq gnus-read-all-available-headers t)
6301 t)
14e6dc54
MB
6302 ((and (stringp gnus-read-all-available-headers)
6303 group)
23f87bed
MB
6304 (string-match gnus-read-all-available-headers group))
6305 (t
6306 nil)))
eec82323
LMI
6307 number headers header)
6308 (save-excursion
6309 (set-buffer nntp-server-buffer)
16409b0b 6310 (subst-char-in-region (point-min) (point-max) ?\r ? t)
eec82323 6311 ;; Allow the user to mangle the headers before parsing them.
6748645f 6312 (gnus-run-hooks 'gnus-parse-headers-hook)
eec82323 6313 (goto-char (point-min))
23f87bed
MB
6314 (gnus-parse-without-error
6315 (while (and (or sequence allp)
6316 (not (eobp)))
6317 (setq number (read cur))
6318 (when (not allp)
6319 (while (and sequence
6320 (< (car sequence) number))
6321 (setq sequence (cdr sequence))))
6322 (when (and (or allp
6323 (and sequence
6324 (eq number (car sequence))))
6325 (progn
6326 (setq sequence (cdr sequence))
6327 (setq header (inline
6328 (gnus-nov-parse-line
6329 number dependencies force-new)))))
6330 (push header headers))
6331 (forward-line 1)))
eec82323
LMI
6332 ;; A common bug in inn is that if you have posted an article and
6333 ;; then retrieves the active file, it will answer correctly --
6334 ;; the new article is included. However, a NOV entry for the
6335 ;; article may not have been generated yet, so this may fail.
6336 ;; We work around this problem by retrieving the last few
6337 ;; headers using HEAD.
6338 (if (or (not also-fetch-heads)
6339 (not sequence))
6340 ;; We (probably) got all the headers.
6341 (nreverse headers)
6342 (let ((gnus-nov-is-evil t))
6343 (nconc
6344 (nreverse headers)
23f87bed 6345 (when (eq (gnus-retrieve-headers sequence group) 'headers)
eec82323
LMI
6346 (gnus-get-newsgroup-headers))))))))
6347
6348(defun gnus-article-get-xrefs ()
6349 "Fill in the Xref value in `gnus-current-headers', if necessary.
6350This is meant to be called in `gnus-article-internal-prepare-hook'."
01c52d31
MB
6351 (let ((headers (with-current-buffer gnus-summary-buffer
6352 gnus-current-headers)))
eec82323
LMI
6353 (or (not gnus-use-cross-reference)
6354 (not headers)
6355 (and (mail-header-xref headers)
6356 (not (string= (mail-header-xref headers) "")))
6357 (let ((case-fold-search t)
6358 xref)
6359 (save-restriction
6360 (nnheader-narrow-to-headers)
6361 (goto-char (point-min))
16409b0b
GM
6362 (when (or (and (not (eobp))
6363 (eq (downcase (char-after)) ?x)
eec82323
LMI
6364 (looking-at "Xref:"))
6365 (search-forward "\nXref:" nil t))
6366 (goto-char (1+ (match-end 0)))
01c52d31 6367 (setq xref (buffer-substring (point) (point-at-eol)))
eec82323
LMI
6368 (mail-header-set-xref headers xref)))))))
6369
6370(defun gnus-summary-insert-subject (id &optional old-header use-old-header)
6748645f
LMI
6371 "Find article ID and insert the summary line for that article.
6372OLD-HEADER can either be a header or a line number to insert
6373the subject line on."
6374 (let* ((line (and (numberp old-header) old-header))
6375 (old-header (and (vectorp old-header) old-header))
6376 (header (cond ((and old-header use-old-header)
16409b0b
GM
6377 old-header)
6378 ((and (numberp id)
6379 (gnus-number-to-header id))
6380 (gnus-number-to-header id))
6381 (t
6382 (gnus-read-header id))))
6383 (number (and (numberp id) id))
6384 d)
eec82323
LMI
6385 (when header
6386 ;; Rebuild the thread that this article is part of and go to the
6387 ;; article we have fetched.
6388 (when (and (not gnus-show-threads)
6389 old-header)
6748645f
LMI
6390 (when (and number
6391 (setq d (gnus-data-find (mail-header-number old-header))))
eec82323
LMI
6392 (goto-char (gnus-data-pos d))
6393 (gnus-data-remove
6394 number
01c52d31 6395 (- (point-at-bol)
eec82323 6396 (prog1
01c52d31 6397 (1+ (point-at-eol))
eec82323 6398 (gnus-delete-line))))))
23f87bed
MB
6399 ;; Remove list identifiers from subject.
6400 (when gnus-list-identifiers
6401 (let ((gnus-newsgroup-headers (list header)))
c3bc41c2 6402 (gnus-summary-remove-list-identifiers)))
eec82323
LMI
6403 (when old-header
6404 (mail-header-set-number header (mail-header-number old-header)))
6405 (setq gnus-newsgroup-sparse
6406 (delq (setq number (mail-header-number header))
6407 gnus-newsgroup-sparse))
6408 (setq gnus-newsgroup-ancient (delq number gnus-newsgroup-ancient))
6748645f
LMI
6409 (push number gnus-newsgroup-limit)
6410 (gnus-rebuild-thread (mail-header-id header) line)
eec82323
LMI
6411 (gnus-summary-goto-subject number nil t))
6412 (when (and (numberp number)
6413 (> number 0))
6414 ;; We have to update the boundaries even if we can't fetch the
6415 ;; article if ID is a number -- so that the next `P' or `N'
6416 ;; command will fetch the previous (or next) article even
6417 ;; if the one we tried to fetch this time has been canceled.
6418 (when (> number gnus-newsgroup-end)
6419 (setq gnus-newsgroup-end number))
6420 (when (< number gnus-newsgroup-begin)
6421 (setq gnus-newsgroup-begin number))
6422 (setq gnus-newsgroup-unselected
6423 (delq number gnus-newsgroup-unselected)))
6424 ;; Report back a success?
6425 (and header (mail-header-number header))))
6426
6427;;; Process/prefix in the summary buffer
6428
6429(defun gnus-summary-work-articles (n)
6748645f
LMI
6430 "Return a list of articles to be worked upon.
6431The prefix argument, the list of process marked articles, and the
6432current article will be taken into consideration."
6433 (save-excursion
6434 (set-buffer gnus-summary-buffer)
6435 (cond
6436 (n
6437 ;; A numerical prefix has been given.
6438 (setq n (prefix-numeric-value n))
6439 (let ((backward (< n 0))
6440 (n (abs (prefix-numeric-value n)))
6441 articles article)
6442 (save-excursion
6443 (while
6444 (and (> n 0)
6445 (push (setq article (gnus-summary-article-number))
6446 articles)
6447 (if backward
6448 (gnus-summary-find-prev nil article)
6449 (gnus-summary-find-next nil article)))
6450 (decf n)))
6451 (nreverse articles)))
6452 ((and (gnus-region-active-p) (mark))
6453 (message "region active")
6454 ;; Work on the region between point and mark.
6455 (let ((max (max (point) (mark)))
6456 articles article)
6457 (save-excursion
7dafe00b 6458 (goto-char (min (point) (mark)))
6748645f
LMI
6459 (while
6460 (and
6461 (push (setq article (gnus-summary-article-number)) articles)
6462 (gnus-summary-find-next nil article)
6463 (< (point) max)))
6464 (nreverse articles))))
6465 (gnus-newsgroup-processable
6466 ;; There are process-marked articles present.
6467 ;; Save current state.
6468 (gnus-summary-save-process-mark)
6469 ;; Return the list.
6470 (reverse gnus-newsgroup-processable))
6471 (t
6472 ;; Just return the current article.
6473 (list (gnus-summary-article-number))))))
6474
6475(defmacro gnus-summary-iterate (arg &rest forms)
6476 "Iterate over the process/prefixed articles and do FORMS.
6477ARG is the interactive prefix given to the command. FORMS will be
6478executed with point over the summary line of the articles."
6479 (let ((articles (make-symbol "gnus-summary-iterate-articles")))
6480 `(let ((,articles (gnus-summary-work-articles ,arg)))
6481 (while ,articles
6482 (gnus-summary-goto-subject (car ,articles))
16409b0b
GM
6483 ,@forms
6484 (pop ,articles)))))
6748645f
LMI
6485
6486(put 'gnus-summary-iterate 'lisp-indent-function 1)
6487(put 'gnus-summary-iterate 'edebug-form-spec '(form body))
eec82323
LMI
6488
6489(defun gnus-summary-save-process-mark ()
6490 "Push the current set of process marked articles on the stack."
6491 (interactive)
6492 (push (copy-sequence gnus-newsgroup-processable)
6493 gnus-newsgroup-process-stack))
6494
6495(defun gnus-summary-kill-process-mark ()
6496 "Push the current set of process marked articles on the stack and unmark."
6497 (interactive)
6498 (gnus-summary-save-process-mark)
6499 (gnus-summary-unmark-all-processable))
6500
6501(defun gnus-summary-yank-process-mark ()
6502 "Pop the last process mark state off the stack and restore it."
6503 (interactive)
6504 (unless gnus-newsgroup-process-stack
6505 (error "Empty mark stack"))
6506 (gnus-summary-process-mark-set (pop gnus-newsgroup-process-stack)))
6507
6508(defun gnus-summary-process-mark-set (set)
6509 "Make SET into the current process marked articles."
6510 (gnus-summary-unmark-all-processable)
01c52d31 6511 (mapc 'gnus-summary-set-process-mark set))
eec82323
LMI
6512
6513;;; Searching and stuff
6514
6515(defun gnus-summary-search-group (&optional backward use-level)
6516 "Search for next unread newsgroup.
6517If optional argument BACKWARD is non-nil, search backward instead."
6518 (save-excursion
6519 (set-buffer gnus-group-buffer)
6520 (when (gnus-group-search-forward
6521 backward nil (if use-level (gnus-group-group-level) nil))
6522 (gnus-group-group-name))))
6523
6524(defun gnus-summary-best-group (&optional exclude-group)
6525 "Find the name of the best unread group.
6526If EXCLUDE-GROUP, do not go to this group."
01c52d31 6527 (with-current-buffer gnus-group-buffer
eec82323
LMI
6528 (save-excursion
6529 (gnus-group-best-unread-group exclude-group))))
6530
23f87bed
MB
6531(defun gnus-summary-find-next (&optional unread article backward)
6532 (if backward
6533 (gnus-summary-find-prev unread article)
eec82323
LMI
6534 (let* ((dummy (gnus-summary-article-intangible-p))
6535 (article (or article (gnus-summary-article-number)))
23f87bed 6536 (data (gnus-data-find-list article))
eec82323
LMI
6537 result)
6538 (when (and (not dummy)
6539 (or (not gnus-summary-check-current)
6540 (not unread)
23f87bed
MB
6541 (not (gnus-data-unread-p (car data)))))
6542 (setq data (cdr data)))
eec82323
LMI
6543 (when (setq result
6544 (if unread
6545 (progn
23f87bed
MB
6546 (while data
6547 (unless (memq (gnus-data-number (car data))
6548 (cond
6549 ((eq gnus-auto-goto-ignores
6550 'always-undownloaded)
6551 gnus-newsgroup-undownloaded)
6552 (gnus-plugged
6553 nil)
6554 ((eq gnus-auto-goto-ignores
6555 'unfetched)
6556 gnus-newsgroup-unfetched)
6557 ((eq gnus-auto-goto-ignores
6558 'undownloaded)
6559 gnus-newsgroup-undownloaded)))
6560 (when (gnus-data-unread-p (car data))
6561 (setq result (car data)
6562 data nil)))
6563 (setq data (cdr data)))
eec82323 6564 result)
23f87bed 6565 (car data)))
eec82323
LMI
6566 (goto-char (gnus-data-pos result))
6567 (gnus-data-number result)))))
6568
6569(defun gnus-summary-find-prev (&optional unread article)
6570 (let* ((eobp (eobp))
6571 (article (or article (gnus-summary-article-number)))
23f87bed 6572 (data (gnus-data-find-list article (gnus-data-list 'rev)))
eec82323
LMI
6573 result)
6574 (when (and (not eobp)
6575 (or (not gnus-summary-check-current)
6576 (not unread)
23f87bed
MB
6577 (not (gnus-data-unread-p (car data)))))
6578 (setq data (cdr data)))
eec82323
LMI
6579 (when (setq result
6580 (if unread
6581 (progn
23f87bed
MB
6582 (while data
6583 (unless (memq (gnus-data-number (car data))
6584 (cond
6585 ((eq gnus-auto-goto-ignores
6586 'always-undownloaded)
6587 gnus-newsgroup-undownloaded)
6588 (gnus-plugged
6589 nil)
6590 ((eq gnus-auto-goto-ignores
6591 'unfetched)
6592 gnus-newsgroup-unfetched)
6593 ((eq gnus-auto-goto-ignores
6594 'undownloaded)
6595 gnus-newsgroup-undownloaded)))
6596 (when (gnus-data-unread-p (car data))
6597 (setq result (car data)
6598 data nil)))
6599 (setq data (cdr data)))
eec82323 6600 result)
23f87bed 6601 (car data)))
eec82323
LMI
6602 (goto-char (gnus-data-pos result))
6603 (gnus-data-number result))))
6604
6605(defun gnus-summary-find-subject (subject &optional unread backward article)
6606 (let* ((simp-subject (gnus-simplify-subject-fully subject))
6607 (article (or article (gnus-summary-article-number)))
6608 (articles (gnus-data-list backward))
6609 (arts (gnus-data-find-list article articles))
6610 result)
6611 (when (or (not gnus-summary-check-current)
6612 (not unread)
6613 (not (gnus-data-unread-p (car arts))))
6614 (setq arts (cdr arts)))
6615 (while arts
6616 (and (or (not unread)
6617 (gnus-data-unread-p (car arts)))
6618 (vectorp (gnus-data-header (car arts)))
6619 (gnus-subject-equal
6620 simp-subject (mail-header-subject (gnus-data-header (car arts))) t)
6621 (setq result (car arts)
6622 arts nil))
6623 (setq arts (cdr arts)))
6624 (and result
6625 (goto-char (gnus-data-pos result))
6626 (gnus-data-number result))))
6627
6628(defun gnus-summary-search-forward (&optional unread subject backward)
6629 "Search forward for an article.
6630If UNREAD, look for unread articles. If SUBJECT, look for
6631articles with that subject. If BACKWARD, search backward instead."
6632 (cond (subject (gnus-summary-find-subject subject unread backward))
6633 (backward (gnus-summary-find-prev unread))
6634 (t (gnus-summary-find-next unread))))
6635
6636(defun gnus-recenter (&optional n)
6637 "Center point in window and redisplay frame.
6638Also do horizontal recentering."
6639 (interactive "P")
6640 (when (and gnus-auto-center-summary
6641 (not (eq gnus-auto-center-summary 'vertical)))
6642 (gnus-horizontal-recenter))
6643 (recenter n))
6644
6645(defun gnus-summary-recenter ()
6646 "Center point in the summary window.
6647If `gnus-auto-center-summary' is nil, or the article buffer isn't
6648displayed, no centering will be performed."
6649 ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
6650 ;; Recenter only when requested. Suggested by popovich@park.cs.columbia.edu.
16409b0b 6651 (interactive)
23f87bed
MB
6652 ;; The user has to want it.
6653 (when gnus-auto-center-summary
6654 (let* ((top (cond ((< (window-height) 4) 0)
6655 ((< (window-height) 7) 1)
6656 (t (if (numberp gnus-auto-center-summary)
6657 gnus-auto-center-summary
01c52d31 6658 (/ (1- (window-height)) 2)))))
23f87bed
MB
6659 (height (1- (window-height)))
6660 (bottom (save-excursion (goto-char (point-max))
6661 (forward-line (- height))
6662 (point)))
6663 (window (get-buffer-window (current-buffer))))
eec82323
LMI
6664 (when (get-buffer-window gnus-article-buffer)
6665 ;; Only do recentering when the article buffer is displayed,
6666 ;; Set the window start to either `bottom', which is the biggest
6667 ;; possible valid number, or the second line from the top,
6668 ;; whichever is the least.
db7ebd73
MB
6669 (let ((top-pos (save-excursion (forward-line (- top)) (point))))
6670 (if (> bottom top-pos)
6671 ;; Keep the second line from the top visible
01c52d31 6672 (set-window-start window top-pos)
db7ebd73
MB
6673 ;; Try to keep the bottom line visible; if it's partially
6674 ;; obscured, either scroll one more line to make it fully
6675 ;; visible, or revert to using TOP-POS.
6676 (save-excursion
6677 (goto-char (point-max))
6678 (forward-line -1)
6679 (let ((last-line-start (point)))
6680 (goto-char bottom)
6681 (set-window-start window (point) t)
6682 (when (not (pos-visible-in-window-p last-line-start window))
6683 (forward-line 1)
6684 (set-window-start window (min (point) top-pos) t)))))))
eec82323
LMI
6685 ;; Do horizontal recentering while we're at it.
6686 (when (and (get-buffer-window (current-buffer) t)
6687 (not (eq gnus-auto-center-summary 'vertical)))
6688 (let ((selected (selected-window)))
6689 (select-window (get-buffer-window (current-buffer) t))
6690 (gnus-summary-position-point)
6691 (gnus-horizontal-recenter)
6692 (select-window selected))))))
6693
6694(defun gnus-summary-jump-to-group (newsgroup)
6695 "Move point to NEWSGROUP in group mode buffer."
6696 ;; Keep update point of group mode buffer if visible.
6697 (if (eq (current-buffer) (get-buffer gnus-group-buffer))
6698 (save-window-excursion
6699 ;; Take care of tree window mode.
6700 (when (get-buffer-window gnus-group-buffer)
6701 (pop-to-buffer gnus-group-buffer))
6702 (gnus-group-jump-to-group newsgroup))
6703 (save-excursion
6704 ;; Take care of tree window mode.
6705 (if (get-buffer-window gnus-group-buffer)
6706 (pop-to-buffer gnus-group-buffer)
6707 (set-buffer gnus-group-buffer))
6708 (gnus-group-jump-to-group newsgroup))))
6709
6710;; This function returns a list of article numbers based on the
6711;; difference between the ranges of read articles in this group and
6712;; the range of active articles.
6713(defun gnus-list-of-unread-articles (group)
6714 (let* ((read (gnus-info-read (gnus-get-info group)))
6715 (active (or (gnus-active group) (gnus-activate-group group)))
01c52d31
MB
6716 (last (or (cdr active)
6717 (error "Group %s couldn't be activated " group)))
4b70e299
MB
6718 (bottom (if gnus-newsgroup-maximum-articles
6719 (max (car active)
6720 (- last gnus-newsgroup-maximum-articles -1))
11abff8e 6721 (car active)))
eec82323
LMI
6722 first nlast unread)
6723 ;; If none are read, then all are unread.
6724 (if (not read)
11abff8e 6725 (setq first bottom)
eec82323
LMI
6726 ;; If the range of read articles is a single range, then the
6727 ;; first unread article is the article after the last read
6728 ;; article. Sounds logical, doesn't it?
16409b0b 6729 (if (and (not (listp (cdr read)))
11abff8e 6730 (or (< (car read) bottom)
16409b0b
GM
6731 (progn (setq read (list read))
6732 nil)))
11abff8e 6733 (setq first (max bottom (1+ (cdr read))))
eec82323
LMI
6734 ;; `read' is a list of ranges.
6735 (when (/= (setq nlast (or (and (numberp (car read)) (car read))
6736 (caar read)))
6737 1)
11abff8e 6738 (setq first bottom))
eec82323
LMI
6739 (while read
6740 (when first
6741 (while (< first nlast)
54506618
MB
6742 (setq unread (cons first unread)
6743 first (1+ first))))
eec82323
LMI
6744 (setq first (1+ (if (atom (car read)) (car read) (cdar read))))
6745 (setq nlast (if (atom (cadr read)) (cadr read) (caadr read)))
6746 (setq read (cdr read)))))
6747 ;; And add the last unread articles.
6748 (while (<= first last)
54506618
MB
6749 (setq unread (cons first unread)
6750 first (1+ first)))
eec82323 6751 ;; Return the list of unread articles.
6748645f 6752 (delq 0 (nreverse unread))))
eec82323
LMI
6753
6754(defun gnus-list-of-read-articles (group)
6755 "Return a list of unread, unticked and non-dormant articles."
6756 (let* ((info (gnus-get-info group))
6757 (marked (gnus-info-marks info))
6758 (active (gnus-active group)))
6759 (and info active
23f87bed
MB
6760 (gnus-list-range-difference
6761 (gnus-list-range-difference
6762 (gnus-sorted-complement
11abff8e 6763 (gnus-uncompress-range
4b70e299 6764 (if gnus-newsgroup-maximum-articles
11abff8e 6765 (cons (max (car active)
4b70e299
MB
6766 (- (cdr active)
6767 gnus-newsgroup-maximum-articles
6768 -1))
11abff8e
MB
6769 (cdr active))
6770 active))
23f87bed
MB
6771 (gnus-list-of-unread-articles group))
6772 (cdr (assq 'dormant marked)))
6773 (cdr (assq 'tick marked))))))
eec82323 6774
54506618
MB
6775;; This function returns a sequence of article numbers based on the
6776;; difference between the ranges of read articles in this group and
6777;; the range of active articles.
6778(defun gnus-sequence-of-unread-articles (group)
6779 (let* ((read (gnus-info-read (gnus-get-info group)))
6780 (active (or (gnus-active group) (gnus-activate-group group)))
6781 (last (cdr active))
4b70e299
MB
6782 (bottom (if gnus-newsgroup-maximum-articles
6783 (max (car active)
6784 (- last gnus-newsgroup-maximum-articles -1))
11abff8e 6785 (car active)))
54506618
MB
6786 first nlast unread)
6787 ;; If none are read, then all are unread.
6788 (if (not read)
11abff8e 6789 (setq first bottom)
54506618
MB
6790 ;; If the range of read articles is a single range, then the
6791 ;; first unread article is the article after the last read
6792 ;; article. Sounds logical, doesn't it?
6793 (if (and (not (listp (cdr read)))
11abff8e 6794 (or (< (car read) bottom)
54506618
MB
6795 (progn (setq read (list read))
6796 nil)))
11abff8e 6797 (setq first (max bottom (1+ (cdr read))))
54506618
MB
6798 ;; `read' is a list of ranges.
6799 (when (/= (setq nlast (or (and (numberp (car read)) (car read))
6800 (caar read)))
6801 1)
11abff8e 6802 (setq first bottom))
54506618
MB
6803 (while read
6804 (when first
6805 (push (cons first nlast) unread))
6806 (setq first (1+ (if (atom (car read)) (car read) (cdar read))))
6807 (setq nlast (if (atom (cadr read)) (cadr read) (caadr read)))
6808 (setq read (cdr read)))))
6809 ;; And add the last unread articles.
ba0226dd
MB
6810 (cond ((not (and first last))
6811 nil)
6812 ((< first last)
6813 (push (cons first last) unread))
6814 ((= first last)
6815 (push first unread)))
54506618
MB
6816 ;; Return the sequence of unread articles.
6817 (delq 0 (nreverse unread))))
6818
eec82323
LMI
6819;; Various summary commands
6820
6748645f
LMI
6821(defun gnus-summary-select-article-buffer ()
6822 "Reconfigure windows to show article buffer."
6823 (interactive)
6824 (if (not (gnus-buffer-live-p gnus-article-buffer))
6825 (error "There is no article buffer for this summary buffer")
6826 (gnus-configure-windows 'article)
6827 (select-window (get-buffer-window gnus-article-buffer))))
6828
eec82323
LMI
6829(defun gnus-summary-universal-argument (arg)
6830 "Perform any operation on all articles that are process/prefixed."
6831 (interactive "P")
eec82323
LMI
6832 (let ((articles (gnus-summary-work-articles arg))
6833 func article)
6834 (if (eq
6835 (setq
6836 func
6837 (key-binding
6838 (read-key-sequence
6839 (substitute-command-keys
16409b0b 6840 "\\<gnus-summary-mode-map>\\[gnus-summary-universal-argument]"))))
eec82323
LMI
6841 'undefined)
6842 (gnus-error 1 "Undefined key")
6843 (save-excursion
6844 (while articles
6845 (gnus-summary-goto-subject (setq article (pop articles)))
6846 (let (gnus-newsgroup-processable)
6847 (command-execute func))
6848 (gnus-summary-remove-process-mark article)))))
6849 (gnus-summary-position-point))
6850
6851(defun gnus-summary-toggle-truncation (&optional arg)
6852 "Toggle truncation of summary lines.
23f87bed 6853With ARG, turn line truncation on if ARG is positive."
eec82323
LMI
6854 (interactive "P")
6855 (setq truncate-lines
6856 (if (null arg) (not truncate-lines)
6857 (> (prefix-numeric-value arg) 0)))
6858 (redraw-display))
6859
23f87bed
MB
6860(defun gnus-summary-find-for-reselect ()
6861 "Return the number of an article to stay on across a reselect.
6862The current article is considered, then following articles, then previous
6863articles. An article is sought which is not cancelled and isn't a temporary
6864insertion from another group. If there's no such then return a dummy 0."
6865 (let (found)
6866 (dolist (rev '(nil t))
6867 (unless found ; don't demand the reverse list if we don't need it
6868 (let ((data (gnus-data-find-list
6869 (gnus-summary-article-number) (gnus-data-list rev))))
6870 (while (and data (not found))
6871 (if (and (< 0 (gnus-data-number (car data)))
6872 (not (eq gnus-canceled-mark (gnus-data-mark (car data)))))
6873 (setq found (gnus-data-number (car data))))
6874 (setq data (cdr data))))))
6875 (or found 0)))
6876
eec82323
LMI
6877(defun gnus-summary-reselect-current-group (&optional all rescan)
6878 "Exit and then reselect the current newsgroup.
6879The prefix argument ALL means to select all articles."
6880 (interactive "P")
eec82323
LMI
6881 (when (gnus-ephemeral-group-p gnus-newsgroup-name)
6882 (error "Ephemeral groups can't be reselected"))
23f87bed 6883 (let ((current-subject (gnus-summary-find-for-reselect))
eec82323
LMI
6884 (group gnus-newsgroup-name))
6885 (setq gnus-newsgroup-begin nil)
23f87bed 6886 (gnus-summary-exit nil 'leave-hidden)
eec82323
LMI
6887 ;; We have to adjust the point of group mode buffer because
6888 ;; point was moved to the next unread newsgroup by exiting.
6889 (gnus-summary-jump-to-group group)
6890 (when rescan
6891 (save-excursion
6892 (gnus-group-get-new-news-this-group 1)))
6893 (gnus-group-read-group all t)
6894 (gnus-summary-goto-subject current-subject nil t)))
6895
6896(defun gnus-summary-rescan-group (&optional all)
6897 "Exit the newsgroup, ask for new articles, and select the newsgroup."
6898 (interactive "P")
6899 (gnus-summary-reselect-current-group all t))
6900
6901(defun gnus-summary-update-info (&optional non-destructive)
6902 (save-excursion
6903 (let ((group gnus-newsgroup-name))
6748645f
LMI
6904 (when group
6905 (when gnus-newsgroup-kill-headers
6906 (setq gnus-newsgroup-killed
6907 (gnus-compress-sequence
23f87bed
MB
6908 (gnus-sorted-union
6909 (gnus-list-range-intersection
6910 gnus-newsgroup-unselected gnus-newsgroup-killed)
6911 gnus-newsgroup-unreads)
6748645f
LMI
6912 t)))
6913 (unless (listp (cdr gnus-newsgroup-killed))
6914 (setq gnus-newsgroup-killed (list gnus-newsgroup-killed)))
6915 (let ((headers gnus-newsgroup-headers))
6916 ;; Set the new ranges of read articles.
01c52d31 6917 (with-current-buffer gnus-group-buffer
6748645f
LMI
6918 (gnus-undo-force-boundary))
6919 (gnus-update-read-articles
23f87bed
MB
6920 group (gnus-sorted-union
6921 gnus-newsgroup-unreads gnus-newsgroup-unselected))
6748645f
LMI
6922 ;; Set the current article marks.
6923 (let ((gnus-newsgroup-scored
6924 (if (and (not gnus-save-score)
6925 (not non-destructive))
6926 nil
6927 gnus-newsgroup-scored)))
6928 (save-excursion
6929 (gnus-update-marks)))
6930 ;; Do the cross-ref thing.
6931 (when gnus-use-cross-reference
6932 (gnus-mark-xrefs-as-read group headers gnus-newsgroup-unreads))
6933 ;; Do not switch windows but change the buffer to work.
a8151ef7 6934 (set-buffer gnus-group-buffer)
6748645f
LMI
6935 (unless (gnus-ephemeral-group-p group)
6936 (gnus-group-update-group group)))))))
eec82323
LMI
6937
6938(defun gnus-summary-save-newsrc (&optional force)
6939 "Save the current number of read/marked articles in the dribble buffer.
6940The dribble buffer will then be saved.
6941If FORCE (the prefix), also save the .newsrc file(s)."
6942 (interactive "P")
6943 (gnus-summary-update-info t)
6944 (if force
6945 (gnus-save-newsrc-file)
6946 (gnus-dribble-save)))
6947
23f87bed 6948(defun gnus-summary-exit (&optional temporary leave-hidden)
eec82323 6949 "Exit reading current newsgroup, and then return to group selection mode.
16409b0b 6950`gnus-exit-group-hook' is called with no arguments if that value is non-nil."
eec82323
LMI
6951 (interactive)
6952 (gnus-set-global-variables)
16409b0b
GM
6953 (when (gnus-buffer-live-p gnus-article-buffer)
6954 (save-excursion
6955 (set-buffer gnus-article-buffer)
6956 (mm-destroy-parts gnus-article-mime-handles)
6957 ;; Set it to nil for safety reason.
6958 (setq gnus-article-mime-handle-alist nil)
6959 (setq gnus-article-mime-handles nil)))
eec82323 6960 (gnus-kill-save-kill-buffer)
6748645f 6961 (gnus-async-halt-prefetch)
eec82323
LMI
6962 (let* ((group gnus-newsgroup-name)
6963 (quit-config (gnus-group-quit-config gnus-newsgroup-name))
23f87bed 6964 (gnus-group-is-exiting-p t)
eec82323 6965 (mode major-mode)
23f87bed 6966 (group-point nil)
eec82323 6967 (buf (current-buffer)))
16409b0b
GM
6968 (unless quit-config
6969 ;; Do adaptive scoring, and possibly save score files.
6970 (when gnus-newsgroup-adaptive
6971 (gnus-score-adaptive))
6972 (when gnus-use-scoring
6973 (gnus-score-save)))
6748645f 6974 (gnus-run-hooks 'gnus-summary-prepare-exit-hook)
eec82323
LMI
6975 ;; If we have several article buffers, we kill them at exit.
6976 (unless gnus-single-article-buffer
01c52d31
MB
6977 (when (gnus-buffer-live-p gnus-article-buffer)
6978 (with-current-buffer gnus-article-buffer
6979 ;; Don't kill sticky article buffers
6980 (unless (eq major-mode 'gnus-sticky-article-mode)
6981 (gnus-kill-buffer gnus-article-buffer)
6982 (setq gnus-article-current nil))))
6983 (gnus-kill-buffer gnus-original-article-buffer))
eec82323
LMI
6984 (when gnus-use-cache
6985 (gnus-cache-possibly-remove-articles)
6986 (gnus-cache-save-buffers))
6987 (gnus-async-prefetch-remove-group group)
6988 (when gnus-suppress-duplicates
6989 (gnus-dup-enter-articles))
6990 (when gnus-use-trees
6991 (gnus-tree-close group))
16409b0b
GM
6992 (when gnus-use-cache
6993 (gnus-cache-write-active))
6748645f
LMI
6994 ;; Remove entries for this group.
6995 (nnmail-purge-split-history (gnus-group-real-name group))
eec82323
LMI
6996 ;; Make all changes in this group permanent.
6997 (unless quit-config
6748645f 6998 (gnus-run-hooks 'gnus-exit-group-hook)
16409b0b 6999 (gnus-summary-update-info))
eec82323
LMI
7000 (gnus-close-group group)
7001 ;; Make sure where we were, and go to next newsgroup.
7002 (set-buffer gnus-group-buffer)
7003 (unless quit-config
7004 (gnus-group-jump-to-group group))
6748645f
LMI
7005 (gnus-run-hooks 'gnus-summary-exit-hook)
7006 (unless (or quit-config
01c52d31 7007 (not gnus-summary-next-group-on-exit)
6748645f
LMI
7008 ;; If this group has disappeared from the summary
7009 ;; buffer, don't skip forwards.
7010 (not (string= group (gnus-group-group-name))))
eec82323 7011 (gnus-group-next-unread-group 1))
a8151ef7 7012 (setq group-point (point))
eec82323
LMI
7013 (if temporary
7014 nil ;Nothing to do.
eec82323
LMI
7015 (set-buffer buf)
7016 (if (not gnus-kill-summary-on-exit)
23f87bed
MB
7017 (progn
7018 (gnus-deaden-summary)
7019 (setq mode nil))
eec82323
LMI
7020 ;; We set all buffer-local variables to nil. It is unclear why
7021 ;; this is needed, but if we don't, buffer-local variables are
7022 ;; not garbage-collected, it seems. This would the lead to en
7023 ;; ever-growing Emacs.
7024 (gnus-summary-clear-local-variables)
23f87bed
MB
7025 (let ((gnus-summary-local-variables gnus-newsgroup-variables))
7026 (gnus-summary-clear-local-variables))
eec82323
LMI
7027 (when (get-buffer gnus-article-buffer)
7028 (bury-buffer gnus-article-buffer))
eec82323
LMI
7029 ;; Return to group mode buffer.
7030 (when (eq mode 'gnus-summary-mode)
7031 (gnus-kill-buffer buf)))
7032 (setq gnus-current-select-method gnus-select-method)
d61c212b
SM
7033 (set-buffer gnus-group-buffer)
7034 (if quit-config
7035 (gnus-handle-ephemeral-exit quit-config)
4e90f2b9
SM
7036 (goto-char group-point)
7037 ;; If gnus-group-buffer is already displayed, make sure we also move
7038 ;; the cursor in the window that displays it.
7039 (let ((win (get-buffer-window (current-buffer) 0)))
7040 (if win (set-window-point win (point))))
d61c212b 7041 (unless leave-hidden
4e90f2b9 7042 (gnus-configure-windows 'group 'force)))
6748645f 7043 ;; Clear the current group name.
eec82323
LMI
7044 (unless quit-config
7045 (setq gnus-newsgroup-name nil)))))
7046
7047(defalias 'gnus-summary-quit 'gnus-summary-exit-no-update)
7048(defun gnus-summary-exit-no-update (&optional no-questions)
7049 "Quit reading current newsgroup without updating read article info."
7050 (interactive)
eec82323 7051 (let* ((group gnus-newsgroup-name)
23f87bed
MB
7052 (gnus-group-is-exiting-p t)
7053 (gnus-group-is-exiting-without-update-p t)
eec82323
LMI
7054 (quit-config (gnus-group-quit-config group)))
7055 (when (or no-questions
7056 gnus-expert-user
7057 (gnus-y-or-n-p "Discard changes to this group and exit? "))
6748645f 7058 (gnus-async-halt-prefetch)
23f87bed 7059 (run-hooks 'gnus-summary-prepare-exit-hook)
16409b0b
GM
7060 (when (gnus-buffer-live-p gnus-article-buffer)
7061 (save-excursion
7062 (set-buffer gnus-article-buffer)
7063 (mm-destroy-parts gnus-article-mime-handles)
7064 ;; Set it to nil for safety reason.
7065 (setq gnus-article-mime-handle-alist nil)
7066 (setq gnus-article-mime-handles nil)))
eec82323
LMI
7067 ;; If we have several article buffers, we kill them at exit.
7068 (unless gnus-single-article-buffer
7069 (gnus-kill-buffer gnus-article-buffer)
7070 (gnus-kill-buffer gnus-original-article-buffer)
7071 (setq gnus-article-current nil))
7072 (if (not gnus-kill-summary-on-exit)
7073 (gnus-deaden-summary)
7074 (gnus-close-group group)
7075 (gnus-summary-clear-local-variables)
23f87bed
MB
7076 (let ((gnus-summary-local-variables gnus-newsgroup-variables))
7077 (gnus-summary-clear-local-variables))
7078 (gnus-kill-buffer gnus-summary-buffer))
eec82323
LMI
7079 (unless gnus-single-article-buffer
7080 (setq gnus-article-current nil))
7081 (when gnus-use-trees
7082 (gnus-tree-close group))
7083 (gnus-async-prefetch-remove-group group)
7084 (when (get-buffer gnus-article-buffer)
7085 (bury-buffer gnus-article-buffer))
7086 ;; Return to the group buffer.
7087 (gnus-configure-windows 'group 'force)
7088 ;; Clear the current group name.
7089 (setq gnus-newsgroup-name nil)
23f87bed
MB
7090 (unless (gnus-ephemeral-group-p group)
7091 (gnus-group-update-group group))
eec82323
LMI
7092 (when (equal (gnus-group-group-name) group)
7093 (gnus-group-next-unread-group 1))
7094 (when quit-config
23f87bed 7095 (gnus-handle-ephemeral-exit quit-config)))))
eec82323
LMI
7096
7097(defun gnus-handle-ephemeral-exit (quit-config)
6748645f
LMI
7098 "Handle movement when leaving an ephemeral group.
7099The state which existed when entering the ephemeral is reset."
eec82323
LMI
7100 (if (not (buffer-name (car quit-config)))
7101 (gnus-configure-windows 'group 'force)
7102 (set-buffer (car quit-config))
7103 (cond ((eq major-mode 'gnus-summary-mode)
23f87bed
MB
7104 (gnus-set-global-variables))
7105 ((eq major-mode 'gnus-article-mode)
7106 (save-excursion
7107 ;; The `gnus-summary-buffer' variable may point
7108 ;; to the old summary buffer when using a single
7109 ;; article buffer.
7110 (unless (gnus-buffer-live-p gnus-summary-buffer)
7111 (set-buffer gnus-group-buffer))
7112 (set-buffer gnus-summary-buffer)
7113 (gnus-set-global-variables))))
eec82323 7114 (if (or (eq (cdr quit-config) 'article)
23f87bed 7115 (eq (cdr quit-config) 'pick))
01c52d31
MB
7116 (if (and (boundp 'gnus-pick-mode) (symbol-value 'gnus-pick-mode))
7117 (gnus-configure-windows 'pick 'force)
7118 (gnus-configure-windows (cdr quit-config) 'force))
eec82323
LMI
7119 (gnus-configure-windows (cdr quit-config) 'force))
7120 (when (eq major-mode 'gnus-summary-mode)
01c52d31
MB
7121 (if (memq gnus-auto-select-on-ephemeral-exit '(next-noselect
7122 next-unread-noselect))
7123 (when (zerop (cond ((eq gnus-auto-select-on-ephemeral-exit
7124 'next-noselect)
7125 (gnus-summary-next-subject 1 nil t))
7126 ((eq gnus-auto-select-on-ephemeral-exit
7127 'next-unread-noselect)
7128 (gnus-summary-next-subject 1 t t))))
7129 ;; Hide the article buffer which displays the article different
7130 ;; from the one that the cursor points to in the summary buffer.
7131 (gnus-configure-windows 'summary 'force))
7132 (cond ((eq gnus-auto-select-on-ephemeral-exit 'next)
7133 (gnus-summary-next-subject 1))
7134 ((eq gnus-auto-select-on-ephemeral-exit 'next-unread)
7135 (gnus-summary-next-subject 1 t))))
eec82323
LMI
7136 (gnus-summary-recenter)
7137 (gnus-summary-position-point))))
7138
7139;;; Dead summaries.
7140
7141(defvar gnus-dead-summary-mode-map nil)
7142
7143(unless gnus-dead-summary-mode-map
7144 (setq gnus-dead-summary-mode-map (make-keymap))
7145 (suppress-keymap gnus-dead-summary-mode-map)
7146 (substitute-key-definition
7147 'undefined 'gnus-summary-wake-up-the-dead gnus-dead-summary-mode-map)
23f87bed
MB
7148 (dolist (key '("\C-d" "\r" "\177" [delete]))
7149 (define-key gnus-dead-summary-mode-map
7150 key 'gnus-summary-wake-up-the-dead))
7151 (dolist (key '("q" "Q"))
7152 (define-key gnus-dead-summary-mode-map key 'bury-buffer)))
eec82323
LMI
7153
7154(defvar gnus-dead-summary-mode nil
7155 "Minor mode for Gnus summary buffers.")
7156
7157(defun gnus-dead-summary-mode (&optional arg)
7158 "Minor mode for Gnus summary buffers."
7159 (interactive "P")
7160 (when (eq major-mode 'gnus-summary-mode)
7161 (make-local-variable 'gnus-dead-summary-mode)
7162 (setq gnus-dead-summary-mode
7163 (if (null arg) (not gnus-dead-summary-mode)
7164 (> (prefix-numeric-value arg) 0)))
7165 (when gnus-dead-summary-mode
01c52d31 7166 (add-minor-mode
a8151ef7 7167 'gnus-dead-summary-mode " Dead" gnus-dead-summary-mode-map))))
eec82323
LMI
7168
7169(defun gnus-deaden-summary ()
7170 "Make the current summary buffer into a dead summary buffer."
7171 ;; Kill any previous dead summary buffer.
7172 (when (and gnus-dead-summary
7173 (buffer-name gnus-dead-summary))
01c52d31 7174 (with-current-buffer gnus-dead-summary
eec82323
LMI
7175 (when gnus-dead-summary-mode
7176 (kill-buffer (current-buffer)))))
7177 ;; Make this the current dead summary.
7178 (setq gnus-dead-summary (current-buffer))
7179 (gnus-dead-summary-mode 1)
7180 (let ((name (buffer-name)))
7181 (when (string-match "Summary" name)
7182 (rename-buffer
7183 (concat (substring name 0 (match-beginning 0)) "Dead "
7184 (substring name (match-beginning 0)))
16409b0b
GM
7185 t)
7186 (bury-buffer))))
eec82323
LMI
7187
7188(defun gnus-kill-or-deaden-summary (buffer)
7189 "Kill or deaden the summary BUFFER."
6748645f
LMI
7190 (save-excursion
7191 (when (and (buffer-name buffer)
7192 (not gnus-single-article-buffer))
01c52d31 7193 (with-current-buffer buffer
6748645f
LMI
7194 (gnus-kill-buffer gnus-article-buffer)
7195 (gnus-kill-buffer gnus-original-article-buffer)))
23f87bed
MB
7196 (cond
7197 ;; Kill the buffer.
7198 (gnus-kill-summary-on-exit
7199 (when (and gnus-use-trees
7200 (gnus-buffer-exists-p buffer))
7201 (save-excursion
7202 (set-buffer buffer)
7203 (gnus-tree-close gnus-newsgroup-name)))
7204 (gnus-kill-buffer buffer))
7205 ;; Deaden the buffer.
7206 ((gnus-buffer-exists-p buffer)
7207 (save-excursion
7208 (set-buffer buffer)
7209 (gnus-deaden-summary))))))
eec82323
LMI
7210
7211(defun gnus-summary-wake-up-the-dead (&rest args)
7212 "Wake up the dead summary buffer."
7213 (interactive)
7214 (gnus-dead-summary-mode -1)
7215 (let ((name (buffer-name)))
7216 (when (string-match "Dead " name)
7217 (rename-buffer
7218 (concat (substring name 0 (match-beginning 0))
7219 (substring name (match-end 0)))
7220 t)))
7221 (gnus-message 3 "This dead summary is now alive again"))
7222
7223;; Suggested by Andrew Eskilsson <pi92ae@pt.hk-r.se>.
7224(defun gnus-summary-fetch-faq (&optional faq-dir)
7225 "Fetch the FAQ for the current group.
7226If FAQ-DIR (the prefix), prompt for a directory to search for the faq
7227in."
7228 (interactive
7229 (list
7230 (when current-prefix-arg
7231 (completing-read
8f688cb0 7232 "FAQ dir: " (and (listp gnus-group-faq-directory)
01c52d31 7233 (mapcar 'list
a8151ef7 7234 gnus-group-faq-directory))))))
eec82323
LMI
7235 (let (gnus-faq-buffer)
7236 (when (setq gnus-faq-buffer
7237 (gnus-group-fetch-faq gnus-newsgroup-name faq-dir))
7238 (gnus-configure-windows 'summary-faq))))
7239
7240;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
7241(defun gnus-summary-describe-group (&optional force)
7242 "Describe the current newsgroup."
7243 (interactive "P")
7244 (gnus-group-describe-group force gnus-newsgroup-name))
7245
7246(defun gnus-summary-describe-briefly ()
7247 "Describe summary mode commands briefly."
7248 (interactive)
16409b0b 7249 (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
7250
7251;; Walking around group mode buffer from summary mode.
7252
7253(defun gnus-summary-next-group (&optional no-article target-group backward)
7254 "Exit current newsgroup and then select next unread newsgroup.
7255If prefix argument NO-ARTICLE is non-nil, no article is selected
23f87bed 7256initially. If TARGET-GROUP, go to this group. If BACKWARD, go to
eec82323
LMI
7257previous group instead."
7258 (interactive "P")
eec82323
LMI
7259 ;; Stop pre-fetching.
7260 (gnus-async-halt-prefetch)
7261 (let ((current-group gnus-newsgroup-name)
7262 (current-buffer (current-buffer))
7263 entered)
7264 ;; First we semi-exit this group to update Xrefs and all variables.
7265 ;; We can't do a real exit, because the window conf must remain
7266 ;; the same in case the user is prompted for info, and we don't
7267 ;; want the window conf to change before that...
7268 (gnus-summary-exit t)
7269 (while (not entered)
7270 ;; Then we find what group we are supposed to enter.
7271 (set-buffer gnus-group-buffer)
7272 (gnus-group-jump-to-group current-group)
7273 (setq target-group
7274 (or target-group
7275 (if (eq gnus-keep-same-level 'best)
7276 (gnus-summary-best-group gnus-newsgroup-name)
7277 (gnus-summary-search-group backward gnus-keep-same-level))))
7278 (if (not target-group)
7279 ;; There are no further groups, so we return to the group
7280 ;; buffer.
7281 (progn
7282 (gnus-message 5 "Returning to the group buffer")
7283 (setq entered t)
7284 (when (gnus-buffer-live-p current-buffer)
7285 (set-buffer current-buffer)
7286 (gnus-summary-exit))
6748645f 7287 (gnus-run-hooks 'gnus-group-no-more-groups-hook))
eec82323
LMI
7288 ;; We try to enter the target group.
7289 (gnus-group-jump-to-group target-group)
7290 (let ((unreads (gnus-group-group-unread)))
7291 (if (and (or (eq t unreads)
7292 (and unreads (not (zerop unreads))))
23f87bed
MB
7293 (gnus-summary-read-group
7294 target-group nil no-article
7295 (and (buffer-name current-buffer) current-buffer)
7296 nil backward))
eec82323
LMI
7297 (setq entered t)
7298 (setq current-group target-group
7299 target-group nil)))))))
7300
7301(defun gnus-summary-prev-group (&optional no-article)
7302 "Exit current newsgroup and then select previous unread newsgroup.
7303If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
7304 (interactive "P")
7305 (gnus-summary-next-group no-article nil t))
7306
7307;; Walking around summary lines.
7308
23f87bed
MB
7309(defun gnus-summary-first-subject (&optional unread undownloaded unseen)
7310 "Go to the first subject satisfying any non-nil constraint.
7311If UNREAD is non-nil, the article should be unread.
7312If UNDOWNLOADED is non-nil, the article should be undownloaded.
7313If UNSEEN is non-nil, the article should be unseen.
7314Returns the article selected or nil if there are no matching articles."
eec82323 7315 (interactive "P")
23f87bed
MB
7316 (cond
7317 ;; Empty summary.
7318 ((null gnus-newsgroup-data)
7319 (gnus-message 3 "No articles in the group")
7320 nil)
7321 ;; Pick the first article.
7322 ((not (or unread undownloaded unseen))
7323 (goto-char (gnus-data-pos (car gnus-newsgroup-data)))
7324 (gnus-data-number (car gnus-newsgroup-data)))
7325 ;; Find the first unread article.
7326 (t
7327 (let ((data gnus-newsgroup-data))
7328 (while (and data
7329 (let ((num (gnus-data-number (car data))))
7330 (or (memq num gnus-newsgroup-unfetched)
7331 (not (or (and unread
7332 (memq num gnus-newsgroup-unreads))
7333 (and undownloaded
7334 (memq num gnus-newsgroup-undownloaded))
7335 (and unseen
7336 (memq num gnus-newsgroup-unseen)))))))
7337 (setq data (cdr data)))
7338 (prog1
7339 (if data
7340 (progn
7341 (goto-char (gnus-data-pos (car data)))
7342 (gnus-data-number (car data)))
7343 (gnus-message 3 "No more%s articles"
7344 (let* ((r (when unread " unread"))
7345 (d (when undownloaded " undownloaded"))
7346 (s (when unseen " unseen"))
7347 (l (delq nil (list r d s))))
7348 (cond ((= 3 (length l))
7349 (concat r "," d ", or" s))
7350 ((= 2 (length l))
7351 (concat (car l) ", or" (cadr l)))
7352 ((= 1 (length l))
7353 (car l))
7354 (t
7355 ""))))
7356 nil
7357 )
7358 (gnus-summary-position-point))))))
eec82323
LMI
7359
7360(defun gnus-summary-next-subject (n &optional unread dont-display)
7361 "Go to next N'th summary line.
7362If N is negative, go to the previous N'th subject line.
7363If UNREAD is non-nil, only unread articles are selected.
7364The difference between N and the actual number of steps taken is
7365returned."
7366 (interactive "p")
7367 (let ((backward (< n 0))
7368 (n (abs n)))
7369 (while (and (> n 0)
7370 (if backward
7371 (gnus-summary-find-prev unread)
7372 (gnus-summary-find-next unread)))
16409b0b
GM
7373 (unless (zerop (setq n (1- n)))
7374 (gnus-summary-show-thread)))
eec82323
LMI
7375 (when (/= 0 n)
7376 (gnus-message 7 "No more%s articles"
7377 (if unread " unread" "")))
7378 (unless dont-display
7379 (gnus-summary-recenter)
7380 (gnus-summary-position-point))
7381 n))
7382
7383(defun gnus-summary-next-unread-subject (n)
7384 "Go to next N'th unread summary line."
7385 (interactive "p")
7386 (gnus-summary-next-subject n t))
7387
7388(defun gnus-summary-prev-subject (n &optional unread)
7389 "Go to previous N'th summary line.
7390If optional argument UNREAD is non-nil, only unread article is selected."
7391 (interactive "p")
7392 (gnus-summary-next-subject (- n) unread))
7393
7394(defun gnus-summary-prev-unread-subject (n)
7395 "Go to previous N'th unread summary line."
7396 (interactive "p")
7397 (gnus-summary-next-subject (- n) t))
7398
23f87bed
MB
7399(defun gnus-summary-goto-subjects (articles)
7400 "Insert the subject header for ARTICLES in the current buffer."
7401 (save-excursion
7402 (dolist (article articles)
7403 (gnus-summary-goto-subject article t)))
7404 (gnus-summary-limit (append articles gnus-newsgroup-limit))
7405 (gnus-summary-position-point))
132cf96d 7406
eec82323
LMI
7407(defun gnus-summary-goto-subject (article &optional force silent)
7408 "Go the subject line of ARTICLE.
7409If FORCE, also allow jumping to articles not currently shown."
7410 (interactive "nArticle number: ")
23f87bed
MB
7411 (unless (numberp article)
7412 (error "Article %s is not a number" article))
eec82323
LMI
7413 (let ((b (point))
7414 (data (gnus-data-find article)))
7415 ;; We read in the article if we have to.
7416 (and (not data)
7417 force
6748645f
LMI
7418 (gnus-summary-insert-subject
7419 article
7420 (if (or (numberp force) (vectorp force)) force)
7421 t)
eec82323
LMI
7422 (setq data (gnus-data-find article)))
7423 (goto-char b)
7424 (if (not data)
7425 (progn
7426 (unless silent
7427 (gnus-message 3 "Can't find article %d" article))
7428 nil)
23f87bed
MB
7429 (let ((pt (gnus-data-pos data)))
7430 (goto-char pt)
7431 (gnus-summary-set-article-display-arrow pt))
6748645f 7432 (gnus-summary-position-point)
eec82323
LMI
7433 article)))
7434
7435;; Walking around summary lines with displaying articles.
7436
7437(defun gnus-summary-expand-window (&optional arg)
7438 "Make the summary buffer take up the entire Emacs frame.
7439Given a prefix, will force an `article' buffer configuration."
7440 (interactive "P")
eec82323
LMI
7441 (if arg
7442 (gnus-configure-windows 'article 'force)
7443 (gnus-configure-windows 'summary 'force)))
7444
7445(defun gnus-summary-display-article (article &optional all-header)
7446 "Display ARTICLE in article buffer."
01c52d31
MB
7447 (unless (and (gnus-buffer-live-p gnus-article-buffer)
7448 (with-current-buffer gnus-article-buffer
7449 (eq major-mode 'gnus-article-mode)))
7450 (gnus-article-setup-buffer))
eec82323 7451 (gnus-set-global-variables)
01c52d31
MB
7452 (with-current-buffer gnus-article-buffer
7453 (setq gnus-article-charset gnus-newsgroup-charset)
7454 (setq gnus-article-ignored-charsets gnus-newsgroup-ignored-charsets)
7455 (mm-enable-multibyte))
eec82323
LMI
7456 (if (null article)
7457 nil
7458 (prog1
7459 (if gnus-summary-display-article-function
7460 (funcall gnus-summary-display-article-function article all-header)
7461 (gnus-article-prepare article all-header))
6748645f 7462 (gnus-run-hooks 'gnus-select-article-hook)
eec82323
LMI
7463 (when (and gnus-current-article
7464 (not (zerop gnus-current-article)))
7465 (gnus-summary-goto-subject gnus-current-article))
7466 (gnus-summary-recenter)
7467 (when (and gnus-use-trees gnus-show-threads)
7468 (gnus-possibly-generate-tree article)
7469 (gnus-highlight-selected-tree article))
7470 ;; Successfully display article.
7471 (gnus-article-set-window-start
7472 (cdr (assq article gnus-newsgroup-bookmarks))))))
7473
7474(defun gnus-summary-select-article (&optional all-headers force pseudo article)
7475 "Select the current article.
7476If ALL-HEADERS is non-nil, show all header fields. If FORCE is
7477non-nil, the article will be re-fetched even if it already present in
7478the article buffer. If PSEUDO is non-nil, pseudo-articles will also
7479be displayed."
7480 ;; Make sure we are in the summary buffer to work around bbdb bug.
7481 (unless (eq major-mode 'gnus-summary-mode)
7482 (set-buffer gnus-summary-buffer))
7483 (let ((article (or article (gnus-summary-article-number)))
f0529b5b 7484 (all-headers (not (not all-headers))) ;Must be t or nil.
16409b0b 7485 gnus-summary-display-article-function)
eec82323
LMI
7486 (and (not pseudo)
7487 (gnus-summary-article-pseudo-p article)
a8151ef7 7488 (error "This is a pseudo-article"))
16409b0b
GM
7489 (save-excursion
7490 (set-buffer gnus-summary-buffer)
7491 (if (or (and gnus-single-article-buffer
7492 (or (null gnus-current-article)
7493 (null gnus-article-current)
7494 (null (get-buffer gnus-article-buffer))
7495 (not (eq article (cdr gnus-article-current)))
7496 (not (equal (car gnus-article-current)
7497 gnus-newsgroup-name))))
7498 (and (not gnus-single-article-buffer)
7499 (or (null gnus-current-article)
7500 (not (eq gnus-current-article article))))
7501 force)
7502 ;; The requested article is different from the current article.
7503 (progn
16409b0b
GM
7504 (gnus-summary-display-article article all-headers)
7505 (when (gnus-buffer-live-p gnus-article-buffer)
23f87bed 7506 (with-current-buffer gnus-article-buffer
16409b0b 7507 (if (not gnus-article-decoded-p) ;; a local variable
87545352 7508 (mm-disable-multibyte))))
16409b0b
GM
7509 (gnus-article-set-window-start
7510 (cdr (assq article gnus-newsgroup-bookmarks)))
7511 article)
16409b0b 7512 'old))))
eec82323 7513
23f87bed
MB
7514(defun gnus-summary-force-verify-and-decrypt ()
7515 "Display buttons for signed/encrypted parts and verify/decrypt them."
7516 (interactive)
7517 (let ((mm-verify-option 'known)
7518 (mm-decrypt-option 'known)
7519 (gnus-article-emulate-mime t)
7520 (gnus-buttonized-mime-types (append (list "multipart/signed"
7521 "multipart/encrypted")
7522 gnus-buttonized-mime-types)))
7523 (gnus-summary-select-article nil 'force)))
7524
eec82323
LMI
7525(defun gnus-summary-set-current-mark (&optional current-mark)
7526 "Obsolete function."
7527 nil)
7528
7529(defun gnus-summary-next-article (&optional unread subject backward push)
7530 "Select the next article.
7531If UNREAD, only unread articles are selected.
7532If SUBJECT, only articles with SUBJECT are selected.
7533If BACKWARD, the previous article is selected instead of the next."
7534 (interactive "P")
11e95b02
MB
7535 ;; Make sure we are in the summary buffer.
7536 (unless (eq major-mode 'gnus-summary-mode)
7537 (set-buffer gnus-summary-buffer))
eec82323
LMI
7538 (cond
7539 ;; Is there such an article?
7540 ((and (gnus-summary-search-forward unread subject backward)
7541 (or (gnus-summary-display-article (gnus-summary-article-number))
7542 (eq (gnus-summary-article-mark) gnus-canceled-mark)))
7543 (gnus-summary-position-point))
7544 ;; If not, we try the first unread, if that is wanted.
7545 ((and subject
7546 gnus-auto-select-same
7547 (gnus-summary-first-unread-article))
7548 (gnus-summary-position-point)
7549 (gnus-message 6 "Wrapped"))
7550 ;; Try to get next/previous article not displayed in this group.
7551 ((and gnus-auto-extend-newsgroup
7552 (not unread) (not subject))
7553 (gnus-summary-goto-article
7554 (if backward (1- gnus-newsgroup-begin) (1+ gnus-newsgroup-end))
6748645f 7555 nil (count-lines (point-min) (point))))
eec82323
LMI
7556 ;; Go to next/previous group.
7557 (t
7558 (unless (gnus-ephemeral-group-p gnus-newsgroup-name)
7559 (gnus-summary-jump-to-group gnus-newsgroup-name))
7560 (let ((cmd last-command-char)
7561 (point
01c52d31 7562 (with-current-buffer gnus-group-buffer
eec82323
LMI
7563 (point)))
7564 (group
7565 (if (eq gnus-keep-same-level 'best)
7566 (gnus-summary-best-group gnus-newsgroup-name)
7567 (gnus-summary-search-group backward gnus-keep-same-level))))
7568 ;; For some reason, the group window gets selected. We change
7569 ;; it back.
7570 (select-window (get-buffer-window (current-buffer)))
7571 ;; Select next unread newsgroup automagically.
7572 (cond
7573 ((or (not gnus-auto-select-next)
7574 (not cmd))
7575 (gnus-message 7 "No more%s articles" (if unread " unread" "")))
7576 ((or (eq gnus-auto-select-next 'quietly)
7577 (and (eq gnus-auto-select-next 'slightly-quietly)
7578 push)
7579 (and (eq gnus-auto-select-next 'almost-quietly)
7580 (gnus-summary-last-article-p)))
7581 ;; Select quietly.
7582 (if (gnus-ephemeral-group-p gnus-newsgroup-name)
7583 (gnus-summary-exit)
7584 (gnus-message 7 "No more%s articles (%s)..."
7585 (if unread " unread" "")
7586 (if group (concat "selecting " group)
7587 "exiting"))
7588 (gnus-summary-next-group nil group backward)))
7589 (t
7590 (when (gnus-key-press-event-p last-input-event)
7591 (gnus-summary-walk-group-buffer
7592 gnus-newsgroup-name cmd unread backward point))))))))
7593
7594(defun gnus-summary-walk-group-buffer (from-group cmd unread backward start)
7595 (let ((keystrokes '((?\C-n (gnus-group-next-unread-group 1))
7596 (?\C-p (gnus-group-prev-unread-group 1))))
7597 (cursor-in-echo-area t)
23f87bed 7598 keve key group ended prompt)
eec82323
LMI
7599 (save-excursion
7600 (set-buffer gnus-group-buffer)
7601 (goto-char start)
7602 (setq group
7603 (if (eq gnus-keep-same-level 'best)
7604 (gnus-summary-best-group gnus-newsgroup-name)
7605 (gnus-summary-search-group backward gnus-keep-same-level))))
7606 (while (not ended)
23f87bed
MB
7607 (setq prompt
7608 (format
7609 "No more%s articles%s " (if unread " unread" "")
7610 (if (and group
7611 (not (gnus-ephemeral-group-p gnus-newsgroup-name)))
7612 (format " (Type %s for %s [%s])"
91472578
MB
7613 (single-key-description cmd)
7614 (gnus-group-decoded-name group)
01c52d31 7615 (gnus-group-unread group))
23f87bed
MB
7616 (format " (Type %s to exit %s)"
7617 (single-key-description cmd)
91472578 7618 (gnus-group-decoded-name gnus-newsgroup-name)))))
eec82323 7619 ;; Confirm auto selection.
23f87bed
MB
7620 (setq key (car (setq keve (gnus-read-event-char prompt)))
7621 ended t)
eec82323
LMI
7622 (cond
7623 ((assq key keystrokes)
7624 (let ((obuf (current-buffer)))
7625 (switch-to-buffer gnus-group-buffer)
7626 (when group
7627 (gnus-group-jump-to-group group))
7628 (eval (cadr (assq key keystrokes)))
7629 (setq group (gnus-group-group-name))
7630 (switch-to-buffer obuf))
7631 (setq ended nil))
7632 ((equal key cmd)
7633 (if (or (not group)
7634 (gnus-ephemeral-group-p gnus-newsgroup-name))
7635 (gnus-summary-exit)
7636 (gnus-summary-next-group nil group backward)))
7637 (t
7638 (push (cdr keve) unread-command-events))))))
7639
7640(defun gnus-summary-next-unread-article ()
7641 "Select unread article after current one."
7642 (interactive)
7643 (gnus-summary-next-article
7644 (or (not (eq gnus-summary-goto-unread 'never))
7645 (gnus-summary-last-article-p (gnus-summary-article-number)))
7646 (and gnus-auto-select-same
7647 (gnus-summary-article-subject))))
7648
7649(defun gnus-summary-prev-article (&optional unread subject)
7650 "Select the article after the current one.
7651If UNREAD is non-nil, only unread articles are selected."
7652 (interactive "P")
7653 (gnus-summary-next-article unread subject t))
7654
7655(defun gnus-summary-prev-unread-article ()
7656 "Select unread article before current one."
7657 (interactive)
7658 (gnus-summary-prev-article
7659 (or (not (eq gnus-summary-goto-unread 'never))
7660 (gnus-summary-first-article-p (gnus-summary-article-number)))
7661 (and gnus-auto-select-same
7662 (gnus-summary-article-subject))))
7663
23f87bed 7664(defun gnus-summary-next-page (&optional lines circular stop)
eec82323
LMI
7665 "Show next page of the selected article.
7666If at the end of the current article, select the next article.
7667LINES says how many lines should be scrolled up.
7668
7669If CIRCULAR is non-nil, go to the start of the article instead of
7670selecting the next article when reaching the end of the current
23f87bed
MB
7671article.
7672
7673If STOP is non-nil, just stop when reaching the end of the message.
7674
7675Also see the variable `gnus-article-skip-boring'."
eec82323
LMI
7676 (interactive "P")
7677 (setq gnus-summary-buffer (current-buffer))
7678 (gnus-set-global-variables)
7679 (let ((article (gnus-summary-article-number))
7680 (article-window (get-buffer-window gnus-article-buffer t))
7681 endp)
6748645f
LMI
7682 ;; If the buffer is empty, we have no article.
7683 (unless article
7684 (error "No article to select"))
eec82323
LMI
7685 (gnus-configure-windows 'article)
7686 (if (eq (cdr (assq article gnus-newsgroup-reads)) gnus-canceled-mark)
7687 (if (and (eq gnus-summary-goto-unread 'never)
7688 (not (gnus-summary-last-article-p article)))
7689 (gnus-summary-next-article)
7690 (gnus-summary-next-unread-article))
7691 (if (or (null gnus-current-article)
7692 (null gnus-article-current)
7693 (/= article (cdr gnus-article-current))
7694 (not (equal (car gnus-article-current) gnus-newsgroup-name)))
7695 ;; Selected subject is different from current article's.
7696 (gnus-summary-display-article article)
7697 (when article-window
7698 (gnus-eval-in-buffer-window gnus-article-buffer
23f87bed
MB
7699 (setq endp (or (gnus-article-next-page lines)
7700 (gnus-article-only-boring-p))))
eec82323 7701 (when endp
23f87bed
MB
7702 (cond (stop
7703 (gnus-message 3 "End of message"))
7704 (circular
eec82323
LMI
7705 (gnus-summary-beginning-of-article))
7706 (lines
7707 (gnus-message 3 "End of message"))
7708 ((null lines)
7709 (if (and (eq gnus-summary-goto-unread 'never)
7710 (not (gnus-summary-last-article-p article)))
7711 (gnus-summary-next-article)
7712 (gnus-summary-next-unread-article))))))))
7713 (gnus-summary-recenter)
7714 (gnus-summary-position-point)))
7715
7716(defun gnus-summary-prev-page (&optional lines move)
7717 "Show previous page of selected article.
7718Argument LINES specifies lines to be scrolled down.
7719If MOVE, move to the previous unread article if point is at
7720the beginning of the buffer."
7721 (interactive "P")
eec82323
LMI
7722 (let ((article (gnus-summary-article-number))
7723 (article-window (get-buffer-window gnus-article-buffer t))
7724 endp)
7725 (gnus-configure-windows 'article)
7726 (if (or (null gnus-current-article)
7727 (null gnus-article-current)
7728 (/= article (cdr gnus-article-current))
7729 (not (equal (car gnus-article-current) gnus-newsgroup-name)))
7730 ;; Selected subject is different from current article's.
7731 (gnus-summary-display-article article)
7732 (gnus-summary-recenter)
7733 (when article-window
7734 (gnus-eval-in-buffer-window gnus-article-buffer
7735 (setq endp (gnus-article-prev-page lines)))
7736 (when (and move endp)
7737 (cond (lines
7738 (gnus-message 3 "Beginning of message"))
7739 ((null lines)
7740 (if (and (eq gnus-summary-goto-unread 'never)
7741 (not (gnus-summary-first-article-p article)))
7742 (gnus-summary-prev-article)
7743 (gnus-summary-prev-unread-article))))))))
7744 (gnus-summary-position-point))
7745
7746(defun gnus-summary-prev-page-or-article (&optional lines)
7747 "Show previous page of selected article.
7748Argument LINES specifies lines to be scrolled down.
7749If at the beginning of the article, go to the next article."
7750 (interactive "P")
7751 (gnus-summary-prev-page lines t))
7752
7753(defun gnus-summary-scroll-up (lines)
7754 "Scroll up (or down) one line current article.
7755Argument LINES specifies lines to be scrolled up (or down if negative)."
7756 (interactive "p")
eec82323
LMI
7757 (gnus-configure-windows 'article)
7758 (gnus-summary-show-thread)
7759 (when (eq (gnus-summary-select-article nil nil 'pseudo) 'old)
7760 (gnus-eval-in-buffer-window gnus-article-buffer
7761 (cond ((> lines 0)
7762 (when (gnus-article-next-page lines)
7763 (gnus-message 3 "End of message")))
7764 ((< lines 0)
7765 (gnus-article-prev-page (- lines))))))
7766 (gnus-summary-recenter)
7767 (gnus-summary-position-point))
7768
6748645f
LMI
7769(defun gnus-summary-scroll-down (lines)
7770 "Scroll down (or up) one line current article.
7771Argument LINES specifies lines to be scrolled down (or up if negative)."
7772 (interactive "p")
7773 (gnus-summary-scroll-up (- lines)))
7774
eec82323
LMI
7775(defun gnus-summary-next-same-subject ()
7776 "Select next article which has the same subject as current one."
7777 (interactive)
eec82323
LMI
7778 (gnus-summary-next-article nil (gnus-summary-article-subject)))
7779
7780(defun gnus-summary-prev-same-subject ()
7781 "Select previous article which has the same subject as current one."
7782 (interactive)
eec82323
LMI
7783 (gnus-summary-prev-article nil (gnus-summary-article-subject)))
7784
7785(defun gnus-summary-next-unread-same-subject ()
7786 "Select next unread article which has the same subject as current one."
7787 (interactive)
eec82323
LMI
7788 (gnus-summary-next-article t (gnus-summary-article-subject)))
7789
7790(defun gnus-summary-prev-unread-same-subject ()
7791 "Select previous unread article which has the same subject as current one."
7792 (interactive)
eec82323
LMI
7793 (gnus-summary-prev-article t (gnus-summary-article-subject)))
7794
7795(defun gnus-summary-first-unread-article ()
7796 "Select the first unread article.
7797Return nil if there are no unread articles."
7798 (interactive)
eec82323
LMI
7799 (prog1
7800 (when (gnus-summary-first-subject t)
7801 (gnus-summary-show-thread)
7802 (gnus-summary-first-subject t)
7803 (gnus-summary-display-article (gnus-summary-article-number)))
7804 (gnus-summary-position-point)))
7805
16409b0b
GM
7806(defun gnus-summary-first-unread-subject ()
7807 "Place the point on the subject line of the first unread article.
7808Return nil if there are no unread articles."
7809 (interactive)
7810 (prog1
7811 (when (gnus-summary-first-subject t)
7812 (gnus-summary-show-thread)
7813 (gnus-summary-first-subject t))
7814 (gnus-summary-position-point)))
7815
23f87bed
MB
7816(defun gnus-summary-first-unseen-subject ()
7817 "Place the point on the subject line of the first unseen article.
7818Return nil if there are no unseen articles."
7819 (interactive)
7820 (prog1
7821 (when (gnus-summary-first-subject nil nil t)
7822 (gnus-summary-show-thread)
7823 (gnus-summary-first-subject nil nil t))
7824 (gnus-summary-position-point)))
7825
7826(defun gnus-summary-first-unseen-or-unread-subject ()
7827 "Place the point on the subject line of the first unseen article or,
7828if all article have been seen, on the subject line of the first unread
7829article."
7830 (interactive)
7831 (prog1
7832 (unless (when (gnus-summary-first-subject nil nil t)
7833 (gnus-summary-show-thread)
7834 (gnus-summary-first-subject nil nil t))
7835 (when (gnus-summary-first-subject t)
7836 (gnus-summary-show-thread)
7837 (gnus-summary-first-subject t)))
7838 (gnus-summary-position-point)))
7839
eec82323
LMI
7840(defun gnus-summary-first-article ()
7841 "Select the first article.
7842Return nil if there are no articles."
7843 (interactive)
eec82323
LMI
7844 (prog1
7845 (when (gnus-summary-first-subject)
16409b0b
GM
7846 (gnus-summary-show-thread)
7847 (gnus-summary-first-subject)
7848 (gnus-summary-display-article (gnus-summary-article-number)))
eec82323
LMI
7849 (gnus-summary-position-point)))
7850
23f87bed
MB
7851(defun gnus-summary-best-unread-article (&optional arg)
7852 "Select the unread article with the highest score.
7853If given a prefix argument, select the next unread article that has a
7854score higher than the default score."
7855 (interactive "P")
7856 (let ((article (if arg
7857 (gnus-summary-better-unread-subject)
7858 (gnus-summary-best-unread-subject))))
7859 (if article
7860 (gnus-summary-goto-article article)
7861 (error "No unread articles"))))
7862
7863(defun gnus-summary-best-unread-subject ()
7864 "Select the unread subject with the highest score."
eec82323 7865 (interactive)
eec82323
LMI
7866 (let ((best -1000000)
7867 (data gnus-newsgroup-data)
7868 article score)
7869 (while data
7870 (and (gnus-data-unread-p (car data))
7871 (> (setq score
7872 (gnus-summary-article-score (gnus-data-number (car data))))
7873 best)
7874 (setq best score
7875 article (gnus-data-number (car data))))
7876 (setq data (cdr data)))
23f87bed
MB
7877 (when article
7878 (gnus-summary-goto-subject article))
7879 (gnus-summary-position-point)
7880 article))
7881
7882(defun gnus-summary-better-unread-subject ()
7883 "Select the first unread subject that has a score over the default score."
7884 (interactive)
7885 (let ((data gnus-newsgroup-data)
7886 article score)
7887 (while (and (setq article (gnus-data-number (car data)))
7888 (or (gnus-data-read-p (car data))
7889 (not (> (gnus-summary-article-score article)
7890 gnus-summary-default-score))))
7891 (setq data (cdr data)))
7892 (when article
7893 (gnus-summary-goto-subject article))
7894 (gnus-summary-position-point)
7895 article))
eec82323
LMI
7896
7897(defun gnus-summary-last-subject ()
7898 "Go to the last displayed subject line in the group."
7899 (let ((article (gnus-data-number (car (gnus-data-list t)))))
7900 (when article
7901 (gnus-summary-goto-subject article))))
7902
7903(defun gnus-summary-goto-article (article &optional all-headers force)
6748645f
LMI
7904 "Fetch ARTICLE (article number or Message-ID) and display it if it exists.
7905If ALL-HEADERS is non-nil, no header lines are hidden.
7906If FORCE, go to the article even if it isn't displayed. If FORCE
7907is a number, it is the line the article is to be displayed on."
eec82323
LMI
7908 (interactive
7909 (list
6748645f
LMI
7910 (completing-read
7911 "Article number or Message-ID: "
7912 (mapcar (lambda (number) (list (int-to-string number)))
7913 gnus-newsgroup-limit))
eec82323
LMI
7914 current-prefix-arg
7915 t))
7916 (prog1
6748645f 7917 (if (and (stringp article)
23f87bed 7918 (string-match "@\\|%40" article))
6748645f
LMI
7919 (gnus-summary-refer-article article)
7920 (when (stringp article)
7921 (setq article (string-to-number article)))
7922 (if (gnus-summary-goto-subject article force)
7923 (gnus-summary-display-article article all-headers)
7924 (gnus-message 4 "Couldn't go to article %s" article) nil))
eec82323
LMI
7925 (gnus-summary-position-point)))
7926
7927(defun gnus-summary-goto-last-article ()
7928 "Go to the previously read article."
7929 (interactive)
7930 (prog1
7931 (when gnus-last-article
6748645f 7932 (gnus-summary-goto-article gnus-last-article nil t))
eec82323
LMI
7933 (gnus-summary-position-point)))
7934
7935(defun gnus-summary-pop-article (number)
7936 "Pop one article off the history and go to the previous.
7937NUMBER articles will be popped off."
7938 (interactive "p")
7939 (let (to)
7940 (setq gnus-newsgroup-history
7941 (cdr (setq to (nthcdr number gnus-newsgroup-history))))
7942 (if to
6748645f 7943 (gnus-summary-goto-article (car to) nil t)
eec82323
LMI
7944 (error "Article history empty")))
7945 (gnus-summary-position-point))
7946
7947;; Summary commands and functions for limiting the summary buffer.
7948
7949(defun gnus-summary-limit-to-articles (n)
7950 "Limit the summary buffer to the next N articles.
7951If not given a prefix, use the process marked articles instead."
7952 (interactive "P")
eec82323
LMI
7953 (prog1
7954 (let ((articles (gnus-summary-work-articles n)))
7955 (setq gnus-newsgroup-processable nil)
7956 (gnus-summary-limit articles))
7957 (gnus-summary-position-point)))
7958
7959(defun gnus-summary-pop-limit (&optional total)
7960 "Restore the previous limit.
7961If given a prefix, remove all limits."
7962 (interactive "P")
eec82323
LMI
7963 (when total
7964 (setq gnus-newsgroup-limits
7965 (list (mapcar (lambda (h) (mail-header-number h))
7966 gnus-newsgroup-headers))))
7967 (unless gnus-newsgroup-limits
7968 (error "No limit to pop"))
7969 (prog1
7970 (gnus-summary-limit nil 'pop)
7971 (gnus-summary-position-point)))
7972
47b63dfa
SZ
7973(defun gnus-summary-limit-to-subject (subject &optional header not-matching)
7974 "Limit the summary buffer to articles that have subjects that match a regexp.
7975If NOT-MATCHING, excluding articles that have subjects that match a regexp."
a1506d29 7976 (interactive
47b63dfa
SZ
7977 (list (read-string (if current-prefix-arg
7978 "Exclude subject (regexp): "
a1506d29 7979 "Limit to subject (regexp): "))
47b63dfa 7980 nil current-prefix-arg))
eec82323
LMI
7981 (unless header
7982 (setq header "subject"))
7983 (when (not (equal "" subject))
7984 (prog1
7985 (let ((articles (gnus-summary-find-matching
a1506d29 7986 (or header "subject") subject 'all nil nil
47b63dfa 7987 not-matching)))
eec82323
LMI
7988 (unless articles
7989 (error "Found no matches for \"%s\"" subject))
7990 (gnus-summary-limit articles))
7991 (gnus-summary-position-point))))
7992
ef6e0ec7 7993(defun gnus-summary-limit-to-author (from &optional not-matching)
47b63dfa
SZ
7994 "Limit the summary buffer to articles that have authors that match a regexp.
7995If NOT-MATCHING, excluding articles that have authors that match a regexp."
a1506d29 7996 (interactive
47b63dfa
SZ
7997 (list (read-string (if current-prefix-arg
7998 "Exclude author (regexp): "
a1506d29 7999 "Limit to author (regexp): "))
ef6e0ec7
SZ
8000 current-prefix-arg))
8001 (gnus-summary-limit-to-subject from "from" not-matching))
eec82323 8002
01c52d31
MB
8003(defun gnus-summary-limit-to-recipient (recipient &optional not-matching)
8004 "Limit the summary buffer to articles with the given RECIPIENT.
8005
8006If NOT-MATCHING, exclude RECIPIENT.
8007
8008To and Cc headers are checked. You need to include them in
8009`nnmail-extra-headers'."
8010 ;; Unlike `rmail-summary-by-recipients', doesn't include From.
8011 (interactive
8012 (list (read-string (format "%s recipient (regexp): "
8013 (if current-prefix-arg "Exclude" "Limit to")))
8014 current-prefix-arg))
8015 (when (not (equal "" recipient))
8016 (prog1 (let* ((to
8017 (if (memq 'To nnmail-extra-headers)
8018 (gnus-summary-find-matching
8019 (cons 'extra 'To) recipient 'all nil nil
8020 not-matching)
8021 (gnus-message
8022 1 "`To' isn't present in `nnmail-extra-headers'")
8023 (sit-for 1)
8024 nil))
8025 (cc
8026 (if (memq 'Cc nnmail-extra-headers)
8027 (gnus-summary-find-matching
8028 (cons 'extra 'Cc) recipient 'all nil nil
8029 not-matching)
8030 (gnus-message
8031 1 "`Cc' isn't present in `nnmail-extra-headers'")
8032 (sit-for 1)
8033 nil))
8034 (articles
8035 (if not-matching
8036 ;; We need the numbers that are in both lists:
8037 (mapcar (lambda (a)
8038 (and (memq a to) a))
8039 cc)
8040 (nconc to cc))))
8041 (unless articles
8042 (error "Found no matches for \"%s\"" recipient))
8043 (gnus-summary-limit articles))
8044 (gnus-summary-position-point))))
8045
8046(defun gnus-summary-limit-to-address (address &optional not-matching)
8047 "Limit the summary buffer to articles with the given ADDRESS.
8048
8049If NOT-MATCHING, exclude ADDRESS.
8050
8051To, Cc and From headers are checked. You need to include `To' and `Cc'
8052in `nnmail-extra-headers'."
8053 (interactive
8054 (list (read-string (format "%s address (regexp): "
8055 (if current-prefix-arg "Exclude" "Limit to")))
8056 current-prefix-arg))
8057 (when (not (equal "" address))
8058 (prog1 (let* ((to
8059 (if (memq 'To nnmail-extra-headers)
8060 (gnus-summary-find-matching
8061 (cons 'extra 'To) address 'all nil nil
8062 not-matching)
8063 (gnus-message
8064 1 "`To' isn't present in `nnmail-extra-headers'")
8065 (sit-for 1)
8066 t))
8067 (cc
8068 (if (memq 'Cc nnmail-extra-headers)
8069 (gnus-summary-find-matching
8070 (cons 'extra 'Cc) address 'all nil nil
8071 not-matching)
8072 (gnus-message
8073 1 "`Cc' isn't present in `nnmail-extra-headers'")
8074 (sit-for 1)
8075 t))
8076 (from
8077 (gnus-summary-find-matching "from" address
8078 'all nil nil not-matching))
8079 (articles
8080 (if not-matching
8081 ;; We need the numbers that are in all lists:
8082 (if (eq cc t)
8083 (if (eq to t)
8084 from
8085 (mapcar (lambda (a) (car (memq a from))) to))
8086 (if (eq to t)
8087 (mapcar (lambda (a) (car (memq a from))) cc)
8088 (mapcar (lambda (a) (car (memq a from)))
8089 (mapcar (lambda (a) (car (memq a to)))
8090 cc))))
8091 (nconc (if (eq to t) nil to)
8092 (if (eq cc t) nil cc)
8093 from))))
8094 (unless articles
8095 (error "Found no matches for \"%s\"" address))
8096 (gnus-summary-limit articles))
8097 (gnus-summary-position-point))))
8098
8099(defun gnus-summary-limit-strange-charsets-predicate (header)
8100 (let ((string (concat (mail-header-subject header)
8101 (mail-header-from header)))
8102 charset found)
8103 (dotimes (i (1- (length string)))
8104 (setq charset (format "%s" (char-charset (aref string (1+ i)))))
8105 (when (string-match "unicode\\|big\\|japanese" charset)
8106 (setq found t)))
8107 found))
8108
8109(defun gnus-summary-limit-to-predicate (predicate)
8110 "Limit to articles where PREDICATE returns non-nil.
8111PREDICATE will be called with the header structures of the
8112articles."
8113 (let ((articles nil)
8114 (case-fold-search t))
8115 (dolist (header gnus-newsgroup-headers)
8116 (when (funcall predicate header)
8117 (push (mail-header-number header) articles)))
8118 (gnus-summary-limit (nreverse articles))))
8119
eec82323
LMI
8120(defun gnus-summary-limit-to-age (age &optional younger-p)
8121 "Limit the summary buffer to articles that are older than (or equal) AGE days.
8122If YOUNGER-P (the prefix) is non-nil, limit the summary buffer to
8123articles that are younger than AGE days."
16409b0b
GM
8124 (interactive
8125 (let ((younger current-prefix-arg)
8126 (days-got nil)
8127 days)
8128 (while (not days-got)
8129 (setq days (if younger
23f87bed
MB
8130 (read-string "Limit to articles younger than (in days, older when negative): ")
8131 (read-string
8132 "Limit to articles older than (in days, younger when negative): ")))
16409b0b
GM
8133 (when (> (length days) 0)
8134 (setq days (read days)))
8135 (if (numberp days)
23f87bed
MB
8136 (progn
8137 (setq days-got t)
01c52d31
MB
8138 (when (< days 0)
8139 (setq younger (not younger))
8140 (setq days (* days -1))))
16409b0b
GM
8141 (message "Please enter a number.")
8142 (sleep-for 1)))
8143 (list days younger)))
eec82323
LMI
8144 (prog1
8145 (let ((data gnus-newsgroup-data)
16409b0b 8146 (cutoff (days-to-time age))
eec82323
LMI
8147 articles d date is-younger)
8148 (while (setq d (pop data))
8149 (when (and (vectorp (gnus-data-header d))
8150 (setq date (mail-header-date (gnus-data-header d))))
16409b0b
GM
8151 (setq is-younger (time-less-p
8152 (time-since (condition-case ()
8153 (date-to-time date)
8154 (error '(0 0))))
eec82323 8155 cutoff))
6748645f
LMI
8156 (when (if younger-p
8157 is-younger
8158 (not is-younger))
eec82323
LMI
8159 (push (gnus-data-number d) articles))))
8160 (gnus-summary-limit (nreverse articles)))
8161 (gnus-summary-position-point)))
8162
47b63dfa 8163(defun gnus-summary-limit-to-extra (header regexp &optional not-matching)
16409b0b
GM
8164 "Limit the summary buffer to articles that match an 'extra' header."
8165 (interactive
8166 (let ((header
8167 (intern
23f87bed 8168 (gnus-completing-read-with-default
16409b0b 8169 (symbol-name (car gnus-extra-headers))
47b63dfa 8170 (if current-prefix-arg
81df110a
RF
8171 "Exclude extra header"
8172 "Limit extra header")
16409b0b
GM
8173 (mapcar (lambda (x)
8174 (cons (symbol-name x) x))
8175 gnus-extra-headers)
8176 nil
8177 t))))
8178 (list header
a1506d29 8179 (read-string (format "%s header %s (regexp): "
47b63dfa
SZ
8180 (if current-prefix-arg "Exclude" "Limit to")
8181 header))
8182 current-prefix-arg)))
16409b0b
GM
8183 (when (not (equal "" regexp))
8184 (prog1
8185 (let ((articles (gnus-summary-find-matching
a1506d29 8186 (cons 'extra header) regexp 'all nil nil
47b63dfa 8187 not-matching)))
16409b0b
GM
8188 (unless articles
8189 (error "Found no matches for \"%s\"" regexp))
8190 (gnus-summary-limit articles))
8191 (gnus-summary-position-point))))
8192
23f87bed
MB
8193(defun gnus-summary-limit-to-display-predicate ()
8194 "Limit the summary buffer to the predicated in the `display' group parameter."
8195 (interactive)
8196 (unless gnus-newsgroup-display
8197 (error "There is no `display' group parameter"))
8198 (let (articles)
8199 (dolist (number gnus-newsgroup-articles)
8200 (when (funcall gnus-newsgroup-display)
8201 (push number articles)))
8202 (gnus-summary-limit articles))
8203 (gnus-summary-position-point))
8204
eec82323
LMI
8205(defalias 'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
8206(make-obsolete
8207 'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
8208
8209(defun gnus-summary-limit-to-unread (&optional all)
8210 "Limit the summary buffer to articles that are not marked as read.
8211If ALL is non-nil, limit strictly to unread articles."
8212 (interactive "P")
8213 (if all
8214 (gnus-summary-limit-to-marks (char-to-string gnus-unread-mark))
8215 (gnus-summary-limit-to-marks
8216 ;; Concat all the marks that say that an article is read and have
8217 ;; those removed.
8218 (list gnus-del-mark gnus-read-mark gnus-ancient-mark
23f87bed 8219 gnus-killed-mark gnus-spam-mark gnus-kill-file-mark
eec82323
LMI
8220 gnus-low-score-mark gnus-expirable-mark
8221 gnus-canceled-mark gnus-catchup-mark gnus-sparse-mark
8222 gnus-duplicate-mark gnus-souped-mark)
8223 'reverse)))
8224
01c52d31
MB
8225(defun gnus-summary-limit-to-headers (match &optional reverse)
8226 "Limit the summary buffer to articles that have headers that match MATCH.
8227If REVERSE (the prefix), limit to articles that don't match."
8228 (interactive "sMatch headers (regexp): \nP")
8229 (gnus-summary-limit-to-bodies match reverse t))
8230
8231(defun gnus-summary-limit-to-bodies (match &optional reverse headersp)
8232 "Limit the summary buffer to articles that have bodies that match MATCH.
8233If REVERSE (the prefix), limit to articles that don't match."
8234 (interactive "sMatch body (regexp): \nP")
8235 (let ((articles nil)
8236 (gnus-select-article-hook nil) ;Disable hook.
8237 (gnus-article-prepare-hook nil)
8238 (gnus-use-article-prefetch nil)
8239 (gnus-keep-backlog nil)
8240 (gnus-break-pages nil)
8241 (gnus-summary-display-arrow nil)
8242 (gnus-updated-mode-lines nil)
8243 (gnus-auto-center-summary nil)
8244 (gnus-display-mime-function nil))
8245 (dolist (data gnus-newsgroup-data)
8246 (let (gnus-mark-article-hook)
8247 (gnus-summary-select-article t t nil (gnus-data-number data)))
8248 (save-excursion
8249 (set-buffer gnus-article-buffer)
8250 (article-goto-body)
8251 (let* ((case-fold-search t)
8252 (found (if headersp
8253 (re-search-backward match nil t)
8254 (re-search-forward match nil t))))
8255 (when (or (and found
8256 (not reverse))
8257 (and (not found)
8258 reverse))
8259 (push (gnus-data-number data) articles)))))
8260 (if (not articles)
8261 (message "No messages matched")
8262 (gnus-summary-limit articles)))
8263 (gnus-summary-position-point))
8264
8265(defun gnus-summary-limit-to-singletons (&optional threadsp)
8266 "Limit the summary buffer to articles that aren't part on any thread.
8267If THREADSP (the prefix), limit to articles that are in threads."
8268 (interactive "P")
8269 (let ((articles nil)
8270 thread-articles
8271 threads)
8272 (dolist (thread gnus-newsgroup-threads)
8273 (if (stringp (car thread))
8274 (dolist (thread (cdr thread))
8275 (push thread threads))
8276 (push thread threads)))
8277 (dolist (thread threads)
8278 (setq thread-articles (gnus-articles-in-thread thread))
8279 (when (or (and threadsp
8280 (> (length thread-articles) 1))
8281 (and (not threadsp)
8282 (= (length thread-articles) 1)))
8283 (setq articles (nconc thread-articles articles))))
8284 (if (not articles)
8285 (message "No messages matched")
8286 (gnus-summary-limit articles))
8287 (gnus-summary-position-point)))
8288
8289(defun gnus-summary-limit-to-replied (&optional unreplied)
8290 "Limit the summary buffer to replied articles.
8291If UNREPLIED (the prefix), limit to unreplied articles."
8292 (interactive "P")
8293 (if unreplied
8294 (gnus-summary-limit
8295 (gnus-set-difference gnus-newsgroup-articles
8296 gnus-newsgroup-replied))
8297 (gnus-summary-limit gnus-newsgroup-replied))
8298 (gnus-summary-position-point))
8299
eec82323
LMI
8300(defalias 'gnus-summary-delete-marked-with 'gnus-summary-limit-exclude-marks)
8301(make-obsolete 'gnus-summary-delete-marked-with
81ceefe2 8302 'gnus-summary-limit-exclude-marks)
eec82323
LMI
8303
8304(defun gnus-summary-limit-exclude-marks (marks &optional reverse)
8305 "Exclude articles that are marked with MARKS (e.g. \"DK\").
8306If REVERSE, limit the summary buffer to articles that are marked
8307with MARKS. MARKS can either be a string of marks or a list of marks.
8308Returns how many articles were removed."
8309 (interactive "sMarks: ")
8310 (gnus-summary-limit-to-marks marks t))
8311
8312(defun gnus-summary-limit-to-marks (marks &optional reverse)
8313 "Limit the summary buffer to articles that are marked with MARKS (e.g. \"DK\").
8314If REVERSE (the prefix), limit the summary buffer to articles that are
8315not marked with MARKS. MARKS can either be a string of marks or a
8316list of marks.
8317Returns how many articles were removed."
6748645f 8318 (interactive "sMarks: \nP")
eec82323
LMI
8319 (prog1
8320 (let ((data gnus-newsgroup-data)
8321 (marks (if (listp marks) marks
8322 (append marks nil))) ; Transform to list.
8323 articles)
8324 (while data
8325 (when (if reverse (not (memq (gnus-data-mark (car data)) marks))
8326 (memq (gnus-data-mark (car data)) marks))
8327 (push (gnus-data-number (car data)) articles))
8328 (setq data (cdr data)))
8329 (gnus-summary-limit articles))
8330 (gnus-summary-position-point)))
8331
23f87bed 8332(defun gnus-summary-limit-to-score (score)
eec82323 8333 "Limit to articles with score at or above SCORE."
23f87bed 8334 (interactive "NLimit to articles with score of at least: ")
eec82323
LMI
8335 (let ((data gnus-newsgroup-data)
8336 articles)
8337 (while data
8338 (when (>= (gnus-summary-article-score (gnus-data-number (car data)))
8339 score)
8340 (push (gnus-data-number (car data)) articles))
8341 (setq data (cdr data)))
8342 (prog1
8343 (gnus-summary-limit articles)
8344 (gnus-summary-position-point))))
8345
23f87bed
MB
8346(defun gnus-summary-limit-to-unseen ()
8347 "Limit to unseen articles."
8348 (interactive)
8349 (prog1
8350 (gnus-summary-limit gnus-newsgroup-unseen)
8351 (gnus-summary-position-point)))
8352
6748645f 8353(defun gnus-summary-limit-include-thread (id)
23f87bed
MB
8354 "Display all the hidden articles that is in the thread with ID in it.
8355When called interactively, ID is the Message-ID of the current
8356article."
6748645f
LMI
8357 (interactive (list (mail-header-id (gnus-summary-article-header))))
8358 (let ((articles (gnus-articles-in-thread
8359 (gnus-id-to-thread (gnus-root-id id)))))
8360 (prog1
8361 (gnus-summary-limit (nconc articles gnus-newsgroup-limit))
23f87bed
MB
8362 (gnus-summary-limit-include-matching-articles
8363 "subject"
8364 (regexp-quote (gnus-simplify-subject-re
8365 (mail-header-subject (gnus-id-to-header id)))))
6748645f
LMI
8366 (gnus-summary-position-point))))
8367
23f87bed
MB
8368(defun gnus-summary-limit-include-matching-articles (header regexp)
8369 "Display all the hidden articles that have HEADERs that match REGEXP."
8370 (interactive (list (read-string "Match on header: ")
8371 (read-string "Regexp: ")))
8372 (let ((articles (gnus-find-matching-articles header regexp)))
8373 (prog1
8374 (gnus-summary-limit (nconc articles gnus-newsgroup-limit))
8375 (gnus-summary-position-point))))
8376
8377(defun gnus-summary-insert-dormant-articles ()
8378 "Insert all the dormant articles for this group into the current buffer."
8379 (interactive)
8380 (let ((gnus-verbose (max 6 gnus-verbose)))
8381 (if (not gnus-newsgroup-dormant)
db629244 8382 (gnus-message 3 "No dormant articles for this group")
23f87bed
MB
8383 (gnus-summary-goto-subjects gnus-newsgroup-dormant))))
8384
01c52d31
MB
8385(defun gnus-summary-insert-ticked-articles ()
8386 "Insert ticked articles for this group into the current buffer."
8387 (interactive)
8388 (let ((gnus-verbose (max 6 gnus-verbose)))
8389 (if (not gnus-newsgroup-marked)
8390 (gnus-message 3 "No ticked articles for this group")
8391 (gnus-summary-goto-subjects gnus-newsgroup-marked))))
8392
eec82323 8393(defun gnus-summary-limit-include-dormant ()
6748645f
LMI
8394 "Display all the hidden articles that are marked as dormant.
8395Note that this command only works on a subset of the articles currently
8396fetched for this group."
eec82323 8397 (interactive)
eec82323
LMI
8398 (unless gnus-newsgroup-dormant
8399 (error "There are no dormant articles in this group"))
8400 (prog1
8401 (gnus-summary-limit (append gnus-newsgroup-dormant gnus-newsgroup-limit))
8402 (gnus-summary-position-point)))
8403
8404(defun gnus-summary-limit-exclude-dormant ()
8405 "Hide all dormant articles."
8406 (interactive)
eec82323
LMI
8407 (prog1
8408 (gnus-summary-limit-to-marks (list gnus-dormant-mark) 'reverse)
8409 (gnus-summary-position-point)))
8410
8411(defun gnus-summary-limit-exclude-childless-dormant ()
8412 "Hide all dormant articles that have no children."
8413 (interactive)
eec82323
LMI
8414 (let ((data (gnus-data-list t))
8415 articles d children)
8416 ;; Find all articles that are either not dormant or have
8417 ;; children.
8418 (while (setq d (pop data))
8419 (when (or (not (= (gnus-data-mark d) gnus-dormant-mark))
8420 (and (setq children
8421 (gnus-article-children (gnus-data-number d)))
8422 (let (found)
8423 (while children
8424 (when (memq (car children) articles)
8425 (setq children nil
8426 found t))
8427 (pop children))
8428 found)))
8429 (push (gnus-data-number d) articles)))
8430 ;; Do the limiting.
8431 (prog1
8432 (gnus-summary-limit articles)
8433 (gnus-summary-position-point))))
8434
8435(defun gnus-summary-limit-mark-excluded-as-read (&optional all)
8436 "Mark all unread excluded articles as read.
8437If ALL, mark even excluded ticked and dormants as read."
8438 (interactive "P")
23f87bed
MB
8439 (setq gnus-newsgroup-limit (sort gnus-newsgroup-limit '<))
8440 (let ((articles (gnus-sorted-ndifference
eec82323
LMI
8441 (sort
8442 (mapcar (lambda (h) (mail-header-number h))
8443 gnus-newsgroup-headers)
8444 '<)
23f87bed 8445 gnus-newsgroup-limit))
eec82323 8446 article)
6748645f 8447 (setq gnus-newsgroup-unreads
23f87bed
MB
8448 (gnus-sorted-intersection gnus-newsgroup-unreads
8449 gnus-newsgroup-limit))
eec82323
LMI
8450 (if all
8451 (setq gnus-newsgroup-dormant nil
8452 gnus-newsgroup-marked nil
8453 gnus-newsgroup-reads
8454 (nconc
8455 (mapcar (lambda (n) (cons n gnus-catchup-mark)) articles)
8456 gnus-newsgroup-reads))
8457 (while (setq article (pop articles))
8458 (unless (or (memq article gnus-newsgroup-dormant)
8459 (memq article gnus-newsgroup-marked))
8460 (push (cons article gnus-catchup-mark) gnus-newsgroup-reads))))))
8461
8462(defun gnus-summary-limit (articles &optional pop)
8463 (if pop
8464 ;; We pop the previous limit off the stack and use that.
8465 (setq articles (car gnus-newsgroup-limits)
8466 gnus-newsgroup-limits (cdr gnus-newsgroup-limits))
8467 ;; We use the new limit, so we push the old limit on the stack.
8468 (push gnus-newsgroup-limit gnus-newsgroup-limits))
8469 ;; Set the limit.
8470 (setq gnus-newsgroup-limit articles)
8471 (let ((total (length gnus-newsgroup-data))
8472 (data (gnus-data-find-list (gnus-summary-article-number)))
8473 (gnus-summary-mark-below nil) ; Inhibit this.
8474 found)
8475 ;; This will do all the work of generating the new summary buffer
8476 ;; according to the new limit.
8477 (gnus-summary-prepare)
8478 ;; Hide any threads, possibly.
23f87bed 8479 (gnus-summary-maybe-hide-threads)
eec82323
LMI
8480 ;; Try to return to the article you were at, or one in the
8481 ;; neighborhood.
8482 (when data
8483 ;; We try to find some article after the current one.
8484 (while data
8485 (when (gnus-summary-goto-subject (gnus-data-number (car data)) nil t)
8486 (setq data nil
8487 found t))
8488 (setq data (cdr data))))
8489 (unless found
8490 ;; If there is no data, that means that we were after the last
8491 ;; article. The same goes when we can't find any articles
8492 ;; after the current one.
8493 (goto-char (point-max))
8494 (gnus-summary-find-prev))
6748645f 8495 (gnus-set-mode-line 'summary)
eec82323
LMI
8496 ;; We return how many articles were removed from the summary
8497 ;; buffer as a result of the new limit.
8498 (- total (length gnus-newsgroup-data))))
8499
8500(defsubst gnus-invisible-cut-children (threads)
8501 (let ((num 0))
8502 (while threads
8503 (when (memq (mail-header-number (caar threads)) gnus-newsgroup-limit)
8504 (incf num))
8505 (pop threads))
8506 (< num 2)))
8507
8508(defsubst gnus-cut-thread (thread)
8509 "Go forwards in the thread until we find an article that we want to display."
8510 (when (or (eq gnus-fetch-old-headers 'some)
6748645f 8511 (eq gnus-fetch-old-headers 'invisible)
16409b0b 8512 (numberp gnus-fetch-old-headers)
eec82323
LMI
8513 (eq gnus-build-sparse-threads 'some)
8514 (eq gnus-build-sparse-threads 'more))
8515 ;; Deal with old-fetched headers and sparse threads.
8516 (while (and
8517 thread
8518 (or
8519 (gnus-summary-article-sparse-p (mail-header-number (car thread)))
8520 (gnus-summary-article-ancient-p
8521 (mail-header-number (car thread))))
6748645f
LMI
8522 (if (or (<= (length (cdr thread)) 1)
8523 (eq gnus-fetch-old-headers 'invisible))
8524 (setq gnus-newsgroup-limit
8525 (delq (mail-header-number (car thread))
8526 gnus-newsgroup-limit)
8527 thread (cadr thread))
8528 (when (gnus-invisible-cut-children (cdr thread))
8529 (let ((th (cdr thread)))
8530 (while th
8531 (if (memq (mail-header-number (caar th))
a8151ef7 8532 gnus-newsgroup-limit)
6748645f
LMI
8533 (setq thread (car th)
8534 th nil)
8535 (setq th (cdr th))))))))))
eec82323
LMI
8536 thread)
8537
8538(defun gnus-cut-threads (threads)
23f87bed 8539 "Cut off all uninteresting articles from the beginning of THREADS."
eec82323 8540 (when (or (eq gnus-fetch-old-headers 'some)
6748645f 8541 (eq gnus-fetch-old-headers 'invisible)
16409b0b 8542 (numberp gnus-fetch-old-headers)
eec82323
LMI
8543 (eq gnus-build-sparse-threads 'some)
8544 (eq gnus-build-sparse-threads 'more))
8545 (let ((th threads))
8546 (while th
8547 (setcar th (gnus-cut-thread (car th)))
8548 (setq th (cdr th)))))
8549 ;; Remove nixed out threads.
8550 (delq nil threads))
8551
8552(defun gnus-summary-initial-limit (&optional show-if-empty)
8553 "Figure out what the initial limit is supposed to be on group entry.
8554This entails weeding out unwanted dormants, low-scored articles,
8555fetch-old-headers verbiage, and so on."
8556 ;; Most groups have nothing to remove.
8557 (if (or gnus-inhibit-limiting
8558 (and (null gnus-newsgroup-dormant)
23f87bed 8559 (eq gnus-newsgroup-display 'gnus-not-ignore)
eec82323 8560 (not (eq gnus-fetch-old-headers 'some))
16409b0b 8561 (not (numberp gnus-fetch-old-headers))
6748645f 8562 (not (eq gnus-fetch-old-headers 'invisible))
eec82323
LMI
8563 (null gnus-summary-expunge-below)
8564 (not (eq gnus-build-sparse-threads 'some))
8565 (not (eq gnus-build-sparse-threads 'more))
8566 (null gnus-thread-expunge-below)
8567 (not gnus-use-nocem)))
8568 () ; Do nothing.
8569 (push gnus-newsgroup-limit gnus-newsgroup-limits)
8570 (setq gnus-newsgroup-limit nil)
8571 (mapatoms
8572 (lambda (node)
8573 (unless (car (symbol-value node))
8574 ;; These threads have no parents -- they are roots.
8575 (let ((nodes (cdr (symbol-value node)))
8576 thread)
8577 (while nodes
8578 (if (and gnus-thread-expunge-below
8579 (< (gnus-thread-total-score (car nodes))
8580 gnus-thread-expunge-below))
8581 (gnus-expunge-thread (pop nodes))
8582 (setq thread (pop nodes))
8583 (gnus-summary-limit-children thread))))))
8584 gnus-newsgroup-dependencies)
8585 ;; If this limitation resulted in an empty group, we might
8586 ;; pop the previous limit and use it instead.
8587 (when (and (not gnus-newsgroup-limit)
8588 show-if-empty)
8589 (setq gnus-newsgroup-limit (pop gnus-newsgroup-limits)))
8590 gnus-newsgroup-limit))
8591
8592(defun gnus-summary-limit-children (thread)
8593 "Return 1 if this subthread is visible and 0 if it is not."
8594 ;; First we get the number of visible children to this thread. This
8595 ;; is done by recursing down the thread using this function, so this
8596 ;; will really go down to a leaf article first, before slowly
8597 ;; working its way up towards the root.
8598 (when thread
04b61ae9 8599 (let* ((max-lisp-eval-depth (max 5000 max-lisp-eval-depth))
23f87bed 8600 (children
eec82323
LMI
8601 (if (cdr thread)
8602 (apply '+ (mapcar 'gnus-summary-limit-children
8603 (cdr thread)))
8604 0))
8605 (number (mail-header-number (car thread)))
8606 score)
8607 (if (and
8608 (not (memq number gnus-newsgroup-marked))
8609 (or
8610 ;; If this article is dormant and has absolutely no visible
8611 ;; children, then this article isn't visible.
8612 (and (memq number gnus-newsgroup-dormant)
8613 (zerop children))
8614 ;; If this is "fetch-old-headered" and there is no
8615 ;; visible children, then we don't want this article.
16409b0b
GM
8616 (and (or (eq gnus-fetch-old-headers 'some)
8617 (numberp gnus-fetch-old-headers))
eec82323
LMI
8618 (gnus-summary-article-ancient-p number)
8619 (zerop children))
6748645f
LMI
8620 ;; If this is "fetch-old-headered" and `invisible', then
8621 ;; we don't want this article.
8622 (and (eq gnus-fetch-old-headers 'invisible)
8623 (gnus-summary-article-ancient-p number))
eec82323
LMI
8624 ;; If this is a sparsely inserted article with no children,
8625 ;; we don't want it.
8626 (and (eq gnus-build-sparse-threads 'some)
8627 (gnus-summary-article-sparse-p number)
8628 (zerop children))
8629 ;; If we use expunging, and this article is really
8630 ;; low-scored, then we don't want this article.
8631 (when (and gnus-summary-expunge-below
8632 (< (setq score
8633 (or (cdr (assq number gnus-newsgroup-scored))
8634 gnus-summary-default-score))
8635 gnus-summary-expunge-below))
8636 ;; We increase the expunge-tally here, but that has
8637 ;; nothing to do with the limits, really.
8638 (incf gnus-newsgroup-expunged-tally)
8639 ;; We also mark as read here, if that's wanted.
8640 (when (and gnus-summary-mark-below
8641 (< score gnus-summary-mark-below))
8642 (setq gnus-newsgroup-unreads
8643 (delq number gnus-newsgroup-unreads))
8644 (if gnus-newsgroup-auto-expire
8645 (push number gnus-newsgroup-expirable)
8646 (push (cons number gnus-low-score-mark)
8647 gnus-newsgroup-reads)))
8648 t)
23f87bed
MB
8649 ;; Do the `display' group parameter.
8650 (and gnus-newsgroup-display
8651 (not (funcall gnus-newsgroup-display)))
eec82323 8652 ;; Check NoCeM things.
01c52d31
MB
8653 (when (and gnus-use-nocem
8654 (gnus-nocem-unwanted-article-p
8655 (mail-header-id (car thread))))
8656 (setq gnus-newsgroup-unreads
8657 (delq number gnus-newsgroup-unreads))
8658 t)))
eec82323
LMI
8659 ;; Nope, invisible article.
8660 0
8661 ;; Ok, this article is to be visible, so we add it to the limit
8662 ;; and return 1.
8663 (push number gnus-newsgroup-limit)
8664 1))))
8665
8666(defun gnus-expunge-thread (thread)
8667 "Mark all articles in THREAD as read."
8668 (let* ((number (mail-header-number (car thread))))
8669 (incf gnus-newsgroup-expunged-tally)
8670 ;; We also mark as read here, if that's wanted.
8671 (setq gnus-newsgroup-unreads
8672 (delq number gnus-newsgroup-unreads))
8673 (if gnus-newsgroup-auto-expire
8674 (push number gnus-newsgroup-expirable)
8675 (push (cons number gnus-low-score-mark)
8676 gnus-newsgroup-reads)))
8677 ;; Go recursively through all subthreads.
8678 (mapcar 'gnus-expunge-thread (cdr thread)))
8679
8680;; Summary article oriented commands
8681
8682(defun gnus-summary-refer-parent-article (n)
8683 "Refer parent article N times.
8684If N is negative, go to ancestor -N instead.
8685The difference between N and the number of articles fetched is returned."
8686 (interactive "p")
eec82323
LMI
8687 (let ((skip 1)
8688 error header ref)
8689 (when (not (natnump n))
8690 (setq skip (abs n)
8691 n 1))
8692 (while (and (> n 0)
8693 (not error))
8694 (setq header (gnus-summary-article-header))
8695 (if (and (eq (mail-header-number header)
8696 (cdr gnus-article-current))
8697 (equal gnus-newsgroup-name
8698 (car gnus-article-current)))
8699 ;; If we try to find the parent of the currently
8700 ;; displayed article, then we take a look at the actual
8701 ;; References header, since this is slightly more
8702 ;; reliable than the References field we got from the
8703 ;; server.
8704 (save-excursion
8705 (set-buffer gnus-original-article-buffer)
8706 (nnheader-narrow-to-headers)
8707 (unless (setq ref (message-fetch-field "references"))
23f87bed
MB
8708 (when (setq ref (message-fetch-field "in-reply-to"))
8709 (setq ref (gnus-extract-message-id-from-in-reply-to ref))))
eec82323
LMI
8710 (widen))
8711 (setq ref
8712 ;; It's not the current article, so we take a bet on
8713 ;; the value we got from the server.
8714 (mail-header-references header)))
8715 (if (and ref
8716 (not (equal ref "")))
8717 (unless (gnus-summary-refer-article (gnus-parent-id ref skip))
8718 (gnus-message 1 "Couldn't find parent"))
8719 (gnus-message 1 "No references in article %d"
8720 (gnus-summary-article-number))
8721 (setq error t))
8722 (decf n))
8723 (gnus-summary-position-point)
8724 n))
8725
8726(defun gnus-summary-refer-references ()
8727 "Fetch all articles mentioned in the References header.
6748645f 8728Return the number of articles fetched."
eec82323 8729 (interactive)
eec82323
LMI
8730 (let ((ref (mail-header-references (gnus-summary-article-header)))
8731 (current (gnus-summary-article-number))
8732 (n 0))
8733 (if (or (not ref)
8734 (equal ref ""))
8735 (error "No References in the current article")
8736 ;; For each Message-ID in the References header...
8737 (while (string-match "<[^>]*>" ref)
8738 (incf n)
8739 ;; ... fetch that article.
8740 (gnus-summary-refer-article
8741 (prog1 (match-string 0 ref)
8742 (setq ref (substring ref (match-end 0))))))
8743 (gnus-summary-goto-subject current)
8744 (gnus-summary-position-point)
8745 n)))
8746
6748645f
LMI
8747(defun gnus-summary-refer-thread (&optional limit)
8748 "Fetch all articles in the current thread.
8749If LIMIT (the numerical prefix), fetch that many old headers instead
8750of what's specified by the `gnus-refer-thread-limit' variable."
8751 (interactive "P")
8752 (let ((id (mail-header-id (gnus-summary-article-header)))
8753 (limit (if limit (prefix-numeric-value limit)
8754 gnus-refer-thread-limit)))
6748645f
LMI
8755 (unless (eq gnus-fetch-old-headers 'invisible)
8756 (gnus-message 5 "Fetching headers for %s..." gnus-newsgroup-name)
8757 ;; Retrieve the headers and read them in.
23f87bed
MB
8758 (if (eq (if (numberp limit)
8759 (gnus-retrieve-headers
8760 (list (min
8761 (+ (mail-header-number
8762 (gnus-summary-article-header))
8763 limit)
8764 gnus-newsgroup-end))
8765 gnus-newsgroup-name (* limit 2))
8766 ;; gnus-refer-thread-limit is t, i.e. fetch _all_
8767 ;; headers.
8768 (gnus-retrieve-headers (list gnus-newsgroup-end)
8769 gnus-newsgroup-name limit))
6748645f
LMI
8770 'nov)
8771 (gnus-build-all-threads)
23f87bed 8772 (error "Can't fetch thread from back ends that don't support NOV"))
6748645f
LMI
8773 (gnus-message 5 "Fetching headers for %s...done" gnus-newsgroup-name))
8774 (gnus-summary-limit-include-thread id)))
8775
16409b0b
GM
8776(defun gnus-summary-refer-article (message-id)
8777 "Fetch an article specified by MESSAGE-ID."
8778 (interactive "sMessage-ID: ")
eec82323
LMI
8779 (when (and (stringp message-id)
8780 (not (zerop (length message-id))))
23f87bed 8781 (setq message-id (gnus-replace-in-string message-id " " ""))
eec82323
LMI
8782 ;; Construct the correct Message-ID if necessary.
8783 ;; Suggested by tale@pawl.rpi.edu.
8784 (unless (string-match "^<" message-id)
8785 (setq message-id (concat "<" message-id)))
8786 (unless (string-match ">$" message-id)
8787 (setq message-id (concat message-id ">")))
23f87bed
MB
8788 ;; People often post MIDs from URLs, so unhex it:
8789 (unless (string-match "@" message-id)
8790 (setq message-id (gnus-url-unhex-string message-id)))
eec82323
LMI
8791 (let* ((header (gnus-id-to-header message-id))
8792 (sparse (and header
8793 (gnus-summary-article-sparse-p
a8151ef7
LMI
8794 (mail-header-number header))
8795 (memq (mail-header-number header)
16409b0b
GM
8796 gnus-newsgroup-limit)))
8797 number)
6748645f
LMI
8798 (cond
8799 ;; If the article is present in the buffer we just go to it.
8800 ((and header
8801 (or (not (gnus-summary-article-sparse-p
8802 (mail-header-number header)))
8803 sparse))
8804 (prog1
8805 (gnus-summary-goto-article
8806 (mail-header-number header) nil t)
8807 (when sparse
8808 (gnus-summary-update-article (mail-header-number header)))))
8809 (t
16409b0b
GM
8810 ;; We fetch the article.
8811 (catch 'found
8812 (dolist (gnus-override-method (gnus-refer-article-methods))
23f87bed
MB
8813 (when (and (gnus-check-server gnus-override-method)
8814 ;; Fetch the header,
8815 (setq number (gnus-summary-insert-subject message-id)))
8816 ;; and display the article.
eec82323 8817 (gnus-summary-select-article nil nil nil number)
16409b0b
GM
8818 (throw 'found t)))
8819 (gnus-message 3 "Couldn't fetch article %s" message-id)))))))
8820
8821(defun gnus-refer-article-methods ()
8f688cb0 8822 "Return a list of referable methods."
16409b0b
GM
8823 (cond
8824 ;; No method, so we default to current and native.
8825 ((null gnus-refer-article-method)
8826 (list gnus-current-select-method gnus-select-method))
8827 ;; Current.
8828 ((eq 'current gnus-refer-article-method)
8829 (list gnus-current-select-method))
8830 ;; List of select methods.
d4dfaa19
DL
8831 ((not (and (symbolp (car gnus-refer-article-method))
8832 (assq (car gnus-refer-article-method) nnoo-definition-alist)))
16409b0b
GM
8833 (let (out)
8834 (dolist (method gnus-refer-article-method)
8835 (push (if (eq 'current method)
8836 gnus-current-select-method
8837 method)
8838 out))
8839 (nreverse out)))
8840 ;; One single select method.
8841 (t
8842 (list gnus-refer-article-method))))
6748645f
LMI
8843
8844(defun gnus-summary-edit-parameters ()
8845 "Edit the group parameters of the current group."
8846 (interactive)
8847 (gnus-group-edit-group gnus-newsgroup-name 'params))
eec82323 8848
16409b0b
GM
8849(defun gnus-summary-customize-parameters ()
8850 "Customize the group parameters of the current group."
8851 (interactive)
8852 (gnus-group-customize gnus-newsgroup-name))
8853
eec82323
LMI
8854(defun gnus-summary-enter-digest-group (&optional force)
8855 "Enter an nndoc group based on the current article.
8856If FORCE, force a digest interpretation. If not, try
8857to guess what the document format is."
8858 (interactive "P")
eec82323 8859 (let ((conf gnus-current-window-configuration))
23f87bed
MB
8860 (save-window-excursion
8861 (save-excursion
8862 (let (gnus-article-prepare-hook
8863 gnus-display-mime-function
8864 gnus-break-pages)
8865 (gnus-summary-select-article))))
eec82323
LMI
8866 (setq gnus-current-window-configuration conf)
8867 (let* ((name (format "%s-%d"
8868 (gnus-group-prefixed-name
8869 gnus-newsgroup-name (list 'nndoc ""))
01c52d31 8870 (with-current-buffer gnus-summary-buffer
eec82323
LMI
8871 gnus-current-article)))
8872 (ogroup gnus-newsgroup-name)
8873 (params (append (gnus-info-params (gnus-get-info ogroup))
8874 (list (cons 'to-group ogroup))
23f87bed 8875 (list (cons 'parent-group ogroup))
eec82323
LMI
8876 (list (cons 'save-article-group ogroup))))
8877 (case-fold-search t)
8878 (buf (current-buffer))
16409b0b 8879 dig to-address)
eec82323 8880 (save-excursion
16409b0b
GM
8881 (set-buffer gnus-original-article-buffer)
8882 ;; Have the digest group inherit the main mail address of
8883 ;; the parent article.
23f87bed
MB
8884 (when (setq to-address (or (gnus-fetch-field "reply-to")
8885 (gnus-fetch-field "from")))
343d6628
MB
8886 (setq params
8887 (append
8888 (list (cons 'to-address
8889 (funcall gnus-decode-encoded-address-function
8890 to-address))))))
eec82323
LMI
8891 (setq dig (nnheader-set-temp-buffer " *gnus digest buffer*"))
8892 (insert-buffer-substring gnus-original-article-buffer)
8893 ;; Remove lines that may lead nndoc to misinterpret the
8894 ;; document type.
8895 (narrow-to-region
8896 (goto-char (point-min))
8897 (or (search-forward "\n\n" nil t) (point)))
8898 (goto-char (point-min))
16409b0b 8899 (delete-matching-lines "^Path:\\|^From ")
eec82323
LMI
8900 (widen))
8901 (unwind-protect
23f87bed 8902 (if (let ((gnus-newsgroup-ephemeral-charset gnus-newsgroup-charset)
16409b0b
GM
8903 (gnus-newsgroup-ephemeral-ignored-charsets
8904 gnus-newsgroup-ignored-charsets))
8905 (gnus-group-read-ephemeral-group
8906 name `(nndoc ,name (nndoc-address ,(get-buffer dig))
8907 (nndoc-article-type
23f87bed
MB
8908 ,(if force 'mbox 'guess)))
8909 t nil nil nil
8910 `((adapt-file . ,(gnus-score-file-name gnus-newsgroup-name
8911 "ADAPT")))))
16409b0b 8912 ;; Make all postings to this group go to the parent group.
23f87bed
MB
8913 (nconc (gnus-info-params (gnus-get-info name))
8914 params)
8915 ;; Couldn't select this doc group.
8916 (switch-to-buffer buf)
8917 (gnus-set-global-variables)
8918 (gnus-configure-windows 'summary)
8919 (gnus-message 3 "Article couldn't be entered?"))
eec82323
LMI
8920 (kill-buffer dig)))))
8921
8922(defun gnus-summary-read-document (n)
8923 "Open a new group based on the current article(s).
8924This will allow you to read digests and other similar
8925documents as newsgroups.
8926Obeys the standard process/prefix convention."
8927 (interactive "P")
01c52d31 8928 (let* ((ogroup gnus-newsgroup-name)
eec82323
LMI
8929 (params (append (gnus-info-params (gnus-get-info ogroup))
8930 (list (cons 'to-group ogroup))))
01c52d31
MB
8931 group egroup groups vgroup)
8932 (dolist (article (gnus-summary-work-articles n))
eec82323
LMI
8933 (setq group (format "%s-%d" gnus-newsgroup-name article))
8934 (gnus-summary-remove-process-mark article)
8935 (when (gnus-summary-display-article article)
8936 (save-excursion
16409b0b 8937 (with-temp-buffer
eec82323
LMI
8938 (insert-buffer-substring gnus-original-article-buffer)
8939 ;; Remove some headers that may lead nndoc to make
8940 ;; the wrong guess.
8941 (message-narrow-to-head)
8942 (goto-char (point-min))
01c52d31 8943 (delete-matching-lines "^Path:\\|^From ")
eec82323
LMI
8944 (widen)
8945 (if (setq egroup
8946 (gnus-group-read-ephemeral-group
8947 group `(nndoc ,group (nndoc-address ,(current-buffer))
8948 (nndoc-article-type guess))
8949 t nil t))
8950 (progn
23f87bed 8951 ;; Make all postings to this group go to the parent group.
eec82323
LMI
8952 (nconc (gnus-info-params (gnus-get-info egroup))
8953 params)
8954 (push egroup groups))
8955 ;; Couldn't select this doc group.
8956 (gnus-error 3 "Article couldn't be entered"))))))
8957 ;; Now we have selected all the documents.
8958 (cond
8959 ((not groups)
8960 (error "None of the articles could be interpreted as documents"))
8961 ((gnus-group-read-ephemeral-group
8962 (setq vgroup (format
8963 "nnvirtual:%s-%s" gnus-newsgroup-name
8964 (format-time-string "%Y%m%dT%H%M%S" (current-time))))
8965 `(nnvirtual ,vgroup (nnvirtual-component-groups ,groups))
8966 t
8967 (cons (current-buffer) 'summary)))
8968 (t
8969 (error "Couldn't select virtual nndoc group")))))
8970
8971(defun gnus-summary-isearch-article (&optional regexp-p)
8972 "Do incremental search forward on the current article.
8973If REGEXP-P (the prefix) is non-nil, do regexp isearch."
8974 (interactive "P")
eec82323
LMI
8975 (gnus-summary-select-article)
8976 (gnus-configure-windows 'article)
8977 (gnus-eval-in-buffer-window gnus-article-buffer
6748645f
LMI
8978 (save-restriction
8979 (widen)
8980 (isearch-forward regexp-p))))
eec82323 8981
01c52d31
MB
8982(defun gnus-summary-repeat-search-article-forward ()
8983 "Repeat the previous search forwards."
8984 (interactive)
8985 (unless gnus-last-search-regexp
8986 (error "No previous search"))
8987 (gnus-summary-search-article-forward gnus-last-search-regexp))
8988
8989(defun gnus-summary-repeat-search-article-backward ()
8990 "Repeat the previous search backwards."
8991 (interactive)
8992 (unless gnus-last-search-regexp
8993 (error "No previous search"))
8994 (gnus-summary-search-article-forward gnus-last-search-regexp t))
8995
eec82323
LMI
8996(defun gnus-summary-search-article-forward (regexp &optional backward)
8997 "Search for an article containing REGEXP forward.
8998If BACKWARD, search backward instead."
8999 (interactive
9000 (list (read-string
9001 (format "Search article %s (regexp%s): "
9002 (if current-prefix-arg "backward" "forward")
9003 (if gnus-last-search-regexp
9004 (concat ", default " gnus-last-search-regexp)
9005 "")))
9006 current-prefix-arg))
eec82323
LMI
9007 (if (string-equal regexp "")
9008 (setq regexp (or gnus-last-search-regexp ""))
23f87bed
MB
9009 (setq gnus-last-search-regexp regexp)
9010 (setq gnus-article-before-search gnus-current-article))
9011 ;; Intentionally set gnus-last-article.
9012 (setq gnus-last-article gnus-article-before-search)
9013 (let ((gnus-last-article gnus-last-article))
9014 (if (gnus-summary-search-article regexp backward)
9015 (gnus-summary-show-thread)
abc40aab 9016 (signal 'search-failed (list regexp)))))
eec82323
LMI
9017
9018(defun gnus-summary-search-article-backward (regexp)
9019 "Search for an article containing REGEXP backward."
9020 (interactive
9021 (list (read-string
9022 (format "Search article backward (regexp%s): "
9023 (if gnus-last-search-regexp
9024 (concat ", default " gnus-last-search-regexp)
9025 "")))))
9026 (gnus-summary-search-article-forward regexp 'backward))
9027
9028(defun gnus-summary-search-article (regexp &optional backward)
9029 "Search for an article containing REGEXP.
9030Optional argument BACKWARD means do search for backward.
9031`gnus-select-article-hook' is not called during the search."
a8151ef7
LMI
9032 ;; We have to require this here to make sure that the following
9033 ;; dynamic binding isn't shadowed by autoloading.
9034 (require 'gnus-async)
16409b0b 9035 (require 'gnus-art)
eec82323 9036 (let ((gnus-select-article-hook nil) ;Disable hook.
16409b0b 9037 (gnus-article-prepare-hook nil)
eec82323
LMI
9038 (gnus-mark-article-hook nil) ;Inhibit marking as read.
9039 (gnus-use-article-prefetch nil)
9040 (gnus-xmas-force-redisplay nil) ;Inhibit XEmacs redisplay.
a8151ef7 9041 (gnus-use-trees nil) ;Inhibit updating tree buffer.
23f87bed
MB
9042 (gnus-visual nil)
9043 (gnus-keep-backlog nil)
9044 (gnus-break-pages nil)
9045 (gnus-summary-display-arrow nil)
9046 (gnus-updated-mode-lines nil)
9047 (gnus-auto-center-summary nil)
eec82323 9048 (sum (current-buffer))
16409b0b 9049 (gnus-display-mime-function nil)
eec82323
LMI
9050 (found nil)
9051 point)
9052 (gnus-save-hidden-threads
9053 (gnus-summary-select-article)
9054 (set-buffer gnus-article-buffer)
16409b0b 9055 (goto-char (window-point (get-buffer-window (current-buffer))))
eec82323
LMI
9056 (when backward
9057 (forward-line -1))
9058 (while (not found)
9059 (gnus-message 7 "Searching article: %d..." (cdr gnus-article-current))
9060 (if (if backward
9061 (re-search-backward regexp nil t)
9062 (re-search-forward regexp nil t))
9063 ;; We found the regexp.
9064 (progn
9065 (setq found 'found)
9066 (beginning-of-line)
9067 (set-window-start
9068 (get-buffer-window (current-buffer))
9069 (point))
9070 (forward-line 1)
16409b0b
GM
9071 (set-window-point
9072 (get-buffer-window (current-buffer))
9073 (point))
eec82323
LMI
9074 (set-buffer sum)
9075 (setq point (point)))
9076 ;; We didn't find it, so we go to the next article.
9077 (set-buffer sum)
9078 (setq found 'not)
9079 (while (eq found 'not)
9080 (if (not (if backward (gnus-summary-find-prev)
9081 (gnus-summary-find-next)))
9082 ;; No more articles.
9083 (setq found t)
9084 ;; Select the next article and adjust point.
9085 (unless (gnus-summary-article-sparse-p
9086 (gnus-summary-article-number))
9087 (setq found nil)
9088 (gnus-summary-select-article)
9089 (set-buffer gnus-article-buffer)
9090 (widen)
9091 (goto-char (if backward (point-max) (point-min))))))))
9092 (gnus-message 7 ""))
9093 ;; Return whether we found the regexp.
9094 (when (eq found 'found)
9095 (goto-char point)
9096 (gnus-summary-show-thread)
9097 (gnus-summary-goto-subject gnus-current-article)
9098 (gnus-summary-position-point)
9099 t)))
9100
23f87bed
MB
9101(defun gnus-find-matching-articles (header regexp)
9102 "Return a list of all articles that match REGEXP on HEADER.
9103This search includes all articles in the current group that Gnus has
9104fetched headers for, whether they are displayed or not."
9105 (let ((articles nil)
9106 (func `(lambda (h) (,(intern (concat "mail-header-" header)) h)))
9107 (case-fold-search t))
9108 (dolist (header gnus-newsgroup-headers)
9109 (when (string-match regexp (funcall func header))
9110 (push (mail-header-number header) articles)))
9111 (nreverse articles)))
9112
eec82323 9113(defun gnus-summary-find-matching (header regexp &optional backward unread
47b63dfa 9114 not-case-fold not-matching)
eec82323
LMI
9115 "Return a list of all articles that match REGEXP on HEADER.
9116The search stars on the current article and goes forwards unless
9117BACKWARD is non-nil. If BACKWARD is `all', do all articles.
9118If UNREAD is non-nil, only unread articles will
9119be taken into consideration. If NOT-CASE-FOLD, case won't be folded
a1506d29 9120in the comparisons. If NOT-MATCHING, return a list of all articles that
47b63dfa
SZ
9121not match REGEXP on HEADER."
9122 (let ((case-fold-search (not not-case-fold))
16409b0b
GM
9123 articles d func)
9124 (if (consp header)
9125 (if (eq (car header) 'extra)
9126 (setq func
9127 `(lambda (h)
9128 (or (cdr (assq ',(cdr header) (mail-header-extra h)))
9129 "")))
9130 (error "%s is an invalid header" header))
9131 (unless (fboundp (intern (concat "mail-header-" header)))
9132 (error "%s is not a valid header" header))
9133 (setq func `(lambda (h) (,(intern (concat "mail-header-" header)) h))))
47b63dfa
SZ
9134 (dolist (d (if (eq backward 'all)
9135 gnus-newsgroup-data
9136 (gnus-data-find-list
9137 (gnus-summary-article-number)
9138 (gnus-data-list backward))))
9139 (when (and (or (not unread) ; We want all articles...
9140 (gnus-data-unread-p d)) ; Or just unreads.
9141 (vectorp (gnus-data-header d)) ; It's not a pseudo.
9142 (if not-matching
a1506d29 9143 (not (string-match
47b63dfa
SZ
9144 regexp
9145 (funcall func (gnus-data-header d))))
9146 (string-match regexp
9147 (funcall func (gnus-data-header d)))))
9148 (push (gnus-data-number d) articles))) ; Success!
eec82323
LMI
9149 (nreverse articles)))
9150
9151(defun gnus-summary-execute-command (header regexp command &optional backward)
9152 "Search forward for an article whose HEADER matches REGEXP and execute COMMAND.
9153If HEADER is an empty string (or nil), the match is done on the entire
9154article. If BACKWARD (the prefix) is non-nil, search backward instead."
9155 (interactive
9156 (list (let ((completion-ignore-case t))
9157 (completing-read
9158 "Header name: "
23f87bed
MB
9159 (mapcar (lambda (header) (list (format "%s" header)))
9160 (append
9161 '("Number" "Subject" "From" "Lines" "Date"
9162 "Message-ID" "Xref" "References" "Body")
9163 gnus-extra-headers))
eec82323
LMI
9164 nil 'require-match))
9165 (read-string "Regexp: ")
9166 (read-key-sequence "Command: ")
9167 current-prefix-arg))
9168 (when (equal header "Body")
9169 (setq header ""))
eec82323
LMI
9170 ;; Hidden thread subtrees must be searched as well.
9171 (gnus-summary-show-all-threads)
9172 ;; We don't want to change current point nor window configuration.
9173 (save-excursion
9174 (save-window-excursion
23f87bed
MB
9175 (let (gnus-visual
9176 gnus-treat-strip-trailing-blank-lines
9177 gnus-treat-strip-leading-blank-lines
9178 gnus-treat-strip-multiple-blank-lines
9179 gnus-treat-hide-boring-headers
9180 gnus-treat-fold-newsgroups
9181 gnus-article-prepare-hook)
9182 (gnus-message 6 "Executing %s..." (key-description command))
9183 ;; We'd like to execute COMMAND interactively so as to give arguments.
9184 (gnus-execute header regexp
9185 `(call-interactively ',(key-binding command))
9186 backward)
9187 (gnus-message 6 "Executing %s...done" (key-description command))))))
eec82323
LMI
9188
9189(defun gnus-summary-beginning-of-article ()
9190 "Scroll the article back to the beginning."
9191 (interactive)
eec82323
LMI
9192 (gnus-summary-select-article)
9193 (gnus-configure-windows 'article)
9194 (gnus-eval-in-buffer-window gnus-article-buffer
9195 (widen)
9196 (goto-char (point-min))
23f87bed 9197 (when gnus-break-pages
eec82323
LMI
9198 (gnus-narrow-to-page))))
9199
9200(defun gnus-summary-end-of-article ()
9201 "Scroll to the end of the article."
9202 (interactive)
eec82323
LMI
9203 (gnus-summary-select-article)
9204 (gnus-configure-windows 'article)
9205 (gnus-eval-in-buffer-window gnus-article-buffer
9206 (widen)
9207 (goto-char (point-max))
9208 (recenter -3)
23f87bed
MB
9209 (when gnus-break-pages
9210 (when (re-search-backward page-delimiter nil t)
9211 (narrow-to-region (match-end 0) (point-max)))
eec82323
LMI
9212 (gnus-narrow-to-page))))
9213
23f87bed
MB
9214(defun gnus-summary-print-truncate-and-quote (string &optional len)
9215 "Truncate to LEN and quote all \"(\"'s in STRING."
9216 (gnus-replace-in-string (if (and len (> (length string) len))
9217 (substring string 0 len)
9218 string)
9219 "[()]" "\\\\\\&"))
9220
6748645f 9221(defun gnus-summary-print-article (&optional filename n)
23f87bed
MB
9222 "Generate and print a PostScript image of the process-marked (mail) articles.
9223
9224If used interactively, print the current article if none are
9225process-marked. With prefix arg, prompt the user for the name of the
9226file to save in.
6748645f 9227
23f87bed
MB
9228When used from Lisp, accept two optional args FILENAME and N. N means
9229to print the next N articles. If N is negative, print the N previous
9230articles. If N is nil and articles have been marked with the process
9231mark, print these instead.
eec82323 9232
16409b0b 9233If the optional first argument FILENAME is nil, send the image to the
6748645f
LMI
9234printer. If FILENAME is a string, save the PostScript image in a file with
9235that name. If FILENAME is a number, prompt the user for the name of the file
eec82323 9236to save in."
676a7cc9 9237 (interactive (list (ps-print-preprint current-prefix-arg)))
6748645f
LMI
9238 (dolist (article (gnus-summary-work-articles n))
9239 (gnus-summary-select-article nil nil 'pseudo article)
9240 (gnus-eval-in-buffer-window gnus-article-buffer
23f87bed 9241 (gnus-print-buffer))
676a7cc9
SZ
9242 (gnus-summary-remove-process-mark article))
9243 (ps-despool filename))
eec82323 9244
23f87bed
MB
9245(defun gnus-print-buffer ()
9246 (let ((buffer (generate-new-buffer " *print*")))
9247 (unwind-protect
9248 (progn
9249 (copy-to-buffer buffer (point-min) (point-max))
9250 (set-buffer buffer)
9251 (gnus-remove-text-with-property 'gnus-decoration)
9252 (when (gnus-visual-p 'article-highlight 'highlight)
9253 ;; Copy-to-buffer doesn't copy overlay. So redo
9254 ;; highlight.
9255 (let ((gnus-article-buffer buffer))
9256 (gnus-article-highlight-citation t)
9257 (gnus-article-highlight-signature)
9258 (gnus-article-emphasize)
9259 (gnus-article-delete-invisible-text)))
9260 (let ((ps-left-header
9261 (list
9262 (concat "("
9263 (gnus-summary-print-truncate-and-quote
9264 (mail-header-subject gnus-current-headers)
9265 66) ")")
9266 (concat "("
9267 (gnus-summary-print-truncate-and-quote
9268 (mail-header-from gnus-current-headers)
9269 45) ")")))
9270 (ps-right-header
9271 (list
9272 "/pagenumberstring load"
9273 (concat "("
9274 (mail-header-date gnus-current-headers) ")"))))
9275 (gnus-run-hooks 'gnus-ps-print-hook)
9276 (save-excursion
9277 (if window-system
9278 (ps-spool-buffer-with-faces)
9279 (ps-spool-buffer)))))
9280 (kill-buffer buffer))))
9281
eec82323 9282(defun gnus-summary-show-article (&optional arg)
23f87bed 9283 "Force redisplaying of the current article.
16409b0b
GM
9284If ARG (the prefix) is a number, show the article with the charset
9285defined in `gnus-summary-show-article-charset-alist', or the charset
23f87bed 9286input.
16409b0b 9287If ARG (the prefix) is non-nil and not a number, show the raw article
23f87bed
MB
9288without any article massaging functions being run. Normally, the key
9289strokes are `C-u g'."
eec82323 9290 (interactive "P")
16409b0b
GM
9291 (cond
9292 ((numberp arg)
23f87bed 9293 (gnus-summary-show-article t)
16409b0b
GM
9294 (let ((gnus-newsgroup-charset
9295 (or (cdr (assq arg gnus-summary-show-article-charset-alist))
23f87bed
MB
9296 (mm-read-coding-system
9297 "View as charset: " ;; actually it is coding system.
01c52d31 9298 (with-current-buffer gnus-article-buffer
23f87bed 9299 (mm-detect-coding-region (point) (point-max))))))
16409b0b 9300 (gnus-newsgroup-ignored-charsets 'gnus-all))
23f87bed
MB
9301 (gnus-summary-select-article nil 'force)
9302 (let ((deps gnus-newsgroup-dependencies)
9303 head header lines)
9304 (save-excursion
9305 (set-buffer gnus-original-article-buffer)
9306 (save-restriction
9307 (message-narrow-to-head)
9308 (setq head (buffer-string))
9309 (goto-char (point-min))
9310 (unless (re-search-forward "^lines:[ \t]\\([0-9]+\\)" nil t)
9311 (goto-char (point-max))
9312 (widen)
9313 (setq lines (1- (count-lines (point) (point-max))))))
9314 (with-temp-buffer
9315 (insert (format "211 %d Article retrieved.\n"
9316 (cdr gnus-article-current)))
9317 (insert head)
9318 (if lines (insert (format "Lines: %d\n" lines)))
9319 (insert ".\n")
9320 (let ((nntp-server-buffer (current-buffer)))
9321 (setq header (car (gnus-get-newsgroup-headers deps t))))))
9322 (gnus-data-set-header
9323 (gnus-data-find (cdr gnus-article-current))
9324 header)
9325 (gnus-summary-update-article-line
9326 (cdr gnus-article-current) header)
9327 (when (gnus-summary-goto-subject (cdr gnus-article-current) nil t)
9328 (gnus-summary-update-secondary-mark (cdr gnus-article-current))))))
16409b0b
GM
9329 ((not arg)
9330 ;; Select the article the normal way.
9331 (gnus-summary-select-article nil 'force))
9332 (t
9333 ;; We have to require this here to make sure that the following
9334 ;; dynamic binding isn't shadowed by autoloading.
9335 (require 'gnus-async)
9336 (require 'gnus-art)
eec82323
LMI
9337 ;; Bind the article treatment functions to nil.
9338 (let ((gnus-have-all-headers t)
eec82323 9339 gnus-article-prepare-hook
16409b0b
GM
9340 gnus-article-decode-hook
9341 gnus-display-mime-function
9342 gnus-break-pages)
9343 ;; Destroy any MIME parts.
9344 (when (gnus-buffer-live-p gnus-article-buffer)
9345 (save-excursion
9346 (set-buffer gnus-article-buffer)
9347 (mm-destroy-parts gnus-article-mime-handles)
9348 ;; Set it to nil for safety reason.
9349 (setq gnus-article-mime-handle-alist nil)
9350 (setq gnus-article-mime-handles nil)))
9351 (gnus-summary-select-article nil 'force))))
eec82323
LMI
9352 (gnus-summary-goto-subject gnus-current-article)
9353 (gnus-summary-position-point))
9354
23f87bed
MB
9355(defun gnus-summary-show-raw-article ()
9356 "Show the raw article without any article massaging functions being run."
9357 (interactive)
9358 (gnus-summary-show-article t))
9359
eec82323
LMI
9360(defun gnus-summary-verbose-headers (&optional arg)
9361 "Toggle permanent full header display.
9362If ARG is a positive number, turn header display on.
9363If ARG is a negative number, turn header display off."
9364 (interactive "P")
eec82323
LMI
9365 (setq gnus-show-all-headers
9366 (cond ((or (not (numberp arg))
9367 (zerop arg))
9368 (not gnus-show-all-headers))
9369 ((natnump arg)
9370 t)))
9371 (gnus-summary-show-article))
9372
9373(defun gnus-summary-toggle-header (&optional arg)
9374 "Show the headers if they are hidden, or hide them if they are shown.
9375If ARG is a positive number, show the entire header.
9376If ARG is a negative number, hide the unwanted header lines."
9377 (interactive "P")
23f87bed
MB
9378 (let ((window (and (gnus-buffer-live-p gnus-article-buffer)
9379 (get-buffer-window gnus-article-buffer t))))
9380 (with-current-buffer gnus-article-buffer
9381 (widen)
9382 (article-narrow-to-head)
16409b0b
GM
9383 (let* ((buffer-read-only nil)
9384 (inhibit-point-motion-hooks t)
23f87bed
MB
9385 (hidden (if (numberp arg)
9386 (>= arg 0)
f0096211
MB
9387 (or (not (looking-at "[^ \t\n]+:"))
9388 (gnus-article-hidden-text-p 'headers))))
23f87bed
MB
9389 s e)
9390 (delete-region (point-min) (point-max))
667e0ba6
SM
9391 (with-current-buffer gnus-original-article-buffer
9392 (goto-char (setq s (point-min)))
23f87bed
MB
9393 (setq e (if (search-forward "\n\n" nil t)
9394 (1- (point))
9395 (point-max))))
667e0ba6 9396 (insert-buffer-substring gnus-original-article-buffer s e)
23f87bed
MB
9397 (run-hooks 'gnus-article-decode-hook)
9398 (if hidden
9399 (let ((gnus-treat-hide-headers nil)
9400 (gnus-treat-hide-boring-headers nil))
9401 (gnus-delete-wash-type 'headers)
9402 (gnus-treat-article 'head))
9403 (gnus-treat-article 'head))
9404 (widen)
9405 (if window
9406 (set-window-start window (goto-char (point-min))))
9407 (if gnus-break-pages
9408 (gnus-narrow-to-page)
9409 (when (gnus-visual-p 'page-marker)
9410 (let ((buffer-read-only nil))
9411 (gnus-remove-text-with-property 'gnus-prev)
9412 (gnus-remove-text-with-property 'gnus-next))))
16409b0b 9413 (gnus-set-mode-line 'article)))))
eec82323
LMI
9414
9415(defun gnus-summary-show-all-headers ()
9416 "Make all header lines visible."
9417 (interactive)
23f87bed 9418 (gnus-summary-toggle-header 1))
eec82323 9419
eec82323
LMI
9420(defun gnus-summary-caesar-message (&optional arg)
9421 "Caesar rotate the current article by 13.
01c52d31
MB
9422With a non-numerical prefix, also rotate headers. A numerical
9423prefix specifies how many places to rotate each letter forward."
eec82323 9424 (interactive "P")
eec82323
LMI
9425 (gnus-summary-select-article)
9426 (let ((mail-header-separator ""))
9427 (gnus-eval-in-buffer-window gnus-article-buffer
9428 (save-restriction
9429 (widen)
9430 (let ((start (window-start))
9431 buffer-read-only)
01c52d31
MB
9432 (if (equal arg '(4))
9433 (message-caesar-buffer-body nil t)
9434 (message-caesar-buffer-body arg))
ff4d3926
MB
9435 (set-window-start (get-buffer-window (current-buffer)) start)))))
9436 ;; Create buttons and stuff...
9437 (gnus-treat-article nil))
eec82323 9438
01c52d31
MB
9439(defun gnus-summary-idna-message (&optional arg)
9440 "Decode IDNA encoded domain names in the current articles.
9441IDNA encoded domain names looks like `xn--bar'. If a string
9442remain unencoded after running this function, it is likely an
9443invalid IDNA string (`xn--bar' is invalid).
9444
9445You must have GNU Libidn (`http://www.gnu.org/software/libidn/')
9446installed for this command to work."
9447 (interactive "P")
9448 (if (not (and (condition-case nil (require 'idna)
9449 (file-error))
9450 (mm-coding-system-p 'utf-8)
9451 (executable-find (symbol-value 'idna-program))))
9452 (gnus-message
9453 5 "GNU Libidn not installed properly (`idn' or `idna.el' missing)")
9454 (gnus-summary-select-article)
9455 (let ((mail-header-separator ""))
9456 (gnus-eval-in-buffer-window gnus-article-buffer
9457 (save-restriction
9458 (widen)
9459 (let ((start (window-start))
9460 buffer-read-only)
9461 (while (re-search-forward "\\(xn--[-0-9a-z]+\\)" nil t)
9462 (replace-match (idna-to-unicode (match-string 1))))
9463 (set-window-start (get-buffer-window (current-buffer)) start)))))))
23f87bed
MB
9464
9465(defun gnus-summary-morse-message (&optional arg)
9466 "Morse decode the current article."
9467 (interactive "P")
9468 (gnus-summary-select-article)
9469 (let ((mail-header-separator ""))
9470 (gnus-eval-in-buffer-window gnus-article-buffer
9471 (save-excursion
9472 (save-restriction
9473 (widen)
9474 (let ((pos (window-start))
9475 buffer-read-only)
9476 (goto-char (point-min))
9477 (when (message-goto-body)
9478 (gnus-narrow-to-body))
9479 (goto-char (point-min))
01c52d31 9480