1 ;;; gnus-group.el --- group mode commands for Gnus
2 ;; Copyright (C) 1996,97,98,99 Free Software Foundation, Inc.
4 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
7 ;; This file is part of GNU Emacs.
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING. If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
28 (eval-when-compile (require 'cl))
40 (defcustom gnus-group-archive-directory
41 "*ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list/"
42 "*The address of the (ding) archives."
43 :group 'gnus-group-foreign
46 (defcustom gnus-group-recent-archive-directory
47 "*ftp@ftp.hpc.uh.edu:/pub/emacs/ding-list-recent/"
48 "*The address of the most recent (ding) articles."
49 :group 'gnus-group-foreign
52 (defcustom gnus-no-groups-message "No gnus is bad news"
53 "*Message displayed by Gnus when no groups are available."
57 (defcustom gnus-keep-same-level nil
58 "*Non-nil means that the next newsgroup after the current will be on the same level.
59 When you type, for instance, `n' after reading the last article in the
60 current newsgroup, you will go to the next newsgroup. If this variable
61 is nil, the next newsgroup will be the next from the group
63 If this variable is non-nil, Gnus will either put you in the
64 next newsgroup with the same level, or, if no such newsgroup is
65 available, the next newsgroup with the lowest possible level higher
66 than the current level.
67 If this variable is `best', Gnus will make the next newsgroup the one
69 :group 'gnus-group-levels
70 :type '(choice (const nil)
72 (sexp :tag "other" t)))
74 (defcustom gnus-group-goto-unread t
75 "*If non-nil, movement commands will go to the next unread and subscribed group."
76 :link '(custom-manual "(gnus)Group Maneuvering")
77 :group 'gnus-group-various
80 (defcustom gnus-goto-next-group-when-activating t
81 "*If non-nil, the \\<gnus-group-mode-map>\\[gnus-group-get-new-news-this-group] command will advance point to the next group."
82 :link '(custom-manual "(gnus)Scanning New Messages")
83 :group 'gnus-group-various
86 (defcustom gnus-permanently-visible-groups nil
87 "*Regexp to match groups that should always be listed in the group buffer.
88 This means that they will still be listed even when there are no
89 unread articles in the groups.
91 If nil, no groups are permanently visible."
92 :group 'gnus-group-listing
93 :type '(choice regexp (const nil)))
95 (defcustom gnus-list-groups-with-ticked-articles t
96 "*If non-nil, list groups that have only ticked articles.
97 If nil, only list groups that have unread articles."
98 :group 'gnus-group-listing
101 (defcustom gnus-group-default-list-level gnus-level-subscribed
102 "*Default listing level.
103 Ignored if `gnus-group-use-permanent-levels' is non-nil."
104 :group 'gnus-group-listing
107 (defcustom gnus-group-list-inactive-groups t
108 "*If non-nil, inactive groups will be listed."
109 :group 'gnus-group-listing
110 :group 'gnus-group-levels
113 (defcustom gnus-group-sort-function 'gnus-group-sort-by-alphabet
114 "*Function used for sorting the group buffer.
115 This function will be called with group info entries as the arguments
116 for the groups to be sorted. Pre-made functions include
117 `gnus-group-sort-by-alphabet', `gnus-group-sort-by-real-name',
118 `gnus-group-sort-by-unread', `gnus-group-sort-by-level',
119 `gnus-group-sort-by-score', `gnus-group-sort-by-method', and
120 `gnus-group-sort-by-rank'.
122 This variable can also be a list of sorting functions. In that case,
123 the most significant sort function should be the last function in the
125 :group 'gnus-group-listing
126 :link '(custom-manual "(gnus)Sorting Groups")
127 :type '(radio (function-item gnus-group-sort-by-alphabet)
128 (function-item gnus-group-sort-by-real-name)
129 (function-item gnus-group-sort-by-unread)
130 (function-item gnus-group-sort-by-level)
131 (function-item gnus-group-sort-by-score)
132 (function-item gnus-group-sort-by-method)
133 (function-item gnus-group-sort-by-rank)
134 (function :tag "other" nil)))
136 (defcustom gnus-group-line-format "%M\%S\%p\%P\%5y: %(%g%)%l\n"
137 "*Format of group lines.
138 It works along the same lines as a normal formatting string,
139 with some simple extensions.
141 %M Only marked articles (character, \"*\" or \" \")
142 %S Whether the group is subscribed (character, \"U\", \"K\", \"Z\" or \" \")
143 %L Level of subscribedness (integer)
144 %N Number of unread articles (integer)
145 %I Number of dormant articles (integer)
146 %i Number of ticked and dormant (integer)
147 %T Number of ticked articles (integer)
148 %R Number of read articles (integer)
149 %t Estimated total number of articles (integer)
150 %y Number of unread, unticked articles (integer)
151 %G Group name (string)
152 %g Qualified group name (string)
153 %D Group description (string)
154 %s Select method (string)
155 %o Moderated group (char, \"m\")
156 %p Process mark (char)
157 %O Moderated group (string, \"(m)\" or \"\")
158 %P Topic indentation (string)
159 %m Whether there is new(ish) mail in the group (char, \"%\")
160 %l Whether there are GroupLens predictions for this group (string)
161 %n Select from where (string)
162 %z A string that look like `<%s:%n>' if a foreign select method is used
163 %d The date the group was last entered.
164 %u User defined specifier. The next character in the format string should
165 be a letter. Gnus will call the function gnus-user-format-function-X,
166 where X is the letter following %u. The function will be passed the
167 current header as argument. The function should return a string, which
168 will be inserted into the buffer just like information from any other
171 Text between %( and %) will be highlighted with `gnus-mouse-face' when
172 the mouse point move inside the area. There can only be one such area.
174 Note that this format specification is not always respected. For
175 reasons of efficiency, when listing killed groups, this specification
176 is ignored altogether. If the spec is changed considerably, your
177 output may end up looking strange when listing both alive and killed
180 If you use %o or %O, reading the active file will be slower and quite
181 a bit of extra memory will be used. %D will also worsen performance.
182 Also note that if you change the format specification to include any
183 of these specs, you must probably re-start Gnus to see them go into
185 :group 'gnus-group-visual
188 (defcustom gnus-group-mode-line-format "Gnus: %%b {%M\%:%S}"
189 "*The format specification for the group mode line.
190 It works along the same lines as a normal formatting string,
191 with some simple extensions:
193 %S The native news server.
194 %M The native select method.
195 %: \":\" if %S isn't \"\"."
196 :group 'gnus-group-visual
199 (defcustom gnus-group-mode-hook nil
200 "Hook for Gnus group mode."
201 :group 'gnus-group-various
202 :options '(gnus-topic-mode)
205 (defcustom gnus-group-menu-hook nil
206 "Hook run after the creation of the group mode menu."
207 :group 'gnus-group-various
210 (defcustom gnus-group-catchup-group-hook nil
211 "Hook run when catching up a group from the group buffer."
212 :group 'gnus-group-various
213 :link '(custom-manual "(gnus)Group Data")
216 (defcustom gnus-group-update-group-hook nil
217 "Hook called when updating group lines."
218 :group 'gnus-group-visual
221 (defcustom gnus-group-prepare-function 'gnus-group-prepare-flat
222 "*A function that is called to generate the group buffer.
223 The function is called with three arguments: The first is a number;
224 all group with a level less or equal to that number should be listed,
225 if the second is non-nil, empty groups should also be displayed. If
226 the third is non-nil, it is a number. No groups with a level lower
227 than this number should be displayed.
229 The only current function implemented is `gnus-group-prepare-flat'."
230 :group 'gnus-group-listing
233 (defcustom gnus-group-prepare-hook nil
234 "Hook called after the group buffer has been generated.
235 If you want to modify the group buffer, you can use this hook."
236 :group 'gnus-group-listing
239 (defcustom gnus-suspend-gnus-hook nil
240 "Hook called when suspending (not exiting) Gnus."
244 (defcustom gnus-exit-gnus-hook nil
245 "Hook called when exiting Gnus."
249 (defcustom gnus-after-exiting-gnus-hook nil
250 "Hook called after exiting Gnus."
254 (defcustom gnus-group-update-hook '(gnus-group-highlight-line)
255 "Hook called when a group line is changed.
256 The hook will not be called if `gnus-visual' is nil.
258 The default function `gnus-group-highlight-line' will
259 highlight the line according to the `gnus-group-highlight'
261 :group 'gnus-group-visual
264 (defcustom gnus-useful-groups
265 '(("(ding) mailing list mirrored at sunsite.auc.dk"
267 (nntp "sunsite.auc.dk"
268 (nntp-address "sunsite.auc.dk")))
271 (nndir "/ftp@ftp.ifi.uio.no:/pub/emacs/gnus/gnus-bug/"))
275 (nndoc-article-type mbox)
276 (eval `(nndoc-address
277 ,(let ((file (nnheader-find-etc-directory
280 (error "Couldn't find doc group"))
282 "*Alist of useful group-server pairs."
283 :group 'gnus-group-listing
284 :type '(repeat (list (string :tag "Description")
286 (sexp :tag "Method"))))
288 (defcustom gnus-group-highlight
290 ((and (= unread 0) (not mailp) (eq level 1)) .
291 gnus-group-news-1-empty-face)
292 ((and (not mailp) (eq level 1)) .
293 gnus-group-news-1-face)
294 ((and (= unread 0) (not mailp) (eq level 2)) .
295 gnus-group-news-2-empty-face)
296 ((and (not mailp) (eq level 2)) .
297 gnus-group-news-2-face)
298 ((and (= unread 0) (not mailp) (eq level 3)) .
299 gnus-group-news-3-empty-face)
300 ((and (not mailp) (eq level 3)) .
301 gnus-group-news-3-face)
302 ((and (= unread 0) (not mailp) (eq level 4)) .
303 gnus-group-news-4-empty-face)
304 ((and (not mailp) (eq level 4)) .
305 gnus-group-news-4-face)
306 ((and (= unread 0) (not mailp) (eq level 5)) .
307 gnus-group-news-5-empty-face)
308 ((and (not mailp) (eq level 5)) .
309 gnus-group-news-5-face)
310 ((and (= unread 0) (not mailp) (eq level 6)) .
311 gnus-group-news-6-empty-face)
312 ((and (not mailp) (eq level 6)) .
313 gnus-group-news-6-face)
314 ((and (= unread 0) (not mailp)) .
315 gnus-group-news-low-empty-face)
317 gnus-group-news-low-face)
319 ((and (= unread 0) (eq level 1)) .
320 gnus-group-mail-1-empty-face)
322 gnus-group-mail-1-face)
323 ((and (= unread 0) (eq level 2)) .
324 gnus-group-mail-2-empty-face)
326 gnus-group-mail-2-face)
327 ((and (= unread 0) (eq level 3)) .
328 gnus-group-mail-3-empty-face)
330 gnus-group-mail-3-face)
332 gnus-group-mail-low-empty-face)
334 gnus-group-mail-low-face))
335 "*Controls the highlighting of group buffer lines.
337 Below is a list of `Form'/`Face' pairs. When deciding how a a
338 particular group line should be displayed, each form is
339 evaluated. The content of the face field after the first true form is
340 used. You can change how those group lines are displayed by
341 editing the face field.
343 It is also possible to change and add form fields, but currently that
344 requires an understanding of Lisp expressions. Hopefully this will
345 change in a future release. For now, you can use the following
346 variables in the Lisp expression:
348 group: The name of the group.
349 unread: The number of unread articles in the group.
350 method: The select method used.
351 mailp: Whether it's a mail group or not.
352 level: The level of the group.
353 score: The score of the group.
354 ticked: The number of ticked articles."
355 :group 'gnus-group-visual
356 :type '(repeat (cons (sexp :tag "Form") face)))
358 (defcustom gnus-new-mail-mark ?%
359 "Mark used for groups with new mail."
360 :group 'gnus-group-visual
363 ;;; Internal variables
365 (defvar gnus-group-sort-alist-function 'gnus-group-sort-flat
366 "Function for sorting the group buffer.")
368 (defvar gnus-group-sort-selected-function 'gnus-group-sort-selected-flat
369 "Function for sorting the selected groups in the group buffer.")
371 (defvar gnus-group-indentation-function nil)
372 (defvar gnus-goto-missing-group-function nil)
373 (defvar gnus-group-update-group-function nil)
374 (defvar gnus-group-goto-next-group-function nil
375 "Function to override finding the next group after listing groups.")
377 (defvar gnus-group-edit-buffer nil)
379 (defvar gnus-group-line-format-alist
380 `((?M gnus-tmp-marked-mark ?c)
381 (?S gnus-tmp-subscribed ?c)
382 (?L gnus-tmp-level ?d)
383 (?N (cond ((eq number t) "*" )
387 (gnus-range-length (cdr (assq 'dormant gnus-tmp-marked)))
388 (gnus-range-length (cdr (assq 'tick gnus-tmp-marked))))))
390 (?R gnus-tmp-number-of-read ?s)
391 (?t gnus-tmp-number-total ?d)
392 (?y gnus-tmp-number-of-unread ?s)
393 (?I (gnus-range-length (cdr (assq 'dormant gnus-tmp-marked))) ?d)
394 (?T (gnus-range-length (cdr (assq 'tick gnus-tmp-marked))) ?d)
395 (?i (+ (gnus-range-length (cdr (assq 'dormant gnus-tmp-marked)))
396 (gnus-range-length (cdr (assq 'tick gnus-tmp-marked)))) ?d)
397 (?g gnus-tmp-group ?s)
398 (?G gnus-tmp-qualified-group ?s)
399 (?c (gnus-short-group-name gnus-tmp-group) ?s)
400 (?D gnus-tmp-newsgroup-description ?s)
401 (?o gnus-tmp-moderated ?c)
402 (?O gnus-tmp-moderated-string ?s)
403 (?p gnus-tmp-process-marked ?c)
404 (?s gnus-tmp-news-server ?s)
405 (?n gnus-tmp-news-method ?s)
406 (?P gnus-group-indentation ?s)
407 (?l gnus-tmp-grouplens ?s)
408 (?z gnus-tmp-news-method-string ?s)
409 (?m (gnus-group-new-mail gnus-tmp-group) ?c)
410 (?d (gnus-group-timestamp-string gnus-tmp-group) ?s)
411 (?u gnus-tmp-user-defined ?s)))
413 (defvar gnus-group-mode-line-format-alist
414 `((?S gnus-tmp-news-server ?s)
415 (?M gnus-tmp-news-method ?s)
416 (?u gnus-tmp-user-defined ?s)
417 (?: gnus-tmp-colon ?s)))
419 (defvar gnus-topic-topology nil
420 "The complete topic hierarchy.")
422 (defvar gnus-topic-alist nil
423 "The complete topic-group alist.")
425 (defvar gnus-group-marked nil)
427 (defvar gnus-group-list-mode nil)
433 (put 'gnus-group-mode 'mode-class 'special)
436 (gnus-define-keys gnus-group-mode-map
437 " " gnus-group-read-group
438 "=" gnus-group-select-group
439 "\r" gnus-group-select-group
440 "\M-\r" gnus-group-quick-select-group
441 [(meta control return)] gnus-group-select-group-ephemerally
442 "j" gnus-group-jump-to-group
443 "n" gnus-group-next-unread-group
444 "p" gnus-group-prev-unread-group
445 "\177" gnus-group-prev-unread-group
446 [delete] gnus-group-prev-unread-group
447 [backspace] gnus-group-prev-unread-group
448 "N" gnus-group-next-group
449 "P" gnus-group-prev-group
450 "\M-n" gnus-group-next-unread-group-same-level
451 "\M-p" gnus-group-prev-unread-group-same-level
452 "," gnus-group-best-unread-group
453 "." gnus-group-first-unread-group
454 "u" gnus-group-unsubscribe-current-group
455 "U" gnus-group-unsubscribe-group
456 "c" gnus-group-catchup-current
457 "C" gnus-group-catchup-current-all
458 "\M-c" gnus-group-clear-data