Add hooks for gcc handling
[gnus] / lisp / gnus-topic.el
index 11f07da..0c6c2d3 100644 (file)
@@ -1,16 +1,17 @@
 ;;; gnus-topic.el --- a folding minor mode for Gnus group buffers
-;; Copyright (C) 1995,96 Free Software Foundation, Inc.
+
+;; Copyright (C) 1995-2012 Free Software Foundation, Inc.
 
 ;; Author: Ilja Weis <kult@uni-paderborn.de>
-;;     Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
+;;     Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 ;;; Code:
 
-(require 'gnus)
 (eval-when-compile (require 'cl))
 
+(require 'gnus)
+(require 'gnus-group)
+(require 'gnus-start)
+(require 'gnus-util)
+
+(defgroup gnus-topic nil
+  "Group topics."
+  :group 'gnus-group)
+
 (defvar gnus-topic-mode nil
   "Minor mode for Gnus group buffers.")
 
-(defvar gnus-topic-mode-hook nil
-  "Hook run in topic mode buffers.")
+(defcustom gnus-topic-mode-hook nil
+  "Hook run in topic mode buffers."
+  :type 'hook
+  :group 'gnus-topic)
+
+(when (featurep 'xemacs)
+  (add-hook 'gnus-topic-mode-hook 'gnus-xmas-topic-menu-add))
 
-(defvar gnus-topic-line-format "%i[ %(%{%n%}%) -- %A ]%v\n"
+(defcustom gnus-topic-line-format "%i[ %(%{%n%}%) -- %A ]%v\n"
   "Format of topic lines.
 It works along the same lines as a normal formatting string,
 with some simple extensions.
@@ -46,22 +58,34 @@ with some simple extensions.
 %g  Number of groups in the topic.
 %a  Number of unread articles in the groups in the topic.
 %A  Number of unread articles in the groups in the topic and its subtopics.
-")
 
-(defvar gnus-topic-indent-level 2
-  "*How much each subtopic should be indented.")
+General format specifiers can also be used.
+See Info node `(gnus)Formatting Variables'."
+  :link '(custom-manual "(gnus)Formatting Variables")
+  :type 'string
+  :group 'gnus-topic)
+
+(defcustom gnus-topic-indent-level 2
+  "*How much each subtopic should be indented."
+  :type 'integer
+  :group 'gnus-topic)
+
+(defcustom gnus-topic-display-empty-topics t
+  "*If non-nil, display the topic lines even of topics that have no unread articles."
+  :type 'boolean
+  :group 'gnus-topic)
 
 ;; Internal variables.
 
 (defvar gnus-topic-active-topology nil)
 (defvar gnus-topic-active-alist nil)
+(defvar gnus-topic-unreads nil)
 
 (defvar gnus-topology-checked-p nil
   "Whether the topology has been checked in this session.")
 
 (defvar gnus-topic-killed-topics nil)
 (defvar gnus-topic-inhibit-change-level nil)
-(defvar gnus-topic-tallied-groups nil)
 
 (defconst gnus-topic-line-format-alist
   `((?n name ?s)
@@ -74,170 +98,135 @@ with some simple extensions.
 
 (defvar gnus-topic-line-format-spec nil)
 
-;; Functions.
+;;; Utility functions
 
 (defun gnus-group-topic-name ()
   "The name of the topic on the current line."
-  (let ((topic (get-text-property (gnus-point-at-bol) 'gnus-topic)))
+  (let ((topic (get-text-property (point-at-bol) 'gnus-topic)))
     (and topic (symbol-name topic))))
 
 (defun gnus-group-topic-level ()
   "The level of the topic on the current line."
-  (get-text-property (gnus-point-at-bol) 'gnus-topic-level))
+  (get-text-property (point-at-bol) 'gnus-topic-level))
 
 (defun gnus-group-topic-unread ()
   "The number of unread articles in topic on the current line."
-  (get-text-property (gnus-point-at-bol) 'gnus-topic-unread))
+  (get-text-property (point-at-bol) 'gnus-topic-unread))
 
 (defun gnus-topic-unread (topic)
   "Return the number of unread articles in TOPIC."
-  (or (save-excursion
-       (and (gnus-topic-goto-topic topic)
-            (gnus-group-topic-unread)))
+  (or (cdr (assoc topic gnus-topic-unreads))
       0))
 
-(defun gnus-topic-init-alist ()
-  "Initialize the topic structures."
-  (setq gnus-topic-topology
-       (cons (list "Gnus" 'visible)
-             (mapcar (lambda (topic)
-                       (list (list (car topic) 'visible)))
-                     '(("misc")))))
-  (setq gnus-topic-alist
-       (list (cons "misc"
-                   (mapcar (lambda (info) (gnus-info-group info))
-                           (cdr gnus-newsrc-alist)))
-             (list "Gnus")))
-  (gnus-topic-enter-dribble))
+(defun gnus-group-topic-p ()
+  "Return non-nil if the current line is a topic."
+  (gnus-group-topic-name))
 
-(defun gnus-group-prepare-topics (level &optional all lowest regexp list-topic topic-level)
-  "List all newsgroups with unread articles of level LEVEL or lower, and
-use the `gnus-group-topics' to sort the groups.
-If ALL is non-nil, list groups that have no unread articles.
-If LOWEST is non-nil, list all newsgroups of level LOWEST or higher."
-  (set-buffer gnus-group-buffer)
-  (let ((buffer-read-only nil)
-        (lowest (or lowest 1)))
+(defun gnus-topic-visible-p ()
+  "Return non-nil if the current topic is visible."
+  (get-text-property (point-at-bol) 'gnus-topic-visible))
 
-    (setq gnus-topic-tallied-groups nil)
+(defun gnus-topic-articles-in-topic (entries)
+  (let ((total 0)
+       number)
+    (while entries
+      (when (numberp (setq number (car (pop entries))))
+       (incf total number)))
+    total))
 
-    (when (or (not gnus-topic-alist)
-             (not gnus-topology-checked-p))
-      (gnus-topic-check-topology))
+(defun gnus-group-topic (group)
+  "Return the topic GROUP is a member of."
+  (let ((alist gnus-topic-alist)
+       out)
+    (while alist
+      (when (member group (cdar alist))
+       (setq out (caar alist)
+             alist nil))
+      (setq alist (cdr alist)))
+    out))
 
-    (unless list-topic 
-      (erase-buffer))
-    
-    ;; List dead groups?
-    (when (and (>= level gnus-level-zombie) (<= lowest gnus-level-zombie))
-      (gnus-group-prepare-flat-list-dead 
-       (setq gnus-zombie-list (sort gnus-zombie-list 'string<)) 
-       gnus-level-zombie ?Z
-       regexp))
-    
-    (when (and (>= level gnus-level-killed) (<= lowest gnus-level-killed))
-      (gnus-group-prepare-flat-list-dead 
-       (setq gnus-killed-list (sort gnus-killed-list 'string<))
-       gnus-level-killed ?K
-       regexp))
+(defun gnus-group-parent-topic (group)
+  "Return the topic GROUP is member of by looking at the group buffer."
+  (with-current-buffer gnus-group-buffer
+    (if (gnus-group-goto-group group)
+       (gnus-current-topic)
+      (gnus-group-topic group))))
 
-    ;; Use topics.
-    (when (< lowest gnus-level-zombie)
-      (if list-topic
-         (let ((top (gnus-topic-find-topology list-topic)))
-           (gnus-topic-prepare-topic (cdr top) (car top)
-                                     (or topic-level level) all))
-       (gnus-topic-prepare-topic gnus-topic-topology 0
-                                 (or topic-level level) all))))
-
-  (gnus-group-set-mode-line)
-  (setq gnus-group-list-mode (cons level all))
-  (run-hooks 'gnus-group-prepare-hook))
-
-(defun gnus-topic-prepare-topic (topicl level &optional list-level all silent)
-  "Insert TOPIC into the group buffer.
-If SILENT, don't insert anything.  Return the number of unread
-articles in the topic and its subtopics."
-  (let* ((type (pop topicl))
-        (entries (gnus-topic-find-groups (car type) list-level all))
-        (visiblep (and (eq (nth 1 type) 'visible) (not silent)))
-        (gnus-group-indentation 
-         (make-string (* gnus-topic-indent-level level) ? ))
-        (beg (progn (beginning-of-line) (point)))
-        (topicl (reverse topicl))
-        (all-entries entries)
-        (unread 0)
-        (topic (car type))
-        info entry end active)
-    ;; Insert any sub-topics.
-    (while topicl
-      (incf unread
-           (gnus-topic-prepare-topic 
-            (pop topicl) (1+ level) list-level all
-            (not visiblep))))
-    (setq end (point))
-    (goto-char beg)
-    ;; Insert all the groups that belong in this topic.
-    (while (setq entry (pop entries))
-      (when visiblep 
-       (if (stringp entry)
-           ;; Dead groups.
-           (gnus-group-insert-group-line
-            entry (if (member entry gnus-zombie-list) 8 9)
-            nil (- (1+ (cdr (setq active (gnus-active entry))))
-                   (car active)) nil)
-         ;; Living groups.
-         (when (setq info (nth 2 entry))
-           (gnus-group-insert-group-line 
-            (gnus-info-group info)
-            (gnus-info-level info) (gnus-info-marks info) 
-            (car entry) (gnus-info-method info)))))
-      (when (and (listp entry)
-                (numberp (car entry))
-                (not (member (gnus-info-group (setq info (nth 2 entry)))
-                             gnus-topic-tallied-groups)))
-       (push (gnus-info-group info) gnus-topic-tallied-groups)
-       (incf unread (car entry))))
-    (goto-char beg)
-    ;; Insert the topic line.
-    (unless silent
-      (gnus-extent-start-open (point))
-      (gnus-topic-insert-topic-line 
-       (car type) visiblep
-       (not (eq (nth 2 type) 'hidden))
-       level all-entries unread))
-    (goto-char end)
-    unread))
+(defun gnus-topic-goto-topic (topic)
+  (when topic
+    (gnus-goto-char (text-property-any (point-min) (point-max)
+                                      'gnus-topic (intern topic)))))
+
+(defun gnus-topic-jump-to-topic (topic)
+  "Go to TOPIC."
+  (interactive
+   (list (gnus-completing-read "Go to topic" (gnus-topic-list) t)))
+  (let ((buffer-read-only nil))
+    (dolist (topic (gnus-current-topics topic))
+      (unless (gnus-topic-goto-topic topic)
+       (gnus-topic-goto-missing-topic topic)
+       (gnus-topic-display-missing-topic topic))))
+  (gnus-topic-goto-topic topic))
+
+(defun gnus-current-topic ()
+  "Return the name of the current topic."
+  (let ((result
+        (or (get-text-property (point) 'gnus-topic)
+            (save-excursion
+              (and (gnus-goto-char (previous-single-property-change
+                                    (point) 'gnus-topic))
+                   (get-text-property (max (1- (point)) (point-min))
+                                      'gnus-topic))))))
+    (when result
+      (symbol-name result))))
 
-(defun gnus-topic-find-groups (topic &optional level all)
-  "Return entries for all visible groups in TOPIC."
+(defun gnus-current-topics (&optional topic)
+  "Return a list of all current topics, lowest in hierarchy first.
+If TOPIC, start with that topic."
+  (let ((topic (or topic (gnus-current-topic)))
+       topics)
+    (while topic
+      (push topic topics)
+      (setq topic (gnus-topic-parent-topic topic)))
+    (nreverse topics)))
+
+(defun gnus-group-active-topic-p ()
+  "Say whether the current topic comes from the active topics."
+  (get-text-property (point-at-bol) 'gnus-active))
+
+(defun gnus-topic-find-groups (topic &optional level all lowest recursive)
+  "Return entries for all visible groups in TOPIC.
+If RECURSIVE is t, return groups in its subtopics too."
   (let ((groups (cdr (assoc topic gnus-topic-alist)))
-        info clevel unread group lowest params visible-groups entry active)
+       info clevel unread group params visible-groups entry active)
     (setq lowest (or lowest 1))
-    (setq level (or level 7))
+    (setq level (or level gnus-level-unsubscribed))
     ;; We go through the newsrc to look for matches.
     (while groups
-      (setq entry (gnus-gethash (setq group (pop groups)) gnus-newsrc-hashtb)
-           info (nth 2 entry)
-           params (gnus-info-params info)
-           active (gnus-active group)
-            unread (or (car entry)
-                      (and (not (equal group "dummy.group"))
-                           active
-                           (- (1+ (cdr active)) (car active))))
-           clevel (or (gnus-info-level info)
-                      (if (member group gnus-zombie-list) 8 9)))
-      (and 
-       unread                          ; nil means that the group is dead.
-       (<= clevel level) 
+      (when (setq group (pop groups))
+       (setq entry (gnus-group-entry group)
+             info (nth 2 entry)
+             params (gnus-info-params info)
+             active (gnus-active group)
+             unread (or (car entry)
+                        (and (not (equal group "dummy.group"))
+                             active
+                             (- (1+ (cdr active)) (car active))))
+             clevel (or (gnus-info-level info)
+                        (if (member group gnus-zombie-list)
+                            gnus-level-zombie gnus-level-killed))))
+      (and
+       info                            ; nil means that the group is dead.
+       (<= clevel level)
        (>= clevel lowest)              ; Is inside the level we want.
        (or all
-          (if (eq unread t)
+          (if (or (eq unread t)
+                  (eq unread nil))
               gnus-group-list-inactive-groups
             (> unread 0))
           (and gnus-list-groups-with-ticked-articles
                (cdr (assq 'tick (gnus-info-marks info))))
-                                       ; Has right readedness.
+          ;; Has right readedness.
           ;; Check for permanent visibility.
           (and gnus-permanently-visible-groups
                (string-match gnus-permanently-visible-groups group))
@@ -245,73 +234,39 @@ articles in the topic and its subtopics."
           (cdr (assq 'visible params)))
        ;; Add this group to the list of visible groups.
        (push (or entry group) visible-groups)))
-    (nreverse visible-groups)))
-
-(defun gnus-topic-remove-topic (&optional insert total-remove hide in-level)
-  "Remove the current topic."
-  (let ((topic (gnus-group-topic-name))
-       (level (gnus-group-topic-level))
-       (beg (progn (beginning-of-line) (point)))
-       buffer-read-only)
-    (when topic
-      (while (and (zerop (forward-line 1))
-                 (> (or (gnus-group-topic-level) (1+ level)) level)))
-      (delete-region beg (point))
-      (setcar (cdadr (gnus-topic-find-topology topic))
-             (if insert 'visible 'invisible))
-      (when hide
-       (setcdr (cdadr (gnus-topic-find-topology topic))
-               (list hide)))
-      (unless total-remove
-       (gnus-topic-insert-topic topic in-level)))))
-
-(defun gnus-topic-insert-topic (topic &optional level)
-  "Insert TOPIC."
-  (gnus-group-prepare-topics 
-   (car gnus-group-list-mode) (cdr gnus-group-list-mode)
-   nil nil topic level))
-  
-(defun gnus-topic-fold (&optional insert)
-  "Remove/insert the current topic."
-  (let ((topic (gnus-group-topic-name))) 
-    (when topic
-      (save-excursion
-       (if (not (gnus-group-active-topic-p))
-           (gnus-topic-remove-topic
-            (or insert (not (gnus-topic-visible-p))))
-         (let ((gnus-topic-topology gnus-topic-active-topology)
-               (gnus-topic-alist gnus-topic-active-alist)
-               (gnus-group-list-mode (cons 5 t)))
-           (gnus-topic-remove-topic
-            (or insert (not (gnus-topic-visible-p))) nil nil 9)))))))
-
-(defun gnus-group-topic-p ()
-  "Return non-nil if the current line is a topic."
-  (gnus-group-topic-name))
-
-(defun gnus-topic-visible-p ()
-  "Return non-nil if the current topic is visible."
-  (get-text-property (gnus-point-at-bol) 'gnus-topic-visible))
+    (setq visible-groups (nreverse visible-groups))
+    (when recursive
+      (if (eq recursive t)
+         (setq recursive (cdr (gnus-topic-find-topology topic))))
+      (dolist (topic-topology (cdr recursive))
+       (setq visible-groups
+             (nconc visible-groups
+                    (gnus-topic-find-groups
+                     (caar topic-topology)
+                     level all lowest topic-topology)))))
+    visible-groups))
+
+(defun gnus-topic-goto-previous-topic (n)
+  "Go to the N'th previous topic."
+  (interactive "p")
+  (gnus-topic-goto-next-topic (- n)))
 
-(defun gnus-topic-insert-topic-line (name visiblep shownp level entries 
-                                         &optional unread)
-  (let* ((visible (if visiblep "" "..."))
-        (indentation (make-string (* gnus-topic-indent-level level) ? ))
-        (total-number-of-articles unread)
-        (number-of-groups (length entries))
-        (active-topic (eq gnus-topic-alist gnus-topic-active-alist)))
-    (beginning-of-line)
-    ;; Insert the text.
-    (gnus-add-text-properties 
-     (point)
-     (prog1 (1+ (point)) 
-       (eval gnus-topic-line-format-spec)
-       (gnus-topic-remove-excess-properties)1)
-     (list 'gnus-topic (intern name)
-          'gnus-topic-level level
-          'gnus-topic-unread unread
-          'gnus-active active-topic
-          'gnus-topic-visible visiblep))))
+(defun gnus-topic-goto-next-topic (n)
+  "Go to the N'th next topic."
+  (interactive "p")
+  (let ((backward (< n 0))
+       (n (abs n))
+       (topic (gnus-current-topic)))
+    (while (and (> n 0)
+               (setq topic
+                     (if backward
+                         (gnus-topic-previous-topic topic)
+                   &