(declare-function): Add compatibility declaration.
[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
b890d447 2198 "Y" gnus-uu-decode-yenc
23f87bed
MB
2199 "p" gnus-uu-decode-postscript
2200 "P" gnus-uu-decode-postscript-and-save)
2201
2202(gnus-define-keys
2203 (gnus-uu-extract-view-map "v" gnus-uu-extract-map)
2204 "u" gnus-uu-decode-uu-view
2205 "U" gnus-uu-decode-uu-and-save-view
2206 "s" gnus-uu-decode-unshar-view
2207 "S" gnus-uu-decode-unshar-and-save-view
2208 "o" gnus-uu-decode-save-view
2209 "O" gnus-uu-decode-save-view
2210 "b" gnus-uu-decode-binhex-view
2211 "B" gnus-uu-decode-binhex-view
2212 "p" gnus-uu-decode-postscript-view
2213 "P" gnus-uu-decode-postscript-and-save-view)
2214
2215(defvar gnus-article-post-menu nil)
2216
2217(defconst gnus-summary-menu-maxlen 20)
2218
2219(defun gnus-summary-menu-split (menu)
2220 ;; If we have lots of elements, divide them into groups of 20
2221 ;; and make a pane (or submenu) for each one.
2222 (if (> (length menu) (/ (* gnus-summary-menu-maxlen 3) 2))
2223 (let ((menu menu) sublists next
2224 (i 1))
2225 (while menu
2226 ;; Pull off the next gnus-summary-menu-maxlen elements
2227 ;; and make them the next element of sublist.
2228 (setq next (nthcdr gnus-summary-menu-maxlen menu))
2229 (if next
2230 (setcdr (nthcdr (1- gnus-summary-menu-maxlen) menu)
2231 nil))
2232 (setq sublists (cons (cons (format "%s ... %s" (aref (car menu) 0)
2233 (aref (car (last menu)) 0)) menu)
2234 sublists))
2235 (setq i (1+ i))
2236 (setq menu next))
2237 (nreverse sublists))
2238 ;; Few elements--put them all in one pane.
2239 menu))
eec82323
LMI
2240
2241(defun gnus-summary-make-menu-bar ()
2242 (gnus-turn-off-edit-menu 'summary)
2243
2244 (unless (boundp 'gnus-summary-misc-menu)
2245
2246 (easy-menu-define
23f87bed
MB
2247 gnus-summary-kill-menu gnus-summary-mode-map ""
2248 (cons
2249 "Score"
2250 (nconc
2251 (list
2252 ["Customize" gnus-score-customize t])
2253 (gnus-make-score-map 'increase)
2254 (gnus-make-score-map 'lower)
2255 '(("Mark"
2256 ["Kill below" gnus-summary-kill-below t]
2257 ["Mark above" gnus-summary-mark-above t]
2258 ["Tick above" gnus-summary-tick-above t]
2259 ["Clear above" gnus-summary-clear-above t])
2260 ["Current score" gnus-summary-current-score t]
2261 ["Set score" gnus-summary-set-score t]
2262 ["Switch current score file..." gnus-score-change-score-file t]
2263 ["Set mark below..." gnus-score-set-mark-below t]
2264 ["Set expunge below..." gnus-score-set-expunge-below t]
2265 ["Edit current score file" gnus-score-edit-current-scores t]
2266 ["Edit score file" gnus-score-edit-file t]
2267 ["Trace score" gnus-score-find-trace t]
2268 ["Find words" gnus-score-find-favourite-words t]
2269 ["Rescore buffer" gnus-summary-rescore t]
2270 ["Increase score..." gnus-summary-increase-score t]
2271 ["Lower score..." gnus-summary-lower-score t]))))
2272
2273 ;; Define both the Article menu in the summary buffer and the
2274 ;; equivalent Commands menu in the article buffer here for
2275 ;; consistency.
6748645f 2276 (let ((innards
23f87bed
MB
2277 `(("Hide"
2278 ["All" gnus-article-hide t]
2279 ["Headers" gnus-article-hide-headers t]
2280 ["Signature" gnus-article-hide-signature t]
2281 ["Citation" gnus-article-hide-citation t]
16409b0b 2282 ["List identifiers" gnus-article-hide-list-identifiers t]
16409b0b 2283 ["Banner" gnus-article-strip-banner t]
23f87bed
MB
2284 ["Boring headers" gnus-article-hide-boring-headers t])
2285 ("Highlight"
2286 ["All" gnus-article-highlight t]
2287 ["Headers" gnus-article-highlight-headers t]
2288 ["Signature" gnus-article-highlight-signature t]
2289 ["Citation" gnus-article-highlight-citation t])
16409b0b
GM
2290 ("MIME"
2291 ["Words" gnus-article-decode-mime-words t]
2292 ["Charset" gnus-article-decode-charset t]
2293 ["QP" gnus-article-de-quoted-unreadable t]
2294 ["Base64" gnus-article-de-base64-unreadable t]
23f87bed
MB
2295 ["View MIME buttons" gnus-summary-display-buttonized t]
2296 ["View all" gnus-mime-view-all-parts t]
2297 ["Verify and Decrypt" gnus-summary-force-verify-and-decrypt t]
2298 ["Encrypt body" gnus-article-encrypt-body
2299 :active (not (gnus-group-read-only-p))
2300 ,@(if (featurep 'xemacs) nil
2301 '(:help "Encrypt the message body on disk"))]
2302 ["Extract all parts..." gnus-summary-save-parts t]
2303 ("Multipart"
2304 ["Repair multipart" gnus-summary-repair-multipart t]
2305 ["Pipe part..." gnus-article-pipe-part t]
2306 ["Inline part" gnus-article-inline-part t]
01c52d31 2307 ["View part as type..." gnus-article-view-part-as-type t]
23f87bed
MB
2308 ["Encrypt body" gnus-article-encrypt-body
2309 :active (not (gnus-group-read-only-p))
2310 ,@(if (featurep 'xemacs) nil
2311 '(:help "Encrypt the message body on disk"))]
2312 ["View part externally" gnus-article-view-part-externally t]
01c52d31 2313 ["View HTML parts in browser" gnus-article-browse-html-article t]
23f87bed
MB
2314 ["View part with charset..." gnus-article-view-part-as-charset t]
2315 ["Copy part" gnus-article-copy-part t]
2316 ["Save part..." gnus-article-save-part t]
2317 ["View part" gnus-article-view-part t]))
2318 ("Date"
2319 ["Local" gnus-article-date-local t]
2320 ["ISO8601" gnus-article-date-iso8601 t]
2321 ["UT" gnus-article-date-ut t]
2322 ["Original" gnus-article-date-original t]
2323 ["Lapsed" gnus-article-date-lapsed t]
2324 ["User-defined" gnus-article-date-user t])
2325 ("Display"
2326 ["Remove images" gnus-article-remove-images t]
2327 ["Toggle smiley" gnus-treat-smiley t]
2328 ["Show X-Face" gnus-article-display-x-face t]
2329 ["Show picons in From" gnus-treat-from-picon t]
2330 ["Show picons in mail headers" gnus-treat-mail-picon t]
2331 ["Show picons in news headers" gnus-treat-newsgroups-picon t]
2332 ("View as different encoding"
2333 ,@(gnus-summary-menu-split
2334 (mapcar
2335 (lambda (cs)
2336 ;; Since easymenu under Emacs doesn't allow
2337 ;; lambda forms for menu commands, we should
2338 ;; provide intern'ed function symbols.
2339 (let ((command (intern (format "\
2340gnus-summary-show-article-from-menu-as-charset-%s" cs))))
2341 (fset command
2342 `(lambda ()
2343 (interactive)
2344 (let ((gnus-summary-show-article-charset-alist
2345 '((1 . ,cs))))
2346 (gnus-summary-show-article 1))))
2347 `[,(symbol-name cs) ,command t]))
2348 (sort (if (fboundp 'coding-system-list)
2349 (coding-system-list)
2350 (mapcar 'car mm-mime-mule-charset-alist))
2351 'string<)))))
2352 ("Washing"
2353 ("Remove Blanks"
2354 ["Leading" gnus-article-strip-leading-blank-lines t]
2355 ["Multiple" gnus-article-strip-multiple-blank-lines t]
2356 ["Trailing" gnus-article-remove-trailing-blank-lines t]
2357 ["All of the above" gnus-article-strip-blank-lines t]
2358 ["All" gnus-article-strip-all-blank-lines t]
2359 ["Leading space" gnus-article-strip-leading-space t]
2360 ["Trailing space" gnus-article-strip-trailing-space t]
2361 ["Leading space in headers"
2362 gnus-article-remove-leading-whitespace t])
2363 ["Overstrike" gnus-article-treat-overstrike t]
2364 ["Dumb quotes" gnus-article-treat-dumbquotes t]
2365 ["Emphasis" gnus-article-emphasize t]
2366 ["Word wrap" gnus-article-fill-cited-article t]
16409b0b 2367 ["Fill long lines" gnus-article-fill-long-lines t]
01c52d31 2368 ["Toggle truncate long lines" gnus-article-toggle-truncate-lines t]
16409b0b 2369 ["Capitalize sentences" gnus-article-capitalize-sentences t]
23f87bed
MB
2370 ["Remove CR" gnus-article-remove-cr t]
2371 ["Quoted-Printable" gnus-article-de-quoted-unreadable t]
2372 ["Base64" gnus-article-de-base64-unreadable t]
2373 ["Rot 13" gnus-summary-caesar-message
2374 ,@(if (featurep 'xemacs) '(t)
2375 '(:help "\"Caesar rotate\" article by 13"))]
01c52d31 2376 ["De-IDNA" gnus-summary-idna-message t]
23f87bed
MB
2377 ["Morse decode" gnus-summary-morse-message t]
2378 ["Unix pipe..." gnus-summary-pipe-message t]
2379 ["Add buttons" gnus-article-add-buttons t]
2380 ["Add buttons to head" gnus-article-add-buttons-to-head t]
2381 ["Stop page breaking" gnus-summary-stop-page-breaking t]
2382 ["Verbose header" gnus-summary-verbose-headers t]
2383 ["Toggle header" gnus-summary-toggle-header t]
2384 ["Unfold headers" gnus-article-treat-unfold-headers t]
2385 ["Fold newsgroups" gnus-article-treat-fold-newsgroups t]
16409b0b 2386 ["Html" gnus-article-wash-html t]
23f87bed
MB
2387 ["Unsplit URLs" gnus-article-unsplit-urls t]
2388 ["Verify X-PGP-Sig" gnus-article-verify-x-pgp-sig t]
2389 ["Decode HZ" gnus-article-decode-HZ t]
01c52d31 2390 ["ANSI sequences" gnus-article-treat-ansi-sequences t]
23f87bed
MB
2391 ("(Outlook) Deuglify"
2392 ["Unwrap lines" gnus-article-outlook-unwrap-lines t]
2393 ["Repair attribution" gnus-article-outlook-repair-attribution t]
2394 ["Rearrange citation" gnus-article-outlook-rearrange-citation t]
2395 ["Full (Outlook) deuglify"
2396 gnus-article-outlook-deuglify-article t])
2397 )
2398 ("Output"
2399 ["Save in default format..." gnus-summary-save-article
2400 ,@(if (featurep 'xemacs) '(t)
2401 '(:help "Save article using default method"))]
2402 ["Save in file..." gnus-summary-save-article-file
2403 ,@(if (featurep 'xemacs) '(t)
2404 '(:help "Save article in file"))]
2405 ["Save in Unix mail format..." gnus-summary-save-article-mail t]
2406 ["Save in MH folder..." gnus-summary-save-article-folder t]
2407 ["Save in VM folder..." gnus-summary-save-article-vm t]
2408 ["Save in RMAIL mbox..." gnus-summary-save-article-rmail t]
2409 ["Save body in file..." gnus-summary-save-article-body-file t]
2410 ["Pipe through a filter..." gnus-summary-pipe-output t]
2411 ["Add to SOUP packet" gnus-soup-add-article t]
2412 ["Print with Muttprint..." gnus-summary-muttprint t]
531e5812
MB
2413 ["Print" gnus-summary-print-article
2414 ,@(if (featurep 'xemacs) '(t)
2415 '(:help "Generate and print a PostScript image"))])
2416 ("Copy, move,... (Backend)"
707f2b38 2417 ,@(if (featurep 'xemacs) nil
531e5812 2418 '(:help "Copying, moving, expiring articles..."))
23f87bed
MB
2419 ["Respool article..." gnus-summary-respool-article t]
2420 ["Move article..." gnus-summary-move-article
2421 (gnus-check-backend-function
2422 'request-move-article gnus-newsgroup-name)]
2423 ["Copy article..." gnus-summary-copy-article t]
2424 ["Crosspost article..." gnus-summary-crosspost-article
2425 (gnus-check-backend-function
2426 'request-replace-article gnus-newsgroup-name)]
2427 ["Import file..." gnus-summary-import-article
2428 (gnus-check-backend-function
2429 'request-accept-article gnus-newsgroup-name)]
2430 ["Create article..." gnus-summary-create-article
2431 (gnus-check-backend-function
2432 'request-accept-article gnus-newsgroup-name)]
2433 ["Check if posted" gnus-summary-article-posted-p t]
2434 ["Edit article" gnus-summary-edit-article
2435 (not (gnus-group-read-only-p))]
2436 ["Delete article" gnus-summary-delete-article
2437 (gnus-check-backend-function
2438 'request-expire-articles gnus-newsgroup-name)]
2439 ["Query respool" gnus-summary-respool-query t]
6748645f 2440 ["Trace respool" gnus-summary-respool-trace t]
23f87bed
MB
2441 ["Delete expirable articles" gnus-summary-expire-articles-now
2442 (gnus-check-backend-function
2443 'request-expire-articles gnus-newsgroup-name)])
2444 ("Extract"
2445 ["Uudecode" gnus-uu-decode-uu
2446 ,@(if (featurep 'xemacs) '(t)
2447 '(:help "Decode uuencoded article(s)"))]
2448 ["Uudecode and save" gnus-uu-decode-uu-and-save t]
2449 ["Unshar" gnus-uu-decode-unshar t]
2450 ["Unshar and save" gnus-uu-decode-unshar-and-save t]
2451 ["Save" gnus-uu-decode-save t]
2452 ["Binhex" gnus-uu-decode-binhex t]
2453 ["Postscript" gnus-uu-decode-postscript t]
2454 ["All MIME parts" gnus-summary-save-parts t])
2455 ("Cache"
2456 ["Enter article" gnus-cache-enter-article t]
2457 ["Remove article" gnus-cache-remove-article t])
16409b0b 2458 ["Translate" gnus-article-babel t]
23f87bed 2459 ["Select article buffer" gnus-summary-select-article-buffer t]
01c52d31 2460 ["Make article buffer sticky" gnus-sticky-article t]
23f87bed
MB
2461 ["Enter digest buffer" gnus-summary-enter-digest-group t]
2462 ["Isearch article..." gnus-summary-isearch-article t]
2463 ["Beginning of the article" gnus-summary-beginning-of-article t]
2464 ["End of the article" gnus-summary-end-of-article t]
2465 ["Fetch parent of article" gnus-summary-refer-parent-article t]
2466 ["Fetch referenced articles" gnus-summary-refer-references t]
2467 ["Fetch current thread" gnus-summary-refer-thread t]
2468 ["Fetch article with id..." gnus-summary-refer-article t]
2469 ["Setup Mailing List Params" gnus-mailing-list-insinuate t]
2470 ["Redisplay" gnus-summary-show-article t]
2471 ["Raw article" gnus-summary-show-raw-article :keys "C-u g"])))
6748645f 2472 (easy-menu-define
23f87bed
MB
2473 gnus-summary-article-menu gnus-summary-mode-map ""
2474 (cons "Article" innards))
6748645f 2475
1653df0f
SZ
2476 (if (not (keymapp gnus-summary-article-menu))
2477 (easy-menu-define
2478 gnus-article-commands-menu gnus-article-mode-map ""
2479 (cons "Commands" innards))
2480 ;; in Emacs, don't share menu.
a1506d29 2481 (setq gnus-article-commands-menu
1653df0f
SZ
2482 (copy-keymap gnus-summary-article-menu))
2483 (define-key gnus-article-mode-map [menu-bar commands]
2484 (cons "Commands" gnus-article-commands-menu))))
eec82323
LMI
2485
2486 (easy-menu-define
23f87bed
MB
2487 gnus-summary-thread-menu gnus-summary-mode-map ""
2488 '("Threads"
2489 ["Find all messages in thread" gnus-summary-refer-thread t]
2490 ["Toggle threading" gnus-summary-toggle-threads t]
2491 ["Hide threads" gnus-summary-hide-all-threads t]
2492 ["Show threads" gnus-summary-show-all-threads t]
2493 ["Hide thread" gnus-summary-hide-thread t]
2494 ["Show thread" gnus-summary-show-thread t]
2495 ["Go to next thread" gnus-summary-next-thread t]
2496 ["Go to previous thread" gnus-summary-prev-thread t]
2497 ["Go down thread" gnus-summary-down-thread t]
2498 ["Go up thread" gnus-summary-up-thread t]
2499 ["Top of thread" gnus-summary-top-thread t]
2500 ["Mark thread as read" gnus-summary-kill-thread t]
01c52d31 2501 ["Mark thread as expired" gnus-summary-expire-thread t]
23f87bed
MB
2502 ["Lower thread score" gnus-summary-lower-thread t]
2503 ["Raise thread score" gnus-summary-raise-thread t]
2504 ["Rethread current" gnus-summary-rethread-current t]))
eec82323
LMI
2505
2506 (easy-menu-define
23f87bed
MB
2507 gnus-summary-post-menu gnus-summary-mode-map ""
2508 `("Post"
2509 ["Send a message (mail or news)" gnus-summary-post-news
2510 ,@(if (featurep 'xemacs) '(t)
531e5812 2511 '(:help "Compose a new message (mail or news)"))]
23f87bed
MB
2512 ["Followup" gnus-summary-followup
2513 ,@(if (featurep 'xemacs) '(t)
2514 '(:help "Post followup to this article"))]
2515 ["Followup and yank" gnus-summary-followup-with-original
2516 ,@(if (featurep 'xemacs) '(t)
2517 '(:help "Post followup to this article, quoting its contents"))]
2518 ["Supersede article" gnus-summary-supersede-article t]
2519 ["Cancel article" gnus-summary-cancel-article
2520 ,@(if (featurep 'xemacs) '(t)
2521 '(:help "Cancel an article you posted"))]
2522 ["Reply" gnus-summary-reply t]
2523 ["Reply and yank" gnus-summary-reply-with-original t]
2524 ["Wide reply" gnus-summary-wide-reply t]
2525 ["Wide reply and yank" gnus-summary-wide-reply-with-original
2526 ,@(if (featurep 'xemacs) '(t)
2527 '(:help "Mail a reply, quoting this article"))]
2528 ["Very wide reply" gnus-summary-very-wide-reply t]
2529 ["Very wide reply and yank" gnus-summary-very-wide-reply-with-original
2530 ,@(if (featurep 'xemacs) '(t)
2531 '(:help "Mail a very wide reply, quoting this article"))]
2532 ["Mail forward" gnus-summary-mail-forward t]
2533 ["Post forward" gnus-summary-post-forward t]
2534 ["Digest and mail" gnus-uu-digest-mail-forward t]
2535 ["Digest and post" gnus-uu-digest-post-forward t]
2536 ["Resend message" gnus-summary-resend-message t]
2537 ["Resend message edit" gnus-summary-resend-message-edit t]
2538 ["Send bounced mail" gnus-summary-resend-bounced-mail t]
2539 ["Send a mail" gnus-summary-mail-other-window t]
2540 ["Create a local message" gnus-summary-news-other-window t]
2541 ["Uuencode and post" gnus-uu-post-news
2542 ,@(if (featurep 'xemacs) '(t)
2543 '(:help "Post a uuencoded article"))]
2544 ["Followup via news" gnus-summary-followup-to-mail t]
2545 ["Followup via news and yank"
2546 gnus-summary-followup-to-mail-with-original t]
2547 ;;("Draft"
2548 ;;["Send" gnus-summary-send-draft t]
2549 ;;["Send bounced" gnus-resend-bounced-mail t])
2550 ))
2551
2552 (cond
2553 ((not (keymapp gnus-summary-post-menu))
2554 (setq gnus-article-post-menu gnus-summary-post-menu))
2555 ((not gnus-article-post-menu)
2556 ;; Don't share post menu.
2557 (setq gnus-article-post-menu
2558 (copy-keymap gnus-summary-post-menu))))
2559 (define-key gnus-article-mode-map [menu-bar post]
2560 (cons "Post" gnus-article-post-menu))
eec82323
LMI
2561
2562 (easy-menu-define
23f87bed
MB
2563 gnus-summary-misc-menu gnus-summary-mode-map ""
2564 `("Gnus"
2565 ("Mark Read"
2566 ["Mark as read" gnus-summary-mark-as-read-forward t]
2567 ["Mark same subject and select"
2568 gnus-summary-kill-same-subject-and-select t]
2569 ["Mark same subject" gnus-summary-kill-same-subject t]
2570 ["Catchup" gnus-summary-catchup
2571 ,@(if (featurep 'xemacs) '(t)
2572 '(:help "Mark unread articles in this group as read"))]
2573 ["Catchup all" gnus-summary-catchup-all t]
2574 ["Catchup to here" gnus-summary-catchup-to-here t]
2575 ["Catchup from here" gnus-summary-catchup-from-here t]
2576 ["Catchup region" gnus-summary-mark-region-as-read
2577 (gnus-mark-active-p)]
2578 ["Mark excluded" gnus-summary-limit-mark-excluded-as-read t])
2579 ("Mark Various"
2580 ["Tick" gnus-summary-tick-article-forward t]
2581 ["Mark as dormant" gnus-summary-mark-as-dormant t]
2582 ["Remove marks" gnus-summary-clear-mark-forward t]
2583 ["Set expirable mark" gnus-summary-mark-as-expirable t]
2584 ["Set bookmark" gnus-summary-set-bookmark t]
2585 ["Remove bookmark" gnus-summary-remove-bookmark t])
2586 ("Limit to"
2587 ["Marks..." gnus-summary-limit-to-marks t]
2588 ["Subject..." gnus-summary-limit-to-subject t]
2589 ["Author..." gnus-summary-limit-to-author t]
01c52d31
MB
2590 ["Recipient..." gnus-summary-limit-to-recipient t]
2591 ["Address..." gnus-summary-limit-to-address t]
23f87bed
MB
2592 ["Age..." gnus-summary-limit-to-age t]
2593 ["Extra..." gnus-summary-limit-to-extra t]
2594 ["Score..." gnus-summary-limit-to-score t]
2595 ["Display Predicate" gnus-summary-limit-to-display-predicate t]
2596 ["Unread" gnus-summary-limit-to-unread t]
2597 ["Unseen" gnus-summary-limit-to-unseen t]
01c52d31
MB
2598 ["Singletons" gnus-summary-limit-to-singletons t]
2599 ["Replied" gnus-summary-limit-to-replied t]
23f87bed 2600 ["Non-dormant" gnus-summary-limit-exclude-dormant t]
996aa8c1 2601 ["Next or process marked articles" gnus-summary-limit-to-articles t]
23f87bed
MB
2602 ["Pop limit" gnus-summary-pop-limit t]
2603 ["Show dormant" gnus-summary-limit-include-dormant t]
2604 ["Hide childless dormant"
2605 gnus-summary-limit-exclude-childless-dormant t]
2606 ;;["Hide thread" gnus-summary-limit-exclude-thread t]
2607 ["Hide marked" gnus-summary-limit-exclude-marks t]
2608 ["Show expunged" gnus-summary-limit-include-expunged t])
2609 ("Process Mark"
2610 ["Set mark" gnus-summary-mark-as-processable t]
2611 ["Remove mark" gnus-summary-unmark-as-processable t]
2612 ["Remove all marks" gnus-summary-unmark-all-processable t]
01c52d31 2613 ["Invert marks" gnus-uu-invert-processable t]
23f87bed
MB
2614 ["Mark above" gnus-uu-mark-over t]
2615 ["Mark series" gnus-uu-mark-series t]
2616 ["Mark region" gnus-uu-mark-region (gnus-mark-active-p)]
2617 ["Unmark region" gnus-uu-unmark-region (gnus-mark-active-p)]
2618 ["Mark by regexp..." gnus-uu-mark-by-regexp t]
2619 ["Unmark by regexp..." gnus-uu-unmark-by-regexp t]
2620 ["Mark all" gnus-uu-mark-all t]
2621 ["Mark buffer" gnus-uu-mark-buffer t]
2622 ["Mark sparse" gnus-uu-mark-sparse t]
2623 ["Mark thread" gnus-uu-mark-thread t]
2624 ["Unmark thread" gnus-uu-unmark-thread t]
2625 ("Process Mark Sets"
2626 ["Kill" gnus-summary-kill-process-mark t]
2627 ["Yank" gnus-summary-yank-process-mark
2628 gnus-newsgroup-process-stack]
2629 ["Save" gnus-summary-save-process-mark t]
2630 ["Run command on marked..." gnus-summary-universal-argument t]))
2631 ("Scroll article"
2632 ["Page forward" gnus-summary-next-page
2633 ,@(if (featurep 'xemacs) '(t)
2634 '(:help "Show next page of article"))]
2635 ["Page backward" gnus-summary-prev-page
2636 ,@(if (featurep 'xemacs) '(t)
2637 '(:help "Show previous page of article"))]
2638 ["Line forward" gnus-summary-scroll-up t])
2639 ("Move"
2640 ["Next unread article" gnus-summary-next-unread-article t]
2641 ["Previous unread article" gnus-summary-prev-unread-article t]
2642 ["Next article" gnus-summary-next-article t]
2643 ["Previous article" gnus-summary-prev-article t]
2644 ["Next unread subject" gnus-summary-next-unread-subject t]
2645 ["Previous unread subject" gnus-summary-prev-unread-subject t]
2646 ["Next article same subject" gnus-summary-next-same-subject t]
2647 ["Previous article same subject" gnus-summary-prev-same-subject t]
2648 ["First unread article" gnus-summary-first-unread-article t]
2649 ["Best unread article" gnus-summary-best-unread-article t]
2650 ["Go to subject number..." gnus-summary-goto-subject t]
2651 ["Go to article number..." gnus-summary-goto-article t]
2652 ["Go to the last article" gnus-summary-goto-last-article t]
2653 ["Pop article off history" gnus-summary-pop-article t])
2654 ("Sort"
2655 ["Sort by number" gnus-summary-sort-by-number t]
2656 ["Sort by author" gnus-summary-sort-by-author t]
01c52d31 2657 ["Sort by recipient" gnus-summary-sort-by-recipient t]
23f87bed
MB
2658 ["Sort by subject" gnus-summary-sort-by-subject t]
2659 ["Sort by date" gnus-summary-sort-by-date t]
2660 ["Sort by score" gnus-summary-sort-by-score t]
2661 ["Sort by lines" gnus-summary-sort-by-lines t]
2662 ["Sort by characters" gnus-summary-sort-by-chars t]
2663 ["Randomize" gnus-summary-sort-by-random t]
2664 ["Original sort" gnus-summary-sort-by-original t])
2665 ("Help"
2666 ["Fetch group FAQ" gnus-summary-fetch-faq t]
2667 ["Describe group" gnus-summary-describe-group t]
2668 ["Fetch charter" gnus-group-fetch-charter
2669 ,@(if (featurep 'xemacs) nil
2670 '(:help "Display the charter of the current group"))]
2671 ["Fetch control message" gnus-group-fetch-control
2672 ,@(if (featurep 'xemacs) nil
2673 '(:help "Display the archived control message for the current group"))]
2674 ["Read manual" gnus-info-find-node t])
2675 ("Modes"
2676 ["Pick and read" gnus-pick-mode t]
2677 ["Binary" gnus-binary-mode t])
2678 ("Regeneration"
2679 ["Regenerate" gnus-summary-prepare t]
2680 ["Insert cached articles" gnus-summary-insert-cached-articles t]
2681 ["Insert dormant articles" gnus-summary-insert-dormant-articles t]
01c52d31 2682 ["Insert ticked articles" gnus-summary-insert-ticked-articles t]
23f87bed
MB
2683 ["Toggle threading" gnus-summary-toggle-threads t])
2684 ["See old articles" gnus-summary-insert-old-articles t]
2685 ["See new articles" gnus-summary-insert-new-articles t]
2686 ["Filter articles..." gnus-summary-execute-command t]
2687 ["Run command on articles..." gnus-summary-universal-argument t]
2688 ["Search articles forward..." gnus-summary-search-article-forward t]
2689 ["Search articles backward..." gnus-summary-search-article-backward t]
2690 ["Toggle line truncation" gnus-summary-toggle-truncation t]
2691 ["Expand window" gnus-summary-expand-window t]
2692 ["Expire expirable articles" gnus-summary-expire-articles
2693 (gnus-check-backend-function
2694 'request-expire-articles gnus-newsgroup-name)]
2695 ["Edit local kill file" gnus-summary-edit-local-kill t]
2696 ["Edit main kill file" gnus-summary-edit-global-kill t]
2697 ["Edit group parameters" gnus-summary-edit-parameters t]
2698 ["Customize group parameters" gnus-summary-customize-parameters t]
2699 ["Send a bug report" gnus-bug t]
2700 ("Exit"
2701 ["Catchup and exit" gnus-summary-catchup-and-exit
2702 ,@(if (featurep 'xemacs) '(t)
2703 '(:help "Mark unread articles in this group as read, then exit"))]
2704 ["Catchup all and exit" gnus-summary-catchup-all-and-exit t]
2705 ["Catchup and goto next" gnus-summary-catchup-and-goto-next-group t]
01c52d31 2706 ["Catchup and goto prev" gnus-summary-catchup-and-goto-prev-group t]
23f87bed
MB
2707 ["Exit group" gnus-summary-exit
2708 ,@(if (featurep 'xemacs) '(t)
2709 '(:help "Exit current group, return to group selection mode"))]
2710 ["Exit group without updating" gnus-summary-exit-no-update t]
2711 ["Exit and goto next group" gnus-summary-next-group t]
2712 ["Exit and goto prev group" gnus-summary-prev-group t]
2713 ["Reselect group" gnus-summary-reselect-current-group t]
2714 ["Rescan group" gnus-summary-rescan-group t]
2715 ["Update dribble" gnus-summary-save-newsrc t])))
eec82323 2716
6748645f 2717 (gnus-run-hooks 'gnus-summary-menu-hook)))
eec82323 2718
60bd5589
DL
2719(defvar gnus-summary-tool-bar-map nil)
2720
18c06a99
RS
2721;; Note: The :set function in the `gnus-summary-tool-bar*' variables will only
2722;; affect _new_ message buffers. We might add a function that walks thru all
2723;; summary-mode buffers and force the update.
2724(defun gnus-summary-tool-bar-update (&optional symbol value)
2725 "Update summary mode toolbar.
2726Setter function for custom variables."
2727 (setq-default gnus-summary-tool-bar-map nil)
2728 (when symbol
2729 ;; When used as ":set" function:
2730 (set-default symbol value))
2731 (when (gnus-buffer-live-p gnus-summary-buffer)
2732 (with-current-buffer gnus-summary-buffer
2733 (gnus-summary-make-tool-bar))))
2734
2735(defcustom gnus-summary-tool-bar (if (eq gmm-tool-bar-style 'gnome)
2736 'gnus-summary-tool-bar-gnome
2737 'gnus-summary-tool-bar-retro)
2738 "Specifies the Gnus summary tool bar.
2739
2740It can be either a list or a symbol refering to a list. See
2741`gmm-tool-bar-from-list' for the format of the list. The
2742default key map is `gnus-summary-mode-map'.
2743
2744Pre-defined symbols include `gnus-summary-tool-bar-gnome' and
2745`gnus-summary-tool-bar-retro'."
2746 :type '(choice (const :tag "GNOME style" gnus-summary-tool-bar-gnome)
2747 (const :tag "Retro look" gnus-summary-tool-bar-retro)
2748 (repeat :tag "User defined list" gmm-tool-bar-item)
2749 (symbol))
01c52d31 2750 :version "23.0" ;; No Gnus
18c06a99
RS
2751 :initialize 'custom-initialize-default
2752 :set 'gnus-summary-tool-bar-update
2753 :group 'gnus-summary)
2754
2755(defcustom gnus-summary-tool-bar-gnome
2756 '((gnus-summary-post-news "mail/compose" nil)
2757 (gnus-summary-insert-new-articles "mail/inbox" nil
2758 :visible (or (not gnus-agent)
2759 gnus-plugged))
2760 (gnus-summary-reply-with-original "mail/reply")
2761 (gnus-summary-reply "mail/reply" nil :visible nil)
2762 (gnus-summary-followup-with-original "mail/reply-all")
2763 (gnus-summary-followup "mail/reply-all" nil :visible nil)
2764 (gnus-summary-mail-forward "mail/forward")
2765 (gnus-summary-save-article "mail/save")
2766 (gnus-summary-search-article-forward "search" nil :visible nil)
2767 (gnus-summary-print-article "print")
2768 (gnus-summary-tick-article-forward "flag-followup" nil :visible nil)
2769 ;; Some new commands that may need more suitable icons:
2770 (gnus-summary-save-newsrc "save" nil :visible nil)
2771 ;; (gnus-summary-show-article "stock_message-display" nil :visible nil)
2772 (gnus-summary-prev-article "left-arrow")
2773 (gnus-summary-next-article "right-arrow")
2774 (gnus-summary-next-page "next-page")
2775 ;; (gnus-summary-enter-digest-group "right_arrow" nil :visible nil)
2776 ;;
2777 ;; Maybe some sort-by-... could be added:
2778 ;; (gnus-summary-sort-by-author "sort-a-z" nil :visible nil)
2779 ;; (gnus-summary-sort-by-date "sort-1-9" nil :visible nil)
2780 (gnus-summary-mark-as-expirable
2781 "delete" nil
2782 :visible (gnus-check-backend-function 'request-expire-articles
2783 gnus-newsgroup-name))
2784 (gnus-summary-mark-as-spam
2785 "mail/spam" t
2786 :visible (and (fboundp 'spam-group-ham-contents-p)
2787 (spam-group-ham-contents-p gnus-newsgroup-name))
2788 :help "Mark as spam")
2789 (gnus-summary-mark-as-read-forward
2790 "mail/not-spam" nil
2791 :visible (and (fboundp 'spam-group-spam-contents-p)
2792 (spam-group-spam-contents-p gnus-newsgroup-name)))
2793 ;;
2794 (gnus-summary-exit "exit")
2795 (gmm-customize-mode "preferences" t :help "Edit mode preferences")
2796 (gnus-info-find-node "help"))
2797 "List of functions for the summary tool bar (GNOME style).
2798
2799See `gmm-tool-bar-from-list' for the format of the list."
2800 :type '(repeat gmm-tool-bar-item)
01c52d31 2801 :version "23.0" ;; No Gnus
18c06a99
RS
2802 :initialize 'custom-initialize-default
2803 :set 'gnus-summary-tool-bar-update
2804 :group 'gnus-summary)
2805
2806(defcustom gnus-summary-tool-bar-retro
2807 '((gnus-summary-prev-unread-article "gnus/prev-ur")
2808 (gnus-summary-next-unread-article "gnus/next-ur")
2809 (gnus-summary-post-news "gnus/post")
2810 (gnus-summary-followup-with-original "gnus/fuwo")
2811 (gnus-summary-followup "gnus/followup")
2812 (gnus-summary-reply-with-original "gnus/reply-wo")
2813 (gnus-summary-reply "gnus/reply")
2814 (gnus-summary-caesar-message "gnus/rot13")
2815 (gnus-uu-decode-uu "gnus/uu-decode")
2816 (gnus-summary-save-article-file "gnus/save-aif")
2817 (gnus-summary-save-article "gnus/save-art")
2818 (gnus-uu-post-news "gnus/uu-post")
2819 (gnus-summary-catchup "gnus/catchup")
2820 (gnus-summary-catchup-and-exit "gnus/cu-exit")
2821 (gnus-summary-exit "gnus/exit-summ")
2822 ;; Some new command that may need more suitable icons:
2823 (gnus-summary-print-article "gnus/print" nil :visible nil)
2824 (gnus-summary-mark-as-expirable "gnus/close" nil :visible nil)
2825 (gnus-summary-save-newsrc "gnus/save" nil :visible nil)
2826 ;; (gnus-summary-enter-digest-group "gnus/right_arrow" nil :visible nil)
2827 (gnus-summary-search-article-forward "gnus/search" nil :visible nil)
2828 ;; (gnus-summary-insert-new-articles "gnus/paste" nil :visible nil)
2829 ;; (gnus-summary-toggle-threads "gnus/open" nil :visible nil)
2830 ;;
2831 (gnus-info-find-node "gnus/help" nil :visible nil))
2832 "List of functions for the summary tool bar (retro look).
2833
2834See `gmm-tool-bar-from-list' for the format of the list."
2835 :type '(repeat gmm-tool-bar-item)
01c52d31 2836 :version "23.0" ;; No Gnus
18c06a99
RS
2837 :initialize 'custom-initialize-default
2838 :set 'gnus-summary-tool-bar-update
2839 :group 'gnus-summary)
2840
2841(defcustom gnus-summary-tool-bar-zap-list t
2842 "List of icon items from the global tool bar.
2843These items are not displayed in the Gnus summary mode tool bar.
2844
2845See `gmm-tool-bar-from-list' for the format of the list."
2846 :type 'gmm-tool-bar-zap-list
01c52d31 2847 :version "23.0" ;; No Gnus
18c06a99
RS
2848 :initialize 'custom-initialize-default
2849 :set 'gnus-summary-tool-bar-update
2850 :group 'gnus-summary)
2851
2852(defvar image-load-path)
2853
2854(defun gnus-summary-make-tool-bar (&optional force)
2855 "Make a summary mode tool bar from `gnus-summary-tool-bar'.
2856When FORCE, rebuild the tool bar."
2857 (when (and (not (featurep 'xemacs))
2858 (boundp 'tool-bar-mode)
2859 tool-bar-mode
2860 (or (not gnus-summary-tool-bar-map) force))
2861 (let* ((load-path
2862 (gmm-image-load-path-for-library "gnus"
2863 "mail/save.xpm"
2864 nil t))
2865 (image-load-path (cons (car load-path)
2866 (when (boundp 'image-load-path)
2867 image-load-path)))
2868 (map (gmm-tool-bar-from-list gnus-summary-tool-bar
2869 gnus-summary-tool-bar-zap-list
2870 'gnus-summary-mode-map)))
2871 (when map
2872 ;; Need to set `gnus-summary-tool-bar-map' because `gnus-article-mode'
2873 ;; uses it's value.
2874 (setq gnus-summary-tool-bar-map map))))
2875 (set (make-local-variable 'tool-bar-map) gnus-summary-tool-bar-map))
60bd5589 2876
eec82323
LMI
2877(defun gnus-score-set-default (var value)
2878 "A version of set that updates the GNU Emacs menu-bar."
2879 (set var value)
2880 ;; It is the message that forces the active status to be updated.
2881 (message ""))
2882
2883(defun gnus-make-score-map (type)
2884 "Make a summary score map of type TYPE."
2885 (if t
2886 nil
2887 (let ((headers '(("author" "from" string)
2888 ("subject" "subject" string)
2889 ("article body" "body" string)
2890 ("article head" "head" string)
2891 ("xref" "xref" string)
16409b0b 2892 ("extra header" "extra" string)
eec82323
LMI
2893 ("lines" "lines" number)
2894 ("followups to author" "followup" string)))
2895 (types '((number ("less than" <)
2896 ("greater than" >)
2897 ("equal" =))
2898 (string ("substring" s)
2899 ("exact string" e)
2900 ("fuzzy string" f)
2901 ("regexp" r))))
2902 (perms '(("temporary" (current-time-string))
2903 ("permanent" nil)
2904 ("immediate" now)))
2905 header)
2906 (list
2907 (apply
2908 'nconc
2909 (list
2910 (if (eq type 'lower)
2911 "Lower score"
2912 "Increase score"))
2913 (let (outh)
2914 (while headers
2915 (setq header (car headers))
2916 (setq outh
2917 (cons
2918 (apply
2919 'nconc
2920 (list (car header))
2921 (let ((ts (cdr (assoc (nth 2 header) types)))
2922 outt)
2923 (while ts
2924 (setq outt
2925 (cons
2926 (apply
2927 'nconc
2928 (list (caar ts))
2929 (let ((ps perms)
2930 outp)
2931 (while ps
2932 (setq outp
2933 (cons
2934 (vector
2935 (caar ps)
2936 (list
2937 'gnus-summary-score-entry
2938 (nth 1 header)
2939 (if (or (string= (nth 1 header)
2940 "head")
2941 (string= (nth 1 header)
2942 "body"))
2943 ""
2944 (list 'gnus-summary-header
2945 (nth 1 header)))
2946 (list 'quote (nth 1 (car ts)))
16409b0b
GM
2947 (list 'gnus-score-delta-default
2948 nil)
eec82323
LMI
2949 (nth 1 (car ps))
2950 t)
2951 t)
2952 outp))
2953 (setq ps (cdr ps)))
2954 (list (nreverse outp))))
2955 outt))
2956 (setq ts (cdr ts)))
2957 (list (nreverse outt))))
2958 outh))
2959 (setq headers (cdr headers)))
2960 (list (nreverse outh))))))))
2961
2962\f
2963
2964(defun gnus-summary-mode (&optional group)
2965 "Major mode for reading articles.
2966
2967All normal editing commands are switched off.
2968\\<gnus-summary-mode-map>
2969Each line in this buffer represents one article. To read an
2970article, you can, for instance, type `\\[gnus-summary-next-page]'. To move forwards
2971and backwards while displaying articles, type `\\[gnus-summary-next-unread-article]' and `\\[gnus-summary-prev-unread-article]',
2972respectively.
2973
2974You can also post articles and send mail from this buffer. To
23f87bed 2975follow up an article, type `\\[gnus-summary-followup]'. To mail a reply to the author
eec82323
LMI
2976of an article, type `\\[gnus-summary-reply]'.
2977
2978There are approx. one gazillion commands you can execute in this
2979buffer; read the info pages for more information (`\\[gnus-info-find-node]').
2980
2981The following commands are available:
2982
2983\\{gnus-summary-mode-map}"
2984 (interactive)
eec82323 2985 (kill-all-local-variables)
01c52d31
MB
2986 (let ((gnus-summary-local-variables gnus-newsgroup-variables))
2987 (gnus-summary-make-local-variables))
2988 (gnus-summary-make-local-variables)
2989 (setq gnus-newsgroup-name group)
60bd5589
DL
2990 (when (gnus-visual-p 'summary-menu 'menu)
2991 (gnus-summary-make-menu-bar)
2992 (gnus-summary-make-tool-bar))
eec82323
LMI
2993 (gnus-make-thread-indent-array)
2994 (gnus-simplify-mode-line)
2995 (setq major-mode 'gnus-summary-mode)
2996 (setq mode-name "Summary")
2997 (make-local-variable 'minor-mode-alist)
2998 (use-local-map gnus-summary-mode-map)
16409b0b 2999 (buffer-disable-undo)
01c52d31
MB
3000 (setq buffer-read-only t ;Disable modification
3001 show-trailing-whitespace nil)
eec82323
LMI
3002 (setq truncate-lines t)
3003 (setq selective-display t)
3004 (setq selective-display-ellipses t) ;Display `...'
3005 (gnus-summary-set-display-table)
3006 (gnus-set-default-directory)
eec82323
LMI
3007 (make-local-variable 'gnus-summary-line-format)
3008 (make-local-variable 'gnus-summary-line-format-spec)
6748645f
LMI
3009 (make-local-variable 'gnus-summary-dummy-line-format)
3010 (make-local-variable 'gnus-summary-dummy-line-format-spec)
eec82323 3011 (make-local-variable 'gnus-summary-mark-positions)
23f87bed 3012 (gnus-make-local-hook 'pre-command-hook)
6748645f 3013 (add-hook 'pre-command-hook 'gnus-set-global-variables nil t)
cfcd5c91 3014 (gnus-run-mode-hooks 'gnus-summary-mode-hook)
23f87bed 3015 (turn-on-gnus-mailing-list-mode)
87545352 3016 (mm-enable-multibyte)
eec82323
LMI
3017 (gnus-update-format-specifications nil 'summary 'summary-mode 'summary-dummy)
3018 (gnus-update-summary-mark-positions))
3019
3020(defun gnus-summary-make-local-variables ()
3021 "Make all the local summary buffer variables."
16409b0b
GM
3022 (let (global)
3023 (dolist (local gnus-summary-local-variables)
eec82323
LMI
3024 (if (consp local)
3025 (progn
3026 (if (eq (cdr local) 'global)
3027 ;; Copy the global value of the variable.
3028 (setq global (symbol-value (car local)))
3029 ;; Use the value from the list.
3030 (setq global (eval (cdr local))))
16409b0b 3031 (set (make-local-variable (car local)) global))
eec82323 3032 ;; Simple nil-valued local variable.
16409b0b 3033 (set (make-local-variable local) nil)))))
eec82323
LMI
3034
3035(defun gnus-summary-clear-local-variables ()
3036 (let ((locals gnus-summary-local-variables))
3037 (while locals
3038 (if (consp (car locals))
01c52d31 3039 (and (symbolp (caar locals))
eec82323 3040 (set (caar locals) nil))
01c52d31 3041 (and (symbolp (car locals))
eec82323
LMI
3042 (set (car locals) nil)))
3043 (setq locals (cdr locals)))))
3044
3045;; Summary data functions.
3046
3047(defmacro gnus-data-number (data)
3048 `(car ,data))
3049
3050(defmacro gnus-data-set-number (data number)
3051 `(setcar ,data ,number))
3052
3053(defmacro gnus-data-mark (data)
3054 `(nth 1 ,data))
3055
3056(defmacro gnus-data-set-mark (data mark)
3057 `(setcar (nthcdr 1 ,data) ,mark))
3058
3059(defmacro gnus-data-pos (data)
3060 `(nth 2 ,data))
3061
3062(defmacro gnus-data-set-pos (data pos)
3063 `(setcar (nthcdr 2 ,data) ,pos))
3064
3065(defmacro gnus-data-header (data)
3066 `(nth 3 ,data))
3067
3068(defmacro gnus-data-set-header (data header)
3069 `(setf (nth 3 ,data) ,header))
3070
3071(defmacro gnus-data-level (data)
3072 `(nth 4 ,data))
3073
3074(defmacro gnus-data-unread-p (data)
3075 `(= (nth 1 ,data) gnus-unread-mark))
3076
3077(defmacro gnus-data-read-p (data)
3078 `(/= (nth 1 ,data) gnus-unread-mark))
3079
3080(defmacro gnus-data-pseudo-p (data)
3081 `(consp (nth 3 ,data)))
3082
3083(defmacro gnus-data-find (number)
3084 `(assq ,number gnus-newsgroup-data))
3085
3086(defmacro gnus-data-find-list (number &optional data)
3087 `(let ((bdata ,(or data 'gnus-newsgroup-data)))
3088 (memq (assq ,number bdata)
3089 bdata)))
3090
3091(defmacro gnus-data-make (number mark pos header level)
3092 `(list ,number ,mark ,pos ,header ,level))
3093
3094(defun gnus-data-enter (after-article number mark pos header level offset)
3095 (let ((data (gnus-data-find-list after-article)))
3096 (unless data
3097 (error "No such article: %d" after-article))
3098 (setcdr data (cons (gnus-data-make number mark pos header level)
3099 (cdr data)))
3100 (setq gnus-newsgroup-data-reverse nil)
3101 (gnus-data-update-list (cddr data) offset)))
3102
3103(defun gnus-data-enter-list (after-article list &optional offset)
3104 (when list
3105 (let ((data (and after-article (gnus-data-find-list after-article)))
3106 (ilist list))
6748645f
LMI
3107 (if (not (or data
3108 after-article))
3109 (let ((odata gnus-newsgroup-data))
3110 (setq gnus-newsgroup-data (nconc list gnus-newsgroup-data))
eec82323 3111 (when offset
6748645f 3112 (gnus-data-update-list odata offset)))
01c52d31 3113 ;; Find the last element in the list to be spliced into the main
6748645f 3114 ;; list.
01c52d31 3115 (setq list (last list))
6748645f
LMI
3116 (if (not data)
3117 (progn
3118 (setcdr list gnus-newsgroup-data)
3119 (setq gnus-newsgroup-data ilist)
3120 (when offset
3121 (gnus-data-update-list (cdr list) offset)))
3122 (setcdr list (cdr data))
3123 (setcdr data ilist)
3124 (when offset
3125 (gnus-data-update-list (cdr list) offset))))
eec82323
LMI
3126 (setq gnus-newsgroup-data-reverse nil))))
3127
3128(defun gnus-data-remove (article &optional offset)
3129 (let ((data gnus-newsgroup-data))
3130 (if (= (gnus-data-number (car data)) article)
3131 (progn
3132 (setq gnus-newsgroup-data (cdr gnus-newsgroup-data)
3133 gnus-newsgroup-data-reverse nil)
3134 (when offset
3135 (gnus-data-update-list gnus-newsgroup-data offset)))
3136 (while (cdr data)
3137 (when (= (gnus-data-number (cadr data)) article)
3138 (setcdr data (cddr data))
3139 (when offset
3140 (gnus-data-update-list (cdr data) offset))
3141 (setq data nil
3142 gnus-newsgroup-data-reverse nil))
3143 (setq data (cdr data))))))
3144
3145(defmacro gnus-data-list (backward)
3146 `(if ,backward
3147 (or gnus-newsgroup-data-reverse
3148 (setq gnus-newsgroup-data-reverse
3149 (reverse gnus-newsgroup-data)))
3150 gnus-newsgroup-data))
3151
3152(defun gnus-data-update-list (data offset)
3153 "Add OFFSET to the POS of all data entries in DATA."
6748645f 3154 (setq gnus-newsgroup-data-reverse nil)
eec82323
LMI
3155 (while data
3156 (setcar (nthcdr 2 (car data)) (+ offset (nth 2 (car data))))
3157 (setq data (cdr data))))
3158
eec82323
LMI
3159(defun gnus-summary-article-pseudo-p (article)
3160 "Say whether this article is a pseudo article or not."
3161 (not (vectorp (gnus-data-header (gnus-data-find article)))))
3162
3163(defmacro gnus-summary-article-sparse-p (article)
3164 "Say whether this article is a sparse article or not."
a8151ef7 3165 `(memq ,article gnus-newsgroup-sparse))
eec82323
LMI
3166
3167(defmacro gnus-summary-article-ancient-p (article)
3168 "Say whether this article is a sparse article or not."
3169 `(memq ,article gnus-newsgroup-ancient))
3170
3171(defun gnus-article-parent-p (number)
3172 "Say whether this article is a parent or not."
3173 (let ((data (gnus-data-find-list number)))
23f87bed 3174 (and (cdr data) ; There has to be an article after...
eec82323
LMI
3175 (< (gnus-data-level (car data)) ; And it has to have a higher level.
3176 (gnus-data-level (nth 1 data))))))
3177
3178(defun gnus-article-children (number)
3179 "Return a list of all children to NUMBER."
3180 (let* ((data (gnus-data-find-list number))
3181 (level (gnus-data-level (car data)))
3182 children)
3183 (setq data (cdr data))
3184 (while (and data
3185 (= (gnus-data-level (car data)) (1+ level)))
3186 (push (gnus-data-number (car data)) children)
3187 (setq data (cdr data)))
3188 children))
3189
3190(defmacro gnus-summary-skip-intangible ()
3191 "If the current article is intangible, then jump to a different article."
3192 '(let ((to (get-text-property (point) 'gnus-intangible)))
3193 (and to (gnus-summary-goto-subject to))))
3194
3195(defmacro gnus-summary-article-intangible-p ()
3196 "Say whether this article is intangible or not."
3197 '(get-text-property (point) 'gnus-intangible))
3198
3199(defun gnus-article-read-p (article)
3200 "Say whether ARTICLE is read or not."
3201 (not (or (memq article gnus-newsgroup-marked)
23f87bed 3202 (memq article gnus-newsgroup-spam-marked)
eec82323
LMI
3203 (memq article gnus-newsgroup-unreads)
3204 (memq article gnus-newsgroup-unselected)
3205 (memq article gnus-newsgroup-dormant))))
3206
3207;; Some summary mode macros.
3208
3209(defmacro gnus-summary-article-number ()
3210 "The article number of the article on the current line.
8f688cb0 3211If there isn't an article number here, then we return the current
eec82323
LMI
3212article number."
3213 '(progn
3214 (gnus-summary-skip-intangible)
3215 (or (get-text-property (point) 'gnus-number)
3216 (gnus-summary-last-subject))))
3217
3218(defmacro gnus-summary-article-header (&optional number)
6748645f 3219 "Return the header of article NUMBER."
eec82323
LMI
3220 `(gnus-data-header (gnus-data-find
3221 ,(or number '(gnus-summary-article-number)))))
3222
3223(defmacro gnus-summary-thread-level (&optional number)
6748645f 3224 "Return the level of thread that starts with article NUMBER."
eec82323
LMI
3225 `(if (and (eq gnus-summary-make-false-root 'dummy)
3226 (get-text-property (point) 'gnus-intangible))
3227 0
3228 (gnus-data-level (gnus-data-find
3229 ,(or number '(gnus-summary-article-number))))))
3230
3231(defmacro gnus-summary-article-mark (&optional number)
6748645f 3232 "Return the mark of article NUMBER."
eec82323
LMI
3233 `(gnus-data-mark (gnus-data-find
3234 ,(or number '(gnus-summary-article-number)))))
3235
3236(defmacro gnus-summary-article-pos (&optional number)
6748645f 3237 "Return the position of the line of article NUMBER."
eec82323
LMI
3238 `(gnus-data-pos (gnus-data-find
3239 ,(or number '(gnus-summary-article-number)))))
3240
3241(defalias 'gnus-summary-subject-string 'gnus-summary-article-subject)
3242(defmacro gnus-summary-article-subject (&optional number)
3243 "Return current subject string or nil if nothing."
3244 `(let ((headers
3245 ,(if number
3246 `(gnus-data-header (assq ,number gnus-newsgroup-data))
3247 '(gnus-data-header (assq (gnus-summary-article-number)
3248 gnus-newsgroup-data)))))
3249 (and headers
3250 (vectorp headers)
3251 (mail-header-subject headers))))
3252
3253(defmacro gnus-summary-article-score (&optional number)
3254 "Return current article score."
3255 `(or (cdr (assq ,(or number '(gnus-summary-article-number))
3256 gnus-newsgroup-scored))
3257 gnus-summary-default-score 0))
3258
3259(defun gnus-summary-article-children (&optional number)
6748645f 3260 "Return a list of article numbers that are children of article NUMBER."
eec82323
LMI
3261 (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))))
3262 (level (gnus-data-level (car data)))
3263 l children)
3264 (while (and (setq data (cdr data))
3265 (> (setq l (gnus-data-level (car data))) level))
3266 (and (= (1+ level) l)
3267 (push (gnus-data-number (car data))
3268 children)))
3269 (nreverse children)))
3270
3271(defun gnus-summary-article-parent (&optional number)
6748645f 3272 "Return the article number of the parent of article NUMBER."
eec82323
LMI
3273 (let* ((data (gnus-data-find-list (or number (gnus-summary-article-number))
3274 (gnus-data-list t)))
3275 (level (gnus-data-level (car data))))
3276 (if (zerop level)
3277 () ; This is a root.
3278 ;; We search until we find an article with a level less than
3279 ;; this one. That function has to be the parent.
3280 (while (and (setq data (cdr data))
3281 (not (< (gnus-data-level (car data)) level))))
3282 (and data (gnus-data-number (car data))))))
3283
3284(defun gnus-unread-mark-p (mark)
3285 "Say whether MARK is the unread mark."
3286 (= mark gnus-unread-mark))
3287
3288(defun gnus-read-mark-p (mark)
3289 "Say whether MARK is one of the marks that mark as read.
3290This is all marks except unread, ticked, dormant, and expirable."
3291 (not (or (= mark gnus-unread-mark)
3292 (= mark gnus-ticked-mark)
23f87bed 3293 (= mark gnus-spam-mark)
eec82323
LMI
3294 (= mark gnus-dormant-mark)
3295 (= mark gnus-expirable-mark))))
3296
3297(defmacro gnus-article-mark (number)
6748645f
LMI
3298 "Return the MARK of article NUMBER.
3299This macro should only be used when computing the mark the \"first\"
3300time; i.e., when generating the summary lines. After that,
3301`gnus-summary-article-mark' should be used to examine the
3302marks of articles."
eec82323 3303 `(cond
6748645f 3304 ((memq ,number gnus-newsgroup-unsendable) gnus-unsendable-mark)
6748645f 3305 ((memq ,number gnus-newsgroup-downloadable) gnus-downloadable-mark)
eec82323
LMI
3306 ((memq ,number gnus-newsgroup-unreads) gnus-unread-mark)
3307 ((memq ,number gnus-newsgroup-marked) gnus-ticked-mark)
23f87bed 3308 ((memq ,number gnus-newsgroup-spam-marked) gnus-spam-mark)
eec82323
LMI
3309 ((memq ,number gnus-newsgroup-dormant) gnus-dormant-mark)
3310 ((memq ,number gnus-newsgroup-expirable) gnus-expirable-mark)
3311 (t (or (cdr (assq ,number gnus-newsgroup-reads))
3312 gnus-ancient-mark))))
3313
3314;; Saving hidden threads.
3315
eec82323
LMI
3316(defmacro gnus-save-hidden-threads (&rest forms)
3317 "Save hidden threads, eval FORMS, and restore the hidden threads."
3318 (let ((config (make-symbol "config")))
3319 `(let ((,config (gnus-hidden-threads-configuration)))
3320 (unwind-protect
3321 (save-excursion
3322 ,@forms)
3323 (gnus-restore-hidden-threads-configuration ,config)))))
23f87bed
MB
3324(put 'gnus-save-hidden-threads 'lisp-indent-function 0)
3325(put 'gnus-save-hidden-threads 'edebug-form-spec '(body))
eec82323 3326
107ecebb
AS
3327(defun gnus-data-compute-positions ()
3328 "Compute the positions of all articles."
3329 (setq gnus-newsgroup-data-reverse nil)
3330 (let ((data gnus-newsgroup-data))
3331 (save-excursion
3332 (gnus-save-hidden-threads
3333 (gnus-summary-show-all-threads)
3334 (goto-char (point-min))
3335 (while data
3336 (while (get-text-property (point) 'gnus-intangible)
3337 (forward-line 1))
3338 (gnus-data-set-pos (car data) (+ (point) 3))
3339 (setq data (cdr data))
3340 (forward-line 1))))))
3341
16409b0b
GM
3342(defun gnus-hidden-threads-configuration ()
3343 "Return the current hidden threads configuration."
3344 (save-excursion
3345 (let (config)
3346 (goto-char (point-min))
3347 (while (search-forward "\r" nil t)
3348 (push (1- (point)) config))
3349 config)))
3350
3351(defun gnus-restore-hidden-threads-configuration (config)
3352 "Restore hidden threads configuration from CONFIG."
3353 (save-excursion
3354 (let (point buffer-read-only)
3355 (while (setq point (pop config))
3356 (when (and (< point (point-max))
3357 (goto-char point)
3358 (eq (char-after) ?\n))
3359 (subst-char-in-region point (1+ point) ?\n ?\r))))))
3360
eec82323
LMI
3361;; Various summary mode internalish functions.
3362
3363(defun gnus-mouse-pick-article (e)
3364 (interactive "e")
3365 (mouse-set-point e)
3366 (gnus-summary-next-page nil t))
3367
3368(defun gnus-summary-set-display-table ()
16409b0b
GM
3369 "Change the display table.
3370Odd characters have a tendency to mess
3371up nicely formatted displays - we make all possible glyphs
3372display only a single character."
eec82323
LMI
3373
3374 ;; We start from the standard display table, if any.
3375 (let ((table (or (copy-sequence standard-display-table)
3376 (make-display-table)))
3377 (i 32))
3378 ;; Nix out all the control chars...
3379 (while (>= (setq i (1- i)) 0)
3380 (aset table i [??]))
23f87bed 3381 ;; ... but not newline and cr, of course. (cr is necessary for the
eec82323
LMI
3382 ;; selective display).
3383 (aset table ?\n nil)
3384 (aset table ?\r nil)
6748645f
LMI
3385 ;; We keep TAB as well.
3386 (aset table ?\t nil)
719120ef 3387 ;; We nix out any glyphs 127 through 255, or 127 through 159 in
fe62aacc 3388 ;; Emacs 23 (unicode), that are not set already.
719120ef
MB
3389 (let ((i (if (ignore-errors (= (make-char 'latin-iso8859-1 160) 160))
3390 160
3391 256)))
eec82323
LMI
3392 (while (>= (setq i (1- i)) 127)
3393 ;; Only modify if the entry is nil.
3394 (unless (aref table i)
3395 (aset table i [??]))))
3396 (setq buffer-display-table table)))
3397
23f87bed
MB
3398(defun gnus-summary-set-article-display-arrow (pos)
3399 "Update the overlay arrow to point to line at position POS."
3400 (when (and gnus-summary-display-arrow
3401 (boundp 'overlay-arrow-position)
3402 (boundp 'overlay-arrow-string))
3403 (save-excursion
3404 (goto-char pos)
3405 (beginning-of-line)
3406 (unless overlay-arrow-position
3407 (setq overlay-arrow-position (make-marker)))
3408 (setq overlay-arrow-string "=>"
3409 overlay-arrow-position (set-marker overlay-arrow-position
3410 (point)
3411 (current-buffer))))))
3412
eec82323
LMI
3413(defun gnus-summary-setup-buffer (group)
3414 "Initialize summary buffer."
23f87bed
MB
3415 (let ((buffer (gnus-summary-buffer-name group))
3416 (dead-name (concat "*Dead Summary "
3417 (gnus-group-decoded-name group) "*")))
3418 ;; If a dead summary buffer exists, we kill it.
3419 (when (gnus-buffer-live-p dead-name)
3420 (gnus-kill-buffer dead-name))
eec82323
LMI
3421 (if (get-buffer buffer)
3422 (progn
3423 (set-buffer buffer)
3424 (setq gnus-summary-buffer (current-buffer))
3425 (not gnus-newsgroup-prepared))
3426 ;; Fix by Sudish Joseph <joseph@cis.ohio-state.edu>
6748645f 3427 (setq gnus-summary-buffer (set-buffer (gnus-get-buffer-create buffer)))
eec82323
LMI
3428 (gnus-summary-mode group)
3429 (when gnus-carpal
3430 (gnus-carpal-setup-buffer 'summary))
01c52d31
MB
3431 (when (gnus-group-quit-config group)
3432 (set (make-local-variable 'gnus-single-article-buffer) nil))
3433 (make-local-variable 'gnus-article-buffer)
3434 (make-local-variable 'gnus-article-current)
3435 (make-local-variable 'gnus-original-article-buffer)
eec82323 3436 (setq gnus-newsgroup-name group)
23f87bed
MB
3437 ;; Set any local variables in the group parameters.
3438 (gnus-summary-set-local-parameters gnus-newsgroup-name)
eec82323
LMI
3439 t)))
3440
3441(defun gnus-set-global-variables ()
16409b0b
GM
3442 "Set the global equivalents of the buffer-local variables.
3443They are set to the latest values they had. These reflect the summary
3444buffer that was in action when the last article was fetched."
eec82323
LMI
3445 (when (eq major-mode 'gnus-summary-mode)
3446 (setq gnus-summary-buffer (current-buffer))
3447 (let ((name gnus-newsgroup-name)
3448 (marked gnus-newsgroup-marked)
23f87bed 3449 (spam gnus-newsgroup-spam-marked)
eec82323
LMI
3450 (unread gnus-newsgroup-unreads)
3451 (headers gnus-current-headers)
3452 (data gnus-newsgroup-data)
3453 (summary gnus-summary-buffer)
3454 (article-buffer gnus-article-buffer)
3455 (original gnus-original-article-buffer)
3456 (gac gnus-article-current)
3457 (reffed gnus-reffed-article-number)
16409b0b 3458 (score-file gnus-current-score-file)
23f87bed
MB
3459 (default-charset gnus-newsgroup-charset)
3460 vlist)
3461 (let ((locals gnus-newsgroup-variables))
3462 (while locals
3463 (if (consp (car locals))
3464 (push (eval (caar locals)) vlist)
3465 (push (eval (car locals)) vlist))
3466 (setq locals (cdr locals)))
3467 (setq vlist (nreverse vlist)))
01c52d31 3468 (with-current-buffer gnus-group-buffer
6748645f
LMI
3469 (setq gnus-newsgroup-name name
3470 gnus-newsgroup-marked marked
23f87bed 3471 gnus-newsgroup-spam-marked spam
6748645f
LMI
3472 gnus-newsgroup-unreads unread
3473 gnus-current-headers headers
3474 gnus-newsgroup-data data
3475 gnus-article-current gac
3476 gnus-summary-buffer summary
3477 gnus-article-buffer article-buffer
3478 gnus-original-article-buffer original
3479 gnus-reffed-article-number reffed
16409b0b
GM
3480 gnus-current-score-file score-file
3481 gnus-newsgroup-charset default-charset)
23f87bed
MB
3482 (let ((locals gnus-newsgroup-variables))
3483 (while locals
3484 (if (consp (car locals))
3485 (set (caar locals) (pop vlist))
3486 (set (car locals) (pop vlist)))
3487 (setq locals (cdr locals))))
eec82323
LMI
3488 ;; The article buffer also has local variables.
3489 (when (gnus-buffer-live-p gnus-article-buffer)
3490 (set-buffer gnus-article-buffer)
3491 (setq gnus-summary-buffer summary))))))
3492
3493(defun gnus-summary-article-unread-p (article)
3494 "Say whether ARTICLE is unread or not."
3495 (memq article gnus-newsgroup-unreads))
3496
3497(defun gnus-summary-first-article-p (&optional article)
3498 "Return whether ARTICLE is the first article in the buffer."
3499 (if (not (setq article (or article (gnus-summary-article-number))))
3500 nil
3501 (eq article (caar gnus-newsgroup-data))))
3502
3503(defun gnus-summary-last-article-p (&optional article)
3504 "Return whether ARTICLE is the last article in the buffer."
3505 (if (not (setq article (or article (gnus-summary-article-number))))
16409b0b
GM
3506 ;; All non-existent numbers are the last article. :-)
3507 t
eec82323
LMI
3508 (not (cdr (gnus-data-find-list article)))))
3509
4921bbdd
CY
3510(defun gnus-make-thread-indent-array (&optional n)
3511 (when (or n
3512 (progn (setq n 200) nil)
3513 (null gnus-thread-indent-array)
3514 (/= gnus-thread-indent-level gnus-thread-indent-array-level))
3515 (setq gnus-thread-indent-array (make-vector (1+ n) "")
3516 gnus-thread-indent-array-level gnus-thread-indent-level)
3517 (while (>= n 0)
3518 (aset gnus-thread-indent-array n
6a30c01d 3519 (make-string (* n gnus-thread-indent-level) ? ))
4921bbdd 3520 (setq n (1- n)))))
eec82323
LMI
3521
3522(defun gnus-update-summary-mark-positions ()
3523 "Compute where the summary marks are to go."
3524 (save-excursion
6748645f 3525 (when (gnus-buffer-exists-p gnus-summary-buffer)
eec82323 3526 (set-buffer gnus-summary-buffer))
5153a47a
MB
3527 (let ((spec gnus-summary-line-format-spec)
3528 pos)
eec82323
LMI
3529 (save-excursion
3530 (gnus-set-work-buffer)
5153a47a
MB
3531 (let ((gnus-tmp-unread ?Z)
3532 (gnus-replied-mark ?Z)
3533 (gnus-score-below-mark ?Z)
3534 (gnus-score-over-mark ?Z)
3535 (gnus-undownloaded-mark ?Z)
3536 (gnus-summary-line-format-spec spec)
54506618 3537 (gnus-newsgroup-downloadable '(0))
5153a47a
MB
3538 (header [0 "" "" "05 Apr 2001 23:33:09 +0400" "" "" 0 0 "" nil])
3539 case-fold-search ignores)
3540 ;; Here, all marks are bound to Z.
3541 (gnus-summary-insert-line header
3542 0 nil t gnus-tmp-unread t nil "" nil 1)
3543 (goto-char (point-min))
3544 ;; Memorize the positions of the same characters as dummy marks.
3545 (while (re-search-forward "[A-D]" nil t)
3546 (push (point) ignores))
54506618 3547 (erase-buffer)
5153a47a
MB
3548 ;; We use A-D as dummy marks in order to know column positions
3549 ;; where marks should be inserted.
3550 (setq gnus-tmp-unread ?A
3551 gnus-replied-mark ?B
3552 gnus-score-below-mark ?C
3553 gnus-score-over-mark ?C
3554 gnus-undownloaded-mark ?D)
3555 (gnus-summary-insert-line header
3556 0 nil t gnus-tmp-unread t nil "" nil 1)
3557 ;; Ignore characters which aren't dummy marks.
3558 (dolist (p ignores)
3559 (delete-region (goto-char (1- p)) p)
3560 (insert ?Z))
eec82323 3561 (goto-char (point-min))
7c3bb5a5 3562 (setq pos (list (cons 'unread
5153a47a 3563 (and (search-forward "A" nil t)
7c3bb5a5 3564 (- (point) (point-min) 1)))))
eec82323 3565 (goto-char (point-min))
5153a47a 3566 (push (cons 'replied (and (search-forward "B" nil t)
667e0ba6 3567 (- (point) (point-min) 1)))
eec82323
LMI
3568 pos)
3569 (goto-char (point-min))
5153a47a 3570 (push (cons 'score (and (search-forward "C" nil t)
667e0ba6 3571 (- (point) (point-min) 1)))
6748645f
LMI
3572 pos)
3573 (goto-char (point-min))
5153a47a 3574 (push (cons 'download (and (search-forward "D" nil t)
7c3bb5a5 3575 (- (point) (point-min) 1)))
eec82323
LMI
3576 pos)))
3577 (setq gnus-summary-mark-positions pos))))
3578
3579(defun gnus-summary-insert-dummy-line (gnus-tmp-subject gnus-tmp-number)
3580 "Insert a dummy root in the summary buffer."
3581 (beginning-of-line)
3582 (gnus-add-text-properties
3583 (point) (progn (eval gnus-summary-dummy-line-format-spec) (point))
3584 (list 'gnus-number gnus-tmp-number 'gnus-intangible gnus-tmp-number)))
3585
23f87bed
MB
3586(defun gnus-summary-extract-address-component (from)
3587 (or (car (funcall gnus-extract-address-components from))
3588 from))
3589
3590(defun gnus-summary-from-or-to-or-newsgroups (header gnus-tmp-from)
3591 (let ((mail-parse-charset gnus-newsgroup-charset)
01c52d31 3592 (ignored-from-addresses (gnus-ignored-from-addresses))
23f87bed
MB
3593 ; Is it really necessary to do this next part for each summary line?
3594 ; Luckily, doesn't seem to slow things down much.
16409b0b 3595 (mail-parse-ignored-charsets
01c52d31
MB
3596 (with-current-buffer gnus-summary-buffer
3597 gnus-newsgroup-ignored-charsets)))
23f87bed 3598 (or
01c52d31
MB
3599 (and ignored-from-addresses
3600 (string-match ignored-from-addresses gnus-tmp-from)
23f87bed
MB
3601 (let ((extra-headers (mail-header-extra header))
3602 to
3603 newsgroups)
3604 (cond
3605 ((setq to (cdr (assq 'To extra-headers)))
01c52d31 3606 (concat gnus-summary-to-prefix
23f87bed
MB
3607 (inline
3608 (gnus-summary-extract-address-component
343d6628 3609 (funcall gnus-decode-encoded-address-function to)))))
01c52d31
MB
3610 ((setq newsgroups
3611 (or
3612 (cdr (assq 'Newsgroups extra-headers))
3613 (and
3614 (memq 'Newsgroups gnus-extra-headers)
3615 (eq (car (gnus-find-method-for-group
3616 gnus-newsgroup-name)) 'nntp)
3617 (gnus-group-real-name gnus-newsgroup-name))))
3618 (concat gnus-summary-newsgroup-prefix newsgroups)))))
23f87bed 3619 (inline (gnus-summary-extract-address-component gnus-tmp-from)))))
16409b0b 3620
eec82323
LMI
3621(defun gnus-summary-insert-line (gnus-tmp-header
3622 gnus-tmp-level gnus-tmp-current
23f87bed 3623 undownloaded gnus-tmp-unread gnus-tmp-replied
eec82323
LMI
3624 gnus-tmp-expirable gnus-tmp-subject-or-nil
3625 &optional gnus-tmp-dummy gnus-tmp-score
3626 gnus-tmp-process)
4921bbdd
CY
3627 (if (>= gnus-tmp-level (length gnus-thread-indent-array))
3628 (gnus-make-thread-indent-array (max (* 2 (length gnus-thread-indent-array))
3629 gnus-tmp-level)))
eec82323
LMI
3630 (let* ((gnus-tmp-indentation (aref gnus-thread-indent-array gnus-tmp-level))
3631 (gnus-tmp-lines (mail-header-lines gnus-tmp-header))
3632 (gnus-tmp-score (or gnus-tmp-score gnus-summary-default-score 0))
3633 (gnus-tmp-score-char
3634 (if (or (null gnus-summary-default-score)
3635 (<= (abs (- gnus-tmp-score gnus-summary-default-score))
3636 gnus-summary-zcore-fuzz))
23f87bed 3637 ? ;Whitespace
eec82323
LMI
3638 (if (< gnus-tmp-score gnus-summary-default-score)
3639 gnus-score-below-mark gnus-score-over-mark)))
23f87bed 3640 (gnus-tmp-number (mail-header-number gnus-tmp-header))
eec82323
LMI
3641 (gnus-tmp-replied
3642 (cond (gnus-tmp-process gnus-process-mark)
3643 ((memq gnus-tmp-current gnus-newsgroup-cached)
3644 gnus-cached-mark)
3645 (gnus-tmp-replied gnus-replied-mark)
23f87bed
MB
3646 ((memq gnus-tmp-current gnus-newsgroup-forwarded)
3647 gnus-forwarded-mark)
eec82323
LMI
3648 ((memq gnus-tmp-current gnus-newsgroup-saved)
3649 gnus-saved-mark)
23f87bed
MB
3650 ((memq gnus-tmp-number gnus-newsgroup-recent)
3651 gnus-recent-mark)
3652 ((memq gnus-tmp-number gnus-newsgroup-unseen)
3653 gnus-unseen-mark)
3654 (t gnus-no-mark)))
3655 (gnus-tmp-downloaded
3656 (cond (undownloaded
3657 gnus-undownloaded-mark)
3658 (gnus-newsgroup-agentized
3659 gnus-downloaded-mark)
3660 (t
3661 gnus-no-mark)))
eec82323
LMI
3662 (gnus-tmp-from (mail-header-from gnus-tmp-header))
3663 (gnus-tmp-name
3664 (cond
3665 ((string-match "<[^>]+> *$" gnus-tmp-from)
3666 (let ((beg (match-beginning 0)))
23f87bed
MB
3667 (or (and (string-match "^\".+\"" gnus-tmp-from)
3668 (substring gnus-tmp-from 1 (1- (match-end 0))))
eec82323
LMI
3669 (substring gnus-tmp-from 0 beg))))
3670 ((string-match "(.+)" gnus-tmp-from)
3671 (substring gnus-tmp-from
3672 (1+ (match-beginning 0)) (1- (match-end 0))))
3673 (t gnus-tmp-from)))
3674 (gnus-tmp-subject (mail-header-subject gnus-tmp-header))
eec82323
LMI
3675 (gnus-tmp-opening-bracket (if gnus-tmp-dummy ?\< ?\[))
3676 (gnus-tmp-closing-bracket (if gnus-tmp-dummy ?\> ?\]))
3677 (buffer-read-only nil))
3678 (when (string= gnus-tmp-name "")
3679 (setq gnus-tmp-name gnus-tmp-from))
3680 (unless (numberp gnus-tmp-lines)
23f87bed
MB
3681 (setq gnus-tmp-lines -1))
3682 (if (= gnus-tmp-lines -1)
3683 (setq gnus-tmp-lines "?")
3684 (setq gnus-tmp-lines (number-to-string gnus-tmp-lines)))
3685 (gnus-put-text-property
eec82323
LMI
3686 (point)
3687 (progn (eval gnus-summary-line-format-spec) (point))
23f87bed 3688 'gnus-number gnus-tmp-number)
eec82323
LMI
3689 (when (gnus-visual-p 'summary-highlight 'highlight)
3690 (forward-line -1)
6748645f 3691 (gnus-run-hooks 'gnus-summary-update-hook)
eec82323
LMI
3692 (forward-line 1))))
3693
3694(defun gnus-summary-update-line (&optional dont-update)
16409b0b 3695 "Update summary line after change."
eec82323
LMI
3696 (when (and gnus-summary-default-score
3697 (not gnus-summary-inhibit-highlight))
3698 (let* ((gnus-summary-inhibit-highlight t) ; Prevent recursion.
3699 (article (gnus-summary-article-number))
3700 (score (gnus-summary-article-score article)))
3701 (unless dont-update
3702 (if (and gnus-summary-mark-below
3703 (< (gnus-summary-article-score)
3704 gnus-summary-mark-below))
3705 ;; This article has a low score, so we mark it as read.
3706 (when (memq article gnus-newsgroup-unreads)
3707 (gnus-summary-mark-article-as-read gnus-low-score-mark))
3708 (when (eq (gnus-summary-article-mark) gnus-low-score-mark)
3709 ;; This article was previously marked as read on account
3710 ;; of a low score, but now it has risen, so we mark it as
3711 ;; unread.
3712 (gnus-summary-mark-article-as-unread gnus-unread-mark)))
3713 (gnus-summary-update-mark
3714 (if (or (null gnus-summary-default-score)
3715 (<= (abs (- score gnus-summary-default-score))
3716 gnus-summary-zcore-fuzz))
23f87bed 3717 ? ;Whitespace
eec82323
LMI
3718 (if (< score gnus-summary-default-score)
3719 gnus-score-below-mark gnus-score-over-mark))
3720 'score))
3721 ;; Do visual highlighting.
3722 (when (gnus-visual-p 'summary-highlight 'highlight)
6748645f 3723 (gnus-run-hooks 'gnus-summary-update-hook)))))
eec82323
LMI
3724
3725(defvar gnus-tmp-new-adopts nil)
3726
3727(defun gnus-summary-number-of-articles-in-thread (thread &optional level char)
3728 "Return the number of articles in THREAD.
3729This may be 0 in some cases -- if none of the articles in
3730the thread are to be displayed."
3731 (let* ((number
23f87bed 3732 ;; Fix by Luc Van Eycken <Luc.VanEycken@esat.kuleuven.ac.be>.
eec82323
LMI
3733 (cond
3734 ((not (listp thread))
3735 1)
3736 ((and (consp thread) (cdr thread))
3737 (apply
3738 '+ 1 (mapcar
3739 'gnus-summary-number-of-articles-in-thread (cdr thread))))
3740 ((null thread)
3741 1)
3742 ((memq (mail-header-number (car thread)) gnus-newsgroup-limit)
3743 1)
3744 (t 0))))
3745 (when (and level (zerop level) gnus-tmp-new-adopts)
3746 (incf number
3747 (apply '+ (mapcar
3748 'gnus-summary-number-of-articles-in-thread
3749 gnus-tmp-new-adopts))))
3750 (if char
3751 (if (> number 1) gnus-not-empty-thread-mark
3752 gnus-empty-thread-mark)
3753 number)))
3754
23f87bed
MB
3755(defsubst gnus-summary-line-message-size (head)
3756 "Return pretty-printed version of message size.
3757This function is intended to be used in
3758`gnus-summary-line-format-alist'."
3759 (let ((c (or (mail-header-chars head) -1)))
3760 (cond ((< c 0) "n/a") ; chars not available
3761 ((< c (* 1000 10)) (format "%1.1fk" (/ c 1024.0)))
3762 ((< c (* 1000 100)) (format "%dk" (/ c 1024.0)))
3763 ((< c (* 1000 10000)) (format "%1.1fM" (/ c (* 1024.0 1024))))
3764 (t (format "%dM" (/ c (* 1024.0 1024)))))))
3765
3766
eec82323
LMI
3767(defun gnus-summary-set-local-parameters (group)
3768 "Go through the local params of GROUP and set all variable specs in that list."
01c52d31
MB
3769 (let ((vars '(quit-config))) ; Ignore quit-config.
3770 (dolist (elem (gnus-group-find-parameter group))
eec82323
LMI
3771 (and (consp elem) ; Has to be a cons.
3772 (consp (cdr elem)) ; The cdr has to be a list.
3773 (symbolp (car elem)) ; Has to be a symbol in there.
23f87bed 3774 (not (memq (car elem) vars))
eec82323 3775 (ignore-errors ; So we set it.
23f87bed 3776 (push (car elem) vars)
eec82323
LMI
3777 (make-local-variable (car elem))
3778 (set (car elem) (eval (nth 1 elem))))))))
3779
3780(defun gnus-summary-read-group (group &optional show-all no-article
6748645f
LMI
3781 kill-buffer no-display backward
3782 select-articles)
eec82323
LMI
3783 "Start reading news in newsgroup GROUP.
3784If SHOW-ALL is non-nil, already read articles are also listed.
3785If NO-ARTICLE is non-nil, no article is selected initially.
3786If NO-DISPLAY, don't generate a summary buffer."
3787 (let (result)
3788 (while (and group
3789 (null (setq result
3790 (let ((gnus-auto-select-next nil))
6748645f
LMI
3791 (or (gnus-summary-read-group-1
3792 group show-all no-article
3793 kill-buffer no-display
3794 select-articles)
3795 (setq show-all nil
16409b0b 3796 select-articles nil)))))
eec82323
LMI
3797 (eq gnus-auto-select-next 'quietly))
3798 (set-buffer gnus-group-buffer)
6748645f
LMI
3799 ;; The entry function called above goes to the next
3800 ;; group automatically, so we go two groups back
3801 ;; if we are searching for the previous group.
3802 (when backward
3803 (gnus-group-prev-unread-group 2))
eec82323
LMI
3804 (if (not (equal group (gnus-group-group-name)))
3805 (setq group (gnus-group-group-name))
3806 (setq group nil)))
3807 result))
3808
3809(defun gnus-summary-read-group-1 (group show-all no-article
6748645f
LMI
3810 kill-buffer no-display
3811 &optional select-articles)
eec82323 3812 ;; Killed foreign groups can't be entered.
23f87bed
MB
3813 ;; (when (and (not (gnus-group-native-p group))
3814 ;; (not (gnus-gethash group gnus-newsrc-hashtb)))
3815 ;; (error "Dead non-native groups can't be entered"))
3816 (gnus-message 5 "Retrieving newsgroup: %s..."
3817 (gnus-group-decoded-name group))
eec82323
LMI
3818 (let* ((new-group (gnus-summary-setup-buffer group))
3819 (quit-config (gnus-group-quit-config group))
6748645f
LMI
3820 (did-select (and new-group (gnus-select-newsgroup
3821 group show-all select-articles))))
eec82323
LMI
3822 (cond
3823 ;; This summary buffer exists already, so we just select it.
3824 ((not new-group)
3825 (gnus-set-global-variables)
3826 (when kill-buffer
3827 (gnus-kill-or-deaden-summary kill-buffer))
3828 (gnus-configure-windows 'summary 'force)
3829 (gnus-set-mode-line 'summary)
3830 (gnus-summary-position-point)
3831 (message "")
3832 t)
3833 ;; We couldn't select this group.
3834 ((null did-select)
3835 (when (and (eq major-mode 'gnus-summary-mode)
3836 (not (equal (current-buffer) kill-buffer)))
3837 (kill-buffer (current-buffer))
3838 (if (not quit-config)
3839 (progn
6748645f
LMI
3840 ;; Update the info -- marks might need to be removed,
3841 ;; for instance.
3842 (gnus-summary-update-info)
eec82323
LMI
3843 (set-buffer gnus-group-buffer)
3844 (gnus-group-jump-to-group group)
3845 (gnus-group-next-unread-group 1))
3846 (gnus-handle-ephemeral-exit quit-config)))
23f87bed
MB
3847 (let ((grpinfo (gnus-get-info group)))
3848 (if (null (gnus-info-read grpinfo))
3849 (gnus-message 3 "Group %s contains no messages"
3850 (gnus-group-decoded-name group))
3851 (gnus-message 3 "Can't select group")))
eec82323
LMI
3852 nil)
3853 ;; The user did a `C-g' while prompting for number of articles,
3854 ;; so we exit this group.
3855 ((eq did-select 'quit)
3856 (and (eq major-mode 'gnus-summary-mode)
3857 (not (equal (current-buffer) kill-buffer))
3858 (kill-buffer (current-buffer)))
3859 (when kill-buffer
3860 (gnus-kill-or-deaden-summary kill-buffer))
3861 (if (not quit-config)
3862 (progn
3863 (set-buffer gnus-group-buffer)
3864 (gnus-group-jump-to-group group)
3865 (gnus-group-next-unread-group 1)
3866 (gnus-configure-windows 'group 'force))
3867 (gnus-handle-ephemeral-exit quit-config))
3868 ;; Finally signal the quit.
3869 (signal 'quit nil))
3870 ;; The group was successfully selected.
3871 (t
3872 (gnus-set-global-variables)
3873 ;; Save the active value in effect when the group was entered.
3874 (setq gnus-newsgroup-active
3875 (gnus-copy-sequence
3876 (gnus-active gnus-newsgroup-name)))
3877 ;; You can change the summary buffer in some way with this hook.
6748645f 3878 (gnus-run-hooks 'gnus-select-group-hook)
5153a47a
MB
3879 (when (memq 'summary (gnus-update-format-specifications
3880 nil 'summary 'summary-mode 'summary-dummy))
3881 ;; The format specification for the summary line was updated,
3882 ;; so we need to update the mark positions as well.
3883 (gnus-update-summary-mark-positions))
eec82323
LMI
3884 ;; Do score processing.
3885 (when gnus-use-scoring
3886 (gnus-possibly-score-headers))
3887 ;; Check whether to fill in the gaps in the threads.
3888 (when gnus-build-sparse-threads
3889 (gnus-build-sparse-threads))
3890 ;; Find the initial limit.
26c9afc3
MB
3891 (if show-all
3892 (let ((gnus-newsgroup-dormant nil))
eec82323 3893 (gnus-summary-initial-limit show-all))
26c9afc3 3894 (gnus-summary-initial-limit show-all))
eec82323
LMI
3895 ;; Generate the summary buffer.
3896 (unless no-display
3897 (gnus-summary-prepare))
3898 (when gnus-use-trees
3899 (gnus-tree-open group)
3900 (setq gnus-summary-highlight-line-function
3901 'gnus-tree-highlight-article))
3902 ;; If the summary buffer is empty, but there are some low-scored
3903 ;; articles or some excluded dormants, we include these in the
3904 ;; buffer.
3905 (when (and (zerop (buffer-size))
3906 (not no-display))
3907 (cond (gnus-newsgroup-dormant
3908 (gnus-summary-limit-include-dormant))
3909 ((and gnus-newsgroup-scored show-all)
3910 (gnus-summary-limit-include-expunged t))))
3911 ;; Function `gnus-apply-kill-file' must be called in this hook.
6748645f 3912 (gnus-run-hooks 'gnus-apply-kill-hook)
eec82323
LMI
3913 (if (and (zerop (buffer-size))
3914 (not no-display))
3915 (progn
3916 ;; This newsgroup is empty.
3917 (gnus-summary-catchup-and-exit nil t)
3918 (gnus-message 6 "No unread news")
3919 (when kill-buffer
3920 (gnus-kill-or-deaden-summary kill-buffer))
3921 ;; Return nil from this function.
3922 nil)
3923 ;; Hide conversation thread subtrees. We cannot do this in
3924 ;; gnus-summary-prepare-hook since kill processing may not
3925 ;; work with hidden articles.
23f87bed 3926 (gnus-summary-maybe-hide-threads)
6748645f
LMI
3927 (when kill-buffer
3928 (gnus-kill-or-deaden-summary kill-buffer))
23f87bed 3929 (gnus-summary-auto-select-subject)
eec82323
LMI
3930 ;; Show first unread article if requested.
3931 (if (and (not no-article)
3932 (not no-display)
3933 gnus-newsgroup-unreads
3934 gnus-auto-select-first)
16409b0b
GM
3935 (progn
3936 (gnus-configure-windows 'summary)
23f87bed
MB
3937 (let ((art (gnus-summary-article-number)))
3938 (unless (and (not gnus-plugged)
3939 (or (memq art gnus-newsgroup-undownloaded)
3940 (memq art gnus-newsgroup-downloadable)))
3941 (gnus-summary-goto-article art))))
3942 ;; Don't select any articles.
eec82323 3943 (gnus-summary-position-point)
6748645f
LMI
3944 (gnus-configure-windows 'summary 'force)
3945 (gnus-set-mode-line 'summary))
23f87bed
MB
3946 (when (and gnus-auto-center-group
3947 (get-buffer-window gnus-group-buffer t))
eec82323
LMI
3948 ;; Gotta use windows, because recenter does weird stuff if
3949 ;; the current buffer ain't the displayed window.
3950 (let ((owin (selected-window)))
3951 (select-window (get-buffer-window gnus-group-buffer t))
3952 (when (gnus-group-goto-group group)
3953 (recenter))
3954 (select-window owin)))
3955 ;; Mark this buffer as "prepared".
3956 (setq gnus-newsgroup-prepared t)
6748645f 3957 (gnus-run-hooks 'gnus-summary-prepared-hook)
23f87bed
MB
3958 (unless (gnus-ephemeral-group-p group)
3959 (gnus-group-update-group group))
eec82323
LMI
3960 t)))))
3961
23f87bed
MB
3962(defun gnus-summary-auto-select-subject ()
3963 "Select the subject line on initial group entry."
3964 (goto-char (point-min))
3965 (cond
3966 ((eq gnus-auto-select-subject 'best)
3967 (gnus-summary-best-unread-subject))
3968 ((eq gnus-auto-select-subject 'unread)
3969 (gnus-summary-first-unread-subject))
3970 ((eq gnus-auto-select-subject 'unseen)
3971 (gnus-summary-first-unseen-subject))
3972 ((eq gnus-auto-select-subject 'unseen-or-unread)
3973 (gnus-summary-first-unseen-or-unread-subject))
3974 ((eq gnus-auto-select-subject 'first)
3975 ;; Do nothing.
3976 )
3977 ((functionp gnus-auto-select-subject)
3978 (funcall gnus-auto-select-subject))))
3979
eec82323
LMI
3980(defun gnus-summary-prepare ()
3981 "Generate the summary buffer."
3982 (interactive)
3983 (let ((buffer-read-only nil))
3984 (erase-buffer)
3985 (setq gnus-newsgroup-data nil
3986 gnus-newsgroup-data-reverse nil)
6748645f 3987 (gnus-run-hooks 'gnus-summary-generate-hook)
eec82323
LMI
3988 ;; Generate the buffer, either with threads or without.
3989 (when gnus-newsgroup-headers
3990 (gnus-summary-prepare-threads
3991 (if gnus-show-threads
3992 (gnus-sort-gathered-threads
3993 (funcall gnus-summary-thread-gathering-function
3994 (gnus-sort-threads
3995 (gnus-cut-threads (gnus-make-threads)))))
3996 ;; Unthreaded display.
3997 (gnus-sort-articles gnus-newsgroup-headers))))
3998 (setq gnus-newsgroup-data (nreverse gnus-newsgroup-data))
3999 ;; Call hooks for modifying summary buffer.
4000 (goto-char (point-min))
6748645f 4001 (gnus-run-hooks 'gnus-summary-prepare-hook)))
eec82323
LMI
4002
4003(defsubst gnus-general-simplify-subject (subject)
23f87bed 4004 "Simplify subject by the same rules as `gnus-gather-threads-by-subject'."
eec82323
LMI
4005 (setq subject
4006 (cond
4007 ;; Truncate the subject.
6748645f
LMI
4008 (gnus-simplify-subject-functions
4009 (gnus-map-function gnus-simplify-subject-functions subject))
eec82323
LMI
4010 ((numberp gnus-summary-gather-subject-limit)
4011 (setq subject (gnus-simplify-subject-re subject))
4012 (if (> (length subject) gnus-summary-gather-subject-limit)
4013 (substring subject 0 gnus-summary-gather-subject-limit)
4014 subject))
4015 ;; Fuzzily simplify it.
4016 ((eq 'fuzzy gnus-summary-gather-subject-limit)
4017 (gnus-simplify-subject-fuzzy subject))
4018 ;; Just remove the leading "Re:".
4019 (t
4020 (gnus-simplify-subject-re subject))))
4021
4022 (if (and gnus-summary-gather-exclude-subject
4023 (string-match gnus-summary-gather-exclude-subject subject))
23f87bed 4024 nil ; This article shouldn't be gathered
eec82323
LMI
4025 subject))
4026
4027(defun gnus-summary-simplify-subject-query ()
4028 "Query where the respool algorithm would put this article."
4029 (interactive)
eec82323 4030 (gnus-summary-select-article)
274f1353 4031 (message "%s" (gnus-general-simplify-subject (gnus-summary-article-subject))))
eec82323
LMI
4032
4033(defun gnus-gather-threads-by-subject (threads)
4034 "Gather threads by looking at Subject headers."
4035 (if (not gnus-summary-make-false-root)
4036 threads
4037 (let ((hashtb (gnus-make-hashtable 1024))
4038 (prev threads)
4039 (result threads)
4040 subject hthread whole-subject)
4041 (while threads
4042 (setq subject (gnus-general-simplify-subject
4043 (setq whole-subject (mail-header-subject
4044 (caar threads)))))
4045 (when subject
4046 (if (setq hthread (gnus-gethash subject hashtb))
4047 (progn
4048 ;; We enter a dummy root into the thread, if we
4049 ;; haven't done that already.
4050 (unless (stringp (caar hthread))
4051 (setcar hthread (list whole-subject (car hthread))))
4052 ;; We add this new gathered thread to this gathered
4053 ;; thread.
4054 (setcdr (car hthread)
4055 (nconc (cdar hthread) (list (car threads))))
4056 ;; Remove it from the list of threads.
4057 (setcdr prev (cdr threads))
4058 (setq threads prev))
4059 ;; Enter this thread into the hash table.
23f87bed
MB
4060 (gnus-sethash subject
4061 (if gnus-summary-make-false-root-always
4062 (progn
4063 ;; If you want a dummy root above all
4064 ;; threads...
4065 (setcar threads (list whole-subject
4066 (car threads)))
4067 threads)
4068 threads)
4069 hashtb)))
eec82323
LMI
4070 (setq prev threads)
4071 (setq threads (cdr threads)))
4072 result)))
4073
4074(defun gnus-gather-threads-by-references (threads)
4075 "Gather threads by looking at References headers."
4076 (let ((idhashtb (gnus-make-hashtable 1024))
4077 (thhashtb (gnus-make-hashtable 1024))
4078 (prev threads)
4079 (result threads)
4080 ids references id gthread gid entered ref)
4081 (while threads
4082 (when (setq references (mail-header-references (caar threads)))
4083 (setq id (mail-header-id (caar threads))
23f87bed 4084 ids (inline (gnus-split-references references))
eec82323
LMI
4085 entered nil)
4086 (while (setq ref (pop ids))
4087 (setq ids (delete ref ids))
4088 (if (not (setq gid (gnus-gethash ref idhashtb)))
4089 (progn
4090 (gnus-sethash ref id idhashtb)
4091 (gnus-sethash id threads thhashtb))
4092 (setq gthread (gnus-gethash gid thhashtb))
4093 (unless entered
4094 ;; We enter a dummy root into the thread, if we
4095 ;; haven't done that already.
4096 (unless (stringp (caar gthread))
4097 (setcar gthread (list (mail-header-subject (caar gthread))
4098 (car gthread))))
4099 ;; We add this new gathered thread to this gathered
4100 ;; thread.
4101 (setcdr (car gthread)
4102 (nconc (cdar gthread) (list (car threads)))))
4103 ;; Add it into the thread hash table.
4104 (gnus-sethash id gthread thhashtb)
4105 (setq entered t)
4106 ;; Remove it from the list of threads.
4107 (setcdr prev (cdr threads))
4108 (setq threads prev))))
4109 (setq prev threads)
4110 (setq threads (cdr threads)))
4111 result))
4112
4113(defun gnus-sort-gathered-threads (threads)
16409b0b 4114 "Sort subtreads inside each gathered thread by `gnus-sort-gathered-threads-function'."
eec82323
LMI
4115 (let ((result threads))
4116 (while threads
4117 (when (stringp (caar threads))
4118 (setcdr (car threads)
16409b0b 4119 (sort (cdar threads) gnus-sort-gathered-threads-function)))
eec82323
LMI
4120 (setq threads (cdr threads)))
4121 result))
4122
4123(defun gnus-thread-loop-p (root thread)
4124 "Say whether ROOT is in THREAD."
4125 (let ((stack (list thread))
4126 (infloop 0)
4127 th)
4128 (while (setq thread (pop stack))
4129 (setq th (cdr thread))
4130 (while (and th
4131 (not (eq (caar th) root)))
4132 (pop th))
4133 (if th
4134 ;; We have found a loop.
4135 (let (ref-dep)
4136 (setcdr thread (delq (car th) (cdr thread)))
4137 (if (boundp (setq ref-dep (intern "none"
4138 gnus-newsgroup-dependencies)))
4139 (setcdr (symbol-value ref-dep)
4140 (nconc (cdr (symbol-value ref-dep))
4141 (list (car th))))
4142 (set ref-dep (list nil (car th))))
4143 (setq infloop 1
4144 stack nil))
4145 ;; Push all the subthreads onto the stack.
4146 (push (cdr thread) stack)))
4147 infloop))
4148
4149(defun gnus-make-threads ()
01ccbb85 4150 "Go through the dependency hashtb and find the roots. Return all threads."
eec82323
LMI
4151 (let (threads)
4152 (while (catch 'infloop
4153 (mapatoms
4154 (lambda (refs)
4155 ;; Deal with self-referencing References loops.
4156 (when (and (car (symbol-value refs))
4157 (not (zerop
4158 (apply
4159 '+
4160 (mapcar
4161 (lambda (thread)
4162 (gnus-thread-loop-p
4163 (car (symbol-value refs)) thread))
4164 (cdr (symbol-value refs)))))))
4165 (setq threads nil)
4166 (throw 'infloop t))
4167 (unless (car (symbol-value refs))
23f87bed
MB
4168 ;; These threads do not refer back to any other
4169 ;; articles, so they're roots.
eec82323
LMI
4170 (setq threads (append (cdr (symbol-value refs)) threads))))
4171 gnus-newsgroup-dependencies)))
4172 threads))
4173
6748645f 4174;; Build the thread tree.
16409b0b 4175(defsubst gnus-dependencies-add-header (header dependencies force-new)
6748645f
LMI
4176 "Enter HEADER into the DEPENDENCIES table if it is not already there.
4177
4178If FORCE-NEW is not nil, enter HEADER into the DEPENDENCIES table even
4179if it was already present.
4180
4181If `gnus-summary-ignore-duplicates' is nil then duplicate Message-IDs
4182will not be entered in the DEPENDENCIES table. Otherwise duplicate
23f87bed
MB
4183Message-IDs will be renamed to a unique Message-ID before being
4184entered.
6748645f
LMI
4185
4186Returns HEADER if it was entered in the DEPENDENCIES. Returns nil otherwise."
4187 (let* ((id (mail-header-id header))
4188 (id-dep (and id (intern id dependencies)))
23f87bed 4189 parent-id ref ref-dep ref-header replaced)
6748645f
LMI
4190 ;; Enter this `header' in the `dependencies' table.
4191 (cond
4192 ((not id-dep)
4193 (setq header nil))
4194 ;; The first two cases do the normal part: enter a new `header'
4195 ;; in the `dependencies' table.
4196 ((not (boundp id-dep))
4197 (set id-dep (list header)))
4198 ((null (car (symbol-value id-dep)))
4199 (setcar (symbol-value id-dep) header))
4200
4201 ;; From here the `header' was already present in the
4202 ;; `dependencies' table.
4203 (force-new
4204 ;; Overrides an existing entry;
4205 ;; just set the header part of the entry.
23f87bed
MB
4206 (setcar (symbol-value id-dep) header)
4207 (setq replaced t))
6748645f
LMI
4208
4209 ;; Renames the existing `header' to a unique Message-ID.
4210 ((not gnus-summary-ignore-duplicates)
4211 ;; An article with this Message-ID has already been seen.
4212 ;; We rename the Message-ID.
4213 (set (setq id-dep (intern (setq id (nnmail-message-id)) dependencies))
4214 (list header))
4215 (mail-header-set-id header id))
4216
4217 ;; The last case ignores an existing entry, except it adds any
4218 ;; additional Xrefs (in case the two articles came from different
4219 ;; servers.
4220 ;; Also sets `header' to `nil' meaning that the `dependencies'
4221 ;; table was *not* modified.
4222 (t
4223 (mail-header-set-xref
4224 (car (symbol-value id-dep))
4225 (concat (or (mail-header-xref (car (symbol-value id-dep)))
4226 "")
4227 (or (mail-header-xref header) "")))
4228 (setq header nil)))
4229
23f87bed
MB
4230 (when (and header (not replaced))
4231 ;; First check that we are not creating a References loop.
4232 (setq parent-id (gnus-parent-id (mail-header-references header)))
4233 (setq ref parent-id)
6748645f
LMI
4234 (while (and ref
4235 (setq ref-dep (intern-soft ref dependencies))
4236 (boundp ref-dep)
4237 (setq ref-header (car (symbol-value ref-dep))))
4238 (if (string= id ref)
4239 ;; Yuk! This is a reference loop. Make the article be a
4240 ;; root article.
4241 (progn
4242 (mail-header-set-references (car (symbol-value id-dep)) "none")
23f87bed
MB
4243 (setq ref nil)
4244 (setq parent-id nil))
6748645f 4245 (setq ref (gnus-parent-id (mail-header-references ref-header)))))
23f87bed 4246 (setq ref-dep (intern (or parent-id "none") dependencies))
6748645f
LMI
4247 (if (boundp ref-dep)
4248 (setcdr (symbol-value ref-dep)
4249 (nconc (cdr (symbol-value ref-dep))
4250 (list (symbol-value id-dep))))
4251 (set ref-dep (list nil (symbol-value id-dep)))))
4252 header))
4253
23f87bed
MB
4254(defun gnus-extract-message-id-from-in-reply-to (string)
4255 (if (string-match "<[^>]+>" string)
4256 (substring string (match-beginning 0) (match-end 0))
4257 nil))
4258
eec82323
LMI
4259(defun gnus-build-sparse-threads ()
4260 (let ((headers gnus-newsgroup-headers)
16409b0b 4261 (mail-parse-charset gnus-newsgroup-charset)
6748645f 4262 (gnus-summary-ignore-duplicates t)
eec82323 4263 header references generation relations
6748645f 4264 subject child end new-child date)
eec82323
LMI
4265 ;; First we create an alist of generations/relations, where
4266 ;; generations is how much we trust the relation, and the relation
4267 ;; is parent/child.
4268 (gnus-message 7 "Making sparse threads...")
4269 (save-excursion
4270 (nnheader-set-temp-buffer " *gnus sparse threads*")
4271 (while (setq header (pop headers))
4272 (when (and (setq references (mail-header-references header))
4273 (not (string= references "")))
4274 (insert references)
4275 (setq child (mail-header-id header)
6748645f
LMI
4276 subject (mail-header-subject header)
4277 date (mail-header-date header)
4278 generation 0)
eec82323
LMI
4279 (while (search-backward ">" nil t)
4280 (setq end (1+ (point)))
4281 (when (search-backward "<" nil t)
6748645f 4282 (setq new-child (buffer-substring (point) end))
eec82323 4283 (push (list (incf generation)
6748645f
LMI
4284 child (setq child new-child)
4285 subject date)
eec82323 4286 relations)))
6748645f
LMI
4287 (when child
4288 (push (list (1+ generation) child nil subject) relations))
eec82323
LMI
4289 (erase-buffer)))
4290 (kill-buffer (current-buffer)))
4291 ;; Sort over trustworthiness.
01c52d31
MB
4292 (dolist (relation (sort relations 'car-less-than-car))
4293 (when (gnus-dependencies-add-header
4294 (make-full-mail-header
4295 gnus-reffed-article-number
4296 (nth 3 relation) "" (or (nth 4 relation) "")
4297 (nth 1 relation)
4298 (or (nth 2 relation) "") 0 0 "")
4299 gnus-newsgroup-dependencies nil)
4300 (push gnus-reffed-article-number gnus-newsgroup-limit)
4301 (push gnus-reffed-article-number gnus-newsgroup-sparse)
4302 (push (cons gnus-reffed-article-number gnus-sparse-mark)
4303 gnus-newsgroup-reads)
4304 (decf gnus-reffed-article-number)))
eec82323
LMI
4305 (gnus-message 7 "Making sparse threads...done")))
4306
4307(defun gnus-build-old-threads ()
4308 ;; Look at all the articles that refer back to old articles, and
4309 ;; fetch the headers for the articles that aren't there. This will
4310 ;; build complete threads - if the roots haven't been expired by the
4311 ;; server, that is.
16409b0b
GM
4312 (let ((mail-parse-charset gnus-newsgroup-charset)
4313 id heads)
eec82323
LMI
4314 (mapatoms
4315 (lambda (refs)
4316 (when (not (car (symbol-value refs)))
4317 (setq heads (cdr (symbol-value refs)))
4318 (while heads
4319 (if (memq (mail-header-number (caar heads))
4320 gnus-newsgroup-dormant)
4321 (setq heads (cdr heads))
4322 (setq id (symbol-name refs))
4323 (while (and (setq id (gnus-build-get-header id))
6748645f 4324 (not (car (gnus-id-to-thread id)))))
eec82323
LMI
4325 (setq heads nil)))))
4326 gnus-newsgroup-dependencies)))
4327
23f87bed
MB
4328(defsubst gnus-remove-odd-characters (string)
4329 "Translate STRING into something that doesn't contain weird characters."
4330 (mm-subst-char-in-string
4331 ?\r ?\-
01c52d31 4332 (mm-subst-char-in-string ?\n ?\- string t) t))
23f87bed 4333
6748645f
LMI
4334;; This function has to be called with point after the article number
4335;; on the beginning of the line.
4336(defsubst gnus-nov-parse-line (number dependencies &optional force-new)
01c52d31 4337 (let ((eol (point-at-eol))
6748645f 4338 (buffer (current-buffer))
23f87bed 4339 header references in-reply-to)
6748645f
LMI
4340
4341 ;; overview: [num subject from date id refs chars lines misc]
4342 (unwind-protect
23f87bed 4343 (let (x)
6748645f
LMI
4344 (narrow-to-region (point) eol)
4345 (unless (eobp)
4346 (forward-char))
4347
4348 (setq header
4349 (make-full-mail-header
4350 number ; number
23f87bed
MB
4351 (condition-case () ; subject
4352 (gnus-remove-odd-characters
4353 (funcall gnus-decode-encoded-word-function
4354 (setq x (nnheader-nov-field))))
4355 (error x))
4356 (condition-case () ; from
4357 (gnus-remove-odd-characters
343d6628 4358 (funcall gnus-decode-encoded-address-function
23f87bed
MB
4359 (setq x (nnheader-nov-field))))
4360 (error x))
16409b0b 4361 (nnheader-nov-field) ; date
01c52d31 4362 (nnheader-nov-read-message-id number) ; id
23f87bed 4363 (setq references (nnheader-nov-field)) ; refs
16409b0b
GM
4364 (nnheader-nov-read-integer) ; chars
4365 (nnheader-nov-read-integer) ; lines
4366 (unless (eobp)
8b93df01
DL
4367 (if (looking-at "Xref: ")
4368 (goto-char (match-end 0)))
4369 (nnheader-nov-field)) ; Xref
16409b0b 4370 (nnheader-nov-parse-extra)))) ; extra
6748645f
LMI
4371
4372 (widen))
4373
23f87bed
MB
4374 (when (and (string= references "")
4375 (setq in-reply-to (mail-header-extra header))
4376 (setq in-reply-to (cdr (assq 'In-Reply-To in-reply-to))))
4377 (mail-header-set-references
4378 header (gnus-extract-message-id-from-in-reply-to in-reply-to)))
4379
6748645f
LMI
4380 (when gnus-alter-header-function
4381 (funcall gnus-alter-header-function header))
4382 (gnus-dependencies-add-header header dependencies force-new)))
4383
eec82323 4384(defun gnus-build-get-header (id)
16409b0b
GM
4385 "Look through the buffer of NOV lines and find the header to ID.
4386Enter this line into the dependencies hash table, and return
4387the id of the parent article (if any)."
eec82323
LMI
4388 (let ((deps gnus-newsgroup-dependencies)
4389 found header)
4390 (prog1
4391 (save-excursion
4392 (set-buffer nntp-server-buffer)
4393 (let ((case-fold-search nil))
4394 (goto-char (point-min))
4395 (while (and (not found)
4396 (search-forward id nil t))
4397 (beginning-of-line)
4398 (setq found (looking-at
4399 (format "^[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t%s"
4400 (regexp-quote id))))
4401 (or found (beginning-of-line 2)))
4402 (when found
4403 (beginning-of-line)
4404 (and
4405 (setq header (gnus-nov-parse-line
4406 (read (current-buffer)) deps))
4407 (gnus-parent-id (mail-header-references header))))))
4408 (when header
4409 (let ((number (mail-header-number header)))
4410 (push number gnus-newsgroup-limit)
4411 (push header gnus-newsgroup-headers)
4412 (if (memq number gnus-newsgroup-unselected)
4413 (progn
23f87bed
MB
4414 (setq gnus-newsgroup-unreads
4415 (gnus-add-to-sorted-list gnus-newsgroup-unreads
4416 number))
eec82323
LMI
4417 (setq gnus-newsgroup-unselected
4418 (delq number gnus-newsgroup-unselected)))
4419 (push number gnus-newsgroup-ancient)))))))
4420
6748645f
LMI
4421(defun gnus-build-all-threads ()
4422 "Read all the headers."
4423 (let ((gnus-summary-ignore-duplicates t)
16409b0b 4424 (mail-parse-charset gnus-newsgroup-charset)
6748645f
LMI
4425 (dependencies gnus-newsgroup-dependencies)
4426 header article)
4427 (save-excursion
4428 (set-buffer nntp-server-buffer)
4429 (let ((case-fold-search nil))
4430 (goto-char (point-min))
4431 (while (not (eobp))
4432 (ignore-errors
4433 (setq article (read (current-buffer))
16409b0b 4434 header (gnus-nov-parse-line article dependencies)))
6748645f 4435 (when header
01c52d31 4436 (with-current-buffer gnus-summary-buffer
6748645f
LMI
4437 (push header gnus-newsgroup-headers)
4438 (if (memq (setq article (mail-header-number header))
4439 gnus-newsgroup-unselected)
4440 (progn
23f87bed
MB
4441 (setq gnus-newsgroup-unreads
4442 (gnus-add-to-sorted-list
4443 gnus-newsgroup-unreads article))
6748645f
LMI
4444 (setq gnus-newsgroup-unselected
4445 (delq article gnus-newsgroup-unselected)))
4446 (push article gnus-newsgroup-ancient)))
4447 (forward-line 1)))))))
4448
eec82323 4449(defun gnus-summary-update-article-line (article header)
23f87bed 4450 "Update the line for ARTICLE using HEADER."
eec82323
LMI
4451 (let* ((id (mail-header-id header))
4452 (thread (gnus-id-to-thread id)))
4453 (unless thread
4454 (error "Article in no thread"))
4455 ;; Update the thread.
4456 (setcar thread header)
4457 (gnus-summary-goto-subject article)
4458 (let* ((datal (gnus-data-find-list article))
4459 (data (car datal))
eec82323
LMI
4460 (buffer-read-only nil)
4461 (level (gnus-summary-thread-level)))
4462 (gnus-delete-line)
23f87bed
MB
4463 (let ((inserted (- (point)
4464 (progn
4465 (gnus-summary-insert-line
4466 header level nil
4467 (memq article gnus-newsgroup-undownloaded)
4468 (gnus-article-mark article)
4469 (memq article gnus-newsgroup-replied)
4470 (memq article gnus-newsgroup-expirable)
4471 ;; Only insert the Subject string when it's different
4472 ;; from the previous Subject string.
4473 (if (and
4474 gnus-show-threads
4475 (gnus-subject-equal
4476 (condition-case ()
4477 (mail-header-subject
4478 (gnus-data-header
4479 (cadr
4480 (gnus-data-find-list
4481 article
4482 (gnus-data-list t)))))
4483 ;; Error on the side of excessive subjects.
4484 (error ""))
4485 (mail-header-subject header)))
4486 ""
4487 (mail-header-subject header))
4488 nil (cdr (assq article gnus-newsgroup-scored))
4489 (memq article gnus-newsgroup-processable))
4490 (point)))))
4491 (when (cdr datal)
4492 (gnus-data-update-list
4493 (cdr datal)
4494 (- (gnus-data-pos data) (gnus-data-pos (cadr datal)) inserted)))))))
eec82323
LMI
4495
4496(defun gnus-summary-update-article (article &optional iheader)
4497 "Update ARTICLE in the summary buffer."
4498 (set-buffer gnus-summary-buffer)
6748645f 4499 (let* ((header (gnus-summary-article-header article))
eec82323
LMI
4500 (id (mail-header-id header))
4501 (data (gnus-data-find article))
4502 (thread (gnus-id-to-thread id))
4503 (references (mail-header-references header))
4504 (parent
4505 (gnus-id-to-thread
4506 (or (gnus-parent-id
4507 (when (and references
4508 (not (equal "" references)))
4509 references))
4510 "none")))
4511 (buffer-read-only nil)
6748645f 4512 (old (car thread)))
eec82323 4513 (when thread
eec82323 4514 (unless iheader
6748645f
LMI
4515 (setcar thread nil)
4516 (when parent
4517 (delq thread parent)))
4518 (if (gnus-summary-insert-subject id header)
eec82323
LMI
4519 ;; Set the (possibly) new article number in the data structure.
4520 (gnus-data-set-number data (gnus-id-to-article id))
4521 (setcar thread old)
4522 nil))))
4523
6748645f
LMI
4524(defun gnus-rebuild-thread (id &optional line)
4525 "Rebuild the thread containing ID.
4526If LINE, insert the rebuilt thread starting on line LINE."
eec82323
LMI
4527 (let ((buffer-read-only nil)
4528 old-pos current thread data)
4529 (if (not gnus-show-threads)
4530 (setq thread (list (car (gnus-id-to-thread id))))
4531 ;; Get the thread this article is part of.
4532 (setq thread (gnus-remove-thread id)))
01c52d31 4533 (setq old-pos (point-at-bol))
eec82323 4534 (setq current (save-excursion
94384150 4535 (and (re-search-backward "[\r\n]" nil t)
eec82323
LMI
4536 (gnus-summary-article-number))))
4537 ;; If this is a gathered thread, we have to go some re-gathering.
4538 (when (stringp (car thread))
4539 (let ((subject (car thread))
4540 roots thr)
4541 (setq thread (cdr thread))
4542 (while thread
4543 (unless (memq (setq thr (gnus-id-to-thread
4544 (gnus-root-id
4545 (mail-header-id (caar thread)))))
4546 roots)
4547 (push thr roots))
4548 (setq thread (cdr thread)))
4549 ;; We now have all (unique) roots.
4550 (if (= (length roots) 1)
4551 ;; All the loose roots are now one solid root.
4552 (setq thread (car roots))
4553 (setq thread (cons subject (gnus-sort-threads roots))))))
4554 (let (threads)
4555 ;; We then insert this thread into the summary buffer.
6748645f
LMI
4556 (when line
4557 (goto-char (point-min))
4558 (forward-line (1- line)))
eec82323
LMI
4559 (let (gnus-newsgroup-data gnus-newsgroup-threads)
4560 (if gnus-show-threads
4561 (gnus-summary-prepare-threads (gnus-cut-threads (list thread)))
4562 (gnus-summary-prepare-unthreaded thread))
4563 (setq data (nreverse gnus-newsgroup-data))
4564 (setq threads gnus-newsgroup-threads))
4565 ;; We splice the new data into the data structure.
6748645f
LMI
4566 ;;!!! This is kinda bogus. We assume that in LINE is non-nil,
4567 ;;!!! then we want to insert at the beginning of the buffer.
4568 ;;!!! That happens to be true with Gnus now, but that may
4569 ;;!!! change in the future. Perhaps.
4570 (gnus-data-enter-list
4571 (if line nil current) data (- (point) old-pos))
4572 (setq gnus-newsgroup-threads
4573 (nconc threads gnus-newsgroup-threads))
4574 (gnus-data-compute-positions))))
eec82323
LMI
4575
4576(defun gnus-number-to-header (number)
4577 "Return the header for article NUMBER."
4578 (let ((headers gnus-newsgroup-headers))
4579 (while (and headers
4580 (not (= number (mail-header-number (car headers)))))
4581 (pop headers))
4582 (when headers
4583 (car headers))))
4584
6748645f 4585(defun gnus-parent-headers (in-headers &optional generation)
eec82323
LMI
4586 "Return the headers of the GENERATIONeth parent of HEADERS."
4587 (unless generation
4588 (setq generation 1))
a8151ef7 4589 (let ((parent t)
6748645f 4590 (headers in-headers)
a8151ef7 4591 references)
6748645f
LMI
4592 (while (and parent
4593 (not (zerop generation))
4594 (setq references (mail-header-references headers)))
4595 (setq headers (if (and references
4596 (setq parent (gnus-parent-id references)))
4597 (car (gnus-id-to-thread parent))
4598 nil))
4599 (decf generation))
4600 (and (not (eq headers in-headers))
4601 headers)))
eec82323
LMI
4602
4603(defun gnus-id-to-thread (id)
4604 "Return the (sub-)thread where ID appears."
4605 (gnus-gethash id gnus-newsgroup-dependencies))
4606
4607(defun gnus-id-to-article (id)
4608 "Return the article number of ID."
4609 (let ((thread (gnus-id-to-thread id)))
4610 (when (and thread
4611 (car thread))
4612 (mail-header-number (car thread)))))
4613
4614(defun gnus-id-to-header (id)
4615 "Return the article headers of ID."
4616 (car (gnus-id-to-thread id)))
4617
4618(defun gnus-article-displayed-root-p (article)
4619 "Say whether ARTICLE is a root(ish) article."
4620 (let ((level (gnus-summary-thread-level article))
4621 (refs (mail-header-references (gnus-summary-article-header article)))
4622 particle)
4623 (cond
4624 ((null level) nil)
4625 ((zerop level) t)
4626 ((null refs) t)
4627 ((null (gnus-parent-id refs)) t)
4628 ((and (= 1 level)
4629 (null (setq particle (gnus-id-to-article
4630 (gnus-parent-id refs))))
4631 (null (gnus-summary-thread-level particle)))))))
4632
4633(defun gnus-root-id (id)
4634 "Return the id of the root of the thread where ID appears."
4635 (let (last-id prev)
6748645f 4636 (while (and id (setq prev (car (gnus-id-to-thread id))))
eec82323
LMI
4637 (setq last-id id
4638 id (gnus-parent-id (mail-header-references prev))))
4639 last-id))
4640
6748645f
LMI
4641(defun gnus-articles-in-thread (thread)
4642 "Return the list of articles in THREAD."
4643 (cons (mail-header-number (car thread))
4644 (apply 'nconc (mapcar 'gnus-articles-in-thread (cdr thread)))))
4645
eec82323
LMI
4646(defun gnus-remove-thread (id &optional dont-remove)
4647 "Remove the thread that has ID in it."
6748645f 4648 (let (headers thread last-id)
eec82323 4649 ;; First go up in this thread until we find the root.
6748645f
LMI
4650 (setq last-id (gnus-root-id id)
4651 headers (message-flatten-list (gnus-id-to-thread last-id)))
01ccbb85 4652 ;; We have now found the real root of this thread. It might have
eec82323
LMI
4653 ;; been gathered into some loose thread, so we have to search
4654 ;; through the threads to find the thread we wanted.
4655 (let ((threads gnus-newsgroup-threads)
4656 sub)
4657 (while threads
4658 (setq sub (car threads))
4659 (if (stringp (car sub))
4660 ;; This is a gathered thread, so we look at the roots
4661 ;; below it to find whether this article is in this
4662 ;; gathered root.
4663 (progn
4664 (setq sub (cdr sub))
4665 (while sub
4666 (when (member (caar sub) headers)
4667 (setq thread (car threads)
4668 threads nil
4669 sub nil))
4670 (setq sub (cdr sub))))
4671 ;; It's an ordinary thread, so we check it.
4672 (when (eq (car sub) (car headers))
4673 (setq thread sub
4674 threads nil)))
4675 (setq threads (cdr threads)))
4676 ;; If this article is in no thread, then it's a root.
4677 (if thread
4678 (unless dont-remove
4679 (setq gnus-newsgroup-threads (delq thread gnus-newsgroup-threads)))
6748645f 4680 (setq thread (gnus-id-to-thread last-id)))
eec82323
LMI
4681 (when thread
4682 (prog1
4683 thread ; We return this thread.
4684 (unless dont-remove
4685 (if (stringp (car thread))
4686 (progn
4687 ;; If we use dummy roots, then we have to remove the
4688 ;; dummy root as well.
4689 (when (eq gnus-summary-make-false-root 'dummy)
6748645f
LMI
4690 ;; We go to the dummy root by going to
4691 ;; the first sub-"thread", and then one line up.
4692 (gnus-summary-goto-article
4693 (mail-header-number (caadr thread)))
4694 (forward-line -1)
eec82323
LMI
4695 (gnus-delete-line)
4696 (gnus-data-compute-positions))
4697 (setq thread (cdr thread))
4698 (while thread
4699 (gnus-remove-thread-1 (car thread))
4700 (setq thread (cdr thread))))
4701 (gnus-remove-thread-1 thread))))))))
4702
4703(defun gnus-remove-thread-1 (thread)
4704 "Remove the thread THREAD recursively."
4705 (let ((number (mail-header-number (pop thread)))
4706 d)
4707 (setq thread (reverse thread))
4708 (while thread
4709 (gnus-remove-thread-1 (pop thread)))
4710 (when (setq d (gnus-data-find number))
4711 (goto-char (gnus-data-pos d))
16409b0b 4712 (gnus-summary-show-thread)
eec82323
LMI
4713 (gnus-data-remove
4714 number
01c52d31 4715 (- (point-at-bol)
eec82323 4716 (prog1
01c52d31 4717 (1+ (point-at-eol))
eec82323
LMI
4718 (gnus-delete-line)))))))
4719
4921bbdd 4720(defun gnus-sort-threads-recursive (threads func)
16409b0b
GM
4721 (sort (mapcar (lambda (thread)
4722 (cons (car thread)
4723 (and (cdr thread)
4921bbdd 4724 (gnus-sort-threads-recursive (cdr thread) func))))
16409b0b
GM
4725 threads) func))
4726
4921bbdd
CY
4727(defun gnus-sort-threads-loop (threads func)
4728 (let* ((superthread (cons nil threads))
4729 (stack (list (cons superthread threads)))
4730 remaining-threads thread)
4731 (while stack
4732 (setq remaining-threads (cdr (car stack)))
4733 (if remaining-threads
4734 (progn (setq thread (car remaining-threads))
4735 (setcdr (car stack) (cdr remaining-threads))
4736 (if (cdr thread)
4737 (push (cons thread (cdr thread)) stack)))
4738 (setq thread (caar stack))
4739 (setcdr thread (sort (cdr thread) func))
4740 (pop stack)))
4741 (cdr superthread)))
4742
eec82323
LMI
4743(defun gnus-sort-threads (threads)
4744 "Sort THREADS."
4745 (if (not gnus-thread-sort-functions)
4746 threads
6748645f 4747 (gnus-message 8 "Sorting threads...")
4921bbdd
CY
4748 (prog1
4749 (condition-case nil
4750 (let ((max-lisp-eval-depth (max max-lisp-eval-depth 5000)))
4751 (gnus-sort-threads-recursive
4752 threads (gnus-make-sort-function gnus-thread-sort-functions)))
4753 ;; Even after binding max-lisp-eval-depth, the recursive
4754 ;; sorter might fail for very long threads. In that case,
4755 ;; try using a (less well-tested) non-recursive sorter.
4756 (error (gnus-sort-threads-loop
4757 threads (gnus-make-sort-function
4758 gnus-thread-sort-functions))))
4759 (gnus-message 8 "Sorting threads...done"))))
eec82323
LMI
4760
4761(defun gnus-sort-articles (articles)
4762 "Sort ARTICLES."
4763 (when gnus-article-sort-functions
4764 (gnus-message 7 "Sorting articles...")
4765 (prog1
4766 (setq gnus-newsgroup-headers
4767 (sort articles (gnus-make-sort-function
4768 gnus-article-sort-functions)))
4769 (gnus-message 7 "Sorting articles...done"))))
4770
4771;; Written by Hallvard B Furuseth <h.b.furuseth@usit.uio.no>.
4772(defmacro gnus-thread-header (thread)
16409b0b
GM
4773 "Return header of first article in THREAD.
4774Note that THREAD must never, ever be anything else than a variable -
4775using some other form will lead to serious barfage."
eec82323
LMI
4776 (or (symbolp thread) (signal 'wrong-type-argument '(symbolp thread)))
4777 ;; (8% speedup to gnus-summary-prepare, just for fun :-)
16409b0b 4778 (list 'byte-code "\10\211:\203\17\0\211@;\203\16\0A@@\207"
eec82323
LMI
4779 (vector thread) 2))
4780
4781(defsubst gnus-article-sort-by-number (h1 h2)
4782 "Sort articles by article number."
4783 (< (mail-header-number h1)
4784 (mail-header-number h2)))
4785
4786(defun gnus-thread-sort-by-number (h1 h2)
4787 "Sort threads by root article number."
4788 (gnus-article-sort-by-number
4789 (gnus-thread-header h1) (gnus-thread-header h2)))
4790
23f87bed
MB
4791(defsubst gnus-article-sort-by-random (h1 h2)
4792 "Sort articles by article number."
4793 (zerop (random 2)))
4794
4795(defun gnus-thread-sort-by-random (h1 h2)
4796 "Sort threads by root article number."
4797 (gnus-article-sort-by-random
4798 (gnus-thread-header h1) (gnus-thread-header h2)))
4799
eec82323
LMI
4800(defsubst gnus-article-sort-by-lines (h1 h2)
4801 "Sort articles by article Lines header."
4802 (< (mail-header-lines h1)
4803 (mail-header-lines h2)))
4804
4805(defun gnus-thread-sort-by-lines (h1 h2)
4806 "Sort threads by root article Lines header."
4807 (gnus-article-sort-by-lines
4808 (gnus-thread-header h1) (gnus-thread-header h2)))
4809
16409b0b
GM
4810(defsubst gnus-article-sort-by-chars (h1 h2)
4811 "Sort articles by octet length."
4812 (< (mail-header-chars h1)
4813 (mail-header-chars h2)))
4814
4815(defun gnus-thread-sort-by-chars (h1 h2)
4816 "Sort threads by root article octet length."
4817 (gnus-article-sort-by-chars
4818 (gnus-thread-header h1) (gnus-thread-header h2)))
4819
eec82323
LMI
4820(defsubst gnus-article-sort-by-author (h1 h2)
4821 "Sort articles by root author."
b4fde39f 4822 (gnus-string<
eec82323
LMI
4823 (let ((extract (funcall
4824 gnus-extract-address-components
4825 (mail-header-from h1))))
4826 (or (car extract) (cadr extract) ""))
4827 (let ((extract (funcall
4828 gnus-extract-address-components
4829 (mail-header-from h2))))
4830 (or (car extract) (cadr extract) ""))))
4831
4832(defun gnus-thread-sort-by-author (h1 h2)
4833 "Sort threads by root author."
4834 (gnus-article-sort-by-author
4835 (gnus-thread-header h1) (gnus-thread-header h2)))
4836
01c52d31
MB
4837(defsubst gnus-article-sort-by-recipient (h1 h2)
4838 "Sort articles by recipient."
4839 (gnus-string<
4840 (let ((extract (funcall
4841 gnus-extract-address-components
4842 (or (cdr (assq 'To (mail-header-extra h1))) ""))))
4843 (or (car extract) (cadr extract)))
4844 (let ((extract (funcall
4845 gnus-extract-address-components
4846 (or (cdr (assq 'To (mail-header-extra h2))) ""))))
4847 (or (car extract) (cadr extract)))))
4848
4849(defun gnus-thread-sort-by-recipient (h1 h2)
4850 "Sort threads by root recipient."
4851 (gnus-article-sort-by-recipient
4852 (gnus-thread-header h1) (gnus-thread-header h2)))
4853
eec82323
LMI
4854(defsubst gnus-article-sort-by-subject (h1 h2)
4855 "Sort articles by root subject."
b4fde39f 4856 (gnus-string<
eec82323
LMI
4857 (downcase (gnus-simplify-subject-re (mail-header-subject h1)))
4858 (downcase (gnus-simplify-subject-re (mail-header-subject h2)))))
4859
4860(defun gnus-thread-sort-by-subject (h1 h2)
4861 "Sort threads by root subject."
4862 (gnus-article-sort-by-subject
4863 (gnus-thread-header h1) (gnus-thread-header h2)))
4864
4865(defsubst gnus-article-sort-by-date (h1 h2)
4866 "Sort articles by root article date."
16409b0b 4867 (time-less-p
eec82323
LMI
4868 (gnus-date-get-time (mail-header-date h1))
4869 (gnus-date-get-time (mail-header-date h2))))
4870
4871(defun gnus-thread-sort-by-date (h1 h2)
4872 "Sort threads by root article date."
4873 (gnus-article-sort-by-date
4874 (gnus-thread-header h1) (gnus-thread-header h2)))
4875
4876(defsubst gnus-article-sort-by-score (h1 h2)
4877 "Sort articles by root article score.
4878Unscored articles will be counted as having a score of zero."
4879 (> (or (cdr (assq (mail-header-number h1)
4880 gnus-newsgroup-scored))
4881 gnus-summary-default-score 0)
4882 (or (cdr (assq (mail-header-number h2)
4883 gnus-newsgroup-scored))
4884 gnus-summary-default-score 0)))
4885
4886(defun gnus-thread-sort-by-score (h1 h2)
4887 "Sort threads by root article score."
4888 (gnus-article-sort-by-score
4889 (gnus-thread-header h1) (gnus-thread-header h2)))
4890
4891(defun gnus-thread-sort-by-total-score (h1 h2)
4892 "Sort threads by the sum of all scores in the thread.
4893Unscored articles will be counted as having a score of zero."
4894 (> (gnus-thread-total-score h1) (gnus-thread-total-score h2)))
4895
4896(defun gnus-thread-total-score (thread)
16409b0b 4897 ;; This function find the total score of THREAD.
23f87bed
MB
4898 (cond
4899 ((null thread)
4900 0)
4901 ((consp thread)
4902 (if (stringp (car thread))
4903 (apply gnus-thread-score-function 0
4904 (mapcar 'gnus-thread-total-score-1 (cdr thread)))
4905 (gnus-thread-total-score-1 thread)))
4906 (t
4907 (gnus-thread-total-score-1 (list thread)))))
4908
4909(defun gnus-thread-sort-by-most-recent-number (h1 h2)
4910 "Sort threads such that the thread with the most recently arrived article comes first."
4911 (> (gnus-thread-highest-number h1) (gnus-thread-highest-number h2)))
4912
4913(defun gnus-thread-highest-number (thread)
4914 "Return the highest article number in THREAD."
4915 (apply 'max (mapcar (lambda (header)
4916 (mail-header-number header))
4917 (message-flatten-list thread))))
4918
4919(defun gnus-thread-sort-by-most-recent-date (h1 h2)
4920 "Sort threads such that the thread with the most recently dated article comes first."
4921 (> (gnus-thread-latest-date h1) (gnus-thread-latest-date h2)))
4922
4923(defun gnus-thread-latest-date (thread)
4924 "Return the highest article date in THREAD."
4925 (let ((previous-time 0))
4926 (apply 'max
4927 (mapcar
4928 (lambda (header)
4929 (setq previous-time
4930 (condition-case ()
4931 (time-to-seconds (mail-header-parse-date
4932 (mail-header-date header)))
4933 (error previous-time))))
4934 (sort
4935 (message-flatten-list thread)
4936 (lambda (h1 h2)
4937 (< (mail-header-number h1)
4938 (mail-header-number h2))))))))
eec82323
LMI
4939
4940(defun gnus-thread-total-score-1 (root)
4941 ;; This function find the total score of the thread below ROOT.
4942 (setq root (car root))
4943 (apply gnus-thread-score-function
4944 (or (append
4945 (mapcar 'gnus-thread-total-score
6748645f 4946 (cdr (gnus-id-to-thread (mail-header-id root))))
eec82323
LMI
4947 (when (> (mail-header-number root) 0)
4948 (list (or (cdr (assq (mail-header-number root)
4949 gnus-newsgroup-scored))
4950 gnus-summary-default-score 0))))
4951 (list gnus-summary-default-score)
4952 '(0))))
4953
4954;; Added by Per Abrahamsen <amanda@iesd.auc.dk>.
4955(defvar gnus-tmp-prev-subject nil)
4956(defvar gnus-tmp-false-parent nil)
4957(defvar gnus-tmp-root-expunged nil)
4958(defvar gnus-tmp-dummy-line nil)
4959
16409b0b
GM
4960(defun gnus-extra-header (type &optional header)
4961 "Return the extra header of TYPE."
4962 (or (cdr (assq type (mail-header-extra (or header gnus-tmp-header))))
4963 ""))
4964
23f87bed
MB
4965(defvar gnus-tmp-thread-tree-header-string "")
4966
4967(defcustom gnus-sum-thread-tree-root "> "
4968 "With %B spec, used for the root of a thread.
4969If nil, use subject instead."
bf247b6e 4970 :version "22.1"
ad136a7c 4971 :type '(radio (const :format "%v " nil) string)
23f87bed 4972 :group 'gnus-thread)
01c52d31 4973
23f87bed
MB
4974(defcustom gnus-sum-thread-tree-false-root "> "
4975 "With %B spec, used for a false root of a thread.
4976If nil, use subject instead."
bf247b6e 4977 :version "22.1"
ad136a7c 4978 :type '(radio (const :format "%v " nil) string)
23f87bed 4979 :group 'gnus-thread)
01c52d31 4980
23f87bed
MB
4981(defcustom gnus-sum-thread-tree-single-indent ""
4982 "With %B spec, used for a thread with just one message.
4983If nil, use subject instead."
bf247b6e 4984 :version "22.1"
ad136a7c 4985 :type '(radio (const :format "%v " nil) string)
23f87bed 4986 :group 'gnus-thread)
01c52d31 4987
23f87bed
MB
4988(defcustom gnus-sum-thread-tree-vertical "| "
4989 "With %B spec, used for drawing a vertical line."
bf247b6e 4990 :version "22.1"
23f87bed
MB
4991 :type 'string
4992 :group 'gnus-thread)
01c52d31 4993
23f87bed
MB
4994(defcustom gnus-sum-thread-tree-indent " "
4995 "With %B spec, used for indenting."
bf247b6e 4996 :version "22.1"
23f87bed
MB
4997 :type 'string
4998 :group 'gnus-thread)
01c52d31 4999
23f87bed
MB
5000(defcustom gnus-sum-thread-tree-leaf-with-other "+-> "
5001 "With %B spec, used for a leaf with brothers."
bf247b6e 5002 :version "22.1"
23f87bed
MB
5003 :type 'string
5004 :group 'gnus-thread)
01c52d31 5005
23f87bed
MB
5006(defcustom gnus-sum-thread-tree-single-leaf "\\-> "
5007 "With %B spec, used for a leaf without brothers."
bf247b6e 5008 :version "22.1"
23f87bed
MB
5009 :type 'string
5010 :group 'gnus-thread)
5011
eec82323
LMI
5012(defun gnus-summary-prepare-threads (threads)
5013 "Prepare summary buffer from THREADS and indentation LEVEL.
5014THREADS is either a list of `(PARENT [(CHILD1 [(GRANDCHILD ...]...) ...])'
5015or a straight list of headers."
5016 (gnus-message 7 "Generating summary...")
5017
5018 (setq gnus-newsgroup-threads threads)
5019 (beginning-of-line)
5020
5021 (let ((gnus-tmp-level 0)
5022 (default-score (or gnus-summary-default-score 0))
5023 (gnus-visual-p (gnus-visual-p 'summary-highlight 'highlight))
23f87bed
MB
5024 (building-line-count gnus-summary-display-while-building)
5025 (building-count (integerp gnus-summary-display-while-building))
eec82323 5026 thread number subject stack state gnus-tmp-gathered beg-match
23f87bed
MB
5027 new-roots gnus-tmp-new-adopts thread-end simp-subject
5028 gnus-tmp-header gnus-tmp-unread gnus-tmp-downloaded
eec82323
LMI
5029 gnus-tmp-replied gnus-tmp-subject-or-nil
5030 gnus-tmp-dummy gnus-tmp-indentation gnus-tmp-lines gnus-tmp-score
5031 gnus-tmp-score-char gnus-tmp-from gnus-tmp-name
23f87bed
MB
5032 gnus-tmp-number gnus-tmp-opening-bracket gnus-tmp-closing-bracket
5033 tree-stack)
eec82323 5034
23f87bed
MB
5035 (setq gnus-tmp-prev-subject nil
5036 gnus-tmp-thread-tree-header-string "")
eec82323
LMI
5037
5038 (if (vectorp (car threads))
5039 ;; If this is a straight (sic) list of headers, then a
5040 ;; threaded summary display isn't required, so we just create
5041 ;; an unthreaded one.
5042 (gnus-summary-prepare-unthreaded threads)
5043
5044 ;; Do the threaded display.
5045
23f87bed
MB
5046 (if gnus-summary-display-while-building
5047 (switch-to-buffer (buffer-name)))
eec82323
LMI
5048 (while (or threads stack gnus-tmp-new-adopts new-roots)
5049
5050 (if (and (= gnus-tmp-level 0)
eec82323
LMI
5051 (or (not stack)
5052 (= (caar stack) 0))
5053 (not gnus-tmp-false-parent)
5054 (or gnus-tmp-new-adopts new-roots))
5055 (if gnus-tmp-new-adopts
5056 (setq gnus-tmp-level (if gnus-tmp-root-expunged 0 1)
5057 thread (list (car gnus-tmp-new-adopts))
5058 gnus-tmp-header (caar thread)
5059 gnus-tmp-new-adopts (cdr gnus-tmp-new-adopts))
5060 (when new-roots
5061 (setq thread (list (car new-roots))
5062 gnus-tmp-header (caar thread)
5063 new-roots (cdr new-roots))))
5064
5065 (if threads
5066 ;; If there are some threads, we do them before the
5067 ;; threads on the stack.
5068 (setq thread threads
5069 gnus-tmp-header (caar thread))
5070 ;; There were no current threads, so we pop something off
5071 ;; the stack.
5072 (setq state (car stack)
5073 gnus-tmp-level (car state)
23f87bed
MB
5074 tree-stack (cadr state)
5075 thread (caddr state)
eec82323
LMI
5076 stack (cdr stack)
5077 gnus-tmp-header (caar thread))))
5078
5079 (setq gnus-tmp-false-parent nil)
5080 (setq gnus-tmp-root-expunged nil)
5081 (setq thread-end nil)
5082
5083 (if (stringp gnus-tmp-header)
5084 ;; The header is a dummy root.
5085 (cond
5086 ((eq gnus-summary-make-false-root 'adopt)
5087 ;; We let the first article adopt the rest.
5088 (setq gnus-tmp-new-adopts (nconc gnus-tmp-new-adopts
5089 (cddar thread)))
5090 (setq gnus-tmp-gathered
5091 (nconc (mapcar
5092 (lambda (h) (mail-header-number (car h)))
5093 (cddar thread))
5094 gnus-tmp-gathered))
5095 (setq thread (cons (list (caar thread)
5096 (cadar thread))
5097 (cdr thread)))
5098 (setq gnus-tmp-level -1
5099 gnus-tmp-false-parent t))
5100 ((eq gnus-summary-make-false-root 'empty)
5101 ;; We print adopted articles with empty subject fields.
5102 (setq gnus-tmp-gathered
5103 (nconc (mapcar
5104 (lambda (h) (mail-header-number (car h)))
5105 (cddar thread))
5106 gnus-tmp-gathered))
5107 (setq gnus-tmp-level -1))
5108 ((eq gnus-summary-make-false-root 'dummy)
5109 ;; We remember that we probably want to output a dummy
5110 ;; root.
5111 (setq gnus-tmp-dummy-line gnus-tmp-header)
5112 (setq gnus-tmp-prev-subject gnus-tmp-header))
5113 (t
5114 ;; We do not make a root for the gathered
5115 ;; sub-threads at all.
5116 (setq gnus-tmp-level -1)))
5117
5118 (setq number (mail-header-number gnus-tmp-header)
23f87bed
MB
5119 subject (mail-header-subject gnus-tmp-header)
5120 simp-subject (gnus-simplify-subject-fully subject))
eec82323
LMI
5121
5122 (cond
5123 ;; If the thread has changed subject, we might want to make
5124 ;; this subthread into a root.
5125 ((and (null gnus-thread-ignore-subject)
5126 (not (zerop gnus-tmp-level))
5127 gnus-tmp-prev-subject
23f87bed 5128 (not (string= gnus-tmp-prev-subject simp-subject)))
eec82323
LMI
5129 (setq new-roots (nconc new-roots (list (car thread)))
5130 thread-end t
5131 gnus-tmp-header nil))
5132 ;; If the article lies outside the current limit,
5133 ;; then we do not display it.
5134 ((not (memq number gnus-newsgroup-limit))
5135 (setq gnus-tmp-gathered
5136 (nconc (mapcar
5137 (lambda (h) (mail-header-number (car h)))
5138 (cdar thread))
5139 gnus-tmp-gathered))
5140 (setq gnus-tmp-new-adopts (if (cdar thread)
5141 (append gnus-tmp-new-adopts
5142 (cdar thread))
5143 gnus-tmp-new-adopts)
5144 thread-end t
5145 gnus-tmp-header nil)
5146 (when (zerop gnus-tmp-level)
5147 (setq gnus-tmp-root-expunged t)))
5148 ;; Perhaps this article is to be marked as read?
5149 ((and gnus-summary-mark-below
5150 (< (or (cdr (assq number gnus-newsgroup-scored))
5151 default-score)
5152 gnus-summary-mark-below)
5153 ;; Don't touch sparse articles.
5154 (not (gnus-summary-article-sparse-p number))
5155 (not (gnus-summary-article-ancient-p number)))
5156 (setq gnus-newsgroup-unreads
5157 (delq number gnus-newsgroup-unreads))
5158 (if gnus-newsgroup-auto-expire
23f87bed
MB
5159 (setq gnus-newsgroup-expirable
5160 (gnus-add-to-sorted-list
5161 gnus-newsgroup-expirable number))
eec82323
LMI
5162 (push (cons number gnus-low-score-mark)
5163 gnus-newsgroup-reads))))
5164
5165 (when gnus-tmp-header
5166 ;; We may have an old dummy line to output before this
5167 ;; article.
6748645f
LMI
5168 (when (and gnus-tmp-dummy-line
5169 (gnus-subject-equal
5170 gnus-tmp-dummy-line
5171 (mail-header-subject gnus-tmp-header)))
eec82323
LMI
5172 (gnus-summary-insert-dummy-line
5173 gnus-tmp-dummy-line (mail-header-number gnus-tmp-header))
5174 (setq gnus-tmp-dummy-line nil))
5175
5176 ;; Compute the mark.
5177 (setq gnus-tmp-unread (gnus-article-mark number))
5178
5179 (push (gnus-data-make number gnus-tmp-unread (1+ (point))
5180 gnus-tmp-header gnus-tmp-level)
5181 gnus-newsgroup-data)
5182
5183 ;; Actually insert the line.
5184 (setq
5185 gnus-tmp-subject-or-nil
5186 (cond
5187 ((and gnus-thread-ignore-subject
5188 gnus-tmp-prev-subject
23f87bed 5189 (not (string= gnus-tmp-prev-subject simp-subject)))
eec82323
LMI
5190 subject)
5191 ((zerop gnus-tmp-level)
5192 (if (and (eq gnus-summary-make-false-root 'empty)
5193 (memq number gnus-tmp-gathered)
5194 gnus-tmp-prev-subject
23f87bed 5195 (string= gnus-tmp-prev-subject simp-subject))
eec82323
LMI
5196 gnus-summary-same-subject
5197 subject))
5198 (t gnus-summary-same-subject)))
5199 (if (and (eq gnus-summary-make-false-root 'adopt)
5200 (= gnus-tmp-level 1)
5201 (memq number gnus-tmp-gathered))
5202 (setq gnus-tmp-opening-bracket ?\<
5203 gnus-tmp-closing-bracket ?\>)
5204 (setq gnus-tmp-opening-bracket ?\[
5205 gnus-tmp-closing-bracket ?\]))
4921bbdd
CY
5206 (if (>= gnus-tmp-level (length gnus-thread-indent-array))
5207 (gnus-make-thread-indent-array
5208 (max (* 2 (length gnus-thread-indent-array))
5209 gnus-tmp-level)))
eec82323
LMI
5210 (setq
5211 gnus-tmp-indentation
5212 (aref gnus-thread-indent-array gnus-tmp-level)
5213 gnus-tmp-lines (mail-header-lines gnus-tmp-header)
5214 gnus-tmp-score (or (cdr (assq number gnus-newsgroup-scored))
5215 gnus-summary-default-score 0)
5216 gnus-tmp-score-char
5217 (if (or (null gnus-summary-default-score)
5218 (<= (abs (- gnus-tmp-score gnus-summary-default-score))
5219 gnus-summary-zcore-fuzz))
23f87bed 5220 ? ;Whitespace
eec82323
LMI
5221 (if (< gnus-tmp-score gnus-summary-default-score)
5222 gnus-score-below-mark gnus-score-over-mark))
5223 gnus-tmp-replied
5224 (cond ((memq number gnus-newsgroup-processable)
5225 gnus-process-mark)
5226 ((memq number gnus-newsgroup-cached)
5227 gnus-cached-mark)
5228 ((memq number gnus-newsgroup-replied)
5229 gnus-replied-mark)
23f87bed
MB
5230 ((memq number gnus-newsgroup-forwarded)
5231 gnus-forwarded-mark)
eec82323
LMI
5232 ((memq number gnus-newsgroup-saved)
5233 gnus-saved-mark)
23f87bed
MB
5234 ((memq number gnus-newsgroup-recent)
5235 gnus-recent-mark)
5236 ((memq number gnus-newsgroup-unseen)
5237 gnus-unseen-mark)
5238 (t gnus-no-mark))
5239 gnus-tmp-downloaded
5240 (cond ((memq number gnus-newsgroup-undownloaded)
5241 gnus-undownloaded-mark)
5242 (gnus-newsgroup-agentized
5243 gnus-downloaded-mark)
5244 (t
5245 gnus-no-mark))
eec82323
LMI
5246 gnus-tmp-from (mail-header-from gnus-tmp-header)
5247 gnus-tmp-name
5248 (cond
5249 ((string-match "<[^>]+> *$" gnus-tmp-from)
5250 (setq beg-match (match-beginning 0))
23f87bed
MB
5251 (or (and (string-match "^\".+\"" gnus-tmp-from)
5252 (substring gnus-tmp-from 1 (1- (match-end 0))))
eec82323
LMI
5253 (substring gnus-tmp-from 0 beg-match)))
5254 ((string-match "(.+)" gnus-tmp-from)
5255 (substring gnus-tmp-from
5256 (1+ (match-beginning 0)) (1- (match-end 0))))
23f87bed
MB
5257 (t gnus-tmp-from))
5258
5259 ;; Do the %B string
5260 gnus-tmp-thread-tree-header-string
5261 (cond
5262 ((not gnus-show-threads) "")
5263 ((zerop gnus-tmp-level)
5264 (cond ((cdar thread)
5265 (or gnus-sum-thread-tree-root subject))
5266 (gnus-tmp-new-adopts
5267 (or gnus-sum-thread-tree-false-root subject))
5268 (t
5269 (or gnus-sum-thread-tree-single-indent subject))))
5270 (t
5271 (concat (apply 'concat
5272 (mapcar (lambda (item)
5273 (if (= item 1)
5274 gnus-sum-thread-tree-vertical
5275 gnus-sum-thread-tree-indent))
5276 (cdr (reverse tree-stack))))
5277 (if (nth 1 thread)
5278 gnus-sum-thread-tree-leaf-with-other
5279 gnus-sum-thread-tree-single-leaf)))))
eec82323
LMI
5280 (when (string= gnus-tmp-name "")
5281 (setq gnus-tmp-name gnus-tmp-from))
5282 (unless (numberp gnus-tmp-lines)
23f87bed
MB
5283 (setq gnus-tmp-lines -1))
5284 (if (= gnus-tmp-lines -1)
5285 (setq gnus-tmp-lines "?")
5286 (setq gnus-tmp-lines (number-to-string gnus-tmp-lines)))
5287 (gnus-put-text-property
eec82323
LMI
5288 (point)
5289 (progn (eval gnus-summary-line-format-spec) (point))
23f87bed 5290 'gnus-number number)
eec82323
LMI
5291 (when gnus-visual-p
5292 (forward-line -1)
6748645f 5293 (gnus-run-hooks 'gnus-summary-update-hook)
eec82323
LMI
5294 (forward-line 1))
5295
23f87bed 5296 (setq gnus-tmp-prev-subject simp-subject)))
eec82323
LMI
5297
5298 (when (nth 1 thread)
23f87bed
MB
5299 (push (list (max 0 gnus-tmp-level)
5300 (copy-sequence tree-stack)
5301 (nthcdr 1 thread))
5302 stack))
5303 (push (if (nth 1 thread) 1 0) tree-stack)
eec82323
LMI
5304 (incf gnus-tmp-level)
5305 (setq threads (if thread-end nil (cdar thread)))
23f87bed
MB
5306 (if gnus-summary-display-while-building
5307 (if building-count
5308 (progn
5309 ;; use a set frequency
5310 (setq building-line-count (1- building-line-count))
5311 (when (= building-line-count 0)
5312 (sit-for 0)
5313 (setq building-line-count
5314 gnus-summary-display-while-building)))
5315 ;; always
5316 (sit-for 0)))
eec82323
LMI
5317 (unless threads
5318 (setq gnus-tmp-level 0)))))
5319 (gnus-message 7 "Generating summary...done"))
5320
5321(defun gnus-summary-prepare-unthreaded (headers)
5322 "Generate an unthreaded summary buffer based on HEADERS."
5323 (let (header number mark)
5324
5325 (beginning-of-line)
5326
5327 (while headers
5328 ;; We may have to root out some bad articles...
5329 (when (memq (setq number (mail-header-number
5330 (setq header (pop headers))))
5331 gnus-newsgroup-limit)
5332 ;; Mark article as read when it has a low score.
5333 (when (and gnus-summary-mark-below
5334 (< (or (cdr (assq number gnus-newsgroup-scored))
5335 gnus-summary-default-score 0)
5336 gnus-summary-mark-below)
5337 (not (gnus-summary-article-ancient-p number)))
5338 (setq gnus-newsgroup-unreads
5339 (delq number gnus-newsgroup-unreads))
5340 (if gnus-newsgroup-auto-expire
5341 (push number gnus-newsgroup-expirable)
5342 (push (cons number gnus-low-score-mark)
5343 gnus-newsgroup-reads)))
5344
5345 (setq mark (gnus-article-mark number))
5346 (push (gnus-data-make number mark (1+ (point)) header 0)
5347 gnus-newsgroup-data)
5348 (gnus-summary-insert-line
5349 header 0 number
23f87bed 5350 (memq number gnus-newsgroup-undownloaded)
eec82323
LMI
5351 mark (memq number gnus-newsgroup-replied)
5352 (memq number gnus-newsgroup-expirable)
5353 (mail-header-subject header) nil
5354 (cdr (assq number gnus-newsgroup-scored))
5355 (memq number gnus-newsgroup-processable))))))
5356
16409b0b
GM
5357(defun gnus-summary-remove-list-identifiers ()
5358 "Remove list identifiers in `gnus-list-identifiers' from articles in the current group."
23f87bed
MB
5359 (let ((regexp (if (consp gnus-list-identifiers)
5360 (mapconcat 'identity gnus-list-identifiers " *\\|")
5361 gnus-list-identifiers))
5362 changed subject)
5363 (when regexp
01c52d31 5364 (setq regexp (concat "^\\(?:R[Ee]: +\\)*\\(" regexp " *\\)"))
23f87bed
MB
5365 (dolist (header gnus-newsgroup-headers)
5366 (setq subject (mail-header-subject header)
5367 changed nil)
01c52d31 5368 (while (string-match regexp subject)
23f87bed 5369 (setq subject
01c52d31 5370 (concat (substring subject 0 (match-beginning 1))
23f87bed
MB
5371 (substring subject (match-end 0)))
5372 changed t))
23f87bed 5373 (when changed
01c52d31
MB
5374 (when (string-match "^\\(\\(?:R[Ee]: +\\)+\\)R[Ee]: +" subject)
5375 (setq subject
5376 (concat (substring subject 0 (match-beginning 1))
5377 (substring subject (match-end 1)))))
23f87bed
MB
5378 (mail-header-set-subject header subject))))))
5379
5380(defun gnus-fetch-headers (articles)
5381 "Fetch headers of ARTICLES."
5382 (let ((name (gnus-group-decoded-name gnus-newsgroup-name)))
5383 (gnus-message 5 "Fetching headers for %s..." name)
5384 (prog1
5385 (if (eq 'nov
5386 (setq gnus-headers-retrieved-by
5387 (gnus-retrieve-headers
5388 articles gnus-newsgroup-name
5389 ;; We might want to fetch old headers, but
5390 ;; not if there is only 1 article.
5391 (and (or (and
5392 (not (eq gnus-fetch-old-headers 'some))
5393 (not (numberp gnus-fetch-old-headers)))
5394 (> (length articles) 1))
5395 gnus-fetch-old-headers))))
5396 (gnus-get-newsgroup-headers-xover
5397 articles nil nil gnus-newsgroup-name t)
5398 (gnus-get-newsgroup-headers))
5399 (gnus-message 5 "Fetching headers for %s...done" name))))
16409b0b 5400
6748645f 5401(defun gnus-select-newsgroup (group &optional read-all select-articles)
eec82323 5402 "Select newsgroup GROUP.
6748645f
LMI
5403If READ-ALL is non-nil, all articles in the group are selected.
5404If SELECT-ARTICLES, only select those articles from GROUP."
01c52d31 5405 (let* ((entry (gnus-group-entry group))
eec82323
LMI
5406 ;;!!! Dirty hack; should be removed.
5407 (gnus-summary-ignore-duplicates
23f87bed 5408 (if (eq (car (gnus-find-method-for-group group)) 'nnvirtual)
eec82323
LMI
5409 t
5410 gnus-summary-ignore-duplicates))
5411 (info (nth 2 entry))
01c52d31 5412 charset articles fetched-articles cached)
eec82323
LMI
5413
5414 (unless (gnus-check-server
475e0e0c
GM
5415 (set (make-local-variable 'gnus-current-select-method)
5416 (gnus-find-method-for-group group)))
eec82323 5417 (error "Couldn't open server"))
01c52d31 5418 (setq charset (gnus-group-name-charset gnus-current-select-method group))
eec82323
LMI
5419
5420 (or (and entry (not (eq (car entry) t))) ; Either it's active...
5421 (gnus-activate-group group) ; Or we can activate it...
5422 (progn ; Or we bug out.
5423 (when (equal major-mode 'gnus-summary-mode)
23f87bed 5424 (gnus-kill-buffer (current-buffer)))
01c52d31
MB
5425 (error
5426 "Couldn't activate group %s: %s"
5427 (mm-decode-coding-string group charset)
5428 (mm-decode-coding-string (gnus-status-message group) charset))))
eec82323
LMI
5429
5430 (unless (gnus-request-group group t)
01c52d31
MB
5431 (when (equal major-mode 'gnus-summary-mode)
5432 (gnus-kill-buffer (current-buffer)))
5433 (error "Couldn't request group %s: %s"
5434 (mm-decode-coding-string group charset)
5435 (mm-decode-coding-string (gnus-status-message group) charset)))
eec82323 5436
23f87bed 5437 (when gnus-agent
54506618 5438 (gnus-agent-possibly-alter-active group (gnus-active group) info)
132cf96d 5439
23f87bed
MB
5440 (setq gnus-summary-use-undownloaded-faces
5441 (gnus-agent-find-parameter
5442 group
5443 'agent-enable-undownloaded-faces)))
5444
5445 (setq gnus-newsgroup-name group
5446 gnus-newsgroup-unselected nil
5447 gnus-newsgroup-unreads (gnus-list-of-unread-articles group))
5448
5449 (let ((display (gnus-group-find-parameter group 'display)))
5450 (setq gnus-newsgroup-display
5451 (cond
5452 ((not (zerop (or (car-safe read-all) 0)))
5453 ;; The user entered the group with C-u SPC/RET, let's show
5454 ;; all articles.
5455 'gnus-not-ignore)
5456 ((eq display 'all)
5457 'gnus-not-ignore)
5458 ((arrayp display)
5459 (gnus-summary-display-make-predicate (mapcar 'identity display)))
5460 ((numberp display)
5461 ;; The following is probably the "correct" solution, but
5462 ;; it makes Gnus fetch all headers and then limit the
5463 ;; articles (which is slow), so instead we hack the
5464 ;; select-articles parameter instead. -- Simon Josefsson
5465 ;; <jas@kth.se>
5466 ;;
5467 ;; (gnus-byte-compile
5468 ;; `(lambda () (> number ,(- (cdr (gnus-active group))
5469 ;; display)))))
5470 (setq select-articles
5471 (gnus-uncompress-range
5472 (cons (let ((tmp (- (cdr (gnus-active group)) display)))
5473 (if (> tmp 0)
5474 tmp
5475 1))
5476 (cdr (gnus-active group)))))
5477 nil)
5478 (t
5479 nil))))
eec82323 5480
23f87bed 5481 (gnus-summary-setup-default-charset)
eec82323
LMI
5482
5483 ;; Kludge to avoid having cached articles nixed out in virtual groups.
5484 (when (gnus-virtual-group-p group)
5485 (setq cached gnus-newsgroup-cached))
5486
5487 (setq gnus-newsgroup-unreads
23f87bed
MB
5488 (gnus-sorted-ndifference
5489 (gnus-sorted-ndifference gnus-newsgroup-unreads
5490 gnus-newsgroup-marked)
eec82323
LMI
5491 gnus-newsgroup-dormant))
5492
5493 (setq gnus-newsgroup-processable nil)
5494
5495 (gnus-update-read-articles group gnus-newsgroup-unreads)
eec82323 5496
23f87bed
MB
5497 ;; Adjust and set lists of article marks.
5498 (when info
5499 (gnus-adjust-marked-articles info))
6748645f
LMI
5500 (if (setq articles select-articles)
5501 (setq gnus-newsgroup-unselected
23f87bed 5502 (gnus-sorted-difference gnus-newsgroup-unreads articles))
6748645f 5503 (setq articles (gnus-articles-to-read group read-all)))
eec82323
LMI
5504
5505 (cond
5506 ((null articles)
5507 ;;(gnus-message 3 "Couldn't select newsgroup -- no articles to display")
5508 'quit)
5509 ((eq articles 0) nil)
5510 (t
5511 ;; Init the dependencies hash table.
5512 (setq gnus-newsgroup-dependencies
5513 (gnus-make-hashtable (length articles)))
16409b0b 5514 (gnus-set-global-variables)
eec82323 5515 ;; Retrieve the headers and read them in.
23f87bed
MB
5516
5517 (setq gnus-newsgroup-headers (gnus-fetch-headers articles))
eec82323
LMI
5518
5519 ;; Kludge to avoid having cached articles nixed out in virtual groups.
5520 (when cached
5521 (setq gnus-newsgroup-cached cached))
5522
5523 ;; Suppress duplicates?
5524 (when gnus-suppress-duplicates
5525 (gnus-dup-suppress-articles))
5526
5527 ;; Set the initial limit.
5528 (setq gnus-newsgroup-limit (copy-sequence articles))
5529 ;; Remove canceled articles from the list of unread articles.
23f87bed
MB
5530 (setq fetched-articles
5531 (mapcar (lambda (headers) (mail-header-number headers))
5532 gnus-newsgroup-headers))
5533 (setq gnus-newsgroup-articles fetched-articles)
eec82323 5534 (setq gnus-newsgroup-unreads
23f87bed
MB
5535 (gnus-sorted-nintersection
5536 gnus-newsgroup-unreads fetched-articles))
5537 (gnus-compute-unseen-list)
5538
eec82323
LMI
5539 ;; Removed marked articles that do not exist.
5540 (gnus-update-missing-marks
23f87bed 5541 (gnus-sorted-difference articles fetched-articles))
eec82323 5542 ;; We might want to build some more threads first.
6748645f
LMI
5543 (when (and gnus-fetch-old-headers
5544 (eq gnus-headers-retrieved-by 'nov))
5545 (if (eq gnus-fetch-old-headers 'invisible)
5546 (gnus-build-all-threads)
5547 (gnus-build-old-threads)))
5548 ;; Let the Gnus agent mark articles as read.
5549 (when gnus-agent
5550 (gnus-agent-get-undownloaded-list))
16409b0b
GM
5551 ;; Remove list identifiers from subject
5552 (when gnus-list-identifiers
5553 (gnus-summary-remove-list-identifiers))
eec82323
LMI
5554 ;; Check whether auto-expire is to be done in this group.
5555 (setq gnus-newsgroup-auto-expire
5556 (gnus-group-auto-expirable-p group))
5557 ;; Set up the article buffer now, if necessary.
01c52d31
MB
5558 (unless (and gnus-single-article-buffer
5559 (equal gnus-article-buffer "*Article*"))
eec82323
LMI
5560 (gnus-article-setup-buffer))
5561 ;; First and last article in this newsgroup.
5562 (when gnus-newsgroup-headers
5563 (setq gnus-newsgroup-begin
5564 (mail-header-number (car gnus-newsgroup-headers))
5565 gnus-newsgroup-end
5566 (mail-header-number
5567 (gnus-last-element gnus-newsgroup-headers))))
5568 ;; GROUP is successfully selected.
5569 (or gnus-newsgroup-headers t)))))
5570
23f87bed
MB
5571(defun gnus-compute-unseen-list ()
5572 ;; The `seen' marks are treated specially.
5573 (if (not gnus-newsgroup-seen)
5574 (setq gnus-newsgroup-unseen gnus-newsgroup-articles)
5575 (setq gnus-newsgroup-unseen
5576 (gnus-inverse-list-range-intersection
5577 gnus-newsgroup-articles gnus-newsgroup-seen))))
5578
5579(defun gnus-summary-display-make-predicate (display)
5580 (require 'gnus-agent)
5581 (when (= (length display) 1)
5582 (setq display (car display)))
5583 (unless gnus-summary-display-cache
5584 (dolist (elem (append '((unread . unread)
5585 (read . read)
5586 (unseen . unseen))
5587 gnus-article-mark-lists))
5588 (push (cons (cdr elem)
5589 (gnus-byte-compile
5590 `(lambda () (gnus-article-marked-p ',(cdr elem)))))
5591 gnus-summary-display-cache)))
5592 (let ((gnus-category-predicate-alist gnus-summary-display-cache)
5593 (gnus-category-predicate-cache gnus-summary-display-cache))
5594 (gnus-get-predicate display)))
5595
5596;; Uses the dynamically bound `number' variable.
9efa445f 5597(defvar number)
23f87bed
MB
5598(defun gnus-article-marked-p (type &optional article)
5599 (let ((article (or article number)))
5600 (cond
5601 ((eq type 'tick)
5602 (memq article gnus-newsgroup-marked))
5603 ((eq type 'spam)
5604 (memq article gnus-newsgroup-spam-marked))
5605 ((eq type 'unsend)
5606 (memq article gnus-newsgroup-unsendable))
5607 ((eq type 'undownload)
5608 (memq article gnus-newsgroup-undownloaded))
5609 ((eq type 'download)
5610 (memq article gnus-newsgroup-downloadable))
5611 ((eq type 'unread)
5612 (memq article gnus-newsgroup-unreads))
5613 ((eq type 'read)
5614 (memq article gnus-newsgroup-reads))
5615 ((eq type 'dormant)
5616 (memq article gnus-newsgroup-dormant) )
5617 ((eq type 'expire)
5618 (memq article gnus-newsgroup-expirable))
5619 ((eq type 'reply)
5620 (memq article gnus-newsgroup-replied))
5621 ((eq type 'killed)
5622 (memq article gnus-newsgroup-killed))
5623 ((eq type 'bookmark)
5624 (assq article gnus-newsgroup-bookmarks))
5625 ((eq type 'score)
5626 (assq article gnus-newsgroup-scored))
5627 ((eq type 'save)
5628 (memq article gnus-newsgroup-saved))
5629 ((eq type 'cache)
5630 (memq article gnus-newsgroup-cached))
5631 ((eq type 'forward)
5632 (memq article gnus-newsgroup-forwarded))
5633 ((eq type 'seen)
5634 (not (memq article gnus-newsgroup-unseen)))
5635 ((eq type 'recent)
5636 (memq article gnus-newsgroup-recent))
5637 (t t))))
5638
eec82323 5639(defun gnus-articles-to-read (group &optional read-all)
16409b0b 5640 "Find out what articles the user wants to read."
26c9afc3 5641 (let* ((articles
eec82323
LMI
5642 ;; Select all articles if `read-all' is non-nil, or if there
5643 ;; are no unread articles.
5644 (if (or read-all
5645 (and (zerop (length gnus-newsgroup-marked))
5646 (zerop (length gnus-newsgroup-unreads)))
23f87bed
MB
5647 ;; Fetch all if the predicate is non-nil.
5648 gnus-newsgroup-display)
5649 ;; We want to select the headers for all the articles in
5650 ;; the group, so we select either all the active
5651 ;; articles in the group, or (if that's nil), the
5652 ;; articles in the cache.
16409b0b 5653 (or
4b70e299 5654 (if gnus-newsgroup-maximum-articles
11abff8e
MB
5655 (let ((active (gnus-active group)))
5656 (gnus-uncompress-range
5657 (cons (max (car active)
4b70e299
MB
5658 (- (cdr active)
5659 gnus-newsgroup-maximum-articles
5660 -1))
11abff8e
MB
5661 (cdr active))))
5662 (gnus-uncompress-range (gnus-active group)))
16409b0b 5663 (gnus-cache-articles-in-group group))
23f87bed
MB
5664 ;; Select only the "normal" subset of articles.
5665 (gnus-sorted-nunion
5666 (gnus-sorted-union gnus-newsgroup-dormant gnus-newsgroup-marked)
5667 gnus-newsgroup-unreads)))
eec82323
LMI
5668 (scored-list (gnus-killed-articles gnus-newsgroup-killed articles))
5669 (scored (length scored-list))
5670 (number (length articles))
5671 (marked (+ (length gnus-newsgroup-marked)
5672 (length gnus-newsgroup-dormant)))
5673 (select
5674 (cond
5675 ((numberp read-all)
5676 read-all)
23f87bed
MB
5677 ((numberp gnus-newsgroup-display)
5678 gnus-newsgroup-display)
eec82323
LMI
5679 (t
5680 (condition-case ()
5681 (cond
5682 ((and (or (<= scored marked) (= scored number))
5683 (numberp gnus-large-newsgroup)
5684 (> number gnus-large-newsgroup))
23f87bed
MB
5685 (let* ((cursor-in-echo-area nil)
5686 (initial (gnus-parameter-large-newsgroup-initial
5687 gnus-newsgroup-name))
5688 (input
5689 (read-string
5690 (format
5691 "How many articles from %s (%s %d): "
01c52d31 5692 (gnus-group-decoded-name gnus-newsgroup-name)
23f87bed
MB
5693 (if initial "max" "default")
5694 number)
5695 (if initial
5696 (cons (number-to-string initial)
5697 0)))))
eec82323
LMI
5698 (if (string-match "^[ \t]*$" input) number input)))
5699 ((and (> scored marked) (< scored number)
5700 (> (- scored number) 20))
5701 (let ((input
5702 (read-string
5703 (format "%s %s (%d scored, %d total): "
5704 "How many articles from"
23f87bed
MB
5705 (gnus-group-decoded-name group)
5706 scored number))))
eec82323
LMI
5707 (if (string-match "^[ \t]*$" input)
5708 number input)))
5709 (t number))
d4dfaa19
DL
5710 (quit
5711 (message "Quit getting the articles to read")
5712 nil))))))
eec82323
LMI
5713 (setq select (if (stringp select) (string-to-number select) select))
5714 (if (or (null select) (zerop select))
5715 select
5716 (if (and (not (zerop scored)) (<= (abs select) scored))
5717 (progn
5718 (setq articles (sort scored-list '<))
5719 (setq number (length articles)))
5720 (setq articles (copy-sequence articles)))
5721
5722 (when (< (abs select) number)
5723 (if (< select 0)
5724 ;; Select the N oldest articles.
5725 (setcdr (nthcdr (1- (abs select)) articles) nil)
5726 ;; Select the N most recent articles.
5727 (setq articles (nthcdr (- number select) articles))))
5728 (setq gnus-newsgroup-unselected
23f87bed 5729 (gnus-sorted-difference gnus-newsgroup-unreads articles))
16409b0b 5730 (when gnus-alter-articles-to-read-function
23f87bed 5731 (setq articles
a1506d29 5732 (sort
16409b0b 5733 (funcall gnus-alter-articles-to-read-function
23f87bed 5734 gnus-newsgroup-name articles)
16409b0b 5735 '<)))
eec82323
LMI
5736 articles)))
5737
5738(defun gnus-killed-articles (killed articles)
5739 (let (out)
5740 (while articles
5741 (when (inline (gnus-member-of-range (car articles) killed))
5742 (push (car articles) out))
5743 (setq articles (cdr articles)))
5744 out))
5745
5746(defun gnus-uncompress-marks (marks)
5747 "Uncompress the mark ranges in MARKS."
5748 (let ((uncompressed '(score bookmark))
5749 out)
5750 (while marks
5751 (if (memq (caar marks) uncompressed)
5752 (push (car marks) out)
5753 (push (cons (caar marks) (gnus-uncompress-range (cdar marks))) out))
5754 (setq marks (cdr marks)))
5755 out))
5756
23f87bed
MB
5757(defun gnus-article-mark-to-type (mark)
5758 "Return the type of MARK."
5759 (or (cadr (assq mark gnus-article-special-mark-lists))
5760 'list))
5761
5762(defun gnus-article-unpropagatable-p (mark)
5763 "Return whether MARK should be propagated to back end."
5764 (memq mark gnus-article-unpropagated-mark-lists))
5765
eec82323 5766(defun gnus-adjust-marked-articles (info)
16409b0b 5767 "Set all article lists and remove all marks that are no longer valid."
eec82323
LMI
5768 (let* ((marked-lists (gnus-info-marks info))
5769 (active (gnus-active (gnus-info-group info)))
5770 (min (car active))
5771 (max (cdr active))
5772 (types gnus-article-mark-lists)
54506618
MB
5773 marks var articles article mark mark-type
5774 bgn end)
eec82323 5775
23f87bed
MB
5776 (dolist (marks marked-lists)
5777 (setq mark (car marks)
5778 mark-type (gnus-article-mark-to-type mark)
5779 var (intern (format "gnus-newsgroup-%s" (car (rassq mark types)))))
eec82323 5780
23f87bed
MB
5781 ;; We set the variable according to the type of the marks list,
5782 ;; and then adjust the marks to a subset of the active articles.
eec82323 5783 (cond
54506618 5784 ;; Adjust "simple" lists - compressed yet unsorted
23f87bed 5785 ((eq mark-type 'list)
54506618
MB
5786 ;; Simultaneously uncompress and clip to active range
5787 ;; See gnus-uncompress-range for a description of possible marks
5788 (let (l lh)
5789 (if (not (cadr marks))
5790 (set var nil)
5791 (setq articles (if (numberp (cddr marks))
5792 (list (cdr marks))
5793 (cdr marks))
5794 lh (cons nil nil)
5795 l lh)
5796
5797 (while (setq article (pop articles))
5798 (cond ((consp article)
5799 (setq bgn (max (car article) min)
5800 end (min (cdr article) max))
5801 (while (<= bgn end)
5802 (setq l (setcdr l (cons bgn nil))
5803 bgn (1+ bgn))))
5804 ((and (<= min article)
5805 (>= max article))
5806 (setq l (setcdr l (cons article nil))))))
5807 (set var (cdr lh)))))
eec82323 5808 ;; Adjust assocs.
23f87bed
MB
5809 ((eq mark-type 'tuple)
5810 (set var (setq articles (cdr marks)))
a8151ef7
LMI
5811 (when (not (listp (cdr (symbol-value var))))
5812 (set var (list (symbol-value var))))
5813 (when (not (listp (cdr articles)))
5814 (setq articles (list articles)))
eec82323
LMI
5815 (while articles
5816 (when (or (not (consp (setq article (pop articles))))
5817 (< (car article) min)
5818 (> (car article) max))
23f87bed
MB
5819 (set var (delq article (symbol-value var))))))
5820 ;; Adjust ranges (sloppily).
5821 ((eq mark-type 'range)
5822 (cond
5823 ((eq mark 'seen)
5824 ;; Fix the record for `seen' if it looks like (seen NUM1 . NUM2).
5825 ;; It should be (seen (NUM1 . NUM2)).
5826 (when (numberp (cddr marks))
5827 (setcdr marks (list (cdr marks))))
5828 (setq articles (cdr marks))
5829 (while (and articles
5830 (or (and (consp (car articles))
5831 (> min (cdar articles)))
5832 (and (numberp (car articles))
5833 (> min (car articles)))))
5834 (pop articles))
5835 (set var articles))))))))
eec82323
LMI
5836
5837(defun gnus-update-missing-marks (missing)
6748645f 5838 "Go through the list of MISSING articles and remove them from the mark lists."
eec82323 5839 (when missing
23f87bed 5840 (let (var m)
eec82323 5841 ;; Go through all types.
23f87bed
MB
5842 (dolist (elem gnus-article-mark-lists)
5843 (when (eq (gnus-article-mark-to-type (cdr elem)) 'list)
5844 (setq var (intern (format "gnus-newsgroup-%s" (car elem))))
5845 (when (symbol-value var)
5846 ;; This list has articles. So we delete all missing
5847 ;; articles from it.
5848 (setq m missing)
5849 (while m
5850 (set var (delq (pop m) (symbol-value var))))))))))
eec82323
LMI
5851
5852(defun gnus-update-marks ()
5853 "Enter the various lists of marked articles into the newsgroup info list."
5854 (let ((types gnus-article-mark-lists)
5855 (info (gnus-get-info gnus-newsgroup-name))
16409b0b 5856 type list newmarked symbol delta-marks)
eec82323 5857 (when info
16409b0b 5858 ;; Add all marks lists to the list of marks lists.
eec82323 5859 (while (setq type (pop types))
16409b0b
GM
5860 (setq list (symbol-value
5861 (setq symbol
23f87bed 5862 (intern (format "gnus-newsgroup-%s" (car type))))))
eec82323 5863
16409b0b 5864 (when list
eec82323
LMI
5865 ;; Get rid of the entries of the articles that have the
5866 ;; default score.
5867 (when (and (eq (cdr type) 'score)
5868 gnus-save-score
5869 list)
5870 (let* ((arts list)
5871 (prev (cons nil list))
5872 (all prev))
5873 (while arts
5874 (if (or (not (consp (car arts)))
5875 (= (cdar arts) gnus-summary-default-score))
5876 (setcdr prev (cdr arts))
5877 (setq prev arts))
5878 (setq arts (cdr arts)))
16409b0b
GM
5879 (setq list (cdr all)))))
5880
23f87bed
MB
5881 (when (eq (cdr type) 'seen)
5882 (setq list (gnus-range-add list gnus-newsgroup-unseen)))
5883
5884 (when (eq (gnus-article-mark-to-type (cdr type)) 'list)
16409b0b 5885 (setq list (gnus-compress-sequence (set symbol (sort list '<)) t)))
a1506d29 5886
23f87bed
MB
5887 (when (and (gnus-check-backend-function
5888 'request-set-mark gnus-newsgroup-name)
5889 (not (gnus-article-unpropagatable-p (cdr type))))
5890 (let* ((old (cdr (assq (cdr type) (gnus-info-marks info))))
5891 (del (gnus-remove-from-range (gnus-copy-sequence old) list))
5892 (add (gnus-remove-from-range
5893 (gnus-copy-sequence list) old)))
5894 (when add
5895 (push (list add 'add (list (cdr type))) delta-marks))
5896 (when del
5897 (push (list del 'del (list (cdr type))) delta-marks))))
a1506d29 5898
16409b0b
GM
5899 (when list
5900 (push (cons (cdr type) list) newmarked)))
5901
5902 (when delta-marks
5903 (unless (gnus-check-group gnus-newsgroup-name)
5904 (error "Can't open server for %s" gnus-newsgroup-name))
5905 (gnus-request-set-mark gnus-newsgroup-name delta-marks))
a1506d29 5906
eec82323
LMI
5907 ;; Enter these new marks into the info of the group.
5908 (if (nthcdr 3 info)
5909 (setcar (nthcdr 3 info) newmarked)
5910 ;; Add the marks lists to the end of the info.
5911 (when newmarked
5912 (setcdr (nthcdr 2 info) (list newmarked))))
5913
5914 ;; Cut off the end of the info if there's nothing else there.
5915 (let ((i 5))
5916 (while (and (> i 2)
5917 (not (nth i info)))
5918 (when (nthcdr (decf i) info)
5919 (setcdr (nthcdr i info) nil)))))))
5920
5921(defun gnus-set-mode-line (where)
16409b0b 5922 "Set the mode line of the article or summary buffers.
eec82323
LMI
5923If WHERE is `summary', the summary mode line format will be used."
5924 ;; Is this mode line one we keep updated?
16409b0b
GM
5925 (when (and (memq where gnus-updated-mode-lines)
5926 (symbol-value
5927 (intern (format "gnus-%s-mode-line-format-spec" where))))
eec82323
LMI
5928 (let (mode-string)
5929 (save-excursion
5930 ;; We evaluate this in the summary buffer since these
5931 ;; variables are buffer-local to that buffer.
5932 (set-buffer gnus-summary-buffer)
23f87bed 5933 ;; We bind all these variables that are used in the `eval' form
eec82323
LMI
5934 ;; below.
5935 (let* ((mformat (symbol-value
5936 (intern
5937 (format "gnus-%s-mode-line-format-spec" where))))
b90a6149
MB
5938 (gnus-tmp-group-name (gnus-mode-string-quote
5939 (gnus-group-decoded-name
5940 gnus-newsgroup-name)))
eec82323
LMI
5941 (gnus-tmp-article-number (or gnus-current-article 0))
5942 (gnus-tmp-unread gnus-newsgroup-unreads)
5943 (gnus-tmp-unread-and-unticked (length gnus-newsgroup-unreads))
5944 (gnus-tmp-unselected (length gnus-newsgroup-unselected))
5945 (gnus-tmp-unread-and-unselected
5946 (cond ((and (zerop gnus-tmp-unread-and-unticked)
5947 (zerop gnus-tmp-unselected))
5948 "")
5949 ((zerop gnus-tmp-unselected)
5950 (format "{%d more}" gnus-tmp-unread-and-unticked))
5951 (t (format "{%d(+%d) more}"
5952 gnus-tmp-unread-and-unticked
5953 gnus-tmp-unselected))))
5954 (gnus-tmp-subject
5955 (if (and gnus-current-headers
5956 (vectorp gnus-current-headers))
5957 (gnus-mode-string-quote
5958 (mail-header-subject gnus-current-headers))
5959 ""))
5960 bufname-length max-len
23f87bed 5961 gnus-tmp-header) ;; passed as argument to any user-format-funcs
eec82323
LMI
5962 (setq mode-string (eval mformat))
5963 (setq bufname-length (if (string-match "%b" mode-string)
5964 (- (length
5965 (buffer-name
5966 (if (eq where 'summary)
5967 nil
5968 (get-buffer gnus-article-buffer))))
5969 2)
5970 0))
5971 (setq max-len (max 4 (if gnus-mode-non-string-length
5972 (- (window-width)
5973 gnus-mode-non-string-length
5974 bufname-length)
5975 (length mode-string))))
5976 ;; We might have to chop a bit of the string off...
5977 (when (> (length mode-string) max-len)
5978 (setq mode-string
16409b0b 5979 (concat (truncate-string-to-width mode-string (- max-len 3))
eec82323
LMI
5980 "...")))
5981 ;; Pad the mode string a bit.
5982 (setq mode-string (format (format "%%-%ds" max-len) mode-string))))
5983 ;; Update the mode line.
5984 (setq mode-line-buffer-identification
5985 (gnus-mode-line-buffer-identification (list mode-string)))
5986 (set-buffer-modified-p t))))
5987
5988(defun gnus-create-xref-hashtb (from-newsgroup headers unreads)
5989 "Go through the HEADERS list and add all Xrefs to a hash table.
5990The resulting hash table is returned, or nil if no Xrefs were found."
5991 (let* ((virtual (gnus-virtual-group-p from-newsgroup))
5992 (prefix (if virtual "" (gnus-group-real-prefix from-newsgroup)))
5993 (xref-hashtb (gnus-make-hashtable))
5994 start group entry number xrefs header)
5995 (while headers
5996 (setq header (pop headers))
5997 (when (and (setq xrefs (mail-header-xref header))
5998 (not (memq (setq number (mail-header-number header))
5999 unreads)))
6000 (setq start 0)
6001 (while (string-match "\\([^ ]+\\)[:/]\\([0-9]+\\)" xrefs start)
6002 (setq start (match-end 0))
6003 (setq group (if prefix
6004 (concat prefix (substring xrefs (match-beginning 1)
6005 (match-end 1)))
6006 (substring xrefs (match-beginning 1) (match-end 1))))
6007 (setq number
e9bd5782 6008 (string-to-number (substring xrefs (match-beginning 2)
eec82323
LMI
6009 (match-end 2))))
6010 (if (setq entry (gnus-gethash group xref-hashtb))
6011 (setcdr entry (cons number (cdr entry)))
6012 (gnus-sethash group (cons number nil) xref-hashtb)))))
6013 (and start xref-hashtb)))
6014
6015(defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads)
6016 "Look through all the headers and mark the Xrefs as read."
6017 (let ((virtual (gnus-virtual-group-p from-newsgroup))
01c52d31 6018 name info xref-hashtb idlist method nth4)
eec82323
LMI
6019 (save-excursion
6020 (set-buffer gnus-group-buffer)
6021 (when (setq xref-hashtb
6022 (gnus-create-xref-hashtb from-newsgroup headers unreads))
6023 (mapatoms
6024 (lambda (group)
6025 (unless (string= from-newsgroup (setq name (symbol-name group)))
6026 (setq idlist (symbol-value group))
6027 ;; Dead groups are not updated.
6028 (and (prog1
01c52d31 6029 (setq info (gnus-get-info name))
eec82323
LMI
6030 (when (stringp (setq nth4 (gnus-info-method info)))
6031 (setq nth4 (gnus-server-to-method nth4))))
6032 ;; Only do the xrefs if the group has the same
6033 ;; select method as the group we have just read.
6034 (or (gnus-methods-equal-p
6035 nth4 (gnus-find-method-for-group from-newsgroup))
6036 virtual
6037 (equal nth4 (setq method (gnus-find-method-for-group
6038 from-newsgroup)))
6039 (and (equal (car nth4) (car method))
6040 (equal (nth 1 nth4) (nth 1 method))))
6041 gnus-use-cross-reference
6042 (or (not (eq gnus-use-cross-reference t))
6043 virtual
6044 ;; Only do cross-references on subscribed
6045 ;; groups, if that is what is wanted.
6046 (<= (gnus-info-level info) gnus-level-subscribed))
6047 (gnus-group-make-articles-read name idlist))))
6048 xref-hashtb)))))
6049
6748645f 6050(defun gnus-compute-read-articles (group articles)
01c52d31 6051 (let* ((entry (gnus-group-entry group))
6748645f
LMI
6052 (info (nth 2 entry))
6053 (active (gnus-active group))
6054 ninfo)
6055 (when entry
16409b0b 6056 ;; First peel off all invalid article numbers.
6748645f
LMI
6057 (when active
6058 (let ((ids articles)
6059 id first)
6060 (while (setq id (pop ids))
6061 (when (and first (> id (cdr active)))
6062 ;; We'll end up in this situation in one particular
6063 ;; obscure situation. If you re-scan a group and get
6064 ;; a new article that is cross-posted to a different
6065 ;; group that has not been re-scanned, you might get
6066 ;; crossposted article that has a higher number than
6067 ;; Gnus believes possible. So we re-activate this
6068 ;; group as well. This might mean doing the
6069 ;; crossposting thingy will *increase* the number
6070 ;; of articles in some groups. Tsk, tsk.
6071 (setq active (or (gnus-activate-group group) active)))
6072 (when (or (> id (cdr active))
6073 (< id (car active)))
6074 (setq articles (delq id articles))))))
6075 ;; If the read list is nil, we init it.
6076 (if (and active
6077 (null (gnus-info-read info))
6078 (> (car active) 1))
6079 (setq ninfo (cons 1 (1- (car active))))
6080 (setq ninfo (gnus-info-read info)))
6081 ;; Then we add the read articles to the range.
6082 (gnus-add-to-range
6083 ninfo (setq articles (sort articles '<))))))
6084
eec82323
LMI
6085(defun gnus-group-make-articles-read (group articles)
6086 "Update the info of GROUP to say that ARTICLES are read."
6087 (let* ((num 0)
01c52d31 6088 (entry (gnus-group-entry group))
eec82323
LMI
6089 (info (nth 2 entry))
6090 (active (gnus-active group))
6091 range)
6748645f
LMI
6092 (when entry
6093 (setq range (gnus-compute-read-articles group articles))
01c52d31 6094 (with-current-buffer gnus-group-buffer
6748645f
LMI
6095 (gnus-undo-register
6096 `(progn
6097 (gnus-info-set-marks ',info ',(gnus-info-marks info) t)
6098 (gnus-info-set-read ',info ',(gnus-info-read info))
6099 (gnus-get-unread-articles-in-group ',info (gnus-active ,group))
23f87bed 6100 (gnus-request-set-mark ,group (list (list ',range 'del '(read))))
6748645f
LMI
6101 (gnus-group-update-group ,group t))))
6102 ;; Add the read articles to the range.
6103 (gnus-info-set-read info range)
23f87bed 6104 (gnus-request-set-mark group (list (list range 'add '(read))))
6748645f
LMI
6105 ;; Then we have to re-compute how many unread
6106 ;; articles there are in this group.
6107 (when active
6108 (cond
6109 ((not range)
6110 (setq num (- (1+ (cdr active)) (car active))))
6111 ((not (listp (cdr range)))
6112 (setq num (- (cdr active) (- (1+ (cdr range))
6113 (car range)))))
6114 (t
6115 (while range
6116 (if (numberp (car range))
6117 (setq num (1+ num))
6118 (setq num (+ num (- (1+ (cdar range)) (caar range)))))
6119 (setq range (cdr range)))
6120 (setq num (- (cdr active) num))))
6121 ;; Update the number of unread articles.
6122 (setcar entry num)
6123 ;; Update the group buffer.
23f87bed
MB
6124 (unless (gnus-ephemeral-group-p group)
6125 (gnus-group-update-group group t))))))
eec82323 6126
eec82323
LMI
6127(defvar gnus-newsgroup-none-id 0)
6128
6129(defun gnus-get-newsgroup-headers (&optional dependencies force-new)
6130 (let ((cur nntp-server-buffer)
6131 (dependencies
6132 (or dependencies
01c52d31
MB
6133 (with-current-buffer gnus-summary-buffer
6134 gnus-newsgroup-dependencies)))
6135 headers id end ref number
16409b0b
GM
6136 (mail-parse-charset gnus-newsgroup-charset)
6137 (mail-parse-ignored-charsets
6138 (save-excursion (condition-case nil
6139 (set-buffer gnus-summary-buffer)
6140 (error))
6141 gnus-newsgroup-ignored-charsets)))
eec82323
LMI
6142 (save-excursion
6143 (set-buffer nntp-server-buffer)
6144 ;; Translate all TAB characters into SPACE characters.
6145 (subst-char-in-region (point-min) (point-max) ?\t ? t)
16409b0b 6146 (subst-char-in-region (point-min) (point-max) ?\r ? t)
23f87bed 6147 (ietf-drums-unfold-fws)
6748645f 6148 (gnus-run-hooks 'gnus-parse-headers-hook)
eec82323 6149 (let ((case-fold-search t)
6748645f 6150 in-reply-to header p lines chars)
eec82323 6151 (goto-char (point-min))
01ccbb85 6152 ;; Search to the beginning of the next header. Error messages
eec82323
LMI
6153 ;; do not begin with 2 or 3.
6154 (while (re-search-forward "^[23][0-9]+ " nil t)
6155 (setq id nil
6156 ref nil)
6157 ;; This implementation of this function, with nine
6158 ;; search-forwards instead of the one re-search-forward and
6159 ;; a case (which basically was the old function) is actually
01ccbb85 6160 ;; about twice as fast, even though it looks messier. You
eec82323
LMI
6161 ;; can't have everything, I guess. Speed and elegance
6162 ;; doesn't always go hand in hand.
6163 (setq
6164 header
6165 (vector
6166 ;; Number.
6167 (prog1
01c52d31 6168 (setq number (read cur))
eec82323
LMI
6169 (end-of-line)
6170 (setq p (point))
6171 (narrow-to-region (point)
6172 (or (and (search-forward "\n.\n" nil t)
6173 (- (point) 2))
6174 (point))))
6175 ;; Subject.
6176 (progn
6177 (goto-char p)
23f87bed 6178 (if (search-forward "\nsubject:" nil t)
16409b0b
GM
6179 (funcall gnus-decode-encoded-word-function
6180 (nnheader-header-value))
2bd3dcae 6181 "(none)"))
eec82323
LMI
6182 ;; From.
6183 (progn
6184 (goto-char p)
23f87bed 6185 (if (search-forward "\nfrom:" nil t)
343d6628 6186 (funcall gnus-decode-encoded-address-function
16409b0b 6187 (nnheader-header-value))
2bd3dcae 6188 "(nobody)"))
eec82323
LMI
6189 ;; Date.
6190 (progn
6191 (goto-char p)
23f87bed 6192 (if (search-forward "\ndate:" nil t)
eec82323
LMI
6193 (nnheader-header-value) ""))
6194 ;; Message-ID.
6195 (progn
6196 (goto-char p)
6748645f
LMI
6197 (setq id (if (re-search-forward
6198 "^message-id: *\\(<[^\n\t> ]+>\\)" nil t)
6199 ;; We do it this way to make sure the Message-ID
6200 ;; is (somewhat) syntactically valid.
6201 (buffer-substring (match-beginning 1)
6202 (match-end 1))
eec82323
LMI
6203 ;; If there was no message-id, we just fake one
6204 ;; to make subsequent routines simpler.
01c52d31 6205 (nnheader-generate-fake-message-id number))))
eec82323
LMI
6206 ;; References.
6207 (progn
6208 (goto-char p)
23f87bed 6209 (if (search-forward "\nreferences:" nil t)
eec82323
LMI
6210 (progn
6211 (setq end (point))
6212 (prog1
6213 (nnheader-header-value)
6214 (setq ref
6215 (buffer-substring
6216 (progn
6217 (end-of-line)
6218 (search-backward ">" end t)
6219 (1+ (point)))
6220 (progn
6221 (search-backward "<" end t)
6222 (point))))))
6223 ;; Get the references from the in-reply-to header if there
6224 ;; were no references and the in-reply-to header looks
6225 ;; promising.
23f87bed 6226 (if (and (search-forward "\nin-reply-to:" nil t)
eec82323
LMI
6227 (setq in-reply-to (nnheader-header-value))
6228 (string-match "<[^>]+>" in-reply-to))
6748645f
LMI
6229 (let (ref2)
6230 (setq ref (substring in-reply-to (match-beginning 0)
6231 (match-end 0)))
6232 (while (string-match "<[^>]+>" in-reply-to (match-end 0))
6233 (setq ref2 (substring in-reply-to (match-beginning 0)
6234 (match-end 0)))
6235 (when (> (length ref2) (length ref))
6236 (setq ref ref2)))
6237 ref)
eec82323
LMI
6238 (setq ref nil))))
6239 ;; Chars.
6748645f
LMI
6240 (progn
6241 (goto-char p)
6242 (if (search-forward "\nchars: " nil t)
6243 (if (numberp (setq chars (ignore-errors (read cur))))
23f87bed
MB
6244 chars -1)
6245 -1))
eec82323
LMI
6246 ;; Lines.
6247 (progn
6248 (goto-char p)
6249 (if (search-forward "\nlines: " nil t)
a8151ef7 6250 (if (numberp (setq lines (ignore-errors (read cur))))
23f87bed
MB
6251 lines -1)
6252 -1))
eec82323
LMI
6253 ;; Xref.
6254 (progn
6255 (goto-char p)
23f87bed 6256 (and (search-forward "\nxref:" nil t)
16409b0b
GM
6257 (nnheader-header-value)))
6258 ;; Extra.
6259 (when gnus-extra-headers
6260 (let ((extra gnus-extra-headers)
6261 out)
6262 (while extra
6263 (goto-char p)
6264 (when (search-forward
23f87bed 6265 (concat "\n" (symbol-name (car extra)) ":") nil t)
16409b0b
GM
6266 (push (cons (car extra) (nnheader-header-value))
6267 out))
6268 (pop extra))
6269 out))))
eec82323
LMI
6270 (when (equal id ref)
6271 (setq ref nil))
6748645f
LMI
6272
6273 (when gnus-alter-header-function
6274 (funcall gnus-alter-header-function header)
6275 (setq id (mail-header-id header)
6276 ref (gnus-parent-id (mail-header-references header))))
6277
6278 (when (setq header
6279 (gnus-dependencies-add-header
6280 header dependencies force-new))
eec82323
LMI
6281 (push header headers))
6282 (goto-char (point-max))
6283 (widen))
6284 (nreverse headers)))))
6285
eec82323
LMI
6286;; Goes through the xover lines and returns a list of vectors
6287(defun gnus-get-newsgroup-headers-xover (sequence &optional
6288 force-new dependencies
6289 group also-fetch-heads)
16409b0b
GM
6290 "Parse the news overview data in the server buffer.
6291Return a list of headers that match SEQUENCE (see
6292`nntp-retrieve-headers')."
eec82323
LMI
6293 ;; Get the Xref when the users reads the articles since most/some
6294 ;; NNTP servers do not include Xrefs when using XOVER.
6295 (setq gnus-article-internal-prepare-hook '(gnus-article-get-xrefs))
16409b0b
GM
6296 (let ((mail-parse-charset gnus-newsgroup-charset)
6297 (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)
6298 (cur nntp-server-buffer)
eec82323 6299 (dependencies (or dependencies gnus-newsgroup-dependencies))
23f87bed
MB
6300 (allp (cond
6301 ((eq gnus-read-all-available-headers t)
6302 t)
14e6dc54
MB
6303 ((and (stringp gnus-read-all-available-headers)
6304 group)
23f87bed
MB
6305 (string-match gnus-read-all-available-headers group))
6306 (t
6307 nil)))
eec82323
LMI
6308 number headers header)
6309 (save-excursion
6310 (set-buffer nntp-server-buffer)
16409b0b 6311 (subst-char-in-region (point-min) (point-max) ?\r ? t)
eec82323 6312 ;; Allow the user to mangle the headers before parsing them.
6748645f 6313 (gnus-run-hooks 'gnus-parse-headers-hook)
eec82323 6314 (goto-char (point-min))
23f87bed
MB
6315 (gnus-parse-without-error
6316 (while (and (or sequence allp)
6317 (not (eobp)))
6318 (setq number (read cur))
6319 (when (not allp)
6320 (while (and sequence
6321 (< (car sequence) number))
6322 (setq sequence (cdr sequence))))
6323 (when (and (or allp
6324 (and sequence
6325 (eq number (car sequence))))
6326 (progn
6327 (setq sequence (cdr sequence))
6328 (setq header (inline
6329 (gnus-nov-parse-line
6330 number dependencies force-new)))))
6331 (push header headers))
6332 (forward-line 1)))
eec82323
LMI
6333 ;; A common bug in inn is that if you have posted an article and
6334 ;; then retrieves the active file, it will answer correctly --
6335 ;; the new article is included. However, a NOV entry for the
6336 ;; article may not have been generated yet, so this may fail.
6337 ;; We work around this problem by retrieving the last few
6338 ;; headers using HEAD.
6339 (if (or (not also-fetch-heads)
6340 (not sequence))
6341 ;; We (probably) got all the headers.
6342 (nreverse headers)
6343 (let ((gnus-nov-is-evil t))
6344 (nconc
6345 (nreverse headers)
23f87bed 6346 (when (eq (gnus-retrieve-headers sequence group) 'headers)
eec82323
LMI
6347 (gnus-get-newsgroup-headers))))))))
6348
6349(defun gnus-article-get-xrefs ()
6350 "Fill in the Xref value in `gnus-current-headers', if necessary.
6351This is meant to be called in `gnus-article-internal-prepare-hook'."
01c52d31
MB
6352 (let ((headers (with-current-buffer gnus-summary-buffer
6353 gnus-current-headers)))
eec82323
LMI
6354 (or (not gnus-use-cross-reference)
6355 (not headers)
6356 (and (mail-header-xref headers)
6357 (not (string= (mail-header-xref headers) "")))
6358 (let ((case-fold-search t)
6359 xref)
6360 (save-restriction
6361 (nnheader-narrow-to-headers)
6362 (goto-char (point-min))
16409b0b
GM
6363 (when (or (and (not (eobp))
6364 (eq (downcase (char-after)) ?x)
eec82323
LMI
6365 (looking-at "Xref:"))
6366 (search-forward "\nXref:" nil t))
6367 (goto-char (1+ (match-end 0)))
01c52d31 6368 (setq xref (buffer-substring (point) (point-at-eol)))
eec82323
LMI
6369 (mail-header-set-xref headers xref)))))))
6370
6371(defun gnus-summary-insert-subject (id &optional old-header use-old-header)
6748645f
LMI
6372 "Find article ID and insert the summary line for that article.
6373OLD-HEADER can either be a header or a line number to insert
6374the subject line on."
6375 (let* ((line (and (numberp old-header) old-header))
6376 (old-header (and (vectorp old-header) old-header))
6377 (header (cond ((and old-header use-old-header)
16409b0b
GM
6378 old-header)
6379 ((and (numberp id)
6380 (gnus-number-to-header id))
6381 (gnus-number-to-header id))
6382 (t
6383 (gnus-read-header id))))
6384 (number (and (numberp id) id))
6385 d)
eec82323
LMI
6386 (when header
6387 ;; Rebuild the thread that this article is part of and go to the
6388 ;; article we have fetched.
6389 (when (and (not gnus-show-threads)
6390 old-header)
6748645f
LMI
6391 (when (and number
6392 (setq d (gnus-data-find (mail-header-number old-header))))
eec82323
LMI
6393 (goto-char (gnus-data-pos d))
6394 (gnus-data-remove
6395 number
01c52d31 6396 (- (point-at-bol)
eec82323 6397 (prog1
01c52d31 6398 (1+ (point-at-eol))
eec82323 6399 (gnus-delete-line))))))
23f87bed
MB
6400 ;; Remove list identifiers from subject.
6401 (when gnus-list-identifiers
6402 (let ((gnus-newsgroup-headers (list header)))
c3bc41c2 6403 (gnus-summary-remove-list-identifiers)))
eec82323
LMI
6404 (when old-header
6405 (mail-header-set-number header (mail-header-number old-header)))
6406 (setq gnus-newsgroup-sparse
6407 (delq (setq number (mail-header-number header))
6408 gnus-newsgroup-sparse))
6409 (setq gnus-newsgroup-ancient (delq number gnus-newsgroup-ancient))
6748645f
LMI
6410 (push number gnus-newsgroup-limit)
6411 (gnus-rebuild-thread (mail-header-id header) line)
eec82323
LMI
6412 (gnus-summary-goto-subject number nil t))
6413 (when (and (numberp number)
6414 (> number 0))
6415 ;; We have to update the boundaries even if we can't fetch the
6416 ;; article if ID is a number -- so that the next `P' or `N'
6417 ;; command will fetch the previous (or next) article even
6418 ;; if the one we tried to fetch this time has been canceled.
6419 (when (> number gnus-newsgroup-end)
6420 (setq gnus-newsgroup-end number))
6421 (when (< number gnus-newsgroup-begin)
6422 (setq gnus-newsgroup-begin number))
6423 (setq gnus-newsgroup-unselected
6424 (delq number gnus-newsgroup-unselected)))
6425 ;; Report back a success?
6426 (and header (mail-header-number header))))
6427
6428;;; Process/prefix in the summary buffer
6429
6430(defun gnus-summary-work-articles (n)
6748645f
LMI
6431 "Return a list of articles to be worked upon.
6432The prefix argument, the list of process marked articles, and the
6433current article will be taken into consideration."
6434 (save-excursion
6435 (set-buffer gnus-summary-buffer)
6436 (cond
6437 (n
6438 ;; A numerical prefix has been given.
6439 (setq n (prefix-numeric-value n))
6440 (let ((backward (< n 0))
6441 (n (abs (prefix-numeric-value n)))
6442 articles article)
6443 (save-excursion
6444 (while
6445 (and (> n 0)
6446 (push (setq article (gnus-summary-article-number))
6447 articles)
6448 (if backward
6449 (gnus-summary-find-prev nil article)
6450 (gnus-summary-find-next nil article)))
6451 (decf n)))
6452 (nreverse articles)))
6453 ((and (gnus-region-active-p) (mark))
6454 (message "region active")
6455 ;; Work on the region between point and mark.
6456 (let ((max (max (point) (mark)))
6457 articles article)
6458 (save-excursion
7dafe00b 6459 (goto-char (min (point) (mark)))
6748645f
LMI
6460 (while
6461 (and
6462 (push (setq article (gnus-summary-article-number)) articles)
6463 (gnus-summary-find-next nil article)
6464 (< (point) max)))
6465 (nreverse articles))))
6466 (gnus-newsgroup-processable
6467 ;; There are process-marked articles present.
6468 ;; Save current state.
6469 (gnus-summary-save-process-mark)
6470 ;; Return the list.
6471 (reverse gnus-newsgroup-processable))
6472 (t
6473 ;; Just return the current article.
6474 (list (gnus-summary-article-number))))))
6475
6476(defmacro gnus-summary-iterate (arg &rest forms)
6477 "Iterate over the process/prefixed articles and do FORMS.
6478ARG is the interactive prefix given to the command. FORMS will be
6479executed with point over the summary line of the articles."
6480 (let ((articles (make-symbol "gnus-summary-iterate-articles")))
6481 `(let ((,articles (gnus-summary-work-articles ,arg)))
6482 (while ,articles
6483 (gnus-summary-goto-subject (car ,articles))
16409b0b
GM
6484 ,@forms
6485 (pop ,articles)))))
6748645f
LMI
6486
6487(put 'gnus-summary-iterate 'lisp-indent-function 1)
6488(put 'gnus-summary-iterate 'edebug-form-spec '(form body))
eec82323
LMI
6489
6490(defun gnus-summary-save-process-mark ()
6491 "Push the current set of process marked articles on the stack."
6492 (interactive)
6493 (push (copy-sequence gnus-newsgroup-processable)
6494 gnus-newsgroup-process-stack))
6495
6496(defun gnus-summary-kill-process-mark ()
6497 "Push the current set of process marked articles on the stack and unmark."
6498 (interactive)
6499 (gnus-summary-save-process-mark)
6500 (gnus-summary-unmark-all-processable))
6501
6502(defun gnus-summary-yank-process-mark ()
6503 "Pop the last process mark state off the stack and restore it."
6504 (interactive)
6505 (unless gnus-newsgroup-process-stack
6506 (error "Empty mark stack"))
6507 (gnus-summary-process-mark-set (pop gnus-newsgroup-process-stack)))
6508
6509(defun gnus-summary-process-mark-set (set)
6510 "Make SET into the current process marked articles."
6511 (gnus-summary-unmark-all-processable)
01c52d31 6512 (mapc 'gnus-summary-set-process-mark set))
eec82323
LMI
6513
6514;;; Searching and stuff
6515
6516(defun gnus-summary-search-group (&optional backward use-level)
6517 "Search for next unread newsgroup.
6518If optional argument BACKWARD is non-nil, search backward instead."
6519 (save-excursion
6520 (set-buffer gnus-group-buffer)
6521 (when (gnus-group-search-forward
6522 backward nil (if use-level (gnus-group-group-level) nil))
6523 (gnus-group-group-name))))
6524
6525(defun gnus-summary-best-group (&optional exclude-group)
6526 "Find the name of the best unread group.
6527If EXCLUDE-GROUP, do not go to this group."
01c52d31 6528 (with-current-buffer gnus-group-buffer
eec82323
LMI
6529 (save-excursion
6530 (gnus-group-best-unread-group exclude-group))))
6531
23f87bed
MB
6532(defun gnus-summary-find-next (&optional unread article backward)
6533 (if backward
6534 (gnus-summary-find-prev unread article)
eec82323
LMI
6535 (let* ((dummy (gnus-summary-article-intangible-p))
6536 (article (or article (gnus-summary-article-number)))
23f87bed 6537 (data (gnus-data-find-list article))
eec82323
LMI
6538 result)
6539 (when (and (not dummy)
6540 (or (not gnus-summary-check-current)
6541 (not unread)
23f87bed
MB
6542 (not (gnus-data-unread-p (car data)))))
6543 (setq data (cdr data)))
eec82323
LMI
6544 (when (setq result
6545 (if unread
6546 (progn
23f87bed
MB
6547 (while data
6548 (unless (memq (gnus-data-number (car data))
6549 (cond
6550 ((eq gnus-auto-goto-ignores
6551 'always-undownloaded)
6552 gnus-newsgroup-undownloaded)
6553 (gnus-plugged
6554 nil)
6555 ((eq gnus-auto-goto-ignores
6556 'unfetched)
6557 gnus-newsgroup-unfetched)
6558 ((eq gnus-auto-goto-ignores
6559 'undownloaded)
6560 gnus-newsgroup-undownloaded)))
6561 (when (gnus-data-unread-p (car data))
6562 (setq result (car data)
6563 data nil)))
6564 (setq data (cdr data)))
eec82323 6565 result)
23f87bed 6566 (car data)))
eec82323
LMI
6567 (goto-char (gnus-data-pos result))
6568 (gnus-data-number result)))))
6569
6570(defun gnus-summary-find-prev (&optional unread article)
6571 (let* ((eobp (eobp))
6572 (article (or article (gnus-summary-article-number)))
23f87bed 6573 (data (gnus-data-find-list article (gnus-data-list 'rev)))
eec82323
LMI
6574 result)
6575 (when (and (not eobp)
6576 (or (not gnus-summary-check-current)
6577 (not unread)
23f87bed
MB
6578 (not (gnus-data-unread-p (car data)))))
6579 (setq data (cdr data)))
eec82323
LMI
6580 (when (setq result
6581 (if unread
6582 (progn
23f87bed
MB
6583 (while data
6584 (unless (memq (gnus-data-number (car data))
6585 (cond
6586 ((eq gnus-auto-goto-ignores
6587 'always-undownloaded)
6588 gnus-newsgroup-undownloaded)
6589 (gnus-plugged
6590 nil)
6591 ((eq gnus-auto-goto-ignores
6592 'unfetched)
6593 gnus-newsgroup-unfetched)
6594 ((eq gnus-auto-goto-ignores
6595 'undownloaded)
6596 gnus-newsgroup-undownloaded)))
6597 (when (gnus-data-unread-p (car data))
6598 (setq result (car data)
6599 data nil)))
6600 (setq data (cdr data)))
eec82323 6601 result)
23f87bed 6602 (car data)))
eec82323
LMI
6603 (goto-char (gnus-data-pos result))
6604 (gnus-data-number result))))
6605
6606(defun gnus-summary-find-subject (subject &optional unread backward article)
6607 (let* ((simp-subject (gnus-simplify-subject-fully subject))
6608 (article (or article (gnus-summary-article-number)))
6609 (articles (gnus-data-list backward))
6610 (arts (gnus-data-find-list article articles))
6611 result)
6612 (when (or (not gnus-summary-check-current)
6613 (not unread)
6614 (not (gnus-data-unread-p (car arts))))
6615 (setq arts (cdr arts)))
6616 (while arts
6617 (and (or (not unread)
6618 (gnus-data-unread-p (car arts)))
6619 (vectorp (gnus-data-header (car arts)))
6620 (gnus-subject-equal
6621 simp-subject (mail-header-subject (gnus-data-header (car arts))) t)
6622 (setq result (car arts)
6623 arts nil))
6624 (setq arts (cdr arts)))
6625 (and result
6626 (goto-char (gnus-data-pos result))
6627 (gnus-data-number result))))
6628
6629(defun gnus-summary-search-forward (&optional unread subject backward)
6630 "Search forward for an article.
6631If UNREAD, look for unread articles. If SUBJECT, look for
6632articles with that subject. If BACKWARD, search backward instead."
6633 (cond (subject (gnus-summary-find-subject subject unread backward))
6634 (backward (gnus-summary-find-prev unread))
6635 (t (gnus-summary-find-next unread))))
6636
6637(defun gnus-recenter (&optional n)
6638 "Center point in window and redisplay frame.
6639Also do horizontal recentering."
6640 (interactive "P")
6641 (when (and gnus-auto-center-summary
6642 (not (eq gnus-auto-center-summary 'vertical)))
6643 (gnus-horizontal-recenter))
6644 (recenter n))
6645
6646(defun gnus-summary-recenter ()
6647 "Center point in the summary window.
6648If `gnus-auto-center-summary' is nil, or the article buffer isn't
6649displayed, no centering will be performed."
6650 ;; Suggested by earle@mahendo.JPL.NASA.GOV (Greg Earle).
6651 ;; Recenter only when requested. Suggested by popovich@park.cs.columbia.edu.
16409b0b 6652 (interactive)
23f87bed
MB
6653 ;; The user has to want it.
6654 (when gnus-auto-center-summary
6655 (let* ((top (cond ((< (window-height) 4) 0)
6656 ((< (window-height) 7) 1)
6657 (t (if (numberp gnus-auto-center-summary)
6658 gnus-auto-center-summary
01c52d31 6659 (/ (1- (window-height)) 2)))))
23f87bed
MB
6660 (height (1- (window-height)))
6661 (bottom (save-excursion (goto-char (point-max))
6662 (forward-line (- height))
6663 (point)))
6664 (window (get-buffer-window (current-buffer))))
eec82323
LMI
6665 (when (get-buffer-window gnus-article-buffer)
6666 ;; Only do recentering when the article buffer is displayed,
6667 ;; Set the window start to either `bottom', which is the biggest
6668 ;; possible valid number, or the second line from the top,
6669 ;; whichever is the least.
db7ebd73
MB
6670 (let ((top-pos (save-excursion (forward-line (- top)) (point))))
6671 (if (> bottom top-pos)
6672 ;; Keep the second line from the top visible
01c52d31 6673 (set-window-start window top-pos)
db7ebd73
MB
6674 ;; Try to keep the bottom line visible; if it's partially
6675 ;; obscured, either scroll one more line to make it fully
6676 ;; visible, or revert to using TOP-POS.
6677 (save-excursion
6678 (goto-char (point-max))
6679 (forward-line -1)
6680 (let ((last-line-start (point)))
6681 (goto-char bottom)
6682 (set-window-start window (point) t)
6683 (when (not (pos-visible-in-window-p last-line-start window))
6684 (forward-line 1)
6685 (set-window-start window (min (point) top-pos) t)))))))
eec82323
LMI
6686 ;; Do horizontal recentering while we're at it.
6687 (when (and (get-buffer-window (current-buffer) t)
6688 (not (eq gnus-auto-center-summary 'vertical)))
6689 (let ((selected (selected-window)))
6690 (select-window (get-buffer-window (current-buffer) t))
6691 (gnus-summary-position-point)
6692 (gnus-horizontal-recenter)
6693 (select-window selected))))))
6694
6695(defun gnus-summary-jump-to-group (newsgroup)
6696 "Move point to NEWSGROUP in group mode buffer."
6697 ;; Keep update point of group mode buffer if visible.
6698 (if (eq (current-buffer) (get-buffer gnus-group-buffer))
6699 (save-window-excursion
6700 ;; Take care of tree window mode.
6701 (when (get-buffer-window gnus-group-buffer)
6702 (pop-to-buffer gnus-group-buffer))
6703 (gnus-group-jump-to-group newsgroup))
6704 (save-excursion
6705 ;; Take care of tree window mode.
6706 (if (get-buffer-window gnus-group-buffer)
6707 (pop-to-buffer gnus-group-buffer)
6708 (set-buffer gnus-group-buffer))
6709 (gnus-group-jump-to-group newsgroup))))
6710
6711;; This function returns a list of article numbers based on the
6712;; difference between the ranges of read articles in this group and
6713;; the range of active articles.
6714(defun gnus-list-of-unread-articles (group)
6715 (let* ((read (gnus-info-read (gnus-get-info group)))
6716 (active (or (gnus-active group) (gnus-activate-group group)))
01c52d31
MB
6717 (last (or (cdr active)
6718 (error "Group %s couldn't be activated " group)))
4b70e299
MB
6719 (bottom (if gnus-newsgroup-maximum-articles
6720 (max (car active)
6721 (- last gnus-newsgroup-maximum-articles -1))
11abff8e 6722 (car active)))
eec82323
LMI
6723 first nlast unread)
6724 ;; If none are read, then all are unread.
6725 (if (not read)
11abff8e 6726 (setq first bottom)
eec82323
LMI
6727 ;; If the range of read articles is a single range, then the
6728 ;; first unread article is the article after the last read
6729 ;; article. Sounds logical, doesn't it?
16409b0b 6730 (if (and (not (listp (cdr read)))
11abff8e 6731 (or (< (car read) bottom)
16409b0b
GM
6732 (progn (setq read (list read))
6733 nil)))
11abff8e 6734 (setq first (max bottom (1+ (cdr read))))
eec82323
LMI
6735 ;; `read' is a list of ranges.
6736 (when (/= (setq nlast (or (and (numberp (car read)) (car read))
6737 (caar read)))
6738 1)
11abff8e 6739 (setq first bottom))
eec82323
LMI
6740 (while read
6741 (when first
6742 (while (< first nlast)
54506618
MB
6743 (setq unread (cons first unread)
6744 first (1+ first))))
eec82323
LMI
6745 (setq first (1+ (if (atom (car read)) (car read) (cdar read))))
6746 (setq nlast (if (atom (cadr read)) (cadr read) (caadr read)))
6747 (setq read (cdr read)))))
6748 ;; And add the last unread articles.
6749 (while (<= first last)
54506618
MB
6750 (setq unread (cons first unread)
6751 first (1+ first)))
eec82323 6752 ;; Return the list of unread articles.
6748645f 6753 (delq 0 (nreverse unread))))
eec82323
LMI
6754
6755(defun gnus-list-of-read-articles (group)
6756 "Return a list of unread, unticked and non-dormant articles."
6757 (let* ((info (gnus-get-info group))
6758 (marked (gnus-info-marks info))
6759 (active (gnus-active group)))
6760 (and info active
23f87bed
MB
6761 (gnus-list-range-difference
6762 (gnus-list-range-difference
6763 (gnus-sorted-complement
11abff8e 6764 (gnus-uncompress-range
4b70e299 6765 (if gnus-newsgroup-maximum-articles
11abff8e 6766 (cons (max (car active)
4b70e299
MB
6767 (- (cdr active)
6768 gnus-newsgroup-maximum-articles
6769 -1))
11abff8e
MB
6770 (cdr active))
6771 active))
23f87bed
MB
6772 (gnus-list-of-unread-articles group))
6773 (cdr (assq 'dormant marked)))
6774 (cdr (assq 'tick marked))))))
eec82323 6775
54506618
MB
6776;; This function returns a sequence of article numbers based on the
6777;; difference between the ranges of read articles in this group and
6778;; the range of active articles.
6779(defun gnus-sequence-of-unread-articles (group)
6780 (let* ((read (gnus-info-read (gnus-get-info group)))
6781 (active (or (gnus-active group) (gnus-activate-group group)))
6782 (last (cdr active))
4b70e299
MB
6783 (bottom (if gnus-newsgroup-maximum-articles
6784 (max (car active)
6785 (- last gnus-newsgroup-maximum-articles -1))
11abff8e 6786 (car active)))
54506618
MB
6787 first nlast unread)
6788 ;; If none are read, then all are unread.
6789 (if (not read)
11abff8e 6790 (setq first bottom)
54506618
MB
6791 ;; If the range of read articles is a single range, then the
6792 ;; first unread article is the article after the last read
6793 ;; article. Sounds logical, doesn't it?
6794 (if (and (not (listp (cdr read)))
11abff8e 6795 (or (< (car read) bottom)
54506618
MB
6796 (progn (setq read (list read))
6797 nil)))
11abff8e 6798 (setq first (max bottom (1+ (cdr read))))
54506618
MB
6799 ;; `read' is a list of ranges.
6800 (when (/= (setq nlast (or (and (numberp (car read)) (car read))
6801 (caar read)))
6802 1)
11abff8e 6803 (setq first bottom))
54506618
MB
6804 (while read
6805 (when first
6806 (push (cons first nlast) unread))
6807 (setq first (1+ (if (atom (car read)) (car read) (cdar read))))
6808 (setq nlast (if (atom (cadr read)) (cadr read) (caadr read)))
6809 (setq read (cdr read)))))
6810 ;; And add the last unread articles.
ba0226dd
MB
6811 (cond ((not (and first last))
6812 nil)
6813 ((< first last)
6814 (push (cons first last) unread))
6815 ((= first last)
6816 (push first unread)))
54506618
MB
6817 ;; Return the sequence of unread articles.
6818 (delq 0 (nreverse unread))))
6819
eec82323
LMI
6820;; Various summary commands
6821
6748645f
LMI
6822(defun gnus-summary-select-article-buffer ()
6823 "Reconfigure windows to show article buffer."
6824 (interactive)
6825 (if (not (gnus-buffer-live-p gnus-article-buffer))
6826 (error "There is no article buffer for this summary buffer")
6827 (gnus-configure-windows 'article)
6828 (select-window (get-buffer-window gnus-article-buffer))))
6829
eec82323
LMI
6830(defun gnus-summary-universal-argument (arg)
6831 "Perform any operation on all articles that are process/prefixed."
6832 (interactive "P")
eec82323
LMI
6833 (let ((articles (gnus-summary-work-articles arg))
6834 func article)
6835 (if (eq
6836 (setq
6837 func
6838 (key-binding
6839 (read-key-sequence
6840 (substitute-command-keys
16409b0b 6841 "\\<gnus-summary-mode-map>\\[gnus-summary-universal-argument]"))))
eec82323
LMI
6842 'undefined)
6843 (gnus-error 1 "Undefined key")
6844 (save-excursion
6845 (while articles
6846 (gnus-summary-goto-subject (setq article (pop articles)))
6847 (let (gnus-newsgroup-processable)
6848 (command-execute func))
6849 (gnus-summary-remove-process-mark article)))))
6850 (gnus-summary-position-point))
6851
6852(defun gnus-summary-toggle-truncation (&optional arg)
6853 "Toggle truncation of summary lines.
23f87bed 6854With ARG, turn line truncation on if ARG is positive."
eec82323
LMI
6855 (interactive "P")
6856 (setq truncate-lines
6857 (if (null arg) (not truncate-lines)
6858 (> (prefix-numeric-value arg) 0)))
6859 (redraw-display))
6860
23f87bed
MB
6861(defun gnus-summary-find-for-reselect ()
6862 "Return the number of an article to stay on across a reselect.
6863The current article is considered, then following articles, then previous
6864articles. An article is sought which is not cancelled and isn't a temporary
6865insertion from another group. If there's no such then return a dummy 0."
6866 (let (found)
6867 (dolist (rev '(nil t))
6868 (unless found ; don't demand the reverse list if we don't need it
6869 (let ((data (gnus-data-find-list
6870 (gnus-summary-article-number) (gnus-data-list rev))))
6871 (while (and data (not found))
6872 (if (and (< 0 (gnus-data-number (car data)))
6873 (not (eq gnus-canceled-mark (gnus-data-mark (car data)))))
6874 (setq found (gnus-data-number (car data))))
6875 (setq data (cdr data))))))
6876 (or found 0)))
6877
eec82323
LMI
6878(defun gnus-summary-reselect-current-group (&optional all rescan)
6879 "Exit and then reselect the current newsgroup.
6880The prefix argument ALL means to select all articles."
6881 (interactive "P")
eec82323
LMI
6882 (when (gnus-ephemeral-group-p gnus-newsgroup-name)
6883 (error "Ephemeral groups can't be reselected"))
23f87bed 6884 (let ((current-subject (gnus-summary-find-for-reselect))
eec82323
LMI
6885 (group gnus-newsgroup-name))
6886 (setq gnus-newsgroup-begin nil)
23f87bed 6887 (gnus-summary-exit nil 'leave-hidden)
eec82323
LMI
6888 ;; We have to adjust the point of group mode buffer because
6889 ;; point was moved to the next unread newsgroup by exiting.
6890 (gnus-summary-jump-to-group group)
6891 (when rescan
6892 (save-excursion
6893 (gnus-group-get-new-news-this-group 1)))
6894 (gnus-group-read-group all t)
6895 (gnus-summary-goto-subject current-subject nil t)))
6896
6897(defun gnus-summary-rescan-group (&optional all)
6898 "Exit the newsgroup, ask for new articles, and select the newsgroup."
6899 (interactive "P")
6900 (gnus-summary-reselect-current-group all t))
6901
6902(defun gnus-summary-update-info (&optional non-destructive)
6903 (save-excursion
6904 (let ((group gnus-newsgroup-name))
6748645f
LMI
6905 (when group
6906 (when gnus-newsgroup-kill-headers
6907 (setq gnus-newsgroup-killed
6908 (gnus-compress-sequence
23f87bed
MB
6909 (gnus-sorted-union
6910 (gnus-list-range-intersection
6911 gnus-newsgroup-unselected gnus-newsgroup-killed)
6912 gnus-newsgroup-unreads)
6748645f
LMI
6913 t)))
6914 (unless (listp (cdr gnus-newsgroup-killed))
6915 (setq gnus-newsgroup-killed (list gnus-newsgroup-killed)))
6916 (let ((headers gnus-newsgroup-headers))
6917 ;; Set the new ranges of read articles.
01c52d31 6918 (with-current-buffer gnus-group-buffer
6748645f
LMI
6919 (gnus-undo-force-boundary))
6920 (gnus-update-read-articles
23f87bed
MB
6921 group (gnus-sorted-union
6922 gnus-newsgroup-unreads gnus-newsgroup-unselected))
6748645f
LMI
6923 ;; Set the current article marks.
6924 (let ((gnus-newsgroup-scored
6925 (if (and (not gnus-save-score)
6926 (not non-destructive))
6927 nil
6928 gnus-newsgroup-scored)))
6929 (save-excursion
6930 (gnus-update-marks)))
6931 ;; Do the cross-ref thing.
6932 (when gnus-use-cross-reference
6933 (gnus-mark-xrefs-as-read group headers gnus-newsgroup-unreads))
6934 ;; Do not switch windows but change the buffer to work.
a8151ef7 6935 (set-buffer gnus-group-buffer)
6748645f
LMI
6936 (unless (gnus-ephemeral-group-p group)
6937 (gnus-group-update-group group)))))))
eec82323
LMI
6938
6939(defun gnus-summary-save-newsrc (&optional force)
6940 "Save the current number of read/marked articles in the dribble buffer.
6941The dribble buffer will then be saved.
6942If FORCE (the prefix), also save the .newsrc file(s)."
6943 (interactive "P")
6944 (gnus-summary-update-info t)
6945 (if force
6946 (gnus-save-newsrc-file)
6947 (gnus-dribble-save)))
6948
23f87bed 6949(defun gnus-summary-exit (&optional temporary leave-hidden)
eec82323 6950 "Exit reading current newsgroup, and then return to group selection mode.
16409b0b 6951`gnus-exit-group-hook' is called with no arguments if that value is non-nil."
eec82323
LMI
6952 (interactive)
6953 (gnus-set-global-variables)
16409b0b
GM
6954 (when (gnus-buffer-live-p gnus-article-buffer)
6955 (save-excursion
6956 (set-buffer gnus-article-buffer)
6957 (mm-destroy-parts gnus-article-mime-handles)
6958 ;; Set it to nil for safety reason.
6959 (setq gnus-article-mime-handle-alist nil)
6960 (setq gnus-article-mime-handles nil)))
eec82323 6961 (gnus-kill-save-kill-buffer)
6748645f 6962 (gnus-async-halt-prefetch)
eec82323
LMI
6963 (let* ((group gnus-newsgroup-name)
6964 (quit-config (gnus-group-quit-config gnus-newsgroup-name))
23f87bed 6965 (gnus-group-is-exiting-p t)
eec82323 6966 (mode major-mode)
23f87bed 6967 (group-point nil)
eec82323 6968 (buf (current-buffer)))
16409b0b
GM
6969 (unless quit-config
6970 ;; Do adaptive scoring, and possibly save score files.
6971 (when gnus-newsgroup-adaptive
6972 (gnus-score-adaptive))
6973 (when gnus-use-scoring
6974 (gnus-score-save)))
6748645f 6975 (gnus-run-hooks 'gnus-summary-prepare-exit-hook)
eec82323
LMI
6976 ;; If we have several article buffers, we kill them at exit.
6977 (unless gnus-single-article-buffer
01c52d31
MB
6978 (when (gnus-buffer-live-p gnus-article-buffer)
6979 (with-current-buffer gnus-article-buffer
6980 ;; Don't kill sticky article buffers
6981 (unless (eq major-mode 'gnus-sticky-article-mode)
6982 (gnus-kill-buffer gnus-article-buffer)
6983 (setq gnus-article-current nil))))
6984 (gnus-kill-buffer gnus-original-article-buffer))
eec82323
LMI
6985 (when gnus-use-cache
6986 (gnus-cache-possibly-remove-articles)
6987 (gnus-cache-save-buffers))
6988 (gnus-async-prefetch-remove-group group)
6989 (when gnus-suppress-duplicates
6990 (gnus-dup-enter-articles))
6991 (when gnus-use-trees
6992 (gnus-tree-close group))
16409b0b
GM
6993 (when gnus-use-cache
6994 (gnus-cache-write-active))
6748645f
LMI
6995 ;; Remove entries for this group.
6996 (nnmail-purge-split-history (gnus-group-real-name group))
eec82323
LMI
6997 ;; Make all changes in this group permanent.
6998 (unless quit-config
6748645f 6999 (gnus-run-hooks 'gnus-exit-group-hook)
16409b0b 7000 (gnus-summary-update-info))
eec82323
LMI
7001 (gnus-close-group group)
7002 ;; Make sure where we were, and go to next newsgroup.
7003 (set-buffer gnus-group-buffer)
7004 (unless quit-config
7005 (gnus-group-jump-to-group group))
6748645f
LMI
7006 (gnus-run-hooks 'gnus-summary-exit-hook)
7007 (unless (or quit-config
01c52d31 7008 (not gnus-summary-next-group-on-exit)
6748645f
LMI
7009 ;; If this group has disappeared from the summary
7010 ;; buffer, don't skip forwards.
7011 (not (string= group (gnus-group-group-name))))
eec82323 7012 (gnus-group-next-unread-group 1))
a8151ef7 7013 (setq group-point (point))
eec82323
LMI
7014 (if temporary
7015 nil ;Nothing to do.
eec82323
LMI
7016 (set-buffer buf)
7017 (if (not gnus-kill-summary-on-exit)
23f87bed
MB
7018 (progn
7019 (gnus-deaden-summary)
7020 (setq mode nil))
eec82323
LMI
7021 ;; We set all buffer-local variables to nil. It is unclear why
7022 ;; this is needed, but if we don't, buffer-local variables are
7023 ;; not garbage-collected, it seems. This would the lead to en
7024 ;; ever-growing Emacs.
7025 (gnus-summary-clear-local-variables)
23f87bed
MB
7026 (let ((gnus-summary-local-variables gnus-newsgroup-variables))
7027 (gnus-summary-clear-local-variables))
eec82323
LMI
7028 (when (get-buffer gnus-article-buffer)
7029 (bury-buffer gnus-article-buffer))
eec82323
LMI
7030 ;; Return to group mode buffer.
7031 (when (eq mode 'gnus-summary-mode)
7032 (gnus-kill-buffer buf)))
7033 (setq gnus-current-select-method gnus-select-method)
d61c212b
SM
7034 (set-buffer gnus-group-buffer)
7035 (if quit-config
7036 (gnus-handle-ephemeral-exit quit-config)
4e90f2b9
SM
7037 (goto-char group-point)
7038 ;; If gnus-group-buffer is already displayed, make sure we also move
7039 ;; the cursor in the window that displays it.
7040 (let ((win (get-buffer-window (current-buffer) 0)))
7041 (if win (set-window-point win (point))))
d61c212b 7042 (unless leave-hidden
4e90f2b9 7043 (gnus-configure-windows 'group 'force)))
6748645f 7044 ;; Clear the current group name.
eec82323
LMI
7045 (unless quit-config
7046 (setq gnus-newsgroup-name nil)))))
7047
7048(defalias 'gnus-summary-quit 'gnus-summary-exit-no-update)
7049(defun gnus-summary-exit-no-update (&optional no-questions)
7050 "Quit reading current newsgroup without updating read article info."
7051 (interactive)
eec82323 7052 (let* ((group gnus-newsgroup-name)
23f87bed
MB
7053 (gnus-group-is-exiting-p t)
7054 (gnus-group-is-exiting-without-update-p t)
eec82323
LMI
7055 (quit-config (gnus-group-quit-config group)))
7056 (when (or no-questions
7057 gnus-expert-user
7058 (gnus-y-or-n-p "Discard changes to this group and exit? "))
6748645f 7059 (gnus-async-halt-prefetch)
23f87bed 7060 (run-hooks 'gnus-summary-prepare-exit-hook)
16409b0b
GM
7061 (when (gnus-buffer-live-p gnus-article-buffer)
7062 (save-excursion
7063 (set-buffer gnus-article-buffer)
7064 (mm-destroy-parts gnus-article-mime-handles)
7065 ;; Set it to nil for safety reason.
7066 (setq gnus-article-mime-handle-alist nil)
7067 (setq gnus-article-mime-handles nil)))
eec82323
LMI
7068 ;; If we have several article buffers, we kill them at exit.
7069 (unless gnus-single-article-buffer
7070 (gnus-kill-buffer gnus-article-buffer)
7071 (gnus-kill-buffer gnus-original-article-buffer)
7072 (setq gnus-article-current nil))
7073 (if (not gnus-kill-summary-on-exit)
7074 (gnus-deaden-summary)
7075 (gnus-close-group group)
7076 (gnus-summary-clear-local-variables)
23f87bed
MB
7077 (let ((gnus-summary-local-variables gnus-newsgroup-variables))
7078 (gnus-summary-clear-local-variables))
7079 (gnus-kill-buffer gnus-summary-buffer))
eec82323
LMI
7080 (unless gnus-single-article-buffer
7081 (setq gnus-article-current nil))
7082 (when gnus-use-trees
7083 (gnus-tree-close group))
7084 (gnus-async-prefetch-remove-group group)
7085 (when (get-buffer gnus-article-buffer)
7086 (bury-buffer gnus-article-buffer))
7087 ;; Return to the group buffer.
7088 (gnus-configure-windows 'group 'force)
7089 ;; Clear the current group name.
7090 (setq gnus-newsgroup-name nil)
23f87bed
MB
7091 (unless (gnus-ephemeral-group-p group)
7092 (gnus-group-update-group group))
eec82323
LMI
7093 (when (equal (gnus-group-group-name) group)
7094 (gnus-group-next-unread-group 1))
7095 (when quit-config
23f87bed 7096 (gnus-handle-ephemeral-exit quit-config)))))
eec82323
LMI
7097
7098(defun gnus-handle-ephemeral-exit (quit-config)
6748645f
LMI
7099 "Handle movement when leaving an ephemeral group.
7100The state which existed when entering the ephemeral is reset."
eec82323
LMI
7101 (if (not (buffer-name (car quit-config)))
7102 (gnus-configure-windows 'group 'force)
7103 (set-buffer (car quit-config))
7104 (cond ((eq major-mode 'gnus-summary-mode)
23f87bed
MB
7105 (gnus-set-global-variables))
7106 ((eq major-mode 'gnus-article-mode)
7107 (save-excursion
7108 ;; The `gnus-summary-buffer' variable may point
7109 ;; to the old summary buffer when using a single
7110 ;; article buffer.
7111 (unless (gnus-buffer-live-p gnus-summary-buffer)
7112 (set-buffer gnus-group-buffer))
7113 (set-buffer gnus-summary-buffer)
7114 (gnus-set-global-variables))))
eec82323 7115 (if (or (eq (cdr quit-config) 'article)
23f87bed 7116 (eq (cdr quit-config) 'pick))
01c52d31
MB
7117 (if (and (boundp 'gnus-pick-mode) (symbol-value 'gnus-pick-mode))
7118 (gnus-configure-windows 'pick 'force)
7119 (gnus-configure-windows (cdr quit-config) 'force))
eec82323
LMI
7120 (gnus-configure-windows (cdr quit-config) 'force))
7121 (when (eq major-mode 'gnus-summary-mode)
01c52d31
MB
7122 (if (memq gnus-auto-select-on-ephemeral-exit '(next-noselect
7123 next-unread-noselect))
7124 (when (zerop (cond ((eq gnus-auto-select-on-ephemeral-exit
7125 'next-noselect)
7126 (gnus-summary-next-subject 1 nil t))
7127 ((eq gnus-auto-select-on-ephemeral-exit
7128 'next-unread-noselect)
7129 (gnus-summary-next-subject 1 t t))))
7130 ;; Hide the article buffer which displays the article different
7131 ;; from the one that the cursor points to in the summary buffer.
7132 (gnus-configure-windows 'summary 'force))
7133 (cond ((eq gnus-auto-select-on-ephemeral-exit 'next)
7134 (gnus-summary-next-subject 1))
7135 ((eq gnus-auto-select-on-ephemeral-exit 'next-unread)
7136 (gnus-summary-next-subject 1 t))))
eec82323
LMI
7137 (gnus-summary-recenter)
7138 (gnus-summary-position-point))))
7139
7140;;; Dead summaries.
7141
7142(defvar gnus-dead-summary-mode-map nil)
7143
7144(unless gnus-dead-summary-mode-map
7145 (setq gnus-dead-summary-mode-map (make-keymap))
7146 (suppress-keymap gnus-dead-summary-mode-map)
7147 (substitute-key-definition
7148 'undefined 'gnus-summary-wake-up-the-dead gnus-dead-summary-mode-map)
23f87bed
MB
7149 (dolist (key '("\C-d" "\r" "\177" [delete]))
7150 (define-key gnus-dead-summary-mode-map
7151 key 'gnus-summary-wake-up-the-dead))
7152 (dolist (key '("q" "Q"))
7153 (define-key gnus-dead-summary-mode-map key 'bury-buffer)))
eec82323
LMI
7154
7155(defvar gnus-dead-summary-mode nil
7156 "Minor mode for Gnus summary buffers.")
7157
7158(defun gnus-dead-summary-mode (&optional arg)
7159 "Minor mode for Gnus summary buffers."
7160 (interactive "P")
7161 (when (eq major-mode 'gnus-summary-mode)
7162 (make-local-variable 'gnus-dead-summary-mode)
7163 (setq gnus-dead-summary-mode
7164 (if (null arg) (not gnus-dead-summary-mode)
7165 (> (prefix-numeric-value arg) 0)))
7166 (when gnus-dead-summary-mode
01c52d31 7167 (add-minor-mode
a8151ef7 7168 'gnus-dead-summary-mode " Dead" gnus-dead-summary-mode-map))))
eec82323
LMI
7169
7170(defun gnus-deaden-summary ()
7171 "Make the current summary buffer into a dead summary buffer."
7172 ;; Kill any previous dead summary buffer.
7173 (when (and gnus-dead-summary
7174 (buffer-name gnus-dead-summary))
01c52d31 7175 (with-current-buffer gnus-dead-summary
eec82323
LMI
7176 (when gnus-dead-summary-mode
7177 (kill-buffer (current-buffer)))))
7178 ;; Make this the current dead summary.
7179 (setq gnus-dead-summary (current-buffer))
7180 (gnus-dead-summary-mode 1)
7181 (let ((name (buffer-name)))
7182 (when (string-match "Summary" name)
7183 (rename-buffer
7184 (concat (substring name 0 (match-beginning 0)) "Dead "
7185 (substring name (match-beginning 0)))
16409b0b
GM
7186 t)
7187 (bury-buffer))))
eec82323
LMI
7188
7189(defun gnus-kill-or-deaden-summary (buffer)
7190 "Kill or deaden the summary BUFFER."
6748645f
LMI
7191 (save-excursion
7192 (when (and (buffer-name buffer)
7193 (not gnus-single-article-buffer))
01c52d31 7194 (with-current-buffer buffer
6748645f
LMI
7195 (gnus-kill-buffer gnus-article-buffer)
7196 (gnus-kill-buffer gnus-original-article-buffer)))
23f87bed
MB
7197 (cond
7198 ;; Kill the buffer.
7199 (gnus-kill-summary-on-exit
7200 (when (and gnus-use-trees
7201 (gnus-buffer-exists-p buffer))
7202 (save-excursion
7203 (set-buffer buffer)
7204 (gnus-tree-close gnus-newsgroup-name)))
7205 (gnus-kill-buffer buffer))
7206 ;; Deaden the buffer.
7207 ((gnus-buffer-exists-p buffer)
7208 (save-excursion
7209 (set-buffer buffer)
7210 (gnus-deaden-summary))))))
eec82323
LMI
7211
7212(defun gnus-summary-wake-up-the-dead (&rest args)
7213 "Wake up the dead summary buffer."
7214 (interactive)
7215 (gnus-dead-summary-mode -1)
7216 (let ((name (buffer-name)))
7217 (when (string-match "Dead " name)
7218 (rename-buffer
7219 (concat (substring name 0 (match-beginning 0))
7220 (substring name (match-end 0)))
7221 t)))
7222 (gnus-message 3 "This dead summary is now alive again"))
7223
7224;; Suggested by Andrew Eskilsson <pi92ae@pt.hk-r.se>.
7225(defun gnus-summary-fetch-faq (&optional faq-dir)
7226 "Fetch the FAQ for the current group.
7227If FAQ-DIR (the prefix), prompt for a directory to search for the faq
7228in."
7229 (interactive
7230 (list
7231 (when current-prefix-arg
7232 (completing-read
8f688cb0 7233 "FAQ dir: " (and (listp gnus-group-faq-directory)
01c52d31 7234 (mapcar 'list
a8151ef7 7235 gnus-group-faq-directory))))))
eec82323
LMI
7236 (let (gnus-faq-buffer)
7237 (when (setq gnus-faq-buffer
7238 (gnus-group-fetch-faq gnus-newsgroup-name faq-dir))
7239 (gnus-configure-windows 'summary-faq))))
7240
7241;; Suggested by Per Abrahamsen <amanda@iesd.auc.dk>.
7242(defun gnus-summary-describe-group (&optional force)
7243 "Describe the current newsgroup."
7244 (interactive "P")
7245 (gnus-group-describe-group force gnus-newsgroup-name))
7246
7247(defun gnus-summary-describe-briefly ()
7248 "Describe summary mode commands briefly."
7249 (interactive)
16409b0b 7250 (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
7251
7252;; Walking around group mode buffer from summary mode.
7253
7254(defun gnus-summary-next-group (&optional no-article target-group backward)
7255 "Exit current newsgroup and then select next unread newsgroup.
7256If prefix argument NO-ARTICLE is non-nil, no article is selected
23f87bed 7257initially. If TARGET-GROUP, go to this group. If BACKWARD, go to
eec82323
LMI
7258previous group instead."
7259 (interactive "P")
eec82323
LMI
7260 ;; Stop pre-fetching.
7261 (gnus-async-halt-prefetch)
7262 (let ((current-group gnus-newsgroup-name)
7263 (current-buffer (current-buffer))
7264 entered)
7265 ;; First we semi-exit this group to update Xrefs and all variables.
7266 ;; We can't do a real exit, because the window conf must remain
7267 ;; the same in case the user is prompted for info, and we don't
7268 ;; want the window conf to change before that...
7269 (gnus-summary-exit t)
7270 (while (not entered)
7271 ;; Then we find what group we are supposed to enter.
7272 (set-buffer gnus-group-buffer)
7273 (gnus-group-jump-to-group current-group)
7274 (setq target-group
7275 (or target-group
7276 (if (eq gnus-keep-same-level 'best)
7277 (gnus-summary-best-group gnus-newsgroup-name)
7278 (gnus-summary-search-group backward gnus-keep-same-level))))
7279 (if (not target-group)
7280 ;; There are no further groups, so we return to the group
7281 ;; buffer.
7282 (progn
7283 (gnus-message 5 "Returning to the group buffer")
7284 (setq entered t)
7285 (when (gnus-buffer-live-p current-buffer)
7286 (set-buffer current-buffer)
7287 (gnus-summary-exit))
6748645f 7288 (gnus-run-hooks 'gnus-group-no-more-groups-hook))
eec82323
LMI
7289 ;; We try to enter the target group.
7290 (gnus-group-jump-to-group target-group)
7291 (let ((unreads (gnus-group-group-unread)))
7292 (if (and (or (eq t unreads)
7293 (and unreads (not (zerop unreads))))
23f87bed
MB
7294 (gnus-summary-read-group
7295 target-group nil no-article
7296 (and (buffer-name current-buffer) current-buffer)
7297 nil backward))
eec82323
LMI
7298 (setq entered t)
7299 (setq current-group target-group
7300 target-group nil)))))))
7301
7302(defun gnus-summary-prev-group (&optional no-article)
7303 "Exit current newsgroup and then select previous unread newsgroup.
7304If prefix argument NO-ARTICLE is non-nil, no article is selected initially."
7305 (interactive "P")
7306 (gnus-summary-next-group no-article nil t))
7307
7308;; Walking around summary lines.
7309
23f87bed
MB
7310(defun gnus-summary-first-subject (&optional unread undownloaded unseen)
7311 "Go to the first subject satisfying any non-nil constraint.
7312If UNREAD is non-nil, the article should be unread.
7313If UNDOWNLOADED is non-nil, the article should be undownloaded.
7314If UNSEEN is non-nil, the article should be unseen.
7315Returns the article selected or nil if there are no matching articles."
eec82323 7316 (interactive "P")
23f87bed
MB
7317 (cond
7318 ;; Empty summary.
7319 ((null gnus-newsgroup-data)
7320 (gnus-message 3 "No articles in the group")
7321 nil)
7322 ;; Pick the first article.
7323 ((not (or unread undownloaded unseen))
7324 (goto-char (gnus-data-pos (car gnus-newsgroup-data)))
7325 (gnus-data-number (car gnus-newsgroup-data)))
7326 ;; Find the first unread article.
7327 (t
7328 (let ((data gnus-newsgroup-data))
7329 (while (and data
7330 (let ((num (gnus-data-number (car data))))
7331 (or (memq num gnus-newsgroup-unfetched)
7332 (not (or (and unread
7333 (memq num gnus-newsgroup-unreads))
7334 (and undownloaded
7335 (memq num gnus-newsgroup-undownloaded))
7336 (and unseen
7337 (memq num gnus-newsgroup-unseen)))))))
7338 (setq data (cdr data)))
7339 (prog1
7340 (if data
7341 (progn
7342 (goto-char (gnus-data-pos (car data)))
7343 (gnus-data-number (car data)))
7344 (gnus-message 3 "No more%s articles"
7345 (let* ((r (when unread " unread"))
7346 (d (when undownloaded " undownloaded"))
7347 (s (when unseen " unseen"))
7348 (l (delq nil (list r d s))))
7349 (cond ((= 3 (length l))
7350 (concat r "," d ", or" s))
7351 ((= 2 (length l))
7352 (concat (car l) ", or" (cadr l)))
7353 ((= 1 (length l))
7354 (car l))
7355 (t
7356 ""))))
7357 nil
7358 )
7359 (gnus-summary-position-point))))))
eec82323
LMI
7360
7361(defun gnus-summary-next-subject (n &optional unread dont-display)
7362 "Go to next N'th summary line.
7363If N is negative, go to the previous N'th subject line.
7364If UNREAD is non-nil, only unread articles are selected.
7365The difference between N and the actual number of steps taken is
7366returned."
7367 (interactive "p")
7368 (let ((backward (< n 0))
7369 (n (abs n)))
7370 (while (and (> n 0)
7371 (if backward
7372 (gnus-summary-find-prev unread)
7373 (gnus-summary-find-next unread)))
16409b0b
GM
7374 (unless (zerop (setq n (1- n)))
7375 (gnus-summary-show-thread)))
eec82323
LMI
7376 (when (/= 0 n)
7377 (gnus-message 7 "No more%s articles"
7378 (if unread " unread" "")))
7379 (unless dont-display
7380 (gnus-summary-recenter)
7381 (gnus-summary-position-point))
7382 n))
7383
7384(defun gnus-summary-next-unread-subject (n)
7385 "Go to next N'th unread summary line."
7386 (interactive "p")
7387 (gnus-summary-next-subject n t))
7388
7389(defun gnus-summary-prev-subject (n &optional unread)
7390 "Go to previous N'th summary line.
7391If optional argument UNREAD is non-nil, only unread article is selected."
7392 (interactive "p")
7393 (gnus-summary-next-subject (- n) unread))
7394
7395(defun gnus-summary-prev-unread-subject (n)
7396 "Go to previous N'th unread summary line."
7397 (interactive "p")
7398 (gnus-summary-next-subject (- n) t))
7399
23f87bed
MB
7400(defun gnus-summary-goto-subjects (articles)
7401 "Insert the subject header for ARTICLES in the current buffer."
7402 (save-excursion
7403 (dolist (article articles)
7404 (gnus-summary-goto-subject article t)))
7405 (gnus-summary-limit (append articles gnus-newsgroup-limit))
7406 (gnus-summary-position-point))
132cf96d 7407
eec82323
LMI
7408(defun gnus-summary-goto-subject (article &optional force silent)
7409 "Go the subject line of ARTICLE.
7410If FORCE, also allow jumping to articles not currently shown."
7411 (interactive "nArticle number: ")
23f87bed
MB
7412 (unless (numberp article)
7413 (error "Article %s is not a number" article))
eec82323
LMI
7414 (let ((b (point))
7415 (data (gnus-data-find article)))
7416 ;; We read in the article if we have to.
7417 (and (not data)
7418 force
6748645f
LMI
7419 (gnus-summary-insert-subject
7420 article
7421 (if (or (numberp force) (vectorp force)) force)
7422 t)
eec82323
LMI
7423 (setq data (gnus-data-find article)))
7424 (goto-char b)
7425 (if (not data)
7426 (progn
7427 (unless silent
7428 (gnus-message 3 "Can't find article %d" article))
7429 nil)
23f87bed
MB
7430 (let ((pt (gnus-data-pos data)))
7431 (goto-char pt)
7432 (gnus-summary-set-article-display-arrow pt))
6748645f 7433 (gnus-summary-position-point)
eec82323
LMI
7434 article)))
7435
7436;; Walking around summary lines with displaying articles.
7437
7438(defun gnus-summary-expand-window (&optional arg)
7439 "Make the summary buffer take up the entire Emacs frame.
7440Given a prefix, will force an `article' buffer configuration."
7441 (interactive "P")
eec82323
LMI
7442 (if arg
7443 (gnus-configure-windows 'article 'force)
7444 (gnus-configure-windows 'summary 'force)))
7445
7446(defun gnus-summary-display-article (article &optional all-header)
7447 "Display ARTICLE in article buffer."
01c52d31
MB
7448 (unless (and (gnus-buffer-live-p gnus-article-buffer)
7449 (with-current-buffer gnus-article-buffer
7450 (eq major-mode 'gnus-article-mode)))
7451 (gnus-article-setup-buffer))
eec82323 7452 (gnus-set-global-variables)
01c52d31
MB
7453 (with-current-buffer gnus-article-buffer
7454 (setq gnus-article-charset gnus-newsgroup-charset)
7455 (setq gnus-article-ignored-charsets gnus-newsgroup-ignored-charsets)
7456 (mm-enable-multibyte))
eec82323
LMI
7457 (if (null article)
7458 nil
7459 (prog1
7460 (if gnus-summary-display-article-function
7461 (funcall gnus-summary-display-article-function article all-header)
7462 (gnus-article-prepare article all-header))
6748645f 7463 (gnus-run-hooks 'gnus-select-article-hook)
eec82323
LMI
7464 (when (and gnus-current-article
7465 (not (zerop gnus-current-article)))
7466 (gnus-summary-goto-subject gnus-current-article))
7467 (gnus-summary-recenter)
7468 (when (and gnus-use-trees gnus-show-threads)
7469 (gnus-possibly-generate-tree article)
7470 (gnus-highlight-selected-tree article))
7471 ;; Successfully display article.
7472 (gnus-article-set-window-start
7473 (cdr (assq article gnus-newsgroup-bookmarks))))))
7474
7475(defun gnus-summary-select-article (&optional all-headers force pseudo article)
7476 "Select the current article.
7477If ALL-HEADERS is non-nil, show all header fields. If FORCE is
7478non-nil, the article will be re-fetched even if it already present in
7479the article buffer. If PSEUDO is non-nil, pseudo-articles will also
7480be displayed."
7481 ;; Make sure we are in the summary buffer to work around bbdb bug.
7482 (unless (eq major-mode 'gnus-summary-mode)
7483 (set-buffer gnus-summary-buffer))
7484 (let ((article (or article (gnus-summary-article-number)))
f0529b5b 7485 (all-headers (not (not all-headers))) ;Must be t or nil.
16409b0b 7486 gnus-summary-display-article-function)
eec82323
LMI
7487 (and (not pseudo)
7488 (gnus-summary-article-pseudo-p article)
a8151ef7 7489 (error "This is a pseudo-article"))
16409b0b
GM
7490 (save-excursion
7491 (set-buffer gnus-summary-buffer)
7492 (if (or (and gnus-single-article-buffer
7493 (or (null gnus-current-article)
7494 (null gnus-article-current)
7495 (null (get-buffer gnus-article-buffer))
7496 (not (eq article (cdr gnus-article-current)))
7497 (not (equal (car gnus-article-current)
7498 gnus-newsgroup-name))))
7499 (and (not gnus-single-article-buffer)
7500 (or (null gnus-current-article)
7501 (not (eq gnus-current-article article))))
7502 force)
7503 ;; The requested article is different from the current article.
7504 (progn
16409b0b
GM
7505 (gnus-summary-display-article article all-headers)
7506 (when (gnus-buffer-live-p gnus-article-buffer)
23f87bed 7507 (with-current-buffer gnus-article-buffer
16409b0b 7508 (if (not gnus-article-decoded-p) ;; a local variable
87545352 7509 (mm-disable-multibyte))))
16409b0b
GM
7510 (gnus-article-set-window-start
7511 (cdr (assq article gnus-newsgroup-bookmarks)))
7512 article)
16409b0b 7513 'old))))
eec82323 7514
23f87bed
MB
7515(defun gnus-summary-force-verify-and-decrypt ()
7516 "Display buttons for signed/encrypted parts and verify/decrypt them."
7517 (interactive)
7518 (let ((mm-verify-option 'known)
7519 (mm-decrypt-option 'known)
7520 (gnus-article-emulate-mime t)
7521 (gnus-buttonized-mime-types (append (list "multipart/signed"
7522 "multipart/encrypted")
7523 gnus-buttonized-mime-types)))
7524 (gnus-summary-select-article nil 'force)))
7525
eec82323
LMI
7526(defun gnus-summary-set-current-mark (&optional current-mark)
7527 "Obsolete function."
7528 nil)
7529
7530(defun gnus-summary-next-article (&optional unread subject backward push)
7531 "Select the next article.
7532If UNREAD, only unread articles are selected.
7533If SUBJECT, only articles with SUBJECT are selected.
7534If BACKWARD, the previous article is selected instead of the next."
7535 (interactive "P")
11e95b02
MB
7536 ;; Make sure we are in the summary buffer.
7537 (unless (eq major-mode 'gnus-summary-mode)
7538 (set-buffer gnus-summary-buffer))
eec82323
LMI
7539 (cond
7540 ;; Is there such an article?
7541 ((and (gnus-summary-search-forward unread subject backward)
7542 (or (gnus-summary-display-article (gnus-summary-article-number))
7543 (eq (gnus-summary-article-mark) gnus-canceled-mark)))
7544 (gnus-summary-position-point))
7545 ;; If not, we try the first unread, if that is wanted.
7546 ((and subject
7547 gnus-auto-select-same
7548 (gnus-summary-first-unread-article))
7549 (gnus-summary-position-point)
7550 (gnus-message 6 "Wrapped"))
7551 ;; Try to get next/previous article not displayed in this group.
7552 ((and gnus-auto-extend-newsgroup
7553 (not unread) (not subject))
7554 (gnus-summary-goto-article
7555 (if backward (1- gnus-newsgroup-begin) (1+ gnus-newsgroup-end))
6748645f 7556 nil (count-lines (point-min) (point))))
eec82323
LMI
7557 ;; Go to next/previous group.
7558 (t
7559 (unless (gnus-ephemeral-group-p gnus-newsgroup-name)
7560 (gnus-summary-jump-to-group gnus-newsgroup-name))
7561 (let ((cmd last-command-char)
7562 (point
01c52d31 7563 (with-current-buffer gnus-group-buffer
eec82323
LMI
7564 (point)))
7565 (group
7566 (if (eq gnus-keep-same-level 'best)
7567 (gnus-summary-best-group gnus-newsgroup-name)
7568 (gnus-summary-search-group backward gnus-keep-same-level))))
7569 ;; For some reason, the group window gets selected. We change
7570 ;; it back.
7571 (select-window (get-buffer-window (current-buffer)))
7572 ;; Select next unread newsgroup automagically.
7573 (cond
7574 ((or (not gnus-auto-select-next)
7575 (not cmd))
7576 (gnus-message 7 "No more%s articles" (if unread " unread" "")))
7577 ((or (eq gnus-auto-select-next 'quietly)
7578 (and (eq gnus-auto-select-next 'slightly-quietly)
7579 push)
7580 (and (eq gnus-auto-select-next 'almost-quietly)
7581 (gnus-summary-last-article-p)))
7582 ;; Select quietly.
7583 (if (gnus-ephemeral-group-p gnus-newsgroup-name)
7584 (gnus-summary-exit)
7585 (gnus-message 7 "No more%s articles (%s)..."
7586 (if unread " unread" "")
7587 (if group (concat "selecting " group)
7588 "exiting"))
7589 (gnus-summary-next-group nil group backward)))
7590 (t
7591 (when (gnus-key-press-event-p last-input-event)
7592 (gnus-summary-walk-group-buffer
7593 gnus-newsgroup-name cmd unread backward point))))))))
7594
7595(defun gnus-summary-walk-group-buffer (from-group cmd unread backward start)
7596 (let ((keystrokes '((?\C-n (gnus-group-next-unread-group 1))
7597 (?\C-p (gnus-group-prev-unread-group 1))))
7598 (cursor-in-echo-area t)
23f87bed 7599 keve key group ended prompt)
eec82323
LMI
7600 (save-excursion
7601 (set-buffer gnus-group-buffer)
7602 (goto-char start)
7603 (setq group
7604 (if (eq gnus-keep-same-level 'best)
7605 (gnus-summary-best-group gnus-newsgroup-name)
7606 (gnus-summary-search-group backward gnus-keep-same-level))))
7607 (while (not ended)
23f87bed
MB
7608 (setq prompt
7609 (format
7610 "No more%s articles%s " (if unread " unread" "")
7611 (if (and group
7612 (not (gnus-ephemeral-group-p gnus-newsgroup-name)))
7613 (format " (Type %s for %s [%s])"
91472578
MB
7614 (single-key-description cmd)
7615 (gnus-group-decoded-name group)
01c52d31 7616 (gnus-group-unread group))
23f87bed
MB
7617 (format " (Type %s to exit %s)"
7618 (single-key-description cmd)
91472578 7619 (gnus-group-decoded-name gnus-newsgroup-name)))))
eec82323 7620 ;; Confirm auto selection.
23f87bed
MB
7621 (setq key (car (setq keve (gnus-read-event-char prompt)))
7622 ended t)
eec82323
LMI
7623 (cond
7624 ((assq key keystrokes)
7625 (let ((obuf (current-buffer)))
7626 (switch-to-buffer gnus-group-buffer)
7627 (when group
7628 (gnus-group-jump-to-group group))
7629 (eval (cadr (assq key keystrokes)))
7630 (setq group (gnus-group-group-name))
7631 (switch-to-buffer obuf))
7632 (setq ended nil))
7633 ((equal key cmd)
7634 (if (or (not group)
7635 (gnus-ephemeral-group-p gnus-newsgroup-name))
7636 (gnus-summary-exit)
7637 (gnus-summary-next-group nil group backward)))
7638 (t
7639 (push (cdr keve) unread-command-events))))))
7640
7641(defun gnus-summary-next-unread-article ()
7642 "Select unread article after current one."
7643 (interactive)
7644 (gnus-summary-next-article
7645 (or (not (eq gnus-summary-goto-unread 'never))
7646 (gnus-summary-last-article-p (gnus-summary-article-number)))
7647 (and gnus-auto-select-same
7648 (gnus-summary-article-subject))))
7649
7650(defun gnus-summary-prev-article (&optional unread subject)
7651 "Select the article after the current one.
7652If UNREAD is non-nil, only unread articles are selected."
7653 (interactive "P")
7654 (gnus-summary-next-article unread subject t))
7655
7656(defun gnus-summary-prev-unread-article ()
7657 "Select unread article before current one."
7658 (interactive)
7659 (gnus-summary-prev-article
7660 (or (not (eq gnus-summary-goto-unread 'never))
7661 (gnus-summary-first-article-p (gnus-summary-article-number)))
7662 (and gnus-auto-select-same
7663 (gnus-summary-article-subject))))
7664
23f87bed 7665(defun gnus-summary-next-page (&optional lines circular stop)
eec82323
LMI
7666 "Show next page of the selected article.
7667If at the end of the current article, select the next article.
7668LINES says how many lines should be scrolled up.
7669
7670If CIRCULAR is non-nil, go to the start of the article instead of
7671selecting the next article when reaching the end of the current
23f87bed
MB
7672article.
7673
7674If STOP is non-nil, just stop when reaching the end of the message.
7675
7676Also see the variable `gnus-article-skip-boring'."
eec82323
LMI
7677 (interactive "P")
7678 (setq gnus-summary-buffer (current-buffer))
7679 (gnus-set-global-variables)
7680 (let ((article (gnus-summary-article-number))
7681 (article-window (get-buffer-window gnus-article-buffer t))
7682 endp)
6748645f
LMI
7683 ;; If the buffer is empty, we have no article.
7684 (unless article
7685 (error "No article to select"))
eec82323
LMI
7686 (gnus-configure-windows 'article)
7687 (if (eq (cdr (assq article gnus-newsgroup-reads)) gnus-canceled-mark)
7688 (if (and (eq gnus-summary-goto-unread 'never)
7689 (not (gnus-summary-last-article-p article)))
7690 (gnus-summary-next-article)
7691 (gnus-summary-next-unread-article))
7692 (if (or (null gnus-current-article)
7693 (null gnus-article-current)
7694 (/= article (cdr gnus-article-current))
7695 (not (equal (car gnus-article-current) gnus-newsgroup-name)))
7696 ;; Selected subject is different from current article's.
7697 (gnus-summary-display-article article)
7698 (when article-window
7699 (gnus-eval-in-buffer-window gnus-article-buffer
23f87bed
MB
7700 (setq endp (or (gnus-article-next-page lines)
7701 (gnus-article-only-boring-p))))
eec82323 7702 (when endp
23f87bed
MB
7703 (cond (stop
7704 (gnus-message 3 "End of message"))
7705 (circular
eec82323
LMI
7706 (gnus-summary-beginning-of-article))
7707 (lines
7708 (gnus-message 3 "End of message"))
7709 ((null lines)
7710 (if (and (eq gnus-summary-goto-unread 'never)
7711 (not (gnus-summary-last-article-p article)))
7712 (gnus-summary-next-article)
7713 (gnus-summary-next-unread-article))))))))
7714 (gnus-summary-recenter)
7715 (gnus-summary-position-point)))
7716
7717(defun gnus-summary-prev-page (&optional lines move)
7718 "Show previous page of selected article.
7719Argument LINES specifies lines to be scrolled down.
7720If MOVE, move to the previous unread article if point is at
7721the beginning of the buffer."
7722 (interactive "P")
eec82323
LMI
7723 (let ((article (gnus-summary-article-number))
7724 (article-window (get-buffer-window gnus-article-buffer t))
7725 endp)
7726 (gnus-configure-windows 'article)
7727 (if (or (null gnus-current-article)
7728 (null gnus-article-current)
7729 (/= article (cdr gnus-article-current))
7730 (not (equal (car gnus-article-current) gnus-newsgroup-name)))
7731 ;; Selected subject is different from current article's.
7732 (gnus-summary-display-article article)
7733 (gnus-summary-recenter)
7734 (when article-window
7735 (gnus-eval-in-buffer-window gnus-article-buffer
7736 (setq endp (gnus-article-prev-page lines)))
7737 (when (and move endp)
7738 (cond (lines
7739 (gnus-message 3 "Beginning of message"))
7740 ((null lines)
7741 (if (and (eq gnus-summary-goto-unread 'never)
7742 (not (gnus-summary-first-article-p article)))
7743 (gnus-summary-prev-article)
7744 (gnus-summary-prev-unread-article))))))))
7745 (gnus-summary-position-point))
7746
7747(defun gnus-summary-prev-page-or-article (&optional lines)
7748 "Show previous page of selected article.
7749Argument LINES specifies lines to be scrolled down.
7750If at the beginning of the article, go to the next article."
7751 (interactive "P")
7752 (gnus-summary-prev-page lines t))
7753
7754(defun gnus-summary-scroll-up (lines)
7755 "Scroll up (or down) one line current article.
7756Argument LINES specifies lines to be scrolled up (or down if negative)."
7757 (interactive "p")
eec82323
LMI
7758 (gnus-configure-windows 'article)
7759 (gnus-summary-show-thread)
7760 (when (eq (gnus-summary-select-article nil nil 'pseudo) 'old)
7761 (gnus-eval-in-buffer-window gnus-article-buffer
7762 (cond ((> lines 0)
7763 (when (gnus-article-next-page lines)
7764 (gnus-message 3 "End of message")))
7765 ((< lines 0)
7766 (gnus-article-prev-page (- lines))))))
7767 (gnus-summary-recenter)
7768 (gnus-summary-position-point))
7769
6748645f
LMI
7770(defun gnus-summary-scroll-down (lines)
7771 "Scroll down (or up) one line current article.
7772Argument LINES specifies lines to be scrolled down (or up if negative)."
7773 (interactive "p")
7774 (gnus-summary-scroll-up (- lines)))
7775
eec82323
LMI
7776(defun gnus-summary-next-same-subject ()
7777 "Select next article which has the same subject as current one."
7778 (interactive)
eec82323
LMI
7779 (gnus-summary-next-article nil (gnus-summary-article-subject)))
7780
7781(defun gnus-summary-prev-same-subject ()
7782 "Select previous article which has the same subject as current one."
7783 (interactive)
eec82323
LMI
7784 (gnus-summary-prev-article nil (gnus-summary-article-subject)))
7785
7786(defun gnus-summary-next-unread-same-subject ()
7787 "Select next unread article which has the same subject as current one."
7788 (interactive)
eec82323
LMI
7789 (gnus-summary-next-article t (gnus-summary-article-subject)))
7790
7791(defun gnus-summary-prev-unread-same-subject ()
7792 "Select previous unread article which has the same subject as current one."
7793 (interactive)
eec82323
LMI
7794 (gnus-summary-prev-article t (gnus-summary-article-subject)))
7795
7796(defun gnus-summary-first-unread-article ()
7797 "Select the first unread article.
7798Return nil if there are no unread articles."
7799 (interactive)
eec82323
LMI
7800 (prog1
7801 (when (gnus-summary-first-subject t)
7802 (gnus-summary-show-thread)
7803 (gnus-summary-first-subject t)
7804 (gnus-summary-display-article (gnus-summary-article-number)))
7805 (gnus-summary-position-point)))
7806
16409b0b
GM
7807(defun gnus-summary-first-unread-subject ()
7808 "Place the point on the subject line of the first unread article.
7809Return nil if there are no unread articles."
7810 (interactive)
7811 (prog1
7812 (when (gnus-summary-first-subject t)
7813 (gnus-summary-show-thread)
7814 (gnus-summary-first-subject t))
7815 (gnus-summary-position-point)))
7816
23f87bed
MB
7817(defun gnus-summary-first-unseen-subject ()
7818 "Place the point on the subject line of the first unseen article.
7819Return nil if there are no unseen articles."
7820 (interactive)
7821 (prog1
7822 (when (gnus-summary-first-subject nil nil t)
7823 (gnus-summary-show-thread)
7824 (gnus-summary-first-subject nil nil t))
7825 (gnus-summary-position-point)))
7826
7827(defun gnus-summary-first-unseen-or-unread-subject ()
7828 "Place the point on the subject line of the first unseen article or,
7829if all article have been seen, on the subject line of the first unread
7830article."
7831 (interactive)
7832 (prog1
7833 (unless (when (gnus-summary-first-subject nil nil t)
7834 (gnus-summary-show-thread)
7835 (gnus-summary-first-subject nil nil t))
7836 (when (gnus-summary-first-subject t)
7837 (gnus-summary-show-thread)
7838 (gnus-summary-first-subject t)))
7839 (gnus-summary-position-point)))
7840
eec82323
LMI
7841(defun gnus-summary-first-article ()
7842 "Select the first article.
7843Return nil if there are no articles."
7844 (interactive)
eec82323
LMI
7845 (prog1
7846 (when (gnus-summary-first-subject)
16409b0b
GM
7847 (gnus-summary-show-thread)
7848 (gnus-summary-first-subject)
7849 (gnus-summary-display-article (gnus-summary-article-number)))
eec82323
LMI
7850 (gnus-summary-position-point)))
7851
23f87bed
MB
7852(defun gnus-summary-best-unread-article (&optional arg)
7853 "Select the unread article with the highest score.
7854If given a prefix argument, select the next unread article that has a
7855score higher than the default score."
7856 (interactive "P")
7857 (let ((article (if arg
7858 (gnus-summary-better-unread-subject)
7859 (gnus-summary-best-unread-subject))))
7860 (if article
7861 (gnus-summary-goto-article article)
7862 (error "No unread articles"))))
7863
7864(defun gnus-summary-best-unread-subject ()
7865 "Select the unread subject with the highest score."
eec82323 7866 (interactive)
eec82323
LMI
7867 (let ((best -1000000)
7868 (data gnus-newsgroup-data)
7869 article score)
7870 (while data
7871 (and (gnus-data-unread-p (car data))
7872 (> (setq score
7873 (gnus-summary-article-score (gnus-data-number (car data))))
7874 best)
7875 (setq best score
7876 article (gnus-data-number (car data))))
7877 (setq data (cdr data)))
23f87bed
MB
7878 (when article
7879 (gnus-summary-goto-subject article))
7880 (gnus-summary-position-point)
7881 article))
7882
7883(defun gnus-summary-better-unread-subject ()
7884 "Select the first unread subject that has a score over the default score."
7885 (interactive)
7886 (let ((data gnus-newsgroup-data)
7887 article score)
7888 (while (and (setq article (gnus-data-number (car data)))
7889 (or (gnus-data-read-p (car data))
7890 (not (> (gnus-summary-article-score article)
7891 gnus-summary-default-score))))
7892 (setq data (cdr data)))
7893 (when article
7894 (gnus-summary-goto-subject article))
7895 (gnus-summary-position-point)
7896 article))
eec82323
LMI
7897
7898(defun gnus-summary-last-subject ()
7899 "Go to the last displayed subject line in the group."
7900 (let ((article (gnus-data-number (car (gnus-data-list t)))))
7901 (when article
7902 (gnus-summary-goto-subject article))))
7903
7904(defun gnus-summary-goto-article (article &optional all-headers force)
6748645f
LMI
7905 "Fetch ARTICLE (article number or Message-ID) and display it if it exists.
7906If ALL-HEADERS is non-nil, no header lines are hidden.
7907If FORCE, go to the article even if it isn't displayed. If FORCE
7908is a number, it is the line the article is to be displayed on."
eec82323
LMI
7909 (interactive
7910 (list
6748645f
LMI
7911 (completing-read
7912 "Article number or Message-ID: "
7913 (mapcar (lambda (number) (list (int-to-string number)))
7914 gnus-newsgroup-limit))
eec82323
LMI
7915 current-prefix-arg
7916 t))
7917 (prog1
6748645f 7918 (if (and (stringp article)
23f87bed 7919 (string-match "@\\|%40" article))
6748645f
LMI
7920 (gnus-summary-refer-article article)
7921 (when (stringp article)
7922 (setq article (string-to-number article)))
7923 (if (gnus-summary-goto-subject article force)
7924 (gnus-summary-display-article article all-headers)
7925 (gnus-message 4 "Couldn't go to article %s" article) nil))
eec82323
LMI
7926 (gnus-summary-position-point)))
7927
7928(defun gnus-summary-goto-last-article ()
7929 "Go to the previously read article."
7930 (interactive)
7931 (prog1
7932 (when gnus-last-article
6748645f 7933 (gnus-summary-goto-article gnus-last-article nil t))
eec82323
LMI
7934 (gnus-summary-position-point)))
7935
7936(defun gnus-summary-pop-article (number)
7937 "Pop one article off the history and go to the previous.
7938NUMBER articles will be popped off."
7939 (interactive "p")
7940 (let (to)
7941 (setq gnus-newsgroup-history
7942 (cdr (setq to (nthcdr number gnus-newsgroup-history))))
7943 (if to
6748645f 7944 (gnus-summary-goto-article (car to) nil t)
eec82323
LMI
7945 (error "Article history empty")))
7946 (gnus-summary-position-point))
7947
7948;; Summary commands and functions for limiting the summary buffer.
7949
7950(defun gnus-summary-limit-to-articles (n)
7951 "Limit the summary buffer to the next N articles.
7952If not given a prefix, use the process marked articles instead."
7953 (interactive "P")
eec82323
LMI
7954 (prog1
7955 (let ((articles (gnus-summary-work-articles n)))
7956 (setq gnus-newsgroup-processable nil)
7957 (gnus-summary-limit articles))
7958 (gnus-summary-position-point)))
7959
7960(defun gnus-summary-pop-limit (&optional total)
7961 "Restore the previous limit.
7962If given a prefix, remove all limits."
7963 (interactive "P")
eec82323
LMI
7964 (when total
7965 (setq gnus-newsgroup-limits
7966 (list (mapcar (lambda (h) (mail-header-number h))
7967 gnus-newsgroup-headers))))
7968 (unless gnus-newsgroup-limits
7969 (error "No limit to pop"))
7970 (prog1
7971 (gnus-summary-limit nil 'pop)
7972 (gnus-summary-position-point)))
7973
47b63dfa
SZ
7974(defun gnus-summary-limit-to-subject (subject &optional header not-matching)
7975 "Limit the summary buffer to articles that have subjects that match a regexp.
7976If NOT-MATCHING, excluding articles that have subjects that match a regexp."
a1506d29 7977 (interactive
47b63dfa
SZ
7978 (list (read-string (if current-prefix-arg
7979 "Exclude subject (regexp): "
a1506d29 7980 "Limit to subject (regexp): "))
47b63dfa 7981 nil current-prefix-arg))
eec82323
LMI
7982 (unless header
7983 (setq header "subject"))
7984 (when (not (equal "" subject))
7985 (prog1
7986 (let ((articles (gnus-summary-find-matching
a1506d29 7987 (or header "subject") subject 'all nil nil
47b63dfa 7988 not-matching)))
eec82323
LMI
7989 (unless articles
7990 (error "Found no matches for \"%s\"" subject))
7991 (gnus-summary-limit articles))
7992 (gnus-summary-position-point))))
7993
ef6e0ec7 7994(defun gnus-summary-limit-to-author (from &optional not-matching)
47b63dfa
SZ
7995 "Limit the summary buffer to articles that have authors that match a regexp.
7996If NOT-MATCHING, excluding articles that have authors that match a regexp."
a1506d29 7997 (interactive
47b63dfa
SZ
7998 (list (read-string (if current-prefix-arg
7999 "Exclude author (regexp): "
a1506d29 8000 "Limit to author (regexp): "))
ef6e0ec7
SZ
8001 current-prefix-arg))
8002 (gnus-summary-limit-to-subject from "from" not-matching))
eec82323 8003
01c52d31
MB
8004(defun gnus-summary-limit-to-recipient (recipient &optional not-matching)
8005 "Limit the summary buffer to articles with the given RECIPIENT.
8006
8007If NOT-MATCHING, exclude RECIPIENT.
8008
8009To and Cc headers are checked. You need to include them in
8010`nnmail-extra-headers'."
8011 ;; Unlike `rmail-summary-by-recipients', doesn't include From.
8012 (interactive
8013 (list (read-string (format "%s recipient (regexp): "
8014 (if current-prefix-arg "Exclude" "Limit to")))
8015 current-prefix-arg))
8016 (when (not (equal "" recipient))
8017 (prog1 (let* ((to
8018 (if (memq 'To nnmail-extra-headers)
8019 (gnus-summary-find-matching
8020 (cons 'extra 'To) recipient 'all nil nil
8021 not-matching)
8022 (gnus-message
8023 1 "`To' isn't present in `nnmail-extra-headers'")
8024 (sit-for 1)
8025 nil))
8026 (cc
8027 (if (memq 'Cc nnmail-extra-headers)
8028 (gnus-summary-find-matching
8029 (cons 'extra 'Cc) recipient 'all nil nil
8030 not-matching)
8031 (gnus-message
8032 1 "`Cc' isn't present in `nnmail-extra-headers'")
8033 (sit-for 1)
8034 nil))
8035 (articles
8036 (if not-matching
8037 ;; We need the numbers that are in both lists:
8038 (mapcar (lambda (a)
8039 (and (memq a to) a))
8040 cc)
8041 (nconc to cc))))
8042 (unless articles
8043 (error "Found no matches for \"%s\"" recipient))
8044 (gnus-summary-limit articles))
8045 (gnus-summary-position-point))))
8046
8047(defun gnus-summary-limit-to-address (address &optional not-matching)
8048 "Limit the summary buffer to articles with the given ADDRESS.
8049
8050If NOT-MATCHING, exclude ADDRESS.
8051
8052To, Cc and From headers are checked. You need to include `To' and `Cc'
8053in `nnmail-extra-headers'."
8054 (interactive
8055 (list (read-string (format "%s address (regexp): "
8056 (if current-prefix-arg "Exclude" "Limit to")))
8057 current-prefix-arg))
8058 (when (not (equal "" address))
8059 (prog1 (let* ((to
8060 (if (memq 'To nnmail-extra-headers)
8061 (gnus-summary-find-matching
8062 (cons 'extra 'To) address 'all nil nil
8063 not-matching)
8064 (gnus-message
8065 1 "`To' isn't present in `nnmail-extra-headers'")
8066 (sit-for 1)
8067 t))
8068 (cc
8069 (if (memq 'Cc nnmail-extra-headers)
8070 (gnus-summary-find-matching
8071 (cons 'extra 'Cc) address 'all nil nil
8072 not-matching)
8073 (gnus-message
8074 1 "`Cc' isn't present in `nnmail-extra-headers'")
8075 (sit-for 1)
8076 t))
8077 (from
8078 (gnus-summary-find-matching "from" address
8079 'all nil nil not-matching))
8080 (articles
8081 (if not-matching
8082 ;; We need the numbers that are in all lists:
8083 (if (eq cc t)
8084 (if (eq to t)
8085 from
8086 (mapcar (lambda (a) (car (memq a from))) to))
8087 (if (eq to t)
8088 (mapcar (lambda (a) (car (memq a from))) cc)
8089 (mapcar (lambda (a) (car (memq a from)))
8090 (mapcar (lambda (a) (car (memq a to)))
8091 cc))))
8092 (nconc (if (eq to t) nil to)
8093 (if (eq cc t) nil cc)
8094 from))))
8095 (unless articles
8096 (error "Found no matches for \"%s\"" address))
8097 (gnus-summary-limit articles))
8098 (gnus-summary-position-point))))
8099
8100(defun gnus-summary-limit-strange-charsets-predicate (header)
8101 (let ((string (concat (mail-header-subject header)
8102 (mail-header-from header)))
8103 charset found)
8104 (dotimes (i (1- (length string)))
8105 (setq charset (format "%s" (char-charset (aref string (1+ i)))))
8106 (when (string-match "unicode\\|big\\|japanese" charset)
8107 (setq found t)))
8108 found))
8109
8110(defun gnus-summary-limit-to-predicate (predicate)
8111 "Limit to articles where PREDICATE returns non-nil.
8112PREDICATE will be called with the header structures of the
8113articles."
8114 (let ((articles nil)
8115 (case-fold-search t))
8116 (dolist (header gnus-newsgroup-headers)
8117 (when (funcall predicate header)
8118 (push (mail-header-number header) articles)))
8119 (gnus-summary-limit (nreverse articles))))
8120
eec82323
LMI
8121(defun gnus-summary-limit-to-age (age &optional younger-p)
8122 "Limit the summary buffer to articles that are older than (or equal) AGE days.
8123If YOUNGER-P (the prefix) is non-nil, limit the summary buffer to
8124articles that are younger than AGE days."
16409b0b
GM
8125 (interactive
8126 (let ((younger current-prefix-arg)
8127 (days-got nil)
8128 days)
8129 (while (not days-got)
8130 (setq days (if younger
23f87bed
MB
8131 (read-string "Limit to articles younger than (in days, older when negative): ")
8132 (read-string
8133 "Limit to articles older than (in days, younger when negative): ")))
16409b0b
GM
8134 (when (> (length days) 0)
8135 (setq days (read days)))
8136 (if (numberp days)
23f87bed
MB
8137 (progn
8138 (setq days-got t)
01c52d31
MB
8139 (when (< days 0)
8140 (setq younger (not younger))
8141 (setq days (* days -1))))
16409b0b
GM
8142 (message "Please enter a number.")
8143 (sleep-for 1)))
8144 (list days younger)))
eec82323
LMI
8145 (prog1
8146 (let ((data gnus-newsgroup-data)
16409b0b 8147 (cutoff (days-to-time age))
eec82323
LMI
8148 articles d date is-younger)
8149 (while (setq d (pop data))
8150 (when (and (vectorp (gnus-data-header d))
8151 (setq date (mail-header-date (gnus-data-header d))))
16409b0b
GM
8152 (setq is-younger (time-less-p
8153 (time-since (condition-case ()
8154 (date-to-time date)
8155 (error '(0 0))))
eec82323 8156 cutoff))
6748645f
LMI
8157 (when (if younger-p
8158 is-younger
8159 (not is-younger))
eec82323
LMI
8160 (push (gnus-data-number d) articles))))
8161 (gnus-summary-limit (nreverse articles)))
8162 (gnus-summary-position-point)))
8163
47b63dfa 8164(defun gnus-summary-limit-to-extra (header regexp &optional not-matching)
16409b0b
GM
8165 "Limit the summary buffer to articles that match an 'extra' header."
8166 (interactive
8167 (let ((header
8168 (intern
23f87bed 8169 (gnus-completing-read-with-default
16409b0b 8170 (symbol-name (car gnus-extra-headers))
47b63dfa 8171 (if current-prefix-arg
81df110a
RF
8172 "Exclude extra header"
8173 "Limit extra header")
16409b0b
GM
8174 (mapcar (lambda (x)
8175 (cons (symbol-name x) x))
8176 gnus-extra-headers)
8177 nil
8178 t))))
8179 (list header
a1506d29 8180 (read-string (format "%s header %s (regexp): "
47b63dfa
SZ
8181 (if current-prefix-arg "Exclude" "Limit to")
8182 header))
8183 current-prefix-arg)))
16409b0b
GM
8184 (when (not (equal "" regexp))
8185 (prog1
8186 (let ((articles (gnus-summary-find-matching
a1506d29 8187 (cons 'extra header) regexp 'all nil nil
47b63dfa 8188 not-matching)))
16409b0b
GM
8189 (unless articles
8190 (error "Found no matches for \"%s\"" regexp))
8191 (gnus-summary-limit articles))
8192 (gnus-summary-position-point))))
8193
23f87bed
MB
8194(defun gnus-summary-limit-to-display-predicate ()
8195 "Limit the summary buffer to the predicated in the `display' group parameter."
8196 (interactive)
8197 (unless gnus-newsgroup-display
8198 (error "There is no `display' group parameter"))
8199 (let (articles)
8200 (dolist (number gnus-newsgroup-articles)
8201 (when (funcall gnus-newsgroup-display)
8202 (push number articles)))
8203 (gnus-summary-limit articles))
8204 (gnus-summary-position-point))
8205
eec82323
LMI
8206(defalias 'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
8207(make-obsolete
8208 'gnus-summary-delete-marked-as-read 'gnus-summary-limit-to-unread)
8209
8210(defun gnus-summary-limit-to-unread (&optional all)
8211 "Limit the summary buffer to articles that are not marked as read.
8212If ALL is non-nil, limit strictly to unread articles."
8213 (interactive "P")
8214 (if all
8215 (gnus-summary-limit-to-marks (char-to-string gnus-unread-mark))
8216 (gnus-summary-limit-to-marks
8217 ;; Concat all the marks that say that an article is read and have
8218 ;; those removed.
8219 (list gnus-del-mark gnus-read-mark gnus-ancient-mark
23f87bed 8220 gnus-killed-mark gnus-spam-mark gnus-kill-file-mark
eec82323
LMI
8221 gnus-low-score-mark gnus-expirable-mark
8222 gnus-canceled-mark gnus-catchup-mark gnus-sparse-mark
8223 gnus-duplicate-mark gnus-souped-mark)
8224 'reverse)))
8225
01c52d31
MB
8226(defun gnus-summary-limit-to-headers (match &optional reverse)
8227 "Limit the summary buffer to articles that have headers that match MATCH.
8228If REVERSE (the prefix), limit to articles that don't match."
8229 (interactive "sMatch headers (regexp): \nP")
8230 (gnus-summary-limit-to-bodies match reverse t))
8231
8232(defun gnus-summary-limit-to-bodies (match &optional reverse headersp)
8233 "Limit the summary buffer to articles that have bodies that match MATCH.
8234If REVERSE (the prefix), limit to articles that don't match."
8235 (interactive "sMatch body (regexp): \nP")
8236 (let ((articles nil)
8237 (gnus-select-article-hook nil) ;Disable hook.
8238 (gnus-article-prepare-hook nil)
8239 (gnus-use-article-prefetch nil)
8240 (gnus-keep-backlog nil)
8241 (gnus-break-pages nil)
8242 (gnus-summary-display-arrow nil)
8243 (gnus-updated-mode-lines nil)
8244 (gnus-auto-center-summary nil)
8245 (gnus-display-mime-function nil))
8246 (dolist (data gnus-newsgroup-data)
8247 (let (gnus-mark-article-hook)
8248 (gnus-summary-select-article t t nil (gnus-data-number data)))
8249 (save-excursion
8250 (set-buffer gnus-article-buffer)
8251 (article-goto-body)
8252 (let* ((case-fold-search t)
8253 (found (if headersp
8254 (re-search-backward match nil t)
8255 (re-search-forward match nil t))))
8256 (when (or (and found
8257 (not reverse))
8258 (and (not found)
8259 reverse))
8260 (push (gnus-data-number data) articles)))))
8261 (if (not articles)
8262 (message "No messages matched")
8263 (gnus-summary-limit articles)))
8264 (gnus-summary-position-point))
8265
8266(defun gnus-summary-limit-to-singletons (&optional threadsp)
8267 "Limit the summary buffer to articles that aren't part on any thread.
8268If THREADSP (the prefix), limit to articles that are in threads."
8269 (interactive "P")
8270 (let ((articles nil)
8271 thread-articles
8272 threads)
8273 (dolist (thread gnus-newsgroup-threads)
8274 (if (stringp (car thread))
8275 (dolist (thread (cdr thread))
8276 (push thread threads))
8277 (push thread threads)))
8278 (dolist (thread threads)
8279 (setq thread-articles (gnus-articles-in-thread thread))
8280 (when (or (and threadsp
8281 (> (length thread-articles) 1))
8282 (and (not threadsp)
8283 (= (length thread-articles) 1)))
8284 (setq articles (nconc thread-articles articles))))
8285 (if (not articles)
8286 (message "No messages matched")
8287 (gnus-summary-limit articles))
8288 (gnus-summary-position-point)))
8289
8290(defun gnus-summary-limit-to-replied (&optional unreplied)
8291 "Limit the summary buffer to replied articles.
8292If UNREPLIED (the prefix), limit to unreplied articles."
8293 (interactive "P")
8294 (if unreplied
8295 (gnus-summary-limit
8296 (gnus-set-difference gnus-newsgroup-articles
8297 gnus-newsgroup-replied))
8298 (gnus-summary-limit gnus-newsgroup-replied))
8299 (gnus-summary-position-point))
8300
eec82323
LMI
8301(defalias 'gnus-summary-delete-marked-with 'gnus-summary-limit-exclude-marks)
8302(make-obsolete 'gnus-summary-delete-marked-with
81ceefe2 8303 'gnus-summary-limit-exclude-marks)
eec82323
LMI
8304
8305(defun gnus-summary-limit-exclude-marks (marks &optional reverse)
8306 "Exclude articles that are marked with MARKS (e.g. \"DK\").
8307If REVERSE, limit the summary buffer to articles that are marked
8308with MARKS. MARKS can either be a string of marks or a list of marks.
8309Returns how many articles were removed."
8310 (interactive "sMarks: ")
8311 (gnus-summary-limit-to-marks marks t))
8312
8313(defun gnus-summary-limit-to-marks (marks &optional reverse)
8314 "Limit the summary buffer to articles that are marked with MARKS (e.g. \"DK\").
8315If REVERSE (the prefix), limit the summary buffer to articles that are
8316not marked with MARKS. MARKS can either be a string of marks or a
8317list of marks.
8318Returns how many articles were removed."
6748645f 8319 (interactive "sMarks: \nP")
eec82323
LMI
8320 (prog1
8321 (let ((data gnus-newsgroup-data)
8322 (marks (if (listp marks) marks
8323 (append marks nil))) ; Transform to list.
8324 articles)
8325 (while data
8326 (when (if reverse (not (memq (gnus-data-mark (car data)) marks))
8327 (memq (gnus-data-mark (car data)) marks))
8328 (push (gnus-data-number (car data)) articles))
8329 (setq data (cdr data)))
8330 (gnus-summary-limit articles))
8331 (gnus-summary-position-point)))
8332
23f87bed 8333(defun gnus-summary-limit-to-score (score)
eec82323 8334 "Limit to articles with score at or above SCORE."
23f87bed 8335 (interactive "NLimit to articles with score of at least: ")
eec82323
LMI
8336 (let ((data gnus-newsgroup-data)
8337 articles)
8338 (while data
8339 (when (>= (gnus-summary-article-score (gnus-data-number (car data)))
8340 score)
8341 (push (gnus-data-number (car data)) articles))
8342 (setq data (cdr data)))
8343 (prog1
8344 (gnus-summary-limit articles)
8345 (gnus-summary-position-point))))
8346
23f87bed
MB
8347(defun gnus-summary-limit-to-unseen ()
8348 "Limit to unseen articles."
8349 (interactive)
8350 (prog1
8351 (gnus-summary-limit gnus-newsgroup-unseen)
8352 (gnus-summary-position-point)))
8353
6748645f 8354(defun gnus-summary-limit-include-thread (id)
23f87bed
MB
8355 "Display all the hidden articles that is in the thread with ID in it.
8356When called interactively, ID is the Message-ID of the current
8357article."
6748645f
LMI
8358 (interactive (list (mail-header-id (gnus-summary-article-header))))
8359 (let ((articles (gnus-articles-in-thread
8360 (gnus-id-to-thread (gnus-root-id id)))))
8361 (prog1
8362 (gnus-summary-limit (nconc articles gnus-newsgroup-limit))
23f87bed
MB
8363 (gnus-summary-limit-include-matching-articles
8364 "subject"
8365 (regexp-quote (gnus-simplify-subject-re
8366 (mail-header-subject (gnus-id-to-header id)))))
6748645f
LMI
8367 (gnus-summary-position-point))))
8368
23f87bed
MB
8369(defun gnus-summary-limit-include-matching-articles (header regexp)
8370 "Display all the hidden articles that have HEADERs that match REGEXP."
8371 (interactive (list (read-string "Match on header: ")
8372 (read-string "Regexp: ")))
8373 (let ((articles (gnus-find-matching-articles header regexp)))
8374 (prog1
8375 (gnus-summary-limit (nconc articles gnus-newsgroup-limit))
8376 (gnus-summary-position-point))))
8377
8378(defun gnus-summary-insert-dormant-articles ()
8379 "Insert all the dormant articles for this group into the current buffer."
8380 (interactive)
8381 (let ((gnus-verbose (max 6 gnus-verbose)))
8382 (if (not gnus-newsgroup-dormant)
db629244 8383 (gnus-message 3 "No dormant articles for this group")
23f87bed
MB
8384 (gnus-summary-goto-subjects gnus-newsgroup-dormant))))
8385
01c52d31
MB
8386(defun gnus-summary-insert-ticked-articles ()
8387 "Insert ticked articles for this group into the current buffer."
8388 (interactive)
8389 (let ((gnus-verbose (max 6 gnus-verbose)))
8390 (if (not gnus-newsgroup-marked)
8391 (gnus-message 3 "No ticked articles for this group")
8392 (gnus-summary-goto-subjects gnus-newsgroup-marked))))
8393
eec82323 8394(defun gnus-summary-limit-include-dormant ()
6748645f
LMI
8395 "Display all the hidden articles that are marked as dormant.
8396Note that this command only works on a subset of the articles currently
8397fetched for this group."
eec82323 8398 (interactive)
eec82323
LMI
8399 (unless gnus-newsgroup-dormant
8400 (error "There are no dormant articles in this group"))
8401 (prog1
8402 (gnus-summary-limit (append gnus-newsgroup-dormant gnus-newsgroup-limit))
8403 (gnus-summary-position-point)))
8404
8405(defun gnus-summary-limit-exclude-dormant ()
8406 "Hide all dormant articles."
8407 (interactive)
eec82323
LMI
8408 (prog1
8409 (gnus-summary-limit-to-marks (list gnus-dormant-mark) 'reverse)
8410 (gnus-summary-position-point)))
8411
8412(defun gnus-summary-limit-exclude-childless-dormant ()
8413 "Hide all dormant articles that have no children."
8414 (interactive)
eec82323
LMI
8415 (let ((data (gnus-data-list t))
8416 articles d children)
8417 ;; Find all articles that are either not dormant or have
8418 ;; children.
8419 (while (setq d (pop data))
8420 (when (or (not (= (gnus-data-mark d) gnus-dormant-mark))
8421 (and (setq children
8422 (gnus-article-children (gnus-data-number d)))
8423 (let (found)
8424 (while children
8425 (when (memq (car children) articles)
8426 (setq children nil
8427 found t))
8428 (pop children))
8429 found)))
8430 (push (gnus-data-number d) articles)))
8431 ;; Do the limiting.
8432 (prog1
8433 (gnus-summary-limit articles)
8434 (gnus-summary-position-point))))
8435
8436(defun gnus-summary-limit-mark-excluded-as-read (&optional all)
8437 "Mark all unread excluded articles as read.
8438If ALL, mark even excluded ticked and dormants as read."
8439 (interactive "P")
23f87bed
MB
8440 (setq gnus-newsgroup-limit (sort gnus-newsgroup-limit '<))
8441 (let ((articles (gnus-sorted-ndifference
eec82323
LMI
8442 (sort
8443 (mapcar (lambda (h) (mail-header-number h))
8444 gnus-newsgroup-headers)
8445 '<)
23f87bed 8446 gnus-newsgroup-limit))
eec82323 8447 article)
6748645f 8448 (setq gnus-newsgroup-unreads
23f87bed
MB
8449 (gnus-sorted-intersection gnus-newsgroup-unreads
8450 gnus-newsgroup-limit))
eec82323
LMI
8451 (if all
8452 (setq gnus-newsgroup-dormant nil
8453 gnus-newsgroup-marked nil
8454 gnus-newsgroup-reads
8455 (nconc
8456 (mapcar (lambda (n) (cons n gnus-catchup-mark)) articles)
8457 gnus-newsgroup-reads))
8458 (while (setq article (pop articles))
8459 (unless (or (memq article gnus-newsgroup-dormant)
8460 (memq article gnus-newsgroup-marked))
8461 (push (cons article gnus-catchup-mark) gnus-newsgroup-reads))))))
8462
8463(defun gnus-summary-limit (articles &optional pop)
8464 (if pop
8465 ;; We pop the previous limit off the stack and use that.
8466 (setq articles (car gnus-newsgroup-limits)
8467 gnus-newsgroup-limits (cdr gnus-newsgroup-limits))
8468 ;; We use the new limit, so we push the old limit on the stack.
8469 (push gnus-newsgroup-limit gnus-newsgroup-limits))
8470 ;; Set the limit.
8471 (setq gnus-newsgroup-limit articles)
8472 (let ((total (length gnus-newsgroup-data))
8473 (data (gnus-data-find-list (gnus-summary-article-number)))
8474 (gnus-summary-mark-below nil) ; Inhibit this.
8475 found)
8476 ;; This will do all the work of generating the new summary buffer
8477 ;; according to the new limit.
8478 (gnus-summary-prepare)
8479 ;; Hide any threads, possibly.
23f87bed 8480 (gnus-summary-maybe-hide-threads)
eec82323
LMI
8481 ;; Try to return to the article you were at, or one in the
8482 ;; neighborhood.
8483 (when data
8484 ;; We try to find some article after the current one.
8485 (while data
8486 (when (gnus-summary-goto-subject (gnus-data-number (car data)) nil t)
8487 (setq data nil
8488 found t))
8489 (setq data (cdr data))))
8490 (unless found
8491 ;; If there is no data, that means that we were after the last
8492 ;; article. The same goes when we can't find any articles
8493 ;; after the current one.
8494 (goto-char (point-max))
8495 (gnus-summary-find-prev))
6748645f 8496 (gnus-set-mode-line 'summary)
eec82323
LMI
8497 ;; We return how many articles were removed from the summary
8498 ;; buffer as a result of the new limit.
8499 (- total (length gnus-newsgroup-data))))
8500
8501(defsubst gnus-invisible-cut-children (threads)
8502 (let ((num 0))
8503 (while threads
8504 (when (memq (mail-header-number (caar threads)) gnus-newsgroup-limit)
8505 (incf num))
8506 (pop threads))
8507 (< num 2)))
8508
8509(defsubst gnus-cut-thread (thread)
8510 "Go forwards in the thread until we find an article that we want to display."
8511 (when (or (eq gnus-fetch-old-headers 'some)
6748645f 8512 (eq gnus-fetch-old-headers 'invisible)
16409b0b 8513 (numberp gnus-fetch-old-headers)
eec82323
LMI
8514 (eq gnus-build-sparse-threads 'some)
8515 (eq gnus-build-sparse-threads 'more))
8516 ;; Deal with old-fetched headers and sparse threads.
8517 (while (and
8518 thread
8519 (or
8520 (gnus-summary-article-sparse-p (mail-header-number (car thread)))
8521 (gnus-summary-article-ancient-p
8522 (mail-header-number (car thread))))
6748645f
LMI
8523 (if (or (<= (length (cdr thread)) 1)
8524 (eq gnus-fetch-old-headers 'invisible))
8525 (setq gnus-newsgroup-limit
8526 (delq (mail-header-number (car thread))
8527 gnus-newsgroup-limit)
8528 thread (cadr thread))
8529 (when (gnus-invisible-cut-children (cdr thread))
8530 (let ((th (cdr thread)))
8531 (while th
8532 (if (memq (mail-header-number (caar th))
a8151ef7 8533 gnus-newsgroup-limit)
6748645f
LMI
8534 (setq thread (car th)
8535 th nil)
8536 (setq th (cdr th))))))))))
eec82323
LMI
8537 thread)
8538
8539(defun gnus-cut-threads (threads)
23f87bed 8540 "Cut off all uninteresting articles from the beginning of THREADS."
eec82323 8541 (when (or (eq gnus-fetch-old-headers 'some)
6748645f 8542 (eq gnus-fetch-old-headers 'invisible)
16409b0b 8543 (numberp gnus-fetch-old-headers)
eec82323
LMI
8544 (eq gnus-build-sparse-threads 'some)
8545 (eq gnus-build-sparse-threads 'more))
8546 (let ((th threads))
8547 (while th
8548 (setcar th (gnus-cut-thread (car th)))
8549 (setq th (cdr th)))))
8550 ;; Remove nixed out threads.
8551 (delq nil threads))
8552
8553(defun gnus-summary-initial-limit (&optional show-if-empty)
8554 "Figure out what the initial limit is supposed to be on group entry.
8555This entails weeding out unwanted dormants, low-scored articles,
8556fetch-old-headers verbiage, and so on."
8557 ;; Most groups have nothing to remove.
8558 (if (or gnus-inhibit-limiting
8559 (and (null gnus-newsgroup-dormant)
23f87bed 8560 (eq gnus-newsgroup-display 'gnus-not-ignore)
eec82323 8561 (not (eq gnus-fetch-old-headers 'some))
16409b0b 8562 (not (numberp gnus-fetch-old-headers))
6748645f 8563 (not (eq gnus-fetch-old-headers 'invisible))
eec82323
LMI
8564 (null gnus-summary-expunge-below)
8565 (not (eq gnus-build-sparse-threads 'some))
8566 (not (eq gnus-build-sparse-threads 'more))
8567 (null gnus-thread-expunge-below)
8568 (not gnus-use-nocem)))
8569 () ; Do nothing.
8570 (push gnus-newsgroup-limit gnus-newsgroup-limits)
8571 (setq gnus-newsgroup-limit nil)
8572 (mapatoms
8573 (lambda (node)
8574 (unless (car (symbol-value node))
8575 ;; These threads have no parents -- they are roots.
8576 (let ((nodes (cdr (symbol-value node)))
8577 thread)
8578 (while nodes
8579 (if (and gnus-thread-expunge-below
8580 (< (gnus-thread-total-score (car nodes))
8581 gnus-thread-expunge-below))
8582 (gnus-expunge-thread (pop nodes))
8583 (setq thread (pop nodes))
8584 (gnus-summary-limit-children thread))))))
8585 gnus-newsgroup-dependencies)
8586 ;; If this limitation resulted in an empty group, we might
8587 ;; pop the previous limit and use it instead.
8588 (when (and (not gnus-newsgroup-limit)
8589 show-if-empty)
8590 (setq gnus-newsgroup-limit (pop gnus-newsgroup-limits)))
8591 gnus-newsgroup-limit))
8592
8593(defun gnus-summary-limit-children (thread)
8594 "Return 1 if this subthread is visible and 0 if it is not."
8595 ;; First we get the number of visible children to this thread. This
8596 ;; is done by recursing down the thread using this function, so this
8597 ;; will really go down to a leaf article first, before slowly
8598 ;; working its way up towards the root.
8599 (when thread
04b61ae9 8600 (let* ((max-lisp-eval-depth (max 5000 max-lisp-eval-depth))
23f87bed 8601 (children
eec82323
LMI
8602 (if (cdr thread)
8603 (apply '+ (mapcar 'gnus-summary-limit-children
8604 (cdr thread)))
8605 0))
8606 (number (mail-header-number (car thread)))
8607 score)
8608 (if (and
8609 (not (memq number gnus-newsgroup-marked))
8610 (or
8611 ;; If this article is dormant and has absolutely no visible
8612 ;; children, then this article isn't visible.
8613 (and (memq number gnus-newsgroup-dormant)
8614 (zerop children))
8615 ;; If this is "fetch-old-headered" and there is no
8616 ;; visible children, then we don't want this article.
16409b0b
GM
8617 (and (or (eq gnus-fetch-old-headers 'some)
8618 (numberp gnus-fetch-old-headers))
eec82323
LMI
8619 (gnus-summary-article-ancient-p number)
8620 (zerop children))
6748645f
LMI
8621 ;; If this is "fetch-old-headered" and `invisible', then
8622 ;; we don't want this article.
8623 (and (eq gnus-fetch-old-headers 'invisible)
8624 (gnus-summary-article-ancient-p number))
eec82323
LMI
8625 ;; If this is a sparsely inserted article with no children,
8626 ;; we don't want it.
8627 (and (eq gnus-build-sparse-threads 'some)
8628 (gnus-summary-article-sparse-p number)
8629 (zerop children))
8630 ;; If we use expunging, and this article is really
8631 ;; low-scored, then we don't want this article.
8632 (when (and gnus-summary-expunge-below
8633 (< (setq score
8634 (or (cdr (assq number gnus-newsgroup-scored))
8635 gnus-summary-default-score))
8636 gnus-summary-expunge-below))
8637 ;; We increase the expunge-tally here, but that has
8638 ;; nothing to do with the limits, really.
8639 (incf gnus-newsgroup-expunged-tally)
8640 ;; We also mark as read here, if that's wanted.
8641 (when (and gnus-summary-mark-below
8642 (< score gnus-summary-mark-below))
8643 (setq gnus-newsgroup-unreads
8644 (delq number gnus-newsgroup-unreads))
8645 (if gnus-newsgroup-auto-expire
8646 (push number gnus-newsgroup-expirable)
8647 (push (cons number gnus-low-score-mark)
8648 gnus-newsgroup-reads)))
8649 t)
23f87bed
MB
8650 ;; Do the `display' group parameter.
8651 (and gnus-newsgroup-display
8652 (not (funcall gnus-newsgroup-display)))
eec82323 8653 ;; Check NoCeM things.
01c52d31
MB
8654 (when (and gnus-use-nocem
8655 (gnus-nocem-unwanted-article-p
8656 (mail-header-id (car thread))))
8657 (setq gnus-newsgroup-unreads
8658 (delq number gnus-newsgroup-unreads))
8659 t)))
eec82323
LMI
8660 ;; Nope, invisible article.
8661 0
8662 ;; Ok, this article is to be visible, so we add it to the limit
8663 ;; and return 1.
8664 (push number gnus-newsgroup-limit)
8665 1))))
8666
8667(defun gnus-expunge-thread (thread)
8668 "Mark all articles in THREAD as read."
8669 (let* ((number (mail-header-number (car thread))))
8670 (incf gnus-newsgroup-expunged-tally)
8671 ;; We also mark as read here, if that's wanted.
8672 (setq gnus-newsgroup-unreads
8673 (delq number gnus-newsgroup-unreads))
8674 (if gnus-newsgroup-auto-expire
8675 (push number gnus-newsgroup-expirable)
8676 (push (cons number gnus-low-score-mark)
8677 gnus-newsgroup-reads)))
8678 ;; Go recursively through all subthreads.
8679 (mapcar 'gnus-expunge-thread (cdr thread)))
8680
8681;; Summary article oriented commands
8682
8683(defun gnus-summary-refer-parent-article (n)
8684 "Refer parent article N times.
8685If N is negative, go to ancestor -N instead.
8686The difference between N and the number of articles fetched is returned."
8687 (interactive "p")
eec82323
LMI
8688 (let ((skip 1)
8689 error header ref)
8690 (when (not (natnump n))
8691 (setq skip (abs n)
8692 n 1))
8693 (while (and (> n 0)
8694 (not error))
8695 (setq header (gnus-summary-article-header))
8696 (if (and (eq (mail-header-number header)
8697 (cdr gnus-article-current))
8698 (equal gnus-newsgroup-name
8699 (car gnus-article-current)))
8700 ;; If we try to find the parent of the currently
8701 ;; displayed article, then we take a look at the actual
8702 ;; References header, since this is slightly more
8703 ;; reliable than the References field we got from the
8704 ;; server.
8705 (save-excursion
8706 (set-buffer gnus-original-article-buffer)
8707 (nnheader-narrow-to-headers)
8708 (unless (setq ref (message-fetch-field "references"))
23f87bed
MB
8709 (when (setq ref (message-fetch-field "in-reply-to"))
8710 (setq ref (gnus-extract-message-id-from-in-reply-to ref))))
eec82323
LMI
8711 (widen))
8712 (setq ref
8713 ;; It's not the current article, so we take a bet on
8714 ;; the value we got from the server.
8715 (mail-header-references header)))
8716 (if (and ref
8717 (not (equal ref "")))
8718 (unless (gnus-summary-refer-article (gnus-parent-id ref skip))
8719 (gnus-message 1 "Couldn't find parent"))
8720 (gnus-message 1 "No references in article %d"
8721 (gnus-summary-article-number))
8722 (setq error t))
8723 (decf n))
8724 (gnus-summary-position-point)
8725 n))
8726
8727(defun gnus-summary-refer-references ()
8728 "Fetch all articles mentioned in the References header.
6748645f 8729Return the number of articles fetched."
eec82323 8730 (interactive)
eec82323
LMI
8731 (let ((ref (mail-header-references (gnus-summary-article-header)))
8732 (current (gnus-summary-article-number))
8733 (n 0))
8734 (if (or (not ref)
8735 (equal ref ""))
8736 (error "No References in the current article")
8737 ;; For each Message-ID in the References header...
8738 (while (string-match "<[^>]*>" ref)
8739 (incf n)
8740 ;; ... fetch that article.
8741 (gnus-summary-refer-article
8742 (prog1 (match-string 0 ref)
8743 (setq ref (substring ref (match-end 0))))))
8744 (gnus-summary-goto-subject current)
8745 (gnus-summary-position-point)
8746 n)))
8747
6748645f
LMI
8748(defun gnus-summary-refer-thread (&optional limit)
8749 "Fetch all articles in the current thread.
8750If LIMIT (the numerical prefix), fetch that many old headers instead
8751of what's specified by the `gnus-refer-thread-limit' variable."
8752 (interactive "P")
8753 (let ((id (mail-header-id (gnus-summary-article-header)))
8754 (limit (if limit (prefix-numeric-value limit)
8755 gnus-refer-thread-limit)))
6748645f
LMI
8756 (unless (eq gnus-fetch-old-headers 'invisible)
8757 (gnus-message 5 "Fetching headers for %s..." gnus-newsgroup-name)
8758 ;; Retrieve the headers and read them in.
23f87bed
MB
8759 (if (eq (if (numberp limit)
8760 (gnus-retrieve-headers
8761 (list (min
8762 (+ (mail-header-number
8763 (gnus-summary-article-header))
8764 limit)
8765 gnus-newsgroup-end))
8766 gnus-newsgroup-name (* limit 2))
8767 ;; gnus-refer-thread-limit is t, i.e. fetch _all_
8768 ;; headers.
8769 (gnus-retrieve-headers (list gnus-newsgroup-end)
8770 gnus-newsgroup-name limit))
6748645f
LMI
8771 'nov)
8772 (gnus-build-all-threads)
23f87bed 8773 (error "Can't fetch thread from back ends that don't support NOV"))
6748645f
LMI
8774 (gnus-message 5 "Fetching headers for %s...done" gnus-newsgroup-name))
8775 (gnus-summary-limit-include-thread id)))
8776
16409b0b
GM
8777(defun gnus-summary-refer-article (message-id)
8778 "Fetch an article specified by MESSAGE-ID."
8779 (interactive "sMessage-ID: ")
eec82323
LMI
8780 (when (and (stringp message-id)
8781 (not (zerop (length message-id))))
23f87bed 8782 (setq message-id (gnus-replace-in-string message-id " " ""))
eec82323
LMI
8783 ;; Construct the correct Message-ID if necessary.
8784 ;; Suggested by tale@pawl.rpi.edu.
8785 (unless (string-match "^<" message-id)
8786 (setq message-id (concat "<" message-id)))
8787 (unless (string-match ">$" message-id)
8788 (setq message-id (concat message-id ">")))
23f87bed
MB
8789 ;; People often post MIDs from URLs, so unhex it:
8790 (unless (string-match "@" message-id)
8791 (setq message-id (gnus-url-unhex-string message-id)))
eec82323
LMI
8792 (let* ((header (gnus-id-to-header message-id))
8793 (sparse (and header
8794 (gnus-summary-article-sparse-p
a8151ef7
LMI
8795 (mail-header-number header))
8796 (memq (mail-header-number header)
16409b0b
GM
8797 gnus-newsgroup-limit)))
8798 number)
6748645f
LMI
8799 (cond
8800 ;; If the article is present in the buffer we just go to it.
8801 ((and header
8802 (or (not (gnus-summary-article-sparse-p
8803 (mail-header-number header)))
8804 sparse))
8805 (prog1
8806 (gnus-summary-goto-article
8807 (mail-header-number header) nil t)
8808 (when sparse
8809 (gnus-summary-update-article (mail-header-number header)))))
8810 (t
16409b0b
GM
8811 ;; We fetch the article.
8812 (catch 'found
8813 (dolist (gnus-override-method (gnus-refer-article-methods))
23f87bed
MB
8814 (when (and (gnus-check-server gnus-override-method)
8815 ;; Fetch the header,
8816 (setq number (gnus-summary-insert-subject message-id)))
8817 ;; and display the article.
eec82323 8818 (gnus-summary-select-article nil nil nil number)
16409b0b
GM
8819 (throw 'found t)))
8820 (gnus-message 3 "Couldn't fetch article %s" message-id)))))))
8821
8822(defun gnus-refer-article-methods ()
8f688cb0 8823 "Return a list of referable methods."
16409b0b
GM
8824 (cond
8825 ;; No method, so we default to current and native.
8826 ((null gnus-refer-article-method)
8827 (list gnus-current-select-method gnus-select-method))
8828 ;; Current.
8829 ((eq 'current gnus-refer-article-method)
8830 (list gnus-current-select-method))
8831 ;; List of select methods.
d4dfaa19
DL
8832 ((not (and (symbolp (car gnus-refer-article-method))
8833 (assq (car gnus-refer-article-method) nnoo-definition-alist)))
16409b0b
GM
8834 (let (out)
8835 (dolist (method gnus-refer-article-method)
8836 (push (if (eq 'current method)
8837 gnus-current-select-method
8838 method)
8839 out))
8840 (nreverse out)))
8841 ;; One single select method.
8842 (t
8843 (list gnus-refer-article-method))))
6748645f
LMI
8844
8845(defun gnus-summary-edit-parameters ()
8846 "Edit the group parameters of the current group."
8847 (interactive)
8848 (gnus-group-edit-group gnus-newsgroup-name 'params))
eec82323 8849
16409b0b
GM
8850(defun gnus-summary-customize-parameters ()
8851 "Customize the group parameters of the current group."
8852 (interactive)
8853 (gnus-group-customize gnus-newsgroup-name))
8854
eec82323
LMI
8855(defun gnus-summary-enter-digest-group (&optional force)
8856 "Enter an nndoc group based on the current article.
8857If FORCE, force a digest interpretation. If not, try
8858to guess what the document format is."
8859 (interactive "P")
eec82323 8860 (let ((conf gnus-current-window-configuration))
23f87bed
MB
8861 (save-window-excursion
8862 (save-excursion
8863 (let (gnus-article-prepare-hook
8864 gnus-display-mime-function
8865 gnus-break-pages)
8866 (gnus-summary-select-article))))
eec82323
LMI
8867 (setq gnus-current-window-configuration conf)
8868 (let* ((name (format "%s-%d"
8869 (gnus-group-prefixed-name
8870 gnus-newsgroup-name (list 'nndoc ""))
01c52d31 8871 (with-current-buffer gnus-summary-buffer
eec82323
LMI
8872 gnus-current-article)))
8873 (ogroup gnus-newsgroup-name)
8874 (params (append (gnus-info-params (gnus-get-info ogroup))
8875 (list (cons 'to-group ogroup))
23f87bed 8876 (list (cons 'parent-group ogroup))
eec82323
LMI
8877 (list (cons 'save-article-group ogroup))))
8878 (case-fold-search t)
8879 (buf (current-buffer))
16409b0b 8880 dig to-address)
eec82323 8881 (save-excursion
16409b0b
GM
8882 (set-buffer gnus-original-article-buffer)
8883 ;; Have the digest group inherit the main mail address of
8884 ;; the parent article.
23f87bed
MB
8885 (when (setq to-address (or (gnus-fetch-field "reply-to")
8886 (gnus-fetch-field "from")))
343d6628
MB
8887 (setq params
8888 (append
8889 (list (cons 'to-address
8890 (funcall gnus-decode-encoded-address-function
8891 to-address))))))
eec82323
LMI
8892 (setq dig (nnheader-set-temp-buffer " *gnus digest buffer*"))
8893 (insert-buffer-substring gnus-original-article-buffer)
8894 ;; Remove lines that may lead nndoc to misinterpret the
8895 ;; document type.
8896 (narrow-to-region
8897 (goto-char (point-min))
8898 (or (search-forward "\n\n" nil t) (point)))
8899 (goto-char (point-min))
16409b0b 8900 (delete-matching-lines "^Path:\\|^From ")
eec82323
LMI
8901 (widen))
8902 (unwind-protect
23f87bed 8903 (if (let ((gnus-newsgroup-ephemeral-charset gnus-newsgroup-charset)
16409b0b
GM
8904 (gnus-newsgroup-ephemeral-ignored-charsets
8905 gnus-newsgroup-ignored-charsets))
8906 (gnus-group-read-ephemeral-group
8907 name `(nndoc ,name (nndoc-address ,(get-buffer dig))
8908 (nndoc-article-type
23f87bed
MB
8909 ,(if force 'mbox 'guess)))
8910 t nil nil nil
8911 `((adapt-file . ,(gnus-score-file-name gnus-newsgroup-name
8912 "ADAPT")))))
16409b0b 8913 ;; Make all postings to this group go to the parent group.
23f87bed
MB
8914 (nconc (gnus-info-params (gnus-get-info name))
8915 params)
8916 ;; Couldn't select this doc group.
8917 (switch-to-buffer buf)
8918 (gnus-set-global-variables)
8919 (gnus-configure-windows 'summary)
8920 (gnus-message 3 "Article couldn't be entered?"))
eec82323
LMI
8921 (kill-buffer dig)))))
8922
8923(defun gnus-summary-read-document (n)
8924 "Open a new group based on the current article(s).
8925This will allow you to read digests and other similar
8926documents as newsgroups.
8927Obeys the standard process/prefix convention."
8928 (interactive "P")
01c52d31 8929 (let* ((ogroup gnus-newsgroup-name)
eec82323
LMI
8930 (params (append (gnus-info-params (gnus-get-info ogroup))
8931 (list (cons 'to-group ogroup))))
01c52d31
MB
8932 group egroup groups vgroup)
8933 (dolist (article (gnus-summary-work-articles n))
eec82323
LMI
8934 (setq group (format "%s-%d" gnus-newsgroup-name article))
8935 (gnus-summary-remove-process-mark article)
8936 (when (gnus-summary-display-article article)
8937 (save-excursion
16409b0b 8938 (with-temp-buffer
eec82323
LMI
8939 (insert-buffer-substring gnus-original-article-buffer)
8940 ;; Remove some headers that may lead nndoc to make
8941 ;; the wrong guess.
8942 (message-narrow-to-head)
8943 (goto-char (point-min))
01c52d31 8944 (delete-matching-lines "^Path:\\|^From ")
eec82323
LMI
8945 (widen)
8946 (if (setq egroup
8947 (gnus-group-read-ephemeral-group
8948 group `(nndoc ,group (nndoc-address ,(current-buffer))
8949 (nndoc-article-type guess))
8950 t nil t))
8951 (progn
23f87bed 8952 ;; Make all postings to this group go to the parent group.
eec82323
LMI
8953 (nconc (gnus-info-params (gnus-get-info egroup))
8954 params)
8955 (push egroup groups))
8956 ;; Couldn't select this doc group.
8957 (gnus-error 3 "Article couldn't be entered"))))))
8958 ;; Now we have selected all the documents.
8959 (cond
8960 ((not groups)
8961 (error "None of the articles could be interpreted as documents"))
8962 ((gnus-group-read-ephemeral-group
8963 (setq vgroup (format
8964 "nnvirtual:%s-%s" gnus-newsgroup-name
8965 (format-time-string "%Y%m%dT%H%M%S" (current-time))))
8966 `(nnvirtual ,vgroup (nnvirtual-component-groups ,groups))
8967 t
8968 (cons (current-buffer) 'summary)))
8969 (t
8970 (error "Couldn't select virtual nndoc group")))))
8971
8972(defun gnus-summary-isearch-article (&optional regexp-p)
8973 "Do incremental search forward on the current article.
8974If REGEXP-P (the prefix) is non-nil, do regexp isearch."
8975 (interactive "P")
eec82323
LMI
8976 (gnus-summary-select-article)
8977 (gnus-configure-windows 'article)
8978 (gnus-eval-in-buffer-window gnus-article-buffer
6748645f
LMI
8979 (save-restriction
8980 (widen)
8981 (isearch-forward regexp-p))))
eec82323 8982
01c52d31
MB
8983(defun gnus-summary-repeat-search-article-forward ()
8984 "Repeat the previous search forwards."
8985 (interactive)
8986 (unless gnus-last-search-regexp
8987 (error "No previous search"))
8988 (gnus-summary-search-article-forward gnus-last-search-regexp))
8989
8990(defun gnus-summary-repeat-search-article-backward ()
8991 "Repeat the previous search backwards."
8992 (interactive)
8993 (unless gnus-last-search-regexp
8994 (error "No previous search"))
8995 (gnus-summary-search-article-forward gnus-last-search-regexp t))
8996
eec82323
LMI
8997(defun gnus-summary-search-article-forward (regexp &optional backward)
8998 "Search for an article containing REGEXP forward.
8999If BACKWARD, search backward instead."
9000 (interactive
9001 (list (read-string
9002 (format "Search article %s (regexp%s): "
9003 (if current-prefix-arg "backward" "forward")
9004 (if gnus-last-search-regexp
9005 (concat ", default " gnus-last-search-regexp)
9006 "")))
9007 current-prefix-arg))
eec82323
LMI
9008 (if (string-equal regexp "")
9009 (setq regexp (or gnus-last-search-regexp ""))
23f87bed
MB
9010 (setq gnus-last-search-regexp regexp)
9011 (setq gnus-article-before-search gnus-current-article))
9012 ;; Intentionally set gnus-last-article.
9013 (setq gnus-last-article gnus-article-before-search)
9014 (let ((gnus-last-article gnus-last-article))
9015 (if (gnus-summary-search-article regexp backward)
9016 (gnus-summary-show-thread)
abc40aab 9017 (signal 'search-failed (list regexp)))))
eec82323
LMI
9018
9019(defun gnus-summary-search-article-backward (regexp)
9020 "Search for an article containing REGEXP backward."
9021 (interactive
9022 (list (read-string
9023 (format "Search article backward (regexp%s): "
9024 (if gnus-last-search-regexp
9025 (concat ", default " gnus-last-search-regexp)
9026 "")))))
9027 (gnus-summary-search-article-forward regexp 'backward))
9028
9029(defun gnus-summary-search-article (regexp &optional backward)
9030 "Search for an article containing REGEXP.
9031Optional argument BACKWARD means do search for backward.
9032`gnus-select-article-hook' is not called during the search."
a8151ef7
LMI
9033 ;; We have to require this here to make sure that the following
9034 ;; dynamic binding isn't shadowed by autoloading.
9035 (require 'gnus-async)
16409b0b 9036 (require 'gnus-art)
eec82323 9037 (let ((gnus-select-article-hook nil) ;Disable hook.
16409b0b 9038 (gnus-article-prepare-hook nil)
eec82323
LMI
9039 (gnus-mark-article-hook nil) ;Inhibit marking as read.
9040 (gnus-use-article-prefetch nil)
9041 (gnus-xmas-force-redisplay nil) ;Inhibit XEmacs redisplay.
a8151ef7 9042 (gnus-use-trees nil) ;Inhibit updating tree buffer.
23f87bed
MB
9043 (gnus-visual nil)
9044 (gnus-keep-backlog nil)
9045 (gnus-break-pages nil)
9046 (gnus-summary-display-arrow nil)
9047 (gnus-updated-mode-lines nil)
9048 (gnus-auto-center-summary nil)
eec82323 9049 (sum (current-buffer))
16409b0b 9050 (gnus-display-mime-function nil)
eec82323
LMI
9051 (found nil)
9052 point)
9053 (gnus-save-hidden-threads
9054 (gnus-summary-select-article)
9055 (set-buffer gnus-article-buffer)
16409b0b 9056 (goto-char (window-point (get-buffer-window (current-buffer))))
eec82323
LMI
9057 (when backward
9058 (forward-line -1))
9059 (while (not found)
9060 (gnus-message 7 "Searching article: %d..." (cdr gnus-article-current))
9061 (if (if backward
9062 (re-search-backward regexp nil t)
9063 (re-search-forward regexp nil t))
9064 ;; We found the regexp.
9065 (progn
9066 (setq found 'found)
9067 (beginning-of-line)
9068 (set-window-start
9069 (get-buffer-window (current-buffer))
9070 (point))
9071 (forward-line 1)
16409b0b
GM
9072 (set-window-point
9073 (get-buffer-window (current-buffer))
9074 (point))
eec82323
LMI
9075 (set-buffer sum)
9076 (setq point (point)))
9077 ;; We didn't find it, so we go to the next article.
9078 (set-buffer sum)
9079 (setq found 'not)
9080 (while (eq found 'not)
9081 (if (not (if backward (gnus-summary-find-prev)
9082 (gnus-summary-find-next)))
9083 ;; No more articles.
9084 (setq found t)
9085 ;; Select the next article and adjust point.
9086 (unless (gnus-summary-article-sparse-p
9087 (gnus-summary-article-number))
9088 (setq found nil)
9089 (gnus-summary-select-article)
9090 (set-buffer gnus-article-buffer)
9091 (widen)
9092 (goto-char (if backward (point-max) (point-min))))))))
9093 (gnus-message 7 ""))
9094 ;; Return whether we found the regexp.
9095 (when (eq found 'found)
9096 (goto-char point)
9097 (gnus-summary-show-thread)
9098 (gnus-summary-goto-subject gnus-current-article)
9099 (gnus-summary-position-point)
9100 t)))
9101
23f87bed
MB
9102(defun gnus-find-matching-articles (header regexp)
9103 "Return a list of all articles that match REGEXP on HEADER.
9104This search includes all articles in the current group that Gnus has
9105fetched headers for, whether they are displayed or not."
9106 (let ((articles nil)
9107 (func `(lambda (h) (,(intern (concat "mail-header-" header)) h)))
9108 (case-fold-search t))
9109 (dolist (header gnus-newsgroup-headers)
9110 (when (string-match regexp (funcall func header))
9111 (push (mail-header-number header) articles)))
9112 (nreverse articles)))
9113
eec82323 9114(defun gnus-summary-find-matching (header regexp &optional backward unread
47b63dfa 9115 not-case-fold not-matching)
eec82323
LMI
9116 "Return a list of all articles that match REGEXP on HEADER.
9117The search stars on the current article and goes forwards unless
9118BACKWARD is non-nil. If BACKWARD is `all', do all articles.
9119If UNREAD is non-nil, only unread articles will
9120be taken into consideration. If NOT-CASE-FOLD, case won't be folded
a1506d29 9121in the comparisons. If NOT-MATCHING, return a list of all articles that
47b63dfa
SZ
9122not match REGEXP on HEADER."
9123 (let ((case-fold-search (not not-case-fold))
16409b0b
GM
9124 articles d func)
9125 (if (consp header)
9126 (if (eq (car header) 'extra)
9127 (setq func
9128 `(lambda (h)
9129 (or (cdr (assq ',(cdr header) (mail-header-extra h)))
9130 "")))
9131 (error "%s is an invalid header" header))
9132 (unless (fboundp (intern (concat "mail-header-" header)))
9133 (error "%s is not a valid header" header))
9134 (setq func `(lambda (h) (,(intern (concat "mail-header-" header)) h))))
47b63dfa
SZ
9135 (dolist (d (if (eq backward 'all)
9136 gnus-newsgroup-data
9137 (gnus-data-find-list
9138 (gnus-summary-article-number)
9139 (gnus-data-list backward))))
9140 (when (and (or (not unread) ; We want all articles...
9141 (gnus-data-unread-p d)) ; Or just unreads.
9142 (vectorp (gnus-data-header d)) ; It's not a pseudo.
9143 (if not-matching
a1506d29 9144 (not (string-match
47b63dfa
SZ
9145 regexp
9146 (funcall func (gnus-data-header d))))
9147 (string-match regexp
9148 (funcall func (gnus-data-header d)))))
9149 (push (gnus-data-number d) articles))) ; Success!
eec82323
LMI
9150 (nreverse articles)))
9151
9152(defun gnus-summary-execute-command (header regexp command &optional backward)
9153 "Search forward for an article whose HEADER matches REGEXP and execute COMMAND.
9154If HEADER is an empty string (or nil), the match is done on the entire
9155article. If BACKWARD (the prefix) is non-nil, search backward instead."
9156 (interactive
9157 (list (let ((completion-ignore-case t))
9158 (completing-read
9159 "Header name: "
23f87bed
MB
9160 (mapcar (lambda (header) (list (format "%s" header)))
9161 (append
9162 '("Number" "Subject" "From" "Lines" "Date"
9163 "Message-ID" "Xref" "References" "Body")
9164 gnus-extra-headers))
eec82323
LMI
9165 nil 'require-match))
9166 (read-string "Regexp: ")
9167 (read-key-sequence "Command: ")
9168 current-prefix-arg))
9169 (when (equal header "Body")
9170 (setq header ""))
eec82323
LMI
9171 ;; Hidden thread subtrees must be searched as well.
9172 (gnus-summary-show-all-threads)
9173 ;; We don't want to change current point nor window configuration.
9174 (save-excursion
9175 (save-window-excursion
23f87bed
MB
9176 (let (gnus-visual
9177 gnus-treat-strip-trailing-blank-lines
9178 gnus-treat-strip-leading-blank-lines
9179 gnus-treat-strip-multiple-blank-lines
9180 gnus-treat-hide-boring-headers
9181 gnus-treat-fold-newsgroups
9182 gnus-article-prepare-hook)
9183 (gnus-message 6 "Executing %s..." (key-description command))
9184 ;; We'd like to execute COMMAND interactively so as to give arguments.
9185 (gnus-execute header regexp
9186 `(call-interactively ',(key-binding command))
9187 backward)
9188 (gnus-message 6 "Executing %s...done" (key-description command))))))
eec82323
LMI
9189
9190(defun gnus-summary-beginning-of-article ()
9191 "Scroll the article back to the beginning."
9192 (interactive)
eec82323
LMI
9193 (gnus-summary-select-article)
9194 (gnus-configure-windows 'article)
9195 (gnus-eval-in-buffer-window gnus-article-buffer
9196 (widen)
9197 (goto-char (point-min))
23f87bed 9198 (when gnus-break-pages
eec82323
LMI
9199 (gnus-narrow-to-page))))
9200
9201(defun gnus-summary-end-of-article ()
9202 "Scroll to the end of the article."
9203 (interactive)
eec82323
LMI
9204 (gnus-summary-select-article)
9205 (gnus-configure-windows 'article)
9206 (gnus-eval-in-buffer-window gnus-article-buffer
9207 (widen)
9208 (goto-char (point-max))
9209 (recenter -3)
23f87bed
MB
9210 (when gnus-break-pages
9211 (when (re-search-backward page-delimiter nil t)
9212 (narrow-to-region (match-end 0) (point-max)))
eec82323
LMI
9213 (gnus-narrow-to-page))))
9214
23f87bed
MB
9215(defun gnus-summary-print-truncate-and-quote (string &optional len)
9216 "Truncate to LEN and quote all \"(\"'s in STRING."
9217 (gnus-replace-in-string (if (and len (> (length string) len))
9218 (substring string 0 len)
9219 string)
9220 "[()]" "\\\\\\&"))
9221
6748645f 9222(defun gnus-summary-print-article (&optional filename n)
23f87bed
MB
9223 "Generate and print a PostScript image of the process-marked (mail) articles.
9224
9225If used interactively, print the current article if none are
9226process-marked. With prefix arg, prompt the user for the name of the
9227file to save in.
6748645f 9228
23f87bed
MB
9229When used from Lisp, accept two optional args FILENAME and N. N means
9230to print the next N articles. If N is negative, print the N previous
9231articles. If N is nil and articles have been marked with the process
9232mark, print these instead.
eec82323 9233
16409b0b 9234If the optional first argument FILENAME is nil, send the image to the
6748645f
LMI
9235printer. If FILENAME is a string, save the PostScript image in a file with
9236that name. If FILENAME is a number, prompt the user for the name of the file
eec82323 9237to save in."
676a7cc9 9238 (interactive (list (ps-print-preprint current-prefix-arg)))
6748645f
LMI
9239 (dolist (article (gnus-summary-work-articles n))
9240 (gnus-summary-select-article nil nil 'pseudo article)
9241 (gnus-eval-in-buffer-window gnus-article-buffer
23f87bed 9242 (gnus-print-buffer))
676a7cc9
SZ
9243 (gnus-summary-remove-process-mark article))
9244 (ps-despool filename))
eec82323 9245
23f87bed
MB
9246(defun gnus-print-buffer ()
9247 (let ((buffer (generate-new-buffer " *print*")))
9248 (unwind-protect
9249 (progn
9250 (copy-to-buffer buffer (point-min) (point-max))
9251 (set-buffer buffer)
9252 (gnus-remove-text-with-property 'gnus-decoration)
9253 (when (gnus-visual-p 'article-highlight 'highlight)
9254 ;; Copy-to-buffer doesn't copy overlay. So redo
9255 ;; highlight.
9256 (let ((gnus-article-buffer buffer))
9257 (gnus-article-highlight-citation t)
9258 (gnus-article-highlight-signature)
9259 (gnus-article-emphasize)
9260 (gnus-article-delete-invisible-text)))
9261 (let ((ps-left-header
9262 (list
9263 (concat "("
9264 (gnus-summary-print-truncate-and-quote
9265 (mail-header-subject gnus-current-headers)
9266 66) ")")
9267 (concat "("
9268 (gnus-summary-print-truncate-and-quote
9269 (mail-header-from gnus-current-headers)
9270 45) ")")))
9271 (ps-right-header
9272 (list
9273 "/pagenumberstring load"
9274 (concat "("
9275 (mail-header-date gnus-current-headers) ")"))))
9276 (gnus-run-hooks 'gnus-ps-print-hook)
9277 (save-excursion
9278 (if window-system
9279 (ps-spool-buffer-with-faces)
9280 (ps-spool-buffer)))))
9281 (kill-buffer buffer))))
9282
eec82323 9283(defun gnus-summary-show-article (&optional arg)
23f87bed 9284 "Force redisplaying of the current article.
16409b0b
GM
9285If ARG (the prefix) is a number, show the article with the charset
9286defined in `gnus-summary-show-article-charset-alist', or the charset
23f87bed 9287input.
16409b0b 9288If ARG (the prefix) is non-nil and not a number, show the raw article
23f87bed
MB
9289without any article massaging functions being run. Normally, the key
9290strokes are `C-u g'."
eec82323 9291 (interactive "P")
16409b0b
GM
9292 (cond
9293 ((numberp arg)
23f87bed 9294 (gnus-summary-show-article t)
16409b0b
GM
9295 (let ((gnus-newsgroup-charset
9296 (or (cdr (assq arg gnus-summary-show-article-charset-alist))
23f87bed
MB
9297 (mm-read-coding-system
9298 "View as charset: " ;; actually it is coding system.
01c52d31 9299 (with-current-buffer gnus-article-buffer
23f87bed 9300 (mm-detect-coding-region (point) (point-max))))))
16409b0b 9301 (gnus-newsgroup-ignored-charsets 'gnus-all))
23f87bed
MB
9302 (gnus-summary-select-article nil 'force)
9303 (let ((deps gnus-newsgroup-dependencies)
9304 head header lines)
9305 (save-excursion
9306 (set-buffer gnus-original-article-buffer)
9307 (save-restriction
9308 (message-narrow-to-head)
9309 (setq head (buffer-string))
9310 (goto-char (point-min))
9311 (unless (re-search-forward "^lines:[ \t]\\([0-9]+\\)" nil t)
9312 (goto-char (point-max))
9313 (widen)
9314 (setq lines (1- (count-lines (point) (point-max))))))
9315 (with-temp-buffer
9316 (insert (format "211 %d Article retrieved.\n"
9317 (cdr gnus-article-current)))
9318 (insert head)
9319 (if lines (insert (format "Lines: %d\n" lines)))
9320 (insert ".\n")
9321 (let ((nntp-server-buffer (current-buffer)))
9322 (setq header (car (gnus-get-newsgroup-headers deps t))))))
9323 (gnus-data-set-header
9324 (gnus-data-find (cdr gnus-article-current))
9325 header)
9326 (gnus-summary-update-article-line
9327 (cdr gnus-article-current) header)
9328 (when (gnus-summary-goto-subject (cdr gnus-article-current) nil t)
9329 (gnus-summary-update-secondary-mark (cdr gnus-article-current))))))
16409b0b
GM
9330 ((not arg)
9331 ;; Select the article the normal way.
9332 (gnus-summary-select-article nil 'force))
9333 (t
9334 ;; We have to require this here to make sure that the following
9335 ;; dynamic binding isn't shadowed by autoloading.
9336 (require 'gnus-async)
9337 (require 'gnus-art)
eec82323
LMI
9338 ;; Bind the article treatment functions to nil.
9339 (let ((gnus-have-all-headers t)
eec82323 9340 gnus-article-prepare-hook
16409b0b
GM
9341 gnus-article-decode-hook
9342 gnus-display-mime-function
9343 gnus-break-pages)
9344 ;; Destroy any MIME parts.
9345 (when (gnus-buffer-live-p gnus-article-buffer)
9346 (save-excursion
9347 (set-buffer gnus-article-buffer)
9348 (mm-destroy-parts gnus-article-mime-handles)
9349 ;; Set it to nil for safety reason.
9350 (setq gnus-article-mime-handle-alist nil)
9351 (setq gnus-article-mime-handles nil)))
9352 (gnus-summary-select-article nil 'force))))
eec82323
LMI
9353 (gnus-summary-goto-subject gnus-current-article)
9354 (gnus-summary-position-point))
9355
23f87bed
MB
9356(defun gnus-summary-show-raw-article ()
9357 "Show the raw article without any article massaging functions being run."
9358 (interactive)
9359 (gnus-summary-show-article t))
9360
eec82323
LMI
9361(defun gnus-summary-verbose-headers (&optional arg)
9362 "Toggle permanent full header display.
9363If ARG is a positive number, turn header display on.
9364If ARG is a negative number, turn header display off."
9365 (interactive "P")
eec82323
LMI
9366 (setq gnus-show-all-headers
9367 (cond ((or (not (numberp arg))
9368 (zerop arg))
9369 (not gnus-show-all-headers))
9370 ((natnump arg)
9371 t)))
9372 (gnus-summary-show-article))
9373
9374(defun gnus-summary-toggle-header (&optional arg)
9375 "Show the headers if they are hidden, or hide them if they are shown.
9376If ARG is a positive number, show the entire header.
9377If ARG is a negative number, hide the unwanted header lines."
9378 (interactive "P")
23f87bed
MB
9379 (let ((window (and (gnus-buffer-live-p gnus-article-buffer)
9380 (get-buffer-window gnus-article-buffer t))))
9381 (with-current-buffer gnus-article-buffer
9382 (widen)
9383 (article-narrow-to-head)
16409b0b
GM
9384 (let* ((buffer-read-only nil)
9385 (inhibit-point-motion-hooks t)
23f87bed
MB
9386 (hidden (if (numberp arg)
9387 (>= arg 0)
f0096211
MB
9388 (or (not (looking-at "[^ \t\n]+:"))
9389 (gnus-article-hidden-text-p 'headers))))
23f87bed
MB
9390 s e)
9391 (delete-region (point-min) (point-max))
667e0ba6
SM
9392 (with-current-buffer gnus-original-article-buffer
9393 (goto-char (setq s (point-min)))
23f87bed
MB
9394 (setq e (if (search-forward "\n\n" nil t)
9395 (1- (point))
9396 (point-max))))
667e0ba6 9397 (insert-buffer-substring gnus-original-article-buffer s e)
23f87bed
MB
9398 (run-hooks 'gnus-article-decode-hook)
9399 (if hidden
9400 (let ((gnus-treat-hide-headers nil)
9401 (gnus-treat-hide-boring-headers nil))
9402 (gnus-delete-wash-type 'headers)
9403 (gnus-treat-article 'head))
9404 (gnus-treat-article 'head))
9405 (widen)
9406 (if window
9407 (set-window-start window (goto-char (point-min))))
9408 (if gnus-break-pages
9409 (gnus-narrow-to-page)
9410 (when (gnus-visual-p 'page-marker)
9411 (let ((buffer-read-only nil))
9412 (gnus-remove-text-with-property 'gnus-prev)
9413 (gnus-remove-text-with-property 'gnus-next))))
16409b0b 9414 (gnus-set-mode-line 'article)))))
eec82323
LMI
9415
9416(defun gnus-summary-show-all-headers ()
9417 "Make all header lines visible."
9418 (interactive)
23f87bed 9419 (gnus-summary-toggle-header 1))
eec82323 9420
eec82323
LMI
9421(defun gnus-summary-caesar-message (&optional arg)
9422 "Caesar rotate the current article by 13.
01c52d31
MB
9423With a non-numerical prefix, also rotate headers. A numerical
9424prefix specifies how many places to rotate each letter forward."
eec82323 9425 (interactive "P")
eec82323
LMI
9426 (gnus-summary-select-article)
9427 (let ((mail-header-separator ""))
9428 (gnus-eval-in-buffer-window gnus-article-buffer
9429 (save-restriction
9430 (widen)
9431 (let ((start (window-start))
9432 buffer-read-only)
01c52d31
MB
9433 (if (equal arg '(4))
9434 (message-caesar-buffer-body nil t)
9435 (message-caesar-buffer-body arg))
ff4d3926
MB
9436 (set-window-start (get-buffer-window (current-buffer)) start)))))
9437 ;; Create buttons and stuff...
9438 (gnus-treat-article nil))
eec82323 9439
01c52d31
MB
9440(defun gnus-summary-idna-message (&optional arg)
9441 "Decode IDNA encoded domain names in the current articles.
9442IDNA encoded domain names looks like `xn--bar'. If a string
9443remain unencoded after running this function, it is likely an
9444invalid IDNA string (`xn--bar' is invalid).
9445
9446You must have GNU Libidn (`http://www.gnu.org/software/libidn/')
9447installed for this command to work."
9448 (interactive "P")
9449 (if (not (and (condition-case nil (require 'idna)
9450 (file-error))
9451 (mm-coding-system-p 'utf-8)
9452 (executable-find (symbol-value 'idna-program))))
9453 (gnus-message
9454 5 "GNU Libidn not installed properly (`idn' or `idna.el' missing)")
9455 (gnus-summary-select-article)
9456 (let ((mail-header-separator ""))
9457 (gnus-eval-in-buffer-window gnus-article-buffer
9458 (save-restriction
9459 (widen)
9460 (let ((start (window-start))
9461 buffer-read-only)
9462 (while (re-search-forward "\\(xn--[-0-9a-z]+\\)" nil t)
9463 (replace-match (idna-to-unicode (match-string 1))))
9464 (set-window-start (get-buffer-window (current-buffer)) start)))))))
23f87bed
MB
9465
9466(defun gnus-summary-morse-message (&optional arg)
9467 "Morse decode the current article."
9468 (interactive "P")
9469 (gnus-summary-select-article)
9470 (let ((mail-header-separator ""))
9471 (gnus-eval-in-buffer-window gnus-article-buffer
9472 (save-excursion
9473 (save-restriction
9474 (widen)
9475 (let ((pos (window-start))
9476 buffer-read-only)
9477 (goto-char (point-min))
9478 (when (message-goto-body)
9479 (gnus-narrow-to-body))
9480 (goto-char (point-min))
01c52d31 9481