]> code.delx.au - gnu-emacs/blob - lisp/mail/rmailsum.el
5c147be310405a962396ed94cd4dac6c07e13c00
[gnu-emacs] / lisp / mail / rmailsum.el
1 ;;; rmailsum.el --- make summary buffers for the mail reader
2
3 ;; Copyright (C) 1985, 1993-1996, 2000-2011 Free Software Foundation, Inc.
4
5 ;; Maintainer: FSF
6 ;; Keywords: mail
7 ;; Package: rmail
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
32 ;; For rmail-select-summary.
33 (require 'rmail)
34 (require 'rfc2047)
35
36 (defcustom rmail-summary-scroll-between-messages t
37 "Non-nil means Rmail summary scroll commands move between messages.
38 That is, after `rmail-summary-scroll-msg-up' reaches the end of a
39 message, it moves to the next message; and similarly for
40 `rmail-summary-scroll-msg-down'."
41 :type 'boolean
42 :group 'rmail-summary)
43
44 ;; FIXME could do with a :set function that regenerates the summary
45 ;; and updates rmail-summary-vector.
46 (defcustom rmail-summary-line-count-flag t
47 "Non-nil means Rmail summary should show the number of lines in each message.
48 Setting this option to nil might speed up the generation of summaries."
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
60 (defvar rmail-summary-redo nil
61 "(FUNCTION . ARGS) to regenerate this Rmail summary buffer.")
62
63 (defvar rmail-summary-overlay nil
64 "Overlay used to highlight the current message in the Rmail summary.")
65 (put 'rmail-summary-overlay 'permanent-local t)
66
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)
257 "Keymap used in Rmail summary mode.")
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)
271 (rmail-new-summary "All" '(rmail-summary) nil))
272
273 ;;;###autoload
274 (defun rmail-summary-by-labels (labels)
275 "Display a summary of all messages with one or more LABELS.
276 LABELS 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
285 (concat " \\("
286 (mail-comma-list-regexp labels)
287 "\\)\\(,\\|\\'\\)")))
288
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.
293 ;;;###autoload
294 (defun rmail-summary-by-recipients (recipients &optional primary-only)
295 "Display a summary of all messages with the given RECIPIENTS.
296 Normally checks the To, From and Cc fields of headers;
297 but if PRIMARY-ONLY is non-nil (prefix arg given),
298 only look in the To and From fields.
299 RECIPIENTS 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)
312 ;; mail-fetch-field does not care where it starts from.
313 (narrow-to-region (point) (progn (search-forward "\n\n") (point)))
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
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.
322 ;;;###autoload
323 (defun rmail-summary-by-regexp (regexp)
324 "Display a summary of all messages according to regexp REGEXP.
325 If 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),
327 Emacs will list the message in the summary."
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)
343 ;; Search functions can expect to start from the beginning.
344 (narrow-to-region (point) (save-excursion (search-forward "\n\n") (point)))
345 (if (and rmail-enable-mime
346 rmail-search-mime-header-function)
347 (funcall rmail-search-mime-header-function msg regexp (point))
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.
353 Normally checks just the Subject field of headers; but with prefix
354 argument WHOLE-MESSAGE is non-nil, looks in the whole message.
355 SUBJECT is a string of regexps separated by commas."
356 (interactive
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)))
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)
377 "Display a summary of all messages whose \"From\" field matches SENDERS.
378 SENDERS is a string of regexps separated by commas."
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.
397 DESC makes part of the mode line of the summary buffer. REDO is form ...
398 For each message, FUNC is applied to the message number and ARGS...
399 and if the result is non-nil, that message is included.
400 nil for FUNCTION means all messages."
401 (message "Computing summary lines...")
402 (unless rmail-buffer
403 (error "No RMAIL buffer found"))
404 (let (mesg was-in-summary sumbuf)
405 (if (eq major-mode 'rmail-summary-mode)
406 (setq was-in-summary t))
407 (with-current-buffer rmail-buffer
408 (setq rmail-summary-buffer (rmail-new-summary-1 desc redo func args)
409 ;; r-s-b is buffer-local.
410 sumbuf rmail-summary-buffer
411 mesg rmail-current-message))
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)))
421 (rmail-pop-to-buffer sumbuf)
422 ;; If pop-to-buffer did not use that window, delete that
423 ;; window. (This can happen if it uses another frame.)
424 (if (not (eq sumbuf (window-buffer (frame-first-window))))
425 (delete-other-windows)))
426 (rmail-pop-to-buffer sumbuf))
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.
438 DESCRIPTION is added to the mode line.
439
440 Return the summary buffer by invoking FUNCTION on each message
441 passing the message number and ARGS...
442
443 REDO is a form ...
444
445 The current buffer must be a Rmail buffer either containing a
446 collection of mbox formatted messages or displaying a single
447 message."
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.
481 (if (and (not (zerop rmail-new-summary-line-count))
482 (zerop (% rmail-new-summary-line-count 10)))
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)))))
487 ;; Temporarily, while summary buffer is unfinished,
488 ;; we "don't have" a summary.
489 (setq rmail-summary-buffer nil)
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
493 (if rmail-enable-mime
494 (with-current-buffer rmail-buffer
495 (setq rmail-summary-buffer nil)))
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 ()
518 "Return the Rmail summary buffer.
519 If necessary, it is created and undo is disabled."
520 (if (and rmail-summary-buffer (buffer-name rmail-summary-buffer))
521 rmail-summary-buffer
522 (let ((buff (generate-new-buffer (concat (buffer-name) "-summary"))))
523 (with-current-buffer buff
524 (setq buffer-undo-list t))
525 buff)))
526
527 \f
528 ;; Low levels of generating a summary.
529
530 (defun rmail-get-summary (msgnum)
531 "Return the summary line for message MSGNUM.
532 The mbox buffer must be current when you call this function
533 even if its text is swapped.
534
535 If the message has a summary line already, it will be stored in
536 the message as a header and simply returned, otherwise the
537 summary line is created, saved in the message header, cached and
538 returned.
539
540 The 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
550 (defcustom rmail-summary-line-decoder (function rfc2047-decode-string)
551 "Function to decode a Rmail summary line.
552 It receives the summary line for one message as a string
553 and should return the decoded string.
554
555 By default, it is `rfc2047-decode-string', which decodes MIME-encoded
556 subject."
557 :type 'function
558 :version "23.3"
559 :group 'rmail-summary)
560
561 (defun rmail-create-summary-line (msgnum)
562 "Return the summary line for message MSGNUM.
563 Obtain the message summary from the header if it is available
564 otherwise create it and store it in the message header.
565
566 The mbox buffer must be current when you call this function
567 even if its text is swapped."
568 (let ((beg (rmail-msgbeg msgnum))
569 (end (rmail-msgend msgnum))
570 (deleted (rmail-message-deleted-p msgnum))
571 ;; Does not work (swapped?)
572 ;;; (unseen (rmail-message-unseen-p msgnum))
573 unseen lines)
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)))
581 ;; Narrow to the message header.
582 (save-excursion
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)))))))
597
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.
607
608 The current buffer must already be narrowed to the message headers for
609 the 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
628 (autoload 'rmail-make-label "rmailkwd")
629
630 (defun rmail-get-summary-labels ()
631 "Return a string wrapped in curly braces with the current message labels.
632 Returns nil if there are no labels. The current buffer must
633 already be narrowed to the message headers for the message being
634 processed."
635 (let ((labels (mail-fetch-field rmail-keyword-header)))
636 (and labels
637 (not (string-equal labels ""))
638 (progn
639 ;; Intern so that rmail-read-label can offer completion.
640 (mapc 'rmail-make-label (split-string labels ", "))
641 (format "{ %s } " labels)))))
642
643 (defun rmail-create-summary (msgnum deleted unseen lines)
644 "Return the summary line for message MSGNUM.
645 The current buffer should already be narrowed to the header for that message.
646 It could be either buffer, so don't access Rmail local variables.
647 DELETED is t if this message is marked deleted.
648 UNSEEN is t if it is marked unseen.
649 LINES 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 ? ))
670 prefix (format "%5d%c " msgnum status)
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
677 (defun rmail-header-summary ()
678 "Return a message summary based on the message headers.
679 The value is a list of two strings, the first and second parts of the summary.
680
681 The current buffer must already be narrowed to the message headers for
682 the message being processed."
683 (goto-char (point-min))
684 (list
685 (concat (save-excursion
686 (if (not (re-search-forward "^Date:" nil t))
687 " "
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))
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))))
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))
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]\\)"
708 (line-end-position) t)
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 ;; We are going to return only 25 characters of the
766 ;; address, so make sure it is RFC2047 decoded before
767 ;; taking its substring. This is important when the address is not on the same line as the name, e.g.:
768 ;; To: =?UTF-8?Q?=C5=A0t=C4=9Bp=C3=A1n_?= =?UTF-8?Q?N=C4=9Bmec?=
769 ;; <stepnem@gmail.com>
770 (setq from (rfc2047-decode-string from))
771 (setq len (length from))
772 (setq mch (string-match "[@%]" from))
773 (format "%25s"
774 (if (or (not mch) (<= len 25))
775 (substring from (max 0 (- len 25)))
776 (substring from
777 (setq lo (cond ((< (- mch 14) 0) 0)
778 ((< len (+ mch 11))
779 (- len 25))
780 (t (- mch 14))))
781 (min len (+ lo 25)))))))))
782 (concat (if (re-search-forward "^Subject:" nil t)
783 (let (pos str)
784 (skip-chars-forward " \t")
785 (setq pos (point))
786 (forward-line 1)
787 (setq str (buffer-substring pos (1- (point))))
788 (while (looking-at "\\s ")
789 (setq str (concat str " "
790 (buffer-substring (match-end 0)
791 (line-end-position))))
792 (forward-line 1))
793 str)
794 (re-search-forward "[\n][\n]+" nil t)
795 (buffer-substring (point) (progn (end-of-line) (point))))
796 "\n")))
797 \f
798 ;; Simple motion in a summary buffer.
799
800 (defun rmail-summary-next-all (&optional number)
801 (interactive "p")
802 (forward-line (if number number 1))
803 ;; It doesn't look nice to move forward past the last message line.
804 (and (eobp) (> number 0)
805 (forward-line -1))
806 (display-buffer rmail-buffer))
807
808 (defun rmail-summary-previous-all (&optional number)
809 (interactive "p")
810 (forward-line (- (if number number 1)))
811 ;; It doesn't look nice to move forward past the last message line.
812 (and (eobp) (< number 0)
813 (forward-line -1))
814 (display-buffer rmail-buffer))
815
816 (defun rmail-summary-next-msg (&optional number)
817 "Display next non-deleted msg from rmail file.
818 With optional prefix argument NUMBER, moves forward this number of non-deleted
819 messages, or backward if NUMBER is negative."
820 (interactive "p")
821 (forward-line 0)
822 (and (> number 0) (end-of-line))
823 (let ((count (if (< number 0) (- number) number))
824 (search (if (> number 0) 're-search-forward 're-search-backward))
825 (non-del-msg-found nil))
826 (while (and (> count 0) (setq non-del-msg-found
827 (or (funcall search "^.....[^D]" nil t)
828 non-del-msg-found)))
829 (setq count (1- count))))
830 (beginning-of-line)
831 (display-buffer rmail-buffer))
832
833 (defun rmail-summary-previous-msg (&optional number)
834 "Display previous non-deleted msg from rmail file.
835 With optional prefix argument NUMBER, moves backward this number of
836 non-deleted messages."
837 (interactive "p")
838 (rmail-summary-next-msg (- (if number number 1))))
839
840 (defun rmail-summary-next-labeled-message (n labels)
841 "Show next message with LABELS. Defaults to last labels used.
842 With prefix argument N moves forward N messages with these labels."
843 (interactive "p\nsMove to next msg with labels: ")
844 (let (msg)
845 (with-current-buffer rmail-buffer
846 (rmail-next-labeled-message n labels)
847 (setq msg rmail-current-message))
848 (rmail-summary-goto-msg msg)))
849
850 (defun rmail-summary-previous-labeled-message (n labels)
851 "Show previous message with LABELS. Defaults to last labels used.
852 With prefix argument N moves backward N messages with these labels."
853 (interactive "p\nsMove to previous msg with labels: ")
854 (let (msg)
855 (with-current-buffer rmail-buffer
856 (rmail-previous-labeled-message n labels)
857 (setq msg rmail-current-message))
858 (rmail-summary-goto-msg msg)))
859
860 (defun rmail-summary-next-same-subject (n)
861 "Go to the next message in the summary having the same subject.
862 With prefix argument N, do this N times.
863 If N is negative, go backwards."
864 (interactive "p")
865 (let ((forward (> n 0))
866 subject i found)
867 (with-current-buffer rmail-buffer
868 (setq subject (rmail-simplified-subject)
869 i rmail-current-message))
870 (save-excursion
871 (while (and (/= n 0)
872 (if forward
873 (not (eobp))
874 (not (bobp))))
875 (let (done)
876 (while (and (not done)
877 (if forward
878 (not (eobp))
879 (not (bobp))))
880 ;; Advance thru summary.
881 (forward-line (if forward 1 -1))
882 ;; Get msg number of this line.
883 (setq i (string-to-number
884 (buffer-substring (point)
885 (min (point-max) (+ 6 (point))))))
886 (setq done (string-equal subject (rmail-simplified-subject i))))
887 (if done (setq found i)))
888 (setq n (if forward (1- n) (1+ n)))))
889 (if found
890 (rmail-summary-goto-msg found)
891 (error "No %s message with same subject"
892 (if forward "following" "previous")))))
893
894 (defun rmail-summary-previous-same-subject (n)
895 "Go to the previous message in the summary having the same subject.
896 With prefix argument N, do this N times.
897 If N is negative, go forwards instead."
898 (interactive "p")
899 (rmail-summary-next-same-subject (- n)))
900 \f
901 ;; Delete and undelete summary commands.
902
903 (defun rmail-summary-delete-forward (&optional count)
904 "Delete this message and move to next nondeleted one.
905 Deleted messages stay in the file until the \\[rmail-expunge] command is given.
906 A prefix argument serves as a repeat count;
907 a negative argument means to delete and move backward."
908 (interactive "p")
909 (unless (numberp count) (setq count 1))
910 (let (end del-msg
911 (backward (< count 0)))
912 (while (/= count 0)
913 (rmail-summary-goto-msg)
914 (with-current-buffer rmail-buffer
915 (rmail-delete-message)
916 (setq del-msg rmail-current-message))
917 (rmail-summary-mark-deleted del-msg)
918 (while (and (not (if backward (bobp) (eobp)))
919 (save-excursion (beginning-of-line)
920 (looking-at " *[0-9]+D")))
921 (forward-line (if backward -1 1)))
922 ;; It looks ugly to move to the empty line at end of buffer.
923 (and (eobp) (not backward)
924 (forward-line -1))
925 (setq count
926 (if (> count 0) (1- count) (1+ count))))))
927
928 (defun rmail-summary-delete-backward (&optional count)
929 "Delete this message and move to previous nondeleted one.
930 Deleted messages stay in the file until the \\[rmail-expunge] command is given.
931 A prefix argument serves as a repeat count;
932 a negative argument means to delete and move forward."
933 (interactive "p")
934 (rmail-summary-delete-forward (- count)))
935
936 (defun rmail-summary-mark-deleted (&optional n undel)
937 ;; Since third arg is t, this only alters the summary, not the Rmail buf.
938 (and n (rmail-summary-goto-msg n t t))
939 (or (eobp)
940 (not (overlay-get rmail-summary-overlay 'face))
941 (let ((buffer-read-only nil))
942 (skip-chars-forward " ")
943 (skip-chars-forward "0-9")
944 (if undel
945 (if (looking-at "D")
946 (progn (delete-char 1) (insert " ")))
947 (delete-char 1)
948 (insert "D"))
949 ;; Register a new summary line.
950 (with-current-buffer rmail-buffer
951 (aset rmail-summary-vector (1- n) (rmail-create-summary-line n)))))
952 (beginning-of-line))
953
954 (defun rmail-summary-update-line (n)
955 "Update the summary line for message N."
956 (when (rmail-summary-goto-msg n t t)
957 (let* ((buffer-read-only nil)
958 (start (line-beginning-position))
959 (end (line-beginning-position 2))
960 (overlays (overlays-in start end))
961 high ov)
962 (while (and (setq ov (car overlays))
963 (not (setq high (overlay-get ov 'rmail-summary))))
964 (setq overlays (cdr overlays)))
965 (delete-region start end)
966 (princ
967 (with-current-buffer rmail-buffer
968 (aset rmail-summary-vector (1- n) (rmail-create-summary-line n)))
969 (current-buffer))
970 (when high
971 (forward-line -1)
972 (rmail-summary-update-highlight nil)))))
973
974 (defun rmail-summary-mark-undeleted (n)
975 (rmail-summary-mark-deleted n t))
976
977 (defun rmail-summary-deleted-p (&optional n)
978 (save-excursion
979 (and n (rmail-summary-goto-msg n nil t))
980 (skip-chars-forward " ")
981 (skip-chars-forward "0-9")
982 (looking-at "D")))
983
984 (defun rmail-summary-undelete (&optional arg)
985 "Undelete current message.
986 Optional prefix ARG means undelete ARG previous messages."
987 (interactive "p")
988 (if (/= arg 1)
989 (rmail-summary-undelete-many arg)
990 (let ((buffer-read-only nil)
991 (opoint (point)))
992 (end-of-line)
993 (cond ((re-search-backward "\\(^ *[0-9]*\\)\\(D\\)" nil t)
994 (replace-match "\\1 ")
995 (rmail-summary-goto-msg)
996 (if rmail-enable-mime
997 (set-buffer rmail-buffer)
998 (rmail-pop-to-buffer rmail-buffer))
999 (and (rmail-message-deleted-p rmail-current-message)
1000 (rmail-undelete-previous-message))
1001 (if rmail-enable-mime
1002 (rmail-pop-to-buffer rmail-buffer))
1003 (rmail-pop-to-buffer rmail-summary-buffer))
1004 (t (goto-char opoint))))))
1005
1006 (defun rmail-summary-undelete-many (&optional n)
1007 "Undelete all deleted msgs, optional prefix arg N means undelete N prev msgs."
1008 (interactive "P")
1009 (with-current-buffer rmail-buffer
1010 (let* ((init-msg (if n rmail-current-message rmail-total-messages))
1011 (rmail-current-message init-msg)
1012 (n (or n rmail-total-messages))
1013 (msgs-undeled 0))
1014 (while (and (> rmail-current-message 0)
1015 (< msgs-undeled n))
1016 (if (rmail-message-deleted-p rmail-current-message)
1017 (progn (rmail-set-attribute rmail-deleted-attr-index nil)
1018 (setq msgs-undeled (1+ msgs-undeled))))
1019 (setq rmail-current-message (1- rmail-current-message)))
1020 (set-buffer rmail-summary-buffer)
1021 (setq rmail-current-message init-msg msgs-undeled 0)
1022 (while (and (> rmail-current-message 0)
1023 (< msgs-undeled n))
1024 (if (rmail-summary-deleted-p rmail-current-message)
1025 (progn (rmail-summary-mark-undeleted rmail-current-message)
1026 (setq msgs-undeled (1+ msgs-undeled))))
1027 (setq rmail-current-message (1- rmail-current-message))))
1028 (rmail-summary-goto-msg)))
1029 \f
1030 ;; Rmail Summary mode is suitable only for specially formatted data.
1031 (put 'rmail-summary-mode 'mode-class 'special)
1032
1033 (defun rmail-summary-mode ()
1034 "Rmail Summary Mode is invoked from Rmail Mode by using \\<rmail-mode-map>\\[rmail-summary].
1035 As commands are issued in the summary buffer, they are applied to the
1036 corresponding mail messages in the rmail buffer.
1037
1038 All normal editing commands are turned off.
1039 Instead, nearly all the Rmail mode commands are available,
1040 though many of them move only among the messages in the summary.
1041
1042 These additional commands exist:
1043
1044 \\[rmail-summary-undelete-many] Undelete all or prefix arg deleted messages.
1045 \\[rmail-summary-wipe] Delete the summary and go to the Rmail buffer.
1046
1047 Commands for sorting the summary:
1048
1049 \\[rmail-summary-sort-by-date] Sort by date.
1050 \\[rmail-summary-sort-by-subject] Sort by subject.
1051 \\[rmail-summary-sort-by-author] Sort by author.
1052 \\[rmail-summary-sort-by-recipient] Sort by recipient.
1053 \\[rmail-summary-sort-by-correspondent] Sort by correspondent.
1054 \\[rmail-summary-sort-by-lines] Sort by lines.
1055 \\[rmail-summary-sort-by-labels] Sort by labels."
1056 (interactive)
1057 (kill-all-local-variables)
1058 (setq major-mode 'rmail-summary-mode)
1059 (setq mode-name "RMAIL Summary")
1060 (setq truncate-lines t)
1061 (setq buffer-read-only t)
1062 (set-syntax-table text-mode-syntax-table)
1063 (make-local-variable 'rmail-buffer)
1064 (make-local-variable 'rmail-total-messages)
1065 (make-local-variable 'rmail-current-message)
1066 (setq rmail-current-message nil)
1067 (make-local-variable 'rmail-summary-redo)
1068 (setq rmail-summary-redo nil)
1069 (make-local-variable 'revert-buffer-function)
1070 (make-local-variable 'font-lock-defaults)
1071 (setq font-lock-defaults '(rmail-summary-font-lock-keywords t))
1072 (rmail-summary-enable)
1073 (run-mode-hooks 'rmail-summary-mode-hook))
1074
1075 ;; Summary features need to be disabled during edit mode.
1076 (defun rmail-summary-disable ()
1077 (use-local-map text-mode-map)
1078 (remove-hook 'post-command-hook 'rmail-summary-rmail-update t)
1079 (setq revert-buffer-function nil))
1080
1081 (defun rmail-summary-enable ()
1082 (use-local-map rmail-summary-mode-map)
1083 (add-hook 'post-command-hook 'rmail-summary-rmail-update nil t)
1084 (setq revert-buffer-function 'rmail-update-summary))
1085
1086 (defun rmail-summary-mark-seen (n &optional nomove unseen)
1087 "Remove the unseen mark from the current message, update the summary vector.
1088 N is the number of the current message. Optional argument NOMOVE
1089 non-nil means we are already at the right column. Optional argument
1090 UNSEEN non-nil means mark the message as unseen."
1091 (save-excursion
1092 (unless nomove
1093 (beginning-of-line)
1094 (skip-chars-forward " ")
1095 (skip-chars-forward "0-9"))
1096 (when (char-equal (following-char) (if unseen ?\s ?-))
1097 (let ((buffer-read-only nil))
1098 (delete-char 1)
1099 (insert (if unseen "-" " ")))
1100 (let ((line (buffer-substring-no-properties (line-beginning-position)
1101 (line-beginning-position 2))))
1102 (with-current-buffer rmail-buffer
1103 (aset rmail-summary-vector (1- n) line))))))
1104
1105 (defvar rmail-summary-put-back-unseen nil
1106 "Used for communicating between calls to `rmail-summary-rmail-update'.
1107 If it moves to a message within an Incremental Search, and removes
1108 the `unseen' attribute from that message, it sets this flag
1109 so that if the next motion between messages is in the same Incremental
1110 Search, the `unseen' attribute is restored.")
1111
1112 ;; Show in Rmail the message described by the summary line that point is on,
1113 ;; but only if the Rmail buffer is already visible.
1114 ;; This is a post-command-hook in summary buffers.
1115 (defun rmail-summary-rmail-update ()
1116 (let (buffer-read-only)
1117 (save-excursion
1118 ;; If at end of buffer, pretend we are on the last text line.
1119 (if (eobp)
1120 (forward-line -1))
1121 (beginning-of-line)
1122 (skip-chars-forward " ")
1123 (let ((msg-num (string-to-number (buffer-substring
1124 (point)
1125 (progn (skip-chars-forward "0-9")
1126 (point))))))
1127 ;; Always leave `unseen' removed
1128 ;; if we get out of isearch mode.
1129 ;; Don't let a subsequent isearch restore that `unseen'.
1130 (if (not isearch-mode)
1131 (setq rmail-summary-put-back-unseen nil))
1132
1133 (or (eq rmail-current-message msg-num)
1134 (let ((window (get-buffer-window rmail-buffer t))
1135 (owin (selected-window)))
1136 (if isearch-mode
1137 (progn
1138 ;; If we first saw the previous message in this search,
1139 ;; and we have gone to a different message while searching,
1140 ;; put back `unseen' on the former one.
1141 (when rmail-summary-put-back-unseen
1142 (rmail-set-attribute rmail-unseen-attr-index t
1143 rmail-current-message)
1144 (save-excursion
1145 (goto-char rmail-summary-put-back-unseen)
1146 (rmail-summary-mark-seen rmail-current-message t t)))
1147 ;; Arrange to do that later, for the new current message,
1148 ;; if it still has `unseen'.
1149 (setq rmail-summary-put-back-unseen
1150 (if (rmail-message-unseen-p msg-num)
1151 (point))))
1152 (setq rmail-summary-put-back-unseen nil))
1153 ;; Go to the desired message.
1154 (setq rmail-current-message msg-num)
1155 ;; Update the summary to show the message has been seen.
1156 (rmail-summary-mark-seen msg-num t)
1157 (if window
1158 ;; Using save-window-excursion would cause the new value
1159 ;; of point to get lost.
1160 (unwind-protect
1161 (progn
1162 (select-window window)
1163 (rmail-show-message msg-num t))
1164 (select-window owin))
1165 (if (buffer-name rmail-buffer)
1166 (with-current-buffer rmail-buffer
1167 (rmail-show-message msg-num t))))
1168 ;; In linum mode, the message buffer must be specially
1169 ;; updated (Bug#4878).
1170 (and (fboundp 'linum-update)
1171 (buffer-name rmail-buffer)
1172 (linum-update rmail-buffer))))
1173 (rmail-summary-update-highlight nil)))))
1174
1175 (defun rmail-summary-save-buffer ()
1176 "Save the buffer associated with this RMAIL summary."
1177 (interactive)
1178 (save-window-excursion
1179 (save-excursion
1180 (switch-to-buffer rmail-buffer)
1181 (save-buffer))))
1182 \f
1183 (defun rmail-summary-mouse-goto-message (event)
1184 "Select the message whose summary line you click on."
1185 (interactive "@e")
1186 (goto-char (posn-point (event-end event)))
1187 (rmail-summary-goto-msg))
1188
1189 (defun rmail-summary-goto-msg (&optional n nowarn skip-rmail)
1190 "Go to message N in the summary buffer and the Rmail buffer.
1191 If N is nil, use the message corresponding to point in the summary
1192 and move to that message in the Rmail buffer.
1193
1194 If NOWARN, don't say anything if N is out of range.
1195 If SKIP-RMAIL, don't do anything to the Rmail buffer.
1196 Returns non-nil if message N was found."
1197 (interactive "P")
1198 (if (consp n) (setq n (prefix-numeric-value n)))
1199 (if (eobp) (forward-line -1))
1200 (beginning-of-line)
1201 (let* ((obuf (current-buffer))
1202 (buf rmail-buffer)
1203 (cur (point))
1204 message-not-found
1205 (curmsg (string-to-number
1206 (buffer-substring (point)
1207 (min (point-max) (+ 6 (point))))))
1208 (total (with-current-buffer buf rmail-total-messages)))
1209 ;; If message number N was specified, find that message's line
1210 ;; or set message-not-found.
1211 ;; If N wasn't specified or that message can't be found.
1212 ;; set N by default.
1213 (if (not n)
1214 (setq n curmsg)
1215 (if (< n 1)
1216 (progn (message "No preceding message")
1217 (setq n 1)))
1218 (if (and (> n total)
1219 (> total 0))
1220 (progn (message "No following message")
1221 (goto-char (point-max))
1222 (rmail-summary-goto-msg nil nowarn skip-rmail)))
1223 (goto-char (point-min))
1224 (if (not (re-search-forward (format "^%5d[^0-9]" n) nil t))
1225 (progn (or nowarn (message "Message %d not found" n))
1226 (setq n curmsg)
1227 (setq message-not-found t)
1228 (goto-char cur))))
1229 (rmail-summary-mark-seen n)
1230 (rmail-summary-update-highlight message-not-found)
1231 (beginning-of-line)
1232 (unless skip-rmail
1233 (let ((selwin (selected-window)))
1234 (unwind-protect
1235 (progn (rmail-pop-to-buffer buf)
1236 (rmail-show-message n))
1237 (select-window selwin)
1238 ;; The actions above can alter the current buffer. Preserve it.
1239 (set-buffer obuf))))
1240 (not message-not-found)))
1241
1242 ;; Update the highlighted line in an rmail summary buffer.
1243 ;; That should be current. We highlight the line point is on.
1244 ;; If NOT-FOUND is non-nil, we turn off highlighting.
1245 (defun rmail-summary-update-highlight (not-found)
1246 ;; Make sure we have an overlay to use.
1247 (or rmail-summary-overlay
1248 (progn
1249 (make-local-variable 'rmail-summary-overlay)
1250 (setq rmail-summary-overlay (make-overlay (point) (point)))
1251 (overlay-put rmail-summary-overlay 'rmail-summary t)))
1252 ;; If this message is in the summary, use the overlay to highlight it.
1253 ;; Otherwise, don't highlight anything.
1254 (if not-found
1255 (overlay-put rmail-summary-overlay 'face nil)
1256 (move-overlay rmail-summary-overlay
1257 (save-excursion (beginning-of-line)
1258 (skip-chars-forward " ")
1259 (point))
1260 (line-end-position))
1261 (overlay-put rmail-summary-overlay 'face 'highlight)))
1262 \f
1263 (defun rmail-summary-scroll-msg-up (&optional dist)
1264 "Scroll the Rmail window forward.
1265 If the Rmail window is displaying the end of a message,
1266 advance to the next message."
1267 (interactive "P")
1268 (if (eq dist '-)
1269 (rmail-summary-scroll-msg-down nil)
1270 (let ((rmail-buffer-window (get-buffer-window rmail-buffer)))
1271 (if rmail-buffer-window
1272 (if (let ((rmail-summary-window (selected-window)))
1273 (select-window rmail-buffer-window)
1274 (prog1
1275 ;; Is EOB visible in the buffer?
1276 (save-excursion
1277 (let ((ht (window-height (selected-window))))
1278 (move-to-window-line (- ht 2))
1279 (end-of-line)
1280 (eobp)))
1281 (select-window rmail-summary-window)))
1282 (if (not rmail-summary-scroll-between-messages)
1283 (error "End of buffer")
1284 (rmail-summary-next-msg (or dist 1)))
1285 (let ((other-window-scroll-buffer rmail-buffer))
1286 (scroll-other-window dist)))
1287 ;; If it isn't visible at all, show the beginning.
1288 (rmail-summary-beginning-of-message)))))
1289
1290 (defun rmail-summary-scroll-msg-down (&optional dist)
1291 "Scroll the Rmail window backward.
1292 If the Rmail window is now displaying the beginning of a message,
1293 move to the previous message."
1294 (interactive "P")
1295 (if (eq dist '-)
1296 (rmail-summary-scroll-msg-up nil)
1297 (let ((rmail-buffer-window (get-buffer-window rmail-buffer)))
1298 (if rmail-buffer-window
1299 (if (let ((rmail-summary-window (selected-window)))
1300 (select-window rmail-buffer-window)
1301 (prog1
1302 ;; Is BOB visible in the buffer?
1303 (save-excursion
1304 (move-to-window-line 0)
1305 (beginning-of-line)
1306 (bobp))
1307 (select-window rmail-summary-window)))
1308 (if (not rmail-summary-scroll-between-messages)
1309 (error "Beginning of buffer")
1310 (rmail-summary-previous-msg (or dist 1)))
1311 (let ((other-window-scroll-buffer rmail-buffer))
1312 (scroll-other-window-down dist)))
1313 ;; If it isn't visible at all, show the beginning.
1314 (rmail-summary-beginning-of-message)))))
1315
1316 (defun rmail-summary-beginning-of-message ()
1317 "Show current message from the beginning."
1318 (interactive)
1319 (rmail-summary-show-message 'BEG))
1320
1321 (defun rmail-summary-end-of-message ()
1322 "Show bottom of current message."
1323 (interactive)
1324 (rmail-summary-show-message 'END))
1325
1326 (defun rmail-summary-show-message (where)
1327 "Show current mail message.
1328 Position it according to WHERE which can be BEG or END"
1329 (if (and (one-window-p) (not pop-up-frames))
1330 ;; If there is just one window, put the summary on the top.
1331 (let ((buffer rmail-buffer))
1332 (split-window (selected-window) rmail-summary-window-size)
1333 (select-window (frame-first-window))
1334 (rmail-pop-to-buffer rmail-buffer)
1335 ;; If pop-to-buffer did not use that window, delete that
1336 ;; window. (This can happen if it uses another frame.)
1337 (or (eq buffer (window-buffer (next-window (frame-first-window))))
1338 (delete-other-windows)))
1339 (rmail-pop-to-buffer rmail-buffer))
1340 (cond
1341 ((eq where 'BEG)
1342 (goto-char (point-min))
1343 (search-forward "\n\n"))
1344 ((eq where 'END)
1345 (goto-char (point-max))
1346 (recenter (1- (window-height))))
1347 )
1348 (rmail-pop-to-buffer rmail-summary-buffer))
1349
1350 (defun rmail-summary-bury ()
1351 "Bury the Rmail buffer and the Rmail summary buffer."
1352 (interactive)
1353 (let ((buffer-to-bury (current-buffer)))
1354 (let (window)
1355 (while (setq window (get-buffer-window rmail-buffer))
1356 (set-window-buffer window (other-buffer rmail-buffer)))
1357 (bury-buffer rmail-buffer))
1358 (switch-to-buffer (other-buffer buffer-to-bury))
1359 (bury-buffer buffer-to-bury)))
1360
1361 (defun rmail-summary-quit ()
1362 "Quit out of Rmail and Rmail summary."
1363 (interactive)
1364 (rmail-summary-wipe)
1365 (rmail-quit))
1366
1367 (defun rmail-summary-wipe ()
1368 "Kill and wipe away Rmail summary, remaining within Rmail."
1369 (interactive)
1370 (with-current-buffer rmail-buffer (setq rmail-summary-buffer nil))
1371 (let ((local-rmail-buffer rmail-buffer))
1372 (kill-buffer (current-buffer))
1373 ;; Delete window if not only one.
1374 (if (not (eq (selected-window) (next-window nil 'no-minibuf)))
1375 (delete-window))
1376 ;; Switch windows to the rmail buffer, or switch to it in this window.
1377 (rmail-pop-to-buffer local-rmail-buffer)))
1378
1379 (defun rmail-summary-expunge ()
1380 "Actually erase all deleted messages and recompute summary headers."
1381 (interactive)
1382 (with-current-buffer rmail-buffer
1383 (when (rmail-expunge-confirmed)
1384 (rmail-only-expunge)))
1385 (rmail-update-summary))
1386
1387 (defun rmail-summary-expunge-and-save ()
1388 "Expunge and save RMAIL file."
1389 (interactive)
1390 (save-excursion
1391 (rmail-expunge-and-save))
1392 (rmail-update-summary)
1393 (set-buffer-modified-p nil))
1394
1395 (defun rmail-summary-get-new-mail (&optional file-name)
1396 "Get new mail and recompute summary headers.
1397
1398 Optionally you can specify the file to get new mail from. In this case,
1399 the file of new mail is not changed or deleted. Noninteractively, you can
1400 pass the inbox file name as an argument. Interactively, a prefix
1401 argument says to read a file name and use that file as the inbox."
1402 (interactive
1403 (list (if current-prefix-arg
1404 (read-file-name "Get new mail from file: "))))
1405 (let (msg)
1406 (with-current-buffer rmail-buffer
1407 (rmail-get-new-mail file-name)
1408 ;; Get the proper new message number.
1409 (setq msg rmail-current-message))
1410 ;; Make sure that message is displayed.
1411 (or (zerop msg)
1412 (rmail-summary-goto-msg msg))))
1413
1414 (defun rmail-summary-input (filename)
1415 "Run Rmail on file FILENAME."
1416 (interactive "FRun rmail on RMAIL file: ")
1417 ;; We switch windows here, then display the other Rmail file there.
1418 (rmail-pop-to-buffer rmail-buffer)
1419 (rmail filename))
1420
1421 (defun rmail-summary-first-message ()
1422 "Show first message in Rmail file from summary buffer."
1423 (interactive)
1424 (with-no-warnings
1425 (beginning-of-buffer)))
1426
1427 (defun rmail-summary-last-message ()
1428 "Show last message in Rmail file from summary buffer."
1429 (interactive)
1430 (with-no-warnings
1431 (end-of-buffer))
1432 (forward-line -1))
1433
1434 (declare-function rmail-abort-edit "rmailedit" ())
1435 (declare-function rmail-cease-edit "rmailedit"())
1436 (declare-function rmail-set-label "rmailkwd" (l state &optional n))
1437 (declare-function rmail-output-read-file-name "rmailout" ())
1438 (declare-function mail-send-and-exit "sendmail" (&optional arg))
1439
1440 (defvar rmail-summary-edit-map nil)
1441 (if rmail-summary-edit-map
1442 nil
1443 (setq rmail-summary-edit-map
1444 (nconc (make-sparse-keymap) text-mode-map))
1445 (define-key rmail-summary-edit-map "\C-c\C-c" 'rmail-cease-edit)
1446 (define-key rmail-summary-edit-map "\C-c\C-]" 'rmail-abort-edit))
1447
1448 (defun rmail-summary-edit-current-message ()
1449 "Edit the contents of this message."
1450 (interactive)
1451 (rmail-pop-to-buffer rmail-buffer)
1452 (rmail-edit-current-message)
1453 (use-local-map rmail-summary-edit-map))
1454
1455 (defun rmail-summary-cease-edit ()
1456 "Finish editing message, then go back to Rmail summary buffer."
1457 (interactive)
1458 (rmail-cease-edit)
1459 (rmail-pop-to-buffer rmail-summary-buffer))
1460
1461 (defun rmail-summary-abort-edit ()
1462 "Abort edit of current message; restore original contents.
1463 Go back to summary buffer."
1464 (interactive)
1465 (rmail-abort-edit)
1466 (rmail-pop-to-buffer rmail-summary-buffer))
1467
1468 (defun rmail-summary-search-backward (regexp &optional n)
1469 "Show message containing next match for REGEXP.
1470 Prefix argument gives repeat count; negative argument means search
1471 backwards (through earlier messages).
1472 Interactively, empty argument means use same regexp used last time."
1473 (interactive
1474 (let* ((reversep (>= (prefix-numeric-value current-prefix-arg) 0))
1475 (prompt
1476 (concat (if reversep "Reverse " "") "Rmail search (regexp"))
1477 regexp)
1478 (setq prompt
1479 (concat prompt
1480 (if rmail-search-last-regexp
1481 (concat ", default "
1482 rmail-search-last-regexp "): ")
1483 "): ")))
1484 (setq regexp (read-string prompt))
1485 (cond ((not (equal regexp ""))
1486 (setq rmail-search-last-regexp regexp))
1487 ((not rmail-search-last-regexp)
1488 (error "No previous Rmail search string")))
1489 (list rmail-search-last-regexp
1490 (prefix-numeric-value current-prefix-arg))))
1491 ;; Don't use save-excursion because that prevents point from moving
1492 ;; properly in the summary buffer.
1493 (with-current-buffer rmail-buffer
1494 (rmail-search regexp (- n))))
1495
1496 (defun rmail-summary-search (regexp &optional n)
1497 "Show message containing next match for REGEXP.
1498 Prefix argument gives repeat count; negative argument means search
1499 backwards (through earlier messages).
1500 Interactively, empty argument means use same regexp used last time."
1501 (interactive
1502 (let* ((reversep (< (prefix-numeric-value current-prefix-arg) 0))
1503 (prompt
1504 (concat (if reversep "Reverse " "") "Rmail search (regexp"))
1505 regexp)
1506 (setq prompt
1507 (concat prompt
1508 (if rmail-search-last-regexp
1509 (concat ", default "
1510 rmail-search-last-regexp "): ")
1511 "): ")))
1512 (setq regexp (read-string prompt))
1513 (cond ((not (equal regexp ""))
1514 (setq rmail-search-last-regexp regexp))
1515 ((not rmail-search-last-regexp)
1516 (error "No previous Rmail search string")))
1517 (list rmail-search-last-regexp
1518 (prefix-numeric-value current-prefix-arg))))
1519 ;; Don't use save-excursion because that prevents point from moving
1520 ;; properly in the summary buffer.
1521 (let ((buffer (current-buffer))
1522 (selwin (selected-window)))
1523 (unwind-protect
1524 (progn
1525 (rmail-pop-to-buffer rmail-buffer)
1526 (rmail-search regexp n))
1527 (select-window selwin)
1528 (set-buffer buffer))))
1529
1530 (defun rmail-summary-toggle-header ()
1531 "Show original message header if pruned header currently shown, or vice versa."
1532 (interactive)
1533 (save-window-excursion
1534 (set-buffer rmail-buffer)
1535 (rmail-toggle-header))
1536 ;; Inside save-excursion, some changes to point in the RMAIL buffer are lost.
1537 ;; Set point to point-min in the RMAIL buffer, if it is visible.
1538 (let ((window (get-buffer-window rmail-buffer)))
1539 (if window
1540 ;; Using save-window-excursion would lose the new value of point.
1541 (let ((owin (selected-window)))
1542 (unwind-protect
1543 (progn
1544 (select-window window)
1545 (goto-char (point-min)))
1546 (select-window owin))))))
1547
1548
1549 (defun rmail-summary-add-label (label)
1550 "Add LABEL to labels associated with current Rmail message.
1551 Completion is performed over known labels when reading."
1552 (interactive (list (with-current-buffer rmail-buffer
1553 (rmail-read-label "Add label"))))
1554 (with-current-buffer rmail-buffer
1555 (rmail-add-label label)))
1556
1557 (defun rmail-summary-kill-label (label)
1558 "Remove LABEL from labels associated with current Rmail message.
1559 Completion is performed over known labels when reading."
1560 (interactive (list (with-current-buffer rmail-buffer
1561 (rmail-read-label "Kill label"))))
1562 (with-current-buffer rmail-buffer
1563 (rmail-set-label label nil)))
1564 \f
1565 ;;;; *** Rmail Summary Mailing Commands ***
1566
1567 (defun rmail-summary-override-mail-send-and-exit ()
1568 "Replace bindings to `mail-send-and-exit' with `rmail-summary-send-and-exit'."
1569 (use-local-map (copy-keymap (current-local-map)))
1570 (dolist (key (where-is-internal 'mail-send-and-exit))
1571 (define-key (current-local-map) key 'rmail-summary-send-and-exit)))
1572
1573 (defun rmail-summary-mail ()
1574 "Send mail in another window.
1575 While composing the message, use \\[mail-yank-original] to yank the
1576 original message into it."
1577 (interactive)
1578 (let ((window (get-buffer-window rmail-buffer)))
1579 (if window
1580 (select-window window)
1581 (set-buffer rmail-buffer)))
1582 (rmail-start-mail nil nil nil nil nil (current-buffer))
1583 (rmail-summary-override-mail-send-and-exit))
1584
1585 (defun rmail-summary-continue ()
1586 "Continue composing outgoing message previously being composed."
1587 (interactive)
1588 (let ((window (get-buffer-window rmail-buffer)))
1589 (if window
1590 (select-window window)
1591 (set-buffer rmail-buffer)))
1592 (rmail-start-mail t))
1593
1594 (defun rmail-summary-reply (just-sender)
1595 "Reply to the current message.
1596 Normally include CC: to all other recipients of original message;
1597 prefix argument means ignore them. While composing the reply,
1598 use \\[mail-yank-original] to yank the original message into it."
1599 (interactive "P")
1600 (let ((window (get-buffer-window rmail-buffer)))
1601 (if window
1602 (select-window window)
1603 (set-buffer rmail-buffer)))
1604 (rmail-reply just-sender)
1605 (rmail-summary-override-mail-send-and-exit))
1606
1607 (defun rmail-summary-retry-failure ()
1608 "Edit a mail message which is based on the contents of the current message.
1609 For a message rejected by the mail system, extract the interesting headers and
1610 the body of the original message; otherwise copy the current message."
1611 (interactive)
1612 (let ((window (get-buffer-window rmail-buffer)))
1613 (if window
1614 (select-window window)
1615 (set-buffer rmail-buffer)))
1616 (rmail-retry-failure)
1617 (rmail-summary-override-mail-send-and-exit))
1618
1619 (defun rmail-summary-send-and-exit ()
1620 "Send mail reply and return to summary buffer."
1621 (interactive)
1622 (mail-send-and-exit t))
1623
1624 (defun rmail-summary-forward (resend)
1625 "Forward the current message to another user.
1626 With prefix argument, \"resend\" the message instead of forwarding it;
1627 see the documentation of `rmail-resend'."
1628 (interactive "P")
1629 (save-excursion
1630 (let ((window (get-buffer-window rmail-buffer)))
1631 (if window
1632 (select-window window)
1633 (set-buffer rmail-buffer)))
1634 (rmail-forward resend)
1635 (rmail-summary-override-mail-send-and-exit)))
1636
1637 (defun rmail-summary-resend ()
1638 "Resend current message using `rmail-resend'."
1639 (interactive)
1640 (save-excursion
1641 (let ((window (get-buffer-window rmail-buffer)))
1642 (if window
1643 (select-window window)
1644 (set-buffer rmail-buffer)))
1645 (call-interactively 'rmail-resend)))
1646 \f
1647 ;; Summary output commands.
1648
1649 (defun rmail-summary-output (&optional file-name n)
1650 "Append this message to mail file FILE-NAME.
1651 This works with both mbox format and Babyl format files,
1652 outputting in the appropriate format for each.
1653 The default file name comes from `rmail-default-file',
1654 which is updated to the name you use in this command.
1655
1656 A prefix argument N says to output that many consecutive messages
1657 from those in the summary, starting with the current one.
1658 Deleted messages are skipped and don't count.
1659 When called from Lisp code, N may be omitted and defaults to 1.
1660
1661 This command always outputs the complete message header,
1662 even the header display is currently pruned."
1663 (interactive
1664 (progn (require 'rmailout)
1665 (list (rmail-output-read-file-name)
1666 (prefix-numeric-value current-prefix-arg))))
1667 (let ((i 0) prev-msg)
1668 (while
1669 (and (< i n)
1670 (progn (rmail-summary-goto-msg)
1671 (not (eq prev-msg
1672 (setq prev-msg
1673 (with-current-buffer rmail-buffer
1674 rmail-current-message))))))
1675 (setq i (1+ i))
1676 (with-current-buffer rmail-buffer
1677 (let ((rmail-delete-after-output nil))
1678 (rmail-output file-name 1)))
1679 (if rmail-delete-after-output
1680 (rmail-summary-delete-forward nil)
1681 (if (< i n)
1682 (rmail-summary-next-msg 1))))))
1683
1684 (defalias 'rmail-summary-output-to-rmail-file 'rmail-summary-output)
1685
1686 (declare-function rmail-output-as-seen "rmailout"
1687 (file-name &optional count noattribute from-gnus))
1688
1689 (defun rmail-summary-output-as-seen (&optional file-name n)
1690 "Append this message to mbox file named FILE-NAME.
1691 A prefix argument N says to output that many consecutive messages,
1692 from the summary, starting with the current one.
1693 Deleted messages are skipped and don't count.
1694 When called from Lisp code, N may be omitted and defaults to 1.
1695
1696 This outputs the message header as you see it (or would see it)
1697 displayed in Rmail.
1698
1699 The default file name comes from `rmail-default-file',
1700 which is updated to the name you use in this command."
1701 (interactive
1702 (progn (require 'rmailout)
1703 (list (rmail-output-read-file-name)
1704 (prefix-numeric-value current-prefix-arg))))
1705 (require 'rmailout) ; for rmail-output-as-seen in non-interactive case
1706 (let ((i 0) prev-msg)
1707 (while
1708 (and (< i n)
1709 (progn (rmail-summary-goto-msg)
1710 (not (eq prev-msg
1711 (setq prev-msg
1712 (with-current-buffer rmail-buffer
1713 rmail-current-message))))))
1714 (setq i (1+ i))
1715 (with-current-buffer rmail-buffer
1716 (let ((rmail-delete-after-output nil))
1717 (rmail-output-as-seen file-name 1)))
1718 (if rmail-delete-after-output
1719 (rmail-summary-delete-forward nil)
1720 (if (< i n)
1721 (rmail-summary-next-msg 1))))))
1722
1723 (defun rmail-summary-output-menu ()
1724 "Output current message to another Rmail file, chosen with a menu.
1725 Also set the default for subsequent \\[rmail-output-to-babyl-file] commands.
1726 The variables `rmail-secondary-file-directory' and
1727 `rmail-secondary-file-regexp' control which files are offered in the menu."
1728 (interactive)
1729 (with-current-buffer rmail-buffer
1730 (let ((rmail-delete-after-output nil))
1731 (call-interactively 'rmail-output-menu)))
1732 (if rmail-delete-after-output
1733 (rmail-summary-delete-forward nil)))
1734
1735 (defun rmail-summary-construct-io-menu ()
1736 (let ((files (rmail-find-all-files rmail-secondary-file-directory)))
1737 (if files
1738 (progn
1739 (define-key rmail-summary-mode-map [menu-bar classify input-menu]
1740 (cons "Input Rmail File"
1741 (rmail-list-to-menu "Input Rmail File"
1742 files
1743 'rmail-summary-input)))
1744 (define-key rmail-summary-mode-map [menu-bar classify output-menu]
1745 (cons "Output Rmail File"
1746 (rmail-list-to-menu "Output Rmail File"
1747 files
1748 'rmail-summary-output))))
1749 (define-key rmail-summary-mode-map [menu-bar classify input-menu]
1750 '("Input Rmail File" . rmail-disable-menu))
1751 (define-key rmail-summary-mode-map [menu-bar classify output-menu]
1752 '("Output Rmail File" . rmail-disable-menu)))))
1753
1754 (defun rmail-summary-output-body (&optional file-name)
1755 "Write this message body to the file FILE-NAME.
1756 FILE-NAME defaults, interactively, from the Subject field of the message."
1757 (interactive)
1758 (with-current-buffer rmail-buffer
1759 (let ((rmail-delete-after-output nil))
1760 (if file-name
1761 (rmail-output-body-to-file file-name)
1762 (call-interactively 'rmail-output-body-to-file))))
1763 (if rmail-delete-after-output
1764 (rmail-summary-delete-forward nil)))
1765 \f
1766 ;; Sorting messages in Rmail Summary buffer.
1767
1768 (defun rmail-summary-sort-by-date (reverse)
1769 "Sort messages of current Rmail summary by \"Date\" header.
1770 If prefix argument REVERSE is non-nil, sorts in reverse order."
1771 (interactive "P")
1772 (rmail-sort-from-summary (function rmail-sort-by-date) reverse))
1773
1774 (defun rmail-summary-sort-by-subject (reverse)
1775 "Sort messages of current Rmail summary by \"Subject\" header.
1776 Ignores any \"Re: \" prefix. If prefix argument REVERSE is
1777 non-nil, sorts in reverse order."
1778 (interactive "P")
1779 (rmail-sort-from-summary (function rmail-sort-by-subject) reverse))
1780
1781 (defun rmail-summary-sort-by-author (reverse)
1782 "Sort messages of current Rmail summary by author.
1783 This uses either the \"From\" or \"Sender\" header, downcased.
1784 If prefix argument REVERSE is non-nil, sorts in reverse order."
1785 (interactive "P")
1786 (rmail-sort-from-summary (function rmail-sort-by-author) reverse))
1787
1788 (defun rmail-summary-sort-by-recipient (reverse)
1789 "Sort messages of current Rmail summary by recipient.
1790 This uses either the \"To\" or \"Apparently-To\" header, downcased.
1791 If prefix argument REVERSE is non-nil, sorts in reverse order."
1792 (interactive "P")
1793 (rmail-sort-from-summary (function rmail-sort-by-recipient) reverse))
1794
1795 (defun rmail-summary-sort-by-correspondent (reverse)
1796 "Sort messages of current Rmail summary by other correspondent.
1797 This uses either the \"From\", \"Sender\", \"To\", or
1798 \"Apparently-To\" header, downcased. Uses the first header not
1799 excluded by `mail-dont-reply-to-names'. If prefix argument
1800 REVERSE is non-nil, sorts in reverse order."
1801 (interactive "P")
1802 (rmail-sort-from-summary (function rmail-sort-by-correspondent) reverse))
1803
1804 (defun rmail-summary-sort-by-lines (reverse)
1805 "Sort messages of current Rmail summary by the number of lines.
1806 If prefix argument REVERSE is non-nil, sorts in reverse order."
1807 (interactive "P")
1808 (rmail-sort-from-summary (function rmail-sort-by-lines) reverse))
1809
1810 (defun rmail-summary-sort-by-labels (reverse labels)
1811 "Sort messages of current Rmail summary by labels.
1812 LABELS is a comma-separated list of labels.
1813 If prefix argument REVERSE is non-nil, sorts in reverse order."
1814 (interactive "P\nsSort by labels: ")
1815 (rmail-sort-from-summary
1816 (lambda (reverse) (rmail-sort-by-labels reverse labels))
1817 reverse))
1818
1819 (defun rmail-sort-from-summary (sortfun reverse)
1820 "Sort the Rmail buffer using sorting function SORTFUN.
1821 Passes REVERSE to SORTFUN as its sole argument. Then regenerates
1822 the summary. Note that the whole Rmail buffer is sorted, even if
1823 the summary is only showing a subset of messages."
1824 (require 'rmailsort)
1825 (let ((selwin (selected-window)))
1826 (unwind-protect
1827 (progn (rmail-pop-to-buffer rmail-buffer)
1828 (funcall sortfun reverse))
1829 (select-window selwin))))
1830
1831 (provide 'rmailsum)
1832
1833 ;; Local Variables:
1834 ;; generated-autoload-file: "rmail.el"
1835 ;; End:
1836
1837 ;;; rmailsum.el ends here