Add newlines between pictures and text.
[gnus] / lisp / gnus-topic.el
index 09dce2b..efa543f 100644 (file)
@@ -1,6 +1,7 @@
 ;;; gnus-topic.el --- a folding minor mode for Gnus group buffers
-;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001
-;;        Free Software Foundation, Inc.
+
+;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+;;   2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 ;; Author: Ilja Weis <kult@uni-paderborn.de>
 ;;     Lars Magne Ingebrigtsen <larsi@gnus.org>
@@ -8,10 +9,10 @@
 
 ;; 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
@@ -19,9 +20,7 @@
 ;; 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:
 
@@ -60,7 +59,10 @@ 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.
-"
+
+General format specifiers can also be used.
+See Info node `(gnus)Formatting Variables'."
+  :link '(custom-manual "(gnus)Formatting Variables")
   :type 'string
   :group 'gnus-topic)
 
@@ -101,16 +103,16 @@ with some simple extensions.
 
 (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."
@@ -123,7 +125,7 @@ with some simple extensions.
 
 (defun gnus-topic-visible-p ()
   "Return non-nil if the current topic is visible."
-  (get-text-property (gnus-point-at-bol) 'gnus-topic-visible))
+  (get-text-property (point-at-bol) 'gnus-topic-visible))
 
 (defun gnus-topic-articles-in-topic (entries)
   (let ((total 0)
@@ -146,8 +148,7 @@ with some simple extensions.
 
 (defun gnus-group-parent-topic (group)
   "Return the topic GROUP is member of by looking at the group buffer."
-  (save-excursion
-    (set-buffer gnus-group-buffer)
+  (with-current-buffer gnus-group-buffer
     (if (gnus-group-goto-group group)
        (gnus-current-topic)
       (gnus-group-topic group))))
@@ -160,11 +161,12 @@ with some simple extensions.
 (defun gnus-topic-jump-to-topic (topic)
   "Go to TOPIC."
   (interactive
-   (list (completing-read "Go to topic: "
-                         (mapcar 'list (gnus-topic-list))
-                         nil t)))
-  (dolist (topic (gnus-current-topics topic))
-    (gnus-topic-fold t))
+   (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 ()
@@ -191,9 +193,7 @@ If TOPIC, start with that topic."
 
 (defun gnus-group-active-topic-p ()
   "Say whether the current topic comes from the active topics."
-  (save-excursion
-    (beginning-of-line)
-    (get-text-property (point) 'gnus-active)))
+  (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.
@@ -205,7 +205,7 @@ If RECURSIVE is t, return groups in its subtopics too."
     ;; We go through the newsrc to look for matches.
     (while groups
       (when (setq group (pop groups))
-       (setq entry (gnus-gethash group gnus-newsrc-hashtb)
+       (setq entry (gnus-group-entry group)
              info (nth 2 entry)
              params (gnus-info-params info)
              active (gnus-active group)
@@ -239,15 +239,36 @@ If RECURSIVE is t, return groups in its subtopics too."
     (when recursive
       (if (eq recursive t)
          (setq recursive (cdr (gnus-topic-find-topology topic))))
-      (mapcar (lambda (topic-topology)
-               (setq visible-groups
-                     (nconc visible-groups
-                            (gnus-topic-find-groups
-                             (caar topic-topology)
-                             level all lowest topic-topology))))
-             (cdr recursive)))
+      (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-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)
+                       (gnus-topic-next-topic topic))))
+      (gnus-topic-goto-topic topic)
+      (setq n (1- n)))
+    (when (/= 0 n)
+      (gnus-message 7 "No more topics"))
+    n))
+
 (defun gnus-topic-previous-topic (topic)
   "Return the previous topic on the same level as TOPIC."
   (let ((top (cddr (gnus-topic-find-topology
@@ -324,7 +345,7 @@ If RECURSIVE is t, return groups in its subtopics too."
     (setq topology gnus-topic-topology
          gnus-tmp-topics nil))
   (push (caar topology) gnus-tmp-topics)
-  (mapcar 'gnus-topic-list (cdr topology))
+  (mapc 'gnus-topic-list (cdr topology))
   gnus-tmp-topics)
 
 ;;; Topic parameter jazz
@@ -351,31 +372,50 @@ If RECURSIVE is t, return groups in its subtopics too."
      (format "(gnus-topic-set-parameters %S '%S)" topic parameters))))
 
 (defun gnus-group-topic-parameters (group)
-  "Compute the group parameters for GROUP taking into account inheritance from topics."
+  "Compute the group parameters for GROUP in topic mode.
+Possibly inherit parameters from topics above GROUP."
   (let ((params-list (copy-sequence (gnus-group-get-parameter group))))
     (save-excursion
-      (gnus-group-goto-group group)
-      (nconc params-list
-            (gnus-topic-hierarchical-parameters (gnus-current-topic))))))
-
-(defun gnus-topic-hierarchical-parameters (topic)
-  "Return a topic list computed for TOPIC."
-  (let ((topics (gnus-current-topics topic))
-       params-list param out params)
-    (while topics
-      (push (gnus-topic-parameters (pop topics)) params-list))
-    ;; We probably have lots of nil elements here, so
-    ;; we remove them.  Probably faster than doing this "properly".
-    (setq params-list (delq nil params-list))
+      (gnus-topic-hierarchical-parameters
+       ;; First we try to go to the group within the group buffer and find the
+       ;; topic for the group that way. This hopefully copes well with groups
+       ;; that are in more than one topic. Failing that (i.e. when the group
+       ;; isn't visible in the group buffer) we find a topic for the group via
+       ;; gnus-group-topic.
+       (or (and (gnus-group-goto-group group)
+               (gnus-current-topic))
+          (gnus-group-topic group))
+       params-list))))
+
+(defun gnus-topic-hierarchical-parameters (topic &optional group-params-list)
+  "Compute the topic parameters for TOPIC.
+Possibly inherit parameters from topics above TOPIC.
+If optional argument GROUP-PARAMS-LIST is non-nil, use it as the basis for
+inheritance."
+  (let ((params-list
+        ;; We probably have lots of nil elements here, so we remove them.
+        ;; Probably faster than doing this "properly".
+        (delq nil (cons group-params-list
+                        (mapcar 'gnus-topic-parameters
+                                (gnus-current-topics topic)))))
+       param out params)
     ;; Now we have all the parameters, so we go through them
     ;; and do inheritance in the obvious way.
-    (while (setq params (pop params-list))
-      (while (setq param (pop params))
-       (when (atom param)
-         (setq param (cons param t)))
-       ;; Override any old versions of this param.
-       (gnus-pull (car param) out)
-       (push param out)))
+    (let (posting-style)
+      (while (setq params (pop params-list))
+       (while (setq param (pop params))
+         (when (atom param)
+           (setq param (cons param t)))
+         (cond ((eq (car param) 'posting-style)
+                (let ((param (cdr param))
+                      elt)
+                  (while (setq elt (pop param))
+                    (unless (assoc (car elt) posting-style)
+                      (push elt posting-style)))))
+               (t
+                (unless (assq (car param) out)
+                  (push param out))))))
+      (and posting-style (push (cons 'posting-style posting-style) out)))
     ;; Return the resulting parameter list.
     out))
 
@@ -401,6 +441,8 @@ If LOWEST is non-nil, list all newsgroups of level LOWEST or higher."
         (and gnus-group-listed-groups
              (copy-sequence gnus-group-listed-groups))))
 
+    (gnus-update-format-specifications nil 'topic)
+
     (when (or (not gnus-topic-alist)
              (not gnus-topology-checked-p))
       (gnus-topic-check-topology))
@@ -427,8 +469,8 @@ If LOWEST is non-nil, list all newsgroups of level LOWEST or higher."
        (unless gnus-killed-hashtb
          (gnus-make-hashtable-from-killed))
        (gnus-group-prepare-flat-list-dead
-        (gnus-delete-if (lambda (group)
-                          (or (gnus-gethash group gnus-newsrc-hashtb)
+        (gnus-remove-if (lambda (group)
+                          (or (gnus-group-entry group)
                               (gnus-gethash group gnus-killed-hashtb)))
                         not-in-list)
         gnus-level-killed ?K regexp)))
@@ -548,6 +590,11 @@ articles in the topic and its subtopics."
        (not (eq (nth 2 type) 'hidden))
        level all-entries unread))
     (gnus-topic-update-unreads (car type) unread)
+    (when gnus-group-update-tool-bar
+      (gnus-put-text-property beg end 'point-entered
+                             'gnus-tool-bar-update)
+      (gnus-put-text-property beg end 'point-left
+                             'gnus-tool-bar-update))
     (goto-char end)
     unread))
 
@@ -662,7 +709,8 @@ articles in the topic and its subtopics."
         (unfound t)
         entry)
     ;; Try to jump to a visible group.
-    (while (and g (not (gnus-group-goto-group (car g) t)))
+    (while (and g
+               (not (gnus-group-goto-group (car g) t)))
       (pop g))
     ;; It wasn't visible, so we try to see where to insert it.
     (when (not g)
@@ -674,20 +722,34 @@ articles in the topic and its subtopics."
       (when (and unfound
                 topic
                 (not (gnus-topic-goto-missing-topic topic)))
-       (let* ((top (gnus-topic-find-topology topic))
-              (children (cddr top))
-              (type (cadr top))
-              (unread 0)
-              (entries (gnus-topic-find-groups
-                        (car type) (car gnus-group-list-mode)
-                        (cdr gnus-group-list-mode))))
-         (while children
-           (incf unread (gnus-topic-unread (caar (pop children)))))
-         (while (setq entry (pop entries))
-           (when (numberp (car entry))
-             (incf unread (car entry))))
-         (gnus-topic-insert-topic-line
-          topic t t (car (gnus-topic-find-topology topic)) nil unread))))))
+       (gnus-topic-display-missing-topic topic)))))
+
+(defun gnus-topic-display-missing-topic (topic)
+  "Insert topic lines recursively for missing topics."
+  (let ((parent (gnus-topic-find-topology
+                (gnus-topic-parent-topic topic))))
+    (when (and parent
+              (not (gnus-topic-goto-missing-topic (caadr parent))))
+      (gnus-topic-display-missing-topic (caadr parent))))
+  (gnus-topic-goto-missing-topic topic)
+  ;; Skip past all groups in the topic we're in.
+  (while (gnus-group-group-name)
+    (forward-line 1))
+  (let* ((top (gnus-topic-find-topology topic))
+        (children (cddr top))
+        (type (cadr top))
+        (unread 0)
+        (entries (gnus-topic-find-groups
+                  (car type) (car gnus-group-list-mode)
+                  (cdr gnus-group-list-mode)))
+       entry)
+    (while children
+      (incf unread (gnus-topic-unread (caar (pop children)))))
+    (while (setq entry (pop entries))
+      (when (numberp (car entry))
+       (incf unread (car entry))))
+    (gnus-topic-insert-topic-line
+     topic t t (car (gnus-topic-find-topology topic)) nil unread)))
 
 (defun gnus-topic-goto-missing-topic (topic)
   (if (gnus-topic-goto-topic topic)
@@ -794,8 +856,7 @@ articles in the topic and its subtopics."
       (pop topics)))
   ;; Go through all living groups and make sure that
   ;; they belong to some topic.
-  (let* ((tgroups (apply 'append (mapcar (lambda (entry) (cdr entry))
-                                        gnus-topic-alist)))
+  (let* ((tgroups (apply 'append (mapcar 'cdr gnus-topic-alist)))
         (entry (last (assoc (caar gnus-topic-topology) gnus-topic-alist)))
         (newsrc (cdr gnus-newsrc-alist))
         group)
@@ -809,7 +870,7 @@ articles in the topic and its subtopics."
     (while (setq topic (pop alist))
       (while (cdr topic)
        (if (and (cadr topic)
-                (gnus-gethash (cadr topic) gnus-newsrc-hashtb))
+                (gnus-group-entry (cadr topic)))
            (setq topic (cdr topic))
          (setcdr topic (cddr topic)))))))
 
@@ -839,7 +900,7 @@ articles in the topic and its subtopics."
       (let ((topic-name (pop topic))
            group filtered-topic)
        (while (setq group (pop topic))
-         (when (and (or (gnus-gethash group gnus-active-hashtb)
+         (when (and (or (gnus-active group)
                         (gnus-info-method (gnus-get-info group)))
                     (not (gnus-gethash group gnus-killed-hashtb)))
            (push group filtered-topic)))
@@ -848,8 +909,7 @@ articles in the topic and its subtopics."
 
 (defun gnus-topic-change-level (group level oldlevel &optional previous)
   "Run when changing levels to enter/remove groups from topics."
-  (save-excursion
-    (set-buffer gnus-group-buffer)
+  (with-current-buffer gnus-group-buffer
     (let ((buffer-read-only nil))
       (unless gnus-topic-inhibit-change-level
        (gnus-group-goto-group (or (car (nth 2 previous)) group))
@@ -881,8 +941,8 @@ articles in the topic and its subtopics."
                       ? ))
                     (yanked (list group))
                     alist talist end)
-               ;; Then we enter the yanked groups into the topics they belong
-               ;; to.
+               ;; Then we enter the yanked groups into the topics
+               ;; they belong to.
                (when (setq alist (assoc (save-excursion
                                           (forward-line -1)
                                           (or
@@ -1000,6 +1060,7 @@ articles in the topic and its subtopics."
     "\r" gnus-topic-select-group
     " " gnus-topic-read-group
     "\C-c\C-x" gnus-topic-expire-articles
+    "c" gnus-topic-catchup-articles
     "\C-k" gnus-topic-kill-group
     "\C-y" gnus-topic-yank-group
     "\M-g" gnus-topic-get-new-news-this-topic
@@ -1026,6 +1087,8 @@ articles in the topic and its subtopics."
     "j" gnus-topic-jump-to-topic
     "M" gnus-topic-move-matching
     "C" gnus-topic-copy-matching
+    "\M-p" gnus-topic-goto-previous-topic
+    "\M-n" gnus-topic-goto-next-topic
     "\C-i" gnus-topic-indent
     [tab] gnus-topic-indent
     "r" gnus-topic-rename
@@ -1038,6 +1101,7 @@ articles in the topic and its subtopics."
     "a" gnus-topic-sort-groups-by-alphabet
     "u" gnus-topic-sort-groups-by-unread
     "l" gnus-topic-sort-groups-by-level
+    "e" gnus-topic-sort-groups-by-server
     "v" gnus-topic-sort-groups-by-score
     "r" gnus-topic-sort-groups-by-rank
     "m" gnus-topic-sort-groups-by-method))
@@ -1049,27 +1113,30 @@ articles in the topic and its subtopics."
      '("Topics"
        ["Toggle topics" gnus-topic-mode t]
        ("Groups"
-       ["Copy" gnus-topic-copy-group t]
-       ["Move" gnus-topic-move-group t]
+       ["Copy..." gnus-topic-copy-group t]
+       ["Move..." gnus-topic-move-group t]
        ["Remove" gnus-topic-remove-group t]
-       ["Copy matching" gnus-topic-copy-matching t]
-       ["Move matching" gnus-topic-move-matching t])
+       ["Copy matching..." gnus-topic-copy-matching t]
+       ["Move matching..." gnus-topic-move-matching t])
        ("Topics"
-       ["Goto" gnus-topic-jump-to-topic t]
+       ["Goto..." gnus-topic-jump-to-topic t]
        ["Show" gnus-topic-show-topic t]
        ["Hide" gnus-topic-hide-topic t]
        ["Delete" gnus-topic-delete t]
-       ["Rename" gnus-topic-rename t]
-       ["Create" gnus-topic-create-topic t]
+       ["Rename..." gnus-topic-rename t]
+       ["Create..." gnus-topic-create-topic t]
        ["Mark" gnus-topic-mark-topic t]
        ["Indent" gnus-topic-indent t]
        ["Sort" gnus-topic-sort-topics t]
+       ["Previous topic" gnus-topic-goto-previous-topic t]
+       ["Next topic" gnus-topic-goto-next-topic t]
        ["Toggle hide empty" gnus-topic-toggle-display-empty-topics t]
        ["Edit parameters" gnus-topic-edit-parameters t])
        ["List active" gnus-topic-list-active t]))))
 
 (defun gnus-topic-mode (&optional arg redisplay)
   "Minor mode for topicsifying Gnus group buffers."
+  ;; FIXME: Use define-minor-mode.
   (interactive (list current-prefix-arg t))
   (when (eq major-mode 'gnus-group-mode)
     (make-local-variable 'gnus-topic-mode)
@@ -1082,10 +1149,7 @@ articles in the topic and its subtopics."
       (when (gnus-visual-p 'topic-menu 'menu)
        (gnus-topic-make-menu-bar))
       (gnus-set-format 'topic t)
-      (gnus-add-minor-mode 'gnus-topic-mode " Topic"
-                          gnus-topic-mode-map nil (lambda (&rest junk)
-                                                    (interactive)
-                                                    (gnus-topic-mode nil t)))
+      (add-minor-mode 'gnus-topic-mode " Topic" gnus-topic-mode-map)
       (add-hook 'gnus-group-catchup-group-hook 'gnus-topic-update-topic)
       (set (make-local-variable 'gnus-group-prepare-function)
           'gnus-group-prepare-topics)
@@ -1101,8 +1165,9 @@ articles in the topic and its subtopics."
           '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)
-      (make-local-hook 'gnus-check-bogus-groups-hook)
-      (add-hook 'gnus-check-bogus-groups-hook 'gnus-topic-clean-alist)
+      (gnus-make-local-hook 'gnus-check-bogus-groups-hook)
+      (add-hook 'gnus-check-bogus-groups-hook 'gnus-topic-clean-alist
+               nil 'local)
       (setq gnus-topology-checked-p nil)
       ;; We check the topology.
       (when gnus-newsrc-alist
@@ -1123,10 +1188,15 @@ articles in the topic and its subtopics."
 No article is selected automatically.
 If the group is opened, just switch the summary buffer.
 If ALL is non-nil, already read articles become readable.
-If ALL is a number, fetch this number of articles.
+
+If ALL is a positive number, fetch this number of the latest
+articles in the group.  If ALL is a negative number, fetch this
+number of the earliest articles in the group.
 
 If performed over a topic line, toggle folding the topic."
   (interactive "P")
+  (when (and (eobp) (not (gnus-group-group-name)))
+    (forward-line -1))
   (if (gnus-group-topic-p)
       (let ((gnus-group-list-mode
             (if all (cons (if (numberp all) all 7) t) gnus-group-list-mode)))
@@ -1149,17 +1219,39 @@ If performed over a topic line, toggle folding the topic."
       (gnus-message 5 "Expiring groups in %s..." topic)
       (let ((gnus-group-marked
             (mapcar (lambda (entry) (car (nth 2 entry)))
-                    (gnus-topic-find-groups topic gnus-level-killed t))))
+                    (gnus-topic-find-groups topic gnus-level-killed t
+                                            nil t))))
        (gnus-group-expire-articles nil))
       (gnus-message 5 "Expiring groups in %s...done" topic))))
 
+(defun gnus-topic-catchup-articles (topic)
+  "Catchup this topic or group.
+Also see `gnus-group-catchup'."
+  (interactive (list (gnus-group-topic-name)))
+  (if (not topic)
+      (call-interactively 'gnus-group-catchup-current)
+    (save-excursion
+      (let* ((groups
+            (mapcar (lambda (entry) (car (nth 2 entry)))
+                    (gnus-topic-find-groups topic gnus-level-killed t
+                                            nil t)))
+            (buffer-read-only nil)
+            (gnus-group-marked groups))
+       (gnus-group-catchup-current)
+       (mapcar 'gnus-topic-update-topics-containing-group groups)))))
+
 (defun gnus-topic-read-group (&optional all no-article group)
   "Read news in this newsgroup.
 If the prefix argument ALL is non-nil, already read articles become
-readable.  IF ALL is a number, fetch this number of articles.  If the
-optional argument NO-ARTICLE is non-nil, no article will be
-auto-selected upon group entry.  If GROUP is non-nil, fetch that
-group.
+readable.
+
+If ALL is a positive number, fetch this number of the latest
+articles in the group.  If ALL is a negative number, fetch this
+number of the earliest articles in the group.
+
+If the optional argument NO-ARTICLE is non-nil, no article will
+be auto-selected upon group entry.  If GROUP is non-nil, fetch
+that group.
 
 If performed over a topic line, toggle folding the topic."
   (interactive "P")
@@ -1209,7 +1301,8 @@ When used interactively, PARENT will be the topic under point."
 If COPYP, copy the groups instead."
   (interactive
    (list current-prefix-arg
-        (completing-read "Move to topic: " gnus-topic-alist nil t)))
+        (gnus-completing-read "Move to topic" (mapcar 'car gnus-topic-alist) t
+                              nil 'gnus-topic-history)))
   (let ((use-marked (and (not n) (not (gnus-region-active-p))
                         gnus-group-marked t))
        (groups (gnus-group-process-prefix n))
@@ -1219,15 +1312,13 @@ If COPYP, copy the groups instead."
        entry)
     (if (and (not groups) (not copyp) start-topic)
        (gnus-topic-move start-topic topic)
-      (mapcar
-       (lambda (g)
-        (gnus-group-remove-mark g use-marked)
-        (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)
+      (dolist (g groups)
+       (gnus-group-remove-mark g use-marked)
+       (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)))
       (gnus-topic-enter-dribble)
       (if start-group
          (gnus-group-goto-group start-group)
@@ -1240,7 +1331,7 @@ If COPYP, copy the groups instead."
   (let ((use-marked (and (not n) (not (gnus-region-active-p))
                         gnus-group-marked t))
        (groups (gnus-group-process-prefix n)))
-    (mapcar
+    (mapc
      (lambda (group)
        (gnus-group-remove-mark group use-marked)
        (let ((topicl (assoc (gnus-current-topic) gnus-topic-alist))
@@ -1257,7 +1348,8 @@ If COPYP, copy the groups instead."
   "Copy the current group to a topic."
   (interactive
    (list current-prefix-arg
-        (completing-read "Copy to topic: " gnus-topic-alist nil t)))
+        (gnus-completing-read
+         "Copy to topic" (mapcar 'car gnus-topic-alist) t)))
   (gnus-topic-move-group n topic t))
 
 (defun gnus-topic-kill-group (&optional n discard)
@@ -1350,14 +1442,15 @@ If PERMANENT, make it stay shown in subsequent sessions as well."
        (gnus-topic-remove-topic t nil)
       (let ((topic
             (gnus-topic-find-topology
-             (completing-read "Show topic: " gnus-topic-alist nil t))))
+             (gnus-completing-read "Show topic"
+                                    (mapcar 'car gnus-topic-alist) t))))
        (setcar (cddr (cadr topic)) nil)
        (setcar (cdr (cadr topic)) 'visible)
        (gnus-group-list-groups)))))
 
-(defun gnus-topic-mark-topic (topic &optional unmark recursive)
+(defun gnus-topic-mark-topic (topic &optional unmark non-recursive)
   "Mark all groups in the TOPIC with the process mark.
-If RECURSIVE is t, mark its subtopics too."
+If NON-RECURSIVE (which is the prefix) is t, don't mark its subtopics."
   (interactive (list (gnus-group-topic-name)
                     nil
                     (and current-prefix-arg t)))
@@ -1365,20 +1458,20 @@ If RECURSIVE is t, mark its subtopics too."
       (call-interactively 'gnus-group-mark-group)
     (save-excursion
       (let ((groups (gnus-topic-find-groups topic gnus-level-killed t nil
-                                           recursive)))
+                                           (not non-recursive))))
        (while groups
          (funcall (if unmark 'gnus-group-remove-mark 'gnus-group-set-mark)
                   (gnus-info-group (nth 2 (pop groups)))))))))
 
-(defun gnus-topic-unmark-topic (topic &optional dummy recursive)
+(defun gnus-topic-unmark-topic (topic &optional dummy non-recursive)
   "Remove the process mark from all groups in the TOPIC.
-If RECURSIVE is t, unmark its subtopics too."
+If NON-RECURSIVE (which is the prefix) is t, don't unmark its subtopics."
   (interactive (list (gnus-group-topic-name)
                     nil
                     (and current-prefix-arg t)))
   (if (not topic)
       (call-interactively 'gnus-group-unmark-group)
-    (gnus-topic-mark-topic topic t recursive)))
+    (gnus-topic-mark-topic topic t non-recursive)))
 
 (defun gnus-topic-get-new-news-this-topic (&optional n)
   "Check for new news in the current topic."
@@ -1398,7 +1491,8 @@ If RECURSIVE is t, unmark its subtopics too."
    (let (topic)
      (nreverse
       (list
-       (setq topic (completing-read "Move to topic: " gnus-topic-alist nil t))
+       (setq topic (gnus-completing-read "Move to topic"
+                                         (mapcar 'car gnus-topic-alist) t))
        (read-string (format "Move to %s (regexp): " topic))))))
   (gnus-group-mark-regexp regexp)
   (gnus-topic-move-group nil topic copyp))
@@ -1409,7 +1503,8 @@ If RECURSIVE is t, unmark its subtopics too."
    (let (topic)
      (nreverse
       (list
-       (setq topic (completing-read "Copy to topic: " gnus-topic-alist nil t))
+       (setq topic (gnus-completing-read "Copy to topic"
+                                         (mapcar 'car gnus-topic-alist) t))
        (read-string (format "Copy to %s (regexp): " topic))))))
   (gnus-topic-move-matching regexp topic t))
 
@@ -1436,7 +1531,7 @@ If RECURSIVE is t, unmark its subtopics too."
   (interactive
    (let ((topic (gnus-current-topic)))
      (list topic
-          (read-string (format "Rename %s to: " topic)))))
+          (read-string (format "Rename %s to: " topic) topic))))
   ;; Check whether the new name exists.
   (when (gnus-topic-find-topology new-name)
     (error "Topic '%s' already exists" new-name))
@@ -1472,7 +1567,7 @@ If UNINDENT, remove an indentation."
        (gnus-topic-kill-group)
        (push (cdar gnus-topic-killed-topics) gnus-topic-alist)
        (gnus-topic-create-topic
-        topic parent nil (cdaar gnus-topic-killed-topics))
+        topic parent nil (cdar (car gnus-topic-killed-topics)))
        (pop gnus-topic-killed-topics)
        (or (gnus-topic-goto-topic topic)
            (gnus-topic-goto-topic parent))))))
@@ -1491,7 +1586,7 @@ If UNINDENT, remove an indentation."
       (push (cdar gnus-topic-killed-topics) gnus-topic-alist)
       (gnus-topic-create-topic
        topic grandparent (gnus-topic-next-topic parent)
-       (cdaar gnus-topic-killed-topics))
+       (cdar (car gnus-topic-killed-topics)))
       (pop gnus-topic-killed-topics)
       (gnus-topic-goto-topic topic))))
 
@@ -1608,6 +1703,12 @@ If REVERSE, sort in reverse order."
   (interactive "P")
   (gnus-topic-sort-groups 'gnus-group-sort-by-method reverse))
 
+(defun gnus-topic-sort-groups-by-server (&optional reverse)
+  "Sort the current topic alphabetically by server name.
+If REVERSE, sort in reverse order."
+  (interactive "P")
+  (gnus-topic-sort-groups 'gnus-group-sort-by-server reverse))
+
 (defun gnus-topic-sort-topics-1 (top reverse)
   (if (cdr top)
       (let ((subtop
@@ -1621,11 +1722,12 @@ If REVERSE, sort in reverse order."
   top)
 
 (defun gnus-topic-sort-topics (&optional topic reverse)
-  "Sort topics in TOPIC alphabeticaly by topic name.
+  "Sort topics in TOPIC alphabetically by topic name.
 If REVERSE, reverse the sorting order."
   (interactive
-   (list (completing-read "Sort topics in : " gnus-topic-alist nil t
-                         (gnus-current-topic))
+   (list (gnus-completing-read "Sort topics in"
+                               (mapcar 'car gnus-topic-alist) t
+                               (gnus-current-topic))
         current-prefix-arg))
   (let ((topic-topology (or (and topic (cdr (gnus-topic-find-topology topic)))
                            gnus-topic-topology)))
@@ -1639,7 +1741,7 @@ If REVERSE, reverse the sorting order."
   (interactive
    (list
     (gnus-group-topic-name)
-    (completing-read "Move to topic: " gnus-topic-alist nil t)))
+    (gnus-completing-read "Move to topic" (mapcar 'car gnus-topic-alist) t)))
   (unless (and current to)
     (error "Can't find topic"))
   (let ((current-top (cdr (gnus-topic-find-topology current)))
@@ -1651,9 +1753,7 @@ If REVERSE, reverse the sorting order."
     (if (gnus-topic-find-topology to current-top 0);; Don't care the level
        (error "Can't move `%s' to its sub-level" current))
     (gnus-topic-find-topology current nil nil 'delete)
-    (while (cdr to-top)
-      (setq to-top (cdr to-top)))
-    (setcdr to-top (list current-top))
+    (setcdr (last to-top) (list current-top))
     (gnus-topic-enter-dribble)
     (gnus-group-list-groups)
     (gnus-topic-goto-topic current)))
@@ -1669,6 +1769,12 @@ If REVERSE, reverse the sorting order."
          (gnus-subscribe-alphabetically newsgroup)
          ;; Add the group to the topic.
          (nconc (assoc topic gnus-topic-alist) (list newsgroup))
+         ;; if this topic specifies a default level, use it
+         (let ((subscribe-level (cdr (assq 'subscribe-level
+                                           (gnus-topic-parameters topic)))))
+           (when subscribe-level
+               (gnus-group-change-level newsgroup subscribe-level
+                                        gnus-level-default-subscribed)))
          (throw 'end t)))
       nil)))