*** empty log message ***
[gnus] / lisp / gnus-topic.el
index fd27326..cf4f601 100644 (file)
 
 ;;; Code:
 
-(require 'gnus-load)
+(require 'gnus)
 (require 'gnus-group)
 (require 'gnus-start)
-(require 'gnus)
 
 (defvar gnus-topic-mode nil
   "Minor mode for Gnus group buffers.")
@@ -189,7 +188,7 @@ with some simple extensions.
                       (if (member group gnus-zombie-list) 8 9)))
       (and 
        unread                          ; nil means that the group is dead.
-       (<= clevel level) 
+       (<= clevel level)
        (>= clevel lowest)              ; Is inside the level we want.
        (or all
           (if (eq unread t)
@@ -288,14 +287,14 @@ with some simple extensions.
       (error "No such topic: %s" topic))
     ;; We may have to extend if there is no parameters here
     ;; to begin with.
-    (unless (nthcdr 2 (car top))
-      (nconc (car top) (list nil)))
-    (unless (nthcdr 3 (car top))
-      (nconc (car top) (list nil)))
-    (setcar (nthcdr 3 (car top)) parameters)))
+    (unless (nthcdr 2 (cadr top))
+      (nconc (cadr top) (list nil)))
+    (unless (nthcdr 3 (cadr top))
+      (nconc (cadr top) (list nil)))
+    (setcar (nthcdr 3 (cadr top)) parameters)))
 
 (defun gnus-group-topic-parameters (group)
-  "Compute the group parameters for GROUP taking into account inheretance from topics."
+  "Compute the group parameters for GROUP taking into account inheritance from topics."
   (let ((params-list (list (gnus-group-get-parameter group)))
        topics params param out)
     (save-excursion
@@ -307,7 +306,7 @@ with some simple extensions.
       ;; we remove them.  Probably faster than doing this "properly".
       (setq params-list (delq nil params-list))
       ;; Now we have all the parameters, so we go through them
-      ;; and do inheretance in the obvious way.
+      ;; and do inheritance in the obvious way.
       (while (setq params (pop params-list))
        (while (setq param (pop params))
          (when (atom param)
@@ -318,7 +317,7 @@ with some simple extensions.
       ;; Return the resulting parameter list.
       out)))
 
-;;; General utility funtions
+;;; General utility functions
 
 (defun gnus-topic-enter-dribble ()
   (gnus-dribble-enter
@@ -347,7 +346,7 @@ If LOWEST is non-nil, list all newsgroups of level LOWEST or higher."
     ;; 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<)) 
+       (setq gnus-zombie-list (sort gnus-zombie-list 'string<))
        gnus-level-zombie ?Z
        regexp))
     
@@ -401,12 +400,13 @@ articles in the topic and its subtopics."
            (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)
+                   (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) 
+            (gnus-info-level info) (gnus-info-marks info)
             (car entry) (gnus-info-method info)))))
       (when (and (listp entry)
                 (numberp (car entry))
@@ -437,12 +437,18 @@ articles in the topic and its subtopics."
       (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
+      ;; Do the change in this rather odd manner because it has been
+      ;; reported that some topics share parts of some lists, for some
+      ;; reason.  I have been unable to determine why this is the
+      ;; case, but this hack seems to take care of things.
+      (let ((data (cadr (gnus-topic-find-topology topic))))
+       (setcdr data
+               (list (if insert 'visible 'invisible)
+                     (if hide 'hide nil)
+                     (cadddr data))))
+      (if total-remove
+         (setq gnus-topic-alist
+               (delq (assoc topic gnus-topic-alist) gnus-topic-alist))
        (gnus-topic-insert-topic topic in-level)))))
 
 (defun gnus-topic-insert-topic (topic &optional level)
@@ -453,7 +459,7 @@ articles in the topic and its subtopics."
   
 (defun gnus-topic-fold (&optional insert)
   "Remove/insert the current topic."
-  (let ((topic (gnus-group-topic-name))) 
+  (let ((topic (gnus-group-topic-name)))
     (when topic
       (save-excursion
        (if (not (gnus-group-active-topic-p))
@@ -476,7 +482,7 @@ articles in the topic and its subtopics."
     ;; Insert the text.
     (gnus-add-text-properties 
      (point)
-     (prog1 (1+ (point)) 
+     (prog1 (1+ (point))
        (eval gnus-topic-line-format-spec)
        (gnus-topic-remove-excess-properties)1)
      (list 'gnus-topic (intern name)
@@ -485,19 +491,36 @@ articles in the topic and its subtopics."
           'gnus-active active-topic
           'gnus-topic-visible visiblep))))
 
+(defun gnus-topic-update-topics-containing-group (group)
+  "Update all topics that have GROUP as a member."
+  (when (and (eq major-mode 'gnus-group-mode)
+            gnus-topic-mode)
+    (save-excursion
+      (let ((alist gnus-topic-alist))
+       ;; This is probably not entirely correct.  If a topic
+       ;; isn't shown, then it's not updated.  But the updating
+       ;; should be performed in any case, since the topic's
+       ;; parent should be updated.  Pfft.
+       (while alist
+         (when (and (member group (cdar alist))
+                    (gnus-topic-goto-topic (caar alist)))
+           (gnus-topic-update-topic-line (caar alist)))
+         (pop alist))))))
+
 (defun gnus-topic-update-topic ()
   "Update all parent topics to the current group."
   (when (and (eq major-mode 'gnus-group-mode)
             gnus-topic-mode)
     (let ((group (gnus-group-group-name))
          (buffer-read-only nil))
-      (when (and group (gnus-get-info group)
+      (when (and group 
+                (gnus-get-info group)
                 (gnus-topic-goto-topic (gnus-current-topic)))
        (gnus-topic-update-topic-line (gnus-group-topic-name))
        (gnus-group-goto-group group)
        (gnus-group-position-point)))))
 
-(defun gnus-topic-goto-missing-group (group) 
+(defun gnus-topic-goto-missing-group (group)
   "Place point where GROUP is supposed to be inserted."
   (let* ((topic (gnus-group-topic group))
         (groups (cdr (assoc topic gnus-topic-alist)))
@@ -554,8 +577,11 @@ articles in the topic and its subtopics."
   (make-string 
    (* gnus-topic-indent-level
       (or (save-excursion
+           (forward-line -1)
            (gnus-topic-goto-topic (gnus-current-topic))
-           (gnus-group-topic-level)) 0)) ? ))
+           (gnus-group-topic-level))
+         0))
+   ? ))
 
 ;;; Initialization
 
@@ -568,7 +594,7 @@ articles in the topic and its subtopics."
        gnus-topic-tallied-groups nil
        gnus-topology-checked-p nil))
 
-(defun gnus-topic-check-topology ()  
+(defun gnus-topic-check-topology ()
   ;; The first time we set the topology to whatever we have
   ;; gotten here, which can be rather random.
   (unless gnus-topic-alist
@@ -639,9 +665,10 @@ articles in the topic and its subtopics."
       (let ((topic-name (pop topic))
            group filtered-topic)
        (while (setq group (pop topic))
-         (if (and (gnus-gethash group gnus-active-hashtb)
-                  (not (gnus-gethash group gnus-killed-hashtb)))
-             (push group filtered-topic)))
+         (when (and (or (gnus-gethash group gnus-active-hashtb)
+                        (gnus-info-method (gnus-get-info group)))
+                    (not (gnus-gethash group gnus-killed-hashtb)))
+           (push group filtered-topic)))
        (push (cons topic-name (nreverse filtered-topic)) result)))
     (setq gnus-topic-alist (nreverse result))))
 
@@ -659,7 +686,7 @@ articles in the topic and its subtopics."
          (forward-line -1)
          (when (setq alist (assoc (gnus-current-topic) gnus-topic-alist))
            (setcdr alist (gnus-delete-first group (cdr alist))))))
-      ;; If the group is subscribed. then we enter it into the topics.
+      ;; If the group is subscribed.  then we enter it into the topics.
       (when (and (< level gnus-level-zombie)
                 (>= oldlevel gnus-level-zombie))
        (let* ((prev (gnus-group-group-name))
@@ -669,7 +696,9 @@ articles in the topic and its subtopics."
                 (* gnus-topic-indent-level
                    (or (save-excursion
                          (gnus-topic-goto-topic (gnus-current-topic))
-                         (gnus-group-topic-level)) 0)) ? ))
+                         (gnus-group-topic-level))
+                       0))
+                ? ))
               (yanked (list group))
               alist talist end)
          ;; Then we enter the yanked groups into the topics they belong
@@ -703,7 +732,7 @@ articles in the topic and its subtopics."
     (if (gnus-group-goto-group group)
        t
       ;; The group is no longer visible.
-      (let* ((list (assoc (gnus-current-topic) gnus-topic-alist))
+      (let* ((list (assoc (gnus-group-topic group) gnus-topic-alist))
             (after (cdr (member group (cdr list)))))
        ;; First try to put point on a group after the current one.
        (while (and after
@@ -782,35 +811,42 @@ articles in the topic and its subtopics."
   (setq gnus-topic-mode-map (make-sparse-keymap))
 
   ;; Override certain group mode keys.
-  (gnus-define-keys
-   gnus-topic-mode-map
-   "=" gnus-topic-select-group
-   "\r" gnus-topic-select-group
-   " " gnus-topic-read-group
-   "\C-k" gnus-topic-kill-group
-   "\C-y" gnus-topic-yank-group
-   "\M-g" gnus-topic-get-new-news-this-topic
-   "AT" gnus-topic-list-active
-   "Gp" gnus-topic-edit-parameters
-   gnus-mouse-2 gnus-mouse-pick-topic)
+  (gnus-define-keys gnus-topic-mode-map
+    "=" gnus-topic-select-group
+    "\r" gnus-topic-select-group
+    " " gnus-topic-read-group
+    "\C-k" gnus-topic-kill-group
+    "\C-y" gnus-topic-yank-group
+    "\M-g" gnus-topic-get-new-news-this-topic
+    "AT" gnus-topic-list-active
+    "Gp" gnus-topic-edit-parameters
+    gnus-mouse-2 gnus-mouse-pick-topic)
 
   ;; Define a new submap.
-  (gnus-define-keys
-   (gnus-group-topic-map "T" gnus-group-mode-map)
-   "#" gnus-topic-mark-topic
-   "\M-#" gnus-topic-unmark-topic
-   "n" gnus-topic-create-topic
-   "m" gnus-topic-move-group
-   "D" gnus-topic-remove-group
-   "c" gnus-topic-copy-group
-   "h" gnus-topic-hide-topic
-   "s" gnus-topic-show-topic
-   "M" gnus-topic-move-matching
-   "C" gnus-topic-copy-matching
-   "\C-i" gnus-topic-indent
-   [tab] gnus-topic-indent
-   "r" gnus-topic-rename
-   "\177" gnus-topic-delete))
+  (gnus-define-keys (gnus-group-topic-map "T" gnus-group-mode-map)
+    "#" gnus-topic-mark-topic
+    "\M-#" gnus-topic-unmark-topic
+    "n" gnus-topic-create-topic
+    "m" gnus-topic-move-group
+    "D" gnus-topic-remove-group
+    "c" gnus-topic-copy-group
+    "h" gnus-topic-hide-topic
+    "s" gnus-topic-show-topic
+    "M" gnus-topic-move-matching
+    "C" gnus-topic-copy-matching
+    "\C-i" gnus-topic-indent
+    [tab] gnus-topic-indent
+    "r" gnus-topic-rename
+    "\177" gnus-topic-delete)
+
+  (gnus-define-keys (gnus-topic-sort-map "S" gnus-group-topic-map)
+    "s" gnus-topic-sort-groups
+    "a" gnus-topic-sort-groups-by-alphabet
+    "u" gnus-topic-sort-groups-by-unread
+    "l" gnus-topic-sort-groups-by-level
+    "v" gnus-topic-sort-groups-by-score
+    "r" gnus-topic-sort-groups-by-rank
+    "m" gnus-topic-sort-groups-by-method))
 
 (defun gnus-topic-make-menu-bar ()
   (unless (boundp 'gnus-topic-menu)
@@ -857,7 +893,6 @@ articles in the topic and its subtopics."
              minor-mode-map-alist))
       (add-hook 'gnus-summary-exit-hook 'gnus-topic-update-topic)
       (add-hook 'gnus-group-catchup-group-hook 'gnus-topic-update-topic)
-      (add-hook 'gnus-group-update-group-hook 'gnus-topic-update-topic)
       (set (make-local-variable 'gnus-group-prepare-function)
           'gnus-group-prepare-topics)
       (set (make-local-variable 'gnus-group-get-parameter-function)
@@ -866,6 +901,10 @@ articles in the topic and its subtopics."
           'gnus-topic-goto-next-group)
       (set (make-local-variable 'gnus-group-indentation-function)
           'gnus-topic-group-indentation)
+      (set (make-local-variable 'gnus-group-update-group-function)
+          'gnus-topic-update-topics-containing-group)
+      (set (make-local-variable 'gnus-group-sort-alist-function)
+          'gnus-group-sort-topic)
       (setq gnus-group-change-level-function 'gnus-topic-change-level)
       (setq gnus-goto-missing-group-function 'gnus-topic-goto-missing-group)
       (gnus-make-local-hook 'gnus-check-bogus-groups-hook)
@@ -881,7 +920,8 @@ articles in the topic and its subtopics."
       (remove-hook 'gnus-group-change-level-function 
                   'gnus-topic-change-level)
       (remove-hook 'gnus-check-bogus-groups-hook 'gnus-topic-clean-alist)
-      (setq gnus-group-prepare-function 'gnus-group-prepare-flat))
+      (setq gnus-group-prepare-function 'gnus-group-prepare-flat)
+      (setq gnus-group-sort-alist-function 'gnus-group-sort-flat))
     (when redisplay
       (gnus-group-list-groups))))
     
@@ -928,7 +968,7 @@ If performed over a topic line, toggle folding the topic."
     (gnus-current-topic)))
   ;; Check whether this topic already exists.
   (when (gnus-topic-find-topology topic)
-    (error "Topic aleady exists"))
+    (error "Topic already exists"))
   (unless parent
     (setq parent (caar gnus-topic-topology)))
   (let ((top (cdr (gnus-topic-find-topology parent)))
@@ -956,30 +996,36 @@ If COPYP, copy the groups instead."
         (completing-read "Move to topic: " gnus-topic-alist nil t)))
   (let ((groups (gnus-group-process-prefix n))
        (topicl (assoc topic gnus-topic-alist))
+       (start-group (progn (forward-line 1) (gnus-group-group-name)))
+       (start-topic (gnus-group-topic-name))
        entry)
-    (mapcar (lambda (g) 
-             (gnus-group-remove-mark g)
-             (when (and
-                    (setq entry (assoc (gnus-current-topic)
-                                       gnus-topic-alist))
-                    (not copyp))
-               (setcdr entry (gnus-delete-first g (cdr entry))))
-             (nconc topicl (list g)))
-           groups)
-    (gnus-group-position-point))
-  (gnus-topic-enter-dribble)
-  (gnus-group-list-groups))
+    (mapcar 
+     (lambda (g)
+       (gnus-group-remove-mark g)
+       (when (and
+             (setq entry (assoc (gnus-current-topic) gnus-topic-alist))
+             (not copyp))
+        (setcdr entry (gnus-delete-first g (cdr entry))))
+       (nconc topicl (list g)))
+     groups)
+    (gnus-topic-enter-dribble)
+    (if start-group
+       (gnus-group-goto-group start-group)
+      (gnus-topic-goto-topic start-topic))
+    (gnus-group-list-groups)))
 
-(defun gnus-topic-remove-group ()
+(defun gnus-topic-remove-group (&optional arg)
   "Remove the current group from the topic."
-  (interactive)
-  (let ((topicl (assoc (gnus-current-topic) gnus-topic-alist))
-       (group (gnus-group-group-name))
-       (buffer-read-only nil))
-    (when (and topicl group)
-      (gnus-delete-line)
-      (gnus-delete-first group topicl))
-    (gnus-group-position-point)))
+  (interactive "P")
+  (gnus-group-iterate arg 
+    (lambda (group)
+      (let ((topicl (assoc (gnus-current-topic) gnus-topic-alist))
+           (buffer-read-only nil))
+       (when (and topicl group)
+         (gnus-delete-line)
+         (gnus-delete-first group topicl))
+       (gnus-topic-update-topic)
+       (gnus-group-position-point)))))
 
 (defun gnus-topic-copy-group (n topic)
   "Copy the current group to a topic."
@@ -995,7 +1041,8 @@ If COPYP, copy the groups instead."
       (let ((topic (gnus-group-topic-name)))
        (gnus-topic-remove-topic nil t)
        (push (gnus-topic-find-topology topic nil nil gnus-topic-topology)
-             gnus-topic-killed-topics))
+             gnus-topic-killed-topics)
+       (gnus-topic-enter-dribble))
     (gnus-group-kill-group n discard)
     (gnus-topic-update-topic)))
   
@@ -1010,6 +1057,7 @@ If COPYP, copy the groups instead."
        (gnus-topic-create-topic
         (caar item) (gnus-topic-parent-topic previous) previous
         item)
+       (gnus-topic-enter-dribble)
        (gnus-topic-goto-topic (caar item)))
     (let* ((prev (gnus-group-group-name))
           (gnus-topic-inhibit-change-level t)
@@ -1018,7 +1066,9 @@ If COPYP, copy the groups instead."
             (* gnus-topic-indent-level
                (or (save-excursion
                      (gnus-topic-goto-topic (gnus-current-topic))
-                     (gnus-group-topic-level)) 0)) ? ))
+                     (gnus-group-topic-level))
+                   0))
+            ? ))
           yanked alist)
       ;; We first yank the groups the normal way...
       (setq yanked (gnus-group-yank-group arg))
@@ -1175,6 +1225,8 @@ If FORCE, always re-read the active file."
        gnus-killed-list gnus-zombie-list)
     (gnus-group-list-groups 9 nil 1)))
 
+;;; Topic sorting functions
+
 (defun gnus-topic-edit-parameters (group)
   "Edit the group parameters of GROUP.
 If performed on a topic, edit the topic parameters instead."
@@ -1190,6 +1242,75 @@ If performed on a topic, edit the topic parameters instead."
         `(lambda (form)
            (gnus-topic-set-parameters ,topic form)))))))
 
+(defun gnus-group-sort-topic (func reverse)
+  "Sort groups in the topics according to FUNC and REVERSE."
+  (let ((alist gnus-topic-alist))
+    (while alist
+      (gnus-topic-sort-topic (pop alist) func reverse))))
+
+(defun gnus-topic-sort-topic (topic func reverse)
+  ;; Each topic only lists the name of the group, while
+  ;; the sort predicates expect group infos as inputs.
+  ;; So we first transform the group names into infos,
+  ;; then sort, and then transform back into group names.
+  (setcdr
+   topic
+   (mapcar
+    (lambda (info) (gnus-info-group info))
+    (sort
+     (mapcar
+      (lambda (group) (gnus-get-info group))
+      (cdr topic))
+     func)))
+  ;; Do the reversal, if necessary.
+  (when reverse
+    (setcdr topic (nreverse (cdr topic)))))
+
+(defun gnus-topic-sort-groups (func &optional reverse)
+  "Sort the current topic according to FUNC.
+If REVERSE, reverse the sorting order."
+  (interactive (list gnus-group-sort-function current-prefix-arg))
+  (let ((topic (assoc (gnus-current-topic) gnus-topic-alist)))
+    (gnus-topic-sort-topic
+     topic (gnus-make-sort-function func) reverse)
+    (gnus-group-list-groups)))
+
+(defun gnus-topic-sort-groups-by-alphabet (&optional reverse)
+  "Sort the current topic alphabetically by group name.
+If REVERSE, sort in reverse order."
+  (interactive "P")
+  (gnus-topic-sort-groups 'gnus-group-sort-by-alphabet reverse))
+
+(defun gnus-topic-sort-groups-by-unread (&optional reverse)
+  "Sort the current topic by number of unread articles.
+If REVERSE, sort in reverse order."
+  (interactive "P")
+  (gnus-topic-sort-groups 'gnus-group-sort-by-unread reverse))
+
+(defun gnus-topic-sort-groups-by-level (&optional reverse)
+  "Sort the current topic by group level.
+If REVERSE, sort in reverse order."
+  (interactive "P")
+  (gnus-topic-sort-groups 'gnus-group-sort-by-level reverse))
+
+(defun gnus-topic-sort-groups-by-score (&optional reverse)
+  "Sort the current topic by group score.
+If REVERSE, sort in reverse order."
+  (interactive "P")
+  (gnus-topic-sort-groups 'gnus-group-sort-by-score reverse))
+
+(defun gnus-topic-sort-groups-by-rank (&optional reverse)
+  "Sort the current topic by group rank.
+If REVERSE, sort in reverse order."
+  (interactive "P")
+  (gnus-topic-sort-groups 'gnus-group-sort-by-rank reverse))
+
+(defun gnus-topic-sort-groups-by-method (&optional reverse)
+  "Sort the current topic alphabetically by backend name.
+If REVERSE, sort in reverse order."
+  (interactive "P")
+  (gnus-topic-sort-groups 'gnus-group-sort-by-method reverse))
+
 (provide 'gnus-topic)
 
 ;;; gnus-topic.el ends here