*** empty log message ***
[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,
af435184 4;; 2000, 2001, 2002, 2003, 2004, 2005, 2006 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
af435184 465Also investigate the command \\[mh-edit-again] for another way to
2dcf34f9 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.
af435184
BW
1117
1118The field is indicated by the previous keystroke (the last
1119keystroke of the command) according to the list in the variable
1120`mh-to-field-choices'.
1121Create the field if it does not exist.
1122Set the mark to point before moving."
c26cf6c8
RS
1123 (interactive)
1124 (expand-abbrev)
847b8219 1125 (let ((target (cdr (or (assoc (char-to-string (logior last-input-char ?`))
c3d9274a
BW
1126 mh-to-field-choices)
1127 ;; also look for a char for version 4 compat
1128 (assoc (logior last-input-char ?`)
1129 mh-to-field-choices))))
1130 (case-fold-search t))
c26cf6c8
RS
1131 (push-mark)
1132 (cond ((mh-position-on-field target)
c3d9274a
BW
1133 (let ((eol (point)))
1134 (skip-chars-backward " \t")
1135 (delete-region (point) eol))
1136 (if (and (not (eq (logior last-input-char ?`) ?s))
1137 (save-excursion
1138 (backward-char 1)
1139 (not (looking-at "[:,]"))))
1140 (insert ", ")
1141 (insert " ")))
1142 (t
1143 (if (mh-position-on-field "To:")
1144 (forward-line 1))
1145 (insert (format "%s \n" target))
1146 (backward-char 1)))))
c26cf6c8 1147
c3d9274a 1148;;;###mh-autoload
c26cf6c8 1149(defun mh-to-fcc (&optional folder)
0c47b17c 1150 "Move to \"Fcc:\" header field.
af435184
BW
1151
1152This command will prompt you for the FOLDER name in which to file
1153a copy of the draft."
c26cf6c8
RS
1154 (interactive)
1155 (or folder
1156 (setq folder (mh-prompt-for-folder
c3d9274a
BW
1157 "Fcc"
1158 (or (and mh-default-folder-for-message-function
1159 (save-excursion
1160 (goto-char (point-min))
1161 (funcall
1162 mh-default-folder-for-message-function)))
1163 "")
1164 t)))
c26cf6c8
RS
1165 (let ((last-input-char ?\C-f))
1166 (expand-abbrev)
1167 (save-excursion
1168 (mh-to-field)
1169 (insert (if (mh-folder-name-p folder)
c3d9274a
BW
1170 (substring folder 1)
1171 folder)))))
c26cf6c8 1172
f0d73c14
BW
1173(defun mh-file-is-vcard-p (file)
1174 "Return t if FILE is a .vcf vcard."
1175 (let ((case-fold-search t))
1176 (and (stringp file)
1177 (file-exists-p file)
1178 (or (and (not (mh-have-file-command))
1179 (not (null (string-match "\.vcf$" file))))
1180 (and (mh-have-file-command)
1181 (string-equal "text/x-vcard" (mh-file-mime-type file)))))))
1182
c3d9274a 1183;;;###mh-autoload
f0d73c14 1184(defun mh-insert-signature (&optional file)
0c47b17c 1185 "Insert signature in message.
d1699462 1186
0c47b17c
BW
1187This command inserts your signature at the current cursor location.
1188
1189By default, the text of your signature is taken from the file
d1699462
BW
1190\"~/.signature\". You can read from other sources by changing the
1191option `mh-signature-file-name'.
0c47b17c 1192
d1699462
BW
1193A signature separator (\"-- \") will be added if the signature block
1194does not contain one and `mh-signature-separator-flag' is on.
0c47b17c 1195
d1699462
BW
1196The hook `mh-insert-signature-hook' is run after the signature is
1197inserted. Hook functions may access the actual name of the file or the
1198function used to insert the signature with `mh-signature-file-name'.
0c47b17c 1199
d1699462
BW
1200The signature can also be inserted using Identities (see
1201`mh-identity-list').
1202
1203In a program, you can pass in a signature FILE."
0c47b17c 1204 (interactive)
f0d73c14
BW
1205 (save-excursion
1206 (insert "\n")
1207 (let ((mh-signature-file-name (or file mh-signature-file-name))
0c47b17c
BW
1208 (mh-mh-p (mh-mh-directive-present-p))
1209 (mh-mml-p (mh-mml-tag-present-p)))
f0d73c14
BW
1210 (save-restriction
1211 (narrow-to-region (point) (point))
1212 (cond
1213 ((mh-file-is-vcard-p mh-signature-file-name)
0c47b17c 1214 (if (equal mh-compose-insertion 'mml)
f0d73c14
BW
1215 (insert "<#part type=\"text/x-vcard\" filename=\""
1216 mh-signature-file-name
1217 "\" disposition=inline description=VCard>\n<#/part>")
1218 (insert "#text/x-vcard; name=\""
1219 (file-name-nondirectory mh-signature-file-name)
1220 "\" [VCard] " (expand-file-name mh-signature-file-name))))
1221 (t
1222 (cond
0c47b17c 1223 (mh-mh-p
f0d73c14
BW
1224 (insert "#\n" "Content-Description: Signature\n"))
1225 (mh-mml-p
1226 (mml-insert-tag 'part 'type "text/plain" 'disposition "inline"
1227 'description "Signature")))
1228 (cond ((null mh-signature-file-name))
1229 ((and (stringp mh-signature-file-name)
1230 (file-readable-p mh-signature-file-name))
1231 (insert-file-contents mh-signature-file-name))
1232 ((functionp mh-signature-file-name)
1233 (funcall mh-signature-file-name)))))
1234 (save-restriction
1235 (widen)
f15ced66 1236 (run-hooks 'mh-insert-signature-hook))
f0d73c14
BW
1237 (goto-char (point-min))
1238 (when (and (not (mh-file-is-vcard-p mh-signature-file-name))
1239 mh-signature-separator-flag
1240 (> (point-max) (point-min))
1241 (not (mh-signature-separator-p)))
0c47b17c 1242 (cond (mh-mh-p
f0d73c14
BW
1243 (forward-line 2))
1244 (mh-mml-p
1245 (forward-line 1)))
1246 (insert mh-signature-separator))
1247 (if (not (> (point-max) (point-min)))
1248 (message "No signature found")))))
2450bd29 1249 (force-mode-line-update))
c26cf6c8 1250
c3d9274a 1251;;;###mh-autoload
c26cf6c8 1252(defun mh-check-whom ()
0c47b17c 1253 "Verify recipients, showing expansion of any aliases.
2dcf34f9
BW
1254
1255This command expands aliases so you can check the actual address(es)
1256in the alias. A new buffer named \"*MH-E Recipients*\" is created with
1257the output of \"whom\"."
c26cf6c8 1258 (interactive)
847b8219 1259 (let ((file-name buffer-file-name))
c26cf6c8
RS
1260 (save-buffer)
1261 (message "Checking recipients...")
3d7ca223 1262 (mh-in-show-buffer (mh-recipients-buffer)
c26cf6c8
RS
1263 (bury-buffer (current-buffer))
1264 (erase-buffer)
1265 (mh-exec-cmd-output "whom" t file-name))
1266 (message "Checking recipients...done")))
1267
3d7ca223
BW
1268(defun mh-tidy-draft-buffer ()
1269 "Run when a draft buffer is destroyed."
1270 (let ((buffer (get-buffer mh-recipients-buffer)))
1271 (if buffer
1272 (kill-buffer buffer))))
1273
c26cf6c8
RS
1274\f
1275
1276;;; Routines to compose and send a letter.
1277
bdcfe844 1278(defun mh-insert-x-face ()
924df208 1279 "Append X-Face, Face or X-Image-URL field to header.
bdcfe844
BW
1280If the field already exists, this function does nothing."
1281 (when (and (file-exists-p mh-x-face-file)
1282 (file-readable-p mh-x-face-file))
1283 (save-excursion
924df208
BW
1284 (unless (or (mh-position-on-field "X-Face")
1285 (mh-position-on-field "Face")
1286 (mh-position-on-field "X-Image-URL"))
1287 (save-excursion
1288 (goto-char (+ (point) (cadr (insert-file-contents mh-x-face-file))))
1289 (if (not (looking-at "^"))
1290 (insert "\n")))
1291 (unless (looking-at "\\(X-Face\\|Face\\|X-Image-URL\\): ")
1292 (insert "X-Face: "))))))
1293
1294(defvar mh-x-mailer-string nil
1295 "*String containing the contents of the X-Mailer header field.
2dcf34f9
BW
1296If nil, this variable is initialized to show the version of MH-E,
1297Emacs, and MH the first time a message is composed.")
bdcfe844 1298
a1b4049d 1299(defun mh-insert-x-mailer ()
bdcfe844
BW
1300 "Append an X-Mailer field to the header.
1301The versions of MH-E, Emacs, and MH are shown."
a1b4049d 1302 ;; Lazily initialize mh-x-mailer-string.
a66894d8 1303 (when (and mh-insert-x-mailer-flag (null mh-x-mailer-string))
f0d73c14
BW
1304 (setq mh-x-mailer-string
1305 (format "MH-E %s; %s; %sEmacs %s"
1306 mh-version mh-variant-in-use
1307 (if mh-xemacs-flag "X" "GNU ")
1308 (cond ((not mh-xemacs-flag) emacs-version)
1309 ((string-match "[0-9.]*\\( +\([ a-z]+[0-9]+\)\\)?"
1310 emacs-version)
1311 (match-string 0 emacs-version))
1312 (t (format "%s.%s" emacs-major-version
1313 emacs-minor-version))))))
a1b4049d
BW
1314 ;; Insert X-Mailer, but only if it doesn't already exist.
1315 (save-excursion
a66894d8
BW
1316 (when (and mh-insert-x-mailer-flag
1317 (null (mh-goto-header-field "X-Mailer")))
c3d9274a 1318 (mh-insert-fields "X-Mailer:" mh-x-mailer-string))))
a1b4049d 1319
bdcfe844
BW
1320(defun mh-regexp-in-field-p (regexp &rest fields)
1321 "Non-nil means REGEXP was found in FIELDS."
1322 (save-excursion
1323 (let ((search-result nil)
1324 (field))
1325 (while fields
1326 (setq field (car fields))
1327 (if (and (mh-goto-header-field field)
1328 (re-search-forward
1329 regexp (save-excursion (mh-header-field-end)(point)) t))
1330 (setq fields nil
1331 search-result t)
1332 (setq fields (cdr fields))))
1333 search-result)))
1334
a66894d8
BW
1335;;;###mh-autoload
1336(defun mh-insert-auto-fields (&optional non-interactive)
3b463df0 1337 "Insert custom fields if recipient is found in `mh-auto-fields-list'.
a66894d8 1338
af435184
BW
1339Sets buffer-local `mh-insert-auto-fields-done-local' if header
1340fields were added. If NON-INTERACTIVE is non-nil, perform actions
1341quietly and only if `mh-insert-auto-fields-done-local' is nil.
2dcf34f9
BW
1342
1343An `identity' entry is skipped if one was already entered
1344manually.
f0d73c14
BW
1345
1346Return t if fields added; otherwise return nil."
a66894d8 1347 (interactive)
f0d73c14
BW
1348 (when (or (not non-interactive)
1349 (not mh-insert-auto-fields-done-local))
a66894d8 1350 (save-excursion
f0d73c14
BW
1351 (when (and (or (mh-goto-header-field "To:")
1352 (mh-goto-header-field "cc:")))
1353 (let ((list mh-auto-fields-list)
1354 (fields-inserted nil))
a66894d8
BW
1355 (while list
1356 (let ((regexp (nth 0 (car list)))
1357 (entries (nth 1 (car list))))
1358 (when (mh-regexp-in-field-p regexp "To:" "cc:")
1359 (setq mh-insert-auto-fields-done-local t)
f0d73c14 1360 (setq fields-inserted t)
a66894d8 1361 (if (not non-interactive)
f0d73c14 1362 (message "Fields for %s added" regexp))
a66894d8
BW
1363 (let ((entry-list entries))
1364 (while entry-list
1365 (let ((field (caar entry-list))
1366 (value (cdar entry-list)))
1367 (cond
f0d73c14 1368 ((equal ":identity" field)
a05fcb7d
BW
1369 (when ;;(and (not mh-identity-local)
1370 ;; Bug 1204506. But do we need to be able
1371 ;; to set an identity manually that won't be
1372 ;; overridden by mh-insert-auto-fields?
1373 (assoc value mh-identity-list)
1374 ;;)
a66894d8
BW
1375 (mh-insert-identity value)))
1376 (t
1377 (mh-modify-header-field field value
1378 (equal field "From")))))
1379 (setq entry-list (cdr entry-list))))))
f0d73c14
BW
1380 (setq list (cdr list)))
1381 fields-inserted)))))
924df208
BW
1382
1383(defun mh-modify-header-field (field value &optional overwrite-flag)
1384 "To header FIELD add VALUE.
2dcf34f9
BW
1385If OVERWRITE-FLAG is non-nil then the old value, if present, is
1386discarded."
a66894d8
BW
1387 (cond ((and overwrite-flag
1388 (mh-goto-header-field (concat field ":")))
1389 (insert " " value)
1390 (delete-region (point) (line-end-position)))
1391 ((and (not overwrite-flag)
1392 (mh-regexp-in-field-p (concat "\\b" value "\\b") field))
1393 ;; Already there, do nothing.
1394 )
1395 ((and (not overwrite-flag)
1396 (mh-goto-header-field (concat field ":")))
1397 (insert " " value ","))
1398 (t
1399 (mh-goto-header-end 0)
1400 (insert field ": " value "\n"))))
1401
c26cf6c8 1402(defun mh-compose-and-send-mail (draft send-args
c3d9274a
BW
1403 sent-from-folder sent-from-msg
1404 to subject cc
1405 annotate-char annotate-field
1406 config)
bdcfe844
BW
1407 "Edit and compose a draft message in buffer DRAFT and send or save it.
1408SEND-ARGS is the argument passed to the send command.
2dcf34f9
BW
1409SENT-FROM-FOLDER is buffer containing scan listing of current folder,
1410or nil if none exists.
bdcfe844
BW
1411SENT-FROM-MSG is the message number or sequence name or nil.
1412The TO, SUBJECT, and CC fields are passed to the
1413`mh-compose-letter-function'.
2dcf34f9
BW
1414If ANNOTATE-CHAR is non-null, it is used to notate the scan listing of
1415the message. In that case, the ANNOTATE-FIELD is used to build a
1416string for `mh-annotate-msg'.
1417CONFIG is the window configuration to restore after sending the
1418letter."
c26cf6c8
RS
1419 (pop-to-buffer draft)
1420 (mh-letter-mode)
a1506d29 1421
f0d73c14 1422 ;; Insert identity.
c3d9274a 1423 (if (and (boundp 'mh-identity-default)
924df208
BW
1424 mh-identity-default
1425 (not mh-identity-local))
c3d9274a 1426 (mh-insert-identity mh-identity-default))
f0d73c14
BW
1427 (mh-identity-make-menu)
1428 (easy-menu-add mh-identity-menu)
a1506d29 1429
f0d73c14 1430 ;; Insert extra fields.
a66894d8
BW
1431 (mh-insert-x-mailer)
1432 (mh-insert-x-face)
f0d73c14 1433
a66894d8
BW
1434 (mh-letter-hide-all-skipped-fields)
1435
c26cf6c8
RS
1436 (setq mh-sent-from-folder sent-from-folder)
1437 (setq mh-sent-from-msg sent-from-msg)
1438 (setq mh-send-args send-args)
1439 (setq mh-annotate-char annotate-char)
1440 (setq mh-annotate-field annotate-field)
1441 (setq mh-previous-window-config config)
3d7ca223
BW
1442 (setq mode-line-buffer-identification (list " {%b}"))
1443 (mh-logo-display)
924df208 1444 (mh-make-local-hook 'kill-buffer-hook)
3d7ca223 1445 (add-hook 'kill-buffer-hook 'mh-tidy-draft-buffer nil t)
c26cf6c8 1446 (if (and (boundp 'mh-compose-letter-function)
c3d9274a 1447 mh-compose-letter-function)
c26cf6c8 1448 ;; run-hooks will not pass arguments.
847b8219 1449 (let ((value mh-compose-letter-function))
c3d9274a
BW
1450 (if (and (listp value) (not (eq (car value) 'lambda)))
1451 (while value
1452 (funcall (car value) to subject cc)
1453 (setq value (cdr value)))
1454 (funcall mh-compose-letter-function to subject cc)))))
c26cf6c8 1455
bdcfe844
BW
1456(defun mh-letter-mode-message ()
1457 "Display a help message for users of `mh-letter-mode'.
1458This should be the last function called when composing the draft."
1459 (message "%s" (substitute-command-keys
c3d9274a 1460 (concat "Type \\[mh-send-letter] to send message, "
f0d73c14
BW
1461 "\\[mh-help] for help"))))
1462
1463(defun mh-ascii-buffer-p ()
1464 "Check if current buffer is entirely composed of ASCII.
2dcf34f9
BW
1465The function doesn't work for XEmacs since `find-charset-region'
1466doesn't exist there."
f0d73c14
BW
1467 (loop for charset in (mh-funcall-if-exists
1468 find-charset-region (point-min) (point-max))
1469 unless (eq charset 'ascii) return nil
1470 finally return t))
c26cf6c8 1471
c3d9274a 1472;;;###mh-autoload
c26cf6c8 1473(defun mh-send-letter (&optional arg)
0c47b17c 1474 "Save draft and send message.
0c47b17c 1475
d1699462
BW
1476When you are all through editing a message, you send it with this
1477command. You can give a prefix argument ARG to monitor the first stage
1478of the delivery\; this output can be found in a buffer called \"*MH-E
1479Mail Delivery*\".
1480
af435184
BW
1481The hook `mh-before-send-letter-hook' is run at the beginning of
1482this command. For example, if you want to check your spelling in
1483your message before sending, add the function `ispell-message'.
0c47b17c 1484
d1699462
BW
1485In case the MH \"send\" program is installed under a different name,
1486use `mh-send-prog' to tell MH-E the name."
c26cf6c8
RS
1487 (interactive "P")
1488 (run-hooks 'mh-before-send-letter-hook)
f0d73c14
BW
1489 (if (and (mh-insert-auto-fields t)
1490 mh-auto-fields-prompt-flag
1491 (goto-char (point-min)))
1492 (if (not (y-or-n-p "Auto fields inserted, send? "))
1493 (error "Send aborted")))
0c47b17c
BW
1494 (cond ((mh-mh-directive-present-p)
1495 (mh-mh-to-mime))
1496 ((or (mh-mml-tag-present-p) (not (mh-ascii-buffer-p)))
924df208 1497 (mh-mml-to-mime)))
c26cf6c8
RS
1498 (save-buffer)
1499 (message "Sending...")
1500 (let ((draft-buffer (current-buffer))
c3d9274a
BW
1501 (file-name buffer-file-name)
1502 (config mh-previous-window-config)
1503 (coding-system-for-write
1504 (if (and (local-variable-p 'buffer-file-coding-system
a1b4049d 1505 (current-buffer)) ;XEmacs needs two args
c3d9274a
BW
1506 ;; We're not sure why, but buffer-file-coding-system
1507 ;; tends to get set to undecided-unix.
1508 (not (memq buffer-file-coding-system
1509 '(undecided undecided-unix undecided-dos))))
1510 buffer-file-coding-system
1511 (or (and (boundp 'sendmail-coding-system) sendmail-coding-system)
1512 (and (boundp 'default-buffer-file-coding-system )
a1b4049d 1513 default-buffer-file-coding-system)
c3d9274a 1514 'iso-latin-1))))
bdcfe844
BW
1515 ;; The default BCC encapsulation will make a MIME message unreadable.
1516 ;; With nmh use the -mime arg to prevent this.
f0d73c14 1517 (if (and (mh-variant-p 'nmh)
c3d9274a
BW
1518 (mh-goto-header-field "Bcc:")
1519 (mh-goto-header-field "Content-Type:"))
1520 (setq mh-send-args (format "-mime %s" mh-send-args)))
c26cf6c8 1521 (cond (arg
924df208 1522 (pop-to-buffer mh-mail-delivery-buffer)
c3d9274a
BW
1523 (erase-buffer)
1524 (mh-exec-cmd-output mh-send-prog t "-watch" "-nopush"
1525 "-nodraftfolder" mh-send-args file-name)
1526 (goto-char (point-max)) ; show the interesting part
1527 (recenter -1)
1528 (set-buffer draft-buffer)) ; for annotation below
1529 (t
3d7ca223 1530 (mh-exec-cmd-daemon mh-send-prog nil "-nodraftfolder" "-noverbose"
c3d9274a 1531 mh-send-args file-name)))
c26cf6c8 1532 (if mh-annotate-char
c3d9274a
BW
1533 (mh-annotate-msg mh-sent-from-msg
1534 mh-sent-from-folder
1535 mh-annotate-char
1536 "-component" mh-annotate-field
1537 "-text" (format "\"%s %s\""
1538 (mh-get-header-field "To:")
1539 (mh-get-header-field "Cc:"))))
c26cf6c8
RS
1540
1541 (cond ((or (not arg)
c3d9274a
BW
1542 (y-or-n-p "Kill draft buffer? "))
1543 (kill-buffer draft-buffer)
1544 (if config
1545 (set-window-configuration config))))
c26cf6c8 1546 (if arg
c3d9274a 1547 (message "Sending...done")
c26cf6c8
RS
1548 (message "Sending...backgrounded"))))
1549
c3d9274a 1550;;;###mh-autoload
847b8219 1551(defun mh-insert-letter (folder message verbatim)
0c47b17c 1552 "Insert a message.
2dcf34f9
BW
1553
1554This command prompts you for the FOLDER and MESSAGE number and inserts
1555the message, indented by `mh-ins-buf-prefix' (\"> \") unless
1556`mh-yank-behavior' is set to one of the supercite flavors in which
1557case supercite is used to format the message. Certain undesirable
1558header fields (see `mh-invisible-header-fields-compiled') are removed
1559before insertion.
1560
1561If given a prefix argument VERBATIM, the header is left intact, the
1562message is not indented, and \"> \" is not inserted before each line.
1563This command leaves the mark before the letter and point after it."
c26cf6c8 1564 (interactive
847b8219 1565 (list (mh-prompt-for-folder "Message from" mh-sent-from-folder nil)
a10f4ace
BW
1566 (read-string (concat "Message number"
1567 (if (numberp mh-sent-from-msg)
1568 (format " (default %d): " mh-sent-from-msg)
1569 ": ")))
c3d9274a 1570 current-prefix-arg))
c26cf6c8
RS
1571 (save-restriction
1572 (narrow-to-region (point) (point))
1573 (let ((start (point-min)))
f0d73c14
BW
1574 (if (and (equal message "") (numberp mh-sent-from-msg))
1575 (setq message (int-to-string mh-sent-from-msg)))
bdcfe844
BW
1576 (insert-file-contents
1577 (expand-file-name message (mh-expand-file-name folder)))
1578 (when (not verbatim)
f0d73c14 1579 (mh-clean-msg-header start mh-invisible-header-fields-compiled nil)
c3d9274a
BW
1580 (goto-char (point-max)) ;Needed for sc-cite-original
1581 (push-mark) ;Needed for sc-cite-original
1582 (goto-char (point-min)) ;Needed for sc-cite-original
bdcfe844
BW
1583 (mh-insert-prefix-string mh-ins-buf-prefix)))))
1584
1585(defun mh-extract-from-attribution ()
1586 "Extract phrase or comment from From header field."
1587 (save-excursion
1588 (if (not (mh-goto-header-field "From: "))
1589 nil
1590 (skip-chars-forward " ")
1591 (cond
1592 ((looking-at "\"\\([^\"\n]+\\)\" \\(<.+>\\)")
f0d73c14 1593 (format "%s %s " (match-string 1)(match-string 2)))
bdcfe844 1594 ((looking-at "\\([^<\n]+<.+>\\)$")
f0d73c14 1595 (format "%s " (match-string 1)))
bdcfe844 1596 ((looking-at "\\([^ ]+@[^ ]+\\) +(\\(.+\\))$")
f0d73c14 1597 (format "%s <%s> " (match-string 2)(match-string 1)))
bdcfe844 1598 ((looking-at " *\\(.+\\)$")
f0d73c14 1599 (format "%s " (match-string 1)))))))
c26cf6c8 1600
c3d9274a 1601;;;###mh-autoload
c26cf6c8
RS
1602(defun mh-yank-cur-msg ()
1603 "Insert the current message into the draft buffer.
0c47b17c 1604
2dcf34f9
BW
1605It is often useful to insert a snippet of text from a letter that
1606someone mailed to provide some context for your reply. This
1607command does this by adding an attribution, yanking a portion of
1608text from the message to which you're replying, and inserting
1609`mh-ins-buf-prefix' (`> ') before each line.
0c47b17c
BW
1610
1611The attribution consists of the sender's name and email address
af435184
BW
1612followed by the content of the option
1613`mh-extract-from-attribution-verb'.
0c47b17c 1614
af435184
BW
1615You can also turn on the option
1616`mh-delete-yanked-msg-window-flag' to delete the window
1617containing the original message after yanking it to make more
1618room on your screen for your reply.
0c47b17c 1619
2dcf34f9
BW
1620You can control how the message to which you are replying is
1621yanked into your reply using `mh-yank-behavior'.
0c47b17c 1622
2dcf34f9
BW
1623If this isn't enough, you can gain full control over the
1624appearance of the included text by setting `mail-citation-hook'
1625to a function that modifies it. For example, if you set this hook
4023e353 1626to `trivial-cite' (which is NOT part of Emacs), set
2dcf34f9 1627`mh-yank-behavior' to \"Body and Header\" (see URL
d1699462
BW
1628`http://shasta.cs.uiuc.edu/~lrclause/tc.html').
1629
2dcf34f9
BW
1630Note that if `mail-citation-hook' is set, `mh-ins-buf-prefix' is
1631not inserted. If the option `mh-yank-behavior' is set to one of
1632the supercite flavors, the hook `mail-citation-hook' is ignored
1633and `mh-ins-buf-prefix' is not inserted."
c26cf6c8 1634 (interactive)
bdcfe844
BW
1635 (if (and mh-sent-from-folder
1636 (save-excursion (set-buffer mh-sent-from-folder) mh-show-buffer)
1637 (save-excursion (set-buffer mh-sent-from-folder)
1638 (get-buffer mh-show-buffer))
1639 mh-sent-from-msg)
c26cf6c8 1640 (let ((to-point (point))
c3d9274a
BW
1641 (to-buffer (current-buffer)))
1642 (set-buffer mh-sent-from-folder)
1643 (if mh-delete-yanked-msg-window-flag
1644 (delete-windows-on mh-show-buffer))
1645 (set-buffer mh-show-buffer) ; Find displayed message
1646 (let* ((from-attr (mh-extract-from-attribution))
1647 (yank-region (mh-mark-active-p nil))
bdcfe844
BW
1648 (mh-ins-str
1649 (cond ((and yank-region
0c47b17c
BW
1650 (or (eq 'supercite mh-yank-behavior)
1651 (eq 'autosupercite mh-yank-behavior)
1652 (eq t mh-yank-behavior)))
bdcfe844
BW
1653 ;; supercite needs the full header
1654 (concat
924df208 1655 (buffer-substring (point-min) (mh-mail-header-end))
bdcfe844
BW
1656 "\n"
1657 (buffer-substring (region-beginning) (region-end))))
1658 (yank-region
1659 (buffer-substring (region-beginning) (region-end)))
0c47b17c
BW
1660 ((or (eq 'body mh-yank-behavior)
1661 (eq 'attribution mh-yank-behavior)
1662 (eq 'autoattrib mh-yank-behavior))
bdcfe844
BW
1663 (buffer-substring
1664 (save-excursion
1665 (goto-char (point-min))
1666 (mh-goto-header-end 1)
1667 (point))
1668 (point-max)))
0c47b17c
BW
1669 ((or (eq 'supercite mh-yank-behavior)
1670 (eq 'autosupercite mh-yank-behavior)
1671 (eq t mh-yank-behavior))
bdcfe844
BW
1672 (buffer-substring (point-min) (point-max)))
1673 (t
1674 (buffer-substring (point) (point-max))))))
c3d9274a
BW
1675 (set-buffer to-buffer)
1676 (save-restriction
1677 (narrow-to-region to-point to-point)
1678 (insert (mh-filter-out-non-text mh-ins-str))
bdcfe844 1679 (goto-char (point-max)) ;Needed for sc-cite-original
c3d9274a 1680 (push-mark) ;Needed for sc-cite-original
bdcfe844 1681 (goto-char (point-min)) ;Needed for sc-cite-original
c3d9274a 1682 (mh-insert-prefix-string mh-ins-buf-prefix)
0c47b17c
BW
1683 (when (or (eq 'attribution mh-yank-behavior)
1684 (eq 'autoattrib mh-yank-behavior))
f0d73c14
BW
1685 (insert from-attr)
1686 (mh-identity-insert-attribution-verb nil)
1687 (insert "\n\n"))
c3d9274a
BW
1688 ;; If the user has selected a region, he has already "edited" the
1689 ;; text, so leave the cursor at the end of the yanked text. In
1690 ;; either case, leave a mark at the opposite end of the included
1691 ;; text to make it easy to jump or delete to the other end of the
1692 ;; text.
1693 (push-mark)
1694 (goto-char (point-max))
1695 (if (null yank-region)
1696 (mh-exchange-point-and-mark-preserving-active-mark)))))
847b8219 1697 (error "There is no current message")))
c26cf6c8 1698
bdcfe844
BW
1699(defun mh-filter-out-non-text (string)
1700 "Return STRING but without adornments such as MIME buttons and smileys."
1701 (with-temp-buffer
1702 ;; Insert the string to filter
1703 (insert string)
1704 (goto-char (point-min))
a1506d29 1705
bdcfe844
BW
1706 ;; Remove the MIME buttons
1707 (let ((can-move-forward t)
1708 (in-button nil))
1709 (while can-move-forward
1710 (cond ((and (not (get-text-property (point) 'mh-data))
1711 in-button)
c3d9274a 1712 (delete-region (1- (point)) (point))
bdcfe844
BW
1713 (setq in-button nil))
1714 ((get-text-property (point) 'mh-data)
1715 (delete-region (point)
1716 (save-excursion (forward-line) (point)))
1717 (setq in-button t))
1718 (t (setq can-move-forward (= (forward-line) 0))))))
1719
1720 ;; Return the contents without properties... This gets rid of emphasis
1721 ;; and smileys
1722 (buffer-substring-no-properties (point-min) (point-max))))
c26cf6c8
RS
1723
1724(defun mh-insert-prefix-string (mh-ins-string)
bdcfe844 1725 "Insert prefix string before each line in buffer.
2dcf34f9
BW
1726The inserted letter is cited using `sc-cite-original' if
1727`mh-yank-behavior' is one of 'supercite or 'autosupercite.
1728Otherwise, simply insert MH-INS-STRING before each line."
847b8219 1729 (goto-char (point-min))
0c47b17c
BW
1730 (cond ((or (eq mh-yank-behavior 'supercite)
1731 (eq mh-yank-behavior 'autosupercite))
bdcfe844
BW
1732 (sc-cite-original))
1733 (mail-citation-hook
c3d9274a
BW
1734 (run-hooks 'mail-citation-hook))
1735 (mh-yank-hooks ;old hook name
1736 (run-hooks 'mh-yank-hooks))
1737 (t
1738 (or (bolp) (forward-line 1))
bdcfe844
BW
1739 (while (< (point) (point-max))
1740 (insert mh-ins-string)
1741 (forward-line 1))
1742 (goto-char (point-min))))) ;leave point like sc-cite-original
c26cf6c8 1743
c3d9274a 1744;;;###mh-autoload
c26cf6c8 1745(defun mh-fully-kill-draft ()
0c47b17c 1746 "Quit editing and delete draft message.
af435184 1747
2dcf34f9 1748If for some reason you are not happy with the draft, you can use
af435184
BW
1749this command to kill the draft buffer and delete the draft
1750message. Use the command \\[kill-buffer] if you don't want to
2dcf34f9 1751delete the draft message."
c26cf6c8
RS
1752 (interactive)
1753 (if (y-or-n-p "Kill draft message? ")
1754 (let ((config mh-previous-window-config))
c3d9274a
BW
1755 (if (file-exists-p buffer-file-name)
1756 (delete-file buffer-file-name))
1757 (set-buffer-modified-p nil)
1758 (kill-buffer (buffer-name))
1759 (message "")
1760 (if config
1761 (set-window-configuration config)))
c26cf6c8
RS
1762 (error "Message not killed")))
1763
a1b4049d 1764(defun mh-current-fill-prefix ()
bdcfe844 1765 "Return the `fill-prefix' on the current line as a string."
a1b4049d
BW
1766 (save-excursion
1767 (beginning-of-line)
1768 ;; This assumes that the major-mode sets up adaptive-fill-regexp
1769 ;; correctly such as mh-letter-mode or sendmail.el's mail-mode. But
1770 ;; perhaps I should use the variable and simply inserts its value here,
1771 ;; and set it locally in a let scope. --psg
1772 (if (re-search-forward adaptive-fill-regexp nil t)
1773 (match-string 0)
1774 "")))
1775
c3d9274a 1776;;;###mh-autoload
a1b4049d 1777(defun mh-open-line ()
af435184 1778 "Insert a newline and leave point before it.
2dcf34f9 1779
af435184 1780This command is similar to the command \\[open-line] in that it
2dcf34f9
BW
1781inserts a newline after point. It differs in that it also inserts
1782the right number of quoting characters and spaces so that the
1783next line begins in the same column as it was. This is useful
1784when breaking up paragraphs in replies."
a1b4049d
BW
1785 (interactive)
1786 (let ((column (current-column))
a1b4049d
BW
1787 (prefix (mh-current-fill-prefix)))
1788 (if (> (length prefix) column)
1789 (message "Sorry, point seems to be within the line prefix")
1790 (newline 2)
1791 (insert prefix)
1792 (while (> column (current-column))
1793 (insert " "))
1794 (forward-line -1))))
847b8219 1795
924df208
BW
1796(mh-do-in-xemacs (defvar mail-abbrevs))
1797
79af55a7
BW
1798(defmacro mh-display-completion-list-compat (word choices)
1799 "Completes WORD from CHOICES using `display-completion-list'.
1800Calls `display-completion-list' correctly in older environments.
2dcf34f9
BW
1801Versions of Emacs prior to version 22 lacked a COMMON-SUBSTRING
1802argument which is used to highlight the next possible character you
1803can enter in the current list of completions."
79af55a7
BW
1804 (if (>= emacs-major-version 22)
1805 `(display-completion-list (all-completions ,word ,choices) ,word)
1806 `(display-completion-list (all-completions ,word ,choices))))
1807
a66894d8
BW
1808;;;###mh-autoload
1809(defun mh-complete-word (word choices begin end)
1810 "Complete WORD at from CHOICES.
1811Any match found replaces the text from BEGIN to END."
e495eaec
BW
1812 (let ((completion (try-completion word choices))
1813 (completions-buffer "*Completions*"))
a66894d8 1814 (cond ((eq completion t)
e495eaec
BW
1815 (ignore-errors
1816 (kill-buffer completions-buffer))
a66894d8
BW
1817 (message "Completed: %s" word))
1818 ((null completion)
e495eaec
BW
1819 (ignore-errors
1820 (kill-buffer completions-buffer))
a66894d8
BW
1821 (message "No completion for `%s'" word))
1822 ((stringp completion)
1823 (if (equal word completion)
e495eaec 1824 (with-output-to-temp-buffer completions-buffer
79af55a7 1825 (mh-display-completion-list-compat word choices))
e495eaec
BW
1826 (ignore-errors
1827 (kill-buffer completions-buffer))
a66894d8
BW
1828 (delete-region begin end)
1829 (insert completion))))))
1830
1831;;;###mh-autoload
1832(defun mh-beginning-of-word (&optional n)
1833 "Return position of the N th word backwards."
1834 (unless n (setq n 1))
1835 (let ((syntax-table (syntax-table)))
1836 (unwind-protect
1837 (save-excursion
f0d73c14 1838 (mh-mail-abbrev-make-syntax-table)
a66894d8
BW
1839 (set-syntax-table mail-abbrev-syntax-table)
1840 (backward-word n)
1841 (point))
1842 (set-syntax-table syntax-table))))
1843
924df208
BW
1844(defun mh-folder-expand-at-point ()
1845 "Do folder name completion in Fcc header field."
1846 (let* ((end (point))
a66894d8 1847 (beg (mh-beginning-of-word))
924df208
BW
1848 (folder (buffer-substring beg end))
1849 (leading-plus (and (> (length folder) 0) (equal (aref folder 0) ?+)))
1850 (last-slash (mh-search-from-end ?/ folder))
1851 (prefix (and last-slash (substring folder 0 last-slash)))
a66894d8
BW
1852 (choices (mapcar #'(lambda (x)
1853 (list (cond (prefix (format "%s/%s" prefix x))
1854 (leading-plus (format "+%s" x))
1855 (t x))))
1856 (mh-folder-completion-function folder nil t))))
1857 (mh-complete-word folder choices beg end)))
1858
a66894d8
BW
1859(defvar mh-letter-complete-function-alist
1860 '((cc . mh-alias-letter-expand-alias)
1861 (bcc . mh-alias-letter-expand-alias)
1862 (dcc . mh-alias-letter-expand-alias)
1863 (fcc . mh-folder-expand-at-point)
1864 (from . mh-alias-letter-expand-alias)
1865 (mail-followup-to . mh-alias-letter-expand-alias)
1866 (reply-to . mh-alias-letter-expand-alias)
1867 (to . mh-alias-letter-expand-alias))
1868 "Alist of header fields and completion functions to use.")
924df208 1869
c3d9274a
BW
1870(defun mh-letter-complete (arg)
1871 "Perform completion on header field or word preceding point.
af435184 1872
2dcf34f9 1873If the field contains addresses (for example, \"To:\" or \"Cc:\")
af435184
BW
1874or folders (for example, \"Fcc:\") then this command will provide
1875alias completion. In the body of the message, this command runs
1876`mh-letter-complete-function' instead, which is set to
1877`ispell-complete-word' by default. This command takes a prefix
1878argument ARG that is passed to the
0c47b17c 1879`mh-letter-complete-function'."
c3d9274a 1880 (interactive "P")
a66894d8
BW
1881 (let ((func nil))
1882 (cond ((not (mh-in-header-p))
1883 (funcall mh-letter-complete-function arg))
1884 ((setq func (cdr (assoc (mh-letter-header-field-at-point)
1885 mh-letter-complete-function-alist)))
1886 (funcall func))
1887 (t (funcall mh-letter-complete-function arg)))))
1888
1889(defun mh-letter-complete-or-space (arg)
1890 "Perform completion or insert space.
a66894d8 1891
af435184
BW
1892Turn on the option `mh-compose-space-does-completion-flag' to use
1893this command to perform completion in the header. Otherwise, a
1894space is inserted; use a prefix argument ARG to specify more than
1895one space."
a66894d8
BW
1896 (interactive "p")
1897 (let ((func nil)
1898 (end-of-prev (save-excursion
1899 (goto-char (mh-beginning-of-word))
1900 (mh-beginning-of-word -1))))
1901 (cond ((not mh-compose-space-does-completion-flag)
1902 (self-insert-command arg))
1903 ((not (mh-in-header-p)) (self-insert-command arg))
1904 ((> (point) end-of-prev) (self-insert-command arg))
1905 ((setq func (cdr (assoc (mh-letter-header-field-at-point)
1906 mh-letter-complete-function-alist)))
1907 (funcall func))
1908 (t (self-insert-command arg)))))
1909
1910(defun mh-letter-confirm-address ()
0c47b17c 1911 "Flash alias expansion.
af435184
BW
1912
1913Addresses are separated by a comma\; when you press the comma,
1914this command flashes the alias expansion in the minibuffer if
1915`mh-alias-flash-on-comma' is turned on."
a66894d8
BW
1916 (interactive)
1917 (cond ((not (mh-in-header-p)) (self-insert-command 1))
1918 ((eq (cdr (assoc (mh-letter-header-field-at-point)
1919 mh-letter-complete-function-alist))
1920 'mh-alias-letter-expand-alias)
1921 (mh-alias-reload-maybe)
1922 (mh-alias-minibuffer-confirm-address))
1923 (t (self-insert-command 1))))
1924
1925(defvar mh-letter-header-field-regexp "^\\([A-Za-z][A-Za-z0-9-]*\\):")
1926
1927(defun mh-letter-header-field-at-point ()
1928 "Return the header field name at point.
2dcf34f9
BW
1929A symbol is returned whose name is the string obtained by
1930downcasing the field name."
a66894d8
BW
1931 (save-excursion
1932 (end-of-line)
1933 (and (re-search-backward mh-letter-header-field-regexp nil t)
1934 (intern (downcase (match-string 1))))))
1935
1936;;;###mh-autoload
1937(defun mh-letter-next-header-field-or-indent (arg)
af435184
BW
1938 "Cycle to next field.
1939
2dcf34f9 1940Within the header of the message, this command moves between
af435184
BW
1941fields that are highlighted with the face
1942`mh-letter-header-field', skipping those fields listed in
2dcf34f9
BW
1943`mh-compose-skipped-header-fields'. After the last field, this
1944command then moves point to the message body before cycling back
1945to the first field. If point is already past the first line of
1946the message body, then this command indents by calling
1947`indent-relative' with the given prefix argument ARG."
a66894d8
BW
1948 (interactive "P")
1949 (let ((header-end (save-excursion
1950 (goto-char (mh-mail-header-end))
1951 (forward-line)
1952 (point))))
1953 (if (> (point) header-end)
1954 (indent-relative arg)
1955 (mh-letter-next-header-field))))
1956
1957(defun mh-letter-next-header-field ()
1958 "Cycle to the next header field.
2dcf34f9
BW
1959If we are at the last header field go to the start of the message
1960body."
a66894d8
BW
1961 (let ((header-end (mh-mail-header-end)))
1962 (cond ((>= (point) header-end) (goto-char (point-min)))
1963 ((< (point) (progn
1964 (beginning-of-line)
1965 (re-search-forward mh-letter-header-field-regexp
1966 (line-end-position) t)
1967 (point)))
1968 (beginning-of-line))
1969 (t (end-of-line)))
1970 (cond ((re-search-forward mh-letter-header-field-regexp header-end t)
1971 (if (mh-letter-skipped-header-field-p (match-string 1))
1972 (mh-letter-next-header-field)
1973 (mh-letter-skip-leading-whitespace-in-header-field)))
1974 (t (goto-char header-end)
1975 (forward-line)))))
1976
1977;;;###mh-autoload
1978(defun mh-letter-previous-header-field ()
1979 "Cycle to the previous header field.
af435184 1980
2dcf34f9 1981This command moves backwards between the fields and cycles to the
af435184
BW
1982body of the message after the first field. Unlike the command
1983\\[mh-letter-next-header-field-or-indent], it will always take
1984point to the last field from anywhere in the body."
a66894d8
BW
1985 (interactive)
1986 (let ((header-end (mh-mail-header-end)))
1987 (if (>= (point) header-end)
1988 (goto-char header-end)
1989 (mh-header-field-beginning))
1990 (cond ((re-search-backward mh-letter-header-field-regexp nil t)
1991 (if (mh-letter-skipped-header-field-p (match-string 1))
1992 (mh-letter-previous-header-field)
1993 (goto-char (match-end 0))
1994 (mh-letter-skip-leading-whitespace-in-header-field)))
1995 (t (goto-char header-end)
1996 (forward-line)))))
1997
1998(defun mh-letter-skipped-header-field-p (field)
1999 "Check if FIELD is to be skipped."
2000 (let ((field (downcase field)))
2001 (loop for x in mh-compose-skipped-header-fields
2002 when (equal (downcase x) field) return t
2003 finally return nil)))
2004
2005(defun mh-letter-skip-leading-whitespace-in-header-field ()
2006 "Skip leading whitespace in a header field.
2dcf34f9
BW
2007If the header field doesn't have at least one space after the
2008colon then a space character is added."
a66894d8
BW
2009 (let ((need-space t))
2010 (while (memq (char-after) '(?\t ?\ ))
2011 (forward-char)
2012 (setq need-space nil))
2013 (when need-space (insert " "))))
2014
2015(defvar mh-hidden-header-keymap
2016 (let ((map (make-sparse-keymap)))
2017 (mh-do-in-gnu-emacs
2018 (define-key map [mouse-2] 'mh-letter-toggle-header-field-display-button))
2019 (mh-do-in-xemacs
2020 (define-key map '(button2)
2021 'mh-letter-toggle-header-field-display-button))
2022 map))
2023
2024(defun mh-letter-toggle-header-field-display-button (event)
2025 "Toggle header field display at location of EVENT.
2dcf34f9
BW
2026This function does the same thing as
2027`mh-letter-toggle-header-field-display' except that it is
2028callable from a mouse button."
a66894d8
BW
2029 (interactive "e")
2030 (mh-do-at-event-location event
2031 (mh-letter-toggle-header-field-display nil)))
2032
2033(defun mh-letter-toggle-header-field-display (arg)
2034 "Toggle display of header field at point.
a66894d8 2035
2dcf34f9
BW
2036Use this command to display truncated header fields. This command
2037is a toggle so entering it again will hide the field. This
2038command takes a prefix argument ARG: if negative then the field
2039is hidden, if positive then the field is displayed."
a66894d8
BW
2040 (interactive (list nil))
2041 (when (and (mh-in-header-p)
2042 (progn
2043 (end-of-line)
2044 (re-search-backward mh-letter-header-field-regexp nil t)))
2045 (let ((buffer-read-only nil)
2046 (modified-flag (buffer-modified-p))
2047 (begin (point))
2048 end)
2049 (end-of-line)
2050 (setq end (1- (if (re-search-forward "^[^ \t]" nil t)
2051 (match-beginning 0)
2052 (point-max))))
2053 (goto-char begin)
2054 ;; Make it clickable...
2055 (add-text-properties begin end `(keymap ,mh-hidden-header-keymap
2056 mouse-face highlight))
2057 (unwind-protect
2058 (cond ((or (and (not arg)
2059 (text-property-any begin end 'invisible 'vanish))
2060 (and (numberp arg) (>= arg 0))
2061 (and (eq arg 'long) (> (line-beginning-position 5) end)))
2062 (remove-text-properties begin end '(invisible nil))
2063 (search-forward ":" (line-end-position) t)
2064 (mh-letter-skip-leading-whitespace-in-header-field))
0c47b17c
BW
2065 ;; XXX Redesign to make usable by user. Perhaps use a positive
2066 ;; numeric prefix to make that many lines visible.
a66894d8
BW
2067 ((eq arg 'long)
2068 (end-of-line 4)
2069 (mh-letter-truncate-header-field end)
2070 (beginning-of-line))
2071 (t (end-of-line)
2072 (mh-letter-truncate-header-field end)
2073 (beginning-of-line)))
2074 (set-buffer-modified-p modified-flag)))))
2075
2076(defun mh-letter-truncate-header-field (end)
2077 "Replace text from current line till END with an ellipsis.
2078If the current line is too long truncate a part of it as well."
2079 (let ((max-len (min (window-width) 62)))
2080 (when (> (+ (current-column) 4) max-len)
2081 (backward-char (- (+ (current-column) 5) max-len)))
2082 (when (> end (point))
2083 (add-text-properties (point) end '(invisible vanish)))))
2084
2085(defun mh-letter-hide-all-skipped-fields ()
2086 "Hide all skipped fields."
2087 (save-excursion
2088 (goto-char (point-min))
2089 (save-restriction
2090 (narrow-to-region (point) (mh-mail-header-end))
2091 (while (re-search-forward mh-letter-header-field-regexp nil t)
2092 (if (mh-letter-skipped-header-field-p (match-string 1))
2093 (mh-letter-toggle-header-field-display -1)
2094 (mh-letter-toggle-header-field-display 'long))
2095 (beginning-of-line 2)))))
2096
2097(defun mh-interactive-read-address (prompt)
2098 "Read an address.
2dcf34f9
BW
2099If `mh-compose-prompt-flag' is non-nil, then read an address with
2100PROMPT.
a66894d8
BW
2101Otherwise return the empty string."
2102 (if mh-compose-prompt-flag (mh-read-address prompt) ""))
2103
2104(defun mh-interactive-read-string (prompt)
2105 "Read a string.
2dcf34f9
BW
2106If `mh-compose-prompt-flag' is non-nil, then read a string with
2107PROMPT.
a66894d8
BW
2108Otherwise return the empty string."
2109 (if mh-compose-prompt-flag (read-string prompt) ""))
2110
2111(defun mh-letter-adjust-point ()
2112 "Move cursor to first header field if are using the no prompt mode."
2113 (unless mh-compose-prompt-flag
2114 (goto-char (point-max))
2115 (mh-letter-next-header-field)))
a1506d29 2116
cee9f5c6
BW
2117\f
2118
2119;;; Build mh-letter-mode keymap
2120
2121;; If this changes, modify mh-letter-mode-help-messages accordingly, above.
a1b4049d 2122(gnus-define-keys mh-letter-mode-map
f0d73c14
BW
2123 " " mh-letter-complete-or-space
2124 "," mh-letter-confirm-address
c3d9274a 2125 "\C-c?" mh-help
f0d73c14
BW
2126 "\C-c\C-\\" mh-fully-kill-draft ;if no C-q
2127 "\C-c\C-^" mh-insert-signature ;if no C-s
c3d9274a
BW
2128 "\C-c\C-c" mh-send-letter
2129 "\C-c\C-d" mh-insert-identity
0c47b17c 2130 "\C-c\C-e" mh-mh-to-mime
c3d9274a
BW
2131 "\C-c\C-f\C-b" mh-to-field
2132 "\C-c\C-f\C-c" mh-to-field
2133 "\C-c\C-f\C-d" mh-to-field
2134 "\C-c\C-f\C-f" mh-to-fcc
2135 "\C-c\C-f\C-r" mh-to-field
2136 "\C-c\C-f\C-s" mh-to-field
2137 "\C-c\C-f\C-t" mh-to-field
2138 "\C-c\C-fb" mh-to-field
2139 "\C-c\C-fc" mh-to-field
2140 "\C-c\C-fd" mh-to-field
2141 "\C-c\C-ff" mh-to-fcc
2142 "\C-c\C-fr" mh-to-field
2143 "\C-c\C-fs" mh-to-field
2144 "\C-c\C-ft" mh-to-field
2145 "\C-c\C-i" mh-insert-letter
f0d73c14 2146 "\C-c\C-m\C-e" mh-mml-secure-message-encrypt
c3d9274a 2147 "\C-c\C-m\C-f" mh-compose-forward
0c47b17c 2148 "\C-c\C-m\C-g" mh-mh-compose-anon-ftp
c3d9274a
BW
2149 "\C-c\C-m\C-i" mh-compose-insertion
2150 "\C-c\C-m\C-m" mh-mml-to-mime
f0d73c14
BW
2151 "\C-c\C-m\C-n" mh-mml-unsecure-message
2152 "\C-c\C-m\C-s" mh-mml-secure-message-sign
0c47b17c
BW
2153 "\C-c\C-m\C-t" mh-mh-compose-external-compressed-tar
2154 "\C-c\C-m\C-u" mh-mh-to-mime-undo
2155 "\C-c\C-m\C-x" mh-mh-compose-external-type
f0d73c14
BW
2156 "\C-c\C-mee" mh-mml-secure-message-encrypt
2157 "\C-c\C-mes" mh-mml-secure-message-signencrypt
c3d9274a 2158 "\C-c\C-mf" mh-compose-forward
0c47b17c 2159 "\C-c\C-mg" mh-mh-compose-anon-ftp
c3d9274a
BW
2160 "\C-c\C-mi" mh-compose-insertion
2161 "\C-c\C-mm" mh-mml-to-mime
f0d73c14
BW
2162 "\C-c\C-mn" mh-mml-unsecure-message
2163 "\C-c\C-mse" mh-mml-secure-message-signencrypt
2164 "\C-c\C-mss" mh-mml-secure-message-sign
0c47b17c
BW
2165 "\C-c\C-mt" mh-mh-compose-external-compressed-tar
2166 "\C-c\C-mu" mh-mh-to-mime-undo
2167 "\C-c\C-mx" mh-mh-compose-external-type
c3d9274a
BW
2168 "\C-c\C-o" mh-open-line
2169 "\C-c\C-q" mh-fully-kill-draft
c3d9274a 2170 "\C-c\C-s" mh-insert-signature
f0d73c14 2171 "\C-c\C-t" mh-letter-toggle-header-field-display
c3d9274a
BW
2172 "\C-c\C-w" mh-check-whom
2173 "\C-c\C-y" mh-yank-cur-msg
f0d73c14 2174 "\C-c\M-d" mh-insert-auto-fields
a66894d8
BW
2175 "\M-\t" mh-letter-complete
2176 "\t" mh-letter-next-header-field-or-indent
f0d73c14 2177 [backtab] mh-letter-previous-header-field)
a1b4049d
BW
2178
2179;; "C-c /" prefix is used in mh-letter-mode by pgp.el and mailcrypt.el.
2180
bdcfe844
BW
2181(provide 'mh-comp)
2182
cee9f5c6
BW
2183;; Local Variables:
2184;; indent-tabs-mode: nil
2185;; sentence-end-double-space: nil
2186;; End:
60370d40 2187
cee9f5c6 2188;; arch-tag: 62865511-e610-4923-b0b5-f45a8ab70a34
60370d40 2189;;; mh-comp.el ends here