Synch with Oort Gnus.
[elisp/gnus.git-] / lisp / gnus-agent.el
index fd5f269..33f14c9 100644 (file)
@@ -1,5 +1,6 @@
 ;;; gnus-agent.el --- unplugged support for Semi-gnus
-;; Copyright (C) 1997, 1998, 1999, 2000 Free Software Foundation, Inc.
+;; Copyright (C) 1997, 1998, 1999, 2000, 2001
+;;        Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;;     Tatsuya Ichikawa <t-ichi@po.shiojiri.ne.jp>
@@ -38,6 +39,9 @@
     (require 'timer))
   (require 'gnus-group))
 
+(eval-and-compile
+  (autoload 'gnus-server-update-server "gnus-srvr"))
+
 (defcustom gnus-agent-directory (nnheader-concat gnus-directory "agent/")
   "Where the Gnus agent will store its files."
   :group 'gnus-agent
@@ -74,16 +78,28 @@ If nil, only read articles will be expired."
   :group 'gnus-agent
   :type 'hook)
 
+;; Extracted from gnus-xmas-redefine in order to preserve user settings
+(when (featurep 'xemacs)
+  (add-hook 'gnus-agent-group-mode-hook 'gnus-xmas-agent-group-menu-add))
+
 (defcustom gnus-agent-summary-mode-hook nil
   "Hook run in Agent summary minor modes."
   :group 'gnus-agent
   :type 'hook)
 
+;; Extracted from gnus-xmas-redefine in order to preserve user settings
+(when (featurep 'xemacs)
+  (add-hook 'gnus-agent-summary-mode-hook 'gnus-xmas-agent-summary-menu-add))
+
 (defcustom gnus-agent-server-mode-hook nil
   "Hook run in Agent summary minor modes."
   :group 'gnus-agent
   :type 'hook)
 
+;; Extracted from gnus-xmas-redefine in order to preserve user settings
+(when (featurep 'xemacs)
+  (add-hook 'gnus-agent-server-mode-hook 'gnus-xmas-agent-server-menu-add))
+
 (defcustom gnus-agent-confirmation-function 'y-or-n-p
   "Function to confirm when error happens."
   :version "21.1"
@@ -275,6 +291,7 @@ If this is `ask' the hook will query the user."
 (defvar gnus-agent-summary-mode-map (make-sparse-keymap))
 (gnus-define-keys gnus-agent-summary-mode-map
   "Jj" gnus-agent-toggle-plugged
+  "Ju" gnus-agent-summary-fetch-group
   "J#" gnus-agent-mark-article
   "J\M-#" gnus-agent-unmark-article
   "@" gnus-agent-toggle-mark
@@ -289,6 +306,7 @@ If this is `ask' the hook will query the user."
        ["Mark as downloadable" gnus-agent-mark-article t]
        ["Unmark as downloadable" gnus-agent-unmark-article t]
        ["Toggle mark" gnus-agent-toggle-mark t]
+       ["Fetch downloadable" gnus-agent-summary-fetch-group t]
        ["Catchup undownloaded" gnus-agent-catchup t]))))
 
 (defvar gnus-agent-server-mode-map (make-sparse-keymap))
@@ -306,6 +324,13 @@ If this is `ask' the hook will query the user."
        ["Add" gnus-agent-add-server t]
        ["Remove" gnus-agent-remove-server t]))))
 
+(defun gnus-agent-make-mode-line-string (string mouse-button mouse-func)
+  (if (and (fboundp 'propertize)
+          (fboundp 'make-mode-line-mouse-map))
+      (propertize string 'local-map
+                 (make-mode-line-mouse-map mouse-button mouse-func))
+    string))
+
 (defun gnus-agent-toggle-plugged (plugged)
   "Toggle whether Gnus is unplugged or not."
   (interactive (list (not gnus-plugged)))
@@ -314,11 +339,17 @@ If this is `ask' the hook will query the user."
        (setq gnus-plugged plugged)
        (gnus-agent-possibly-synchronize-flags)
        (gnus-run-hooks 'gnus-agent-plugged-hook)
-       (setcar (cdr gnus-agent-mode-status) " Plugged"))
+       (setcar (cdr gnus-agent-mode-status)
+               (gnus-agent-make-mode-line-string " Plugged"
+                                                 'mouse-2
+                                                 'gnus-agent-toggle-plugged)))
     (gnus-agent-close-connections)
     (setq gnus-plugged plugged)
     (gnus-run-hooks 'gnus-agent-unplugged-hook)
-    (setcar (cdr gnus-agent-mode-status) " Unplugged"))
+    (setcar (cdr gnus-agent-mode-status)
+           (gnus-agent-make-mode-line-string " Unplugged"
+                                             'mouse-2
+                                             'gnus-agent-toggle-plugged)))
   (force-mode-line-update))
 
 (defun gnus-agent-close-connections ()
@@ -349,15 +380,17 @@ last form in your `.gnus.el' file:
 
 \(gnus-agentize)
 
-This will modify the `gnus-before-startup-hook', `gnus-post-method',
-and `message-send-mail-function' variables, and install the Gnus
-agent minor mode in all Gnus buffers."
+This will modify the `gnus-setup-news-hook', and
+`message-send-mail-real-function' variables, and install the Gnus agent
+minor mode in all Gnus buffers."
   (interactive)
   (gnus-open-agent)
   (add-hook 'gnus-setup-news-hook 'gnus-agent-queue-setup)
   (unless gnus-agent-send-mail-function
-    (setq gnus-agent-send-mail-function message-send-mail-function
-         message-send-mail-function 'gnus-agent-send-mail))
+    (setq gnus-agent-send-mail-function (or
+                                        message-send-mail-real-function
+                                        message-send-mail-function)
+         message-send-mail-real-function 'gnus-agent-send-mail))
   (unless gnus-agent-covered-methods
     (setq gnus-agent-covered-methods (list gnus-select-method))))
 
@@ -412,8 +445,7 @@ be a select method."
                                   gcc " ,")))))
           covered)
       (while (and (not covered) methods)
-       (setq covered
-             (member (car methods) gnus-agent-covered-methods)
+       (setq covered (gnus-agent-method-p (car methods))
              methods (cdr methods)))
       covered)))
 
@@ -445,14 +477,20 @@ be a select method."
 (defun gnus-agent-fetch-group (group)
   "Put all new articles in GROUP into the Agent."
   (interactive (list (gnus-group-group-name)))
-  (unless gnus-plugged
-    (error "Groups can't be fetched when Gnus is unplugged"))
-  (unless group
-    (error "No group on the current line"))
-  (let ((gnus-command-method (gnus-find-method-for-group group)))
-    (gnus-agent-with-fetch
-      (gnus-agent-fetch-group-1 group gnus-command-method)
-      (gnus-message 5 "Fetching %s...done" group))))
+  (let ((state gnus-plugged))
+    (unwind-protect
+       (progn
+         (unless group
+           (error "No group on the current line"))
+         (unless state
+           (gnus-agent-toggle-plugged gnus-plugged))
+         (let ((gnus-command-method (gnus-find-method-for-group group)))
+           (gnus-agent-with-fetch
+             (gnus-agent-fetch-group-1 group gnus-command-method)
+             (gnus-message 5 "Fetching %s...done" group))))
+      (when (and (not state)
+                gnus-plugged)
+       (gnus-agent-toggle-plugged gnus-plugged)))))
 
 (defun gnus-agent-add-group (category arg)
   "Add the current group to an agent category."
@@ -539,9 +577,10 @@ be a select method."
   (unless server
     (error "No server on the current line"))
   (let ((method (gnus-server-get-method nil (gnus-server-server-name))))
-    (when (member method gnus-agent-covered-methods)
+    (when (gnus-agent-method-p method)
       (error "Server already in the agent program"))
     (push method gnus-agent-covered-methods)
+    (gnus-server-update-server server)
     (gnus-agent-write-servers)
     (message "Entered %s into the Agent" server)))
 
@@ -551,10 +590,11 @@ be a select method."
   (unless server
     (error "No server on the current line"))
   (let ((method (gnus-server-get-method nil (gnus-server-server-name))))
-    (unless (member method gnus-agent-covered-methods)
+    (unless (gnus-agent-method-p method)
       (error "Server not in the agent program"))
     (setq gnus-agent-covered-methods
          (delete method gnus-agent-covered-methods))
+    (gnus-server-update-server server)
     (gnus-agent-write-servers)
     (message "Removed %s from the agent" server)))
 
@@ -637,23 +677,18 @@ the actual number of articles toggled is returned."
               (gnus-agent-method-p gnus-command-method))
       (gnus-agent-load-alist gnus-newsgroup-name)
       ;; First mark all undownloaded articles as undownloaded.
-      (let ((articles (append gnus-newsgroup-unreads
-                             gnus-newsgroup-marked
-                             gnus-newsgroup-dormant))
-           article)
-       (while (setq article (pop articles))
-         (unless (or (cdr (assq article gnus-agent-article-alist))
-                     (memq article gnus-newsgroup-downloadable)
-                     (memq article gnus-newsgroup-cached))
-           (push article gnus-newsgroup-undownloaded))))
+      (dolist (article (mapcar (lambda (header) (mail-header-number header))
+                              gnus-newsgroup-headers))
+       (unless (or (cdr (assq article gnus-agent-article-alist))
+                   (memq article gnus-newsgroup-downloadable)
+                   (memq article gnus-newsgroup-cached))
+         (push article gnus-newsgroup-undownloaded)))
       ;; Then mark downloaded downloadable as not-downloadable,
       ;; if you get my drift.
-      (let ((articles gnus-newsgroup-downloadable)
-           article)
-       (while (setq article (pop articles))
-         (when (cdr (assq article gnus-agent-article-alist))
-           (setq gnus-newsgroup-downloadable
-                 (delq article gnus-newsgroup-downloadable))))))))
+      (dolist (article gnus-newsgroup-downloadable)
+       (when (cdr (assq article gnus-agent-article-alist))
+         (setq gnus-newsgroup-downloadable
+               (delq article gnus-newsgroup-downloadable)))))))
 
 (defun gnus-agent-catchup ()
   "Mark all undownloaded articles as read."
@@ -664,6 +699,29 @@ the actual number of articles toggled is returned."
        (pop gnus-newsgroup-undownloaded) gnus-catchup-mark)))
   (gnus-summary-position-point))
 
+(defun gnus-agent-summary-fetch-group ()
+  "Fetch the downloadable articles in the group."
+  (interactive)
+  (let ((articles gnus-newsgroup-downloadable)
+       (gnus-command-method (gnus-find-method-for-group gnus-newsgroup-name))
+       (state gnus-plugged))
+    (unwind-protect
+       (progn
+         (unless state
+           (gnus-agent-toggle-plugged t))
+         (unless articles
+           (error "No articles to download"))
+         (gnus-agent-with-fetch
+           (gnus-agent-fetch-articles gnus-newsgroup-name articles))
+         (save-excursion
+           (dolist (article articles)
+             (setq gnus-newsgroup-downloadable
+                   (delq article gnus-newsgroup-downloadable))
+             (gnus-summary-mark-article article gnus-unread-mark))))
+      (when (and (not state)
+                gnus-plugged)
+       (gnus-agent-toggle-plugged nil)))))
+
 ;;;
 ;;; Internal functions
 ;;;
@@ -696,7 +754,12 @@ the actual number of articles toggled is returned."
         (when (and sym (boundp sym))
           (if (and (boundp (setq osym (intern (symbol-name sym) orig)))
                    (setq elem (symbol-value osym)))
-              (setcdr elem (cdr (symbol-value sym)))
+              (progn
+                (if (and (integerp (car (symbol-value sym)))
+                         (> (car elem) (car (symbol-value sym))))
+                    (setcar elem (car (symbol-value sym))))
+                (if (integerp (cdr (symbol-value sym)))
+                    (setcdr elem (cdr (symbol-value sym)))))
             (set (intern (symbol-name sym) orig) (symbol-value sym)))))
        new))
     (gnus-make-directory (file-name-directory file))
@@ -715,7 +778,7 @@ the actual number of articles toggled is returned."
           (file-name-coding-system nnmail-pathname-coding-system)
           (pathname-coding-system nnmail-pathname-coding-system)
           (file (gnus-agent-lib-file "active"))
-          oactive)
+          oactive-min)
       (gnus-make-directory (file-name-directory file))
       (with-temp-file file
        (when (file-exists-p file)
@@ -724,16 +787,12 @@ the actual number of articles toggled is returned."
        (when (re-search-forward
               (concat "^" (regexp-quote group) " ") nil t)
          (save-excursion
-           (save-restriction
-             (narrow-to-region (match-beginning 0)
-                               (progn
-                                 (forward-line 1)
-                                 (point)))
-             (setq oactive (car (nnmail-parse-active)))))
+           (read (current-buffer))                      ;; max
+           (setq oactive-min (read (current-buffer))))  ;; min
          (gnus-delete-line))
        (insert (format "%S %d %d y\n" (intern group)
                        (cdr active)
-                       (or (car oactive) (car active))))
+                       (or oactive-min (car active))))
        (goto-char (point-max))
        (while (search-backward "\\." nil t)
          (delete-char 1))))))
@@ -963,9 +1022,8 @@ the actual number of articles toggled is returned."
           (setq articles (nthcdr i articles))))
     ;; add article with marks to list of article headers we want to fetch.
     (dolist (arts (gnus-info-marks (gnus-get-info group)))
-      (setq articles (gnus-union (gnus-uncompress-sequence (cdr arts))
-                                articles)))
-    (setq articles (sort articles '<))
+      (setq articles (gnus-range-add articles (cdr arts))))
+    (setq articles (sort (gnus-uncompress-sequence articles) '<))
     ;; Remove known articles.
     (when (gnus-agent-load-alist group)
       (setq articles (gnus-sorted-intersection
@@ -1056,7 +1114,8 @@ the actual number of articles toggled is returned."
 (defun gnus-agent-save-alist (group &optional articles state dir)
   "Save the article-state alist for GROUP."
   (let ((file-name-coding-system nnmail-pathname-coding-system)
-       (pathname-coding-system nnmail-pathname-coding-system))
+       (pathname-coding-system nnmail-pathname-coding-system)
+       print-level print-length)
     (with-temp-file (if dir
                        (expand-file-name ".agentview" dir)
                      (gnus-agent-article-name ".agentview" group))
@@ -1106,11 +1165,12 @@ the actual number of articles toggled is returned."
          (error
           (unless (funcall gnus-agent-confirmation-function
                            (format "Error (%s).  Continue? " err))
-            (error "Cannot fetch articles into the Gnus agent.")))
-         (quit 
+            (error "Cannot fetch articles into the Gnus agent")))
+         (quit
           (unless (funcall gnus-agent-confirmation-function
-                           (format "Quit (%s).  Continue? " err))
-            (signal 'quit "Cannot fetch articles into the Gnus agent."))))
+                           (format "Quit fetching session (%s).  Continue? "
+                                   err))
+            (signal 'quit "Cannot fetch articles into the Gnus agent"))))
        (pop methods))
       (gnus-message 6 "Finished fetching articles into the Gnus agent"))))
 
@@ -1131,7 +1191,8 @@ the actual number of articles toggled is returned."
     (unless (gnus-check-group group)
       (error "Can't open server for %s" group))
     ;; Fetch headers.
-    (when (and (or (gnus-active group) (gnus-activate-group group))
+    (when (and (or (gnus-active group)
+                  (gnus-activate-group group))
               (setq articles (gnus-agent-fetch-headers group))
               (let ((nntp-server-buffer gnus-agent-overview-buffer))
                 ;; Parse them and see which articles we want to fetch.
@@ -1148,10 +1209,9 @@ the actual number of articles toggled is returned."
            (gnus-get-predicate
             (or (gnus-group-find-parameter group 'agent-predicate t)
                 (cadr category))))
-      (if (memq (caaddr predicate) '(gnus-agent-true gnus-agent-false))
+      (if (memq predicate '(gnus-agent-true gnus-agent-false))
          ;; Simple implementation
-         (setq arts
-               (and (eq (caaddr predicate) 'gnus-agent-true) articles))
+         (setq arts (and (eq predicate 'gnus-agent-true) articles))
        (setq arts nil)
        (setq score-param
              (or (gnus-group-get-parameter group 'agent-score t)
@@ -1181,6 +1241,7 @@ the actual number of articles toggled is returned."
     (setq arts (assq 'download (gnus-info-marks
                                (setq info (gnus-get-info group)))))
     (when (cdr arts)
+      (gnus-message 8 "Agent is downloading marked articles...")
       (gnus-agent-fetch-articles
        group (gnus-uncompress-range (cdr arts)))
       (setq marks (delq arts (gnus-info-marks info)))
@@ -1298,7 +1359,7 @@ The following commands are available:
 (defalias 'gnus-category-position-point 'gnus-goto-colon)
 
 (defun gnus-category-insert-line (category)
-  (let* ((gnus-tmp-name (car category))
+  (let* ((gnus-tmp-name (format "%s" (car category)))
         (gnus-tmp-groups (length (cadddr category))))
     (beginning-of-line)
     (gnus-add-text-properties
@@ -1467,7 +1528,11 @@ The following commands are available:
 
 (defun gnus-category-make-function (cat)
   "Make a function from category CAT."
-  `(lambda () ,(gnus-category-make-function-1 cat)))
+  (let ((func (gnus-category-make-function-1 cat)))
+    (if (and (= (length func) 1)
+            (symbolp (car func)))
+       (car func)
+      (gnus-byte-compile `(lambda () ,func)))))
 
 (defun gnus-agent-true ()
   "Return t."
@@ -1551,8 +1616,8 @@ The following commands are available:
                      (if (numberp fetch-date)
                          (>  fetch-date day)
                        ;; History file is corrupted.
-                       (gnus-message 
-                        5 
+                       (gnus-message
+                        5
                         (format "File %s is corrupted!"
                                 (gnus-agent-lib-file "history")))
                        (sit-for 1)