Minor tweak.
[bpt/emacs.git] / lisp / mh-e / mh-comp.el
CommitLineData
bdcfe844 1;;; mh-comp.el --- MH-E functions for composing messages
c26cf6c8 2
e495eaec 3;; Copyright (C) 1993, 1995, 1997,
a05fcb7d 4;; 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
7382bcae 5
a1b4049d 6;; Author: Bill Wohler <wohler@newt.com>
6e65a812 7;; Maintainer: Bill Wohler <wohler@newt.com>
7382bcae 8;; Keywords: mail
a1b4049d 9;; See: mh-e.el
c26cf6c8 10
60370d40 11;; This file is part of GNU Emacs.
c26cf6c8 12
9b7bc076 13;; GNU Emacs is free software; you can redistribute it and/or modify
c26cf6c8
RS
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
9b7bc076 18;; GNU Emacs is distributed in the hope that it will be useful,
c26cf6c8
RS
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
b578f267 24;; along with GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
25;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26;; Boston, MA 02110-1301, USA.
c26cf6c8
RS
27
28;;; Commentary:
29
bdcfe844 30;; Internal support for MH-E package.
c26cf6c8 31
847b8219
KH
32;;; Change Log:
33
c26cf6c8
RS
34;;; Code:
35
f0d73c14
BW
36(eval-when-compile (require 'mh-acros))
37(mh-require-cl)
a1b4049d 38(require 'mh-e)
a1b4049d
BW
39(require 'gnus-util)
40(require 'easymenu)
f0d73c14 41(require 'mh-gnus)
924df208
BW
42(eval-when (compile load eval)
43 (ignore-errors (require 'mailabbrev)))
bdcfe844
BW
44
45;; Shush the byte-compiler
46(defvar adaptive-fill-first-line-regexp)
47(defvar font-lock-defaults)
48(defvar mark-active)
49(defvar sendmail-coding-system)
c3d9274a
BW
50(defvar mh-identity-list)
51(defvar mh-identity-default)
f0d73c14 52(defvar mh-mml-mode-default)
c3d9274a 53(defvar mh-identity-menu)
a1b4049d 54
cee9f5c6 55\f
c3d9274a 56;;; Autoloads
cee9f5c6 57
a1b4049d 58(autoload 'mail-mode-fill-paragraph "sendmail")
bdcfe844
BW
59(autoload 'mm-handle-displayed-p "mm-decode")
60
61(autoload 'sc-cite-original "sc"
62 "Workhorse citing function which performs the initial citation.
63This is callable from the various mail and news readers' reply
f0d73c14 64function according to the agreed upon standard. See `sc-describe'
bdcfe844
BW
65for more details. `sc-cite-original' does not do any yanking of the
66original message but it does require a few things:
67
68 1) The reply buffer is the current buffer.
69
70 2) The original message has been yanked and inserted into the
71 reply buffer.
72
73 3) Verbose mail headers from the original message have been
74 inserted into the reply buffer directly before the text of the
75 original message.
76
77 4) Point is at the beginning of the verbose headers.
78
79 5) Mark is at the end of the body of text to be cited.
80
81For Emacs 19's, the region need not be active (and typically isn't
82when this function is called. Also, the hook `sc-pre-hook' is run
83before, and `sc-post-hook' is run after the guts of this function.")
c26cf6c8 84
cee9f5c6
BW
85\f
86
847b8219
KH
87;;; Site customization (see also mh-utils.el):
88
89(defvar mh-send-prog "send"
90 "Name of the MH send program.
91Some sites need to change this because of a name conflict.")
92
a1b4049d
BW
93(defvar mh-redist-background nil
94 "If non-nil redist will be done in background like send.
2dcf34f9
BW
95This allows transaction log to be visible if -watch, -verbose or
96-snoop are used.")
847b8219 97
cee9f5c6
BW
98\f
99
f0d73c14
BW
100;;; Scan Line Formats
101
102(defvar mh-note-repl ?-
103 "Messages that have been replied to are marked by this character.")
c26cf6c8 104
f0d73c14
BW
105(defvar mh-note-forw ?F
106 "Messages that have been forwarded are marked by this character.")
c26cf6c8 107
f0d73c14
BW
108(defvar mh-note-dist ?R
109 "Messages that have been redistributed are marked by this character.")
c26cf6c8 110
c26cf6c8
RS
111(defvar mh-yank-hooks nil
112 "Obsolete hook for modifying a citation just inserted in the mail buffer.
2dcf34f9 113
c26cf6c8 114Each hook function can find the citation between point and mark.
2dcf34f9
BW
115And each hook function should leave point and mark around the
116citation text as modified.
c26cf6c8 117
2dcf34f9
BW
118This is a normal hook, misnamed for historical reasons. It is
119semi-obsolete and is only used if `mail-citation-hook' is nil.")
c26cf6c8 120
c26cf6c8
RS
121(defvar mh-comp-formfile "components"
122 "Name of file to be used as a skeleton for composing messages.
2dcf34f9
BW
123
124Default is \"components\".
125
126If not an absolute file name, the file is searched for first in the
127user's MH directory, then in the system MH lib directory.")
c26cf6c8 128
847b8219
KH
129(defvar mh-repl-formfile "replcomps"
130 "Name of file to be used as a skeleton for replying to messages.
2dcf34f9
BW
131
132Default is \"replcomps\".
133
134If not an absolute file name, the file is searched for first in the
135user's MH directory, then in the system MH lib directory.")
847b8219 136
c3d6278e 137(defvar mh-repl-group-formfile "replgroupcomps"
bdcfe844 138 "Name of file to be used as a skeleton for replying to messages.
2dcf34f9 139
f0d73c14 140Default is \"replgroupcomps\".
2dcf34f9
BW
141
142This file is used to form replies to the sender and all recipients of
143a message. Only used if `(mh-variant-p 'nmh)' is non-nil.
144If not an absolute file name, the file is searched for first in the
145user's MH directory, then in the system MH lib directory.")
a1b4049d 146
c26cf6c8 147(defvar mh-rejected-letter-start
bdcfe844 148 (format "^%s$"
c3d9274a
BW
149 (regexp-opt
150 '("Content-Type: message/rfc822" ;MIME MDN
f0d73c14
BW
151 "------ This is a copy of the message, including all the headers. ------";from exim
152 "--- Below this line is a copy of the message."; from qmail
c3d9274a
BW
153 " ----- Unsent message follows -----" ;from sendmail V5
154 " --------Unsent Message below:" ; from sendmail at BU
155 " ----- Original message follows -----" ;from sendmail V8
156 "------- Unsent Draft" ;from MH itself
157 "---------- Original Message ----------" ;from zmailer
158 " --- The unsent message follows ---" ;from AIX mail system
159 " Your message follows:" ;from MMDF-II
160 "Content-Description: Returned Content" ;1993 KJ sendmail
161 ))))
c26cf6c8
RS
162
163(defvar mh-new-draft-cleaned-headers
847b8219 164 "^Date:\\|^Received:\\|^Message-Id:\\|^From:\\|^Sender:\\|^Errors-To:\\|^Delivery-Date:\\|^Return-Path:"
5a4aad03
BW
165 "Regexp of header lines to remove before offering a message as a new draft\\<mh-folder-mode-map>.
166Used by the \\[mh-edit-again] and \\[mh-extract-rejected-mail] commands.")
c26cf6c8 167
847b8219 168(defvar mh-to-field-choices '(("t" . "To:") ("s" . "Subject:") ("c" . "Cc:")
c3d9274a
BW
169 ("b" . "Bcc:") ("f" . "Fcc:") ("r" . "From:")
170 ("d" . "Dcc:"))
a1b4049d 171 "Alist of (final-character . field-name) choices for `mh-to-field'.")
c26cf6c8
RS
172
173(defvar mh-letter-mode-map (copy-keymap text-mode-map)
174 "Keymap for composing mail.")
175
176(defvar mh-letter-mode-syntax-table nil
bdcfe844 177 "Syntax table used by MH-E while in MH-Letter mode.")
c26cf6c8
RS
178
179(if mh-letter-mode-syntax-table
180 ()
c3d9274a
BW
181 (setq mh-letter-mode-syntax-table
182 (make-syntax-table text-mode-syntax-table))
183 (modify-syntax-entry ?% "." mh-letter-mode-syntax-table))
c26cf6c8 184
bdcfe844
BW
185(defvar mh-sent-from-folder nil
186 "Folder of msg assoc with this letter.")
187
188(defvar mh-sent-from-msg nil
189 "Number of msg assoc with this letter.")
190
191(defvar mh-send-args nil
192 "Extra args to pass to \"send\" command.")
193
194(defvar mh-annotate-char nil
195 "Character to use to annotate `mh-sent-from-msg'.")
196
197(defvar mh-annotate-field nil
198 "Field name for message annotation.")
c26cf6c8 199
a66894d8 200(defvar mh-insert-auto-fields-done-local nil
f0d73c14 201 "Buffer-local variable set when `mh-insert-auto-fields' called successfully.")
a66894d8
BW
202(make-variable-buffer-local 'mh-insert-auto-fields-done-local)
203
c26cf6c8
RS
204;;;###autoload
205(defun mh-smail ()
b2064e08 206 "Compose a message with the MH mail system.
f0d73c14 207See `mh-send' for more details on composing mail."
c26cf6c8
RS
208 (interactive)
209 (mh-find-path)
210 (call-interactively 'mh-send))
211
b2064e08
BW
212;;;###autoload
213(defun mh-smail-other-window ()
214 "Compose a message with the MH mail system in other window.
215See `mh-send' for more details on composing mail."
216 (interactive)
217 (mh-find-path)
218 (call-interactively 'mh-send-other-window))
219
c3d9274a 220(defvar mh-error-if-no-draft nil) ;raise error over using old draft
283b03f4 221
283b03f4 222;;;###autoload
c3d6278e 223(defun mh-smail-batch (&optional to subject other-headers &rest ignored)
b2064e08
BW
224 "Compose a message with the MH mail system.
225
2dcf34f9
BW
226This function does not prompt the user for any header fields, and
227thus is suitable for use by programs that want to create a mail
228buffer. Users should use \\[mh-smail] to compose mail.
f0d73c14 229
2dcf34f9 230Optional arguments for setting certain fields include TO,
0d887b77
BW
231SUBJECT, and OTHER-HEADERS. Additional arguments are IGNORED.
232
233This function remains for Emacs 21 compatibility. New
234applications should use `mh-user-agent-compose'."
283b03f4
KH
235 (mh-find-path)
236 (let ((mh-error-if-no-draft t))
016fbe59 237 (mh-send (or to "") "" (or subject ""))))
283b03f4 238
0d887b77
BW
239;;;###autoload
240(define-mail-user-agent 'mh-e-user-agent
241 'mh-user-agent-compose 'mh-send-letter 'mh-fully-kill-draft
242 'mh-before-send-letter-hook)
243
a1b4049d
BW
244;;;###autoload
245(defun mh-user-agent-compose (&optional to subject other-headers continue
c3d9274a
BW
246 switch-function yank-action
247 send-actions)
a1b4049d 248 "Set up mail composition draft with the MH mail system.
0d887b77
BW
249This is the `mail-user-agent' entry point to MH-E. This function
250conforms to the contract specified by `define-mail-user-agent'
251which means that this function should accept the same arguments
252as `compose-mail'.
a1b4049d
BW
253
254The optional arguments TO and SUBJECT specify recipients and the
255initial Subject field, respectively.
256
2dcf34f9
BW
257OTHER-HEADERS is an alist specifying additional header fields.
258Elements look like (HEADER . VALUE) where both HEADER and VALUE
259are strings.
a1b4049d 260
2dcf34f9
BW
261CONTINUE, SWITCH-FUNCTION, YANK-ACTION and SEND-ACTIONS are
262ignored."
a1b4049d
BW
263 (mh-find-path)
264 (let ((mh-error-if-no-draft t))
265 (mh-send to "" subject)
266 (while other-headers
267 (mh-insert-fields (concat (car (car other-headers)) ":")
c3d9274a 268 (cdr (car other-headers)))
a1b4049d 269 (setq other-headers (cdr other-headers)))))
283b03f4 270
c3d9274a 271;;;###mh-autoload
b2064e08
BW
272(defun mh-edit-again (message)
273 "Edit a MESSAGE to send it again.
274
2dcf34f9
BW
275If you don't complete a draft for one reason or another, and if
276the draft buffer is no longer available, you can pick your draft
277up again with this command. If you don't use a draft folder, your
278last \"draft\" file will be used. If you use draft folders,
279you'll need to visit the draft folder with \"\\[mh-visit-folder]
280drafts <RET>\", use \\[mh-next-undeleted-msg] to move to the
281appropriate message, and then use \\[mh-edit-again] to prepare
282the message for editing.
b2064e08 283
2dcf34f9
BW
284This command can also be used to take messages that were sent to
285you and to send them to more people.
b2064e08 286
2dcf34f9
BW
287Don't use this command to re-edit a message from a Mailer-Daemon
288who complained that your mail wasn't posted for some reason or
289another (see `mh-extract-rejected-mail').
b2064e08
BW
290
291The default message is the current message.
f0d73c14
BW
292
293See also `mh-send'."
c26cf6c8
RS
294 (interactive (list (mh-get-msg-num t)))
295 (let* ((from-folder mh-current-folder)
c3d9274a
BW
296 (config (current-window-configuration))
297 (draft
298 (cond ((and mh-draft-folder (equal from-folder mh-draft-folder))
b2064e08
BW
299 (pop-to-buffer (find-file-noselect (mh-msg-filename message))
300 t)
301 (rename-buffer (format "draft-%d" message))
bdcfe844
BW
302 ;; Make buffer writable...
303 (setq buffer-read-only nil)
304 ;; If buffer was being used to display the message reinsert
305 ;; from file...
306 (when (eq major-mode 'mh-show-mode)
307 (erase-buffer)
308 (insert-file-contents buffer-file-name))
c3d9274a
BW
309 (buffer-name))
310 (t
b2064e08 311 (mh-read-draft "clean-up" (mh-msg-filename message) nil)))))
c26cf6c8 312 (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil)
a1b4049d 313 (mh-insert-header-separator)
c26cf6c8 314 (goto-char (point-min))
283b03f4 315 (save-buffer)
c26cf6c8 316 (mh-compose-and-send-mail draft "" from-folder nil nil nil nil nil nil
c3d9274a 317 config)
a66894d8
BW
318 (mh-letter-mode-message)
319 (mh-letter-adjust-point)))
c26cf6c8 320
c3d9274a 321;;;###mh-autoload
b2064e08
BW
322(defun mh-extract-rejected-mail (message)
323 "Edit a MESSAGE that was returned by the mail system.
324
2dcf34f9
BW
325This command prepares the message for editing by removing the
326Mailer-Daemon envelope and unneeded header fields. Fix whatever
327addressing problem you had, and send the message again with
328\\[mh-send-letter].
b2064e08
BW
329
330The default message is the current message.
f0d73c14
BW
331
332See also `mh-send'."
c26cf6c8
RS
333 (interactive (list (mh-get-msg-num t)))
334 (let ((from-folder mh-current-folder)
c3d9274a 335 (config (current-window-configuration))
b2064e08 336 (draft (mh-read-draft "extraction" (mh-msg-filename message) nil)))
c26cf6c8
RS
337 (goto-char (point-min))
338 (cond ((re-search-forward mh-rejected-letter-start nil t)
c3d9274a
BW
339 (skip-chars-forward " \t\n")
340 (delete-region (point-min) (point))
341 (mh-clean-msg-header (point-min) mh-new-draft-cleaned-headers nil))
342 (t
f0d73c14 343 (message "Does not appear to be a rejected letter")))
a1b4049d 344 (mh-insert-header-separator)
c26cf6c8 345 (goto-char (point-min))
283b03f4 346 (save-buffer)
b2064e08 347 (mh-compose-and-send-mail draft "" from-folder message
c3d9274a
BW
348 (mh-get-header-field "To:")
349 (mh-get-header-field "From:")
350 (mh-get-header-field "Cc:")
351 nil nil config)
bdcfe844 352 (mh-letter-mode-message)))
c26cf6c8 353
c3d9274a 354;;;###mh-autoload
a66894d8 355(defun mh-forward (to cc &optional range)
2be362c2 356 "Forward message.
a66894d8 357
2dcf34f9
BW
358You are prompted for the TO and CC recipients. You are given a
359draft to edit that looks like it would if you had run the MH
360command \"forw\". You can then add some text.
bdcfe844 361
2dcf34f9
BW
362You can forward several messages by using a RANGE. All of the
363messages in the range are inserted into your draft. Check the
364documentation of `mh-interactive-range' to see how RANGE is read
365in interactive use.
b2064e08 366
d1699462
BW
367The hook `mh-forward-hook' is called on the draft.
368
369See also `mh-compose-forward-as-mime-flag',
370`mh-forward-subject-format', and `mh-send'."
a66894d8
BW
371 (interactive (list (mh-interactive-read-address "To: ")
372 (mh-interactive-read-address "Cc: ")
373 (mh-interactive-range "Forward")))
c26cf6c8 374 (let* ((folder mh-current-folder)
a66894d8 375 (msgs (mh-range-to-msg-list range))
c3d9274a
BW
376 (config (current-window-configuration))
377 (fwd-msg-file (mh-msg-filename (car msgs) folder))
378 ;; forw always leaves file in "draft" since it doesn't have -draft
379 (draft-name (expand-file-name "draft" mh-user-path))
380 (draft (cond ((or (not (file-exists-p draft-name))
381 (y-or-n-p "The file 'draft' exists. Discard it? "))
f0d73c14
BW
382 (mh-exec-cmd "forw" "-build"
383 (if (and (mh-variant-p 'nmh)
384 mh-compose-forward-as-mime-flag)
385 "-mime")
924df208
BW
386 mh-current-folder
387 (mh-coalesce-msg-list msgs))
c3d9274a
BW
388 (prog1
389 (mh-read-draft "" draft-name t)
390 (mh-insert-fields "To:" to "Cc:" cc)
391 (save-buffer)))
392 (t
393 (mh-read-draft "" draft-name nil)))))
847b8219 394 (let (orig-from
c3d9274a 395 orig-subject)
41b9a988 396 (save-excursion
c3d9274a
BW
397 (set-buffer (get-buffer-create mh-temp-buffer))
398 (erase-buffer)
399 (insert-file-contents fwd-msg-file)
400 (setq orig-from (mh-get-header-field "From:"))
401 (setq orig-subject (mh-get-header-field "Subject:")))
c26cf6c8 402 (let ((forw-subject
924df208 403 (mh-forwarded-letter-subject orig-from orig-subject)))
c3d9274a
BW
404 (mh-insert-fields "Subject:" forw-subject)
405 (goto-char (point-min))
0c47b17c
BW
406 ;; If using MML, translate MH-style directive
407 (if (equal mh-compose-insertion 'mml)
c3d9274a 408 (save-excursion
a66894d8 409 (goto-char (mh-mail-header-end))
c3d9274a
BW
410 (while
411 (re-search-forward
412 "^#forw \\[\\([^]]+\\)\\] \\(+\\S-+\\) \\(.*\\)$"
413 (point-max) t)
414 (let ((description (if (equal (match-string 1)
415 "forwarded messages")
416 "forwarded message %d"
417 (match-string 1)))
418 (msgs (split-string (match-string 3)))
419 (i 0))
420 (beginning-of-line)
421 (delete-region (point) (progn (forward-line 1) (point)))
422 (dolist (msg msgs)
423 (setq i (1+ i))
424 (mh-mml-forward-message (format description i)
425 folder msg))))))
426 ;; Postition just before forwarded message
427 (if (re-search-forward "^------- Forwarded Message" nil t)
428 (forward-line -1)
a66894d8 429 (goto-char (mh-mail-header-end))
c3d9274a
BW
430 (forward-line 1))
431 (delete-other-windows)
432 (mh-add-msgs-to-seq msgs 'forwarded t)
924df208 433 (mh-compose-and-send-mail draft "" folder msgs
c3d9274a
BW
434 to forw-subject cc
435 mh-note-forw "Forwarded:"
436 config)
a66894d8 437 (mh-letter-mode-message)
f0d73c14
BW
438 (mh-letter-adjust-point)
439 (run-hooks 'mh-forward-hook)))))
c26cf6c8
RS
440
441(defun mh-forwarded-letter-subject (from subject)
bdcfe844
BW
442 "Return a Subject suitable for a forwarded message.
443Original message has headers FROM and SUBJECT."
c26cf6c8 444 (let ((addr-start (string-match "<" from))
c3d9274a 445 (comment (string-match "(" from)))
c26cf6c8 446 (cond ((and addr-start (> addr-start 0))
c3d9274a
BW
447 ;; Full Name <luser@host>
448 (setq from (substring from 0 (1- addr-start))))
449 (comment
450 ;; luser@host (Full Name)
451 (setq from (substring from (1+ comment) (1- (length from)))))))
c26cf6c8
RS
452 (format mh-forward-subject-format from subject))
453
b2064e08
BW
454;;;###mh-autoload
455(defun mh-redistribute (to cc &optional message)
456 "Redistribute a message.
847b8219 457
2dcf34f9
BW
458This command is similar in function to forwarding mail, but it
459does not allow you to edit the message, nor does it add your name
460to the \"From\" header field. It appears to the recipient as if
461the message had come from the original sender. When you run this
462command, you are prompted for the TO and CC recipients. The
463default MESSAGE is the current message.
c26cf6c8 464
2dcf34f9
BW
465Also investigate the \\[mh-edit-again] command for another way to
466redistribute messages.
b2064e08
BW
467
468See also `mh-redist-full-contents-flag'."
c26cf6c8 469 (interactive (list (mh-read-address "Redist-To: ")
c3d9274a
BW
470 (mh-read-address "Redist-Cc: ")
471 (mh-get-msg-num t)))
b2064e08
BW
472 (or message
473 (setq message (mh-get-msg-num t)))
c26cf6c8
RS
474 (save-window-excursion
475 (let ((folder mh-current-folder)
c3d9274a 476 (draft (mh-read-draft "redistribution"
b2064e08
BW
477 (if mh-redist-full-contents-flag
478 (mh-msg-filename message)
c3d9274a
BW
479 nil)
480 nil)))
c26cf6c8
RS
481 (mh-goto-header-end 0)
482 (insert "Resent-To: " to "\n")
483 (if (not (equal cc "")) (insert "Resent-cc: " cc "\n"))
924df208
BW
484 (mh-clean-msg-header
485 (point-min)
486 "^Message-Id:\\|^Received:\\|^Return-Path:\\|^Sender:\\|^Date:\\|^From:"
487 nil)
c26cf6c8
RS
488 (save-buffer)
489 (message "Redistributing...")
924df208
BW
490 (let ((env "mhdist=1"))
491 ;; Setup environment...
b2064e08
BW
492 (setq env (concat env " mhaltmsg="
493 (if mh-redist-full-contents-flag
494 buffer-file-name
495 (mh-msg-filename message folder))))
496 (unless mh-redist-full-contents-flag
924df208
BW
497 (setq env (concat env " mhannotate=1")))
498 ;; Redistribute...
499 (if mh-redist-background
500 (mh-exec-cmd-env-daemon env mh-send-prog nil buffer-file-name)
501 (mh-exec-cmd-error env mh-send-prog "-push" buffer-file-name))
502 ;; Annotate...
b2064e08 503 (mh-annotate-msg message folder mh-note-dist
924df208
BW
504 "-component" "Resent:"
505 "-text" (format "\"%s %s\"" to cc)))
c26cf6c8
RS
506 (kill-buffer draft)
507 (message "Redistributing...done"))))
508
bdcfe844
BW
509(defun mh-show-buffer-message-number (&optional buffer)
510 "Message number of displayed message in corresponding show buffer.
2dcf34f9 511
bdcfe844 512Return nil if show buffer not displayed.
2dcf34f9
BW
513If in `mh-letter-mode', don't display the message number being replied
514to, but rather the message number of the show buffer associated with
515our originating folder buffer.
bdcfe844
BW
516Optional argument BUFFER can be used to specify the buffer."
517 (save-excursion
518 (if buffer
519 (set-buffer buffer))
520 (cond ((eq major-mode 'mh-show-mode)
c3d9274a
BW
521 (let ((number-start (mh-search-from-end ?/ buffer-file-name)))
522 (car (read-from-string (substring buffer-file-name
523 (1+ number-start))))))
bdcfe844
BW
524 ((and (eq major-mode 'mh-folder-mode)
525 mh-show-buffer
526 (get-buffer mh-show-buffer))
527 (mh-show-buffer-message-number mh-show-buffer))
528 ((and (eq major-mode 'mh-letter-mode)
529 mh-sent-from-folder
530 (get-buffer mh-sent-from-folder))
531 (mh-show-buffer-message-number mh-sent-from-folder))
532 (t
533 nil))))
534
c3d9274a 535;;;###mh-autoload
bdcfe844 536(defun mh-reply (message &optional reply-to includep)
b2064e08 537 "Reply to a MESSAGE.
f0d73c14 538
2dcf34f9
BW
539When you reply to a message, you are first prompted with \"Reply
540to whom?\" (unless the optional argument REPLY-TO is provided).
541You have several choices here.
b2064e08
BW
542
543 Response Reply Goes To
544
2dcf34f9
BW
545 from The person who sent the message. This is the
546 default, so <RET> is sufficient.
b2064e08
BW
547
548 to Replies to the sender, plus all recipients in the
549 \"To:\" header field.
550
551 all
552 cc Forms a reply to the sender, plus all recipients.
553
2dcf34f9
BW
554Depending on your answer, \"repl\" is given a different argument
555to form your reply. Specifically, a choice of \"from\" or none at
556all runs \"repl -nocc all\", and a choice of \"to\" runs \"repl
557-cc to\". Finally, either \"cc\" or \"all\" runs \"repl -cc all
558-nocc me\".
b2064e08 559
2dcf34f9
BW
560Two windows are then created. One window contains the message to
561which you are replying in an MH-Show buffer. Your draft, in
4023e353 562MH-Letter mode (see `mh-letter-mode'), is in the other window.
b2064e08 563
2dcf34f9
BW
564If you supply a prefix argument INCLUDEP, the message you are
565replying to is inserted in your reply after having first been run
566through \"mhl\" with the format file \"mhl.reply\".
b2064e08 567
2dcf34f9
BW
568Alternatively, you can customize the option `mh-yank-behavior'
569and choose one of its \"Automatically\" variants to do the same
570thing. If you do so, the prefix argument has no effect.
b2064e08 571
2dcf34f9
BW
572Another way to include the message automatically in your draft is
573to use \"repl: -filter repl.filter\" in your MH profile.
b2064e08 574
2dcf34f9
BW
575If you wish to customize the header or other parts of the reply
576draft, please see \"repl\" and \"mh-format\".
b2064e08 577
2dcf34f9
BW
578See also `mh-reply-show-message-flag',
579`mh-reply-default-reply-to', and `mh-send'."
bdcfe844
BW
580 (interactive (list
581 (mh-get-msg-num t)
582 (let ((minibuffer-help-form
583 "from => Sender only\nto => Sender and primary recipients\ncc or all => Sender and all recipients"))
584 (or mh-reply-default-reply-to
f0d73c14 585 (completing-read "Reply to whom: [from] "
bdcfe844
BW
586 '(("from") ("to") ("cc") ("all"))
587 nil
588 t)))
589 current-prefix-arg))
590 (let* ((folder mh-current-folder)
591 (show-buffer mh-show-buffer)
592 (config (current-window-configuration))
593 (group-reply (or (equal reply-to "cc") (equal reply-to "all")))
f0d73c14 594 (form-file (cond ((and (mh-variant-p 'nmh 'mu-mh) group-reply
bdcfe844
BW
595 (stringp mh-repl-group-formfile))
596 mh-repl-group-formfile)
597 ((stringp mh-repl-formfile) mh-repl-formfile)
598 (t nil))))
599 (message "Composing a reply...")
600 (mh-exec-cmd "repl" "-build" "-noquery" "-nodraftfolder"
601 (if form-file
602 (list "-form" form-file))
603 mh-current-folder message
604 (cond ((or (equal reply-to "from") (equal reply-to ""))
605 '("-nocc" "all"))
606 ((equal reply-to "to")
607 '("-cc" "to"))
f0d73c14 608 (group-reply (if (mh-variant-p 'nmh 'mu-mh)
bdcfe844
BW
609 '("-group" "-nocc" "me")
610 '("-cc" "all" "-nocc" "me"))))
0c47b17c
BW
611 (cond ((or (eq mh-yank-behavior 'autosupercite)
612 (eq mh-yank-behavior 'autoattrib))
c3d9274a
BW
613 '("-noformat"))
614 (includep '("-filter" "mhl.reply"))
615 (t '())))
bdcfe844
BW
616 (let ((draft (mh-read-draft "reply"
617 (expand-file-name "reply" mh-user-path)
618 t)))
619 (delete-other-windows)
620 (save-buffer)
a1506d29 621
bdcfe844
BW
622 (let ((to (mh-get-header-field "To:"))
623 (subject (mh-get-header-field "Subject:"))
624 (cc (mh-get-header-field "Cc:")))
625 (goto-char (point-min))
626 (mh-goto-header-end 1)
627 (or includep
628 (not mh-reply-show-message-flag)
629 (mh-in-show-buffer (show-buffer)
630 (mh-display-msg message folder)))
631 (mh-add-msgs-to-seq message 'answered t)
632 (message "Composing a reply...done")
633 (mh-compose-and-send-mail draft "" folder message to subject cc
634 mh-note-repl "Replied:" config))
0c47b17c
BW
635 (when (and (or (eq 'autosupercite mh-yank-behavior)
636 (eq 'autoattrib mh-yank-behavior))
bdcfe844
BW
637 (eq (mh-show-buffer-message-number) mh-sent-from-msg))
638 (undo-boundary)
639 (mh-yank-cur-msg))
640 (mh-letter-mode-message))))
c26cf6c8 641
c3d9274a 642;;;###mh-autoload
c26cf6c8 643(defun mh-send (to cc subject)
b2064e08
BW
644 "Compose a message.
645
2dcf34f9
BW
646Your letter appears in an Emacs buffer whose mode is
647MH-Letter (see `mh-letter-mode').
b2064e08 648
2dcf34f9
BW
649The arguments TO, CC, and SUBJECT can be used to prefill the
650draft fields or suppress the prompts if `mh-compose-prompt-flag'
651is on. They are also passed to the function set in the option
652`mh-compose-letter-function'.
b2064e08
BW
653
654See also `mh-insert-x-mailer-flag' and `mh-letter-mode-hook'.
655
2dcf34f9
BW
656Outside of an MH-Folder buffer (`mh-folder-mode'), you must call
657either \\[mh-smail] or \\[mh-smail-other-window] to compose a new
658message."
c26cf6c8 659 (interactive (list
a66894d8
BW
660 (mh-interactive-read-address "To: ")
661 (mh-interactive-read-address "Cc: ")
662 (mh-interactive-read-string "Subject: ")))
c26cf6c8
RS
663 (let ((config (current-window-configuration)))
664 (delete-other-windows)
665 (mh-send-sub to cc subject config)))
666
c3d9274a 667;;;###mh-autoload
c26cf6c8 668(defun mh-send-other-window (to cc subject)
b2064e08
BW
669 "Compose a message in another window.
670
2dcf34f9
BW
671See `mh-send' for more information and a description of how the
672TO, CC, and SUBJECT arguments are used."
c26cf6c8 673 (interactive (list
a66894d8
BW
674 (mh-interactive-read-address "To: ")
675 (mh-interactive-read-address "Cc: ")
676 (mh-interactive-read-string "Subject: ")))
c26cf6c8
RS
677 (let ((pop-up-windows t))
678 (mh-send-sub to cc subject (current-window-configuration))))
679
c26cf6c8 680(defun mh-send-sub (to cc subject config)
bdcfe844
BW
681 "Do the real work of composing and sending a letter.
682Expects the TO, CC, and SUBJECT fields as arguments.
683CONFIG is the window configuration before sending mail."
c26cf6c8 684 (let ((folder mh-current-folder)
c3d9274a 685 (msg-num (mh-get-msg-num nil)))
c26cf6c8
RS
686 (message "Composing a message...")
687 (let ((draft (mh-read-draft
c3d9274a
BW
688 "message"
689 (let (components)
690 (cond
691 ((file-exists-p
692 (setq components
693 (expand-file-name mh-comp-formfile mh-user-path)))
694 components)
695 ((file-exists-p
696 (setq components
697 (expand-file-name mh-comp-formfile mh-lib)))
698 components)
699 ((file-exists-p
700 (setq components
701 (expand-file-name mh-comp-formfile
702 ;; What is this mh-etc ?? -sm
bdcfe844
BW
703 ;; This is dead code, so
704 ;; remove it.
c3d9274a 705 ;(and (boundp 'mh-etc) mh-etc)
bdcfe844 706 )))
c3d9274a
BW
707 components)
708 (t
e9c2f101
JB
709 (error "Can't find components file \"%s\""
710 components))))
c3d9274a 711 nil)))
c26cf6c8
RS
712 (mh-insert-fields "To:" to "Subject:" subject "Cc:" cc)
713 (goto-char (point-max))
c26cf6c8 714 (mh-compose-and-send-mail draft "" folder msg-num
c3d9274a
BW
715 to subject cc
716 nil nil config)
a66894d8
BW
717 (mh-letter-mode-message)
718 (mh-letter-adjust-point))))
c26cf6c8
RS
719
720(defun mh-read-draft (use initial-contents delete-contents-file)
bdcfe844 721 "Read draft file into a draft buffer and make that buffer the current one.
2dcf34f9
BW
722
723USE is a message used for prompting about the intended use of the
724message.
bdcfe844 725INITIAL-CONTENTS is filename that is read into an empty buffer, or nil
2dcf34f9 726if buffer should not be modified. Delete the initial-contents file if
bdcfe844
BW
727DELETE-CONTENTS-FILE flag is set.
728Returns the draft folder's name.
2dcf34f9
BW
729If the draft folder facility is enabled in ~/.mh_profile, a new buffer
730is used each time and saved in the draft folder. The draft file can
731then be reused."
c26cf6c8 732 (cond (mh-draft-folder
c3d9274a
BW
733 (let ((orig-default-dir default-directory)
734 (draft-file-name (mh-new-draft-name)))
735 (pop-to-buffer (generate-new-buffer
736 (format "draft-%s"
737 (file-name-nondirectory draft-file-name))))
738 (condition-case ()
739 (insert-file-contents draft-file-name t)
740 (file-error))
741 (setq default-directory orig-default-dir)))
742 (t
743 (let ((draft-name (expand-file-name "draft" mh-user-path)))
744 (pop-to-buffer "draft") ; Create if necessary
745 (if (buffer-modified-p)
746 (if (y-or-n-p "Draft has been modified; kill anyway? ")
747 (set-buffer-modified-p nil)
748 (error "Draft preserved")))
749 (setq buffer-file-name draft-name)
750 (clear-visited-file-modtime)
751 (unlock-buffer)
752 (cond ((and (file-exists-p draft-name)
753 (not (equal draft-name initial-contents)))
754 (insert-file-contents draft-name)
755 (delete-file draft-name))))))
c26cf6c8 756 (cond ((and initial-contents
c3d9274a
BW
757 (or (zerop (buffer-size))
758 (if (y-or-n-p
759 (format "A draft exists. Use for %s? " use))
760 (if mh-error-if-no-draft
761 (error "A prior draft exists"))
762 t)))
763 (erase-buffer)
764 (insert-file-contents initial-contents)
765 (if delete-contents-file (delete-file initial-contents))))
c26cf6c8
RS
766 (auto-save-mode 1)
767 (if mh-draft-folder
c3d9274a 768 (save-buffer)) ; Do not reuse draft name
c26cf6c8
RS
769 (buffer-name))
770
c26cf6c8 771(defun mh-new-draft-name ()
bdcfe844 772 "Return the pathname of folder for draft messages."
c26cf6c8
RS
773 (save-excursion
774 (mh-exec-cmd-quiet t "mhpath" mh-draft-folder "new")
775 (buffer-substring (point-min) (1- (point-max)))))
776
c26cf6c8 777(defun mh-annotate-msg (msg buffer note &rest args)
924df208 778 "Mark MSG in BUFFER with character NOTE and annotate message with ARGS.
2dcf34f9
BW
779MSG can be a message number, a list of message numbers, or a
780sequence."
924df208
BW
781 (apply 'mh-exec-cmd "anno" buffer
782 (if (listp msg) (append msg args) (cons msg args)))
c26cf6c8 783 (save-excursion
c3d9274a
BW
784 (cond ((get-buffer buffer) ; Buffer may be deleted
785 (set-buffer buffer)
a66894d8 786 (mh-iterate-on-range nil msg
50df64d6
BW
787 (mh-notate nil note
788 (+ mh-cmd-note mh-scan-field-destination-offset)))))))
c26cf6c8 789
c26cf6c8 790(defun mh-insert-fields (&rest name-values)
bdcfe844
BW
791 "Insert the NAME-VALUES pairs in the current buffer.
792If the field exists, append the value to it.
793Do not insert any pairs whose value is the empty string."
c26cf6c8
RS
794 (let ((case-fold-search t))
795 (while name-values
796 (let ((field-name (car name-values))
c3d9274a 797 (value (car (cdr name-values))))
f0d73c14
BW
798 (if (not (string-match "^.*:$" field-name))
799 (setq field-name (concat field-name ":")))
c3d9274a
BW
800 (cond ((equal value "")
801 nil)
802 ((mh-position-on-field field-name)
803 (insert " " (or value "")))
804 (t
805 (insert field-name " " value "\n")))
806 (setq name-values (cdr (cdr name-values)))))))
c26cf6c8 807
bdcfe844
BW
808(defun mh-position-on-field (field &optional ignored)
809 "Move to the end of the FIELD in the header.
810Move to end of entire header if FIELD not found.
811Returns non-nil iff FIELD was found.
2dcf34f9
BW
812The optional second arg is for pre-version 4 compatibility and is
813IGNORED."
a1b4049d 814 (cond ((mh-goto-header-field field)
c3d9274a
BW
815 (mh-header-field-end)
816 t)
817 ((mh-goto-header-end 0)
818 nil)))
847b8219 819
f0d73c14 820;;;###mh-autoload
847b8219 821(defun mh-get-header-field (field)
bdcfe844
BW
822 "Find and return the body of FIELD in the mail header.
823Returns the empty string if the field is not in the header of the
824current buffer."
847b8219
KH
825 (if (mh-goto-header-field field)
826 (progn
c3d9274a
BW
827 (skip-chars-forward " \t") ;strip leading white space in body
828 (let ((start (point)))
829 (mh-header-field-end)
830 (buffer-substring-no-properties start (point))))
847b8219
KH
831 ""))
832
bdcfe844 833(fset 'mh-get-field 'mh-get-header-field) ;MH-E 4 compatibility
847b8219
KH
834
835(defun mh-goto-header-field (field)
bdcfe844
BW
836 "Move to FIELD in the message header.
837Move to the end of the FIELD name, which should end in a colon.
838Returns t if found, nil if not."
847b8219
KH
839 (goto-char (point-min))
840 (let ((case-fold-search t)
c3d9274a
BW
841 (headers-end (save-excursion
842 (mh-goto-header-end 0)
843 (point))))
847b8219
KH
844 (re-search-forward (format "^%s" field) headers-end t)))
845
c26cf6c8 846(defun mh-goto-header-end (arg)
bdcfe844 847 "Move the cursor ARG lines after the header."
9303c8db 848 (if (re-search-forward "^-*$" nil nil)
c26cf6c8
RS
849 (forward-line arg)))
850
c3d9274a
BW
851(defun mh-extract-from-header-value ()
852 "Extract From: string from header."
853 (save-excursion
854 (if (not (mh-goto-header-field "From:"))
924df208 855 nil
c3d9274a
BW
856 (skip-chars-forward " \t")
857 (buffer-substring-no-properties
858 (point) (progn (mh-header-field-end)(point))))))
c26cf6c8
RS
859
860\f
861
862;;; Mode for composing and sending a draft message.
863
bdcfe844 864(put 'mh-letter-mode 'mode-class 'special)
c26cf6c8 865
cee9f5c6 866;; Menu extracted from mh-menubar.el V1.1 (31 July 2001)
bdcfe844 867(eval-when-compile (defvar mh-letter-menu nil))
f0d73c14
BW
868(easy-menu-define
869 mh-letter-menu mh-letter-mode-map "Menu for MH-E letter mode."
870 '("Letter"
871 ["Send This Draft" mh-send-letter t]
872 ["Split Current Line" mh-open-line t]
873 ["Check Recipient" mh-check-whom t]
874 ["Yank Current Message" mh-yank-cur-msg t]
875 ["Insert a Message..." mh-insert-letter t]
876 ["Insert Signature" mh-insert-signature t]
877 ("Encrypt/Sign Message"
878 ["Sign Message"
0c47b17c 879 mh-mml-secure-message-sign mh-pgp-support-flag]
f0d73c14 880 ["Encrypt Message"
0c47b17c 881 mh-mml-secure-message-encrypt mh-pgp-support-flag]
f0d73c14 882 ["Sign+Encrypt Message"
0c47b17c 883 mh-mml-secure-message-signencrypt mh-pgp-support-flag]
f0d73c14 884 ["Disable Security"
0c47b17c 885 mh-mml-unsecure-message mh-pgp-support-flag]
f0d73c14
BW
886 "--"
887 "Security Method"
888 ["PGP (MIME)" (setq mh-mml-method-default "pgpmime")
889 :style radio
890 :selected (equal mh-mml-method-default "pgpmime")]
891 ["PGP" (setq mh-mml-method-default "pgp")
892 :style radio
893 :selected (equal mh-mml-method-default "pgp")]
894 ["S/MIME" (setq mh-mml-method-default "smime")
895 :style radio
896 :selected (equal mh-mml-method-default "smime")]
897 "--"
898 ["Save Method as Default"
899 (customize-save-variable 'mh-mml-method-default mh-mml-method-default) t]
900 )
0c47b17c
BW
901 ["Compose Insertion..." mh-compose-insertion t]
902 ["Compose Compressed tar (MH)..."
903 mh-mh-compose-external-compressed-tar t]
904 ["Compose Get File (MH)..." mh-mh-compose-anon-ftp t]
905 ["Compose Forward..." mh-compose-forward t]
f0d73c14 906 ;; The next two will have to be merged. But I also need to make sure the
0c47b17c
BW
907 ;; user can't mix tags of both types.
908 ["Pull in All Compositions (MH)"
909 mh-mh-to-mime (mh-mh-directive-present-p)]
910 ["Pull in All Compositions (MML)"
911 mh-mml-to-mime (mh-mml-tag-present-p)]
912 ["Revert to Non-MIME Edit (MH)"
913 mh-mh-to-mime-undo (equal mh-compose-insertion 'mh)]
f0d73c14 914 ["Kill This Draft" mh-fully-kill-draft t]))
c26cf6c8 915
cee9f5c6
BW
916\f
917
bdcfe844 918;;; Help Messages
cee9f5c6
BW
919
920;; Group messages logically, more or less.
bdcfe844
BW
921(defvar mh-letter-mode-help-messages
922 '((nil
923 "Send letter: \\[mh-send-letter]"
924 "\t\tOpen line: \\[mh-open-line]\n"
925 "Kill letter: \\[mh-fully-kill-draft]"
926 "\t\tInsert:\n"
927 "Check recipients: \\[mh-check-whom]"
928 "\t\t Current message: \\[mh-yank-cur-msg]\n"
f0d73c14
BW
929 "\t\t Attachment: \\[mh-compose-insertion]\n"
930 "\t\t Message to forward: \\[mh-compose-forward]\n"
931 " "
932 "Security:"
933 "\t\t Encrypt message: \\[mh-mml-secure-message-encrypt]"
934 "\t\t Sign+Encrypt message: \\[mh-mml-secure-message-signencrypt]"
935 "\t\t Sign message: \\[mh-mml-secure-message-sign]\n"
bdcfe844 936 " "
f0d73c14 937 "\t\t Signature: \\[mh-insert-signature]"))
bdcfe844
BW
938 "Key binding cheat sheet.
939
2dcf34f9
BW
940This is an associative array which is used to show the most
941common commands. The key is a prefix char. The value is one or
942more strings which are concatenated together and displayed in the
943minibuffer if ? is pressed after the prefix character. The
944special key nil is used to display the non-prefixed commands.
bdcfe844 945
2dcf34f9
BW
946The substitutions described in `substitute-command-keys' are
947performed as well.")
bdcfe844 948
c3d9274a 949;;;###mh-autoload
bdcfe844
BW
950(defun mh-fill-paragraph-function (arg)
951 "Fill paragraph at or after point.
2dcf34f9
BW
952Prefix ARG means justify as well. This function enables
953`fill-paragraph' to work better in MH-Letter mode (see
954`mh-letter-mode')."
bdcfe844
BW
955 (interactive "P")
956 (let ((fill-paragraph-function) (fill-prefix))
957 (if (mh-in-header-p)
958 (mail-mode-fill-paragraph arg)
959 (fill-paragraph arg))))
c26cf6c8 960
924df208
BW
961;; Avoid compiler warnings in XEmacs and Emacs 20
962(eval-when-compile
963 (defvar tool-bar-mode)
964 (defvar tool-bar-map))
965
1dd9796d
SD
966(defvar mh-letter-buttons-init-flag nil)
967
c26cf6c8 968;;;###autoload
f7c4478f 969(define-derived-mode mh-letter-mode text-mode "MH-Letter"
d1699462 970 "Mode for composing letters in MH-E\\<mh-letter-mode-map>.
a1b4049d 971
2dcf34f9
BW
972When you have finished composing, type \\[mh-send-letter] to send
973the message using the MH mail handling system.
c26cf6c8 974
2dcf34f9
BW
975There are two types of tags used by MH-E when composing MIME
976messages: MML and MH. The option `mh-compose-insertion' controls
977what type of tags are inserted by MH-E commands. These tags can
978be converted to MIME body parts by running \\[mh-mh-to-mime] for
979MH-style directives or \\[mh-mml-to-mime] for MML tags.
c26cf6c8 980
d1699462
BW
981Options that control this mode can be changed with
982\\[customize-group]; specify the \"mh-compose\" group.
c26cf6c8 983
a1b4049d
BW
984When a message is composed, the hooks `text-mode-hook' and
985`mh-letter-mode-hook' are run.
c26cf6c8 986
a1b4049d 987\\{mh-letter-mode-map}"
f0d73c14 988 (mh-find-path)
c26cf6c8
RS
989 (make-local-variable 'mh-send-args)
990 (make-local-variable 'mh-annotate-char)
991 (make-local-variable 'mh-annotate-field)
992 (make-local-variable 'mh-previous-window-config)
993 (make-local-variable 'mh-sent-from-folder)
994 (make-local-variable 'mh-sent-from-msg)
1dd9796d
SD
995 (mh-do-in-gnu-emacs
996 (unless mh-letter-buttons-init-flag
997 (mh-tool-bar-letter-buttons-init)
998 (setq mh-letter-buttons-init-flag t)))
f0d73c14
BW
999 ;; Set the local value of mh-mail-header-separator according to what is
1000 ;; present in the buffer...
1001 (set (make-local-variable 'mh-mail-header-separator)
1002 (save-excursion
1003 (goto-char (mh-mail-header-end))
1004 (buffer-substring-no-properties (point) (line-end-position))))
c26cf6c8 1005 (make-local-variable 'mail-header-separator)
a1b4049d 1006 (setq mail-header-separator mh-mail-header-separator) ;override sendmail.el
bdcfe844
BW
1007 (make-local-variable 'mh-help-messages)
1008 (setq mh-help-messages mh-letter-mode-help-messages)
a66894d8
BW
1009 (setq buffer-invisibility-spec '((vanish . t) t))
1010 (set (make-local-variable 'line-move-ignore-invisible) t)
1011
a1b4049d
BW
1012 ;; From sendmail.el for proper paragraph fill
1013 ;; sendmail.el also sets a normal-auto-fill-function (not done here)
1014 (make-local-variable 'paragraph-separate)
1015 (make-local-variable 'paragraph-start)
1016 (make-local-variable 'fill-paragraph-function)
bdcfe844 1017 (setq fill-paragraph-function 'mh-fill-paragraph-function)
a1b4049d
BW
1018 (make-local-variable 'adaptive-fill-regexp)
1019 (setq adaptive-fill-regexp
c3d9274a
BW
1020 (concat adaptive-fill-regexp
1021 "\\|[ \t]*[-[:alnum:]]*>+[ \t]*"))
a1b4049d
BW
1022 (make-local-variable 'adaptive-fill-first-line-regexp)
1023 (setq adaptive-fill-first-line-regexp
c3d9274a
BW
1024 (concat adaptive-fill-first-line-regexp
1025 "\\|[ \t]*[-[:alnum:]]*>+[ \t]*"))
a1b4049d
BW
1026 ;; `-- ' precedes the signature. `-----' appears at the start of the
1027 ;; lines that delimit forwarded messages.
1028 ;; Lines containing just >= 3 dashes, perhaps after whitespace,
1029 ;; are also sometimes used and should be separators.
1030 (setq paragraph-start (concat (regexp-quote mail-header-separator)
c3d9274a
BW
1031 "\\|\t*\\([-|#;>* ]\\|(?[0-9]+[.)]\\)+$"
1032 "\\|[ \t]*[[:alnum:]]*>+[ \t]*$\\|[ \t]*$\\|"
1033 "-- $\\|---+$\\|"
1034 page-delimiter))
a1b4049d
BW
1035 (setq paragraph-separate paragraph-start)
1036 ;; --- End of code from sendmail.el ---
1037
924df208
BW
1038 ;; Enable undo since a show-mode buffer might have been reused.
1039 (buffer-enable-undo)
a66894d8 1040 (set (make-local-variable 'tool-bar-map) mh-letter-tool-bar-map)
fa4075e3 1041 (mh-funcall-if-exists mh-tool-bar-init :letter)
a1b4049d
BW
1042 (make-local-variable 'font-lock-defaults)
1043 (cond
553fb735
BW
1044 ((or (equal mh-highlight-citation-style 'font-lock)
1045 (equal mh-highlight-citation-style 'gnus))
bdcfe844
BW
1046 ;; Let's use font-lock even if gnus is used in show-mode. The reason
1047 ;; is that gnus uses static text properties which are not appropriate
1048 ;; for a buffer that will be edited. So the choice here is either fontify
1049 ;; the citations and header...
a66894d8 1050 (setq font-lock-defaults '(mh-letter-font-lock-keywords t)))
a1b4049d 1051 (t
bdcfe844 1052 ;; ...or the header only
a1b4049d
BW
1053 (setq font-lock-defaults '(mh-show-font-lock-keywords t))))
1054 (easy-menu-add mh-letter-menu)
a1b4049d 1055 (setq fill-column mh-letter-fill-column)
c3d9274a 1056 ;; If text-mode-hook turned on auto-fill, tune it for messages
f7c4478f
SM
1057 (when auto-fill-function
1058 (make-local-variable 'auto-fill-function)
1059 (setq auto-fill-function 'mh-auto-fill-for-letter)))
c26cf6c8 1060
a66894d8
BW
1061(defun mh-font-lock-field-data (limit)
1062 "Find header field region between point and LIMIT."
1063 (and (< (point) (mh-letter-header-end))
1064 (< (point) limit)
1065 (let ((end (min limit (mh-letter-header-end)))
1066 (point (point))
1067 data-end data-begin field)
1068 (end-of-line)
1069 (setq data-end (if (re-search-forward "^[^ \t]" end t)
1070 (match-beginning 0)
1071 end))
1072 (goto-char (1- data-end))
1073 (if (not (re-search-backward "\\(^[^ \t][^:]*\\):[ \t]*" nil t))
1074 (setq data-begin (point-min))
1075 (setq data-begin (match-end 0))
1076 (setq field (match-string 1)))
1077 (setq data-begin (max point data-begin))
a66894d8 1078 (goto-char (if (equal point data-end) (1+ data-end) data-end))
a05fcb7d
BW
1079 (cond ((and field (mh-letter-skipped-header-field-p field))
1080 (set-match-data nil)
1081 nil)
1082 (t (set-match-data
1083 (list data-begin data-end data-begin data-end))
1084 t)))))
a66894d8
BW
1085
1086(defun mh-letter-header-end ()
f0d73c14 1087 "Find the end of the message header.
2dcf34f9
BW
1088This function is to be used only for font locking. It works by
1089searching for `mh-mail-header-separator' in the buffer."
a66894d8 1090 (save-excursion
f0d73c14
BW
1091 (goto-char (point-min))
1092 (cond ((equal mh-mail-header-separator "") (point-min))
1093 ((search-forward (format "\n%s\n" mh-mail-header-separator) nil t)
1094 (line-beginning-position 0))
1095 (t (point-min)))))
a66894d8 1096
c26cf6c8 1097(defun mh-auto-fill-for-letter ()
bdcfe844 1098 "Perform auto-fill for message.
2dcf34f9
BW
1099Header is treated specially by inserting a tab before continuation
1100lines."
c26cf6c8 1101 (if (mh-in-header-p)
9303c8db 1102 (let ((fill-prefix "\t"))
c3d9274a 1103 (do-auto-fill))
9303c8db 1104 (do-auto-fill)))
c26cf6c8 1105
a1b4049d 1106(defun mh-insert-header-separator ()
bdcfe844 1107 "Insert `mh-mail-header-separator', if absent."
c26cf6c8 1108 (save-excursion
a1b4049d
BW
1109 (goto-char (point-min))
1110 (rfc822-goto-eoh)
1111 (if (looking-at "$")
c3d9274a 1112 (insert mh-mail-header-separator))))
c26cf6c8 1113
c3d9274a 1114;;;###mh-autoload
c26cf6c8 1115(defun mh-to-field ()
0c47b17c 1116 "Move to specified header field.
c26cf6c8 1117The field is indicated by the previous keystroke (the last keystroke
2dcf34f9
BW
1118of the command) according to the list in the variable
1119`mh-to-field-choices'. Create the field if it does not exist. Set the
1120mark to point before moving."
c26cf6c8
RS
1121 (interactive)
1122 (expand-abbrev)
847b8219 1123 (let ((target (cdr (or (assoc (char-to-string (logior last-input-char ?`))
c3d9274a
BW
1124 mh-to-field-choices)
1125 ;; also look for a char for version 4 compat
1126 (assoc (logior last-input-char ?`)
1127 mh-to-field-choices))))
1128 (case-fold-search t))
c26cf6c8
RS
1129 (push-mark)
1130 (cond ((mh-position-on-field target)
c3d9274a
BW
1131 (let ((eol (point)))
1132 (skip-chars-backward " \t")
1133 (delete-region (point) eol))
1134 (if (and (not (eq (logior last-input-char ?`) ?s))
1135 (save-excursion
1136 (backward-char 1)
1137 (not (looking-at "[:,]"))))
1138 (insert ", ")
1139 (insert " ")))
1140 (t
1141 (if (mh-position-on-field "To:")
1142 (forward-line 1))
1143 (insert (format "%s \n" target))
1144 (backward-char 1)))))
c26cf6c8 1145
c3d9274a 1146;;;###mh-autoload
c26cf6c8 1147(defun mh-to-fcc (&optional folder)
0c47b17c 1148 "Move to \"Fcc:\" header field.
2dcf34f9
BW
1149This command will prompt you for the FOLDER name in which to file a
1150copy of the draft."
c26cf6c8
RS
1151 (interactive)
1152 (or folder
1153 (setq folder (mh-prompt-for-folder
c3d9274a
BW
1154 "Fcc"
1155 (or (and mh-default-folder-for-message-function
1156 (save-excursion
1157 (goto-char (point-min))
1158 (funcall
1159 mh-default-folder-for-message-function)))
1160 "")
1161 t)))
c26cf6c8
RS
1162 (let ((last-input-char ?\C-f))
1163 (expand-abbrev)
1164 (save-excursion
1165 (mh-to-field)
1166 (insert (if (mh-folder-name-p folder)
c3d9274a
BW
1167 (substring folder 1)
1168 folder)))))
c26cf6c8 1169
f0d73c14
BW
1170(defun mh-file-is-vcard-p (file)
1171 "Return t if FILE is a .vcf vcard."
1172 (let ((case-fold-search t))
1173 (and (stringp file)
1174 (file-exists-p file)
1175 (or (and (not (mh-have-file-command))
1176 (not (null (string-match "\.vcf$" file))))
1177 (and (mh-have-file-command)
1178 (string-equal "text/x-vcard" (mh-file-mime-type file)))))))
1179
c3d9274a 1180;;;###mh-autoload
f0d73c14 1181(defun mh-insert-signature (&optional file)
0c47b17c 1182 "Insert signature in message.
d1699462 1183
0c47b17c
BW
1184This command inserts your signature at the current cursor location.
1185
1186By default, the text of your signature is taken from the file
d1699462
BW
1187\"~/.signature\". You can read from other sources by changing the
1188option `mh-signature-file-name'.
0c47b17c 1189
d1699462
BW
1190A signature separator (\"-- \") will be added if the signature block
1191does not contain one and `mh-signature-separator-flag' is on.
0c47b17c 1192
d1699462
BW
1193The hook `mh-insert-signature-hook' is run after the signature is
1194inserted. Hook functions may access the actual name of the file or the
1195function used to insert the signature with `mh-signature-file-name'.
0c47b17c 1196
d1699462
BW
1197The signature can also be inserted using Identities (see
1198`mh-identity-list').
1199
1200In a program, you can pass in a signature FILE."
0c47b17c 1201 (interactive)
f0d73c14
BW
1202 (save-excursion
1203 (insert "\n")
1204 (let ((mh-signature-file-name (or file mh-signature-file-name))
0c47b17c
BW
1205 (mh-mh-p (mh-mh-directive-present-p))
1206 (mh-mml-p (mh-mml-tag-present-p)))
f0d73c14
BW
1207 (save-restriction
1208 (narrow-to-region (point) (point))
1209 (cond
1210 ((mh-file-is-vcard-p mh-signature-file-name)
0c47b17c 1211 (if (equal mh-compose-insertion 'mml)
f0d73c14
BW
1212 (insert "<#part type=\"text/x-vcard\" filename=\""
1213 mh-signature-file-name
1214 "\" disposition=inline description=VCard>\n<#/part>")
1215 (insert "#text/x-vcard; name=\""
1216 (file-name-nondirectory mh-signature-file-name)
1217 "\" [VCard] " (expand-file-name mh-signature-file-name))))
1218 (t
1219 (cond
0c47b17c 1220 (mh-mh-p
f0d73c14
BW
1221 (insert "#\n" "Content-Description: Signature\n"))
1222 (mh-mml-p
1223 (mml-insert-tag 'part 'type "text/plain" 'disposition "inline"
1224 'description "Signature")))
1225 (cond ((null mh-signature-file-name))
1226 ((and (stringp mh-signature-file-name)
1227 (file-readable-p mh-signature-file-name))
1228 (insert-file-contents mh-signature-file-name))
1229 ((functionp mh-signature-file-name)
1230 (funcall mh-signature-file-name)))))
1231 (save-restriction
1232 (widen)
f15ced66 1233 (run-hooks 'mh-insert-signature-hook))
f0d73c14
BW
1234 (goto-char (point-min))
1235 (when (and (not (mh-file-is-vcard-p mh-signature-file-name))
1236 mh-signature-separator-flag
1237 (> (point-max) (point-min))
1238 (not (mh-signature-separator-p)))
0c47b17c 1239 (cond (mh-mh-p
f0d73c14
BW
1240 (forward-line 2))
1241 (mh-mml-p
1242 (forward-line 1)))
1243 (insert mh-signature-separator))
1244 (if (not (> (point-max) (point-min)))
1245 (message "No signature found")))))
2450bd29 1246 (force-mode-line-update))
c26cf6c8 1247
c3d9274a 1248;;;###mh-autoload
c26cf6c8 1249(defun mh-check-whom ()
0c47b17c 1250 "Verify recipients, showing expansion of any aliases.
2dcf34f9
BW
1251
1252This command expands aliases so you can check the actual address(es)
1253in the alias. A new buffer named \"*MH-E Recipients*\" is created with
1254the output of \"whom\"."
c26cf6c8 1255 (interactive)
847b8219 1256 (let ((file-name buffer-file-name))
c26cf6c8
RS
1257 (save-buffer)
1258 (message "Checking recipients...")
3d7ca223 1259 (mh-in-show-buffer (mh-recipients-buffer)
c26cf6c8
RS
1260 (bury-buffer (current-buffer))
1261 (erase-buffer)
1262 (mh-exec-cmd-output "whom" t file-name))
1263 (message "Checking recipients...done")))
1264
3d7ca223
BW
1265(defun mh-tidy-draft-buffer ()
1266 "Run when a draft buffer is destroyed."
1267 (let ((buffer (get-buffer mh-recipients-buffer)))
1268 (if buffer
1269 (kill-buffer buffer))))
1270
c26cf6c8
RS
1271\f
1272
1273;;; Routines to compose and send a letter.
1274
bdcfe844 1275(defun mh-insert-x-face ()
924df208 1276 "Append X-Face, Face or X-Image-URL field to header.
bdcfe844
BW
1277If the field already exists, this function does nothing."
1278 (when (and (file-exists-p mh-x-face-file)
1279 (file-readable-p mh-x-face-file))
1280 (save-excursion
924df208
BW
1281 (unless (or (mh-position-on-field "X-Face")
1282 (mh-position-on-field "Face")
1283 (mh-position-on-field "X-Image-URL"))
1284 (save-excursion
1285 (goto-char (+ (point) (cadr (insert-file-contents mh-x-face-file))))
1286 (if (not (looking-at "^"))
1287 (insert "\n")))
1288 (unless (looking-at "\\(X-Face\\|Face\\|X-Image-URL\\): ")
1289 (insert "X-Face: "))))))
1290
1291(defvar mh-x-mailer-string nil
1292 "*String containing the contents of the X-Mailer header field.
2dcf34f9
BW
1293If nil, this variable is initialized to show the version of MH-E,
1294Emacs, and MH the first time a message is composed.")
bdcfe844 1295
a1b4049d 1296(defun mh-insert-x-mailer ()
bdcfe844
BW
1297 "Append an X-Mailer field to the header.
1298The versions of MH-E, Emacs, and MH are shown."
a1b4049d 1299 ;; Lazily initialize mh-x-mailer-string.
a66894d8 1300 (when (and mh-insert-x-mailer-flag (null mh-x-mailer-string))
f0d73c14
BW
1301 (setq mh-x-mailer-string
1302 (format "MH-E %s; %s; %sEmacs %s"
1303 mh-version mh-variant-in-use
1304 (if mh-xemacs-flag "X" "GNU ")
1305 (cond ((not mh-xemacs-flag) emacs-version)
1306 ((string-match "[0-9.]*\\( +\([ a-z]+[0-9]+\)\\)?"
1307 emacs-version)
1308 (match-string 0 emacs-version))
1309 (t (format "%s.%s" emacs-major-version
1310 emacs-minor-version))))))
a1b4049d
BW
1311 ;; Insert X-Mailer, but only if it doesn't already exist.
1312 (save-excursion
a66894d8
BW
1313 (when (and mh-insert-x-mailer-flag
1314 (null (mh-goto-header-field "X-Mailer")))
c3d9274a 1315 (mh-insert-fields "X-Mailer:" mh-x-mailer-string))))
a1b4049d 1316
bdcfe844
BW
1317(defun mh-regexp-in-field-p (regexp &rest fields)
1318 "Non-nil means REGEXP was found in FIELDS."
1319 (save-excursion
1320 (let ((search-result nil)
1321 (field))
1322 (while fields
1323 (setq field (car fields))
1324 (if (and (mh-goto-header-field field)
1325 (re-search-forward
1326 regexp (save-excursion (mh-header-field-end)(point)) t))
1327 (setq fields nil
1328 search-result t)
1329 (setq fields (cdr fields))))
1330 search-result)))
1331
a66894d8
BW
1332;;;###mh-autoload
1333(defun mh-insert-auto-fields (&optional non-interactive)
3b463df0 1334 "Insert custom fields if recipient is found in `mh-auto-fields-list'.
a66894d8 1335
2dcf34f9
BW
1336Sets buffer-local `mh-insert-auto-fields-done-local' when done
1337and inserted something. If NON-INTERACTIVE is non-nil, do not be
1338verbose and only attempt matches if
1339`mh-insert-auto-fields-done-local' is nil.
1340
1341An `identity' entry is skipped if one was already entered
1342manually.
f0d73c14
BW
1343
1344Return t if fields added; otherwise return nil."
a66894d8 1345 (interactive)
f0d73c14
BW
1346 (when (or (not non-interactive)
1347 (not mh-insert-auto-fields-done-local))
a66894d8 1348 (save-excursion
f0d73c14
BW
1349 (when (and (or (mh-goto-header-field "To:")
1350 (mh-goto-header-field "cc:")))
1351 (let ((list mh-auto-fields-list)
1352 (fields-inserted nil))
a66894d8
BW
1353 (while list
1354 (let ((regexp (nth 0 (car list)))
1355 (entries (nth 1 (car list))))
1356 (when (mh-regexp-in-field-p regexp "To:" "cc:")
1357 (setq mh-insert-auto-fields-done-local t)
f0d73c14 1358 (setq fields-inserted t)
a66894d8 1359 (if (not non-interactive)
f0d73c14 1360 (message "Fields for %s added" regexp))
a66894d8
BW
1361 (let ((entry-list entries))
1362 (while entry-list
1363 (let ((field (caar entry-list))
1364 (value (cdar entry-list)))
1365 (cond
f0d73c14 1366 ((equal ":identity" field)
a05fcb7d
BW
1367 (when ;;(and (not mh-identity-local)
1368 ;; Bug 1204506. But do we need to be able
1369 ;; to set an identity manually that won't be
1370 ;; overridden by mh-insert-auto-fields?
1371 (assoc value mh-identity-list)
1372 ;;)
a66894d8
BW
1373 (mh-insert-identity value)))
1374 (t
1375 (mh-modify-header-field field value
1376 (equal field "From")))))
1377 (setq entry-list (cdr entry-list))))))
f0d73c14
BW
1378 (setq list (cdr list)))
1379 fields-inserted)))))
924df208
BW
1380
1381(defun mh-modify-header-field (field value &optional overwrite-flag)
1382 "To header FIELD add VALUE.
2dcf34f9
BW
1383If OVERWRITE-FLAG is non-nil then the old value, if present, is
1384discarded."
a66894d8
BW
1385 (cond ((and overwrite-flag
1386 (mh-goto-header-field (concat field ":")))
1387 (insert " " value)
1388 (delete-region (point) (line-end-position)))
1389 ((and (not overwrite-flag)
1390 (mh-regexp-in-field-p (concat "\\b" value "\\b") field))
1391 ;; Already there, do nothing.
1392 )
1393 ((and (not overwrite-flag)
1394 (mh-goto-header-field (concat field ":")))
1395 (insert " " value ","))
1396 (t
1397 (mh-goto-header-end 0)
1398 (insert field ": " value "\n"))))
1399
c26cf6c8 1400(defun mh-compose-and-send-mail (draft send-args
c3d9274a
BW
1401 sent-from-folder sent-from-msg
1402 to subject cc
1403 annotate-char annotate-field
1404 config)
bdcfe844
BW
1405 "Edit and compose a draft message in buffer DRAFT and send or save it.
1406SEND-ARGS is the argument passed to the send command.
2dcf34f9
BW
1407SENT-FROM-FOLDER is buffer containing scan listing of current folder,
1408or nil if none exists.
bdcfe844
BW
1409SENT-FROM-MSG is the message number or sequence name or nil.
1410The TO, SUBJECT, and CC fields are passed to the
1411`mh-compose-letter-function'.
2dcf34f9
BW
1412If ANNOTATE-CHAR is non-null, it is used to notate the scan listing of
1413the message. In that case, the ANNOTATE-FIELD is used to build a
1414string for `mh-annotate-msg'.
1415CONFIG is the window configuration to restore after sending the
1416letter."
c26cf6c8
RS
1417 (pop-to-buffer draft)
1418 (mh-letter-mode)
a1506d29 1419
f0d73c14 1420 ;; Insert identity.
c3d9274a 1421 (if (and (boundp 'mh-identity-default)
924df208
BW
1422 mh-identity-default
1423 (not mh-identity-local))
c3d9274a 1424 (mh-insert-identity mh-identity-default))
f0d73c14
BW
1425 (mh-identity-make-menu)
1426 (easy-menu-add mh-identity-menu)
a1506d29 1427
f0d73c14 1428 ;; Insert extra fields.
a66894d8
BW
1429 (mh-insert-x-mailer)
1430 (mh-insert-x-face)
f0d73c14 1431
a66894d8
BW
1432 (mh-letter-hide-all-skipped-fields)
1433
c26cf6c8
RS
1434 (setq mh-sent-from-folder sent-from-folder)
1435 (setq mh-sent-from-msg sent-from-msg)
1436 (setq mh-send-args send-args)
1437 (setq mh-annotate-char annotate-char)
1438 (setq mh-annotate-field annotate-field)
1439 (setq mh-previous-window-config config)
3d7ca223
BW
1440 (setq mode-line-buffer-identification (list " {%b}"))
1441 (mh-logo-display)
924df208 1442 (mh-make-local-hook 'kill-buffer-hook)
3d7ca223 1443 (add-hook 'kill-buffer-hook 'mh-tidy-draft-buffer nil t)
c26cf6c8 1444 (if (and (boundp 'mh-compose-letter-function)
c3d9274a 1445 mh-compose-letter-function)
c26cf6c8 1446 ;; run-hooks will not pass arguments.
847b8219 1447 (let ((value mh-compose-letter-function))
c3d9274a
BW
1448 (if (and (listp value) (not (eq (car value) 'lambda)))
1449 (while value
1450 (funcall (car value) to subject cc)
1451 (setq value (cdr value)))
1452 (funcall mh-compose-letter-function to subject cc)))))
c26cf6c8 1453
bdcfe844
BW
1454(defun mh-letter-mode-message ()
1455 "Display a help message for users of `mh-letter-mode'.
1456This should be the last function called when composing the draft."
1457 (message "%s" (substitute-command-keys
c3d9274a 1458 (concat "Type \\[mh-send-letter] to send message, "
f0d73c14
BW
1459 "\\[mh-help] for help"))))
1460
1461(defun mh-ascii-buffer-p ()
1462 "Check if current buffer is entirely composed of ASCII.
2dcf34f9
BW
1463The function doesn't work for XEmacs since `find-charset-region'
1464doesn't exist there."
f0d73c14
BW
1465 (loop for charset in (mh-funcall-if-exists
1466 find-charset-region (point-min) (point-max))
1467 unless (eq charset 'ascii) return nil
1468 finally return t))
c26cf6c8 1469
c3d9274a 1470;;;###mh-autoload
c26cf6c8 1471(defun mh-send-letter (&optional arg)
0c47b17c 1472 "Save draft and send message.
0c47b17c 1473
d1699462
BW
1474When you are all through editing a message, you send it with this
1475command. You can give a prefix argument ARG to monitor the first stage
1476of the delivery\; this output can be found in a buffer called \"*MH-E
1477Mail Delivery*\".
1478
1479The hook `mh-before-send-letter-hook' is run at the beginning of the
1480this command. For example, if you want to check your spelling in your
1481message before sending, add the `ispell-message' function.
0c47b17c 1482
d1699462
BW
1483In case the MH \"send\" program is installed under a different name,
1484use `mh-send-prog' to tell MH-E the name."
c26cf6c8
RS
1485 (interactive "P")
1486 (run-hooks 'mh-before-send-letter-hook)
f0d73c14
BW
1487 (if (and (mh-insert-auto-fields t)
1488 mh-auto-fields-prompt-flag
1489 (goto-char (point-min)))
1490 (if (not (y-or-n-p "Auto fields inserted, send? "))
1491 (error "Send aborted")))
0c47b17c
BW
1492 (cond ((mh-mh-directive-present-p)
1493 (mh-mh-to-mime))
1494 ((or (mh-mml-tag-present-p) (not (mh-ascii-buffer-p)))
924df208 1495 (mh-mml-to-mime)))
c26cf6c8
RS
1496 (save-buffer)
1497 (message "Sending...")
1498 (let ((draft-buffer (current-buffer))
c3d9274a
BW
1499 (file-name buffer-file-name)
1500 (config mh-previous-window-config)
1501 (coding-system-for-write
1502 (if (and (local-variable-p 'buffer-file-coding-system
a1b4049d 1503 (current-buffer)) ;XEmacs needs two args
c3d9274a
BW
1504 ;; We're not sure why, but buffer-file-coding-system
1505 ;; tends to get set to undecided-unix.
1506 (not (memq buffer-file-coding-system
1507 '(undecided undecided-unix undecided-dos))))
1508 buffer-file-coding-system
1509 (or (and (boundp 'sendmail-coding-system) sendmail-coding-system)
1510 (and (boundp 'default-buffer-file-coding-system )
a1b4049d 1511 default-buffer-file-coding-system)
c3d9274a 1512 'iso-latin-1))))
bdcfe844
BW
1513 ;; The default BCC encapsulation will make a MIME message unreadable.
1514 ;; With nmh use the -mime arg to prevent this.
f0d73c14 1515 (if (and (mh-variant-p 'nmh)
c3d9274a
BW
1516 (mh-goto-header-field "Bcc:")
1517 (mh-goto-header-field "Content-Type:"))
1518 (setq mh-send-args (format "-mime %s" mh-send-args)))
c26cf6c8 1519 (cond (arg
924df208 1520 (pop-to-buffer mh-mail-delivery-buffer)
c3d9274a
BW
1521 (erase-buffer)
1522 (mh-exec-cmd-output mh-send-prog t "-watch" "-nopush"
1523 "-nodraftfolder" mh-send-args file-name)
1524 (goto-char (point-max)) ; show the interesting part
1525 (recenter -1)
1526 (set-buffer draft-buffer)) ; for annotation below
1527 (t
3d7ca223 1528 (mh-exec-cmd-daemon mh-send-prog nil "-nodraftfolder" "-noverbose"
c3d9274a 1529 mh-send-args file-name)))
c26cf6c8 1530 (if mh-annotate-char
c3d9274a
BW
1531 (mh-annotate-msg mh-sent-from-msg
1532 mh-sent-from-folder
1533 mh-annotate-char
1534 "-component" mh-annotate-field
1535 "-text" (format "\"%s %s\""
1536 (mh-get-header-field "To:")
1537 (mh-get-header-field "Cc:"))))
c26cf6c8
RS
1538
1539 (cond ((or (not arg)
c3d9274a
BW
1540 (y-or-n-p "Kill draft buffer? "))
1541 (kill-buffer draft-buffer)
1542 (if config
1543 (set-window-configuration config))))
c26cf6c8 1544 (if arg
c3d9274a 1545 (message "Sending...done")
c26cf6c8
RS
1546 (message "Sending...backgrounded"))))
1547
c3d9274a 1548;;;###mh-autoload
847b8219 1549(defun mh-insert-letter (folder message verbatim)
0c47b17c 1550 "Insert a message.
2dcf34f9
BW
1551
1552This command prompts you for the FOLDER and MESSAGE number and inserts
1553the message, indented by `mh-ins-buf-prefix' (\"> \") unless
1554`mh-yank-behavior' is set to one of the supercite flavors in which
1555case supercite is used to format the message. Certain undesirable
1556header fields (see `mh-invisible-header-fields-compiled') are removed
1557before insertion.
1558
1559If given a prefix argument VERBATIM, the header is left intact, the
1560message is not indented, and \"> \" is not inserted before each line.
1561This command leaves the mark before the letter and point after it."
c26cf6c8 1562 (interactive
847b8219 1563 (list (mh-prompt-for-folder "Message from" mh-sent-from-folder nil)
a10f4ace
BW
1564 (read-string (concat "Message number"
1565 (if (numberp mh-sent-from-msg)
1566 (format " (default %d): " mh-sent-from-msg)
1567 ": ")))
c3d9274a 1568 current-prefix-arg))
c26cf6c8
RS
1569 (save-restriction
1570 (narrow-to-region (point) (point))
1571 (let ((start (point-min)))
f0d73c14
BW
1572 (if (and (equal message "") (numberp mh-sent-from-msg))
1573 (setq message (int-to-string mh-sent-from-msg)))
bdcfe844
BW
1574 (insert-file-contents
1575 (expand-file-name message (mh-expand-file-name folder)))
1576 (when (not verbatim)
f0d73c14 1577 (mh-clean-msg-header start mh-invisible-header-fields-compiled nil)
c3d9274a
BW
1578 (goto-char (point-max)) ;Needed for sc-cite-original
1579 (push-mark) ;Needed for sc-cite-original
1580 (goto-char (point-min)) ;Needed for sc-cite-original
bdcfe844
BW
1581 (mh-insert-prefix-string mh-ins-buf-prefix)))))
1582
1583(defun mh-extract-from-attribution ()
1584 "Extract phrase or comment from From header field."
1585 (save-excursion
1586 (if (not (mh-goto-header-field "From: "))
1587 nil
1588 (skip-chars-forward " ")
1589 (cond
1590 ((looking-at "\"\\([^\"\n]+\\)\" \\(<.+>\\)")
f0d73c14 1591 (format "%s %s " (match-string 1)(match-string 2)))
bdcfe844 1592 ((looking-at "\\([^<\n]+<.+>\\)$")
f0d73c14 1593 (format "%s " (match-string 1)))
bdcfe844 1594 ((looking-at "\\([^ ]+@[^ ]+\\) +(\\(.+\\))$")
f0d73c14 1595 (format "%s <%s> " (match-string 2)(match-string 1)))
bdcfe844 1596 ((looking-at " *\\(.+\\)$")
f0d73c14 1597 (format "%s " (match-string 1)))))))
c26cf6c8 1598
c3d9274a 1599;;;###mh-autoload
c26cf6c8
RS
1600(defun mh-yank-cur-msg ()
1601 "Insert the current message into the draft buffer.
0c47b17c 1602
2dcf34f9
BW
1603It is often useful to insert a snippet of text from a letter that
1604someone mailed to provide some context for your reply. This
1605command does this by adding an attribution, yanking a portion of
1606text from the message to which you're replying, and inserting
1607`mh-ins-buf-prefix' (`> ') before each line.
0c47b17c
BW
1608
1609The attribution consists of the sender's name and email address
1610followed by the content of the `mh-extract-from-attribution-verb'
1611option.
1612
2dcf34f9
BW
1613You can also turn on the `mh-delete-yanked-msg-window-flag'
1614option to delete the window containing the original message after
1615yanking it to make more room on your screen for your reply.
0c47b17c 1616
2dcf34f9
BW
1617You can control how the message to which you are replying is
1618yanked into your reply using `mh-yank-behavior'.
0c47b17c 1619
2dcf34f9
BW
1620If this isn't enough, you can gain full control over the
1621appearance of the included text by setting `mail-citation-hook'
1622to a function that modifies it. For example, if you set this hook
4023e353 1623to `trivial-cite' (which is NOT part of Emacs), set
2dcf34f9 1624`mh-yank-behavior' to \"Body and Header\" (see URL
d1699462
BW
1625`http://shasta.cs.uiuc.edu/~lrclause/tc.html').
1626
2dcf34f9
BW
1627Note that if `mail-citation-hook' is set, `mh-ins-buf-prefix' is
1628not inserted. If the option `mh-yank-behavior' is set to one of
1629the supercite flavors, the hook `mail-citation-hook' is ignored
1630and `mh-ins-buf-prefix' is not inserted."
c26cf6c8 1631 (interactive)
bdcfe844
BW
1632 (if (and mh-sent-from-folder
1633 (save-excursion (set-buffer mh-sent-from-folder) mh-show-buffer)
1634 (save-excursion (set-buffer mh-sent-from-folder)
1635 (get-buffer mh-show-buffer))
1636 mh-sent-from-msg)
c26cf6c8 1637 (let ((to-point (point))
c3d9274a
BW
1638 (to-buffer (current-buffer)))
1639 (set-buffer mh-sent-from-folder)
1640 (if mh-delete-yanked-msg-window-flag
1641 (delete-windows-on mh-show-buffer))
1642 (set-buffer mh-show-buffer) ; Find displayed message
1643 (let* ((from-attr (mh-extract-from-attribution))
1644 (yank-region (mh-mark-active-p nil))
bdcfe844
BW
1645 (mh-ins-str
1646 (cond ((and yank-region
0c47b17c
BW
1647 (or (eq 'supercite mh-yank-behavior)
1648 (eq 'autosupercite mh-yank-behavior)
1649 (eq t mh-yank-behavior)))
bdcfe844
BW
1650 ;; supercite needs the full header
1651 (concat
924df208 1652 (buffer-substring (point-min) (mh-mail-header-end))
bdcfe844
BW
1653 "\n"
1654 (buffer-substring (region-beginning) (region-end))))
1655 (yank-region
1656 (buffer-substring (region-beginning) (region-end)))
0c47b17c
BW
1657 ((or (eq 'body mh-yank-behavior)
1658 (eq 'attribution mh-yank-behavior)
1659 (eq 'autoattrib mh-yank-behavior))
bdcfe844
BW
1660 (buffer-substring
1661 (save-excursion
1662 (goto-char (point-min))
1663 (mh-goto-header-end 1)
1664 (point))
1665 (point-max)))
0c47b17c
BW
1666 ((or (eq 'supercite mh-yank-behavior)
1667 (eq 'autosupercite mh-yank-behavior)
1668 (eq t mh-yank-behavior))
bdcfe844
BW
1669 (buffer-substring (point-min) (point-max)))
1670 (t
1671 (buffer-substring (point) (point-max))))))
c3d9274a
BW
1672 (set-buffer to-buffer)
1673 (save-restriction
1674 (narrow-to-region to-point to-point)
1675 (insert (mh-filter-out-non-text mh-ins-str))
bdcfe844 1676 (goto-char (point-max)) ;Needed for sc-cite-original
c3d9274a 1677 (push-mark) ;Needed for sc-cite-original
bdcfe844 1678 (goto-char (point-min)) ;Needed for sc-cite-original
c3d9274a 1679 (mh-insert-prefix-string mh-ins-buf-prefix)
0c47b17c
BW
1680 (when (or (eq 'attribution mh-yank-behavior)
1681 (eq 'autoattrib mh-yank-behavior))
f0d73c14
BW
1682 (insert from-attr)
1683 (mh-identity-insert-attribution-verb nil)
1684 (insert "\n\n"))
c3d9274a
BW
1685 ;; If the user has selected a region, he has already "edited" the
1686 ;; text, so leave the cursor at the end of the yanked text. In
1687 ;; either case, leave a mark at the opposite end of the included
1688 ;; text to make it easy to jump or delete to the other end of the
1689 ;; text.
1690 (push-mark)
1691 (goto-char (point-max))
1692 (if (null yank-region)
1693 (mh-exchange-point-and-mark-preserving-active-mark)))))
847b8219 1694 (error "There is no current message")))
c26cf6c8 1695
bdcfe844
BW
1696(defun mh-filter-out-non-text (string)
1697 "Return STRING but without adornments such as MIME buttons and smileys."
1698 (with-temp-buffer
1699 ;; Insert the string to filter
1700 (insert string)
1701 (goto-char (point-min))
a1506d29 1702
bdcfe844
BW
1703 ;; Remove the MIME buttons
1704 (let ((can-move-forward t)
1705 (in-button nil))
1706 (while can-move-forward
1707 (cond ((and (not (get-text-property (point) 'mh-data))
1708 in-button)
c3d9274a 1709 (delete-region (1- (point)) (point))
bdcfe844
BW
1710 (setq in-button nil))
1711 ((get-text-property (point) 'mh-data)
1712 (delete-region (point)
1713 (save-excursion (forward-line) (point)))
1714 (setq in-button t))
1715 (t (setq can-move-forward (= (forward-line) 0))))))
1716
1717 ;; Return the contents without properties... This gets rid of emphasis
1718 ;; and smileys
1719 (buffer-substring-no-properties (point-min) (point-max))))
c26cf6c8
RS
1720
1721(defun mh-insert-prefix-string (mh-ins-string)
bdcfe844 1722 "Insert prefix string before each line in buffer.
2dcf34f9
BW
1723The inserted letter is cited using `sc-cite-original' if
1724`mh-yank-behavior' is one of 'supercite or 'autosupercite.
1725Otherwise, simply insert MH-INS-STRING before each line."
847b8219 1726 (goto-char (point-min))
0c47b17c
BW
1727 (cond ((or (eq mh-yank-behavior 'supercite)
1728 (eq mh-yank-behavior 'autosupercite))
bdcfe844
BW
1729 (sc-cite-original))
1730 (mail-citation-hook
c3d9274a
BW
1731 (run-hooks 'mail-citation-hook))
1732 (mh-yank-hooks ;old hook name
1733 (run-hooks 'mh-yank-hooks))
1734 (t
1735 (or (bolp) (forward-line 1))
bdcfe844
BW
1736 (while (< (point) (point-max))
1737 (insert mh-ins-string)
1738 (forward-line 1))
1739 (goto-char (point-min))))) ;leave point like sc-cite-original
c26cf6c8 1740
c3d9274a 1741;;;###mh-autoload
c26cf6c8 1742(defun mh-fully-kill-draft ()
0c47b17c 1743 "Quit editing and delete draft message.
2dcf34f9
BW
1744If for some reason you are not happy with the draft, you can use
1745the this command to kill the draft buffer and delete the draft
1746message. Use the \\[kill-buffer] command if you don't want to
1747delete the draft message."
c26cf6c8
RS
1748 (interactive)
1749 (if (y-or-n-p "Kill draft message? ")
1750 (let ((config mh-previous-window-config))
c3d9274a
BW
1751 (if (file-exists-p buffer-file-name)
1752 (delete-file buffer-file-name))
1753 (set-buffer-modified-p nil)
1754 (kill-buffer (buffer-name))
1755 (message "")
1756 (if config
1757 (set-window-configuration config)))
c26cf6c8
RS
1758 (error "Message not killed")))
1759
a1b4049d 1760(defun mh-current-fill-prefix ()
bdcfe844 1761 "Return the `fill-prefix' on the current line as a string."
a1b4049d
BW
1762 (save-excursion
1763 (beginning-of-line)
1764 ;; This assumes that the major-mode sets up adaptive-fill-regexp
1765 ;; correctly such as mh-letter-mode or sendmail.el's mail-mode. But
1766 ;; perhaps I should use the variable and simply inserts its value here,
1767 ;; and set it locally in a let scope. --psg
1768 (if (re-search-forward adaptive-fill-regexp nil t)
1769 (match-string 0)
1770 "")))
1771
c3d9274a 1772;;;###mh-autoload
a1b4049d
BW
1773(defun mh-open-line ()
1774 "Insert a newline and leave point after it.
2dcf34f9
BW
1775
1776This command is similar to the \\[open-line] command in that it
1777inserts a newline after point. It differs in that it also inserts
1778the right number of quoting characters and spaces so that the
1779next line begins in the same column as it was. This is useful
1780when breaking up paragraphs in replies."
a1b4049d
BW
1781 (interactive)
1782 (let ((column (current-column))
a1b4049d
BW
1783 (prefix (mh-current-fill-prefix)))
1784 (if (> (length prefix) column)
1785 (message "Sorry, point seems to be within the line prefix")
1786 (newline 2)
1787 (insert prefix)
1788 (while (> column (current-column))
1789 (insert " "))
1790 (forward-line -1))))
847b8219 1791
924df208
BW
1792(mh-do-in-xemacs (defvar mail-abbrevs))
1793
79af55a7
BW
1794(defmacro mh-display-completion-list-compat (word choices)
1795 "Completes WORD from CHOICES using `display-completion-list'.
1796Calls `display-completion-list' correctly in older environments.
2dcf34f9
BW
1797Versions of Emacs prior to version 22 lacked a COMMON-SUBSTRING
1798argument which is used to highlight the next possible character you
1799can enter in the current list of completions."
79af55a7
BW
1800 (if (>= emacs-major-version 22)
1801 `(display-completion-list (all-completions ,word ,choices) ,word)
1802 `(display-completion-list (all-completions ,word ,choices))))
1803
a66894d8
BW
1804;;;###mh-autoload
1805(defun mh-complete-word (word choices begin end)
1806 "Complete WORD at from CHOICES.
1807Any match found replaces the text from BEGIN to END."
e495eaec
BW
1808 (let ((completion (try-completion word choices))
1809 (completions-buffer "*Completions*"))
a66894d8 1810 (cond ((eq completion t)
e495eaec
BW
1811 (ignore-errors
1812 (kill-buffer completions-buffer))
a66894d8
BW
1813 (message "Completed: %s" word))
1814 ((null completion)
e495eaec
BW
1815 (ignore-errors
1816 (kill-buffer completions-buffer))
a66894d8
BW
1817 (message "No completion for `%s'" word))
1818 ((stringp completion)
1819 (if (equal word completion)
e495eaec 1820 (with-output-to-temp-buffer completions-buffer
79af55a7 1821 (mh-display-completion-list-compat word choices))
e495eaec
BW
1822 (ignore-errors
1823 (kill-buffer completions-buffer))
a66894d8
BW
1824 (delete-region begin end)
1825 (insert completion))))))
1826
1827;;;###mh-autoload
1828(defun mh-beginning-of-word (&optional n)
1829 "Return position of the N th word backwards."
1830 (unless n (setq n 1))
1831 (let ((syntax-table (syntax-table)))
1832 (unwind-protect
1833 (save-excursion
f0d73c14 1834 (mh-mail-abbrev-make-syntax-table)
a66894d8
BW
1835 (set-syntax-table mail-abbrev-syntax-table)
1836 (backward-word n)
1837 (point))
1838 (set-syntax-table syntax-table))))
1839
924df208
BW
1840(defun mh-folder-expand-at-point ()
1841 "Do folder name completion in Fcc header field."
1842 (let* ((end (point))
a66894d8 1843 (beg (mh-beginning-of-word))
924df208
BW
1844 (folder (buffer-substring beg end))
1845 (leading-plus (and (> (length folder) 0) (equal (aref folder 0) ?+)))
1846 (last-slash (mh-search-from-end ?/ folder))
1847 (prefix (and last-slash (substring folder 0 last-slash)))
a66894d8
BW
1848 (choices (mapcar #'(lambda (x)
1849 (list (cond (prefix (format "%s/%s" prefix x))
1850 (leading-plus (format "+%s" x))
1851 (t x))))
1852 (mh-folder-completion-function folder nil t))))
1853 (mh-complete-word folder choices beg end)))
1854
a66894d8
BW
1855(defvar mh-letter-complete-function-alist
1856 '((cc . mh-alias-letter-expand-alias)
1857 (bcc . mh-alias-letter-expand-alias)
1858 (dcc . mh-alias-letter-expand-alias)
1859 (fcc . mh-folder-expand-at-point)
1860 (from . mh-alias-letter-expand-alias)
1861 (mail-followup-to . mh-alias-letter-expand-alias)
1862 (reply-to . mh-alias-letter-expand-alias)
1863 (to . mh-alias-letter-expand-alias))
1864 "Alist of header fields and completion functions to use.")
924df208 1865
c3d9274a
BW
1866(defun mh-letter-complete (arg)
1867 "Perform completion on header field or word preceding point.
2dcf34f9 1868If the field contains addresses (for example, \"To:\" or \"Cc:\")
4023e353 1869or folders (for example, \"Fcc:\") then this command will
2dcf34f9
BW
1870provide alias completion. In the body of the message, this
1871command runs `mh-letter-complete-function' instead, which is set
1872to \"'ispell-complete-word\" by default. This command takes a
1873prefix argument ARG that is passed to the
0c47b17c 1874`mh-letter-complete-function'."
c3d9274a 1875 (interactive "P")
a66894d8
BW
1876 (let ((func nil))
1877 (cond ((not (mh-in-header-p))
1878 (funcall mh-letter-complete-function arg))
1879 ((setq func (cdr (assoc (mh-letter-header-field-at-point)
1880 mh-letter-complete-function-alist)))
1881 (funcall func))
1882 (t (funcall mh-letter-complete-function arg)))))
1883
1884(defun mh-letter-complete-or-space (arg)
1885 "Perform completion or insert space.
2dcf34f9
BW
1886Turn on the `mh-compose-space-does-completion-flag' option to use
1887this command to perform completion in the header. Otherwise, a
1888space is inserted.
a66894d8
BW
1889
1890ARG is the number of spaces inserted."
1891 (interactive "p")
1892 (let ((func nil)
1893 (end-of-prev (save-excursion
1894 (goto-char (mh-beginning-of-word))
1895 (mh-beginning-of-word -1))))
1896 (cond ((not mh-compose-space-does-completion-flag)
1897 (self-insert-command arg))
1898 ((not (mh-in-header-p)) (self-insert-command arg))
1899 ((> (point) end-of-prev) (self-insert-command arg))
1900 ((setq func (cdr (assoc (mh-letter-header-field-at-point)
1901 mh-letter-complete-function-alist)))
1902 (funcall func))
1903 (t (self-insert-command arg)))))
1904
1905(defun mh-letter-confirm-address ()
0c47b17c 1906 "Flash alias expansion.
2dcf34f9
BW
1907Addresses are separated by a comma\; and when you press the
1908comma, this command flashes the alias expansion in the minibuffer
1909if `mh-alias-flash-on-comma' is turned on."
a66894d8
BW
1910 (interactive)
1911 (cond ((not (mh-in-header-p)) (self-insert-command 1))
1912 ((eq (cdr (assoc (mh-letter-header-field-at-point)
1913 mh-letter-complete-function-alist))
1914 'mh-alias-letter-expand-alias)
1915 (mh-alias-reload-maybe)
1916 (mh-alias-minibuffer-confirm-address))
1917 (t (self-insert-command 1))))
1918
1919(defvar mh-letter-header-field-regexp "^\\([A-Za-z][A-Za-z0-9-]*\\):")
1920
1921(defun mh-letter-header-field-at-point ()
1922 "Return the header field name at point.
2dcf34f9
BW
1923A symbol is returned whose name is the string obtained by
1924downcasing the field name."
a66894d8
BW
1925 (save-excursion
1926 (end-of-line)
1927 (and (re-search-backward mh-letter-header-field-regexp nil t)
1928 (intern (downcase (match-string 1))))))
1929
1930;;;###mh-autoload
1931(defun mh-letter-next-header-field-or-indent (arg)
1932 "Move to next field or indent depending on point.
2dcf34f9
BW
1933Within the header of the message, this command moves between
1934fields, but skips those fields listed in
1935`mh-compose-skipped-header-fields'. After the last field, this
1936command then moves point to the message body before cycling back
1937to the first field. If point is already past the first line of
1938the message body, then this command indents by calling
1939`indent-relative' with the given prefix argument ARG."
a66894d8
BW
1940 (interactive "P")
1941 (let ((header-end (save-excursion
1942 (goto-char (mh-mail-header-end))
1943 (forward-line)
1944 (point))))
1945 (if (> (point) header-end)
1946 (indent-relative arg)
1947 (mh-letter-next-header-field))))
1948
1949(defun mh-letter-next-header-field ()
1950 "Cycle to the next header field.
2dcf34f9
BW
1951If we are at the last header field go to the start of the message
1952body."
a66894d8
BW
1953 (let ((header-end (mh-mail-header-end)))
1954 (cond ((>= (point) header-end) (goto-char (point-min)))
1955 ((< (point) (progn
1956 (beginning-of-line)
1957 (re-search-forward mh-letter-header-field-regexp
1958 (line-end-position) t)
1959 (point)))
1960 (beginning-of-line))
1961 (t (end-of-line)))
1962 (cond ((re-search-forward mh-letter-header-field-regexp header-end t)
1963 (if (mh-letter-skipped-header-field-p (match-string 1))
1964 (mh-letter-next-header-field)
1965 (mh-letter-skip-leading-whitespace-in-header-field)))
1966 (t (goto-char header-end)
1967 (forward-line)))))
1968
1969;;;###mh-autoload
1970(defun mh-letter-previous-header-field ()
1971 "Cycle to the previous header field.
2dcf34f9
BW
1972This command moves backwards between the fields and cycles to the
1973body of the message after the first field. Unlike the
1974\\[mh-letter-next-header-field-or-indent] command, it will always
1975take point to the last field from anywhere in the body."
a66894d8
BW
1976 (interactive)
1977 (let ((header-end (mh-mail-header-end)))
1978 (if (>= (point) header-end)
1979 (goto-char header-end)
1980 (mh-header-field-beginning))
1981 (cond ((re-search-backward mh-letter-header-field-regexp nil t)
1982 (if (mh-letter-skipped-header-field-p (match-string 1))
1983 (mh-letter-previous-header-field)
1984 (goto-char (match-end 0))
1985 (mh-letter-skip-leading-whitespace-in-header-field)))
1986 (t (goto-char header-end)
1987 (forward-line)))))
1988
1989(defun mh-letter-skipped-header-field-p (field)
1990 "Check if FIELD is to be skipped."
1991 (let ((field (downcase field)))
1992 (loop for x in mh-compose-skipped-header-fields
1993 when (equal (downcase x) field) return t
1994 finally return nil)))
1995
1996(defun mh-letter-skip-leading-whitespace-in-header-field ()
1997 "Skip leading whitespace in a header field.
2dcf34f9
BW
1998If the header field doesn't have at least one space after the
1999colon then a space character is added."
a66894d8
BW
2000 (let ((need-space t))
2001 (while (memq (char-after) '(?\t ?\ ))
2002 (forward-char)
2003 (setq need-space nil))
2004 (when need-space (insert " "))))
2005
2006(defvar mh-hidden-header-keymap
2007 (let ((map (make-sparse-keymap)))
2008 (mh-do-in-gnu-emacs
2009 (define-key map [mouse-2] 'mh-letter-toggle-header-field-display-button))
2010 (mh-do-in-xemacs
2011 (define-key map '(button2)
2012 'mh-letter-toggle-header-field-display-button))
2013 map))
2014
2015(defun mh-letter-toggle-header-field-display-button (event)
2016 "Toggle header field display at location of EVENT.
2dcf34f9
BW
2017This function does the same thing as
2018`mh-letter-toggle-header-field-display' except that it is
2019callable from a mouse button."
a66894d8
BW
2020 (interactive "e")
2021 (mh-do-at-event-location event
2022 (mh-letter-toggle-header-field-display nil)))
2023
2024(defun mh-letter-toggle-header-field-display (arg)
2025 "Toggle display of header field at point.
a66894d8 2026
2dcf34f9
BW
2027Use this command to display truncated header fields. This command
2028is a toggle so entering it again will hide the field. This
2029command takes a prefix argument ARG: if negative then the field
2030is hidden, if positive then the field is displayed."
a66894d8
BW
2031 (interactive (list nil))
2032 (when (and (mh-in-header-p)
2033 (progn
2034 (end-of-line)
2035 (re-search-backward mh-letter-header-field-regexp nil t)))
2036 (let ((buffer-read-only nil)
2037 (modified-flag (buffer-modified-p))
2038 (begin (point))
2039 end)
2040 (end-of-line)
2041 (setq end (1- (if (re-search-forward "^[^ \t]" nil t)
2042 (match-beginning 0)
2043 (point-max))))
2044 (goto-char begin)
2045 ;; Make it clickable...
2046 (add-text-properties begin end `(keymap ,mh-hidden-header-keymap
2047 mouse-face highlight))
2048 (unwind-protect
2049 (cond ((or (and (not arg)
2050 (text-property-any begin end 'invisible 'vanish))
2051 (and (numberp arg) (>= arg 0))
2052 (and (eq arg 'long) (> (line-beginning-position 5) end)))
2053 (remove-text-properties begin end '(invisible nil))
2054 (search-forward ":" (line-end-position) t)
2055 (mh-letter-skip-leading-whitespace-in-header-field))
0c47b17c
BW
2056 ;; XXX Redesign to make usable by user. Perhaps use a positive
2057 ;; numeric prefix to make that many lines visible.
a66894d8
BW
2058 ((eq arg 'long)
2059 (end-of-line 4)
2060 (mh-letter-truncate-header-field end)
2061 (beginning-of-line))
2062 (t (end-of-line)
2063 (mh-letter-truncate-header-field end)
2064 (beginning-of-line)))
2065 (set-buffer-modified-p modified-flag)))))
2066
2067(defun mh-letter-truncate-header-field (end)
2068 "Replace text from current line till END with an ellipsis.
2069If the current line is too long truncate a part of it as well."
2070 (let ((max-len (min (window-width) 62)))
2071 (when (> (+ (current-column) 4) max-len)
2072 (backward-char (- (+ (current-column) 5) max-len)))
2073 (when (> end (point))
2074 (add-text-properties (point) end '(invisible vanish)))))
2075
2076(defun mh-letter-hide-all-skipped-fields ()
2077 "Hide all skipped fields."
2078 (save-excursion
2079 (goto-char (point-min))
2080 (save-restriction
2081 (narrow-to-region (point) (mh-mail-header-end))
2082 (while (re-search-forward mh-letter-header-field-regexp nil t)
2083 (if (mh-letter-skipped-header-field-p (match-string 1))
2084 (mh-letter-toggle-header-field-display -1)
2085 (mh-letter-toggle-header-field-display 'long))
2086 (beginning-of-line 2)))))
2087
2088(defun mh-interactive-read-address (prompt)
2089 "Read an address.
2dcf34f9
BW
2090If `mh-compose-prompt-flag' is non-nil, then read an address with
2091PROMPT.
a66894d8
BW
2092Otherwise return the empty string."
2093 (if mh-compose-prompt-flag (mh-read-address prompt) ""))
2094
2095(defun mh-interactive-read-string (prompt)
2096 "Read a string.
2dcf34f9
BW
2097If `mh-compose-prompt-flag' is non-nil, then read a string with
2098PROMPT.
a66894d8
BW
2099Otherwise return the empty string."
2100 (if mh-compose-prompt-flag (read-string prompt) ""))
2101
2102(defun mh-letter-adjust-point ()
2103 "Move cursor to first header field if are using the no prompt mode."
2104 (unless mh-compose-prompt-flag
2105 (goto-char (point-max))
2106 (mh-letter-next-header-field)))
a1506d29 2107
cee9f5c6
BW
2108\f
2109
2110;;; Build mh-letter-mode keymap
2111
2112;; If this changes, modify mh-letter-mode-help-messages accordingly, above.
a1b4049d 2113(gnus-define-keys mh-letter-mode-map
f0d73c14
BW
2114 " " mh-letter-complete-or-space
2115 "," mh-letter-confirm-address
c3d9274a 2116 "\C-c?" mh-help
f0d73c14
BW
2117 "\C-c\C-\\" mh-fully-kill-draft ;if no C-q
2118 "\C-c\C-^" mh-insert-signature ;if no C-s
c3d9274a
BW
2119 "\C-c\C-c" mh-send-letter
2120 "\C-c\C-d" mh-insert-identity
0c47b17c 2121 "\C-c\C-e" mh-mh-to-mime
c3d9274a
BW
2122 "\C-c\C-f\C-b" mh-to-field
2123 "\C-c\C-f\C-c" mh-to-field
2124 "\C-c\C-f\C-d" mh-to-field
2125 "\C-c\C-f\C-f" mh-to-fcc
2126 "\C-c\C-f\C-r" mh-to-field
2127 "\C-c\C-f\C-s" mh-to-field
2128 "\C-c\C-f\C-t" mh-to-field
2129 "\C-c\C-fb" mh-to-field
2130 "\C-c\C-fc" mh-to-field
2131 "\C-c\C-fd" mh-to-field
2132 "\C-c\C-ff" mh-to-fcc
2133 "\C-c\C-fr" mh-to-field
2134 "\C-c\C-fs" mh-to-field
2135 "\C-c\C-ft" mh-to-field
2136 "\C-c\C-i" mh-insert-letter
f0d73c14 2137 "\C-c\C-m\C-e" mh-mml-secure-message-encrypt
c3d9274a 2138 "\C-c\C-m\C-f" mh-compose-forward
0c47b17c 2139 "\C-c\C-m\C-g" mh-mh-compose-anon-ftp
c3d9274a
BW
2140 "\C-c\C-m\C-i" mh-compose-insertion
2141 "\C-c\C-m\C-m" mh-mml-to-mime
f0d73c14
BW
2142 "\C-c\C-m\C-n" mh-mml-unsecure-message
2143 "\C-c\C-m\C-s" mh-mml-secure-message-sign
0c47b17c
BW
2144 "\C-c\C-m\C-t" mh-mh-compose-external-compressed-tar
2145 "\C-c\C-m\C-u" mh-mh-to-mime-undo
2146 "\C-c\C-m\C-x" mh-mh-compose-external-type
f0d73c14
BW
2147 "\C-c\C-mee" mh-mml-secure-message-encrypt
2148 "\C-c\C-mes" mh-mml-secure-message-signencrypt
c3d9274a 2149 "\C-c\C-mf" mh-compose-forward
0c47b17c 2150 "\C-c\C-mg" mh-mh-compose-anon-ftp
c3d9274a
BW
2151 "\C-c\C-mi" mh-compose-insertion
2152 "\C-c\C-mm" mh-mml-to-mime
f0d73c14
BW
2153 "\C-c\C-mn" mh-mml-unsecure-message
2154 "\C-c\C-mse" mh-mml-secure-message-signencrypt
2155 "\C-c\C-mss" mh-mml-secure-message-sign
0c47b17c
BW
2156 "\C-c\C-mt" mh-mh-compose-external-compressed-tar
2157 "\C-c\C-mu" mh-mh-to-mime-undo
2158 "\C-c\C-mx" mh-mh-compose-external-type
c3d9274a
BW
2159 "\C-c\C-o" mh-open-line
2160 "\C-c\C-q" mh-fully-kill-draft
c3d9274a 2161 "\C-c\C-s" mh-insert-signature
f0d73c14 2162 "\C-c\C-t" mh-letter-toggle-header-field-display
c3d9274a
BW
2163 "\C-c\C-w" mh-check-whom
2164 "\C-c\C-y" mh-yank-cur-msg
f0d73c14 2165 "\C-c\M-d" mh-insert-auto-fields
a66894d8
BW
2166 "\M-\t" mh-letter-complete
2167 "\t" mh-letter-next-header-field-or-indent
f0d73c14 2168 [backtab] mh-letter-previous-header-field)
a1b4049d
BW
2169
2170;; "C-c /" prefix is used in mh-letter-mode by pgp.el and mailcrypt.el.
2171
bdcfe844
BW
2172(provide 'mh-comp)
2173
cee9f5c6
BW
2174;; Local Variables:
2175;; indent-tabs-mode: nil
2176;; sentence-end-double-space: nil
2177;; End:
60370d40 2178
cee9f5c6 2179;; arch-tag: 62865511-e610-4923-b0b5-f45a8ab70a34
60370d40 2180;;; mh-comp.el ends here