]> code.delx.au - gnu-emacs/blob - lisp/mail/rmailsum.el
(rmail-summary-mode-map): Add local menu bar bindings.
[gnu-emacs] / lisp / mail / rmailsum.el
1 ;;; rmailsum.el --- make summary buffers for the mail reader
2
3 ;; Copyright (C) 1985, 1993 Free Software Foundation, Inc.
4
5 ;; Maintainer: FSF
6 ;; Keywords: mail
7
8 ;; This file is part of GNU Emacs.
9
10 ;; GNU Emacs is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
19
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to
22 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
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 ;; Entry points for making a summary buffer.
33
34 ;; Regenerate the contents of the summary
35 ;; using the same selection criterion as last time.
36 ;; M-x revert-buffer in a summary buffer calls this function.
37 (defun rmail-update-summary (&rest ignore)
38 (apply (car rmail-summary-redo) (cdr rmail-summary-redo)))
39
40 (defun rmail-summary ()
41 "Display a summary of all messages, one line per message."
42 (interactive)
43 (rmail-new-summary "All" '(rmail-summary) nil))
44
45 (defun rmail-summary-by-labels (labels)
46 "Display a summary of all messages with one or more LABELS.
47 LABELS should be a string containing the desired labels, separated by commas."
48 (interactive "sLabels to summarize by: ")
49 (if (string= labels "")
50 (setq labels (or rmail-last-multi-labels
51 (error "No label specified"))))
52 (setq rmail-last-multi-labels labels)
53 (rmail-new-summary (concat "labels " labels)
54 (list 'rmail-summary-by-labels labels)
55 'rmail-message-labels-p
56 (concat ", \\(" (mail-comma-list-regexp labels) "\\),")))
57
58 (defun rmail-summary-by-recipients (recipients &optional primary-only)
59 "Display a summary of all messages with the given RECIPIENTS.
60 Normally checks the To, From and Cc fields of headers;
61 but if PRIMARY-ONLY is non-nil (prefix arg given),
62 only look in the To and From fields.
63 RECIPIENTS is a string of regexps separated by commas."
64 (interactive "sRecipients to summarize by: \nP")
65 (rmail-new-summary
66 (concat "recipients " recipients)
67 (list 'rmail-summary-by-recipients recipients primary-only)
68 'rmail-message-recipients-p
69 (mail-comma-list-regexp recipients) primary-only))
70
71 (defun rmail-summary-by-regexp (regexp)
72 "Display a summary of all messages according to regexp REGEXP.
73 If the regular expression is found in the header of the message
74 \(including in the date and other lines, as well as the subject line),
75 Emacs will list the header line in the RMAIL-summary."
76 (interactive "sRegexp to summarize by: ")
77 (if (string= regexp "")
78 (setq regexp (or rmail-last-regexp
79 (error "No regexp specified."))))
80 (setq rmail-last-regexp regexp)
81 (rmail-new-summary (concat "regexp " regexp)
82 (list 'rmail-summary-by-regexp regexp)
83 'rmail-message-regexp-p
84 regexp))
85
86 ;; rmail-summary-by-topic
87 ;; 1989 R.A. Schnitzler
88
89 (defun rmail-summary-by-topic (subject &optional whole-message)
90 "Display a summary of all messages with the given SUBJECT.
91 Normally checks the Subject field of headers;
92 but if WHOLE-MESSAGE is non-nil (prefix arg given),
93 look in the whole message.
94 SUBJECT is a string of regexps separated by commas."
95 (interactive "sTopics to summarize by: \nP")
96 (rmail-new-summary
97 (concat "about " subject)
98 (list 'rmail-summary-by-topic subject whole-message)
99 'rmail-message-subject-p
100 (mail-comma-list-regexp subject) whole-message))
101
102 (defun rmail-message-subject-p (msg subject &optional whole-message)
103 (save-restriction
104 (goto-char (rmail-msgbeg msg))
105 (search-forward "\n*** EOOH ***\n")
106 (narrow-to-region
107 (point)
108 (progn (search-forward (if whole-message "\^_" "\n\n")) (point)))
109 (goto-char (point-min))
110 (if whole-message (re-search-forward subject nil t)
111 (string-match subject (or (mail-fetch-field "Subject") "")) )))
112
113 (defun rmail-summary-by-senders (senders)
114 "Display a summary of all messages with the given SENDERS.
115 SENDERS is a string of names separated by commas."
116 (interactive "sSenders to summarize by: ")
117 (rmail-new-summary
118 (concat "senders " senders)
119 'rmail-message-senders-p
120 (mail-comma-list-regexp senders)))
121
122 (defun rmail-message-senders-p (msg senders)
123 (save-restriction
124 (goto-char (rmail-msgbeg msg))
125 (search-forward "\n*** EOOH ***\n")
126 (narrow-to-region (point) (progn (search-forward "\n\n") (point)))
127 (string-match senders (or (mail-fetch-field "From") ""))))
128 \f
129 ;; General making of a summary buffer.
130
131 (defvar rmail-summary-symbol-number 0)
132
133 (defun rmail-new-summary (description redo-form function &rest args)
134 "Create a summary of selected messages.
135 DESCRIPTION makes part of the mode line of the summary buffer.
136 For each message, FUNCTION is applied to the message number and ARGS...
137 and if the result is non-nil, that message is included.
138 nil for FUNCTION means all messages."
139 (message "Computing summary lines...")
140 (let (sumbuf mesg was-in-summary)
141 (save-excursion
142 ;; Go to the Rmail buffer.
143 (if (eq major-mode 'rmail-summary-mode)
144 (progn
145 (setq was-in-summary t)
146 (set-buffer rmail-buffer)))
147 ;; Find its summary buffer, or make one.
148 (setq sumbuf
149 (if (and rmail-summary-buffer
150 (buffer-name rmail-summary-buffer))
151 rmail-summary-buffer
152 (generate-new-buffer (concat (buffer-name) "-summary"))))
153 (setq mesg rmail-current-message)
154 ;; Filter the messages; make or get their summary lines.
155 (let ((summary-msgs ())
156 (new-summary-line-count 0))
157 (let ((msgnum 1)
158 (buffer-read-only nil))
159 (save-restriction
160 (save-excursion
161 (widen)
162 (goto-char (point-min))
163 (while (>= rmail-total-messages msgnum)
164 (if (or (null function)
165 (apply function (cons msgnum args)))
166 (setq summary-msgs
167 (cons (cons msgnum (rmail-make-summary-line msgnum))
168 summary-msgs)))
169 (setq msgnum (1+ msgnum)))
170 (setq summary-msgs (nreverse summary-msgs)))))
171 ;; Temporarily, while summary buffer is unfinished,
172 ;; we "don't have" a summary.
173 (setq rmail-summary-buffer nil)
174 (save-excursion
175 (let ((rbuf (current-buffer))
176 (total rmail-total-messages))
177 (set-buffer sumbuf)
178 ;; Set up the summary buffer's contents.
179 (let ((buffer-read-only nil))
180 (erase-buffer)
181 (while summary-msgs
182 (princ (cdr (car summary-msgs)) sumbuf)
183 (setq summary-msgs (cdr summary-msgs)))
184 (goto-char (point-min)))
185 ;; Set up the rest of its state and local variables.
186 (setq buffer-read-only t)
187 (rmail-summary-mode)
188 (make-local-variable 'minor-mode-alist)
189 (setq minor-mode-alist (list '(t (concat ": " description))))
190 (setq rmail-buffer rbuf
191 rmail-summary-redo redo-form
192 rmail-total-messages total))))
193 (setq rmail-summary-buffer sumbuf))
194 ;; Now display the summary buffer and go to the right place in it.
195 (or was-in-summary
196 (pop-to-buffer sumbuf))
197 (rmail-summary-goto-msg mesg t t)
198 (message "Computing summary lines...done")))
199 \f
200 ;; Low levels of generating a summary.
201
202 (defun rmail-make-summary-line (msg)
203 (let ((line (or (aref rmail-summary-vector (1- msg))
204 (progn
205 (setq new-summary-line-count
206 (1+ new-summary-line-count))
207 (if (zerop (% new-summary-line-count 10))
208 (message "Computing summary lines...%d"
209 new-summary-line-count))
210 (rmail-make-summary-line-1 msg)))))
211 ;; Fix up the part of the summary that says "deleted" or "unseen".
212 (aset line 4
213 (if (rmail-message-deleted-p msg) ?\D
214 (if (= ?0 (char-after (+ 3 (rmail-msgbeg msg))))
215 ?\- ?\ )))
216 line))
217
218 (defun rmail-make-summary-line-1 (msg)
219 (goto-char (rmail-msgbeg msg))
220 (let* ((lim (save-excursion (forward-line 2) (point)))
221 pos
222 (labels
223 (progn
224 (forward-char 3)
225 (concat
226 ; (if (save-excursion (re-search-forward ",answered," lim t))
227 ; "*" "")
228 ; (if (save-excursion (re-search-forward ",filed," lim t))
229 ; "!" "")
230 (if (progn (search-forward ",,") (eolp))
231 ""
232 (concat "{"
233 (buffer-substring (point)
234 (progn (end-of-line) (point)))
235 "} ")))))
236 (line
237 (progn
238 (forward-line 1)
239 (if (looking-at "Summary-line: ")
240 (progn
241 (goto-char (match-end 0))
242 (setq line
243 (buffer-substring (point)
244 (progn (forward-line 1) (point)))))))))
245 ;; Obsolete status lines lacking a # should be flushed.
246 (and line
247 (not (string-match "#" line))
248 (progn
249 (delete-region (point)
250 (progn (forward-line -1) (point)))
251 (setq line nil)))
252 ;; If we didn't get a valid status line from the message,
253 ;; make a new one and put it in the message.
254 (or line
255 (let* ((case-fold-search t)
256 (next (rmail-msgend msg))
257 (beg (if (progn (goto-char (rmail-msgbeg msg))
258 (search-forward "\n*** EOOH ***\n" next t))
259 (point)
260 (forward-line 1)
261 (point)))
262 (end (progn (search-forward "\n\n" nil t) (point))))
263 (save-restriction
264 (narrow-to-region beg end)
265 (goto-char beg)
266 (setq line (rmail-make-basic-summary-line)))
267 (goto-char (rmail-msgbeg msg))
268 (forward-line 2)
269 (insert "Summary-line: " line)))
270 (setq pos (string-match "#" line))
271 (aset rmail-summary-vector (1- msg)
272 (concat (format "%4d " msg)
273 (substring line 0 pos)
274 labels
275 (substring line (1+ pos))))))
276
277 (defun rmail-make-basic-summary-line ()
278 (goto-char (point-min))
279 (concat (save-excursion
280 (if (not (re-search-forward "^Date:" nil t))
281 " "
282 (cond ((re-search-forward "\\([^0-9:]\\)\\([0-3]?[0-9]\\)\\([- \t_]+\\)\\([adfjmnos][aceopu][bcglnprtvy]\\)"
283 (save-excursion (end-of-line) (point)) t)
284 (format "%2d-%3s"
285 (string-to-int (buffer-substring
286 (match-beginning 2)
287 (match-end 2)))
288 (buffer-substring
289 (match-beginning 4) (match-end 4))))
290 ((re-search-forward "\\([^a-z]\\)\\([adfjmnos][acepou][bcglnprtvy]\\)\\([-a-z \t_]*\\)\\([0-9][0-9]?\\)"
291 (save-excursion (end-of-line) (point)) t)
292 (format "%2d-%3s"
293 (string-to-int (buffer-substring
294 (match-beginning 4)
295 (match-end 4)))
296 (buffer-substring
297 (match-beginning 2) (match-end 2))))
298 (t "??????"))))
299 " "
300 (save-excursion
301 (if (not (re-search-forward "^From:[ \t]*" nil t))
302 " "
303 (let* ((from (mail-strip-quoted-names
304 (buffer-substring
305 (1- (point))
306 (progn (end-of-line)
307 (skip-chars-backward " \t")
308 (point)))))
309 len mch lo)
310 (if (string-match (concat "^"
311 (regexp-quote (user-login-name))
312 "\\($\\|@\\)")
313 from)
314 (save-excursion
315 (goto-char (point-min))
316 (if (not (re-search-forward "^To:[ \t]*" nil t))
317 nil
318 (setq from
319 (concat "to: "
320 (mail-strip-quoted-names
321 (buffer-substring
322 (point)
323 (progn (end-of-line)
324 (skip-chars-backward " \t")
325 (point)))))))))
326 (setq len (length from))
327 (setq mch (string-match "[@%]" from))
328 (format "%25s"
329 (if (or (not mch) (<= len 25))
330 (substring from (max 0 (- len 25)))
331 (substring from
332 (setq lo (cond ((< (- mch 9) 0) 0)
333 ((< len (+ mch 16))
334 (- len 25))
335 (t (- mch 9))))
336 (min len (+ lo 25))))))))
337 " #"
338 (if (re-search-forward "^Subject:" nil t)
339 (progn (skip-chars-forward " \t")
340 (buffer-substring (point)
341 (progn (end-of-line)
342 (point))))
343 (re-search-forward "[\n][\n]+" nil t)
344 (buffer-substring (point) (progn (end-of-line) (point))))
345 "\n"))
346 \f
347 ;; Simple motion in a summary buffer.
348
349 (defun rmail-summary-next-all (&optional number)
350 (interactive "p")
351 (forward-line (if number number 1))
352 (display-buffer rmail-buffer))
353
354 (defun rmail-summary-previous-all (&optional number)
355 (interactive "p")
356 (forward-line (- (if number number 1)))
357 (display-buffer rmail-buffer))
358
359 (defun rmail-summary-next-msg (&optional number)
360 "Display next non-deleted msg from rmail file.
361 With optional prefix argument NUMBER, moves forward this number of non-deleted
362 messages, or backward if NUMBER is negative."
363 (interactive "p")
364 (forward-line 0)
365 (and (> number 0) (end-of-line))
366 (let ((count (if (< number 0) (- number) number))
367 (search (if (> number 0) 're-search-forward 're-search-backward))
368 (non-del-msg-found nil))
369 (while (and (> count 0) (setq non-del-msg-found
370 (or (funcall search "^....[^D]" nil t)
371 non-del-msg-found)))
372 (setq count (1- count))))
373 (beginning-of-line)
374 (display-buffer rmail-buffer))
375
376 (defun rmail-summary-previous-msg (&optional number)
377 (interactive "p")
378 (rmail-summary-next-msg (- (if number number 1))))
379
380 (defun rmail-summary-next-labeled-message (n labels)
381 "Show next message with LABEL. Defaults to last labels used.
382 With prefix argument N moves forward N messages with these labels."
383 (interactive "p\nsMove to next msg with labels: ")
384 (save-excursion
385 (set-buffer rmail-buffer)
386 (rmail-next-labeled-message n labels)))
387
388 (defun rmail-summary-previous-labeled-message (n labels)
389 "Show previous message with LABEL. Defaults to last labels used.
390 With prefix argument N moves backward N messages with these labels."
391 (interactive "p\nsMove to previous msg with labels: ")
392 (save-excursion
393 (set-buffer rmail-buffer)
394 (rmail-previous-labeled-message n labels)))
395 \f
396 ;; Delete and undelete summary commands.
397
398 (defun rmail-summary-delete-forward (&optional backward)
399 "Delete this message and move to next nondeleted one.
400 Deleted messages stay in the file until the \\[rmail-expunge] command is given.
401 With prefix argument, delete and move backward."
402 (interactive "P")
403 (let (end)
404 (rmail-summary-goto-msg)
405 (pop-to-buffer rmail-buffer)
406 (rmail-delete-forward backward)
407 (pop-to-buffer rmail-summary-buffer)))
408
409 (defun rmail-summary-delete-backward ()
410 "Delete this message and move to previous nondeleted one.
411 Deleted messages stay in the file until the \\[rmail-expunge] command is given."
412 (interactive)
413 (rmail-summary-delete-forward t))
414
415 (defun rmail-summary-mark-deleted (&optional n undel)
416 (and n (rmail-summary-goto-msg n t t))
417 (or (eobp)
418 (let ((buffer-read-only nil))
419 (skip-chars-forward " ")
420 (skip-chars-forward "[0-9]")
421 (if undel
422 (if (looking-at "D")
423 (progn (delete-char 1) (insert " ")))
424 (delete-char 1)
425 (insert "D"))))
426 (beginning-of-line))
427
428 (defun rmail-summary-mark-undeleted (n)
429 (rmail-summary-mark-deleted n t))
430
431 (defun rmail-summary-deleted-p (&optional n)
432 (save-excursion
433 (and n (rmail-summary-goto-msg n nil t))
434 (skip-chars-forward " ")
435 (skip-chars-forward "[0-9]")
436 (looking-at "D")))
437
438 (defun rmail-summary-undelete (&optional arg)
439 "Undelete current message.
440 Optional prefix ARG means undelete ARG previous messages."
441 (interactive "p")
442 (if (/= arg 1)
443 (rmail-summary-undelete-many arg)
444 (let ((buffer-read-only nil))
445 (end-of-line)
446 (cond ((re-search-backward "\\(^ *[0-9]*\\)\\(D\\)" nil t)
447 (replace-match "\\1 ")
448 (rmail-summary-goto-msg)
449 (pop-to-buffer rmail-buffer)
450 (and (rmail-message-deleted-p rmail-current-message)
451 (rmail-undelete-previous-message))
452 (pop-to-buffer rmail-summary-buffer))))))
453
454 (defun rmail-summary-undelete-many (&optional n)
455 "Undelete all deleted msgs, optional prefix arg N means undelete N prev msgs."
456 (interactive "P")
457 (save-excursion
458 (set-buffer rmail-buffer)
459 (let* ((init-msg (if n rmail-current-message rmail-total-messages))
460 (rmail-current-message init-msg)
461 (n (or n rmail-total-messages))
462 (msgs-undeled 0))
463 (while (and (> rmail-current-message 0)
464 (< msgs-undeled n))
465 (if (rmail-message-deleted-p rmail-current-message)
466 (progn (rmail-set-attribute "deleted" nil)
467 (setq msgs-undeled (1+ msgs-undeled))))
468 (setq rmail-current-message (1- rmail-current-message)))
469 (set-buffer rmail-summary-buffer)
470 (setq rmail-current-message init-msg msgs-undeled 0)
471 (while (and (> rmail-current-message 0)
472 (< msgs-undeled n))
473 (if (rmail-summary-deleted-p rmail-current-message)
474 (progn (rmail-summary-mark-undeleted rmail-current-message)
475 (setq msgs-undeled (1+ msgs-undeled))))
476 (setq rmail-current-message (1- rmail-current-message))))
477 (rmail-summary-goto-msg)))
478 \f
479 ;; Rmail Summary mode is suitable only for specially formatted data.
480 (put 'rmail-summary-mode 'mode-class 'special)
481
482 (defun rmail-summary-mode ()
483 "Rmail Summary Mode is invoked from Rmail Mode by using \\<rmail-mode-map>\\[rmail-summary].
484 As commands are issued in the summary buffer, they are applied to the
485 corresponding mail messages in the rmail buffer.
486
487 All normal editing commands are turned off.
488 Instead, all of the Rmail Mode commands are available, plus:
489
490 \\[rmail-summary-undelete-many] Undelete all or prefix arg deleted messages."
491 (interactive)
492 (kill-all-local-variables)
493 (setq major-mode 'rmail-summary-mode)
494 (setq mode-name "RMAIL Summary")
495 (use-local-map rmail-summary-mode-map)
496 (setq truncate-lines t)
497 (setq buffer-read-only t)
498 (set-syntax-table text-mode-syntax-table)
499 (make-local-variable 'rmail-buffer)
500 (make-local-variable 'rmail-total-messages)
501 (make-local-variable 'rmail-current-message)
502 (setq rmail-current-message nil)
503 (make-local-variable 'rmail-summary-redo)
504 (setq rmail-summary-redo nil)
505 (make-local-variable 'revert-buffer-function)
506 (setq revert-buffer-function 'rmail-update-summary)
507 (make-local-variable 'post-command-hook)
508 (add-hook 'post-command-hook 'rmail-summary-rmail-update)
509 (run-hooks 'rmail-summary-mode-hook))
510
511 ;; Show in Rmail the message described by the summary line that point is on,
512 ;; but only if the Rmail buffer is already visible.
513 ;; This is a post-command-hook in summary buffers.
514 (defun rmail-summary-rmail-update ()
515 (if (get-buffer-window rmail-buffer)
516 (let (buffer-read-only)
517 (save-excursion
518 (beginning-of-line)
519 (skip-chars-forward " ")
520 (let ((beg (point))
521 msg-num
522 (buf rmail-buffer))
523 (skip-chars-forward "0-9")
524 (setq msg-num (string-to-int (buffer-substring beg (point))))
525 (or (eq rmail-current-message msg-num)
526 (let (go-where window (owin (selected-window)))
527 (setq rmail-current-message msg-num)
528 (if (= (following-char) ?-)
529 (progn
530 (delete-char 1)
531 (insert " ")))
532 (setq window (display-buffer rmail-buffer))
533 ;; Using save-window-excursion caused the new value
534 ;; of point to get lost.
535 (unwind-protect
536 (progn
537 (select-window window)
538 (rmail-show-message msg-num))
539 (select-window owin)))))))))
540 \f
541 (defvar rmail-summary-mode-map nil)
542
543 (if rmail-summary-mode-map
544 nil
545 (setq rmail-summary-mode-map (make-keymap))
546 (suppress-keymap rmail-summary-mode-map)
547 (define-key rmail-summary-mode-map "a" 'rmail-summary-add-label)
548 (define-key rmail-summary-mode-map "c" 'rmail-summary-continue)
549 (define-key rmail-summary-mode-map "d" 'rmail-summary-delete-forward)
550 (define-key rmail-summary-mode-map "\C-d" 'rmail-summary-delete-backward)
551 (define-key rmail-summary-mode-map "e" 'rmail-summary-edit-current-message)
552 (define-key rmail-summary-mode-map "f" 'rmail-summary-forward)
553 (define-key rmail-summary-mode-map "g" 'rmail-summary-get-new-mail)
554 (define-key rmail-summary-mode-map "h" 'rmail-summary)
555 (define-key rmail-summary-mode-map "i" 'rmail-summary-input)
556 (define-key rmail-summary-mode-map "j" 'rmail-summary-goto-msg)
557 (define-key rmail-summary-mode-map "k" 'rmail-summary-kill-label)
558 (define-key rmail-summary-mode-map "l" 'rmail-summary-by-labels)
559 (define-key rmail-summary-mode-map "\e\C-h" 'rmail-summary)
560 (define-key rmail-summary-mode-map "\e\C-l" 'rmail-summary-by-labels)
561 (define-key rmail-summary-mode-map "\e\C-r" 'rmail-summary-by-recipients)
562 (define-key rmail-summary-mode-map "\e\C-s" 'rmail-summary-by-regexp)
563 (define-key rmail-summary-mode-map "\e\C-t" 'rmail-summary-by-topic)
564 (define-key rmail-summary-mode-map "m" 'rmail-summary-mail)
565 (define-key rmail-summary-mode-map "\M-m" 'rmail-summary-retry-failure)
566 (define-key rmail-summary-mode-map "n" 'rmail-summary-next-msg)
567 (define-key rmail-summary-mode-map "\en" 'rmail-summary-next-all)
568 (define-key rmail-summary-mode-map "\e\C-n" 'rmail-summary-next-labeled-message)
569 (define-key rmail-summary-mode-map "o" 'rmail-summary-output-to-rmail-file)
570 (define-key rmail-summary-mode-map "\C-o" 'rmail-summary-output)
571 (define-key rmail-summary-mode-map "p" 'rmail-summary-previous-msg)
572 (define-key rmail-summary-mode-map "\ep" 'rmail-summary-previous-all)
573 (define-key rmail-summary-mode-map "\e\C-p" 'rmail-summary-previous-labeled-message)
574 (define-key rmail-summary-mode-map "q" 'rmail-summary-quit)
575 (define-key rmail-summary-mode-map "r" 'rmail-summary-reply)
576 (define-key rmail-summary-mode-map "s" 'rmail-summary-expunge-and-save)
577 (define-key rmail-summary-mode-map "\es" 'rmail-summary-search)
578 (define-key rmail-summary-mode-map "t" 'rmail-summary-toggle-header)
579 (define-key rmail-summary-mode-map "u" 'rmail-summary-undelete)
580 (define-key rmail-summary-mode-map "\M-u" 'rmail-summary-undelete-many)
581 (define-key rmail-summary-mode-map "w" 'rmail-summary-wipe)
582 (define-key rmail-summary-mode-map "x" 'rmail-summary-expunge)
583 (define-key rmail-summary-mode-map "." 'rmail-summary-beginning-of-message)
584 (define-key rmail-summary-mode-map "<" 'rmail-summary-first-message)
585 (define-key rmail-summary-mode-map ">" 'rmail-summary-last-message)
586 (define-key rmail-summary-mode-map " " 'rmail-summary-scroll-msg-up)
587 (define-key rmail-summary-mode-map "\177" 'rmail-summary-scroll-msg-down)
588 (define-key rmail-summary-mode-map "?" 'describe-mode)
589 )
590 \f
591 ;;; Menu bar bindings.
592
593 (define-key rmail-summary-mode-map [menu-bar] (make-sparse-keymap))
594
595 (define-key rmail-summary-mode-map [menu-bar classify]
596 (cons "Classify" (make-sparse-keymap "Classify")))
597
598 (define-key rmail-summary-mode-map [menu-bar classify output-inbox]
599 '("Output (inbox)" . rmail-summary-output))
600
601 (define-key rmail-summary-mode-map [menu-bar classify output]
602 '("Output (Rmail)" . rmail-summary-output-to-rmail-file))
603
604 (define-key rmail-summary-mode-map [menu-bar classify kill-label]
605 '("Kill Label" . rmail-summary-kill-label))
606
607 (define-key rmail-summary-mode-map [menu-bar classify add-label]
608 '("Add Label" . rmail-summary-add-label))
609
610 (define-key rmail-summary-mode-map [menu-bar summary]
611 (cons "Summary" (make-sparse-keymap "Summary")))
612
613 (define-key rmail-summary-mode-map [menu-bar summary labels]
614 '("By Labels" . rmail-summary-by-labels))
615
616 (define-key rmail-summary-mode-map [menu-bar summary recipients]
617 '("By Recipients" . rmail-summary-by-recipients))
618
619 (define-key rmail-summary-mode-map [menu-bar summary topic]
620 '("By Topic" . rmail-summary-by-topic))
621
622 (define-key rmail-summary-mode-map [menu-bar summary regexp]
623 '("By Regexp" . rmail-summary-by-regexp))
624
625 (define-key rmail-summary-mode-map [menu-bar summary all]
626 '("All" . rmail-summary))
627
628 (define-key rmail-summary-mode-map [menu-bar mail]
629 (cons "Mail" (make-sparse-keymap "Mail")))
630
631 (define-key rmail-summary-mode-map [menu-bar mail continue]
632 '("Continue" . rmail-summary-continue))
633
634 (define-key rmail-summary-mode-map [menu-bar mail forward]
635 '("Forward" . rmail-summary-forward))
636
637 (define-key rmail-summary-mode-map [menu-bar mail retry]
638 '("Retry" . rmail-summary-retry-failure))
639
640 (define-key rmail-summary-mode-map [menu-bar mail reply]
641 '("Reply" . rmail-summary-reply))
642
643 (define-key rmail-summary-mode-map [menu-bar mail mail]
644 '("Mail" . rmail-summary-mail))
645
646 (define-key rmail-summary-mode-map [menu-bar delete]
647 (cons "Delete" (make-sparse-keymap "Delete")))
648
649 (define-key rmail-summary-mode-map [menu-bar delete expunge/save]
650 '("Expunge/Save" . rmail-summary-expunge-and-save))
651
652 (define-key rmail-summary-mode-map [menu-bar delete expunge]
653 '("Expunge" . rmail-summary-expunge))
654
655 (define-key rmail-summary-mode-map [menu-bar delete undelete]
656 '("Undelete" . rmail-summary-undelete))
657
658 (define-key rmail-summary-mode-map [menu-bar delete delete]
659 '("Delete" . rmail-summary-delete-forward))
660
661 (define-key rmail-summary-mode-map [menu-bar move]
662 (cons "Move" (make-sparse-keymap "Move")))
663
664 (define-key rmail-summary-mode-map [menu-bar move search-back]
665 '("Search Back" . rmail-summary-search-backward))
666
667 (define-key rmail-summary-mode-map [menu-bar move search]
668 '("Search" . rmail-summary-search))
669
670 (define-key rmail-summary-mode-map [menu-bar move previous]
671 '("Previous Nondeleted" . rmail-summary-previous-msg))
672
673 (define-key rmail-summary-mode-map [menu-bar move next]
674 '("Next Nondeleted" . rmail-summary-next-msg))
675
676 (define-key rmail-summary-mode-map [menu-bar move last]
677 '("Last" . rmail-summary-last-message))
678
679 (define-key rmail-summary-mode-map [menu-bar move first]
680 '("First" . rmail-summary-first-message))
681
682 (define-key rmail-summary-mode-map [menu-bar move previous]
683 '("Previous" . rmail-summary-previous-all))
684
685 (define-key rmail-summary-mode-map [menu-bar move next]
686 '("Next" . rmail-summary-next-all))
687 \f
688 (defun rmail-summary-goto-msg (&optional n nowarn skip-rmail)
689 (interactive "P")
690 (if (consp n) (setq n (prefix-numeric-value n)))
691 (if (eobp) (forward-line -1))
692 (beginning-of-line)
693 (let ((buf rmail-buffer)
694 (cur (point))
695 (curmsg (string-to-int
696 (buffer-substring (point)
697 (min (point-max) (+ 5 (point)))))))
698 (if (not n)
699 (setq n curmsg)
700 (if (< n 1)
701 (progn (message "No preceding message")
702 (setq n 1)))
703 (if (> n rmail-total-messages)
704 (progn (message "No following message")
705 (goto-char (point-max))
706 (rmail-summary-goto-msg)))
707 (goto-char (point-min))
708 (if (not (re-search-forward (concat "^ *" (int-to-string n)) nil t))
709 (progn (or nowarn (message "Message %d not found" n))
710 (setq n curmsg)
711 (goto-char cur))))
712 (beginning-of-line)
713 (skip-chars-forward " ")
714 (skip-chars-forward "0-9")
715 (save-excursion (if (= (following-char) ?-)
716 (let ((buffer-read-only nil))
717 (delete-char 1)
718 (insert " "))))
719 (beginning-of-line)
720 (if skip-rmail
721 nil
722 (pop-to-buffer buf)
723 (rmail-show-message n)
724 (pop-to-buffer rmail-summary-buffer))))
725 \f
726 (defun rmail-summary-scroll-msg-up (&optional dist)
727 "Scroll other window forward."
728 (interactive "P")
729 (scroll-other-window dist))
730
731 (defun rmail-summary-scroll-msg-down (&optional dist)
732 "Scroll other window backward."
733 (interactive "P")
734 (scroll-other-window
735 (cond ((eq dist '-) nil)
736 ((null dist) '-)
737 (t (- (prefix-numeric-value dist))))))
738
739 (defun rmail-summary-beginning-of-message ()
740 "Show current message from the beginning."
741 (interactive)
742 (pop-to-buffer rmail-buffer)
743 (beginning-of-buffer)
744 (pop-to-buffer rmail-summary-buffer))
745
746 (defun rmail-summary-quit ()
747 "Quit out of Rmail and Rmail summary."
748 (interactive)
749 (rmail-summary-wipe)
750 (rmail-quit))
751
752 (defun rmail-summary-wipe ()
753 "Kill and wipe away Rmail summary, remaining within Rmail."
754 (interactive)
755 (save-excursion (set-buffer rmail-buffer) (setq rmail-summary-buffer nil))
756 (let ((rmail-wind (get-buffer-window rmail-buffer)))
757 (kill-buffer (current-buffer))
758 ;; Delete window if not only one.
759 (if (not (eq (selected-window) (next-window nil 'no-minibuf)))
760 (delete-window))
761 ;; Switch to the rmail buffer in this window.
762 ;; Select the window with rmail in it, then delete this window.
763 (and rmail-wind (select-window rmail-wind))))
764
765 (defun rmail-summary-expunge ()
766 "Actually erase all deleted messages and recompute summary headers."
767 (interactive)
768 (save-excursion
769 (set-buffer rmail-buffer)
770 (rmail-only-expunge))
771 (rmail-update-summary))
772
773 (defun rmail-summary-expunge-and-save ()
774 "Expunge and save RMAIL file."
775 (interactive)
776 (save-excursion
777 (set-buffer rmail-buffer)
778 (rmail-only-expunge))
779 (rmail-update-summary)
780 (save-excursion
781 (set-buffer rmail-buffer)
782 (save-buffer)))
783
784 (defun rmail-summary-get-new-mail ()
785 "Get new mail and recompute summary headers."
786 (interactive)
787 (let (msg)
788 (save-excursion
789 (set-buffer rmail-buffer)
790 (rmail-get-new-mail)
791 ;; Get the proper new message number.
792 (setq msg rmail-current-message))
793 ;; Make sure that message is displayed.
794 (rmail-summary-goto-msg msg)))
795
796 (defun rmail-summary-input (filename)
797 "Run Rmail on file FILENAME."
798 (interactive "FRun rmail on RMAIL file: ")
799 ;; We switch windows here, then display the other Rmail file there.
800 (pop-to-buffer rmail-buffer)
801 (rmail filename))
802
803 (defun rmail-summary-first-message ()
804 "Show first message in Rmail file from summary buffer."
805 (interactive)
806 (beginning-of-buffer))
807
808 (defun rmail-summary-last-message ()
809 "Show last message in Rmail file from summary buffer."
810 (interactive)
811 (end-of-buffer)
812 (forward-line -1))
813
814 (defvar rmail-summary-edit-map nil)
815 (if rmail-summary-edit-map
816 nil
817 (setq rmail-summary-edit-map
818 (nconc (make-sparse-keymap) (cdr text-mode-map)))
819 (define-key rmail-summary-edit-map "\C-c\C-c" 'rmail-cease-edit)
820 (define-key rmail-summary-edit-map "\C-c\C-]" 'rmail-abort-edit))
821
822 (defun rmail-summary-edit-current-message ()
823 "Edit the contents of this message."
824 (interactive)
825 (pop-to-buffer rmail-buffer)
826 (rmail-edit-current-message)
827 (use-local-map rmail-summary-edit-map))
828
829 (defun rmail-summary-cease-edit ()
830 "Finish editing message, then go back to Rmail summary buffer."
831 (interactive)
832 (rmail-cease-edit)
833 (pop-to-buffer rmail-summary-buffer))
834
835 (defun rmail-summary-abort-edit ()
836 "Abort edit of current message; restore original contents.
837 Go back to summary buffer."
838 (interactive)
839 (rmail-abort-edit)
840 (pop-to-buffer rmail-summary-buffer))
841
842 (defun rmail-summary-search-backward (regexp &optional n)
843 "Show message containing next match for REGEXP.
844 Prefix argument gives repeat count; negative argument means search
845 backwards (through earlier messages).
846 Interactively, empty argument means use same regexp used last time."
847 (interactive
848 (let* ((reversep (>= (prefix-numeric-value current-prefix-arg) 0))
849 (prompt
850 (concat (if reversep "Reverse " "") "Rmail search (regexp): "))
851 regexp)
852 (if rmail-search-last-regexp
853 (setq prompt (concat prompt
854 "(default "
855 rmail-search-last-regexp
856 ") ")))
857 (setq regexp (read-string prompt))
858 (cond ((not (equal regexp ""))
859 (setq rmail-search-last-regexp regexp))
860 ((not rmail-search-last-regexp)
861 (error "No previous Rmail search string")))
862 (list rmail-search-last-regexp
863 (prefix-numeric-value current-prefix-arg))))
864 ;; Don't use save-excursion because that prevents point from moving
865 ;; properly in the summary buffer.
866 (let ((buffer (current-buffer)))
867 (unwind-protect
868 (progn
869 (set-buffer rmail-buffer)
870 (rmail-search regexp (- n)))
871 (set-buffer buffer))))
872
873 (defun rmail-summary-search (regexp &optional n)
874 "Show message containing next match for REGEXP.
875 Prefix argument gives repeat count; negative argument means search
876 backwards (through earlier messages).
877 Interactively, empty argument means use same regexp used last time."
878 (interactive
879 (let* ((reversep (< (prefix-numeric-value current-prefix-arg) 0))
880 (prompt
881 (concat (if reversep "Reverse " "") "Rmail search (regexp): "))
882 regexp)
883 (if rmail-search-last-regexp
884 (setq prompt (concat prompt
885 "(default "
886 rmail-search-last-regexp
887 ") ")))
888 (setq regexp (read-string prompt))
889 (cond ((not (equal regexp ""))
890 (setq rmail-search-last-regexp regexp))
891 ((not rmail-search-last-regexp)
892 (error "No previous Rmail search string")))
893 (list rmail-search-last-regexp
894 (prefix-numeric-value current-prefix-arg))))
895 ;; Don't use save-excursion because that prevents point from moving
896 ;; properly in the summary buffer.
897 (let ((buffer (current-buffer)))
898 (unwind-protect
899 (progn
900 (set-buffer rmail-buffer)
901 (rmail-search regexp n))
902 (set-buffer buffer))))
903
904 (defun rmail-summary-toggle-header ()
905 "Show original message header if pruned header currently shown, or vice versa."
906 (interactive)
907 (save-excursion
908 (set-buffer rmail-buffer)
909 (rmail-toggle-header)))
910
911 (defun rmail-summary-add-label (label)
912 "Add LABEL to labels associated with current Rmail message.
913 Completion is performed over known labels when reading."
914 (interactive (list (save-excursion
915 (set-buffer rmail-buffer)
916 (rmail-read-label "Add label"))))
917 (save-excursion
918 (set-buffer rmail-buffer)
919 (rmail-add-label label)))
920
921 (defun rmail-summary-kill-label (label)
922 "Remove LABEL from labels associated with current Rmail message.
923 Completion is performed over known labels when reading."
924 (interactive (list (save-excursion
925 (set-buffer rmail-buffer)
926 (rmail-read-label "Kill label"))))
927 (save-excursion
928 (set-buffer rmail-buffer)
929 (rmail-set-label label nil)))
930 \f
931 ;;;; *** Rmail Summary Mailing Commands ***
932
933 (defun rmail-summary-mail ()
934 "Send mail in another window.
935 While composing the message, use \\[mail-yank-original] to yank the
936 original message into it."
937 (interactive)
938 (mail-other-window nil nil nil nil nil rmail-buffer)
939 (use-local-map (copy-keymap (current-local-map)))
940 (define-key (current-local-map)
941 "\C-c\C-c" 'rmail-summary-send-and-exit))
942
943 (defun rmail-summary-continue ()
944 "Continue composing outgoing message previously being composed."
945 (interactive)
946 (mail-other-window t))
947
948 (defun rmail-summary-reply (just-sender)
949 "Reply to the current message.
950 Normally include CC: to all other recipients of original message;
951 prefix argument means ignore them.
952 While composing the reply, use \\[mail-yank-original] to yank the
953 original message into it."
954 (interactive "P")
955 (let (mailbuf)
956 (save-window-excursion
957 (set-buffer rmail-buffer)
958 (rmail-reply just-sender)
959 (setq mailbuf (current-buffer)))
960 (pop-to-buffer mailbuf)
961 (use-local-map (copy-keymap (current-local-map)))
962 (define-key (current-local-map)
963 "\C-c\C-c" 'rmail-summary-send-and-exit)))
964
965 (defun rmail-summary-retry-failure ()
966 "Edit a mail message which is based on the contents of the current message.
967 For a message rejected by the mail system, extract the interesting headers and
968 the body of the original message; otherwise copy the current message."
969 (interactive)
970 (let (mailbuf)
971 (save-window-excursion
972 (set-buffer rmail-buffer)
973 (rmail-retry-failure)
974 (setq mailbuf (current-buffer)))
975 (pop-to-buffer mailbuf)
976 (use-local-map (copy-keymap (current-local-map)))
977 (define-key (current-local-map)
978 "\C-c\C-c" 'rmail-summary-send-and-exit)))
979
980 (defun rmail-summary-send-and-exit ()
981 "Send mail reply and return to summary buffer."
982 (interactive)
983 (mail-send-and-exit t))
984
985 (defun rmail-summary-forward ()
986 "Forward the current message to another user."
987 (interactive)
988 (save-excursion
989 (set-buffer rmail-buffer)
990 (rmail-forward)
991 (use-local-map (copy-keymap (current-local-map)))
992 (define-key (current-local-map)
993 "\C-c\C-c" 'rmail-summary-send-and-exit)))
994 \f
995 ;; Summary output commands.
996
997 (defun rmail-summary-output-to-rmail-file ()
998 "Append the current message to an Rmail file named FILE-NAME.
999 If the file does not exist, ask if it should be created.
1000 If file is being visited, the message is appended to the Emacs
1001 buffer visiting that file."
1002 (interactive)
1003 (save-excursion
1004 (set-buffer rmail-buffer)
1005 (call-interactively 'rmail-output-to-rmail-file)))
1006
1007 (defun rmail-summary-output ()
1008 "Append this message to Unix mail file named FILE-NAME."
1009 (interactive)
1010 (save-excursion
1011 (set-buffer rmail-buffer)
1012 (call-interactively 'rmail-output)))
1013
1014 ;;; rmailsum.el ends here