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