]> code.delx.au - gnu-emacs/blob - lisp/mh-e/mh-letter.el
* mh-compat.el (mh-image-load-path-for-library): Incorporate changes
[gnu-emacs] / lisp / mh-e / mh-letter.el
1 ;;; mh-letter.el --- MH-Letter mode
2
3 ;; Copyright (C) 1993, 1995, 1997,
4 ;; 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
5
6 ;; Author: Bill Wohler <wohler@newt.com>
7 ;; Maintainer: Bill Wohler <wohler@newt.com>
8 ;; Keywords: mail
9 ;; See: mh-e.el
10
11 ;; This file is part of GNU Emacs.
12
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; any later version.
17
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
22
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
25 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 ;; Boston, MA 02110-1301, USA.
27
28 ;;; Commentary:
29
30 ;; Mode for composing and sending a draft message.
31
32 ;; Functions that would ordinarily be in here that are needed by
33 ;; mh-show.el should be placed in the Message Utilities section in
34 ;; mh-utils.el. That will help prevent the loading of this file until
35 ;; a message is actually composed.
36
37 ;;; Change Log:
38
39 ;;; Code:
40
41 (require 'mh-e)
42
43 (require 'gnus-util)
44
45 ;; Dynamically-created functions not found in mh-loaddefs.el.
46 (autoload 'mh-tool-bar-letter-buttons-init "mh-tool-bar")
47 (autoload 'mh-tool-bar-init "mh-tool-bar")
48
49 (autoload 'mml-insert-tag "mml")
50
51 ;;; Variables
52
53 (defvar mh-letter-complete-function-alist
54 '((bcc . mh-alias-letter-expand-alias)
55 (cc . mh-alias-letter-expand-alias)
56 (dcc . mh-alias-letter-expand-alias)
57 (fcc . mh-folder-expand-at-point)
58 (from . mh-alias-letter-expand-alias)
59 (mail-followup-to . mh-alias-letter-expand-alias)
60 (mail-reply-to . mh-alias-letter-expand-alias)
61 (reply-to . mh-alias-letter-expand-alias)
62 (to . mh-alias-letter-expand-alias))
63 "Alist of header fields and completion functions to use.")
64
65 (defvar mh-yank-hooks nil
66 "Obsolete hook for modifying a citation just inserted in the mail buffer.
67
68 Each hook function can find the citation between point and mark.
69 And each hook function should leave point and mark around the
70 citation text as modified.
71
72 This is a normal hook, misnamed for historical reasons. It is
73 semi-obsolete and is only used if `mail-citation-hook' is nil.")
74
75 \f
76
77 ;;; Letter Menu
78
79 (easy-menu-define
80 mh-letter-menu mh-letter-mode-map "Menu for MH-E letter mode."
81 '("Letter"
82 ["Send This Draft" mh-send-letter t]
83 ["Split Current Line" mh-open-line t]
84 ["Check Recipient" mh-check-whom t]
85 ["Yank Current Message" mh-yank-cur-msg t]
86 ["Insert a Message..." mh-insert-letter t]
87 ["Insert Signature" mh-insert-signature t]
88 ("Encrypt/Sign Message"
89 ["Sign Message"
90 mh-mml-secure-message-sign mh-pgp-support-flag]
91 ["Encrypt Message"
92 mh-mml-secure-message-encrypt mh-pgp-support-flag]
93 ["Sign+Encrypt Message"
94 mh-mml-secure-message-signencrypt mh-pgp-support-flag]
95 ["Disable Security"
96 mh-mml-unsecure-message mh-pgp-support-flag]
97 "--"
98 "Security Method"
99 ["PGP (MIME)" (setq mh-mml-method-default "pgpmime")
100 :style radio
101 :selected (equal mh-mml-method-default "pgpmime")]
102 ["PGP" (setq mh-mml-method-default "pgp")
103 :style radio
104 :selected (equal mh-mml-method-default "pgp")]
105 ["S/MIME" (setq mh-mml-method-default "smime")
106 :style radio
107 :selected (equal mh-mml-method-default "smime")]
108 "--"
109 ["Save Method as Default"
110 (customize-save-variable 'mh-mml-method-default mh-mml-method-default) t]
111 )
112 ["Compose Insertion..." mh-compose-insertion t]
113 ["Compose Compressed tar (MH)..."
114 mh-mh-compose-external-compressed-tar t]
115 ["Compose Get File (MH)..." mh-mh-compose-anon-ftp t]
116 ["Compose Forward..." mh-compose-forward t]
117 ;; The next two will have to be merged. But I also need to make sure the
118 ;; user can't mix tags of both types.
119 ["Pull in All Compositions (MH)"
120 mh-mh-to-mime (mh-mh-directive-present-p)]
121 ["Pull in All Compositions (MML)"
122 mh-mml-to-mime (mh-mml-tag-present-p)]
123 ["Revert to Non-MIME Edit (MH)"
124 mh-mh-to-mime-undo (equal mh-compose-insertion 'mh)]
125 ["Kill This Draft" mh-fully-kill-draft t]))
126
127 \f
128
129 ;;; MH-Letter Keys
130
131 ;; If this changes, modify mh-letter-mode-help-messages accordingly, above.
132 (gnus-define-keys mh-letter-mode-map
133 " " mh-letter-complete-or-space
134 "," mh-letter-confirm-address
135 "\C-c?" mh-help
136 "\C-c\C-\\" mh-fully-kill-draft ;if no C-q
137 "\C-c\C-^" mh-insert-signature ;if no C-s
138 "\C-c\C-c" mh-send-letter
139 "\C-c\C-d" mh-insert-identity
140 "\C-c\C-e" mh-mh-to-mime
141 "\C-c\C-f\C-a" mh-to-field
142 "\C-c\C-f\C-b" mh-to-field
143 "\C-c\C-f\C-c" mh-to-field
144 "\C-c\C-f\C-d" mh-to-field
145 "\C-c\C-f\C-f" mh-to-fcc
146 "\C-c\C-f\C-l" mh-to-field
147 "\C-c\C-f\C-m" mh-to-field
148 "\C-c\C-f\C-r" mh-to-field
149 "\C-c\C-f\C-s" mh-to-field
150 "\C-c\C-f\C-t" mh-to-field
151 "\C-c\C-fa" mh-to-field
152 "\C-c\C-fb" mh-to-field
153 "\C-c\C-fc" mh-to-field
154 "\C-c\C-fd" mh-to-field
155 "\C-c\C-ff" mh-to-fcc
156 "\C-c\C-fl" mh-to-field
157 "\C-c\C-fm" mh-to-field
158 "\C-c\C-fr" mh-to-field
159 "\C-c\C-fs" mh-to-field
160 "\C-c\C-ft" mh-to-field
161 "\C-c\C-i" mh-insert-letter
162 "\C-c\C-m\C-e" mh-mml-secure-message-encrypt
163 "\C-c\C-m\C-f" mh-compose-forward
164 "\C-c\C-m\C-g" mh-mh-compose-anon-ftp
165 "\C-c\C-m\C-i" mh-compose-insertion
166 "\C-c\C-m\C-m" mh-mml-to-mime
167 "\C-c\C-m\C-n" mh-mml-unsecure-message
168 "\C-c\C-m\C-s" mh-mml-secure-message-sign
169 "\C-c\C-m\C-t" mh-mh-compose-external-compressed-tar
170 "\C-c\C-m\C-u" mh-mh-to-mime-undo
171 "\C-c\C-m\C-x" mh-mh-compose-external-type
172 "\C-c\C-mee" mh-mml-secure-message-encrypt
173 "\C-c\C-mes" mh-mml-secure-message-signencrypt
174 "\C-c\C-mf" mh-compose-forward
175 "\C-c\C-mg" mh-mh-compose-anon-ftp
176 "\C-c\C-mi" mh-compose-insertion
177 "\C-c\C-mm" mh-mml-to-mime
178 "\C-c\C-mn" mh-mml-unsecure-message
179 "\C-c\C-mse" mh-mml-secure-message-signencrypt
180 "\C-c\C-mss" mh-mml-secure-message-sign
181 "\C-c\C-mt" mh-mh-compose-external-compressed-tar
182 "\C-c\C-mu" mh-mh-to-mime-undo
183 "\C-c\C-mx" mh-mh-compose-external-type
184 "\C-c\C-o" mh-open-line
185 "\C-c\C-q" mh-fully-kill-draft
186 "\C-c\C-s" mh-insert-signature
187 "\C-c\C-t" mh-letter-toggle-header-field-display
188 "\C-c\C-w" mh-check-whom
189 "\C-c\C-y" mh-yank-cur-msg
190 "\C-c\M-d" mh-insert-auto-fields
191 "\M-\t" mh-letter-complete
192 "\t" mh-letter-next-header-field-or-indent
193 [backtab] mh-letter-previous-header-field)
194
195 ;; "C-c /" prefix is used in mh-letter-mode by pgp.el and mailcrypt.el.
196
197 \f
198
199 ;;; MH-Letter Help Messages
200
201 ;; Group messages logically, more or less.
202 (defvar mh-letter-mode-help-messages
203 '((nil
204 "Send letter: \\[mh-send-letter] "
205 "Open line: \\[mh-open-line]\n"
206 "Kill letter: \\[mh-fully-kill-draft] "
207 "Check recipients: \\[mh-check-whom]\n\n"
208 "Insert:\n"
209 " Current message: \\[mh-yank-cur-msg]\n"
210 " Attachment: \\[mh-compose-insertion]\n"
211 " Message to forward: \\[mh-compose-forward]\n"
212 " Signature: \\[mh-insert-signature]\n\n"
213 "Security:\n"
214 " Encrypt message: \\[mh-mml-secure-message-encrypt]\n"
215 " Sign message: \\[mh-mml-secure-message-sign]\n"
216 " Sign+Encrypt message: \\[mh-mml-secure-message-signencrypt]"))
217 "Key binding cheat sheet.
218
219 This is an associative array which is used to show the most
220 common commands. The key is a prefix char. The value is one or
221 more strings which are concatenated together and displayed in the
222 minibuffer if ? is pressed after the prefix character. The
223 special key nil is used to display the non-prefixed commands.
224
225 The substitutions described in `substitute-command-keys' are
226 performed as well.")
227
228 \f
229
230 ;;; MH-Letter Font Lock
231
232 (defvar mh-letter-font-lock-keywords
233 `(,@(mh-show-font-lock-keywords-with-cite)
234 (mh-font-lock-field-data
235 (1 'mh-letter-header-field prepend t)))
236 "Additional expressions to highlight in MH-Letter buffers.")
237
238 (defun mh-font-lock-field-data (limit)
239 "Find header field region between point and LIMIT."
240 (and (< (point) (mh-letter-header-end))
241 (< (point) limit)
242 (let ((end (min limit (mh-letter-header-end)))
243 (point (point))
244 data-end data-begin field)
245 (end-of-line)
246 (setq data-end (if (re-search-forward "^[^ \t]" end t)
247 (match-beginning 0)
248 end))
249 (goto-char (1- data-end))
250 (if (not (re-search-backward "\\(^[^ \t][^:]*\\):[ \t]*" nil t))
251 (setq data-begin (point-min))
252 (setq data-begin (match-end 0))
253 (setq field (match-string 1)))
254 (setq data-begin (max point data-begin))
255 (goto-char (if (equal point data-end) (1+ data-end) data-end))
256 (cond ((and field (mh-letter-skipped-header-field-p field))
257 (set-match-data nil)
258 nil)
259 (t (set-match-data
260 (list data-begin data-end data-begin data-end))
261 t)))))
262
263 (defun mh-letter-header-end ()
264 "Find the end of the message header.
265 This function is to be used only for font locking. It works by
266 searching for `mh-mail-header-separator' in the buffer."
267 (save-excursion
268 (goto-char (point-min))
269 (cond ((equal mh-mail-header-separator "") (point-min))
270 ((search-forward (format "\n%s\n" mh-mail-header-separator) nil t)
271 (mh-line-beginning-position 0))
272 (t (point-min)))))
273
274 \f
275
276 ;;; MH-Letter Mode
277
278 (defvar mh-letter-buttons-init-flag nil)
279
280 ;; Shush compiler.
281 (eval-when-compile (mh-do-in-xemacs (defvar font-lock-defaults)))
282
283 ;; Ensure new buffers won't get this mode if default-major-mode is nil.
284 (put 'mh-letter-mode 'mode-class 'special)
285
286 ;;;###mh-autoload
287 (define-derived-mode mh-letter-mode mail-mode "MH-Letter"
288 "Mode for composing letters in MH-E\\<mh-letter-mode-map>.
289
290 When you have finished composing, type \\[mh-send-letter] to send
291 the message using the MH mail handling system.
292
293 There are two types of tags used by MH-E when composing MIME
294 messages: MML and MH. The option `mh-compose-insertion' controls
295 what type of tags are inserted by MH-E commands. These tags can
296 be converted to MIME body parts by running \\[mh-mh-to-mime] for
297 MH-style directives or \\[mh-mml-to-mime] for MML tags.
298
299 Options that control this mode can be changed with
300 \\[customize-group]; specify the \"mh-compose\" group.
301
302 When a message is composed, the hooks `text-mode-hook',
303 `mail-mode-hook', and `mh-letter-mode-hook' are run (in that
304 order).
305
306 \\{mh-letter-mode-map}"
307 (mh-find-path)
308 (make-local-variable 'mh-send-args)
309 (make-local-variable 'mh-annotate-char)
310 (make-local-variable 'mh-annotate-field)
311 (make-local-variable 'mh-previous-window-config)
312 (make-local-variable 'mh-sent-from-folder)
313 (make-local-variable 'mh-sent-from-msg)
314 (mh-do-in-gnu-emacs
315 (unless mh-letter-buttons-init-flag
316 (let* ((load-path (mh-image-load-path-for-library "mh-e" "mh-logo.xpm"))
317 (image-load-path (cons (car load-path) image-load-path)))
318 (mh-tool-bar-letter-buttons-init)
319 (setq mh-letter-buttons-init-flag t)))
320 (set (make-local-variable 'tool-bar-map) mh-letter-tool-bar-map))
321 (mh-do-in-xemacs
322 (mh-tool-bar-init :letter))
323 ;; Set the local value of mh-mail-header-separator according to what is
324 ;; present in the buffer...
325 (set (make-local-variable 'mh-mail-header-separator)
326 (save-excursion
327 (goto-char (mh-mail-header-end))
328 (buffer-substring-no-properties (point) (mh-line-end-position))))
329 (make-local-variable 'mail-header-separator)
330 (setq mail-header-separator mh-mail-header-separator) ;override sendmail.el
331 (mh-set-help mh-letter-mode-help-messages)
332 (setq buffer-invisibility-spec '((vanish . t) t))
333 (set (make-local-variable 'line-move-ignore-invisible) t)
334
335 ;; Enable undo since a show-mode buffer might have been reused.
336 (buffer-enable-undo)
337 (make-local-variable 'font-lock-defaults)
338 (cond
339 ((or (equal mh-highlight-citation-style 'font-lock)
340 (equal mh-highlight-citation-style 'gnus))
341 ;; Let's use font-lock even if gnus is used in show-mode. The reason
342 ;; is that gnus uses static text properties which are not appropriate
343 ;; for a buffer that will be edited. So the choice here is either fontify
344 ;; the citations and header...
345 (setq font-lock-defaults '(mh-letter-font-lock-keywords t)))
346 (t
347 ;; ...or the header only
348 (setq font-lock-defaults '((mh-show-font-lock-keywords) t))))
349 (easy-menu-add mh-letter-menu)
350 ;; Maybe we want to use the existing Mail menu from mail-mode in
351 ;; 9.0; in the mean time, let's remove it since the redundancy will
352 ;; only produce confusion.
353 (define-key mh-letter-mode-map [menu-bar mail] 'undefined)
354 (mh-do-in-xemacs (easy-menu-remove mail-menubar-menu))
355 (setq fill-column mh-letter-fill-column)
356 ;; If text-mode-hook turned on auto-fill, tune it for messages
357 (when auto-fill-function
358 (make-local-variable 'auto-fill-function)
359 (setq auto-fill-function 'mh-auto-fill-for-letter)))
360
361 \f
362
363 ;;; MH-Letter Commands
364
365 ;; Alphabetical.
366 ;; See also mh-comp.el and mh-mime.el.
367
368 (defun mh-check-whom ()
369 "Verify recipients, showing expansion of any aliases.
370
371 This command expands aliases so you can check the actual address(es)
372 in the alias. A new buffer named \"*MH-E Recipients*\" is created with
373 the output of \"whom\"."
374 (interactive)
375 (let ((file-name buffer-file-name))
376 (save-buffer)
377 (message "Checking recipients...")
378 (mh-in-show-buffer (mh-recipients-buffer)
379 (bury-buffer (current-buffer))
380 (erase-buffer)
381 (mh-exec-cmd-output "whom" t file-name))
382 (message "Checking recipients...done")))
383
384 (defun mh-insert-letter (folder message verbatim)
385 "Insert a message.
386
387 This command prompts you for the FOLDER and MESSAGE number, which
388 defaults to the current message in that folder. It then inserts
389 the message, indented by `mh-ins-buf-prefix' (\"> \") unless
390 `mh-yank-behavior' is set to one of the supercite flavors in
391 which case supercite is used to format the message. Certain
392 undesirable header fields (see
393 `mh-invisible-header-fields-compiled') are removed before
394 insertion.
395
396 If given a prefix argument VERBATIM, the header is left intact, the
397 message is not indented, and \"> \" is not inserted before each line.
398 This command leaves the mark before the letter and point after it."
399 (interactive
400 (let* ((folder
401 (mh-prompt-for-folder "Message from"
402 mh-sent-from-folder nil))
403 (default
404 (if (and (equal folder mh-sent-from-folder)
405 (numberp mh-sent-from-msg))
406 mh-sent-from-msg
407 (nth 0 (mh-translate-range folder "cur"))))
408 (message
409 (read-string (concat "Message number"
410 (or (and default
411 (format " (default %d): " default))
412 ": ")))))
413 (list folder message current-prefix-arg)))
414 (save-restriction
415 (narrow-to-region (point) (point))
416 (let ((start (point-min)))
417 (if (and (equal message "") (numberp mh-sent-from-msg))
418 (setq message (int-to-string mh-sent-from-msg)))
419 (insert-file-contents
420 (expand-file-name message (mh-expand-file-name folder)))
421 (when (not verbatim)
422 (mh-clean-msg-header start mh-invisible-header-fields-compiled nil)
423 (goto-char (point-max)) ;Needed for sc-cite-original
424 (push-mark) ;Needed for sc-cite-original
425 (goto-char (point-min)) ;Needed for sc-cite-original
426 (mh-insert-prefix-string mh-ins-buf-prefix)))))
427
428 ;;;###mh-autoload
429 (defun mh-insert-signature (&optional file)
430 "Insert signature in message.
431
432 This command inserts your signature at the current cursor location.
433
434 By default, the text of your signature is taken from the file
435 \"~/.signature\". You can read from other sources by changing the
436 option `mh-signature-file-name'.
437
438 A signature separator (\"-- \") will be added if the signature block
439 does not contain one and `mh-signature-separator-flag' is on.
440
441 The hook `mh-insert-signature-hook' is run after the signature is
442 inserted. Hook functions may access the actual name of the file or the
443 function used to insert the signature with `mh-signature-file-name'.
444
445 The signature can also be inserted using Identities (see
446 `mh-identity-list').
447
448 In a program, you can pass in a signature FILE."
449 (interactive)
450 (save-excursion
451 (insert "\n")
452 (let ((mh-signature-file-name (or file mh-signature-file-name))
453 (mh-mh-p (mh-mh-directive-present-p))
454 (mh-mml-p (mh-mml-tag-present-p)))
455 (save-restriction
456 (narrow-to-region (point) (point))
457 (cond
458 ((mh-file-is-vcard-p mh-signature-file-name)
459 (if (equal mh-compose-insertion 'mml)
460 (insert "<#part type=\"text/x-vcard\" filename=\""
461 mh-signature-file-name
462 "\" disposition=inline description=VCard>\n<#/part>")
463 (insert "#text/x-vcard; name=\""
464 (file-name-nondirectory mh-signature-file-name)
465 "\" [VCard] " (expand-file-name mh-signature-file-name))))
466 (t
467 (cond
468 (mh-mh-p
469 (insert "#\n" "Content-Description: Signature\n"))
470 (mh-mml-p
471 (mml-insert-tag 'part 'type "text/plain" 'disposition "inline"
472 'description "Signature")))
473 (cond ((null mh-signature-file-name))
474 ((and (stringp mh-signature-file-name)
475 (file-readable-p mh-signature-file-name))
476 (insert-file-contents mh-signature-file-name))
477 ((functionp mh-signature-file-name)
478 (funcall mh-signature-file-name)))))
479 (save-restriction
480 (widen)
481 (run-hooks 'mh-insert-signature-hook))
482 (goto-char (point-min))
483 (when (and (not (mh-file-is-vcard-p mh-signature-file-name))
484 mh-signature-separator-flag
485 (> (point-max) (point-min))
486 (not (mh-signature-separator-p)))
487 (cond (mh-mh-p
488 (forward-line 2))
489 (mh-mml-p
490 (forward-line 1)))
491 (insert mh-signature-separator))
492 (if (not (> (point-max) (point-min)))
493 (message "No signature found")))))
494 (force-mode-line-update))
495
496 (defun mh-letter-complete (arg)
497 "Perform completion on header field or word preceding point.
498
499 If the field contains addresses (for example, \"To:\" or \"Cc:\")
500 or folders (for example, \"Fcc:\") then this command will provide
501 alias completion. In the body of the message, this command runs
502 `mh-letter-complete-function' instead, which is set to
503 `ispell-complete-word' by default. This command takes a prefix
504 argument ARG that is passed to the
505 `mh-letter-complete-function'."
506 (interactive "P")
507 (let ((func nil))
508 (cond ((not (mh-in-header-p))
509 (funcall mh-letter-complete-function arg))
510 ((setq func (cdr (assoc (mh-letter-header-field-at-point)
511 mh-letter-complete-function-alist)))
512 (funcall func))
513 (t (funcall mh-letter-complete-function arg)))))
514
515 (defun mh-letter-complete-or-space (arg)
516 "Perform completion or insert space.
517
518 Turn on the option `mh-compose-space-does-completion-flag' to use
519 this command to perform completion in the header. Otherwise, a
520 space is inserted; use a prefix argument ARG to specify more than
521 one space."
522 (interactive "p")
523 (let ((func nil)
524 (end-of-prev (save-excursion
525 (goto-char (mh-beginning-of-word))
526 (mh-beginning-of-word -1))))
527 (cond ((not mh-compose-space-does-completion-flag)
528 (self-insert-command arg))
529 ((not (mh-in-header-p)) (self-insert-command arg))
530 ((> (point) end-of-prev) (self-insert-command arg))
531 ((setq func (cdr (assoc (mh-letter-header-field-at-point)
532 mh-letter-complete-function-alist)))
533 (funcall func))
534 (t (self-insert-command arg)))))
535
536 (defun mh-letter-confirm-address ()
537 "Flash alias expansion.
538
539 Addresses are separated by a comma\; when you press the comma,
540 this command flashes the alias expansion in the minibuffer if
541 `mh-alias-flash-on-comma' is turned on."
542 (interactive)
543 (cond ((not (mh-in-header-p)) (self-insert-command 1))
544 ((eq (cdr (assoc (mh-letter-header-field-at-point)
545 mh-letter-complete-function-alist))
546 'mh-alias-letter-expand-alias)
547 (mh-alias-reload-maybe)
548 (mh-alias-minibuffer-confirm-address))
549 (t (self-insert-command 1))))
550
551 (defun mh-letter-next-header-field-or-indent (arg)
552 "Cycle to next field.
553
554 Within the header of the message, this command moves between
555 fields that are highlighted with the face
556 `mh-letter-header-field', skipping those fields listed in
557 `mh-compose-skipped-header-fields'. After the last field, this
558 command then moves point to the message body before cycling back
559 to the first field. If point is already past the first line of
560 the message body, then this command indents by calling
561 `indent-relative' with the given prefix argument ARG."
562 (interactive "P")
563 (let ((header-end (save-excursion
564 (goto-char (mh-mail-header-end))
565 (forward-line)
566 (point))))
567 (if (> (point) header-end)
568 (indent-relative arg)
569 (mh-letter-next-header-field))))
570
571 (defun mh-letter-previous-header-field ()
572 "Cycle to the previous header field.
573
574 This command moves backwards between the fields and cycles to the
575 body of the message after the first field. Unlike the command
576 \\[mh-letter-next-header-field-or-indent], it will always take
577 point to the last field from anywhere in the body."
578 (interactive)
579 (let ((header-end (mh-mail-header-end)))
580 (if (>= (point) header-end)
581 (goto-char header-end)
582 (mh-header-field-beginning))
583 (cond ((re-search-backward mh-letter-header-field-regexp nil t)
584 (if (mh-letter-skipped-header-field-p (match-string 1))
585 (mh-letter-previous-header-field)
586 (goto-char (match-end 0))
587 (mh-letter-skip-leading-whitespace-in-header-field)))
588 (t (goto-char header-end)
589 (forward-line)))))
590
591 (defun mh-open-line ()
592 "Insert a newline and leave point before it.
593
594 This command is similar to the command \\[open-line] in that it
595 inserts a newline after point. It differs in that it also inserts
596 the right number of quoting characters and spaces so that the
597 next line begins in the same column as it was. This is useful
598 when breaking up paragraphs in replies."
599 (interactive)
600 (let ((column (current-column))
601 (prefix (mh-current-fill-prefix)))
602 (if (> (length prefix) column)
603 (message "Sorry, point seems to be within the line prefix")
604 (newline 2)
605 (insert prefix)
606 (while (> column (current-column))
607 (insert " "))
608 (forward-line -1))))
609
610 (defun mh-to-fcc (&optional folder)
611 "Move to \"Fcc:\" header field.
612
613 This command will prompt you for the FOLDER name in which to file
614 a copy of the draft."
615 (interactive (list (mh-prompt-for-folder
616 "Fcc"
617 (or (and mh-default-folder-for-message-function
618 (save-excursion
619 (goto-char (point-min))
620 (funcall
621 mh-default-folder-for-message-function)))
622 "")
623 t)))
624 (let ((last-input-char ?\C-f))
625 (expand-abbrev)
626 (save-excursion
627 (mh-to-field)
628 (insert (if (mh-folder-name-p folder)
629 (substring folder 1)
630 folder)))))
631
632 (defvar mh-to-field-choices '(("a" . "Mail-Reply-To:")
633 ("b" . "Bcc:")
634 ("c" . "Cc:")
635 ("d" . "Dcc:")
636 ("f" . "Fcc:")
637 ("l" . "Mail-Followup-To:")
638 ("m" . "From:")
639 ("r" . "Reply-To:")
640 ("s" . "Subject:")
641 ("t" . "To:"))
642 "Alist of (final-character . field-name) choices for `mh-to-field'.")
643
644 (defun mh-to-field ()
645 "Move to specified header field.
646
647 The field is indicated by the previous keystroke (the last
648 keystroke of the command) according to the list in the variable
649 `mh-to-field-choices'.
650 Create the field if it does not exist.
651 Set the mark to point before moving."
652 (interactive)
653 (expand-abbrev)
654 (let ((target (cdr (or (assoc (char-to-string (logior last-input-char ?`))
655 mh-to-field-choices)
656 ;; also look for a char for version 4 compat
657 (assoc (logior last-input-char ?`)
658 mh-to-field-choices))))
659 (case-fold-search t))
660 (push-mark)
661 (cond ((mh-position-on-field target)
662 (let ((eol (point)))
663 (skip-chars-backward " \t")
664 (delete-region (point) eol))
665 (if (and (not (eq (logior last-input-char ?`) ?s))
666 (save-excursion
667 (backward-char 1)
668 (not (looking-at "[:,]"))))
669 (insert ", ")
670 (insert " ")))
671 (t
672 (if (mh-position-on-field "To:")
673 (forward-line 1))
674 (insert (format "%s \n" target))
675 (backward-char 1)))))
676
677 ;;;###mh-autoload
678 (defun mh-yank-cur-msg ()
679 "Insert the current message into the draft buffer.
680
681 It is often useful to insert a snippet of text from a letter that
682 someone mailed to provide some context for your reply. This
683 command does this by adding an attribution, yanking a portion of
684 text from the message to which you're replying, and inserting
685 `mh-ins-buf-prefix' (`> ') before each line.
686
687 The attribution consists of the sender's name and email address
688 followed by the content of the option
689 `mh-extract-from-attribution-verb'.
690
691 You can also turn on the option
692 `mh-delete-yanked-msg-window-flag' to delete the window
693 containing the original message after yanking it to make more
694 room on your screen for your reply.
695
696 You can control how the message to which you are replying is
697 yanked into your reply using `mh-yank-behavior'.
698
699 If this isn't enough, you can gain full control over the
700 appearance of the included text by setting `mail-citation-hook'
701 to a function that modifies it. For example, if you set this hook
702 to `trivial-cite' (which is NOT part of Emacs), set
703 `mh-yank-behavior' to \"Body and Header\" (see URL
704 `http://shasta.cs.uiuc.edu/~lrclause/tc.html').
705
706 Note that if `mail-citation-hook' is set, `mh-ins-buf-prefix' is
707 not inserted. If the option `mh-yank-behavior' is set to one of
708 the supercite flavors, the hook `mail-citation-hook' is ignored
709 and `mh-ins-buf-prefix' is not inserted."
710 (interactive)
711 (if (and mh-sent-from-folder
712 (save-excursion (set-buffer mh-sent-from-folder) mh-show-buffer)
713 (save-excursion (set-buffer mh-sent-from-folder)
714 (get-buffer mh-show-buffer))
715 mh-sent-from-msg)
716 (let ((to-point (point))
717 (to-buffer (current-buffer)))
718 (set-buffer mh-sent-from-folder)
719 (if mh-delete-yanked-msg-window-flag
720 (delete-windows-on mh-show-buffer))
721 (set-buffer mh-show-buffer) ; Find displayed message
722 (let* ((from-attr (mh-extract-from-attribution))
723 (yank-region (mh-mark-active-p nil))
724 (mh-ins-str
725 (cond ((and yank-region
726 (or (eq 'supercite mh-yank-behavior)
727 (eq 'autosupercite mh-yank-behavior)
728 (eq t mh-yank-behavior)))
729 ;; supercite needs the full header
730 (concat
731 (buffer-substring (point-min) (mh-mail-header-end))
732 "\n"
733 (buffer-substring (region-beginning) (region-end))))
734 (yank-region
735 (buffer-substring (region-beginning) (region-end)))
736 ((or (eq 'body mh-yank-behavior)
737 (eq 'attribution mh-yank-behavior)
738 (eq 'autoattrib mh-yank-behavior))
739 (buffer-substring
740 (save-excursion
741 (goto-char (point-min))
742 (mh-goto-header-end 1)
743 (point))
744 (point-max)))
745 ((or (eq 'supercite mh-yank-behavior)
746 (eq 'autosupercite mh-yank-behavior)
747 (eq t mh-yank-behavior))
748 (buffer-substring (point-min) (point-max)))
749 (t
750 (buffer-substring (point) (point-max))))))
751 (set-buffer to-buffer)
752 (save-restriction
753 (narrow-to-region to-point to-point)
754 (insert (mh-filter-out-non-text mh-ins-str))
755 (goto-char (point-max)) ;Needed for sc-cite-original
756 (push-mark) ;Needed for sc-cite-original
757 (goto-char (point-min)) ;Needed for sc-cite-original
758 (mh-insert-prefix-string mh-ins-buf-prefix)
759 (when (or (eq 'attribution mh-yank-behavior)
760 (eq 'autoattrib mh-yank-behavior))
761 (insert from-attr)
762 (mh-identity-insert-attribution-verb nil)
763 (insert "\n\n"))
764 ;; If the user has selected a region, he has already "edited" the
765 ;; text, so leave the cursor at the end of the yanked text. In
766 ;; either case, leave a mark at the opposite end of the included
767 ;; text to make it easy to jump or delete to the other end of the
768 ;; text.
769 (push-mark)
770 (goto-char (point-max))
771 (if (null yank-region)
772 (mh-exchange-point-and-mark-preserving-active-mark)))))
773 (error "There is no current message")))
774
775 \f
776
777 ;;; Support Routines
778
779 (defun mh-auto-fill-for-letter ()
780 "Perform auto-fill for message.
781 Header is treated specially by inserting a tab before continuation
782 lines."
783 (if (mh-in-header-p)
784 (let ((fill-prefix "\t"))
785 (do-auto-fill))
786 (do-auto-fill)))
787
788 (defun mh-filter-out-non-text (string)
789 "Return STRING but without adornments such as MIME buttons and smileys."
790 (with-temp-buffer
791 ;; Insert the string to filter
792 (insert string)
793 (goto-char (point-min))
794
795 ;; Remove the MIME buttons
796 (let ((can-move-forward t)
797 (in-button nil))
798 (while can-move-forward
799 (cond ((and (not (get-text-property (point) 'mh-data))
800 in-button)
801 (delete-region (1- (point)) (point))
802 (setq in-button nil))
803 ((get-text-property (point) 'mh-data)
804 (delete-region (point)
805 (save-excursion (forward-line) (point)))
806 (setq in-button t))
807 (t (setq can-move-forward (= (forward-line) 0))))))
808
809 ;; Return the contents without properties... This gets rid of emphasis
810 ;; and smileys
811 (buffer-substring-no-properties (point-min) (point-max))))
812
813 (defun mh-current-fill-prefix ()
814 "Return the `fill-prefix' on the current line as a string."
815 (save-excursion
816 (beginning-of-line)
817 ;; This assumes that the major-mode sets up adaptive-fill-regexp
818 ;; correctly such as mh-letter-mode or sendmail.el's mail-mode. But
819 ;; perhaps I should use the variable and simply inserts its value here,
820 ;; and set it locally in a let scope. --psg
821 (if (re-search-forward adaptive-fill-regexp nil t)
822 (match-string 0)
823 "")))
824
825 ;;;###mh-autoload
826 (defun mh-letter-next-header-field ()
827 "Cycle to the next header field.
828 If we are at the last header field go to the start of the message
829 body."
830 (let ((header-end (mh-mail-header-end)))
831 (cond ((>= (point) header-end) (goto-char (point-min)))
832 ((< (point) (progn
833 (beginning-of-line)
834 (re-search-forward mh-letter-header-field-regexp
835 (mh-line-end-position) t)
836 (point)))
837 (beginning-of-line))
838 (t (end-of-line)))
839 (cond ((re-search-forward mh-letter-header-field-regexp header-end t)
840 (if (mh-letter-skipped-header-field-p (match-string 1))
841 (mh-letter-next-header-field)
842 (mh-letter-skip-leading-whitespace-in-header-field)))
843 (t (goto-char header-end)
844 (forward-line)))))
845
846 ;;;###mh-autoload
847 (defun mh-position-on-field (field &optional ignored)
848 "Move to the end of the FIELD in the header.
849 Move to end of entire header if FIELD not found.
850 Returns non-nil iff FIELD was found.
851 The optional second arg is for pre-version 4 compatibility and is
852 IGNORED."
853 (cond ((mh-goto-header-field field)
854 (mh-header-field-end)
855 t)
856 ((mh-goto-header-end 0)
857 nil)))
858
859 (defun mh-letter-header-field-at-point ()
860 "Return the header field name at point.
861 A symbol is returned whose name is the string obtained by
862 downcasing the field name."
863 (save-excursion
864 (end-of-line)
865 (and (re-search-backward mh-letter-header-field-regexp nil t)
866 (intern (downcase (match-string 1))))))
867
868 (defun mh-folder-expand-at-point ()
869 "Do folder name completion in Fcc header field."
870 (let* ((end (point))
871 (beg (mh-beginning-of-word))
872 (folder (buffer-substring beg end))
873 (leading-plus (and (> (length folder) 0) (equal (aref folder 0) ?+)))
874 (last-slash (mh-search-from-end ?/ folder))
875 (prefix (and last-slash (substring folder 0 last-slash)))
876 (choices (mapcar #'(lambda (x)
877 (list (cond (prefix (format "%s/%s" prefix x))
878 (leading-plus (format "+%s" x))
879 (t x))))
880 (mh-folder-completion-function folder nil t))))
881 (mh-complete-word folder choices beg end)))
882
883 ;;;###mh-autoload
884 (defun mh-complete-word (word choices begin end)
885 "Complete WORD at from CHOICES.
886 Any match found replaces the text from BEGIN to END."
887 (let ((completion (try-completion word choices))
888 (completions-buffer "*Completions*"))
889 (cond ((eq completion t)
890 (ignore-errors
891 (kill-buffer completions-buffer))
892 (message "Completed: %s" word))
893 ((null completion)
894 (ignore-errors
895 (kill-buffer completions-buffer))
896 (message "No completion for %s" word))
897 ((stringp completion)
898 (if (equal word completion)
899 (with-output-to-temp-buffer completions-buffer
900 (mh-display-completion-list (all-completions word choices)
901 word))
902 (ignore-errors
903 (kill-buffer completions-buffer))
904 (delete-region begin end)
905 (insert completion))))))
906
907 (defun mh-file-is-vcard-p (file)
908 "Return t if FILE is a .vcf vcard."
909 (let ((case-fold-search t))
910 (and (stringp file)
911 (file-exists-p file)
912 (or (and (not (mh-have-file-command))
913 (not (null (string-match "\.vcf$" file))))
914 (string-equal "text/x-vcard" (mh-file-mime-type file))))))
915
916 ;;;###mh-autoload
917 (defun mh-letter-toggle-header-field-display-button (event)
918 "Toggle header field display at location of EVENT.
919 This function does the same thing as
920 `mh-letter-toggle-header-field-display' except that it is
921 callable from a mouse button."
922 (interactive "e")
923 (mh-do-at-event-location event
924 (mh-letter-toggle-header-field-display nil)))
925
926 (defun mh-extract-from-attribution ()
927 "Extract phrase or comment from From header field."
928 (save-excursion
929 (if (not (mh-goto-header-field "From: "))
930 nil
931 (skip-chars-forward " ")
932 (cond
933 ((looking-at "\"\\([^\"\n]+\\)\" \\(<.+>\\)")
934 (format "%s %s " (match-string 1)(match-string 2)))
935 ((looking-at "\\([^<\n]+<.+>\\)$")
936 (format "%s " (match-string 1)))
937 ((looking-at "\\([^ ]+@[^ ]+\\) +(\\(.+\\))$")
938 (format "%s <%s> " (match-string 2)(match-string 1)))
939 ((looking-at " *\\(.+\\)$")
940 (format "%s " (match-string 1)))))))
941
942 (defun mh-insert-prefix-string (mh-ins-string)
943 "Insert prefix string before each line in buffer.
944 The inserted letter is cited using `sc-cite-original' if
945 `mh-yank-behavior' is one of 'supercite or 'autosupercite.
946 Otherwise, simply insert MH-INS-STRING before each line."
947 (goto-char (point-min))
948 (cond ((or (eq mh-yank-behavior 'supercite)
949 (eq mh-yank-behavior 'autosupercite))
950 (sc-cite-original))
951 (mail-citation-hook
952 (run-hooks 'mail-citation-hook))
953 (mh-yank-hooks ;old hook name
954 (run-hooks 'mh-yank-hooks))
955 (t
956 (or (bolp) (forward-line 1))
957 (while (< (point) (point-max))
958 (insert mh-ins-string)
959 (forward-line 1))
960 (goto-char (point-min))))) ;leave point like sc-cite-original
961
962 (provide 'mh-letter)
963
964 ;; Local Variables:
965 ;; indent-tabs-mode: nil
966 ;; sentence-end-double-space: nil
967 ;; End:
968
969 ;; arch-tag: 0548632c-aadb-4e3b-bb80-bbd62ff90bf3
970 ;;; mh-letter.el ends here