1 ;;; gnus-topic.el --- a folding minor mode for Gnus group buffers
2 ;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
3 ;; Free Software Foundation, Inc.
5 ;; Author: Ilja Weis <kult@uni-paderborn.de>
6 ;; Lars Magne Ingebrigtsen <larsi@gnus.org>
9 ;; This file is part of GNU Emacs.
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
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
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
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
30 (eval-when-compile (require 'cl))
37 (defgroup gnus-topic nil
41 (defvar gnus-topic-mode nil
42 "Minor mode for Gnus group buffers.")
44 (defcustom gnus-topic-mode-hook nil
45 "Hook run in topic mode buffers."
49 (when (featurep 'xemacs)
50 (add-hook 'gnus-topic-mode-hook 'gnus-xmas-topic-menu-add))
52 (defcustom gnus-topic-line-format "%i[ %(%{%n%}%) -- %A ]%v\n"
53 "Format of topic lines.
54 It works along the same lines as a normal formatting string,
55 with some simple extensions.
57 %i Indentation based on topic level.
59 %v Nothing if the topic is visible, \"...\" otherwise.
60 %g Number of groups in the topic.
61 %a Number of unread articles in the groups in the topic.
62 %A Number of unread articles in the groups in the topic and its subtopics.
64 General format specifiers can also be used.
65 See Info node `(gnus)Formatting Variables'."
66 :link '(custom-manual "(gnus)Formatting Variables")
70 (defcustom gnus-topic-indent-level 2
71 "*How much each subtopic should be indented."
75 (defcustom gnus-topic-display-empty-topics t
76 "*If non-nil, display the topic lines even of topics that have no unread articles."
80 ;; Internal variables.
82 (defvar gnus-topic-active-topology nil)
83 (defvar gnus-topic-active-alist nil)
84 (defvar gnus-topic-unreads nil)
86 (defvar gnus-topology-checked-p nil
87 "Whether the topology has been checked in this session.")
89 (defvar gnus-topic-killed-topics nil)
90 (defvar gnus-topic-inhibit-change-level nil)
92 (defconst gnus-topic-line-format-alist
96 (?g number-of-groups ?d)
97 (?a (gnus-topic-articles-in-topic entries) ?d)
98 (?A total-number-of-articles ?d)
101 (defvar gnus-topic-line-format-spec nil)
103 ;;; Utility functions
105 (defun gnus-group-topic-name ()
106 "The name of the topic on the current line."
107 (let ((topic (get-text-property (gnus-point-at-bol) 'gnus-topic)))
108 (and topic (symbol-name topic))))
110 (defun gnus-group-topic-level ()
111 "The level of the topic on the current line."
112 (get-text-property (gnus-point-at-bol) 'gnus-topic-level))
114 (defun gnus-group-topic-unread ()
115 "The number of unread articles in topic on the current line."
116 (get-text-property (gnus-point-at-bol) 'gnus-topic-unread))
118 (defun gnus-topic-unread (topic)
119 "Return the number of unread articles in TOPIC."
120 (or (cdr (assoc topic gnus-topic-unreads))
123 (defun gnus-group-topic-p ()
124 "Return non-nil if the current line is a topic."
125 (gnus-group-topic-name))
127 (defun gnus-topic-visible-p ()
128 "Return non-nil if the current topic is visible."
129 (get-text-property (gnus-point-at-bol) 'gnus-topic-visible))
131 (defun gnus-topic-articles-in-topic (entries)
135 (when (numberp (setq number (car (pop entries))))
136 (incf total number)))
139 (defun gnus-group-topic (group)
140 "Return the topic GROUP is a member of."
141 (let ((alist gnus-topic-alist)
144 (when (member group (cdar alist))
145 (setq out (caar alist)
147 (setq alist (cdr alist)))
150 (defun gnus-group-parent-topic (group)
151 "Return the topic GROUP is member of by looking at the group buffer."
153 (set-buffer gnus-group-buffer)
154 (if (gnus-group-goto-group group)
156 (gnus-group-topic group))))
158 (defun gnus-topic-goto-topic (topic)
160 (gnus-goto-char (text-property-any (point-min) (point-max)
161 'gnus-topic (intern topic)))))
163 (defun gnus-topic-jump-to-topic (topic)
166 (list (completing-read "Go to topic: "
167 (mapcar 'list (gnus-topic-list))
169 (dolist (topic (gnus-current-topics topic))
170 (gnus-topic-goto-topic topic)
172 (gnus-topic-goto-topic topic))
174 (defun gnus-current-topic ()
175 "Return the name of the current topic."
177 (or (get-text-property (point) 'gnus-topic)
179 (and (gnus-goto-char (previous-single-property-change
180 (point) 'gnus-topic))
181 (get-text-property (max (1- (point)) (point-min))
184 (symbol-name result))))
186 (defun gnus-current-topics (&optional topic)
187 "Return a list of all current topics, lowest in hierarchy first.
188 If TOPIC, start with that topic."
189 (let ((topic (or topic (gnus-current-topic)))
193 (setq topic (gnus-topic-parent-topic topic)))
196 (defun gnus-group-active-topic-p ()
197 "Say whether the current topic comes from the active topics."
200 (get-text-property (point) 'gnus-active)))
202 (defun gnus-topic-find-groups (topic &optional level all lowest recursive)
203 "Return entries for all visible groups in TOPIC.
204 If RECURSIVE is t, return groups in its subtopics too."
205 (let ((groups (cdr (assoc topic gnus-topic-alist)))
206 info clevel unread group params visible-groups entry active)
207 (setq lowest (or lowest 1))
208 (setq level (or level gnus-level-unsubscribed))
209 ;; We go through the newsrc to look for matches.
211 (when (setq group (pop groups))
212 (setq entry (gnus-gethash group gnus-newsrc-hashtb)
214 params (gnus-info-params info)
215 active (gnus-active group)
216 unread (or (car entry)
217 (and (not (equal group "dummy.group"))
219 (- (1+ (cdr active)) (car active))))
220 clevel (or (gnus-info-level info)
221 (if (member group gnus-zombie-list)
222 gnus-level-zombie gnus-level-killed))))
224 info ; nil means that the group is dead.
226 (>= clevel lowest) ; Is inside the level we want.
228 (if (or (eq unread t)
230 gnus-group-list-inactive-groups
232 (and gnus-list-groups-with-ticked-articles
233 (cdr (assq 'tick (gnus-info-marks info))))
234 ;; Has right readedness.
235 ;; Check for permanent visibility.
236 (and gnus-permanently-visible-groups
237 (string-match gnus-permanently-visible-groups group))
238 (memq 'visible params)
239 (cdr (assq 'visible params)))
240 ;; Add this group to the list of visible groups.
241 (push (or entry group) visible-groups)))
242 (setq visible-groups (nreverse visible-groups))
245 (setq recursive (cdr (gnus-topic-find-topology topic))))
246 (mapcar (lambda (topic-topology)
248 (nconc visible-groups
249 (gnus-topic-find-groups
250 (caar topic-topology)
251 level all lowest topic-topology))))
255 (defun gnus-topic-goto-previous-topic (n)
256 "Go to the N'th previous topic."
258 (gnus-topic-goto-next-topic (- n)))
260 (defun gnus-topic-goto-next-topic (n)
261 "Go to the N'th next topic."
263 (let ((backward (< n 0))
265 (topic (gnus-current-topic)))
269 (gnus-topic-previous-topic topic)
270 (gnus-topic-next-topic topic))))
271 (gnus-topic-goto-topic topic)