Small rmail fixes.
[bpt/emacs.git] / lisp / mail / rmailsum.el
CommitLineData
537ab246
BG
1;;; rmailsum.el --- make summary buffers for the mail reader
2
73b0cd50 3;; Copyright (C) 1985, 1993-1996, 2000-2011 Free Software Foundation, Inc.
537ab246
BG
4
5;; Maintainer: FSF
6;; Keywords: mail
bd78fa1d 7;; Package: rmail
537ab246
BG
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software: you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23
24;;; Commentary:
25
26;; Extended by Bob Weiner of Motorola
27;; Provided all commands from rmail-mode in rmail-summary-mode and made key
28;; bindings in both modes wholly compatible.
29
30;;; Code:
31
97e92ffb 32;; For rmail-select-summary.
537ab246 33(require 'rmail)
d1be4ec2 34(require 'rfc2047)
537ab246 35
537ab246 36(defcustom rmail-summary-scroll-between-messages t
4b12219b
GM
37 "Non-nil means Rmail summary scroll commands move between messages.
38That is, after `rmail-summary-scroll-msg-up' reaches the end of a
39message, it moves to the next message; and similarly for
40`rmail-summary-scroll-msg-down'."
537ab246
BG
41 :type 'boolean
42 :group 'rmail-summary)
43
4b12219b
GM
44;; FIXME could do with a :set function that regenerates the summary
45;; and updates rmail-summary-vector.
537ab246 46(defcustom rmail-summary-line-count-flag t
4b12219b
GM
47 "Non-nil means Rmail summary should show the number of lines in each message.
48Setting this option to nil might speed up the generation of summaries."
537ab246
BG
49 :type 'boolean
50 :group 'rmail-summary)
51
52(defvar rmail-summary-font-lock-keywords
53 '(("^.....D.*" . font-lock-string-face) ; Deleted.
54 ("^.....-.*" . font-lock-type-face) ; Unread.
55 ;; Neither of the below will be highlighted if either of the above are:
56 ("^.....[^D-] \\(......\\)" 1 font-lock-keyword-face) ; Date.
57 ("{ \\([^\n}]+\\) }" 1 font-lock-comment-face)) ; Labels.
58 "Additional expressions to highlight in Rmail Summary mode.")
59
97e92ffb 60(defvar rmail-summary-redo nil
537ab246
BG
61 "(FUNCTION . ARGS) to regenerate this Rmail summary buffer.")
62
97e92ffb
GM
63(defvar rmail-summary-overlay nil
64 "Overlay used to highlight the current message in the Rmail summary.")
537ab246
BG
65(put 'rmail-summary-overlay 'permanent-local t)
66
b016851c
SM
67(defvar rmail-summary-mode-map
68 (let ((map (make-keymap)))
69 (suppress-keymap map)
70 (define-key map [mouse-2] 'rmail-summary-mouse-goto-message)
71 (define-key map "a" 'rmail-summary-add-label)
72 (define-key map "b" 'rmail-summary-bury)
73 (define-key map "c" 'rmail-summary-continue)
74 (define-key map "d" 'rmail-summary-delete-forward)
75 (define-key map "\C-d" 'rmail-summary-delete-backward)
76 (define-key map "e" 'rmail-summary-edit-current-message)
77 (define-key map "f" 'rmail-summary-forward)
78 (define-key map "g" 'rmail-summary-get-new-mail)
79 (define-key map "h" 'rmail-summary)
80 (define-key map "i" 'rmail-summary-input)
81 (define-key map "j" 'rmail-summary-goto-msg)
82 (define-key map "\C-m" 'rmail-summary-goto-msg)
83 (define-key map "k" 'rmail-summary-kill-label)
84 (define-key map "l" 'rmail-summary-by-labels)
85 (define-key map "\e\C-h" 'rmail-summary)
86 (define-key map "\e\C-l" 'rmail-summary-by-labels)
87 (define-key map "\e\C-r" 'rmail-summary-by-recipients)
88 (define-key map "\e\C-s" 'rmail-summary-by-regexp)
89 ;; `f' for "from".
90 (define-key map "\e\C-f" 'rmail-summary-by-senders)
91 (define-key map "\e\C-t" 'rmail-summary-by-topic)
92 (define-key map "m" 'rmail-summary-mail)
93 (define-key map "\M-m" 'rmail-summary-retry-failure)
94 (define-key map "n" 'rmail-summary-next-msg)
95 (define-key map "\en" 'rmail-summary-next-all)
96 (define-key map "\e\C-n" 'rmail-summary-next-labeled-message)
97 (define-key map "o" 'rmail-summary-output)
98 (define-key map "\C-o" 'rmail-summary-output-as-seen)
99 (define-key map "p" 'rmail-summary-previous-msg)
100 (define-key map "\ep" 'rmail-summary-previous-all)
101 (define-key map "\e\C-p" 'rmail-summary-previous-labeled-message)
102 (define-key map "q" 'rmail-summary-quit)
103 (define-key map "Q" 'rmail-summary-wipe)
104 (define-key map "r" 'rmail-summary-reply)
105 (define-key map "s" 'rmail-summary-expunge-and-save)
106 ;; See rms's comment in rmail.el
107 ;; (define-key map "\er" 'rmail-summary-search-backward)
108 (define-key map "\es" 'rmail-summary-search)
109 (define-key map "t" 'rmail-summary-toggle-header)
110 (define-key map "u" 'rmail-summary-undelete)
111 (define-key map "\M-u" 'rmail-summary-undelete-many)
112 (define-key map "x" 'rmail-summary-expunge)
113 (define-key map "w" 'rmail-summary-output-body)
114 (define-key map "v" 'rmail-mime)
115 (define-key map "." 'rmail-summary-beginning-of-message)
116 (define-key map "/" 'rmail-summary-end-of-message)
117 (define-key map "<" 'rmail-summary-first-message)
118 (define-key map ">" 'rmail-summary-last-message)
119 (define-key map " " 'rmail-summary-scroll-msg-up)
120 (define-key map "\177" 'rmail-summary-scroll-msg-down)
121 (define-key map "?" 'describe-mode)
122 (define-key map "\C-c\C-n" 'rmail-summary-next-same-subject)
123 (define-key map "\C-c\C-p" 'rmail-summary-previous-same-subject)
124 (define-key map "\C-c\C-s\C-d" 'rmail-summary-sort-by-date)
125 (define-key map "\C-c\C-s\C-s" 'rmail-summary-sort-by-subject)
126 (define-key map "\C-c\C-s\C-a" 'rmail-summary-sort-by-author)
127 (define-key map "\C-c\C-s\C-r" 'rmail-summary-sort-by-recipient)
128 (define-key map "\C-c\C-s\C-c" 'rmail-summary-sort-by-correspondent)
129 (define-key map "\C-c\C-s\C-l" 'rmail-summary-sort-by-lines)
130 (define-key map "\C-c\C-s\C-k" 'rmail-summary-sort-by-labels)
131 (define-key map "\C-x\C-s" 'rmail-summary-save-buffer)
132
133 ;; Menu bar bindings.
134
135 (define-key map [menu-bar] (make-sparse-keymap))
136
137 (define-key map [menu-bar classify]
138 (cons "Classify" (make-sparse-keymap "Classify")))
139
140 (define-key map [menu-bar classify output-menu]
141 '("Output (Rmail Menu)..." . rmail-summary-output-menu))
142
143 (define-key map [menu-bar classify input-menu]
144 '("Input Rmail File (menu)..." . rmail-input-menu))
145
146 (define-key map [menu-bar classify input-menu]
147 '(nil))
148
149 (define-key map [menu-bar classify output-menu]
150 '(nil))
151
152 (define-key map [menu-bar classify output-body]
153 '("Output body..." . rmail-summary-output-body))
154
155 (define-key map [menu-bar classify output-inbox]
156 '("Output..." . rmail-summary-output))
157
158 (define-key map [menu-bar classify output]
159 '("Output as seen..." . rmail-summary-output-as-seen))
160
161 (define-key map [menu-bar classify kill-label]
162 '("Kill Label..." . rmail-summary-kill-label))
163
164 (define-key map [menu-bar classify add-label]
165 '("Add Label..." . rmail-summary-add-label))
166
167 (define-key map [menu-bar summary]
168 (cons "Summary" (make-sparse-keymap "Summary")))
169
170 (define-key map [menu-bar summary senders]
171 '("By Senders..." . rmail-summary-by-senders))
172
173 (define-key map [menu-bar summary labels]
174 '("By Labels..." . rmail-summary-by-labels))
175
176 (define-key map [menu-bar summary recipients]
177 '("By Recipients..." . rmail-summary-by-recipients))
178
179 (define-key map [menu-bar summary topic]
180 '("By Topic..." . rmail-summary-by-topic))
181
182 (define-key map [menu-bar summary regexp]
183 '("By Regexp..." . rmail-summary-by-regexp))
184
185 (define-key map [menu-bar summary all]
186 '("All" . rmail-summary))
187
188 (define-key map [menu-bar mail]
189 (cons "Mail" (make-sparse-keymap "Mail")))
190
191 (define-key map [menu-bar mail rmail-summary-get-new-mail]
192 '("Get New Mail" . rmail-summary-get-new-mail))
193
194 (define-key map [menu-bar mail lambda]
195 '("----"))
196
197 (define-key map [menu-bar mail continue]
198 '("Continue" . rmail-summary-continue))
199
200 (define-key map [menu-bar mail resend]
201 '("Re-send..." . rmail-summary-resend))
202
203 (define-key map [menu-bar mail forward]
204 '("Forward" . rmail-summary-forward))
205
206 (define-key map [menu-bar mail retry]
207 '("Retry" . rmail-summary-retry-failure))
208
209 (define-key map [menu-bar mail reply]
210 '("Reply" . rmail-summary-reply))
211
212 (define-key map [menu-bar mail mail]
213 '("Mail" . rmail-summary-mail))
214
215 (define-key map [menu-bar delete]
216 (cons "Delete" (make-sparse-keymap "Delete")))
217
218 (define-key map [menu-bar delete expunge/save]
219 '("Expunge/Save" . rmail-summary-expunge-and-save))
220
221 (define-key map [menu-bar delete expunge]
222 '("Expunge" . rmail-summary-expunge))
223
224 (define-key map [menu-bar delete undelete]
225 '("Undelete" . rmail-summary-undelete))
226
227 (define-key map [menu-bar delete delete]
228 '("Delete" . rmail-summary-delete-forward))
229
230 (define-key map [menu-bar move]
231 (cons "Move" (make-sparse-keymap "Move")))
232
233 (define-key map [menu-bar move search-back]
234 '("Search Back..." . rmail-summary-search-backward))
235
236 (define-key map [menu-bar move search]
237 '("Search..." . rmail-summary-search))
238
239 (define-key map [menu-bar move previous]
240 '("Previous Nondeleted" . rmail-summary-previous-msg))
241
242 (define-key map [menu-bar move next]
243 '("Next Nondeleted" . rmail-summary-next-msg))
244
245 (define-key map [menu-bar move last]
246 '("Last" . rmail-summary-last-message))
247
248 (define-key map [menu-bar move first]
249 '("First" . rmail-summary-first-message))
250
251 (define-key map [menu-bar move previous]
252 '("Previous" . rmail-summary-previous-all))
253
254 (define-key map [menu-bar move next]
255 '("Next" . rmail-summary-next-all))
256 map)
97e92ffb 257 "Keymap used in Rmail summary mode.")
537ab246
BG
258
259;; Entry points for making a summary buffer.
260
261;; Regenerate the contents of the summary
262;; using the same selection criterion as last time.
263;; M-x revert-buffer in a summary buffer calls this function.
264(defun rmail-update-summary (&rest ignore)
265 (apply (car rmail-summary-redo) (cdr rmail-summary-redo)))
266
267;;;###autoload
268(defun rmail-summary ()
269 "Display a summary of all messages, one line per message."
270 (interactive)
40500957 271 (rmail-new-summary "All" '(rmail-summary) nil))
537ab246
BG
272
273;;;###autoload
274(defun rmail-summary-by-labels (labels)
275 "Display a summary of all messages with one or more LABELS.
276LABELS should be a string containing the desired labels, separated by commas."
277 (interactive "sLabels to summarize by: ")
278 (if (string= labels "")
279 (setq labels (or rmail-last-multi-labels
280 (error "No label specified"))))
281 (setq rmail-last-multi-labels labels)
282 (rmail-new-summary (concat "labels " labels)
283 (list 'rmail-summary-by-labels labels)
284 'rmail-message-labels-p
bfc03247
CY
285 (concat " \\("
286 (mail-comma-list-regexp labels)
287 "\\)\\(,\\|\\'\\)")))
537ab246 288
4b12219b
GM
289;; FIXME "a string of regexps separated by commas" makes no sense because:
290;; i) it's pointless (you can just use \\|)
291;; ii) it's broken (you can't specify a literal comma)
292;; rmail-summary-by-topic and rmail-summary-by-senders have the same issue.
537ab246
BG
293;;;###autoload
294(defun rmail-summary-by-recipients (recipients &optional primary-only)
295 "Display a summary of all messages with the given RECIPIENTS.
296Normally checks the To, From and Cc fields of headers;
297but if PRIMARY-ONLY is non-nil (prefix arg given),
298 only look in the To and From fields.
299RECIPIENTS is a string of regexps separated by commas."
300 (interactive "sRecipients to summarize by: \nP")
301 (rmail-new-summary
302 (concat "recipients " recipients)
303 (list 'rmail-summary-by-recipients recipients primary-only)
304 'rmail-message-recipients-p
305 (mail-comma-list-regexp recipients) primary-only))
306
307(defun rmail-message-recipients-p (msg recipients &optional primary-only)
308 (rmail-apply-in-message msg 'rmail-message-recipients-p-1
309 recipients primary-only))
310
311(defun rmail-message-recipients-p-1 (recipients &optional primary-only)
3ea5d765
GM
312 ;; mail-fetch-field does not care where it starts from.
313 (narrow-to-region (point) (progn (search-forward "\n\n") (point)))
537ab246
BG
314 (or (string-match recipients (or (mail-fetch-field "To") ""))
315 (string-match recipients (or (mail-fetch-field "From") ""))
316 (if (not primary-only)
317 (string-match recipients (or (mail-fetch-field "Cc") "")))))
318
4b12219b
GM
319;; FIXME I find this a non-obvious name for what this function does.
320;; Also, the optional WHOLE-MESSAGE argument of r-s-by-topic would
321;; seem more natural here.
537ab246
BG
322;;;###autoload
323(defun rmail-summary-by-regexp (regexp)
324 "Display a summary of all messages according to regexp REGEXP.
325If the regular expression is found in the header of the message
326\(including in the date and other lines, as well as the subject line),
4b12219b 327Emacs will list the message in the summary."
537ab246
BG
328 (interactive "sRegexp to summarize by: ")
329 (if (string= regexp "")
330 (setq regexp (or rmail-last-regexp
331 (error "No regexp specified"))))
332 (setq rmail-last-regexp regexp)
333 (rmail-new-summary (concat "regexp " regexp)
334 (list 'rmail-summary-by-regexp regexp)
335 'rmail-message-regexp-p
336 regexp))
337
338(defun rmail-message-regexp-p (msg regexp)
339 "Return t, if for message number MSG, regexp REGEXP matches in the header."
340 (rmail-apply-in-message msg 'rmail-message-regexp-p-1 msg regexp))
341
342(defun rmail-message-regexp-p-1 (msg regexp)
3ea5d765 343 ;; Search functions can expect to start from the beginning.
3fbc055f 344 (narrow-to-region (point) (save-excursion (search-forward "\n\n") (point)))
7fb18e9e
GM
345 (if (and rmail-enable-mime
346 rmail-search-mime-header-function)
347 (funcall rmail-search-mime-header-function msg regexp (point))
537ab246
BG
348 (re-search-forward regexp nil t)))
349
350;;;###autoload
351(defun rmail-summary-by-topic (subject &optional whole-message)
352 "Display a summary of all messages with the given SUBJECT.
70369cd3
GM
353Normally checks just the Subject field of headers; but with prefix
354argument WHOLE-MESSAGE is non-nil, looks in the whole message.
537ab246
BG
355SUBJECT is a string of regexps separated by commas."
356 (interactive
70369cd3
GM
357 ;; We quote the default subject, because if it contains regexp
358 ;; special characters (eg "?"), it can fail to match itself. (Bug#2333)
359 (let* ((subject (regexp-quote (rmail-simplified-subject)))
537ab246
BG
360 (prompt (concat "Topics to summarize by (regexp"
361 (if subject ", default current subject" "")
362 "): ")))
363 (list (read-string prompt nil nil subject) current-prefix-arg)))
364 (rmail-new-summary
365 (concat "about " subject)
366 (list 'rmail-summary-by-topic subject whole-message)
367 'rmail-message-subject-p
368 (mail-comma-list-regexp subject) whole-message))
369
370(defun rmail-message-subject-p (msg subject &optional whole-message)
371 (if whole-message
372 (rmail-apply-in-message msg 're-search-forward subject nil t)
373 (string-match subject (rmail-simplified-subject msg))))
374
375;;;###autoload
376(defun rmail-summary-by-senders (senders)
ac203e01
GM
377 "Display a summary of all messages whose \"From\" field matches SENDERS.
378SENDERS is a string of regexps separated by commas."
537ab246
BG
379 (interactive "sSenders to summarize by: ")
380 (rmail-new-summary
381 (concat "senders " senders)
382 (list 'rmail-summary-by-senders senders)
383 'rmail-message-senders-p
384 (mail-comma-list-regexp senders)))
385
386(defun rmail-message-senders-p (msg senders)
387 (string-match senders (or (rmail-get-header "From" msg) "")))
388\f
389;; General making of a summary buffer.
390
391(defvar rmail-summary-symbol-number 0)
392
393(defvar rmail-new-summary-line-count)
394
395(defun rmail-new-summary (desc redo func &rest args)
396 "Create a summary of selected messages.
397DESC makes part of the mode line of the summary buffer. REDO is form ...
398For each message, FUNC is applied to the message number and ARGS...
399and if the result is non-nil, that message is included.
400nil for FUNCTION means all messages."
401 (message "Computing summary lines...")
402 (unless rmail-buffer
403 (error "No RMAIL buffer found"))
71e027ac 404 (let (mesg was-in-summary sumbuf)
537ab246
BG
405 (if (eq major-mode 'rmail-summary-mode)
406 (setq was-in-summary t))
407 (with-current-buffer rmail-buffer
71e027ac
GM
408 (setq rmail-summary-buffer (rmail-new-summary-1 desc redo func args)
409 ;; r-s-b is buffer-local.
071c2340
GM
410 sumbuf rmail-summary-buffer
411 mesg rmail-current-message))
537ab246
BG
412 ;; Now display the summary buffer and go to the right place in it.
413 (unless was-in-summary
414 (if (and (one-window-p)
415 pop-up-windows
416 (not pop-up-frames))
417 ;; If there is just one window, put the summary on the top.
418 (progn
419 (split-window (selected-window) rmail-summary-window-size)
420 (select-window (next-window (frame-first-window)))
71e027ac 421 (rmail-pop-to-buffer sumbuf)
537ab246
BG
422 ;; If pop-to-buffer did not use that window, delete that
423 ;; window. (This can happen if it uses another frame.)
71e027ac 424 (if (not (eq sumbuf (window-buffer (frame-first-window))))
537ab246 425 (delete-other-windows)))
71e027ac 426 (rmail-pop-to-buffer sumbuf))
537ab246
BG
427 (set-buffer rmail-buffer)
428 ;; This is how rmail makes the summary buffer reappear.
429 ;; We do this here to make the window the proper size.
430 (rmail-select-summary nil)
431 (set-buffer rmail-summary-buffer))
432 (rmail-summary-goto-msg mesg t t)
433 (rmail-summary-construct-io-menu)
434 (message "Computing summary lines...done")))
435
436(defun rmail-new-summary-1 (description form function args)
437 "Filter messages to obtain summary lines.
438DESCRIPTION is added to the mode line.
439
440Return the summary buffer by invoking FUNCTION on each message
441passing the message number and ARGS...
442
443REDO is a form ...
444
445The current buffer must be a Rmail buffer either containing a
446collection of mbox formatted messages or displaying a single
447message."
448 (let ((summary-msgs ())
449 (rmail-new-summary-line-count 0)
450 (sumbuf (rmail-get-create-summary-buffer)))
451 ;; Scan the messages, getting their summary strings
452 ;; and putting the list of them in SUMMARY-MSGS.
453 (let ((msgnum 1)
454 (main-buffer (current-buffer))
455 (total rmail-total-messages)
456 (inhibit-read-only t))
457 (save-excursion
458 ;; Go where the mbox text is.
459 (if (rmail-buffers-swapped-p)
460 (set-buffer rmail-view-buffer))
461 (let ((old-min (point-min-marker))
462 (old-max (point-max-marker)))
463 (unwind-protect
464 ;; Can't use save-restriction here; that doesn't work if we
465 ;; plan to modify text outside the original restriction.
466 (save-excursion
467 (widen)
468 (goto-char (point-min))
469 (while (>= total msgnum)
470 ;; Go back to the Rmail buffer so
471 ;; so FUNCTION and rmail-get-summary can see its local vars.
472 (with-current-buffer main-buffer
473 ;; First test whether to include this message.
474 (if (or (null function)
475 (apply function msgnum args))
476 (setq summary-msgs
477 (cons (cons msgnum (rmail-get-summary msgnum))
478 summary-msgs))))
479 (setq msgnum (1+ msgnum))
480 ;; Provide a periodic User progress message.
435f0dd2
EZ
481 (if (and (not (zerop rmail-new-summary-line-count))
482 (zerop (% rmail-new-summary-line-count 10)))
537ab246
BG
483 (message "Computing summary lines...%d"
484 rmail-new-summary-line-count)))
485 (setq summary-msgs (nreverse summary-msgs)))
486 (narrow-to-region old-min old-max)))))
537ab246
BG
487 ;; Temporarily, while summary buffer is unfinished,
488 ;; we "don't have" a summary.
70369cd3 489 (setq rmail-summary-buffer nil)
537ab246
BG
490 ;; I have not a clue what this clause is doing. If you read this
491 ;; chunk of code and have a clue, then please email that clue to
492 ;; pmr@pajato.com
537ab246
BG
493 (if rmail-enable-mime
494 (with-current-buffer rmail-buffer
495 (setq rmail-summary-buffer nil)))
537ab246
BG
496 (save-excursion
497 (let ((rbuf (current-buffer))
498 (total rmail-total-messages))
499 (set-buffer sumbuf)
500 ;; Set up the summary buffer's contents.
501 (let ((buffer-read-only nil))
502 (erase-buffer)
503 (while summary-msgs
504 (princ (cdr (car summary-msgs)) sumbuf)
505 (setq summary-msgs (cdr summary-msgs)))
506 (goto-char (point-min)))
507 ;; Set up the rest of its state and local variables.
508 (setq buffer-read-only t)
509 (rmail-summary-mode)
510 (make-local-variable 'minor-mode-alist)
511 (setq minor-mode-alist (list (list t (concat ": " description))))
512 (setq rmail-buffer rbuf
513 rmail-summary-redo form
514 rmail-total-messages total)))
515 sumbuf))
516
517(defun rmail-get-create-summary-buffer ()
11d6c061
GM
518 "Return the Rmail summary buffer.
519If necessary, it is created and undo is disabled."
537ab246
BG
520 (if (and rmail-summary-buffer (buffer-name rmail-summary-buffer))
521 rmail-summary-buffer
11d6c061
GM
522 (let ((buff (generate-new-buffer (concat (buffer-name) "-summary"))))
523 (with-current-buffer buff
524 (setq buffer-undo-list t))
525 buff)))
8d2b2df4 526
537ab246
BG
527\f
528;; Low levels of generating a summary.
529
530(defun rmail-get-summary (msgnum)
531 "Return the summary line for message MSGNUM.
532The mbox buffer must be current when you call this function
533even if its text is swapped.
534
535If the message has a summary line already, it will be stored in
536the message as a header and simply returned, otherwise the
537summary line is created, saved in the message header, cached and
538returned.
539
540The current buffer contains the unrestricted message collection."
541 (let ((line (aref rmail-summary-vector (1- msgnum))))
542 (unless line
543 ;; Register a summary line for MSGNUM.
544 (setq rmail-new-summary-line-count (1+ rmail-new-summary-line-count)
545 line (rmail-create-summary-line msgnum))
546 ;; Cache the summary line for use during this Rmail session.
547 (aset rmail-summary-vector (1- msgnum) line))
548 line))
549
d1be4ec2 550(defcustom rmail-summary-line-decoder (function rfc2047-decode-string)
8d2b2df4 551 "Function to decode a Rmail summary line.
537ab246
BG
552It receives the summary line for one message as a string
553and should return the decoded string.
554
d1be4ec2
KH
555By default, it is `rfc2047-decode-string', which decodes MIME-encoded
556subject."
537ab246 557 :type 'function
d1be4ec2 558 :version "23.3"
537ab246
BG
559 :group 'rmail-summary)
560
561(defun rmail-create-summary-line (msgnum)
562 "Return the summary line for message MSGNUM.
563Obtain the message summary from the header if it is available
564otherwise create it and store it in the message header.
565
566The mbox buffer must be current when you call this function
567even if its text is swapped."
568 (let ((beg (rmail-msgbeg msgnum))
569 (end (rmail-msgend msgnum))
570 (deleted (rmail-message-deleted-p msgnum))
3ea5d765
GM
571 ;; Does not work (swapped?)
572;;; (unseen (rmail-message-unseen-p msgnum))
573 unseen lines)
537ab246
BG
574 (save-excursion
575 ;; Switch to the buffer that has the whole mbox text.
576 (if (rmail-buffers-swapped-p)
577 (set-buffer rmail-view-buffer))
578 ;; Now we can compute the line count.
579 (if rmail-summary-line-count-flag
580 (setq lines (count-lines beg end)))
537ab246
BG
581 ;; Narrow to the message header.
582 (save-excursion
8acdd748
GM
583 (save-restriction
584 (widen)
585 (goto-char beg)
586 (if (search-forward "\n\n" end t)
587 (progn
588 (narrow-to-region beg (point))
589 ;; Replace rmail-message-unseen-p from above.
590 (goto-char beg)
591 (setq unseen (and (search-forward
592 (concat rmail-attribute-header ": ") nil t)
593 (looking-at "......U")))
594 ;; Generate a status line from the message.
595 (rmail-create-summary msgnum deleted unseen lines))
596 (rmail-error-bad-format msgnum)))))))
537ab246 597
3ea5d765
GM
598;; FIXME this is now unused.
599;; The intention was to display in the summary something like {E}
600;; for an edited messaged, similarly for answered, etc.
601;; But that conflicts with the previous rmail usage, where
602;; any user-defined { labels } occupied this space.
603;; So whilst it would be nice to have this information in the summary,
604;; it would need to go somewhere else.
605(defun rmail-get-summary-status ()
606 "Return a coded string wrapped in curly braces denoting the status.
537ab246
BG
607
608The current buffer must already be narrowed to the message headers for
609the message being processed."
610 (let ((status (mail-fetch-field rmail-attribute-header))
611 (index 0)
612 (result "")
613 char)
614 ;; Strip off the read/unread and the deleted attribute which are
615 ;; handled separately.
616 (setq status
617 (if status
618 (concat (substring status 0 1) (substring status 2 6))
619 ""))
620 (while (< index (length status))
621 (unless (string= "-" (setq char (substring status index (1+ index))))
622 (setq result (concat result char)))
623 (setq index (1+ index)))
624 (when (> (length result) 0)
625 (setq result (concat "{" result "}")))
626 result))
627
5c9b1aaa
GM
628(autoload 'rmail-make-label "rmailkwd")
629
3ea5d765
GM
630(defun rmail-get-summary-labels ()
631 "Return a string wrapped in curly braces with the current message labels.
632Returns nil if there are no labels. The current buffer must
633already be narrowed to the message headers for the message being
634processed."
635 (let ((labels (mail-fetch-field rmail-keyword-header)))
c49edcd1
GM
636 (and labels
637 (not (string-equal labels ""))
5c9b1aaa
GM
638 (progn
639 ;; Intern so that rmail-read-label can offer completion.
640 (mapc 'rmail-make-label (split-string labels ", "))
641 (format "{ %s } " labels)))))
3ea5d765 642
537ab246
BG
643(defun rmail-create-summary (msgnum deleted unseen lines)
644 "Return the summary line for message MSGNUM.
645The current buffer should already be narrowed to the header for that message.
646It could be either buffer, so don't access Rmail local variables.
647DELETED is t if this message is marked deleted.
648UNSEEN is t if it is marked unseen.
649LINES is the number of lines in the message (if we should display that)
650 or else nil."
651 (goto-char (point-min))
652 (let ((line (rmail-header-summary))
653 (labels (rmail-get-summary-labels))
654 pos status prefix basic-start basic-end linecount-string)
655
656 (setq linecount-string
657 (cond
658 ((not lines) " ")
659 ((<= lines 9) (format " [%d]" lines))
660 ((<= lines 99) (format " [%d]" lines))
661 ((<= lines 999) (format " [%d]" lines))
662 ((<= lines 9999) (format " [%dk]" (/ lines 1000)))
663 ((<= lines 99999) (format " [%dk]" (/ lines 1000)))
664 (t (format "[%dk]" (/ lines 1000)))))
665
666 (setq status (cond
667 (deleted ?D)
668 (unseen ?-)
669 (t ? ))
3ea5d765 670 prefix (format "%5d%c " msgnum status)
537ab246
BG
671 basic-start (car line)
672 basic-end (cadr line))
673 (funcall rmail-summary-line-decoder
674 (concat prefix basic-start linecount-string " "
675 labels basic-end))))
676
537ab246
BG
677(defun rmail-header-summary ()
678 "Return a message summary based on the message headers.
679The value is a list of two strings, the first and second parts of the summary.
680
681The current buffer must already be narrowed to the message headers for
682the message being processed."
683 (goto-char (point-min))
684 (list
685 (concat (save-excursion
686 (if (not (re-search-forward "^Date:" nil t))
687 " "
8f94cbed
EZ
688 ;; Match month names case-insensitively
689 (cond ((let ((case-fold-search t))
690 (re-search-forward "\\([^0-9:]\\)\\([0-3]?[0-9]\\)\\([- \t_]+\\)\\([adfjmnos][aceopu][bcglnprtvy]\\)"
691 (line-end-position) t))
537ab246
BG
692 (format "%2d-%3s"
693 (string-to-number (buffer-substring
694 (match-beginning 2)
695 (match-end 2)))
696 (buffer-substring
697 (match-beginning 4) (match-end 4))))
8f94cbed
EZ
698 ((let ((case-fold-search t))
699 (re-search-forward "\\([^a-z]\\)\\([adfjmnos][acepou][bcglnprtvy]\\)\\([-a-z \t_]*\\)\\([0-9][0-9]?\\)"
700 (line-end-position) t))
537ab246
BG
701 (format "%2d-%3s"
702 (string-to-number (buffer-substring
703 (match-beginning 4)
704 (match-end 4)))
705 (buffer-substring
706 (match-beginning 2) (match-end 2))))
707 ((re-search-forward "\\(19\\|20\\)\\([0-9][0-9]\\)-\\([01][0-9]\\)-\\([0-3][0-9]\\)"
98a9d488 708 (line-end-position) t)
537ab246
BG
709 (format "%2s%2s%2s"
710 (buffer-substring
711 (match-beginning 2) (match-end 2))
712 (buffer-substring
713 (match-beginning 3) (match-end 3))
714 (buffer-substring
715 (match-beginning 4) (match-end 4))))
716 (t "??????"))))
717 " "
718 (save-excursion
719 (let* ((from (and (re-search-forward "^From:[ \t]*" nil t)
720 (mail-strip-quoted-names
721 (buffer-substring
722 (1- (point))
723 ;; Get all the lines of the From field
724 ;; so that we get a whole comment if there is one,
725 ;; so that mail-strip-quoted-names can discard it.
726 (let ((opoint (point)))
727 (while (progn (forward-line 1)
728 (looking-at "[ \t]")))
729 ;; Back up over newline, then trailing spaces or tabs
730 (forward-char -1)
731 (skip-chars-backward " \t")
732 (point))))))
733 len mch lo)
734 (if (or (null from)
735 (string-match
736 (or rmail-user-mail-address-regexp
737 (concat "^\\("
738 (regexp-quote (user-login-name))
739 "\\($\\|@\\)\\|"
740 (regexp-quote
741 ;; Don't lose if run from init file
742 ;; where user-mail-address is not
743 ;; set yet.
744 (or user-mail-address
745 (concat (user-login-name) "@"
746 (or mail-host-address
747 (system-name)))))
748 "\\>\\)"))
749 from))
750 ;; No From field, or it's this user.
751 (save-excursion
752 (goto-char (point-min))
753 (if (not (re-search-forward "^To:[ \t]*" nil t))
754 nil
755 (setq from
756 (concat "to: "
757 (mail-strip-quoted-names
758 (buffer-substring
759 (point)
760 (progn (end-of-line)
761 (skip-chars-backward " \t")
762 (point)))))))))
763 (if (null from)
764 " "
765 (setq len (length from))
766 (setq mch (string-match "[@%]" from))
767 (format "%25s"
768 (if (or (not mch) (<= len 25))
769 (substring from (max 0 (- len 25)))
770 (substring from
771 (setq lo (cond ((< (- mch 14) 0) 0)
772 ((< len (+ mch 11))
773 (- len 25))
774 (t (- mch 14))))
775 (min len (+ lo 25)))))))))
776 (concat (if (re-search-forward "^Subject:" nil t)
d1be4ec2
KH
777 (let (pos str)
778 (skip-chars-forward " \t")
779 (setq pos (point))
780 (forward-line 1)
781 (setq str (buffer-substring pos (1- (point))))
782 (while (looking-at "\\s ")
783 (setq str (concat str " "
784 (buffer-substring (match-end 0)
785 (line-end-position))))
786 (forward-line 1))
787 str)
537ab246
BG
788 (re-search-forward "[\n][\n]+" nil t)
789 (buffer-substring (point) (progn (end-of-line) (point))))
790 "\n")))
791\f
792;; Simple motion in a summary buffer.
793
794(defun rmail-summary-next-all (&optional number)
795 (interactive "p")
796 (forward-line (if number number 1))
797 ;; It doesn't look nice to move forward past the last message line.
798 (and (eobp) (> number 0)
799 (forward-line -1))
800 (display-buffer rmail-buffer))
801
802(defun rmail-summary-previous-all (&optional number)
803 (interactive "p")
804 (forward-line (- (if number number 1)))
805 ;; It doesn't look nice to move forward past the last message line.
806 (and (eobp) (< number 0)
807 (forward-line -1))
808 (display-buffer rmail-buffer))
809
810(defun rmail-summary-next-msg (&optional number)
811 "Display next non-deleted msg from rmail file.
812With optional prefix argument NUMBER, moves forward this number of non-deleted
813messages, or backward if NUMBER is negative."
814 (interactive "p")
815 (forward-line 0)
816 (and (> number 0) (end-of-line))
817 (let ((count (if (< number 0) (- number) number))
818 (search (if (> number 0) 're-search-forward 're-search-backward))
819 (non-del-msg-found nil))
820 (while (and (> count 0) (setq non-del-msg-found
821 (or (funcall search "^.....[^D]" nil t)
822 non-del-msg-found)))
823 (setq count (1- count))))
824 (beginning-of-line)
825 (display-buffer rmail-buffer))
826
827(defun rmail-summary-previous-msg (&optional number)
828 "Display previous non-deleted msg from rmail file.
829With optional prefix argument NUMBER, moves backward this number of
830non-deleted messages."
831 (interactive "p")
832 (rmail-summary-next-msg (- (if number number 1))))
833
834(defun rmail-summary-next-labeled-message (n labels)
835 "Show next message with LABELS. Defaults to last labels used.
836With prefix argument N moves forward N messages with these labels."
837 (interactive "p\nsMove to next msg with labels: ")
838 (let (msg)
937e6a56 839 (with-current-buffer rmail-buffer
537ab246
BG
840 (rmail-next-labeled-message n labels)
841 (setq msg rmail-current-message))
842 (rmail-summary-goto-msg msg)))
843
844(defun rmail-summary-previous-labeled-message (n labels)
845 "Show previous message with LABELS. Defaults to last labels used.
846With prefix argument N moves backward N messages with these labels."
847 (interactive "p\nsMove to previous msg with labels: ")
848 (let (msg)
937e6a56 849 (with-current-buffer rmail-buffer
537ab246
BG
850 (rmail-previous-labeled-message n labels)
851 (setq msg rmail-current-message))
852 (rmail-summary-goto-msg msg)))
853
854(defun rmail-summary-next-same-subject (n)
855 "Go to the next message in the summary having the same subject.
856With prefix argument N, do this N times.
857If N is negative, go backwards."
858 (interactive "p")
859 (let ((forward (> n 0))
860 subject i found)
861 (with-current-buffer rmail-buffer
862 (setq subject (rmail-simplified-subject)
863 i rmail-current-message))
864 (save-excursion
865 (while (and (/= n 0)
866 (if forward
867 (not (eobp))
868 (not (bobp))))
869 (let (done)
870 (while (and (not done)
871 (if forward
872 (not (eobp))
873 (not (bobp))))
874 ;; Advance thru summary.
875 (forward-line (if forward 1 -1))
876 ;; Get msg number of this line.
877 (setq i (string-to-number
878 (buffer-substring (point)
879 (min (point-max) (+ 6 (point))))))
880 (setq done (string-equal subject (rmail-simplified-subject i))))
881 (if done (setq found i)))
882 (setq n (if forward (1- n) (1+ n)))))
883 (if found
884 (rmail-summary-goto-msg found)
885 (error "No %s message with same subject"
886 (if forward "following" "previous")))))
887
888(defun rmail-summary-previous-same-subject (n)
889 "Go to the previous message in the summary having the same subject.
890With prefix argument N, do this N times.
891If N is negative, go forwards instead."
892 (interactive "p")
893 (rmail-summary-next-same-subject (- n)))
894\f
895;; Delete and undelete summary commands.
896
897(defun rmail-summary-delete-forward (&optional count)
898 "Delete this message and move to next nondeleted one.
899Deleted messages stay in the file until the \\[rmail-expunge] command is given.
900A prefix argument serves as a repeat count;
901a negative argument means to delete and move backward."
902 (interactive "p")
903 (unless (numberp count) (setq count 1))
904 (let (end del-msg
905 (backward (< count 0)))
906 (while (/= count 0)
907 (rmail-summary-goto-msg)
908 (with-current-buffer rmail-buffer
909 (rmail-delete-message)
910 (setq del-msg rmail-current-message))
911 (rmail-summary-mark-deleted del-msg)
912 (while (and (not (if backward (bobp) (eobp)))
913 (save-excursion (beginning-of-line)
914 (looking-at " *[0-9]+D")))
915 (forward-line (if backward -1 1)))
916 ;; It looks ugly to move to the empty line at end of buffer.
917 (and (eobp) (not backward)
918 (forward-line -1))
919 (setq count
920 (if (> count 0) (1- count) (1+ count))))))
921
922(defun rmail-summary-delete-backward (&optional count)
923 "Delete this message and move to previous nondeleted one.
924Deleted messages stay in the file until the \\[rmail-expunge] command is given.
925A prefix argument serves as a repeat count;
926a negative argument means to delete and move forward."
927 (interactive "p")
928 (rmail-summary-delete-forward (- count)))
929
930(defun rmail-summary-mark-deleted (&optional n undel)
931 ;; Since third arg is t, this only alters the summary, not the Rmail buf.
932 (and n (rmail-summary-goto-msg n t t))
933 (or (eobp)
934 (not (overlay-get rmail-summary-overlay 'face))
935 (let ((buffer-read-only nil))
936 (skip-chars-forward " ")
97e92ffb 937 (skip-chars-forward "0-9")
537ab246
BG
938 (if undel
939 (if (looking-at "D")
940 (progn (delete-char 1) (insert " ")))
941 (delete-char 1)
96a07ae9
CY
942 (insert "D"))
943 ;; Register a new summary line.
944 (with-current-buffer rmail-buffer
945 (aset rmail-summary-vector (1- n) (rmail-create-summary-line n)))))
537ab246
BG
946 (beginning-of-line))
947
8fbbdd44
GM
948(defun rmail-summary-update-line (n)
949 "Update the summary line for message N."
950 (when (rmail-summary-goto-msg n t t)
951 (let* ((buffer-read-only nil)
952 (start (line-beginning-position))
953 (end (line-beginning-position 2))
954 (overlays (overlays-in start end))
955 high ov)
956 (while (and (setq ov (car overlays))
957 (not (setq high (overlay-get ov 'rmail-summary))))
958 (setq overlays (cdr overlays)))
45bd6461 959 (delete-region start end)
8fbbdd44
GM
960 (princ
961 (with-current-buffer rmail-buffer
962 (aset rmail-summary-vector (1- n) (rmail-create-summary-line n)))
963 (current-buffer))
964 (when high
965 (forward-line -1)
966 (rmail-summary-update-highlight nil)))))
967
537ab246
BG
968(defun rmail-summary-mark-undeleted (n)
969 (rmail-summary-mark-deleted n t))
970
971(defun rmail-summary-deleted-p (&optional n)
972 (save-excursion
973 (and n (rmail-summary-goto-msg n nil t))
974 (skip-chars-forward " ")
97e92ffb 975 (skip-chars-forward "0-9")
537ab246
BG
976 (looking-at "D")))
977
978(defun rmail-summary-undelete (&optional arg)
979 "Undelete current message.
980Optional prefix ARG means undelete ARG previous messages."
981 (interactive "p")
982 (if (/= arg 1)
983 (rmail-summary-undelete-many arg)
984 (let ((buffer-read-only nil)
985 (opoint (point)))
986 (end-of-line)
987 (cond ((re-search-backward "\\(^ *[0-9]*\\)\\(D\\)" nil t)
988 (replace-match "\\1 ")
989 (rmail-summary-goto-msg)
990 (if rmail-enable-mime
991 (set-buffer rmail-buffer)
60f2013c 992 (rmail-pop-to-buffer rmail-buffer))
537ab246
BG
993 (and (rmail-message-deleted-p rmail-current-message)
994 (rmail-undelete-previous-message))
995 (if rmail-enable-mime
60f2013c
GM
996 (rmail-pop-to-buffer rmail-buffer))
997 (rmail-pop-to-buffer rmail-summary-buffer))
537ab246
BG
998 (t (goto-char opoint))))))
999
1000(defun rmail-summary-undelete-many (&optional n)
1001 "Undelete all deleted msgs, optional prefix arg N means undelete N prev msgs."
1002 (interactive "P")
937e6a56 1003 (with-current-buffer rmail-buffer
537ab246
BG
1004 (let* ((init-msg (if n rmail-current-message rmail-total-messages))
1005 (rmail-current-message init-msg)
1006 (n (or n rmail-total-messages))
1007 (msgs-undeled 0))
1008 (while (and (> rmail-current-message 0)
1009 (< msgs-undeled n))
1010 (if (rmail-message-deleted-p rmail-current-message)
8acdd748 1011 (progn (rmail-set-attribute rmail-deleted-attr-index nil)
537ab246
BG
1012 (setq msgs-undeled (1+ msgs-undeled))))
1013 (setq rmail-current-message (1- rmail-current-message)))
1014 (set-buffer rmail-summary-buffer)
1015 (setq rmail-current-message init-msg msgs-undeled 0)
1016 (while (and (> rmail-current-message 0)
1017 (< msgs-undeled n))
1018 (if (rmail-summary-deleted-p rmail-current-message)
1019 (progn (rmail-summary-mark-undeleted rmail-current-message)
1020 (setq msgs-undeled (1+ msgs-undeled))))
1021 (setq rmail-current-message (1- rmail-current-message))))
1022 (rmail-summary-goto-msg)))
1023\f
1024;; Rmail Summary mode is suitable only for specially formatted data.
1025(put 'rmail-summary-mode 'mode-class 'special)
1026
1027(defun rmail-summary-mode ()
1028 "Rmail Summary Mode is invoked from Rmail Mode by using \\<rmail-mode-map>\\[rmail-summary].
1029As commands are issued in the summary buffer, they are applied to the
1030corresponding mail messages in the rmail buffer.
1031
1032All normal editing commands are turned off.
1033Instead, nearly all the Rmail mode commands are available,
1034though many of them move only among the messages in the summary.
1035
1036These additional commands exist:
1037
1038\\[rmail-summary-undelete-many] Undelete all or prefix arg deleted messages.
1039\\[rmail-summary-wipe] Delete the summary and go to the Rmail buffer.
1040
1041Commands for sorting the summary:
1042
1043\\[rmail-summary-sort-by-date] Sort by date.
1044\\[rmail-summary-sort-by-subject] Sort by subject.
1045\\[rmail-summary-sort-by-author] Sort by author.
1046\\[rmail-summary-sort-by-recipient] Sort by recipient.
1047\\[rmail-summary-sort-by-correspondent] Sort by correspondent.
1048\\[rmail-summary-sort-by-lines] Sort by lines.
1049\\[rmail-summary-sort-by-labels] Sort by labels."
1050 (interactive)
1051 (kill-all-local-variables)
1052 (setq major-mode 'rmail-summary-mode)
1053 (setq mode-name "RMAIL Summary")
1054 (setq truncate-lines t)
1055 (setq buffer-read-only t)
1056 (set-syntax-table text-mode-syntax-table)
1057 (make-local-variable 'rmail-buffer)
1058 (make-local-variable 'rmail-total-messages)
1059 (make-local-variable 'rmail-current-message)
1060 (setq rmail-current-message nil)
1061 (make-local-variable 'rmail-summary-redo)
1062 (setq rmail-summary-redo nil)
1063 (make-local-variable 'revert-buffer-function)
1064 (make-local-variable 'font-lock-defaults)
1065 (setq font-lock-defaults '(rmail-summary-font-lock-keywords t))
1066 (rmail-summary-enable)
1067 (run-mode-hooks 'rmail-summary-mode-hook))
1068
1069;; Summary features need to be disabled during edit mode.
1070(defun rmail-summary-disable ()
1071 (use-local-map text-mode-map)
1072 (remove-hook 'post-command-hook 'rmail-summary-rmail-update t)
1073 (setq revert-buffer-function nil))
1074
1075(defun rmail-summary-enable ()
1076 (use-local-map rmail-summary-mode-map)
1077 (add-hook 'post-command-hook 'rmail-summary-rmail-update nil t)
1078 (setq revert-buffer-function 'rmail-update-summary))
1079
36aecf22 1080(defun rmail-summary-mark-seen (n &optional nomove unseen)
97e92ffb
GM
1081 "Remove the unseen mark from the current message, update the summary vector.
1082N is the number of the current message. Optional argument NOMOVE
36aecf22
GM
1083non-nil means we are already at the right column. Optional argument
1084UNSEEN non-nil means mark the message as unseen."
97e92ffb
GM
1085 (save-excursion
1086 (unless nomove
1087 (beginning-of-line)
1088 (skip-chars-forward " ")
1089 (skip-chars-forward "0-9"))
36aecf22 1090 (when (char-equal (following-char) (if unseen ?\s ?-))
97e92ffb
GM
1091 (let ((buffer-read-only nil))
1092 (delete-char 1)
36aecf22 1093 (insert (if unseen "-" " ")))
97e92ffb
GM
1094 (let ((line (buffer-substring-no-properties (line-beginning-position)
1095 (line-beginning-position 2))))
1096 (with-current-buffer rmail-buffer
1097 (aset rmail-summary-vector (1- n) line))))))
1098
537ab246
BG
1099(defvar rmail-summary-put-back-unseen nil
1100 "Used for communicating between calls to `rmail-summary-rmail-update'.
1101If it moves to a message within an Incremental Search, and removes
1102the `unseen' attribute from that message, it sets this flag
1103so that if the next motion between messages is in the same Incremental
1104Search, the `unseen' attribute is restored.")
1105
1106;; Show in Rmail the message described by the summary line that point is on,
1107;; but only if the Rmail buffer is already visible.
1108;; This is a post-command-hook in summary buffers.
1109(defun rmail-summary-rmail-update ()
1110 (let (buffer-read-only)
1111 (save-excursion
1112 ;; If at end of buffer, pretend we are on the last text line.
1113 (if (eobp)
1114 (forward-line -1))
1115 (beginning-of-line)
1116 (skip-chars-forward " ")
1117 (let ((msg-num (string-to-number (buffer-substring
1118 (point)
1119 (progn (skip-chars-forward "0-9")
1120 (point))))))
1121 ;; Always leave `unseen' removed
1122 ;; if we get out of isearch mode.
1123 ;; Don't let a subsequent isearch restore that `unseen'.
1124 (if (not isearch-mode)
1125 (setq rmail-summary-put-back-unseen nil))
1126
1127 (or (eq rmail-current-message msg-num)
1128 (let ((window (get-buffer-window rmail-buffer t))
1129 (owin (selected-window)))
1130 (if isearch-mode
36aecf22 1131 (progn
537ab246
BG
1132 ;; If we first saw the previous message in this search,
1133 ;; and we have gone to a different message while searching,
1134 ;; put back `unseen' on the former one.
36aecf22
GM
1135 (when rmail-summary-put-back-unseen
1136 (rmail-set-attribute rmail-unseen-attr-index t
1137 rmail-current-message)
1138 (save-excursion
1139 (goto-char rmail-summary-put-back-unseen)
1140 (rmail-summary-mark-seen rmail-current-message t t)))
537ab246
BG
1141 ;; Arrange to do that later, for the new current message,
1142 ;; if it still has `unseen'.
1143 (setq rmail-summary-put-back-unseen
36aecf22
GM
1144 (if (rmail-message-unseen-p msg-num)
1145 (point))))
537ab246 1146 (setq rmail-summary-put-back-unseen nil))
537ab246
BG
1147 ;; Go to the desired message.
1148 (setq rmail-current-message msg-num)
537ab246 1149 ;; Update the summary to show the message has been seen.
97e92ffb 1150 (rmail-summary-mark-seen msg-num t)
537ab246
BG
1151 (if window
1152 ;; Using save-window-excursion would cause the new value
1153 ;; of point to get lost.
1154 (unwind-protect
1155 (progn
1156 (select-window window)
bc04f207 1157 (rmail-show-message msg-num t))
537ab246
BG
1158 (select-window owin))
1159 (if (buffer-name rmail-buffer)
937e6a56 1160 (with-current-buffer rmail-buffer
b42b2189
CY
1161 (rmail-show-message msg-num t))))
1162 ;; In linum mode, the message buffer must be specially
1163 ;; updated (Bug#4878).
1164 (and (fboundp 'linum-update)
1165 (buffer-name rmail-buffer)
1166 (linum-update rmail-buffer))))
537ab246
BG
1167 (rmail-summary-update-highlight nil)))))
1168
1169(defun rmail-summary-save-buffer ()
1170 "Save the buffer associated with this RMAIL summary."
1171 (interactive)
1172 (save-window-excursion
1173 (save-excursion
1174 (switch-to-buffer rmail-buffer)
1175 (save-buffer))))
537ab246
BG
1176\f
1177(defun rmail-summary-mouse-goto-message (event)
1178 "Select the message whose summary line you click on."
1179 (interactive "@e")
1180 (goto-char (posn-point (event-end event)))
1181 (rmail-summary-goto-msg))
1182
1183(defun rmail-summary-goto-msg (&optional n nowarn skip-rmail)
1184 "Go to message N in the summary buffer and the Rmail buffer.
1185If N is nil, use the message corresponding to point in the summary
1186and move to that message in the Rmail buffer.
1187
1188If NOWARN, don't say anything if N is out of range.
8fbbdd44
GM
1189If SKIP-RMAIL, don't do anything to the Rmail buffer.
1190Returns non-nil if message N was found."
537ab246
BG
1191 (interactive "P")
1192 (if (consp n) (setq n (prefix-numeric-value n)))
1193 (if (eobp) (forward-line -1))
1194 (beginning-of-line)
1195 (let* ((obuf (current-buffer))
1196 (buf rmail-buffer)
1197 (cur (point))
1198 message-not-found
1199 (curmsg (string-to-number
1200 (buffer-substring (point)
1201 (min (point-max) (+ 6 (point))))))
937e6a56 1202 (total (with-current-buffer buf rmail-total-messages)))
537ab246
BG
1203 ;; If message number N was specified, find that message's line
1204 ;; or set message-not-found.
1205 ;; If N wasn't specified or that message can't be found.
1206 ;; set N by default.
1207 (if (not n)
1208 (setq n curmsg)
1209 (if (< n 1)
1210 (progn (message "No preceding message")
1211 (setq n 1)))
1212 (if (and (> n total)
1213 (> total 0))
1214 (progn (message "No following message")
1215 (goto-char (point-max))
1216 (rmail-summary-goto-msg nil nowarn skip-rmail)))
1217 (goto-char (point-min))
1218 (if (not (re-search-forward (format "^%5d[^0-9]" n) nil t))
1219 (progn (or nowarn (message "Message %d not found" n))
1220 (setq n curmsg)
1221 (setq message-not-found t)
1222 (goto-char cur))))
97e92ffb 1223 (rmail-summary-mark-seen n)
537ab246
BG
1224 (rmail-summary-update-highlight message-not-found)
1225 (beginning-of-line)
97e92ffb 1226 (unless skip-rmail
537ab246
BG
1227 (let ((selwin (selected-window)))
1228 (unwind-protect
60f2013c 1229 (progn (rmail-pop-to-buffer buf)
bc04f207 1230 (rmail-show-message n))
537ab246
BG
1231 (select-window selwin)
1232 ;; The actions above can alter the current buffer. Preserve it.
8fbbdd44
GM
1233 (set-buffer obuf))))
1234 (not message-not-found)))
537ab246
BG
1235
1236;; Update the highlighted line in an rmail summary buffer.
1237;; That should be current. We highlight the line point is on.
1238;; If NOT-FOUND is non-nil, we turn off highlighting.
1239(defun rmail-summary-update-highlight (not-found)
1240 ;; Make sure we have an overlay to use.
1241 (or rmail-summary-overlay
1242 (progn
1243 (make-local-variable 'rmail-summary-overlay)
8fbbdd44
GM
1244 (setq rmail-summary-overlay (make-overlay (point) (point)))
1245 (overlay-put rmail-summary-overlay 'rmail-summary t)))
537ab246
BG
1246 ;; If this message is in the summary, use the overlay to highlight it.
1247 ;; Otherwise, don't highlight anything.
1248 (if not-found
1249 (overlay-put rmail-summary-overlay 'face nil)
1250 (move-overlay rmail-summary-overlay
1251 (save-excursion (beginning-of-line)
1252 (skip-chars-forward " ")
1253 (point))
8fbbdd44 1254 (line-end-position))
537ab246
BG
1255 (overlay-put rmail-summary-overlay 'face 'highlight)))
1256\f
1257(defun rmail-summary-scroll-msg-up (&optional dist)
1258 "Scroll the Rmail window forward.
1259If the Rmail window is displaying the end of a message,
1260advance to the next message."
1261 (interactive "P")
1262 (if (eq dist '-)
1263 (rmail-summary-scroll-msg-down nil)
1264 (let ((rmail-buffer-window (get-buffer-window rmail-buffer)))
1265 (if rmail-buffer-window
1266 (if (let ((rmail-summary-window (selected-window)))
1267 (select-window rmail-buffer-window)
1268 (prog1
1269 ;; Is EOB visible in the buffer?
1270 (save-excursion
1271 (let ((ht (window-height (selected-window))))
1272 (move-to-window-line (- ht 2))
1273 (end-of-line)
1274 (eobp)))
1275 (select-window rmail-summary-window)))
1276 (if (not rmail-summary-scroll-between-messages)
1277 (error "End of buffer")
1278 (rmail-summary-next-msg (or dist 1)))
1279 (let ((other-window-scroll-buffer rmail-buffer))
1280 (scroll-other-window dist)))
1281 ;; If it isn't visible at all, show the beginning.
1282 (rmail-summary-beginning-of-message)))))
1283
1284(defun rmail-summary-scroll-msg-down (&optional dist)
1285 "Scroll the Rmail window backward.
1286If the Rmail window is now displaying the beginning of a message,
1287move to the previous message."
1288 (interactive "P")
1289 (if (eq dist '-)
1290 (rmail-summary-scroll-msg-up nil)
1291 (let ((rmail-buffer-window (get-buffer-window rmail-buffer)))
1292 (if rmail-buffer-window
1293 (if (let ((rmail-summary-window (selected-window)))
1294 (select-window rmail-buffer-window)
1295 (prog1
1296 ;; Is BOB visible in the buffer?
1297 (save-excursion
1298 (move-to-window-line 0)
1299 (beginning-of-line)
1300 (bobp))
1301 (select-window rmail-summary-window)))
1302 (if (not rmail-summary-scroll-between-messages)
1303 (error "Beginning of buffer")
1304 (rmail-summary-previous-msg (or dist 1)))
1305 (let ((other-window-scroll-buffer rmail-buffer))
1306 (scroll-other-window-down dist)))
1307 ;; If it isn't visible at all, show the beginning.
1308 (rmail-summary-beginning-of-message)))))
1309
1310(defun rmail-summary-beginning-of-message ()
1311 "Show current message from the beginning."
1312 (interactive)
1313 (rmail-summary-show-message 'BEG))
1314
1315(defun rmail-summary-end-of-message ()
1316 "Show bottom of current message."
1317 (interactive)
1318 (rmail-summary-show-message 'END))
1319
1320(defun rmail-summary-show-message (where)
1321 "Show current mail message.
1322Position it according to WHERE which can be BEG or END"
1323 (if (and (one-window-p) (not pop-up-frames))
1324 ;; If there is just one window, put the summary on the top.
1325 (let ((buffer rmail-buffer))
1326 (split-window (selected-window) rmail-summary-window-size)
1327 (select-window (frame-first-window))
60f2013c 1328 (rmail-pop-to-buffer rmail-buffer)
537ab246
BG
1329 ;; If pop-to-buffer did not use that window, delete that
1330 ;; window. (This can happen if it uses another frame.)
1331 (or (eq buffer (window-buffer (next-window (frame-first-window))))
1332 (delete-other-windows)))
60f2013c 1333 (rmail-pop-to-buffer rmail-buffer))
537ab246
BG
1334 (cond
1335 ((eq where 'BEG)
1336 (goto-char (point-min))
1337 (search-forward "\n\n"))
1338 ((eq where 'END)
1339 (goto-char (point-max))
1340 (recenter (1- (window-height))))
1341 )
60f2013c 1342 (rmail-pop-to-buffer rmail-summary-buffer))
537ab246
BG
1343
1344(defun rmail-summary-bury ()
1345 "Bury the Rmail buffer and the Rmail summary buffer."
1346 (interactive)
1347 (let ((buffer-to-bury (current-buffer)))
1348 (let (window)
1349 (while (setq window (get-buffer-window rmail-buffer))
1350 (set-window-buffer window (other-buffer rmail-buffer)))
1351 (bury-buffer rmail-buffer))
1352 (switch-to-buffer (other-buffer buffer-to-bury))
1353 (bury-buffer buffer-to-bury)))
1354
1355(defun rmail-summary-quit ()
1356 "Quit out of Rmail and Rmail summary."
1357 (interactive)
1358 (rmail-summary-wipe)
1359 (rmail-quit))
1360
1361(defun rmail-summary-wipe ()
1362 "Kill and wipe away Rmail summary, remaining within Rmail."
1363 (interactive)
937e6a56 1364 (with-current-buffer rmail-buffer (setq rmail-summary-buffer nil))
537ab246
BG
1365 (let ((local-rmail-buffer rmail-buffer))
1366 (kill-buffer (current-buffer))
1367 ;; Delete window if not only one.
1368 (if (not (eq (selected-window) (next-window nil 'no-minibuf)))
1369 (delete-window))
1370 ;; Switch windows to the rmail buffer, or switch to it in this window.
60f2013c 1371 (rmail-pop-to-buffer local-rmail-buffer)))
537ab246
BG
1372
1373(defun rmail-summary-expunge ()
1374 "Actually erase all deleted messages and recompute summary headers."
1375 (interactive)
937e6a56 1376 (with-current-buffer rmail-buffer
537ab246
BG
1377 (when (rmail-expunge-confirmed)
1378 (rmail-only-expunge)))
1379 (rmail-update-summary))
1380
1381(defun rmail-summary-expunge-and-save ()
1382 "Expunge and save RMAIL file."
1383 (interactive)
1384 (save-excursion
1385 (rmail-expunge-and-save))
1386 (rmail-update-summary)
1387 (set-buffer-modified-p nil))
1388
1389(defun rmail-summary-get-new-mail (&optional file-name)
1390 "Get new mail and recompute summary headers.
1391
1392Optionally you can specify the file to get new mail from. In this case,
1393the file of new mail is not changed or deleted. Noninteractively, you can
1394pass the inbox file name as an argument. Interactively, a prefix
1395argument says to read a file name and use that file as the inbox."
1396 (interactive
1397 (list (if current-prefix-arg
1398 (read-file-name "Get new mail from file: "))))
1399 (let (msg)
937e6a56 1400 (with-current-buffer rmail-buffer
537ab246
BG
1401 (rmail-get-new-mail file-name)
1402 ;; Get the proper new message number.
1403 (setq msg rmail-current-message))
1404 ;; Make sure that message is displayed.
1405 (or (zerop msg)
1406 (rmail-summary-goto-msg msg))))
1407
1408(defun rmail-summary-input (filename)
1409 "Run Rmail on file FILENAME."
1410 (interactive "FRun rmail on RMAIL file: ")
1411 ;; We switch windows here, then display the other Rmail file there.
60f2013c 1412 (rmail-pop-to-buffer rmail-buffer)
537ab246
BG
1413 (rmail filename))
1414
1415(defun rmail-summary-first-message ()
1416 "Show first message in Rmail file from summary buffer."
1417 (interactive)
1418 (with-no-warnings
1419 (beginning-of-buffer)))
1420
1421(defun rmail-summary-last-message ()
1422 "Show last message in Rmail file from summary buffer."
1423 (interactive)
1424 (with-no-warnings
1425 (end-of-buffer))
1426 (forward-line -1))
1427
1428(declare-function rmail-abort-edit "rmailedit" ())
1429(declare-function rmail-cease-edit "rmailedit"())
1430(declare-function rmail-set-label "rmailkwd" (l state &optional n))
1431(declare-function rmail-output-read-file-name "rmailout" ())
1432(declare-function mail-send-and-exit "sendmail" (&optional arg))
1433
1434(defvar rmail-summary-edit-map nil)
1435(if rmail-summary-edit-map
1436 nil
1437 (setq rmail-summary-edit-map
1438 (nconc (make-sparse-keymap) text-mode-map))
1439 (define-key rmail-summary-edit-map "\C-c\C-c" 'rmail-cease-edit)
1440 (define-key rmail-summary-edit-map "\C-c\C-]" 'rmail-abort-edit))
1441
1442(defun rmail-summary-edit-current-message ()
1443 "Edit the contents of this message."
1444 (interactive)
60f2013c 1445 (rmail-pop-to-buffer rmail-buffer)
537ab246
BG
1446 (rmail-edit-current-message)
1447 (use-local-map rmail-summary-edit-map))
1448
1449(defun rmail-summary-cease-edit ()
1450 "Finish editing message, then go back to Rmail summary buffer."
1451 (interactive)
1452 (rmail-cease-edit)
60f2013c 1453 (rmail-pop-to-buffer rmail-summary-buffer))
537ab246
BG
1454
1455(defun rmail-summary-abort-edit ()
1456 "Abort edit of current message; restore original contents.
1457Go back to summary buffer."
1458 (interactive)
1459 (rmail-abort-edit)
60f2013c 1460 (rmail-pop-to-buffer rmail-summary-buffer))
537ab246
BG
1461
1462(defun rmail-summary-search-backward (regexp &optional n)
1463 "Show message containing next match for REGEXP.
1464Prefix argument gives repeat count; negative argument means search
1465backwards (through earlier messages).
1466Interactively, empty argument means use same regexp used last time."
1467 (interactive
1468 (let* ((reversep (>= (prefix-numeric-value current-prefix-arg) 0))
1469 (prompt
1470 (concat (if reversep "Reverse " "") "Rmail search (regexp"))
1471 regexp)
1472 (setq prompt
1473 (concat prompt
1474 (if rmail-search-last-regexp
1475 (concat ", default "
1476 rmail-search-last-regexp "): ")
1477 "): ")))
1478 (setq regexp (read-string prompt))
1479 (cond ((not (equal regexp ""))
1480 (setq rmail-search-last-regexp regexp))
1481 ((not rmail-search-last-regexp)
1482 (error "No previous Rmail search string")))
1483 (list rmail-search-last-regexp
1484 (prefix-numeric-value current-prefix-arg))))
1485 ;; Don't use save-excursion because that prevents point from moving
1486 ;; properly in the summary buffer.
937e6a56
SM
1487 (with-current-buffer rmail-buffer
1488 (rmail-search regexp (- n))))
537ab246
BG
1489
1490(defun rmail-summary-search (regexp &optional n)
1491 "Show message containing next match for REGEXP.
1492Prefix argument gives repeat count; negative argument means search
1493backwards (through earlier messages).
1494Interactively, empty argument means use same regexp used last time."
1495 (interactive
1496 (let* ((reversep (< (prefix-numeric-value current-prefix-arg) 0))
1497 (prompt
1498 (concat (if reversep "Reverse " "") "Rmail search (regexp"))
1499 regexp)
1500 (setq prompt
1501 (concat prompt
1502 (if rmail-search-last-regexp
1503 (concat ", default "
1504 rmail-search-last-regexp "): ")
1505 "): ")))
1506 (setq regexp (read-string prompt))
1507 (cond ((not (equal regexp ""))
1508 (setq rmail-search-last-regexp regexp))
1509 ((not rmail-search-last-regexp)
1510 (error "No previous Rmail search string")))
1511 (list rmail-search-last-regexp
1512 (prefix-numeric-value current-prefix-arg))))
1513 ;; Don't use save-excursion because that prevents point from moving
1514 ;; properly in the summary buffer.
a5110f66
GM
1515 (let ((buffer (current-buffer))
1516 (selwin (selected-window)))
537ab246
BG
1517 (unwind-protect
1518 (progn
60f2013c 1519 (rmail-pop-to-buffer rmail-buffer)
537ab246 1520 (rmail-search regexp n))
a5110f66 1521 (select-window selwin)
537ab246
BG
1522 (set-buffer buffer))))
1523
1524(defun rmail-summary-toggle-header ()
1525 "Show original message header if pruned header currently shown, or vice versa."
1526 (interactive)
1527 (save-window-excursion
1528 (set-buffer rmail-buffer)
1529 (rmail-toggle-header))
1530 ;; Inside save-excursion, some changes to point in the RMAIL buffer are lost.
1531 ;; Set point to point-min in the RMAIL buffer, if it is visible.
1532 (let ((window (get-buffer-window rmail-buffer)))
1533 (if window
1534 ;; Using save-window-excursion would lose the new value of point.
1535 (let ((owin (selected-window)))
1536 (unwind-protect
1537 (progn
1538 (select-window window)
1539 (goto-char (point-min)))
1540 (select-window owin))))))
1541
1542
1543(defun rmail-summary-add-label (label)
1544 "Add LABEL to labels associated with current Rmail message.
1545Completion is performed over known labels when reading."
937e6a56 1546 (interactive (list (with-current-buffer rmail-buffer
537ab246 1547 (rmail-read-label "Add label"))))
937e6a56 1548 (with-current-buffer rmail-buffer
537ab246
BG
1549 (rmail-add-label label)))
1550
1551(defun rmail-summary-kill-label (label)
1552 "Remove LABEL from labels associated with current Rmail message.
1553Completion is performed over known labels when reading."
937e6a56 1554 (interactive (list (with-current-buffer rmail-buffer
537ab246 1555 (rmail-read-label "Kill label"))))
937e6a56 1556 (with-current-buffer rmail-buffer
537ab246
BG
1557 (rmail-set-label label nil)))
1558\f
1559;;;; *** Rmail Summary Mailing Commands ***
1560
1561(defun rmail-summary-override-mail-send-and-exit ()
1562 "Replace bindings to `mail-send-and-exit' with `rmail-summary-send-and-exit'."
1563 (use-local-map (copy-keymap (current-local-map)))
1564 (dolist (key (where-is-internal 'mail-send-and-exit))
1565 (define-key (current-local-map) key 'rmail-summary-send-and-exit)))
1566
1567(defun rmail-summary-mail ()
1568 "Send mail in another window.
1569While composing the message, use \\[mail-yank-original] to yank the
1570original message into it."
1571 (interactive)
1572 (let ((window (get-buffer-window rmail-buffer)))
1573 (if window
1574 (select-window window)
1575 (set-buffer rmail-buffer)))
1576 (rmail-start-mail nil nil nil nil nil (current-buffer))
1577 (rmail-summary-override-mail-send-and-exit))
1578
1579(defun rmail-summary-continue ()
1580 "Continue composing outgoing message previously being composed."
1581 (interactive)
1582 (let ((window (get-buffer-window rmail-buffer)))
1583 (if window
1584 (select-window window)
1585 (set-buffer rmail-buffer)))
1586 (rmail-start-mail t))
1587
1588(defun rmail-summary-reply (just-sender)
1589 "Reply to the current message.
1590Normally include CC: to all other recipients of original message;
1591prefix argument means ignore them. While composing the reply,
1592use \\[mail-yank-original] to yank the original message into it."
1593 (interactive "P")
1594 (let ((window (get-buffer-window rmail-buffer)))
1595 (if window
1596 (select-window window)
1597 (set-buffer rmail-buffer)))
1598 (rmail-reply just-sender)
1599 (rmail-summary-override-mail-send-and-exit))
1600
1601(defun rmail-summary-retry-failure ()
1602 "Edit a mail message which is based on the contents of the current message.
1603For a message rejected by the mail system, extract the interesting headers and
1604the body of the original message; otherwise copy the current message."
1605 (interactive)
1606 (let ((window (get-buffer-window rmail-buffer)))
1607 (if window
1608 (select-window window)
1609 (set-buffer rmail-buffer)))
1610 (rmail-retry-failure)
1611 (rmail-summary-override-mail-send-and-exit))
1612
1613(defun rmail-summary-send-and-exit ()
1614 "Send mail reply and return to summary buffer."
1615 (interactive)
1616 (mail-send-and-exit t))
1617
1618(defun rmail-summary-forward (resend)
1619 "Forward the current message to another user.
1620With prefix argument, \"resend\" the message instead of forwarding it;
1621see the documentation of `rmail-resend'."
1622 (interactive "P")
1623 (save-excursion
1624 (let ((window (get-buffer-window rmail-buffer)))
1625 (if window
1626 (select-window window)
1627 (set-buffer rmail-buffer)))
1628 (rmail-forward resend)
1629 (rmail-summary-override-mail-send-and-exit)))
1630
1631(defun rmail-summary-resend ()
1632 "Resend current message using `rmail-resend'."
1633 (interactive)
1634 (save-excursion
1635 (let ((window (get-buffer-window rmail-buffer)))
1636 (if window
1637 (select-window window)
1638 (set-buffer rmail-buffer)))
1639 (call-interactively 'rmail-resend)))
1640\f
1641;; Summary output commands.
1642
1643(defun rmail-summary-output (&optional file-name n)
1644 "Append this message to mail file FILE-NAME.
1645This works with both mbox format and Babyl format files,
1646outputting in the appropriate format for each.
1647The default file name comes from `rmail-default-file',
1648which is updated to the name you use in this command.
1649
1650A prefix argument N says to output that many consecutive messages
1651from those in the summary, starting with the current one.
1652Deleted messages are skipped and don't count.
1653When called from Lisp code, N may be omitted and defaults to 1.
1654
1655This command always outputs the complete message header,
1656even the header display is currently pruned."
1657 (interactive
1658 (progn (require 'rmailout)
1659 (list (rmail-output-read-file-name)
1660 (prefix-numeric-value current-prefix-arg))))
1661 (let ((i 0) prev-msg)
1662 (while
1663 (and (< i n)
1664 (progn (rmail-summary-goto-msg)
1665 (not (eq prev-msg
1666 (setq prev-msg
1667 (with-current-buffer rmail-buffer
1668 rmail-current-message))))))
1669 (setq i (1+ i))
1670 (with-current-buffer rmail-buffer
1671 (let ((rmail-delete-after-output nil))
1672 (rmail-output file-name 1)))
1673 (if rmail-delete-after-output
1674 (rmail-summary-delete-forward nil)
1675 (if (< i n)
1676 (rmail-summary-next-msg 1))))))
1677
1678(defalias 'rmail-summary-output-to-rmail-file 'rmail-summary-output)
1679
1680(declare-function rmail-output-as-seen "rmailout"
1681 (file-name &optional count noattribute from-gnus))
1682
1683(defun rmail-summary-output-as-seen (&optional file-name n)
8f8cecb3 1684 "Append this message to mbox file named FILE-NAME.
537ab246
BG
1685A prefix argument N says to output that many consecutive messages,
1686from the summary, starting with the current one.
1687Deleted messages are skipped and don't count.
1688When called from Lisp code, N may be omitted and defaults to 1.
1689
1690This outputs the message header as you see it (or would see it)
1691displayed in Rmail.
1692
1693The default file name comes from `rmail-default-file',
1694which is updated to the name you use in this command."
1695 (interactive
1696 (progn (require 'rmailout)
1697 (list (rmail-output-read-file-name)
1698 (prefix-numeric-value current-prefix-arg))))
1699 (require 'rmailout) ; for rmail-output-as-seen in non-interactive case
1700 (let ((i 0) prev-msg)
1701 (while
1702 (and (< i n)
1703 (progn (rmail-summary-goto-msg)
1704 (not (eq prev-msg
1705 (setq prev-msg
1706 (with-current-buffer rmail-buffer
1707 rmail-current-message))))))
1708 (setq i (1+ i))
1709 (with-current-buffer rmail-buffer
1710 (let ((rmail-delete-after-output nil))
1711 (rmail-output-as-seen file-name 1)))
1712 (if rmail-delete-after-output
1713 (rmail-summary-delete-forward nil)
1714 (if (< i n)
1715 (rmail-summary-next-msg 1))))))
1716
1717(defun rmail-summary-output-menu ()
1718 "Output current message to another Rmail file, chosen with a menu.
1719Also set the default for subsequent \\[rmail-output-to-babyl-file] commands.
1720The variables `rmail-secondary-file-directory' and
1721`rmail-secondary-file-regexp' control which files are offered in the menu."
1722 (interactive)
937e6a56 1723 (with-current-buffer rmail-buffer
537ab246
BG
1724 (let ((rmail-delete-after-output nil))
1725 (call-interactively 'rmail-output-menu)))
1726 (if rmail-delete-after-output
1727 (rmail-summary-delete-forward nil)))
1728
1729(defun rmail-summary-construct-io-menu ()
1730 (let ((files (rmail-find-all-files rmail-secondary-file-directory)))
1731 (if files
1732 (progn
1733 (define-key rmail-summary-mode-map [menu-bar classify input-menu]
1734 (cons "Input Rmail File"
1735 (rmail-list-to-menu "Input Rmail File"
1736 files
1737 'rmail-summary-input)))
1738 (define-key rmail-summary-mode-map [menu-bar classify output-menu]
1739 (cons "Output Rmail File"
1740 (rmail-list-to-menu "Output Rmail File"
1741 files
1742 'rmail-summary-output))))
1743 (define-key rmail-summary-mode-map [menu-bar classify input-menu]
1744 '("Input Rmail File" . rmail-disable-menu))
1745 (define-key rmail-summary-mode-map [menu-bar classify output-menu]
1746 '("Output Rmail File" . rmail-disable-menu)))))
1747
1748(defun rmail-summary-output-body (&optional file-name)
1749 "Write this message body to the file FILE-NAME.
1750FILE-NAME defaults, interactively, from the Subject field of the message."
1751 (interactive)
937e6a56 1752 (with-current-buffer rmail-buffer
537ab246
BG
1753 (let ((rmail-delete-after-output nil))
1754 (if file-name
1755 (rmail-output-body-to-file file-name)
1756 (call-interactively 'rmail-output-body-to-file))))
1757 (if rmail-delete-after-output
1758 (rmail-summary-delete-forward nil)))
1759\f
1760;; Sorting messages in Rmail Summary buffer.
1761
1762(defun rmail-summary-sort-by-date (reverse)
11d6c061
GM
1763 "Sort messages of current Rmail summary by \"Date\" header.
1764If prefix argument REVERSE is non-nil, sorts in reverse order."
537ab246
BG
1765 (interactive "P")
1766 (rmail-sort-from-summary (function rmail-sort-by-date) reverse))
1767
1768(defun rmail-summary-sort-by-subject (reverse)
11d6c061
GM
1769 "Sort messages of current Rmail summary by \"Subject\" header.
1770Ignores any \"Re: \" prefix. If prefix argument REVERSE is
1771non-nil, sorts in reverse order."
537ab246
BG
1772 (interactive "P")
1773 (rmail-sort-from-summary (function rmail-sort-by-subject) reverse))
1774
1775(defun rmail-summary-sort-by-author (reverse)
1776 "Sort messages of current Rmail summary by author.
11d6c061
GM
1777This uses either the \"From\" or \"Sender\" header, downcased.
1778If prefix argument REVERSE is non-nil, sorts in reverse order."
537ab246
BG
1779 (interactive "P")
1780 (rmail-sort-from-summary (function rmail-sort-by-author) reverse))
1781
1782(defun rmail-summary-sort-by-recipient (reverse)
1783 "Sort messages of current Rmail summary by recipient.
11d6c061
GM
1784This uses either the \"To\" or \"Apparently-To\" header, downcased.
1785If prefix argument REVERSE is non-nil, sorts in reverse order."
537ab246
BG
1786 (interactive "P")
1787 (rmail-sort-from-summary (function rmail-sort-by-recipient) reverse))
1788
1789(defun rmail-summary-sort-by-correspondent (reverse)
1790 "Sort messages of current Rmail summary by other correspondent.
11d6c061
GM
1791This uses either the \"From\", \"Sender\", \"To\", or
1792\"Apparently-To\" header, downcased. Uses the first header not
38a71655 1793excluded by `mail-dont-reply-to-names'. If prefix argument
11d6c061 1794REVERSE is non-nil, sorts in reverse order."
537ab246
BG
1795 (interactive "P")
1796 (rmail-sort-from-summary (function rmail-sort-by-correspondent) reverse))
1797
1798(defun rmail-summary-sort-by-lines (reverse)
11d6c061
GM
1799 "Sort messages of current Rmail summary by the number of lines.
1800If prefix argument REVERSE is non-nil, sorts in reverse order."
537ab246
BG
1801 (interactive "P")
1802 (rmail-sort-from-summary (function rmail-sort-by-lines) reverse))
1803
1804(defun rmail-summary-sort-by-labels (reverse labels)
1805 "Sort messages of current Rmail summary by labels.
11d6c061
GM
1806LABELS is a comma-separated list of labels.
1807If prefix argument REVERSE is non-nil, sorts in reverse order."
537ab246
BG
1808 (interactive "P\nsSort by labels: ")
1809 (rmail-sort-from-summary
11d6c061 1810 (lambda (reverse) (rmail-sort-by-labels reverse labels))
537ab246
BG
1811 reverse))
1812
1813(defun rmail-sort-from-summary (sortfun reverse)
11d6c061
GM
1814 "Sort the Rmail buffer using sorting function SORTFUN.
1815Passes REVERSE to SORTFUN as its sole argument. Then regenerates
1816the summary. Note that the whole Rmail buffer is sorted, even if
1817the summary is only showing a subset of messages."
537ab246
BG
1818 (require 'rmailsort)
1819 (let ((selwin (selected-window)))
1820 (unwind-protect
60f2013c 1821 (progn (rmail-pop-to-buffer rmail-buffer)
537ab246
BG
1822 (funcall sortfun reverse))
1823 (select-window selwin))))
1824
1825(provide 'rmailsum)
1826
a5e92116
GM
1827;; Local Variables:
1828;; generated-autoload-file: "rmail.el"
1829;; End:
1830
537ab246 1831;;; rmailsum.el ends here