Remove <a name...> tags, which confuses the rest of the function.
[gnus] / lisp / gnus-sum.el
index fb80d60..a99426a 100644 (file)
@@ -1,7 +1,7 @@
 ;;; gnus-sum.el --- summary mode commands for Gnus
 
 ;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+;;   2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news
@@ -30,6 +30,9 @@
   (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
 (eval-when-compile
   (require 'cl))
+(eval-when-compile
+  (when (featurep 'xemacs)
+    (require 'easy-mmode))) ; for `define-minor-mode'
 
 (defvar tool-bar-mode)
 (defvar gnus-tmp-header)
@@ -73,6 +76,13 @@ See `gnus-group-goto-unread'."
   :version "23.1" ;; No Gnus
   :type 'boolean)
 
+(defcustom gnus-summary-stop-at-end-of-message nil
+  "If non-nil, don't select the next message when using `SPC'."
+  :link '(custom-manual "(gnus)Group Maneuvering")
+  :group 'gnus-summary-maneuvering
+  :version "24.1"
+  :type 'boolean)
+
 (defcustom gnus-fetch-old-headers nil
   "*Non-nil means that Gnus will try to build threads by grabbing old headers.
 If an unread article in the group refers to an older, already
@@ -211,7 +221,7 @@ This variable will only be used if the value of
   :group 'gnus-summary-format
   :type 'string)
 
-(defcustom gnus-summary-goto-unread t
+(defcustom gnus-summary-goto-unread nil
   "*If t, many commands will go to the next unread article.
 This applies to marking commands as well as other commands that
 \"naturally\" select the next article, like, for instance, `SPC' at
@@ -221,6 +231,7 @@ If nil, the marking commands do NOT go to the next unread article
 \(they go to the next article instead).  If `never', commands that
 usually go to the next unread article, will go to the next article,
 whether it is read or not."
+  :version "24.1"
   :group 'gnus-summary-marks
   :link '(custom-manual "(gnus)Setting Marks")
   :type '(choice (const :tag "off" nil)
@@ -339,7 +350,7 @@ newsgroups, set the variable to nil in `gnus-select-group-hook'."
   :type '(choice (const :tag "none" nil)
                 (sexp :menu-tag "first" t)))
 
-(defcustom gnus-auto-select-subject 'unread
+(defcustom gnus-auto-select-subject 'unseen-or-unread
   "*Says what subject to place under point when entering a group.
 
 This variable can either be the symbols `first' (place point on the
@@ -350,7 +361,7 @@ the first unseen article), `unseen-or-unread' (place point on the subject
 line of the first unseen article or, if all article have been seen, on the
 subject line of the first unread article), or a function to be called to
 place point on some subject line."
-  :version "22.1"
+  :version "24.1"
   :group 'gnus-group-select
   :type '(choice (const best)
                 (const unread)
@@ -454,9 +465,10 @@ and non-`vertical', do both horizontal and vertical recentering."
   :group 'gnus-summary
   :type 'boolean)
 
-(defcustom gnus-single-article-buffer t
+(defcustom gnus-single-article-buffer nil
   "*If non-nil, display all articles in the same buffer.
 If nil, each group will get its own article buffer."
+  :version "24.1"
   :group 'gnus-article-various
   :type 'boolean)
 
@@ -528,11 +540,6 @@ string with the suggested prefix."
   :group 'gnus-summary-marks
   :type 'character)
 
-(defcustom gnus-souped-mark ?F
-  "*Mark used for souped articles."
-  :group 'gnus-summary-marks
-  :type 'character)
-
 (defcustom gnus-kill-file-mark ?X
   "*Mark used for articles killed by kill files."
   :group 'gnus-summary-marks
@@ -656,9 +663,9 @@ string with the suggested prefix."
 (defcustom gnus-auto-expirable-marks
   (list gnus-killed-mark gnus-del-mark gnus-catchup-mark
        gnus-low-score-mark gnus-ancient-mark gnus-read-mark
-       gnus-souped-mark gnus-duplicate-mark)
+       gnus-duplicate-mark)
   "*The list of marks converted into expiration if a group is auto-expirable."
-  :version "21.1"
+  :version "24.1"
   :group 'gnus-summary
   :type '(repeat character))
 
@@ -1248,7 +1255,7 @@ type of files to save."
   "Whether Gnus should parse all headers made available to it.
 This is mostly relevant for slow back ends where the user may
 wish to widen the summary buffer to include all headers
-that were fetched.  Say, for nnultimate groups."
+that were fetched."
   :version "22.1"
   :group 'gnus-summary
   :type '(choice boolean regexp))
@@ -1850,7 +1857,6 @@ increase the score of each group you read."
   "=" gnus-summary-expand-window
   "\C-x\C-s" gnus-summary-reselect-current-group
   "\M-g" gnus-summary-rescan-group
-  "w" gnus-summary-stop-page-breaking
   "\C-c\C-r" gnus-summary-caesar-message
   "f" gnus-summary-followup
   "F" gnus-summary-followup-with-original
@@ -1872,7 +1878,6 @@ increase the score of each group you read."
   [follow-link] mouse-face
   "m" gnus-summary-mail-other-window
   "a" gnus-summary-post-news
-  "i" gnus-summary-news-other-window
   "x" gnus-summary-limit-to-unread
   "s" gnus-summary-isearch-article
   "t" gnus-summary-toggle-header
@@ -2105,6 +2110,7 @@ increase the score of each group you read."
   "d" gnus-article-display-face
   "s" gnus-treat-smiley
   "D" gnus-article-remove-images
+  "W" gnus-html-show-images
   "f" gnus-treat-from-picon
   "m" gnus-treat-mail-picon
   "n" gnus-treat-newsgroups-picon)
@@ -2172,8 +2178,7 @@ increase the score of each group you read."
   "h" gnus-summary-save-article-folder
   "v" gnus-summary-save-article-vm
   "p" gnus-summary-pipe-output
-  "P" gnus-summary-muttprint
-  "s" gnus-soup-add-article)
+  "P" gnus-summary-muttprint)
 
 (gnus-define-keys (gnus-summary-mime-map "K" gnus-summary-mode-map)
   "b" gnus-summary-display-buttonized
@@ -2437,7 +2442,6 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
              ["Save in RMAIL mbox..." gnus-summary-save-article-rmail t]
              ["Save body in file..." gnus-summary-save-article-body-file t]
              ["Pipe through a filter..." gnus-summary-pipe-output t]
-             ["Add to SOUP packet" gnus-soup-add-article t]
              ["Print with Muttprint..." gnus-summary-muttprint t]
              ["Print" gnus-summary-print-article
               ,@(if (featurep 'xemacs) '(t)
@@ -2635,17 +2639,6 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
         ["Set expirable mark" gnus-summary-mark-as-expirable t]
         ["Set bookmark" gnus-summary-set-bookmark t]
         ["Remove bookmark" gnus-summary-remove-bookmark t])
-       ("Registry Mark"
-        ["Important"     gnus-registry-set-article-Important-mark    t]
-        ["Not Important" gnus-registry-remove-article-Important-mark t]
-        ["Work"          gnus-registry-set-article-Work-mark         t]
-        ["Not Work"      gnus-registry-remove-article-Work-mark      t]
-        ["Later"         gnus-registry-set-article-Later-mark        t]
-        ["Not Later"     gnus-registry-remove-article-Later-mark     t]
-        ["Personal"      gnus-registry-set-article-Personal-mark     t]
-        ["Not Personal"  gnus-registry-remove-article-Personal-mark  t]
-        ["To Do"         gnus-registry-set-article-To-Do-mark        t]
-        ["Not To Do"     gnus-registry-remove-article-To-Do-mark     t])
        ("Limit to"
         ["Marks..." gnus-summary-limit-to-marks t]
         ["Subject..." gnus-summary-limit-to-subject t]
@@ -2691,6 +2684,7 @@ gnus-summary-show-article-from-menu-as-charset-%s" cs))))
           gnus-newsgroup-process-stack]
          ["Save" gnus-summary-save-process-mark t]
          ["Run command on marked..." gnus-summary-universal-argument t]))
+       ("Registry Marks")
        ("Scroll article"
         ["Page forward" gnus-summary-next-page
          ,@(if (featurep 'xemacs) '(t)
@@ -3027,7 +3021,7 @@ When FORCE, rebuild the tool bar."
 
 
 (declare-function turn-on-gnus-mailing-list-mode "gnus-ml" ())
-
+(defvar bookmark-make-record-function)
 \f
 
 (defun gnus-summary-mode (&optional group)
@@ -3063,7 +3057,6 @@ The following commands are available:
   (gnus-simplify-mode-line)
   (setq major-mode 'gnus-summary-mode)
   (setq mode-name "Summary")
-  (make-local-variable 'minor-mode-alist)
   (use-local-map gnus-summary-mode-map)
   (buffer-disable-undo)
   (setq buffer-read-only t             ;Disable modification
@@ -3082,6 +3075,8 @@ The following commands are available:
   (gnus-run-mode-hooks 'gnus-summary-mode-hook)
   (turn-on-gnus-mailing-list-mode)
   (mm-enable-multibyte)
+  (set (make-local-variable 'bookmark-make-record-function)
+       'gnus-summary-bookmark-make-record)
   (gnus-update-format-specifications nil 'summary 'summary-mode 'summary-dummy)
   (gnus-update-summary-mark-positions))
 
@@ -3412,8 +3407,10 @@ marks of articles."
   (save-excursion
     (let (config)
       (goto-char (point-min))
-      (while (search-forward "\r" nil t)
-       (push (1- (point)) config))
+      (while (not (eobp))
+        (when (eq (get-char-property (point-at-eol) 'invisible) 'gnus-sum)
+          (push (save-excursion (forward-line 0) (point)) config))
+        (forward-line 1))
       config)))
 
 (defun gnus-restore-hidden-threads-configuration (config)
@@ -3421,10 +3418,8 @@ marks of articles."
   (save-excursion
     (let (point (inhibit-read-only t))
       (while (setq point (pop config))
-       (when (and (< point (point-max))
-                  (goto-char point)
-                  (eq (char-after) ?\n))
-         (subst-char-in-region point (1+ point) ?\n ?\r))))))
+        (goto-char point)
+        (gnus-summary-hide-thread)))))
 
 ;; Various summary mode internalish functions.
 
@@ -3937,7 +3932,6 @@ If NO-DISPLAY, don't generate a summary buffer."
          (progn
            (set-buffer gnus-group-buffer)
            (gnus-group-jump-to-group group)
-           (gnus-group-next-unread-group 1)
            (gnus-configure-windows 'group 'force))
        (gnus-handle-ephemeral-exit quit-config))
       ;; Finally signal the quit.
@@ -4826,7 +4820,8 @@ If LINE, insert the rebuilt thread starting on line LINE."
          ;; Even after binding max-lisp-eval-depth, the recursive
          ;; sorter might fail for very long threads.  In that case,
          ;; try using a (less well-tested) non-recursive sorter.
-         (error (gnus-sort-threads-loop
+         (error (gnus-message 9 "Sorting threads with loop...")
+                (gnus-sort-threads-loop
                  threads (gnus-make-sort-function
                           gnus-thread-sort-functions))))
       (gnus-message 8 "Sorting threads...done"))))
@@ -4993,22 +4988,17 @@ Unscored articles will be counted as having a score of zero."
   "Sort threads such that the thread with the most recently dated article comes first."
   (> (gnus-thread-latest-date h1) (gnus-thread-latest-date h2)))
 
+; Since this is called not only to sort the top-level threads, but
+; also in recursive sorts to order the articles within a thread, each
+; article will be processed many times.  Thus it speeds things up
+; quite a bit to use gnus-date-get-time, which caches the time value.
 (defun gnus-thread-latest-date (thread)
   "Return the highest article date in THREAD."
-  (let ((previous-time 0))
-    (apply 'max
-          (mapcar
-           (lambda (header)
-             (setq previous-time
-                   (condition-case ()
-                       (gnus-float-time (mail-header-parse-date
-                                         (mail-header-date header)))
-                     (error previous-time))))
-           (sort
-            (message-flatten-list thread)
-            (lambda (h1 h2)
-              (< (mail-header-number h1)
-                 (mail-header-number h2))))))))
+  (apply 'max
+        (mapcar (lambda (header) (gnus-float-time
+                                  (gnus-date-get-time
+                                   (mail-header-date header))))
+                (message-flatten-list thread))))
 
 (defun gnus-thread-total-score-1 (root)
   ;; This function find the total score of the thread below ROOT.
@@ -6061,9 +6051,7 @@ If WHERE is `summary', the summary mode line format will be used."
          (when (> (length mode-string) max-len)
            (setq mode-string
                  (concat (truncate-string-to-width mode-string (- max-len 3))
-                         "...")))
-         ;; Pad the mode string a bit.
-         (setq mode-string (format (format "%%-%ds" max-len) mode-string))))
+                         "...")))))
       ;; Update the mode line.
       (setq mode-line-buffer-identification
            (gnus-mode-line-buffer-identification (list mode-string)))
@@ -6100,8 +6088,7 @@ The resulting hash table is returned, or nil if no Xrefs were found."
   "Look through all the headers and mark the Xrefs as read."
   (let ((virtual (gnus-virtual-group-p from-newsgroup))
        name info xref-hashtb idlist method nth4)
-    (save-excursion
-      (set-buffer gnus-group-buffer)
+    (with-current-buffer gnus-group-buffer
       (when (setq xref-hashtb
                  (gnus-create-xref-hashtb from-newsgroup headers unreads))
        (mapatoms
@@ -6721,7 +6708,31 @@ Also do horizontal recentering."
   (when (and gnus-auto-center-summary
             (not (eq gnus-auto-center-summary 'vertical)))
     (gnus-horizontal-recenter))
-  (recenter n))
+  (if (fboundp 'recenter-top-bottom)
+      (recenter-top-bottom n)
+    (recenter n)))
+
+(put 'gnus-recenter 'isearch-scroll t)
+
+(defun gnus-forward-line-ignore-invisible (n)
+  "Move N lines forward (backward if N is negative).
+Like forward-line, but skip over (and don't count) invisible lines."
+  (let (done)
+    (while (and (> n 0) (not done))
+      ;; If the following character is currently invisible,
+      ;; skip all characters with that same `invisible' property value.
+      (while (gnus-invisible-p (point))
+       (goto-char (gnus-next-char-property-change (point))))
+      (forward-line 1)
+      (if (eobp)
+         (setq done t)
+       (setq n (1- n))))
+    (while (and (< n 0) (not done))
+      (forward-line -1)
+      (if (bobp) (setq done t)
+       (setq n (1+ n))
+       (while (and (not (bobp)) (gnus-invisible-p (1- (point))))
+         (goto-char (gnus-previous-char-property-change (point))))))))
 
 (defun gnus-summary-recenter ()
   "Center point in the summary window.
@@ -6738,16 +6749,19 @@ displayed, no centering will be performed."
                             gnus-auto-center-summary
                            (/ (1- (window-height)) 2)))))
           (height (1- (window-height)))
-          (bottom (save-excursion (goto-char (point-max))
-                                  (forward-line (- height))
-                                  (point)))
+          (bottom (save-excursion
+                    (goto-char (point-max))
+                    (gnus-forward-line-ignore-invisible (- height))
+                    (point)))
           (window (get-buffer-window (current-buffer))))
       (when (get-buffer-window gnus-article-buffer)
        ;; Only do recentering when the article buffer is displayed,
        ;; Set the window start to either `bottom', which is the biggest
        ;; possible valid number, or the second line from the top,
        ;; whichever is the least.
-       (let ((top-pos (save-excursion (forward-line (- top)) (point))))
+       (let ((top-pos (save-excursion
+                        (gnus-forward-line-ignore-invisible (- top))
+                        (point))))
          (if (> bottom top-pos)
              ;; Keep the second line from the top visible
              (set-window-start window top-pos)
@@ -6756,12 +6770,12 @@ displayed, no centering will be performed."
            ;; visible, or revert to using TOP-POS.
            (save-excursion
              (goto-char (point-max))
-             (forward-line -1)
+             (gnus-forward-line-ignore-invisible -1)
              (let ((last-line-start (point)))
                (goto-char bottom)
                (set-window-start window (point) t)
                (when (not (pos-visible-in-window-p last-line-start window))
-                 (forward-line 1)
+                 (gnus-forward-line-ignore-invisible 1)
                  (set-window-start window (min (point) top-pos) t)))))))
       ;; Do horizontal recentering while we're at it.
       (when (and (get-buffer-window (current-buffer) t)
@@ -7219,33 +7233,21 @@ The state which existed when entering the ephemeral is reset."
 
 ;;; Dead summaries.
 
-(defvar gnus-dead-summary-mode-map nil)
-
-(unless gnus-dead-summary-mode-map
-  (setq gnus-dead-summary-mode-map (make-keymap))
-  (suppress-keymap gnus-dead-summary-mode-map)
-  (substitute-key-definition
-   'undefined 'gnus-summary-wake-up-the-dead gnus-dead-summary-mode-map)
-  (dolist (key '("\C-d" "\r" "\177" [delete]))
-    (define-key gnus-dead-summary-mode-map
-      key 'gnus-summary-wake-up-the-dead))
-  (dolist (key '("q" "Q"))
-    (define-key gnus-dead-summary-mode-map key 'bury-buffer)))
-
-(defvar gnus-dead-summary-mode nil
-  "Minor mode for Gnus summary buffers.")
-
-(defun gnus-dead-summary-mode (&optional arg)
+(defvar gnus-dead-summary-mode-map
+  (let ((map (make-keymap)))
+    (suppress-keymap map)
+    (substitute-key-definition 'undefined 'gnus-summary-wake-up-the-dead map)
+    (dolist (key '("\C-d" "\r" "\177" [delete]))
+      (define-key map key 'gnus-summary-wake-up-the-dead))
+    (dolist (key '("q" "Q"))
+      (define-key map key 'bury-buffer))
+    map))
+
+(define-minor-mode gnus-dead-summary-mode
   "Minor mode for Gnus summary buffers."
-  (interactive "P")
-  (when (eq major-mode 'gnus-summary-mode)
-    (make-local-variable 'gnus-dead-summary-mode)
-    (setq gnus-dead-summary-mode
-         (if (null arg) (not gnus-dead-summary-mode)
-           (> (prefix-numeric-value arg) 0)))
-    (when gnus-dead-summary-mode
-      (add-minor-mode
-       'gnus-dead-summary-mode " Dead" gnus-dead-summary-mode-map))))
+  :lighter " Dead" :keymap gnus-dead-summary-mode-map
+  (unless (derived-mode-p 'gnus-summary-mode)
+    (setq gnus-dead-summary-mode nil)))
 
 (defun gnus-deaden-summary ()
   "Make the current summary buffer into a dead summary buffer."
@@ -7778,7 +7780,7 @@ Also see the variable `gnus-article-skip-boring'."
            (setq endp (or (gnus-article-next-page lines)
                           (gnus-article-only-boring-p))))
          (when endp
-           (cond (stop
+           (cond ((or stop gnus-summary-stop-at-end-of-message)
                   (gnus-message 3 "End of message"))
                  (circular
                   (gnus-summary-beginning-of-article))
@@ -8176,14 +8178,15 @@ in `nnmail-extra-headers'."
       (gnus-summary-position-point))))
 
 (defun gnus-summary-limit-strange-charsets-predicate (header)
-  (let ((string (concat (mail-header-subject header)
-                       (mail-header-from header)))
-       charset found)
-    (dotimes (i (1- (length string)))
-      (setq charset (format "%s" (char-charset (aref string (1+ i)))))
-      (when (string-match "unicode\\|big\\|japanese" charset)
-       (setq found t)))
-    found))
+  (when (fboundp 'char-charset)
+    (let ((string (concat (mail-header-subject header)
+                         (mail-header-from header)))
+         charset found)
+      (dotimes (i (1- (length string)))
+       (setq charset (format "%s" (char-charset (aref string (1+ i)))))
+       (when (string-match "unicode\\|big\\|japanese" charset)
+         (setq found t)))
+      found)))
 
 (defun gnus-summary-limit-to-predicate (predicate)
   "Limit to articles where PREDICATE returns non-nil.
@@ -8228,9 +8231,7 @@ articles that are younger than AGE days."
          (when (and (vectorp (gnus-data-header d))
                     (setq date (mail-header-date (gnus-data-header d))))
            (setq is-younger (time-less-p
-                             (time-since (condition-case ()
-                                             (date-to-time date)
-                                           (error '(0 0))))
+                             (time-since (gnus-date-get-time date))
                              cutoff))
            (when (if younger-p
                      is-younger
@@ -8298,7 +8299,7 @@ If ALL is non-nil, limit strictly to unread articles."
           gnus-killed-mark gnus-spam-mark gnus-kill-file-mark
           gnus-low-score-mark gnus-expirable-mark
           gnus-canceled-mark gnus-catchup-mark gnus-sparse-mark
-          gnus-duplicate-mark gnus-souped-mark)
+          gnus-duplicate-mark)
      'reverse)))
 
 (defun gnus-summary-limit-to-headers (match &optional reverse)
@@ -8324,8 +8325,7 @@ If REVERSE (the prefix), limit to articles that don't match."
     (dolist (data gnus-newsgroup-data)
       (let (gnus-mark-article-hook)
        (gnus-summary-select-article t t nil (gnus-data-number data)))
-      (save-excursion
-       (set-buffer gnus-article-buffer)
+      (with-current-buffer gnus-article-buffer
        (article-goto-body)
        (let* ((case-fold-search t)
               (found (if headersp
@@ -9009,7 +9009,7 @@ Obeys the standard process/prefix convention."
       (setq group (format "%s-%d" gnus-newsgroup-name article))
       (gnus-summary-remove-process-mark article)
       (when (gnus-summary-display-article article)
-       (save-excursion
+       (save-excursion ;;What for?
          (with-temp-buffer
            (insert-buffer-substring gnus-original-article-buffer)
            ;; Remove some headers that may lead nndoc to make
@@ -9517,7 +9517,7 @@ IDNA encoded domain names looks like `xn--bar'.  If a string
 remain unencoded after running this function, it is likely an
 invalid IDNA string (`xn--bar' is invalid).
 
-You must have GNU Libidn (`http://www.gnu.org/software/libidn/')
+You must have GNU Libidn (URL `http://www.gnu.org/software/libidn/')
 installed for this command to work."
   (interactive "P")
   (if (not (and (condition-case nil (require 'idna)
@@ -9847,12 +9847,14 @@ ACTION can be either `move' (the default), `crosspost' or `copy'."
        ;;;!!!Why is this necessary?
        (set-buffer gnus-summary-buffer)
 
-       (gnus-summary-goto-subject article)
        (when (eq action 'move)
-         (gnus-summary-mark-article article gnus-canceled-mark))))
+         (save-excursion
+           (gnus-summary-goto-subject article)
+           (gnus-summary-mark-article article gnus-canceled-mark)))))
       (push article articles-to-update-marks))
 
-    (apply 'gnus-summary-remove-process-mark articles-to-update-marks)
+    (save-excursion
+      (apply 'gnus-summary-remove-process-mark articles-to-update-marks))
     ;; Re-activate all groups that have been moved to.
     (with-current-buffer gnus-group-buffer
       (let ((gnus-group-marked to-groups))
@@ -10108,19 +10110,20 @@ confirmation before the articles are deleted."
       ;; Delete the articles.
       (setq not-deleted (gnus-request-expire-articles
                         articles gnus-newsgroup-name 'force))
-      (while articles
-       (gnus-summary-remove-process-mark (car articles))
-       ;; The backend might not have been able to delete the article
-       ;; after all.
-       (unless (memq (car articles) not-deleted)
-         (gnus-summary-mark-article (car articles) gnus-canceled-mark))
-       (let* ((article (car articles))
-              (ghead  (gnus-data-header
-                                   (assoc article (gnus-data-list nil)))))
-         (run-hook-with-args 'gnus-summary-article-delete-hook
-                             'delete ghead gnus-newsgroup-name nil
-                             nil))
-       (setq articles (cdr articles)))
+      (save-excursion
+       (while articles
+         (gnus-summary-remove-process-mark (car articles))
+         ;; The backend might not have been able to delete the article
+         ;; after all.
+         (unless (memq (car articles) not-deleted)
+           (gnus-summary-mark-article (car articles) gnus-canceled-mark))
+         (let* ((article (car articles))
+                (ghead  (gnus-data-header
+                         (assoc article (gnus-data-list nil)))))
+           (run-hook-with-args 'gnus-summary-article-delete-hook
+                               'delete ghead gnus-newsgroup-name nil
+                               nil))
+         (setq articles (cdr articles))))
       (when not-deleted
        (gnus-message 4 "Couldn't delete articles %s" not-deleted)))
     (gnus-summary-position-point)
@@ -11277,14 +11280,15 @@ If ARG is positive number, turn showing conversation threads on."
     (gnus-message 6 "Threading is now %s" (if gnus-show-threads "on" "off"))
     (gnus-summary-position-point)))
 
-(if (fboundp 'remove-overlays)
-    (defalias 'gnus-remove-overlays 'remove-overlays)
-  (defun gnus-remove-overlays (beg end name val)
-    "Clear BEG and END of overlays whose property NAME has value VAL.
+(eval-and-compile
+  (if (fboundp 'remove-overlays)
+      (defalias 'gnus-remove-overlays 'remove-overlays)
+    (defun gnus-remove-overlays (beg end name val)
+      "Clear BEG and END of overlays whose property NAME has value VAL.
 For compatibility with Emacs 21 and XEmacs."
-    (dolist (ov (gnus-overlays-in beg end))
-      (when (eq (gnus-overlay-get ov name) val)
-       (gnus-delete-overlay ov)))))
+      (dolist (ov (gnus-overlays-in beg end))
+       (when (eq (gnus-overlay-get ov name) val)
+         (gnus-delete-overlay ov))))))
 
 (defun gnus-summary-show-all-threads ()
   "Show all threads."
@@ -11499,7 +11503,7 @@ If the prefix argument is negative, tick articles instead."
              ((> unmark 0)
               (gnus-summary-mark-article-as-unread gnus-unread-mark))
              ((= unmark 0)
-              (gnus-summary-mark-article-as-unread gnus-expirable-mark))
+              (gnus-summary-mark-article nil gnus-expirable-mark))
              (t
               (gnus-summary-mark-article-as-unread gnus-ticked-mark)))
        (setq articles (cdr articles))))
@@ -11656,12 +11660,8 @@ will not be marked as saved."
            (gnus-message 1 "Article %d is unsaveable" article))
        ;; This is a real article.
        (save-window-excursion
-         (let ((gnus-display-mime-function (when decode
-                                             gnus-display-mime-function))
-               (gnus-article-prepare-hook (when decode
-                                            gnus-article-prepare-hook)))
-           (gnus-summary-select-article t t nil article)
-           (gnus-summary-goto-subject article)))
+         (gnus-summary-select-article decode decode nil article)
+         (gnus-summary-goto-subject article))
        (with-current-buffer save-buffer
          (erase-buffer)
          (insert-buffer-substring (if decode
@@ -12622,6 +12622,64 @@ If ALL is a number, fetch this number of articles."
          (gnus-summary-limit (gnus-sorted-nunion old new))))
     (gnus-summary-position-point)))
 
+;;; Bookmark support for Gnus.
+(declare-function bookmark-make-record-default
+                  "bookmark" (&optional no-file no-context posn))
+(declare-function bookmark-prop-get "bookmark" (bookmark prop))
+(declare-function bookmark-default-handler "bookmark" (bmk))
+(declare-function bookmark-get-bookmark-record "bookmark" (bmk))
+(defvar bookmark-yank-point)
+(defvar bookmark-current-buffer)
+
+(defun gnus-summary-bookmark-make-record ()
+  "Make a bookmark entry for a Gnus summary buffer."
+  (let (pos buf)
+    (unless (and (derived-mode-p 'gnus-summary-mode) gnus-article-current)
+      (save-restriction              ; FIXME is it necessary to widen?
+        (widen) (setq pos (point))) ; Set position in gnus-article buffer.
+      (setq buf "art") ; We are recording bookmark from article buffer.
+      (setq bookmark-yank-point (point))
+      (setq bookmark-current-buffer (current-buffer))
+      (gnus-article-show-summary))      ; Go back in summary buffer.
+    ;; We are now recording bookmark from summary buffer.
+    (unless buf (setq buf "sum"))
+    (let* ((subject (elt (gnus-summary-article-header) 1))
+           (grp     (car gnus-article-current))
+           (art     (cdr gnus-article-current))
+           (head    (gnus-summary-article-header art))
+           (id      (mail-header-id head)))
+      `(,subject
+       ,@(condition-case nil
+             (bookmark-make-record-default 'no-file 'no-context pos)
+           (wrong-number-of-arguments
+            (bookmark-make-record-default 'point-only)))
+        (location . ,(format "Gnus-%s %s:%d:%s" buf grp art id))
+        (group . ,grp) (article . ,art)
+        (message-id . ,id) (handler . gnus-summary-bookmark-jump)))))
+
+;;;###autoload
+(defun gnus-summary-bookmark-jump (bookmark)
+  "Handler function for record returned by `gnus-summary-bookmark-make-record'.
+BOOKMARK is a bookmark name or a bookmark record."
+  (let ((group    (bookmark-prop-get bookmark 'group))
+        (article  (bookmark-prop-get bookmark 'article))
+        (id       (bookmark-prop-get bookmark 'message-id))
+        (buf      (car (split-string (bookmark-prop-get bookmark 'location)))))
+    (gnus-fetch-group group (list article))
+    (gnus-summary-insert-cached-articles)
+    (gnus-summary-goto-article id nil 'force)
+    ;; FIXME we have to wait article buffer is ready (only large buffer)
+    ;; Is there a better solution to know that?
+    ;; If we don't wait `bookmark-default-handler' will have no chance
+    ;; to set position. However there is no error, just wrong pos.
+    (sit-for 1)
+    (when (string= buf "Gnus-art")
+      (other-window 1))
+    (bookmark-default-handler
+     `(""
+       (buffer . ,(current-buffer))
+       . ,(bookmark-get-bookmark-record bookmark)))))
+
 (gnus-summary-make-all-marking-commands)
 
 (gnus-ems-redefine)
@@ -12634,5 +12692,4 @@ If ALL is a number, fetch this number of articles."
 ;; coding: iso-8859-1
 ;; End:
 
-;; arch-tag: 17c6748f-6d00-4d36-bf01-835c42f31235
 ;;; gnus-sum.el ends here