Cosmetics.
[elisp.git] / vm
1 ;; -*-Emacs-Lisp-*-
2
3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
4 ;; This program is free software: you can redistribute it and/or modify  ;;
5 ;; it under the terms of the version 3 of the GNU General Public License ;;
6 ;; as published by the Free Software Foundation.                         ;;
7 ;;                                                                       ;;
8 ;; This program is distributed in the hope that it will be useful, but   ;;
9 ;; WITHOUT ANY WARRANTY; without even the implied warranty of            ;;
10 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      ;;
11 ;; General Public License for more details.                              ;;
12 ;;                                                                       ;;
13 ;; You should have received a copy of the GNU General Public License     ;;
14 ;; along with this program. If not, see <http://www.gnu.org/licenses/>.  ;;
15 ;;                                                                       ;;
16 ;; Written by and Copyright (C) Francois Fleuret                         ;;
17 ;; Contact < francois@fleuret.org > for comments & bug reports             ;;
18 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
19
20 (setq-default vm-summary-show-threads t)
21
22 (setq vm-startup-message-displayed t ;; Yes, we already saw it, no need to insist
23       vm-use-menus nil
24       vm-skip-deleted-messages nil
25       vm-skip-read-messages nil
26       vm-use-toolbar nil
27       ;; vm-jump-to-new-messages nil
28       vm-startup-with-summary t
29       ;; vm-preview-read-messages t
30       vm-preview-lines nil
31       vm-auto-get-new-mail t
32       vm-circular-folders nil
33       vm-confirm-new-folders t
34       vm-mutable-windows t
35       vm-mutable-frames nil
36       vm-summary-uninteresting-senders-arrow "->"
37       vm-summary-arrow "> "
38       vm-included-text-prefix " > "
39       vm-forwarding-digest-type "mime"
40       vm-mime-attachment-save-directory "~/"
41       vm-use-toolbar nil
42       vm-frame-per-folder nil
43       vm-frame-per-summary nil
44       vm-mime-yank-attachments nil
45
46       ;; vm-mime-7bit-composition-charset "latin-1"
47       vm-mime-8bit-composition-charset "iso-8859-1"
48       ;; vm-mime-8bit-composition-charset "utf-8"
49       ;; browse-url-mozilla-program "iceweasel"
50       vm-netscape-program browse-url-mozilla-program
51       ;; vm-coding-system-priorities '(utf-8)
52       ;; mail-from-style nil
53       ;; mail-complete-style nil
54
55       ;; vm-summary-format " %*%A %-3.3m %2d %5US %I%UA  %s\n"
56       vm-summary-format " %*%a %-3.3m %2d %5US %I%UA  %s\n"
57       ;; vm-highlighted-header-regexp "From:\\|Subject:\\|Cc:\\|To:\\|Bcc:\\|Reply-To:"
58       vm-highlighted-header-regexp "From:\\|Subject:"
59
60       vm-auto-folder-case-fold-search t
61
62       vm-keep-sent-messages nil
63       vm-delete-after-saving t
64       vm-delete-after-archiving t
65
66       vm-forwarding-subject-format "(forwarded from %F) %s"
67       vm-in-reply-to-format nil
68       vm-included-text-attribution-format "\nOn %w, %m %d, %y (%h), %F wrote:\n\n"
69       ;; vm-included-text-attribution-format "\nOn %w, %m %d, at %H, you wrote:\n\n"
70       vm-reply-subject-prefix "Re: "
71
72       mail-signature t
73       mail-specify-envelope-from t
74
75       bbdb/mail-auto-create-p nil
76       bbdb-send-mail-style 'vm
77
78       )
79
80 ;; (add-to-list 'vm-visible-headers "Reply-To:" t)
81 ;; (add-to-list 'vm-visible-headers "X-Mailer:" t)
82 ;; (add-to-list 'vm-visible-headers "X-from-in-bbdb:" t)
83 ;; (add-to-list 'vm-visible-headers "Return-Path:")
84
85 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
86 ;; Mime-related stuff
87
88 (setq
89
90  ;; vm-auto-displayed-mime-content-types '("text/plain" "text" "image" "multipart")
91  ;; vm-display-using-mime t
92  ;; vm-coding-system-priorities '(iso-8859-1 iso-8859-15 utf-8)
93
94  vm-infer-mime-types t
95  vm-mime-use-image-strips nil
96  vm-mime-base64-decoder-program "mimencode"
97  vm-mime-base64-decoder-switches '("-u")
98  vm-mime-base64-encoder-program "mimencode"
99  vm-mime-base64-encoder-switches '()
100
101  vm-auto-displayed-mime-content-types '(
102                                         ;; "plain text"
103                                         "text"
104                                         "multipart"
105                                         "image/xpm"
106                                         )
107
108  ;; vm-auto-displayed-mime-content-type-exceptions '("text/html")
109
110  vm-mime-internal-content-types '(
111                                   "multipart"
112                                   "text"
113                                   ;; "plain text"
114                                   ;; "plain text/utf8"
115                                   "image/xpm"
116                                   )
117
118  ;; To force it to be converted to plain text
119  vm-mime-internal-content-type-exceptions '("text/html")
120
121  vm-mime-external-content-types-alist  '(
122                                          ("application/x-dvi"      "xdvi")
123                                          ("image/postscript"       "gv")
124                                          ("application/pdf"        "xpdf")
125                                          ;; ("application/pdf"        "epdfview")
126                                          ("application/postscript" "gv")
127                                          ;;("image"                  "feh")
128                                          ("video"                  "mplayer")
129                                          ;; ("text/html"             "iceweasel")
130                                          )
131
132  )
133
134 (require 'vm-rfaddons)
135
136 ;; (add-to-list 'vm-mime-default-face-charsets  "utf-8")
137
138 (add-to-list 'vm-mime-default-face-charsets  "iso-8859-1")
139 (add-hook 'vm-mail-send-hook 'vm-mime-encode-headers)
140 (add-hook 'vm-mail-send-hook 'vm-mail-check-recipients)
141 (add-hook 'vm-reply-hook (lambda () (set-buffer-modified-p nil)))
142
143 (add-to-list 'vm-mime-attachment-auto-type-alist '(".*" . "application/octet-stream") t)
144
145 ;; (add-to-list 'vm-mime-type-converter-alist '("text/html" "text/plain" "cat"))
146 ;; (add-to-list 'vm-mime-type-converter-alist '("text/html" "text/plain" "lynx -nolist -force_html -dump -stdin"))
147
148 ;; (add-to-list 'vm-mime-type-converter-alist
149              ;; '("text/html" "text/plain"
150                ;; "w3m -cols 75 -graph -dump -T text/html"
151                ;; ))
152
153 (add-to-list 'vm-mime-type-converter-alist
154              '("text/html" "text/plain"
155                "html2text -nobs"
156                ))
157
158 (add-to-list 'vm-mime-type-converter-alist
159              '("image" "image/xpm"
160                "/usr/bin/convert -geometry 640x480 - xpm:-"))
161
162 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
163
164 ;; (defun ff/vm-remove-properties () (interactive)
165 ;; (save-excursion
166 ;; (goto-char (point-min))
167 ;; (re-search-forward (concat "^" mail-header-separator "$"))
168 ;; (set-text-properties (point) (point-max) nil)
169 ;; )
170 ;; )
171
172 ;; (add-hook 'vm-mail-send-hook 'ff/vm-remove-properties)
173
174 (defun ff/vm-mime-save-all-files (&optional delete)
175   "Save all the mail attachments. With delete argument, remove
176 the attachement from mail."
177   (interactive "P")
178   (let ((vm-mime-delete-after-saving delete))
179     (while (and (vm-mime-reader-map-save-file)
180                 (condition-case nil (vm-move-to-next-button 1)
181                   (error nil)))))
182   )
183
184 (defun ff/vm-mime-save-file (&optional delete)
185   "Save the current attachement. With delete argument, remove the
186 attachement from mail."
187   (interactive "P")
188   (let ((vm-mime-delete-after-saving delete))
189     (vm-mime-reader-map-save-file))
190   (condition-case nil (vm-move-to-next-button 1) (error (message "No more attachment"))))
191
192 ;; (define-key vm-summary-mode-map [(r)] 'vm-reply-include-text)
193 ;; (define-key vm-summary-mode-map [(R)] 'vm-followup-include-text)
194
195 (define-key vm-summary-mode-map [(control o)] 'ff/vm-mime-save-file)
196
197 (define-key vm-summary-mode-map [(control t)]
198   (lambda () (interactive)
199     (vm-toggle-threads-display)
200     (unless vm-summary-show-threads
201       (vm-sort-messages "date"))))
202
203 (defun ff/vm-select-thread-for-next-command () (interactive)
204   (vm-mark-thread-subtree)
205   (vm-next-command-uses-marks))
206
207 (define-key vm-summary-mode-map "T" 'ff/vm-select-thread-for-next-command)
208
209 (defun ff/vm-attach-file-or-dir
210   (&optional dir)
211   "Attaches the file or recursively the content of the directory with
212 `vm-mime-attach-file'."
213   (interactive "fFile or directory: ")
214
215   (save-excursion
216     (goto-char (point-max))
217     (insert "\n")
218     (if (file-regular-p dir)
219         (vm-mime-attach-file dir (vm-mime-default-type-from-filename dir))
220       (if (file-directory-p dir)
221           (mapcar
222            (lambda (x)
223              (when (not (string-match "^\\." (car x)))
224                (ff/vm-attach-file-or-dir
225                 (concat dir
226                         (unless (string-match "/$" dir) "/")
227                         (car x)))))
228            (directory-files-and-attributes dir)
229            )
230
231         (error "Can attach only files and directories")
232         ))))
233
234 (define-key vm-mail-mode-map [(control c) (control a)] 'ff/vm-attach-file-or-dir)
235
236 ;; Found no other way to avoid displaying the icones
237 (load "vm-mime")
238 (defun vm-mime-set-image-stamp-for-type (e type))
239
240 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
241 ;; Summary stuff
242
243 (defun vm-summary-function-A (message)
244   (let* ((from (vm-su-from message)))
245     (if (string-match vm-summary-uninteresting-senders from)
246         (concat vm-summary-uninteresting-senders-arrow " " (ff/explicit-name (vm-su-to message)))
247       (ff/explicit-name from))))
248
249 (defun vm-summary-function-S (&optional message)
250   (let ((s (string-to-int (vm-su-byte-count message))))
251     (if (> s 32768)
252         (propertize (concat (int-to-string (/ s 1024)) "k") 'face 'bold)
253       "")))
254
255 (defun ff/vm-delete-and-go-down () (interactive)
256   ;; (vm-goto-message)
257   (vm-delete-message 1)
258   (condition-case nil (vm-next-message-no-skip 1) (error nil)))
259
260 (add-hook 'vm-quit-hook 'vm-expunge-folder)
261 (add-hook 'vm-quit-hook 'bbdb-save-db)
262 (add-hook 'vm-retrieved-spooled-mail-hook 'display-time-update)
263
264 (ff/configure-faces '((ff/summary-highlight-face :background "yellow"
265                                                  ;; :weight 'bold
266                                                  )))
267
268 (setq vm-summary-highlight-face 'ff/summary-highlight-face)
269
270 (define-key vm-summary-mode-map [(K)] 'ff/vm-delete-and-go-down)
271
272 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
273 ;; Activate the required modes and authorize the commenting
274
275 (defun ff/prepare-mail-mode ()
276   (bbdb-define-all-aliases)
277   (flyspell-mode)
278   (auto-fill-mode)
279   (mail-abbrevs-setup)
280
281   ;; (setq fill-paragraph-function 'mail-mode-fill-paragraph)
282
283   ;; Since I set the comment prefix, I have to tell the filling
284   ;; functions not to use it
285
286   ;; ******************* removed Aug 23
287   ;; (setq fill-paragraph-handle-comment nil)
288   ;; ;; (when message-yank-prefix
289   (set (make-local-variable 'comment-start) vm-included-text-prefix)
290   ;; (set (make-local-variable 'comment-start-skip)
291   ;; (concat "^\\(" (regexp-quote vm-included-text-prefix) "\\)"))
292   ;; ;; )
293   )
294
295 (add-hook 'mail-mode-hook 'ff/prepare-mail-mode)
296 ;; (add-hook 'mail-mode-hook 'orgtbl-mode)
297
298 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
299 ;; To have a slightly darker background for headers
300
301 (ff/configure-faces
302  '((ff/mail-header-face :background "#d0d0e8"))
303  )
304
305 (defun ff/colorize-headers () (interactive)
306   (let ((inhibit-read-only t))
307     (save-excursion
308       (goto-char (point-min))
309       (while (vm-match-header)
310         (goto-char (vm-matched-header-end)))
311       (add-text-properties
312        ;; (vm-matched-header-contents-start)
313        ;; (vm-matched-header-contents-end)
314        (point-min)
315        (point-at-bol)
316        ;; '(face (:background "gray85"))
317        ;; '(face (:background "gray50" :foreground "gray95"))
318        '(face ff/mail-header-face)
319        )
320       )))
321
322 (defadvice vm-highlight-headers (after ff/colorize-headers nil activate)
323   (ff/colorize-headers))
324
325 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
326 ;; I want to have a file associated to every mail I am writing
327
328 (defcustom ff/vm-mail-draft-directory "~/"
329   "Where to save mail drafts with VM")
330
331 (defun ff/associate-file-to-vm-mail-buffer ()
332   "Associate the current buffer to a file whose name is built from the current time."
333   (unless (buffer-file-name)
334     (set-visited-file-name (format
335                             "%s/mail-%s"
336                             ff/vm-mail-draft-directory
337                             (format-time-string "%04Y%02m%02d-%02H%02M%02S" (current-time))))
338     (set-buffer-modified-p nil)))
339
340 (add-hook 'mail-setup-hook 'ff/associate-file-to-vm-mail-buffer)
341
342 (defun ff/mail-header-field (field) (interactive)
343   "Grab the value of a certain field from the mail header."
344   (let ((s "no-subject"))
345     (save-excursion
346       (goto-char (point-min))
347       (let ((l (re-search-forward (concat "^" mail-header-separator "$") nil t)))
348         (when l
349           (goto-char (point-min))
350           (when (re-search-forward (concat "^" field ": ") l t nil)
351             (setq s (buffer-substring-no-properties (point) (point-at-eol))))
352           )
353         )
354       )
355     s))
356
357 (defun ff/dissociate-file-from-vm-mail-buffer ()
358   "Save the file under a new name and set the associated file to nil."
359   (let ((bn (buffer-file-name)))
360     (when bn
361       (set-visited-file-name (concat (file-name-directory bn)
362                                      "sent-"
363                                      (file-name-nondirectory bn)
364                                      "-"
365                                      (replace-regexp-in-string "[^a-zA-Z0-9]+" "_"
366                                                                (ff/mail-header-field "Subject"))
367                                      ))
368       (save-buffer)
369       (set-visited-file-name nil))
370     )
371   )
372
373 (defun ff/find-file-in-vm-mail-mode (filename) (interactive)
374   ;; No easy way to activate vm-mail-mode, so we create such a
375   ;; buffer, erase its content and insert the file
376   (vm-compose-mail)
377   (when (file-exists-p filename)
378     (erase-buffer)
379     (insert-file filename))
380   (set-visited-file-name filename)
381   (set-buffer-modified-p nil)
382   ;; (run-hooks find-file-hooks)
383   (when (functionp 'alarm-vc-check) (alarm-vc-check))
384   ;; Move the cursor at a convenient location
385   (when (re-search-forward (concat "^" mail-header-separator "$") nil t)
386     (if (re-search-forward "^-- $" nil t)
387         (previous-line 1)
388       (next-line 1))
389     (end-of-line))
390   )
391
392 ;; All this mess to activate the vm-mail-mode when loading a file
393 ;; looking like a mail draft. Did I miss something ?
394
395 (defadvice find-file (around ff/find-file-or-mail
396                              (filename &optional wildcards)
397                              activate)
398
399   (interactive "FFind file: \np")
400
401   (if (string-match "^\\(mail\\|sent-mail\\)-[^/]+$"
402                     (file-name-nondirectory filename))
403
404       (if (find-buffer-visiting filename)
405           (switch-to-buffer (find-buffer-visiting filename))
406         (ff/find-file-in-vm-mail-mode filename))
407     ad-do-it
408     ))
409
410 (setq ff/vm-mail-draft-directory "~/private/drafts")
411
412 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
413 ;; Check there are no missing attachment (the idea comes from
414 ;; http://home.cc.gatech.edu/eaganj/MailApp) and no leading "From"
415 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
416
417 (defcustom ff/check-vm-attachement-regexp "attach"
418   "*A mail whose body matches this regular expression should contain
419 an attachment")
420
421 (defun ff/check-vm-attachment ()
422   (when (save-excursion
423           (goto-char (point-min))
424           (and (re-search-forward "\\[ATTACHMENT" nil t)
425                (not (get-text-property (point) 'vm-mime-object))))
426     (error "Buggy attachment"))
427
428   (if (and
429        (save-excursion (goto-char (point-min))
430                        (re-search-forward ff/check-vm-attachement-regexp nil t))
431        (not (save-excursion (goto-char (point-min))
432                             (re-search-forward "\\[ATTACHMENT" nil t)))
433        (not (y-or-n-p "An attachment seems to be missing, send message ? ")))
434       (error "You refer to an unexisting attachment."))
435
436   )
437
438 ;; You can not have a line starting with "From:" in a pure text
439 ;; mail. The smtp server would add a leading character to prevent it.
440
441 (defun ff/check-no-leading-from ()
442   (and (let ((case-fold-search nil))
443          (save-excursion
444            (goto-char (point-min))
445            (re-search-forward (concat "^" mail-header-separator "$"))
446            (re-search-forward "^From " nil t)))
447        (not (y-or-n-p "There is a leading ``From '', send message ? "))
448        (error "There is a leading ``From ''.")))
449
450 ;; An attempt at limiting excess wording in sent mails
451
452 (defface ff/strong-words
453   '((t (:background "red")))
454   "The face to highlight upper caps, exclamation marks and such.")
455
456 (defun ff/max-in-a-row (overlay regexp max)
457   (let ((case-fold-search nil))
458     (save-excursion
459       (goto-char (point-min))
460       (re-search-forward (concat "^" mail-header-separator "$"))
461       (when (and (re-search-forward regexp nil t nil)
462                  (>= (- (match-end 0) (match-beginning 0)) max))
463         (move-overlay overlay (match-beginning 0) (match-end 0))
464         t))))
465
466 (defun ff/check-no-excess-wording () (interactive)
467   (let ((overlay (make-overlay 0 0)))
468     (overlay-put overlay 'face 'media/current-tune-face)
469     (let ((err (and
470                 (or (ff/max-in-a-row overlay "[A-Z\?\!][A-Z\?\! ]+[A-Z\?\!]" 6)
471                     (ff/max-in-a-row overlay "[\?\!]+" 2)
472                     )
473                 (not (y-or-n-p "That does not look good. Send message ? ")))))
474       (delete-overlay overlay)
475       (when err (error "Good idea. Chill out a bit.")))
476     ))
477
478 (defun ff/check-badly-encoded-address () (interactive)
479   (let (bodysep bad-adr)
480     (save-excursion
481       (goto-char (point-min))
482       (search-forward mail-header-separator)
483       (setq bodysep (vm-marker (match-beginning 0)))
484       (goto-char (point-min))
485       (setq bad-adr (re-search-forward "[^<a-zA-Z_0-9\-\. \n\t]+[^ \n]*@" nil t))
486       (when (and bad-adr (< bad-adr bodysep))
487         (error "There is an invalid address in the header (%s)"
488                (match-string 0)))))
489   )
490
491 (add-hook 'vm-mail-send-hook 'ff/check-vm-attachment)
492 (add-hook 'vm-mail-send-hook 'ff/check-no-leading-from)
493 (add-hook 'vm-mail-send-hook 'ff/check-no-excess-wording)
494 (add-hook 'vm-mail-send-hook 'flyspell-mode-off)
495 ;; Append so that it happens after the mime encoding
496 ;; (add-hook 'vm-mail-send-hook 'ff/check-badly-encoded-address t)
497 ;; Append this hook so that it runs after all other checks
498 (add-hook 'vm-mail-send-hook 'ff/dissociate-file-from-vm-mail-buffer t)
499
500 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
501 ;; If we can, encrypt!
502
503 ;; (defun ff/encrypt-mail-if-possible () (interactive)
504   ;; (condition-case nil (mc-encrypt)
505     ;; (error nil)))
506
507 ;; (add-hook 'vm-mail-send-hook 'ff/encrypt-mail-if-possible t)
508
509 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
510 ;; Composing a mail
511
512 ;; Move through important points in the mail ("To:" field, "Subject:"
513 ;; field and the end of the body)
514
515 (defun ff/goto-next-mail-field () (interactive)
516   (let ((field (save-excursion
517                  (end-of-line)
518                  (re-search-backward (concat "\\(^[a-zA-Z\-]*: \\|^" mail-header-separator "$\\)") nil t)
519                  (match-string 1))))
520
521     (cond ((string= field "To: ")
522            (expand-abbrev)
523            (re-search-forward "Subject: ")
524            (end-of-line))
525
526           ((string= field "Subject: ")
527            (re-search-forward (concat "^" mail-header-separator "$"))
528            (if (re-search-forward "^-- $" nil t)
529                (previous-line 1)
530              (next-line 1))
531            (end-of-line))
532
533           (t (beginning-of-buffer)
534              (re-search-forward "^To: ")
535              (end-of-line)
536              (re-search-forward "^[a-zA-Z\-]*: ")
537              (beginning-of-line)
538              (backward-char)))))
539
540 ;; (define-key vm-mail-mode-map [(control tab)] 'ff/goto-next-mail-field)
541 (define-key vm-mail-mode-map [(iso-lefttab)] 'ff/goto-next-mail-field)
542 ;; (define-key vm-mail-mode-map [(shift iso-lefttab)] 'mail-mode-smart-tab)
543 (define-key vm-mail-mode-map [(shift iso-lefttab)] 'bbdb-complete-name)
544
545 ;; The definition of "\t" is forced through a hook defined in
546 ;; vm-init.el, so I add mine. This is ugly.
547
548 (add-hook 'mail-setup-hook
549           '(lambda () (local-set-key "\t" 'ff/goto-next-mail-field))
550           t)
551
552 (substitute-key-definition 'next-line 'mail-abbrev-next-line vm-mail-mode-map global-map)
553 (substitute-key-definition 'end-of-buffer 'mail-abbrev-end-of-buffer vm-mail-mode-map global-map)
554
555 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
556 ;; I rarely edit two mails at the same time, and it makes sense to
557 ;; come back to the one currently being edited with the same function
558
559 (defun ff/first-buffer-in-mode (l m)
560   (if l
561       (if (eq (save-excursion
562                 (set-buffer (car l)) major-mode) m)
563           (car l)
564         (ff/first-buffer-in-mode (cdr l) m))))
565
566 (defun ff/compose-mail (&optional force-new)
567   "Switch to an existing buffer with major mode `mail-mode',
568 or invoke `vm-compose-mail' if none can be found or if FORCE-NEW
569 is t. If already in a mail buffer, burry it and go to the next."
570   (interactive "P")
571   (when (eq major-mode 'mail-mode) (bury-buffer))
572   (let ((buf (and (not force-new)
573                   (ff/first-buffer-in-mode (buffer-list) 'mail-mode))))
574     (if buf (switch-to-buffer buf)
575       (vm-compose-mail))))
576
577 (define-key global-map [(control x) (m)] 'ff/compose-mail)
578
579 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
580 ;; bbdb
581
582 (load "bbdb")
583 (load "bbdb-hooks")
584
585 (when (load "bbdb-vm" t)
586
587   ;; (defadvice define-mail-abbrev (before ff/remove-explicit-name (name definition &optional from-mailrc-file) activate)
588   ;; (message "%s -> %s" name definition))
589
590   (defun ff/mail-aliases-from-bbdb ()
591     "Creates automatically mail aliases from the bbdb records. For
592 instance, someone in bbdb named \"Paul Smith\" would generate an alias
593 'pm'. Does not replace existing aliases."
594     (interactive)
595     (let* ((records (bbdb-records)))
596       (while records
597         (let* ((record (car records))
598                (name (concat (elt record 0) " " (elt record 1)))
599                (email (car (elt record 6)))
600                (alias (downcase (replace-regexp-in-string "\\([a-zA-Z]?\\)[^- ]*[- ]*" "\\1" name))))
601           (if (and (> (length alias) 1)
602                    ;; Do not overwrite an existing alias
603                    (not (and mail-abbrevs (intern-soft alias mail-abbrevs))))
604               (define-mail-abbrev alias email))
605           (setq records (cdr records))))))
606
607   (when (>= emacs-major-version 22)
608     (bbdb-insinuate-vm)
609     (ff/mail-aliases-from-bbdb))
610   )