Mercurial > emacs
comparison lisp/mh-e/mh-utils.el @ 50702:7dd3d5eae9c7
Upgraded to MH-E version 7.3.
See etc/MH-E-NEWS and lisp/mh-e/ChangeLog for details.
author | Bill Wohler <wohler@newt.com> |
---|---|
date | Fri, 25 Apr 2003 05:52:00 +0000 |
parents | 0d8b17d428b5 |
children | 695cf19ef79e |
comparison
equal
deleted
inserted
replaced
50701:cb5f0a5d5b36 | 50702:7dd3d5eae9c7 |
---|---|
1 ;;; mh-utils.el --- MH-E code needed for both sending and reading | 1 ;;; mh-utils.el --- MH-E code needed for both sending and reading |
2 | 2 |
3 ;; Copyright (C) 1993, 1995, 1997, 2000, 2001, 2002 Free Software Foundation, Inc. | 3 ;; Copyright (C) 1993, 95, 1997, |
4 ;; 2000, 01, 02, 2003 Free Software Foundation, Inc. | |
4 | 5 |
5 ;; Author: Bill Wohler <wohler@newt.com> | 6 ;; Author: Bill Wohler <wohler@newt.com> |
6 ;; Maintainer: Bill Wohler <wohler@newt.com> | 7 ;; Maintainer: Bill Wohler <wohler@newt.com> |
7 ;; Keywords: mail | 8 ;; Keywords: mail |
8 ;; See: mh-e.el | 9 ;; See: mh-e.el |
27 ;;; Commentary: | 28 ;;; Commentary: |
28 | 29 |
29 ;; Internal support for MH-E package. | 30 ;; Internal support for MH-E package. |
30 | 31 |
31 ;;; Change Log: | 32 ;;; Change Log: |
32 | |
33 ;; $Id: mh-utils.el,v 1.2 2003/02/03 20:55:30 wohler Exp $ | |
34 | 33 |
35 ;;; Code: | 34 ;;; Code: |
36 | 35 |
37 ;; Is this XEmacs-land? Located here since needed by mh-customize.el. | 36 ;; Is this XEmacs-land? Located here since needed by mh-customize.el. |
38 (defvar mh-xemacs-flag (featurep 'xemacs) | 37 (defvar mh-xemacs-flag (featurep 'xemacs) |
55 (defvar mark-active) | 54 (defvar mark-active) |
56 (defvar tool-bar-mode) | 55 (defvar tool-bar-mode) |
57 | 56 |
58 ;;; Autoloads | 57 ;;; Autoloads |
59 (autoload 'gnus-article-highlight-citation "gnus-cite") | 58 (autoload 'gnus-article-highlight-citation "gnus-cite") |
60 (autoload 'mail-header-end "sendmail") | 59 (require 'sendmail) |
61 (autoload 'Info-goto-node "info") | 60 (autoload 'Info-goto-node "info") |
62 (unless (fboundp 'make-hash-table) | 61 (unless (fboundp 'make-hash-table) |
63 (autoload 'make-hash-table "cl")) | 62 (autoload 'make-hash-table "cl")) |
64 | 63 |
65 ;;; Set for local environment: | 64 ;;; Set for local environment: |
98 of `search' in the CL package." | 97 of `search' in the CL package." |
99 (loop for index from (1- (length string)) downto 0 | 98 (loop for index from (1- (length string)) downto 0 |
100 when (equal (aref string index) char) return index | 99 when (equal (aref string index) char) return index |
101 finally return nil)) | 100 finally return nil)) |
102 | 101 |
103 ;;; Macro to generate correct code for different emacs variants | 102 ;;; Macros to generate correct code for different emacs variants |
103 | |
104 (defmacro mh-do-in-gnu-emacs (&rest body) | |
105 "Execute BODY if in GNU Emacs." | |
106 (unless mh-xemacs-flag `(progn ,@body))) | |
107 (put 'mh-do-in-gnu-emacs 'lisp-indent-hook 'defun) | |
108 | |
109 (defmacro mh-do-in-xemacs (&rest body) | |
110 "Execute BODY if in GNU Emacs." | |
111 (when mh-xemacs-flag `(progn ,@body))) | |
112 (put 'mh-do-in-xemacs 'lisp-indent-hook 'defun) | |
113 | |
114 (defmacro mh-funcall-if-exists (function &rest args) | |
115 "Call FUNCTION with ARGS as parameters if it exists." | |
116 (if (fboundp function) | |
117 `(funcall ',function ,@args))) | |
118 | |
119 (defmacro mh-make-local-hook (hook) | |
120 "Make HOOK local if needed. | |
121 XEmacs and versions of GNU Emacs before 21.1 require `make-local-hook' to be | |
122 called." | |
123 (when (and (fboundp 'make-local-hook) | |
124 (not (get 'make-local-hook 'byte-obsolete-info))) | |
125 `(make-local-hook ,hook))) | |
104 | 126 |
105 (defmacro mh-mark-active-p (check-transient-mark-mode-flag) | 127 (defmacro mh-mark-active-p (check-transient-mark-mode-flag) |
106 "A macro that expands into appropriate code in XEmacs and nil in GNU Emacs. | 128 "A macro that expands into appropriate code in XEmacs and nil in GNU Emacs. |
107 In GNU Emacs if CHECK-TRANSIENT-MARK-MODE-FLAG is non-nil then check if | 129 In GNU Emacs if CHECK-TRANSIENT-MARK-MODE-FLAG is non-nil then check if |
108 variable `transient-mark-mode' is active." | 130 variable `transient-mark-mode' is active." |
285 ("image/.*" ignore ignore) | 307 ("image/.*" ignore ignore) |
286 ;; Default to displaying as text | 308 ;; Default to displaying as text |
287 (".*" mm-inline-text mm-readable-p)) | 309 (".*" mm-inline-text mm-readable-p)) |
288 "Alist of media types/tests saying whether types can be displayed inline.") | 310 "Alist of media types/tests saying whether types can be displayed inline.") |
289 | 311 |
290 ;; Needed by mh-comp.el and mh-mime.el | |
291 (defvar mh-mhn-compose-insert-flag nil | |
292 "Non-nil means MIME insertion was done. | |
293 Triggers an automatic call to `mh-edit-mhn' in `mh-send-letter'. | |
294 This variable is buffer-local.") | |
295 (make-variable-buffer-local 'mh-mhn-compose-insert-flag) | |
296 | |
297 (defvar mh-mml-compose-insert-flag nil | |
298 "Non-nil means that a MIME insertion was done. | |
299 This buffer-local variable is used to remember if a MIME insertion was done. | |
300 Triggers an automatic call to `mh-mml-to-mime' in `mh-send-letter'.") | |
301 (make-variable-buffer-local 'mh-mml-compose-insert-flag) | |
302 | |
303 ;; Copy of `goto-address-mail-regexp' | 312 ;; Copy of `goto-address-mail-regexp' |
304 (defvar mh-address-mail-regexp | 313 (defvar mh-address-mail-regexp |
305 "[-a-zA-Z0-9._]+@[-a-zA-z0-9_]+\\.+[a-zA-Z0-9]+" | 314 "[-a-zA-Z0-9._]+@[-a-zA-z0-9_]+\\.+[a-zA-Z0-9]+" |
306 "A regular expression probably matching an e-mail address.") | 315 "A regular expression probably matching an e-mail address.") |
307 | 316 |
316 (and (re-search-forward mh-address-mail-regexp | 325 (and (re-search-forward mh-address-mail-regexp |
317 (line-end-position) 'lim) | 326 (line-end-position) 'lim) |
318 (goto-char (match-beginning 0)))) | 327 (goto-char (match-beginning 0)))) |
319 (match-string-no-properties 0))) | 328 (match-string-no-properties 0))) |
320 | 329 |
330 (defun mh-mail-header-end () | |
331 "Substitute for `mail-header-end' that doesn't widen the buffer. | |
332 In MH-E we frequently need to find the end of headers in nested messages, where | |
333 the buffer has been narrowed. This function works in this situation." | |
334 (save-excursion | |
335 (rfc822-goto-eoh) | |
336 (point))) | |
337 | |
321 (defun mh-in-header-p () | 338 (defun mh-in-header-p () |
322 "Return non-nil if the point is in the header of a draft message." | 339 "Return non-nil if the point is in the header of a draft message." |
323 (< (point) (mail-header-end))) | 340 (< (point) (mh-mail-header-end))) |
324 | 341 |
325 (defun mh-header-field-beginning () | 342 (defun mh-header-field-beginning () |
326 "Move to the beginning of the current header field. | 343 "Move to the beginning of the current header field. |
327 Handles RFC 822 continuation lines." | 344 Handles RFC 822 continuation lines." |
328 (beginning-of-line) | 345 (beginning-of-line) |
340 (defun mh-letter-header-font-lock (limit) | 357 (defun mh-letter-header-font-lock (limit) |
341 "Return the entire mail header to font-lock. | 358 "Return the entire mail header to font-lock. |
342 Argument LIMIT limits search." | 359 Argument LIMIT limits search." |
343 (if (= (point) limit) | 360 (if (= (point) limit) |
344 nil | 361 nil |
345 (let* ((mail-header-end (save-match-data (mail-header-end))) | 362 (let* ((mail-header-end (save-match-data (mh-mail-header-end))) |
346 (lesser-limit (if (< mail-header-end limit) mail-header-end limit))) | 363 (lesser-limit (if (< mail-header-end limit) mail-header-end limit))) |
347 (when (mh-in-header-p) | 364 (when (mh-in-header-p) |
348 (set-match-data (list 1 lesser-limit)) | 365 (set-match-data (list 1 lesser-limit)) |
349 (goto-char lesser-limit) | 366 (goto-char lesser-limit) |
350 t)))) | 367 t)))) |
352 (defun mh-header-field-font-lock (field limit) | 369 (defun mh-header-field-font-lock (field limit) |
353 "Return the value of a header field FIELD to font-lock. | 370 "Return the value of a header field FIELD to font-lock. |
354 Argument LIMIT limits search." | 371 Argument LIMIT limits search." |
355 (if (= (point) limit) | 372 (if (= (point) limit) |
356 nil | 373 nil |
357 (let* ((mail-header-end (mail-header-end)) | 374 (let* ((mail-header-end (mh-mail-header-end)) |
358 (lesser-limit (if (< mail-header-end limit) mail-header-end limit)) | 375 (lesser-limit (if (< mail-header-end limit) mail-header-end limit)) |
359 (case-fold-search t)) | 376 (case-fold-search t)) |
360 (when (and (< (point) mail-header-end) ;Only within header | 377 (when (and (< (point) mail-header-end) ;Only within header |
361 (re-search-forward (format "^%s" field) lesser-limit t)) | 378 (re-search-forward (format "^%s" field) lesser-limit t)) |
362 (let ((match-one-b (match-beginning 0)) | 379 (let ((match-one-b (match-beginning 0)) |
422 "Limit font-lock in `mh-show-mode' to the header. | 439 "Limit font-lock in `mh-show-mode' to the header. |
423 Used when `mh-highlight-citation-p' is set to gnus, leaving the body to be | 440 Used when `mh-highlight-citation-p' is set to gnus, leaving the body to be |
424 dealt with by gnus highlighting. The region between BEG and END is | 441 dealt with by gnus highlighting. The region between BEG and END is |
425 given over to be fontified and LOUDLY controls if a user sees a | 442 given over to be fontified and LOUDLY controls if a user sees a |
426 message about the fontification operation." | 443 message about the fontification operation." |
427 (let ((header-end (mail-header-end))) | 444 (let ((header-end (mh-mail-header-end))) |
428 (cond | 445 (cond |
429 ((and (< beg header-end)(< end header-end)) | 446 ((and (< beg header-end)(< end header-end)) |
430 (font-lock-default-fontify-region beg end loudly)) | 447 (font-lock-default-fontify-region beg end loudly)) |
431 ((and (< beg header-end)(>= end header-end)) | 448 ((and (< beg header-end)(>= end header-end)) |
432 (font-lock-default-fontify-region beg header-end loudly)) | 449 (font-lock-default-fontify-region beg header-end loudly)) |
499 (defconst mh-folders-buffer "*MH-E Folders*") ;folder list | 516 (defconst mh-folders-buffer "*MH-E Folders*") ;folder list |
500 (defconst mh-info-buffer "*MH-E Info*") ;version information buffer | 517 (defconst mh-info-buffer "*MH-E Info*") ;version information buffer |
501 (defconst mh-log-buffer "*MH-E Log*") ;output of MH commands and so on | 518 (defconst mh-log-buffer "*MH-E Log*") ;output of MH commands and so on |
502 (defconst mh-recipients-buffer "*MH-E Recipients*") ;killed when draft sent | 519 (defconst mh-recipients-buffer "*MH-E Recipients*") ;killed when draft sent |
503 (defconst mh-sequences-buffer "*MH-E Sequences*") ;sequences list | 520 (defconst mh-sequences-buffer "*MH-E Sequences*") ;sequences list |
521 (defconst mh-mail-delivery-buffer "*MH-E Mail Delivery*") ;mail delivery log | |
522 | |
523 ;; Number of lines to keep in mh-log-buffer. | |
524 (defvar mh-log-buffer-lines 100) | |
504 | 525 |
505 ;; Window configuration before MH-E command. | 526 ;; Window configuration before MH-E command. |
506 (defvar mh-previous-window-config nil) | 527 (defvar mh-previous-window-config nil) |
507 | 528 |
508 ;;Non-nil means next SPC or whatever goes to next undeleted message. | 529 ;;Non-nil means next SPC or whatever goes to next undeleted message. |
533 | 554 |
534 (defvar mh-logo-cache nil) | 555 (defvar mh-logo-cache nil) |
535 | 556 |
536 (defun mh-logo-display () | 557 (defun mh-logo-display () |
537 "Modify mode line to display MH-E logo." | 558 "Modify mode line to display MH-E logo." |
538 (when (fboundp 'find-image) | 559 (mh-do-in-gnu-emacs |
539 (add-text-properties | 560 (add-text-properties |
540 0 2 | 561 0 2 |
541 `(display ,(or mh-logo-cache | 562 `(display ,(or mh-logo-cache |
542 (setq mh-logo-cache | 563 (setq mh-logo-cache |
543 (find-image '((:type xpm :ascent center | 564 (mh-funcall-if-exists |
544 :file "mh-logo.xpm")))))) | 565 find-image '((:type xpm :ascent center |
545 (car mode-line-buffer-identification)))) | 566 :file "mh-logo.xpm")))))) |
567 (car mode-line-buffer-identification))) | |
568 (mh-do-in-xemacs | |
569 (setq modeline-buffer-identification | |
570 (list | |
571 (if mh-modeline-glyph | |
572 (cons modeline-buffer-id-left-extent mh-modeline-glyph) | |
573 (cons modeline-buffer-id-left-extent "XEmacs%N:")) | |
574 (cons modeline-buffer-id-right-extent " %17b"))))) | |
575 | |
546 | 576 |
547 ;;; This holds a documentation string used by describe-mode. | 577 ;;; This holds a documentation string used by describe-mode. |
548 (defun mh-showing-mode (&optional arg) | 578 (defun mh-showing-mode (&optional arg) |
549 "Change whether messages should be displayed. | 579 "Change whether messages should be displayed. |
550 With arg, display messages iff ARG is positive." | 580 With arg, display messages iff ARG is positive." |
583 ,@body) | 613 ,@body) |
584 (mh-set-folder-modified-p mh-folder-updating-mod-flag))) | 614 (mh-set-folder-modified-p mh-folder-updating-mod-flag))) |
585 ,@(if (not save-modification-flag) | 615 ,@(if (not save-modification-flag) |
586 '((mh-set-folder-modified-p nil))))) | 616 '((mh-set-folder-modified-p nil))))) |
587 | 617 |
588 (put 'with-mh-folder-updating 'lisp-indent-hook 1) | 618 (put 'with-mh-folder-updating 'lisp-indent-hook 'defun) |
589 | 619 |
590 (defmacro mh-in-show-buffer (show-buffer &rest body) | 620 (defmacro mh-in-show-buffer (show-buffer &rest body) |
591 "Format is (mh-in-show-buffer (SHOW-BUFFER) &body BODY). | 621 "Format is (mh-in-show-buffer (SHOW-BUFFER) &body BODY). |
592 Display buffer SHOW-BUFFER in other window and execute BODY in it. | 622 Display buffer SHOW-BUFFER in other window and execute BODY in it. |
593 Stronger than `save-excursion', weaker than `save-window-excursion'." | 623 Stronger than `save-excursion', weaker than `save-window-excursion'." |
598 (unwind-protect | 628 (unwind-protect |
599 (progn | 629 (progn |
600 ,@body) | 630 ,@body) |
601 (select-window mh-in-show-buffer-saved-window)))) | 631 (select-window mh-in-show-buffer-saved-window)))) |
602 | 632 |
603 (put 'mh-in-show-buffer 'lisp-indent-hook 1) | 633 (put 'mh-in-show-buffer 'lisp-indent-hook 'defun) |
604 | 634 |
605 (defmacro mh-make-seq (name msgs) | 635 (defmacro mh-make-seq (name msgs) |
606 "Create sequence NAME with the given MSGS." | 636 "Create sequence NAME with the given MSGS." |
607 (list 'cons name msgs)) | 637 (list 'cons name msgs)) |
608 | 638 |
724 (unless (equal (buffer-name | 754 (unless (equal (buffer-name |
725 (window-buffer (frame-first-window (selected-frame)))) | 755 (window-buffer (frame-first-window (selected-frame)))) |
726 folder-buffer) | 756 folder-buffer) |
727 (delete-other-windows)) | 757 (delete-other-windows)) |
728 (mh-goto-cur-msg t) | 758 (mh-goto-cur-msg t) |
729 (and (fboundp 'deactivate-mark) (deactivate-mark)) | 759 (mh-funcall-if-exists deactivate-mark) |
730 (unwind-protect | 760 (unwind-protect |
731 (prog1 (call-interactively (function ,original-function)) | 761 (prog1 (call-interactively (function ,original-function)) |
732 (setq normal-exit t)) | 762 (setq normal-exit t)) |
733 (and (fboundp 'deactivate-mark) (deactivate-mark)) | 763 (mh-funcall-if-exists deactivate-mark) |
734 (cond ((not normal-exit) | 764 (cond ((not normal-exit) |
735 (set-window-configuration config)) | 765 (set-window-configuration config)) |
736 ,(if dont-return | 766 ,(if dont-return |
737 `(t (setq mh-previous-window-config config)) | 767 `(t (setq mh-previous-window-config config)) |
738 `((and (get-buffer cur-buffer-name) | 768 `((and (get-buffer cur-buffer-name) |
817 (mh-defun-show-buffer mh-show-thread-ancestor mh-thread-ancestor) | 847 (mh-defun-show-buffer mh-show-thread-ancestor mh-thread-ancestor) |
818 (mh-defun-show-buffer mh-show-thread-next-sibling mh-thread-next-sibling) | 848 (mh-defun-show-buffer mh-show-thread-next-sibling mh-thread-next-sibling) |
819 (mh-defun-show-buffer mh-show-thread-previous-sibling | 849 (mh-defun-show-buffer mh-show-thread-previous-sibling |
820 mh-thread-previous-sibling) | 850 mh-thread-previous-sibling) |
821 (mh-defun-show-buffer mh-show-index-visit-folder mh-index-visit-folder t) | 851 (mh-defun-show-buffer mh-show-index-visit-folder mh-index-visit-folder t) |
852 (mh-defun-show-buffer mh-show-toggle-tick mh-toggle-tick) | |
853 (mh-defun-show-buffer mh-show-narrow-to-tick mh-narrow-to-tick) | |
854 (mh-defun-show-buffer mh-show-junk-blacklist mh-junk-blacklist) | |
855 (mh-defun-show-buffer mh-show-junk-whitelist mh-junk-whitelist) | |
856 (mh-defun-show-buffer mh-show-index-new-messages mh-index-new-messages) | |
822 | 857 |
823 ;;; Populate mh-show-mode-map | 858 ;;; Populate mh-show-mode-map |
824 (gnus-define-keys mh-show-mode-map | 859 (gnus-define-keys mh-show-mode-map |
825 " " mh-show-page-msg | 860 " " mh-show-page-msg |
826 "!" mh-show-refile-or-write-again | 861 "!" mh-show-refile-or-write-again |
862 "'" mh-show-toggle-tick | |
827 "," mh-show-header-display | 863 "," mh-show-header-display |
828 "." mh-show-show | 864 "." mh-show-show |
829 ">" mh-show-write-message-to-file | 865 ">" mh-show-write-message-to-file |
830 "?" mh-help | 866 "?" mh-help |
831 "E" mh-show-extract-rejected-mail | 867 "E" mh-show-extract-rejected-mail |
865 "S" mh-show-sort-folder | 901 "S" mh-show-sort-folder |
866 "f" mh-show-visit-folder | 902 "f" mh-show-visit-folder |
867 "i" mh-index-search | 903 "i" mh-index-search |
868 "k" mh-show-kill-folder | 904 "k" mh-show-kill-folder |
869 "l" mh-show-list-folders | 905 "l" mh-show-list-folders |
906 "n" mh-index-new-messages | |
870 "o" mh-show-visit-folder | 907 "o" mh-show-visit-folder |
871 "r" mh-show-rescan-folder | 908 "r" mh-show-rescan-folder |
872 "s" mh-show-search-folder | 909 "s" mh-show-search-folder |
873 "t" mh-show-toggle-threads | 910 "t" mh-show-toggle-threads |
874 "u" mh-show-undo-folder | 911 "u" mh-show-undo-folder |
882 "n" mh-show-narrow-to-seq | 919 "n" mh-show-narrow-to-seq |
883 "p" mh-show-put-msg-in-seq | 920 "p" mh-show-put-msg-in-seq |
884 "s" mh-show-msg-is-in-seq | 921 "s" mh-show-msg-is-in-seq |
885 "w" mh-show-widen) | 922 "w" mh-show-widen) |
886 | 923 |
924 (define-key mh-show-mode-map "I" mh-inc-spool-map) | |
925 | |
926 (gnus-define-keys (mh-show-junk-map "J" mh-show-mode-map) | |
927 "?" mh-prefix-help | |
928 "b" mh-show-junk-blacklist | |
929 "w" mh-show-junk-whitelist) | |
930 | |
887 (gnus-define-keys (mh-show-thread-map "T" mh-show-mode-map) | 931 (gnus-define-keys (mh-show-thread-map "T" mh-show-mode-map) |
888 "?" mh-prefix-help | 932 "?" mh-prefix-help |
889 "u" mh-show-thread-ancestor | 933 "u" mh-show-thread-ancestor |
890 "p" mh-show-thread-previous-sibling | 934 "p" mh-show-thread-previous-sibling |
891 "n" mh-show-thread-next-sibling | 935 "n" mh-show-thread-next-sibling |
892 "t" mh-show-toggle-threads | 936 "t" mh-show-toggle-threads |
893 "d" mh-show-thread-delete | 937 "d" mh-show-thread-delete |
894 "o" mh-show-thread-refile) | 938 "o" mh-show-thread-refile) |
895 | 939 |
896 (gnus-define-keys (mh-show-limit-map "/" mh-show-mode-map) | 940 (gnus-define-keys (mh-show-limit-map "/" mh-show-mode-map) |
941 "'" mh-show-narrow-to-tick | |
897 "?" mh-prefix-help | 942 "?" mh-prefix-help |
898 "s" mh-show-narrow-to-subject | 943 "s" mh-show-narrow-to-subject |
899 "w" mh-show-widen) | 944 "w" mh-show-widen) |
900 | 945 |
901 (gnus-define-keys (mh-show-extract-map "X" mh-show-mode-map) | 946 (gnus-define-keys (mh-show-extract-map "X" mh-show-mode-map) |
930 ["Delete Sequence..." mh-show-delete-seq t] | 975 ["Delete Sequence..." mh-show-delete-seq t] |
931 ["Narrow to Sequence..." mh-show-narrow-to-seq t] | 976 ["Narrow to Sequence..." mh-show-narrow-to-seq t] |
932 ["Widen from Sequence" mh-show-widen t] | 977 ["Widen from Sequence" mh-show-widen t] |
933 "--" | 978 "--" |
934 ["Narrow to Subject Sequence" mh-show-narrow-to-subject t] | 979 ["Narrow to Subject Sequence" mh-show-narrow-to-subject t] |
980 ["Narrow to Tick Sequence" mh-show-narrow-to-tick | |
981 (save-excursion | |
982 (set-buffer mh-show-folder-buffer) | |
983 (and mh-tick-seq (mh-seq-msgs (mh-find-seq mh-tick-seq))))] | |
935 ["Delete Rest of Same Subject" mh-show-delete-subject t] | 984 ["Delete Rest of Same Subject" mh-show-delete-subject t] |
985 ["Toggle Tick Mark" mh-show-toggle-tick t] | |
936 "--" | 986 "--" |
937 ["Push State Out to MH" mh-show-update-sequences t])) | 987 ["Push State Out to MH" mh-show-update-sequences t])) |
938 | 988 |
939 (easy-menu-define | 989 (easy-menu-define |
940 mh-show-message-menu mh-show-mode-map "Menu for MH-E folder-message." | 990 mh-show-message-menu mh-show-mode-map "Menu for MH-E folder-message." |
977 ["Pack Folder" mh-show-pack-folder t] | 1027 ["Pack Folder" mh-show-pack-folder t] |
978 ["Sort Folder" mh-show-sort-folder t] | 1028 ["Sort Folder" mh-show-sort-folder t] |
979 "--" | 1029 "--" |
980 ["List Folders" mh-show-list-folders t] | 1030 ["List Folders" mh-show-list-folders t] |
981 ["Visit a Folder..." mh-show-visit-folder t] | 1031 ["Visit a Folder..." mh-show-visit-folder t] |
1032 ["View New Messages" mh-show-index-new-messages t] | |
982 ["Search a Folder..." mh-show-search-folder t] | 1033 ["Search a Folder..." mh-show-search-folder t] |
983 ["Indexed Search..." mh-index-search t] | 1034 ["Indexed Search..." mh-index-search t] |
984 "--" | 1035 "--" |
985 ["Quit MH-E" mh-quit t])) | 1036 ["Quit MH-E" mh-quit t])) |
986 | 1037 |
987 | 1038 |
988 ;;; Ensure new buffers won't get this mode if default-major-mode is nil. | 1039 ;;; Ensure new buffers won't get this mode if default-major-mode is nil. |
989 (put 'mh-show-mode 'mode-class 'special) | 1040 (put 'mh-show-mode 'mode-class 'special) |
1041 | |
1042 ;; Avoid compiler warning | |
1043 (defvar tool-bar-map) | |
990 | 1044 |
991 (define-derived-mode mh-show-mode text-mode "MH-Show" | 1045 (define-derived-mode mh-show-mode text-mode "MH-Show" |
992 "Major mode for showing messages in MH-E.\\<mh-show-mode-map> | 1046 "Major mode for showing messages in MH-E.\\<mh-show-mode-map> |
993 The value of `mh-show-mode-hook' is a list of functions to | 1047 The value of `mh-show-mode-hook' is a list of functions to |
994 be called, with no arguments, upon entry to this mode." | 1048 be called, with no arguments, upon entry to this mode." |
1013 (if (and mh-xemacs-flag | 1067 (if (and mh-xemacs-flag |
1014 font-lock-auto-fontify) | 1068 font-lock-auto-fontify) |
1015 (turn-on-font-lock)) | 1069 (turn-on-font-lock)) |
1016 (if (and (boundp 'tool-bar-mode) tool-bar-mode) | 1070 (if (and (boundp 'tool-bar-mode) tool-bar-mode) |
1017 (set (make-local-variable 'tool-bar-map) mh-show-tool-bar-map)) | 1071 (set (make-local-variable 'tool-bar-map) mh-show-tool-bar-map)) |
1072 (mh-funcall-if-exists mh-toolbar-init :show) | |
1018 (when mh-decode-mime-flag | 1073 (when mh-decode-mime-flag |
1074 (mh-make-local-hook 'kill-buffer-hook) | |
1019 (add-hook 'kill-buffer-hook 'mh-mime-cleanup nil t)) | 1075 (add-hook 'kill-buffer-hook 'mh-mime-cleanup nil t)) |
1020 (easy-menu-add mh-show-sequence-menu) | 1076 (easy-menu-add mh-show-sequence-menu) |
1021 (easy-menu-add mh-show-message-menu) | 1077 (easy-menu-add mh-show-message-menu) |
1022 (easy-menu-add mh-show-folder-menu) | 1078 (easy-menu-add mh-show-folder-menu) |
1023 (make-local-variable 'mh-show-folder-buffer) | 1079 (make-local-variable 'mh-show-folder-buffer) |
1032 (if (not (featurep 'goto-addr)) | 1088 (if (not (featurep 'goto-addr)) |
1033 (load "goto-addr" t t)) | 1089 (load "goto-addr" t t)) |
1034 (if (fboundp 'goto-address) | 1090 (if (fboundp 'goto-address) |
1035 (goto-address)))) | 1091 (goto-address)))) |
1036 | 1092 |
1093 | |
1094 | |
1095 ;; X-Face and Face display | |
1037 (defvar mh-show-xface-function | 1096 (defvar mh-show-xface-function |
1038 (cond ((and mh-xemacs-flag (locate-library "x-face")) | 1097 (cond ((and mh-xemacs-flag (locate-library "x-face") (not (featurep 'xface))) |
1039 (load "x-face" t t) | 1098 (load "x-face" t t) |
1040 (if (fboundp 'x-face-xmas-wl-display-x-face) | 1099 #'mh-face-display-function) |
1041 #'x-face-xmas-wl-display-x-face | 1100 ((>= emacs-major-version 21) |
1042 #'ignore)) | 1101 #'mh-face-display-function) |
1043 ((and (not mh-xemacs-flag) (>= emacs-major-version 21)) | |
1044 (load "x-face-e21" t t) | |
1045 (if (fboundp 'x-face-decode-message-header) | |
1046 #'x-face-decode-message-header | |
1047 #'ignore)) | |
1048 (t #'ignore)) | 1102 (t #'ignore)) |
1049 "Determine at run time what function should be called to display X-Face.") | 1103 "Determine at run time what function should be called to display X-Face.") |
1050 | 1104 |
1105 (defvar mh-uncompface-executable | |
1106 (and (fboundp 'executable-find) (executable-find "uncompface"))) | |
1107 | |
1108 (defun mh-face-to-png (data) | |
1109 "Convert base64 encoded DATA to png image." | |
1110 (with-temp-buffer | |
1111 (insert data) | |
1112 (ignore-errors (base64-decode-region (point-min) (point-max))) | |
1113 (buffer-string))) | |
1114 | |
1115 (defun mh-uncompface (data) | |
1116 "Run DATA through `uncompface' to generate bitmap." | |
1117 (with-temp-buffer | |
1118 (insert data) | |
1119 (when (and mh-uncompface-executable | |
1120 (equal (call-process-region (point-min) (point-max) | |
1121 mh-uncompface-executable t '(t nil)) | |
1122 0)) | |
1123 (mh-icontopbm) | |
1124 (buffer-string)))) | |
1125 | |
1126 (defun mh-icontopbm () | |
1127 "Elisp substitute for `icontopbm'." | |
1128 (goto-char (point-min)) | |
1129 (let ((end (point-max))) | |
1130 (while (re-search-forward "0x\\(..\\)\\(..\\)," nil t) | |
1131 (save-excursion | |
1132 (goto-char (point-max)) | |
1133 (insert (string-to-number (match-string 1) 16)) | |
1134 (insert (string-to-number (match-string 2) 16)))) | |
1135 (delete-region (point-min) end) | |
1136 (goto-char (point-min)) | |
1137 (insert "P4\n48 48\n"))) | |
1138 | |
1139 (mh-do-in-xemacs (defvar default-enable-multibyte-characters)) | |
1140 | |
1141 (defun mh-face-display-function () | |
1142 "Display a Face or X-Face header field. | |
1143 Display Face if both are present." | |
1144 (save-restriction | |
1145 (goto-char (point-min)) | |
1146 (re-search-forward "\n\n" (point-max) t) | |
1147 (narrow-to-region (point-min) (point)) | |
1148 (let* ((case-fold-search t) | |
1149 (default-enable-multibyte-characters nil) | |
1150 (face (message-fetch-field "face" t)) | |
1151 (x-face (message-fetch-field "x-face" t)) | |
1152 (url (message-fetch-field "x-image-url" t)) | |
1153 raw type) | |
1154 (cond (face (setq raw (mh-face-to-png face) | |
1155 type 'png)) | |
1156 (x-face (setq raw (mh-uncompface x-face) | |
1157 type 'pbm)) | |
1158 (url (setq type 'url))) | |
1159 (when type | |
1160 (goto-char (point-min)) | |
1161 (when (re-search-forward "^from:" (point-max) t) | |
1162 ;; GNU Emacs | |
1163 (mh-do-in-gnu-emacs | |
1164 (if (eq type 'url) | |
1165 (mh-x-image-url-display url) | |
1166 (mh-funcall-if-exists | |
1167 insert-image (create-image | |
1168 raw type t | |
1169 :foreground (face-foreground 'mh-show-xface-face) | |
1170 :background (face-background 'mh-show-xface-face)) | |
1171 " "))) | |
1172 ;; XEmacs | |
1173 (mh-do-in-xemacs | |
1174 (cond | |
1175 ((eq type 'url) | |
1176 (mh-x-image-url-display url)) | |
1177 ((eq type 'png) | |
1178 (when (featurep 'png) | |
1179 (set-extent-begin-glyph | |
1180 (make-extent (point) (point)) | |
1181 (make-glyph (vector 'png ':data (mh-face-to-png face)))))) | |
1182 ;; Try internal xface support if available... | |
1183 ((and (eq type 'pbm) (featurep 'xface)) | |
1184 (set-glyph-face | |
1185 (set-extent-begin-glyph | |
1186 (make-extent (point) (point)) | |
1187 (make-glyph (vector 'xface ':data (concat "X-Face: " x-face)))) | |
1188 'mh-show-xface-face)) | |
1189 ;; Otherwise try external support with x-face... | |
1190 ((and (eq type 'pbm) | |
1191 (fboundp 'x-face-xmas-wl-display-x-face) | |
1192 (fboundp 'executable-find) (executable-find "uncompface")) | |
1193 (mh-funcall-if-exists x-face-xmas-wl-display-x-face))) | |
1194 (when raw (insert " ")))))))) | |
1195 | |
1196 | |
1051 (defun mh-show-xface () | 1197 (defun mh-show-xface () |
1052 "Display X-Face." | 1198 "Display X-Face." |
1053 (when (and mh-show-use-xface-flag | 1199 (when (and window-system mh-show-use-xface-flag |
1054 (or mh-decode-mime-flag mhl-formfile | 1200 (or mh-decode-mime-flag mhl-formfile |
1055 mh-clean-message-header-flag)) | 1201 mh-clean-message-header-flag)) |
1056 (funcall mh-show-xface-function))) | 1202 (funcall mh-show-xface-function))) |
1203 | |
1204 | |
1205 | |
1206 ;; X-Image-URL display | |
1207 | |
1208 (defvar mh-x-image-cache-directory nil | |
1209 "Directory where X-Image-URL images are cached.") | |
1210 | |
1211 (defvar mh-convert-executable (executable-find "convert")) | |
1212 (defvar mh-wget-executable (executable-find "wget")) | |
1213 (defvar mh-x-image-temp-file nil) | |
1214 (defvar mh-x-image-url nil) | |
1215 (defvar mh-x-image-marker nil) | |
1216 (defvar mh-x-image-url-cache-file nil) | |
1217 | |
1218 (defun mh-x-image-url-cache-canonicalize (url) | |
1219 "Canonicalize URL. | |
1220 Replace the ?/ character with a ?! character." | |
1221 (with-temp-buffer | |
1222 (insert url) | |
1223 (goto-char (point-min)) | |
1224 (while (search-forward "/" nil t) (replace-match "!")) | |
1225 (format "%s/%s.png" mh-x-image-cache-directory (buffer-string)))) | |
1226 | |
1227 (defun mh-x-image-url-fetch-image (url cache-file marker sentinel) | |
1228 "Fetch and display the image specified by URL. | |
1229 After the image is fetched, it is stored in CACHE-FILE. It will be displayed | |
1230 in a buffer and position specified by MARKER. The actual display is carried | |
1231 out by the SENTINEL function." | |
1232 (if (and mh-wget-executable | |
1233 mh-fetch-x-image-url | |
1234 (or (eq mh-fetch-x-image-url t) | |
1235 (y-or-n-p (format "Fetch %s? " url)))) | |
1236 (let ((buffer (get-buffer-create (generate-new-buffer-name " *mh-url*"))) | |
1237 (filename (make-temp-name "/tmp/mhe-wget"))) | |
1238 (save-excursion | |
1239 (set-buffer buffer) | |
1240 (set (make-local-variable 'mh-x-image-url-cache-file) cache-file) | |
1241 (set (make-local-variable 'mh-x-image-marker) marker) | |
1242 (set (make-local-variable 'mh-x-image-temp-file) filename)) | |
1243 (set-process-sentinel | |
1244 (start-process "*wget*" buffer mh-wget-executable "-O" filename url) | |
1245 sentinel)) | |
1246 ;; Make sure we don't ask about this image again | |
1247 (when (and mh-wget-executable (eq mh-fetch-x-image-url 'ask)) | |
1248 (make-symbolic-link mh-x-image-cache-directory cache-file t)))) | |
1249 | |
1250 (defun mh-x-image-display (image marker) | |
1251 "Display IMAGE at MARKER." | |
1252 (save-excursion | |
1253 (set-buffer (marker-buffer marker)) | |
1254 (let ((buffer-read-only nil) | |
1255 (default-enable-multibyte-characters nil) | |
1256 (buffer-modified-flag (buffer-modified-p))) | |
1257 (unwind-protect | |
1258 (when (and (file-readable-p image) (not (file-symlink-p image))) | |
1259 (goto-char marker) | |
1260 (mh-do-in-gnu-emacs | |
1261 (mh-funcall-if-exists insert-image (create-image image 'png))) | |
1262 (mh-do-in-xemacs | |
1263 (when (featurep 'png) | |
1264 (set-extent-begin-glyph | |
1265 (make-extent (point) (point)) | |
1266 (make-glyph | |
1267 (vector 'png ':data (with-temp-buffer | |
1268 (insert-file-contents-literally image) | |
1269 (buffer-string)))))))) | |
1270 (set-buffer-modified-p buffer-modified-flag))))) | |
1271 | |
1272 (defun mh-x-image-scale-and-display (process change) | |
1273 "When the wget PROCESS terminates scale and display image. | |
1274 The argument CHANGE is ignored." | |
1275 (when (eq (process-status process) 'exit) | |
1276 (let (marker temp-file cache-filename wget-buffer) | |
1277 (save-excursion | |
1278 (set-buffer (setq wget-buffer (process-buffer process))) | |
1279 (setq marker mh-x-image-marker | |
1280 cache-filename mh-x-image-url-cache-file | |
1281 temp-file mh-x-image-temp-file)) | |
1282 (when mh-convert-executable | |
1283 (call-process mh-convert-executable nil nil nil "-resize" "96x48" | |
1284 temp-file cache-filename)) | |
1285 (if (file-exists-p cache-filename) | |
1286 (mh-x-image-display cache-filename marker) | |
1287 (make-symbolic-link mh-x-image-cache-directory cache-filename t)) | |
1288 (ignore-errors | |
1289 (set-marker marker nil) | |
1290 (delete-process process) | |
1291 (kill-buffer wget-buffer) | |
1292 (delete-file temp-file))))) | |
1293 | |
1294 (defun mh-x-image-url-display (url) | |
1295 "Display image from location URL. | |
1296 If the URL isn't present in the cache then it is fetched with wget." | |
1297 (let ((cache-filename (mh-x-image-url-cache-canonicalize url)) | |
1298 (marker (set-marker (make-marker) (point)))) | |
1299 (cond ((file-exists-p cache-filename) | |
1300 (mh-x-image-display cache-filename marker)) | |
1301 ((not mh-fetch-x-image-url) | |
1302 (set-marker marker nil)) | |
1303 ((and (not (file-exists-p mh-x-image-cache-directory)) | |
1304 (call-process "mkdir" nil nil nil mh-x-image-cache-directory) | |
1305 nil)) | |
1306 ((and (file-exists-p mh-x-image-cache-directory) | |
1307 (file-directory-p mh-x-image-cache-directory)) | |
1308 (mh-x-image-url-fetch-image url cache-filename marker | |
1309 'mh-x-image-scale-and-display))))) | |
1310 | |
1311 | |
1057 | 1312 |
1058 (defun mh-maybe-show (&optional msg) | 1313 (defun mh-maybe-show (&optional msg) |
1059 "Display message at cursor, but only if in show mode. | 1314 "Display message at cursor, but only if in show mode. |
1060 If optional arg MSG is non-nil, display that message instead." | 1315 If optional arg MSG is non-nil, display that message instead." |
1061 (if mh-showing-mode (mh-show msg))) | 1316 (if mh-showing-mode (mh-show msg))) |
1108 (shrink-window (- (window-height) mh-summary-height))) | 1363 (shrink-window (- (window-height) mh-summary-height))) |
1109 (mh-recenter nil) | 1364 (mh-recenter nil) |
1110 (if (not (memq msg mh-seen-list)) | 1365 (if (not (memq msg mh-seen-list)) |
1111 (setq mh-seen-list (cons msg mh-seen-list))) | 1366 (setq mh-seen-list (cons msg mh-seen-list))) |
1112 (when mh-update-sequences-after-mh-show-flag | 1367 (when mh-update-sequences-after-mh-show-flag |
1368 (if mh-index-data (mh-index-update-unseen msg)) | |
1113 (mh-update-sequences)) | 1369 (mh-update-sequences)) |
1114 (run-hooks 'mh-show-hook)) | 1370 (run-hooks 'mh-show-hook)) |
1115 | 1371 |
1116 (defun mh-modify (&optional message) | 1372 (defun mh-modify (&optional message) |
1117 "Edit message at cursor. | 1373 "Edit message at cursor. |
1145 | 1401 |
1146 ;; Just show the edit buffer... | 1402 ;; Just show the edit buffer... |
1147 (delete-other-windows) | 1403 (delete-other-windows) |
1148 (switch-to-buffer edit-buffer))) | 1404 (switch-to-buffer edit-buffer))) |
1149 | 1405 |
1150 (defun mh-decode-content-transfer-encoded-message () | |
1151 "Run mimencode on message body, if needed." | |
1152 (let ((case-fold-search t) | |
1153 (header-end (mail-header-end))) | |
1154 (goto-char (point-min)) | |
1155 (when (re-search-forward "^content-transfer-encoding: " header-end t) | |
1156 (let ((enc (buffer-substring-no-properties (point) (line-end-position))) | |
1157 cmdline) | |
1158 (setq cmdline | |
1159 (cond ((string-match "base64" enc) (list "-u" "-b" "-p")) | |
1160 ((string-match "quoted-printable" enc) (list "-u" "-q")) | |
1161 (t nil))) | |
1162 (when cmdline | |
1163 (beginning-of-line) | |
1164 (insert "Removed-") | |
1165 (setq header-end (mail-header-end)) | |
1166 (goto-char (1+ header-end)) | |
1167 (apply #'call-process-region (1+ header-end) (point-max) "mimencode" | |
1168 t t nil cmdline)))))) | |
1169 | |
1170 (defun mh-show-unquote-From () | 1406 (defun mh-show-unquote-From () |
1171 "Decode >From at beginning of lines for `mh-show-mode'." | 1407 "Decode >From at beginning of lines for `mh-show-mode'." |
1172 (save-excursion | 1408 (save-excursion |
1173 (let ((modified (buffer-modified-p)) | 1409 (let ((modified (buffer-modified-p)) |
1174 (case-fold-search nil)) | 1410 (case-fold-search nil)) |
1175 (goto-char (mail-header-end)) | 1411 (goto-char (mh-mail-header-end)) |
1176 (while (re-search-forward "^>From" nil t) | 1412 (while (re-search-forward "^>From" nil t) |
1177 (replace-match "From")) | 1413 (replace-match "From")) |
1178 (set-buffer-modified-p modified)))) | 1414 (set-buffer-modified-p modified)))) |
1179 | 1415 |
1180 (defun mh-msg-folder (folder-name) | 1416 (defun mh-msg-folder (folder-name) |
1224 (mh-exec-lib-cmd-output "mhl" "-nobell" "-noclear" | 1460 (mh-exec-lib-cmd-output "mhl" "-nobell" "-noclear" |
1225 (if (stringp formfile) | 1461 (if (stringp formfile) |
1226 (list "-form" formfile)) | 1462 (list "-form" formfile)) |
1227 msg-filename) | 1463 msg-filename) |
1228 (insert-file-contents-literally msg-filename)) | 1464 (insert-file-contents-literally msg-filename)) |
1229 (if mh-decode-content-transfer-encoded-message-flag | |
1230 (mh-decode-content-transfer-encoded-message)) | |
1231 ;; Cleanup old mime handles | 1465 ;; Cleanup old mime handles |
1232 (mh-mime-cleanup) | 1466 (mh-mime-cleanup) |
1233 ;; Use mm to display buffer | 1467 ;; Use mm to display buffer |
1234 (when (and mh-decode-mime-flag (not formfile)) | 1468 (when (and mh-decode-mime-flag (not formfile)) |
1235 (mh-add-missing-mime-version-header) | 1469 (mh-add-missing-mime-version-header) |
1236 (setf (mh-buffer-data) (mh-make-buffer-data)) | 1470 (setf (mh-buffer-data) (mh-make-buffer-data)) |
1237 (mh-mime-display)) | 1471 (mh-mime-display)) |
1472 (mh-show-mode) | |
1238 ;; Header cleanup | 1473 ;; Header cleanup |
1239 (goto-char (point-min)) | 1474 (goto-char (point-min)) |
1240 (cond (clean-message-header | 1475 (cond (clean-message-header |
1241 (mh-clean-msg-header (point-min) | 1476 (mh-clean-msg-header (point-min) |
1242 invisible-headers | 1477 invisible-headers |
1243 visible-headers) | 1478 visible-headers) |
1244 (goto-char (point-min))) | 1479 (goto-char (point-min))) |
1245 (t | 1480 (t |
1246 (mh-start-of-uncleaned-message))) | 1481 (mh-start-of-uncleaned-message))) |
1482 (mh-decode-message-header) | |
1247 ;; the parts of visiting we want to do (no locking) | 1483 ;; the parts of visiting we want to do (no locking) |
1248 (or (eq buffer-undo-list t) ;don't save undo info for prev msgs | 1484 (or (eq buffer-undo-list t) ;don't save undo info for prev msgs |
1249 (setq buffer-undo-list nil)) | 1485 (setq buffer-undo-list nil)) |
1250 (set-buffer-auto-saved) | 1486 (set-buffer-auto-saved) |
1251 ;; the parts of set-visited-file-name we want to do (no locking) | 1487 ;; the parts of set-visited-file-name we want to do (no locking) |
1252 (setq buffer-file-name msg-filename) | 1488 (setq buffer-file-name msg-filename) |
1253 (setq buffer-backed-up nil) | 1489 (setq buffer-backed-up nil) |
1254 (auto-save-mode 1) | 1490 (auto-save-mode 1) |
1255 (set-mark nil) | 1491 (set-mark nil) |
1256 (mh-show-mode) | |
1257 (unwind-protect | 1492 (unwind-protect |
1258 (when (and mh-decode-mime-flag (not formfile)) | 1493 (when (and mh-decode-mime-flag (not formfile)) |
1259 (setq buffer-read-only nil) | 1494 (setq buffer-read-only nil) |
1260 (mh-display-smileys) | 1495 (mh-display-smileys) |
1261 (mh-display-emphasis)) | 1496 (mh-display-emphasis)) |
1274 Header is cleaned from START to the end of the message header. | 1509 Header is cleaned from START to the end of the message header. |
1275 INVISIBLE-HEADERS contains a regular expression specifying lines to delete | 1510 INVISIBLE-HEADERS contains a regular expression specifying lines to delete |
1276 from the header. VISIBLE-HEADERS contains a regular expression specifying the | 1511 from the header. VISIBLE-HEADERS contains a regular expression specifying the |
1277 lines to display. INVISIBLE-HEADERS is ignored if VISIBLE-HEADERS is non-nil." | 1512 lines to display. INVISIBLE-HEADERS is ignored if VISIBLE-HEADERS is non-nil." |
1278 (let ((case-fold-search t) | 1513 (let ((case-fold-search t) |
1514 (buffer-read-only nil) | |
1279 (after-change-functions nil)) ;Work around emacs-20 font-lock bug | 1515 (after-change-functions nil)) ;Work around emacs-20 font-lock bug |
1280 ;causing an endless loop. | 1516 ;causing an endless loop. |
1281 (save-restriction | 1517 (save-restriction |
1282 (goto-char start) | 1518 (goto-char start) |
1283 (if (search-forward "\n\n" nil 'move) | 1519 (if (search-forward "\n\n" nil 'move) |
1304 "Delete the next LINES lines." | 1540 "Delete the next LINES lines." |
1305 (delete-region (point) (progn (forward-line lines) (point)))) | 1541 (delete-region (point) (progn (forward-line lines) (point)))) |
1306 | 1542 |
1307 (defun mh-notate (msg notation offset) | 1543 (defun mh-notate (msg notation offset) |
1308 "Mark MSG with the character NOTATION at position OFFSET. | 1544 "Mark MSG with the character NOTATION at position OFFSET. |
1309 Null MSG means the message at cursor." | 1545 Null MSG means the message at cursor. |
1546 If NOTATION is nil then no change in the buffer occurs." | |
1310 (save-excursion | 1547 (save-excursion |
1311 (if (or (null msg) | 1548 (if (or (null msg) |
1312 (mh-goto-msg msg t t)) | 1549 (mh-goto-msg msg t t)) |
1313 (with-mh-folder-updating (t) | 1550 (with-mh-folder-updating (t) |
1314 (beginning-of-line) | 1551 (beginning-of-line) |
1315 (forward-char offset) | 1552 (forward-char offset) |
1316 (delete-char 1) | 1553 (let ((notation (or notation (char-after)))) |
1317 (insert notation))))) | 1554 (delete-char 1) |
1555 (insert notation)))))) | |
1318 | 1556 |
1319 (defun mh-find-msg-get-num (step) | 1557 (defun mh-find-msg-get-num (step) |
1320 "Return the message number of the message nearest the cursor. | 1558 "Return the message number of the message nearest the cursor. |
1321 Jumps over non-message lines, such as inc errors. | 1559 Jumps over non-message lines, such as inc errors. |
1322 If we have to search, STEP tells whether to search forward or backward." | 1560 If we have to search, STEP tells whether to search forward or backward." |
1403 (if (not mh-user-path) | 1641 (if (not mh-user-path) |
1404 (setq mh-user-path "Mail")) | 1642 (setq mh-user-path "Mail")) |
1405 (setq mh-user-path | 1643 (setq mh-user-path |
1406 (file-name-as-directory | 1644 (file-name-as-directory |
1407 (expand-file-name mh-user-path (expand-file-name "~")))) | 1645 (expand-file-name mh-user-path (expand-file-name "~")))) |
1646 (unless mh-x-image-cache-directory | |
1647 (setq mh-x-image-cache-directory | |
1648 (expand-file-name ".mhe-x-image-cache" mh-user-path))) | |
1408 (setq mh-draft-folder (mh-get-profile-field "Draft-Folder:")) | 1649 (setq mh-draft-folder (mh-get-profile-field "Draft-Folder:")) |
1409 (if mh-draft-folder | 1650 (if mh-draft-folder |
1410 (progn | 1651 (progn |
1411 (if (not (mh-folder-name-p mh-draft-folder)) | 1652 (if (not (mh-folder-name-p mh-draft-folder)) |
1412 (setq mh-draft-folder (format "+%s" mh-draft-folder))) | 1653 (setq mh-draft-folder (format "+%s" mh-draft-folder))) |
1540 (width 0)) | 1781 (width 0)) |
1541 (save-excursion | 1782 (save-excursion |
1542 (set-buffer tmp-buffer) | 1783 (set-buffer tmp-buffer) |
1543 (erase-buffer) | 1784 (erase-buffer) |
1544 (apply 'call-process | 1785 (apply 'call-process |
1545 (expand-file-name "scan" mh-progs) nil '(t nil) nil | 1786 (expand-file-name mh-scan-prog mh-progs) nil '(t nil) nil |
1546 (list folder "last" "-format" "%(msg)")) | 1787 (list folder "last" "-format" "%(msg)")) |
1547 (goto-char (point-min)) | 1788 (goto-char (point-min)) |
1548 (if (re-search-forward mh-scan-msg-number-regexp nil 0 1) | 1789 (if (re-search-forward mh-scan-msg-number-regexp nil 0 1) |
1549 (setq width (length (buffer-substring | 1790 (setq width (length (buffer-substring |
1550 (match-beginning 1) (match-end 1)))))) | 1791 (match-beginning 1) (match-end 1)))))) |
1580 (setcdr head (cddr head)) | 1821 (setcdr head (cddr head)) |
1581 (setq head (cdr head)))) | 1822 (setq head (cdr head)))) |
1582 sorted-msgs)) | 1823 sorted-msgs)) |
1583 | 1824 |
1584 (defvar mh-sub-folders-cache (make-hash-table :test #'equal)) | 1825 (defvar mh-sub-folders-cache (make-hash-table :test #'equal)) |
1826 (defvar mh-current-folder-name nil) | |
1585 | 1827 |
1586 (defun mh-normalize-folder-name (folder &optional empty-string-okay | 1828 (defun mh-normalize-folder-name (folder &optional empty-string-okay |
1587 dont-remove-trailing-slash) | 1829 dont-remove-trailing-slash) |
1588 "Normalizes FOLDER name. | 1830 "Normalizes FOLDER name. |
1589 Makes sure that two '/' characters never occur next to each other. Also all | 1831 Makes sure that two '/' characters never occur next to each other. Also all |
1600 ;; Replace two or more consecutive '/' characters with a single '/' | 1842 ;; Replace two or more consecutive '/' characters with a single '/' |
1601 (while (string-match "//" folder) | 1843 (while (string-match "//" folder) |
1602 (setq folder (replace-match "/" nil t folder))) | 1844 (setq folder (replace-match "/" nil t folder))) |
1603 (let* ((length (length folder)) | 1845 (let* ((length (length folder)) |
1604 (trailing-slash-present (and (> length 0) | 1846 (trailing-slash-present (and (> length 0) |
1605 (equal (aref folder (1- length)) ?/)))) | 1847 (equal (aref folder (1- length)) ?/))) |
1606 (let ((components (split-string folder "/")) | 1848 (leading-slash-present (and (> length 0) |
1849 (equal (aref folder 0) ?/)))) | |
1850 (when (and (> length 0) (equal (aref folder 0) ?@) | |
1851 (stringp mh-current-folder-name)) | |
1852 (setq folder (format "%s/%s/" mh-current-folder-name | |
1853 (substring folder 1)))) | |
1854 ;; XXX: Purge empty strings from the list that split-string returns. In | |
1855 ;; XEmacs, (split-string "+foo/" "/") returns ("+foo" "") while in GNU | |
1856 ;; Emacs it returns ("+foo"). In the code it is assumed that the | |
1857 ;; components list has no empty strings. | |
1858 (let ((components (delete "" (split-string folder "/"))) | |
1607 (result ())) | 1859 (result ())) |
1608 ;; Remove .. and . from the pathname. | 1860 ;; Remove .. and . from the pathname. |
1609 (dolist (component components) | 1861 (dolist (component components) |
1610 (cond ((and (equal component "..") result) | 1862 (cond ((and (equal component "..") result) |
1611 (pop result)) | 1863 (pop result)) |
1616 (dolist (component result) | 1868 (dolist (component result) |
1617 (setq folder (concat component "/" folder))) | 1869 (setq folder (concat component "/" folder))) |
1618 ;; Remove trailing '/' if needed. | 1870 ;; Remove trailing '/' if needed. |
1619 (unless (and trailing-slash-present dont-remove-trailing-slash) | 1871 (unless (and trailing-slash-present dont-remove-trailing-slash) |
1620 (when (not (equal folder "")) | 1872 (when (not (equal folder "")) |
1621 (setq folder (substring folder 0 (1- (length folder)))))))) | 1873 (setq folder (substring folder 0 (1- (length folder)))))) |
1874 (when leading-slash-present | |
1875 (setq folder (concat "/" folder))))) | |
1622 (cond ((and empty-string-okay (equal folder ""))) | 1876 (cond ((and empty-string-okay (equal folder ""))) |
1623 ((equal folder "") (setq folder "+")) | 1877 ((equal folder "") (setq folder "+")) |
1624 ((not (equal (aref folder 0) ?+)) (setq folder (concat "+" folder))))) | 1878 ((not (equal (aref folder 0) ?+)) (setq folder (concat "+" folder))))) |
1625 folder) | 1879 folder) |
1626 | 1880 |
1711 (setq one-ancestor-found t)))) | 1965 (setq one-ancestor-found t)))) |
1712 (remhash nil mh-sub-folders-cache)))) | 1966 (remhash nil mh-sub-folders-cache)))) |
1713 | 1967 |
1714 (defvar mh-folder-hist nil) | 1968 (defvar mh-folder-hist nil) |
1715 (defvar mh-speed-folder-map) | 1969 (defvar mh-speed-folder-map) |
1970 (defvar mh-speed-flists-cache) | |
1971 | |
1972 (defvar mh-allow-root-folder-flag nil | |
1973 "Non-nil means \"+\" is an acceptable folder name. | |
1974 This variable is used to communicate with `mh-folder-completion-function'. That | |
1975 function can have exactly three arguments so we bind this variable to t or nil. | |
1976 | |
1977 This variable should never be set.") | |
1978 | |
1716 (defvar mh-folder-completion-map (copy-keymap minibuffer-local-completion-map)) | 1979 (defvar mh-folder-completion-map (copy-keymap minibuffer-local-completion-map)) |
1717 (define-key mh-folder-completion-map " " 'minibuffer-complete) | 1980 (define-key mh-folder-completion-map " " 'minibuffer-complete) |
1981 | |
1982 (defun mh-speed-flists-active-p () | |
1983 "Check if speedbar is running with message counts enabled." | |
1984 (and (featurep 'mh-speed) | |
1985 (> (hash-table-count mh-speed-flists-cache) 0))) | |
1718 | 1986 |
1719 (defun mh-folder-completion-function (name predicate flag) | 1987 (defun mh-folder-completion-function (name predicate flag) |
1720 "Programmable completion for folder names. | 1988 "Programmable completion for folder names. |
1721 NAME is the partial folder name that has been input. PREDICATE if non-nil is a | 1989 NAME is the partial folder name that has been input. PREDICATE if non-nil is a |
1722 function that is used to filter the possible choices and FLAG determines | 1990 function that is used to filter the possible choices and FLAG determines |
1745 (t try-res)))) | 2013 (t try-res)))) |
1746 ((eq flag t) | 2014 ((eq flag t) |
1747 (all-completions | 2015 (all-completions |
1748 remainder (mh-sub-folders last-complete t) predicate)) | 2016 remainder (mh-sub-folders last-complete t) predicate)) |
1749 ((eq flag 'lambda) | 2017 ((eq flag 'lambda) |
1750 (file-exists-p | 2018 (let ((path (concat mh-user-path |
1751 (concat mh-user-path | 2019 (substring (mh-normalize-folder-name name) 1)))) |
1752 (substring (mh-normalize-folder-name name) 1))))))) | 2020 (cond (mh-allow-root-folder-flag (file-exists-p path)) |
1753 | 2021 ((equal path mh-user-path) nil) |
1754 (defun mh-folder-completing-read (prompt default) | 2022 (t (file-exists-p path)))))))) |
1755 "Read folder name with PROMPT and default result DEFAULT." | 2023 |
2024 (defun mh-folder-completing-read (prompt default allow-root-folder-flag) | |
2025 "Read folder name with PROMPT and default result DEFAULT. | |
2026 If ALLOW-ROOT-FOLDER-FLAG is non-nil then \"+\" is allowed to be a folder name | |
2027 corresponding to `mh-user-path'." | |
1756 (mh-normalize-folder-name | 2028 (mh-normalize-folder-name |
1757 (let ((minibuffer-local-completion-map mh-folder-completion-map)) | 2029 (let ((minibuffer-local-completion-map mh-folder-completion-map) |
2030 (mh-allow-root-folder-flag allow-root-folder-flag)) | |
1758 (completing-read prompt 'mh-folder-completion-function nil nil nil | 2031 (completing-read prompt 'mh-folder-completion-function nil nil nil |
1759 'mh-folder-hist default)) | 2032 'mh-folder-hist default)) |
1760 t)) | 2033 t)) |
1761 | 2034 |
1762 (defun mh-prompt-for-folder (prompt default can-create | 2035 (defun mh-prompt-for-folder (prompt default can-create |
1773 (let* ((default-string (cond (default-string (format " [%s]? " | 2046 (let* ((default-string (cond (default-string (format " [%s]? " |
1774 default-string)) | 2047 default-string)) |
1775 ((equal "" default) "? ") | 2048 ((equal "" default) "? ") |
1776 (t (format " [%s]? " default)))) | 2049 (t (format " [%s]? " default)))) |
1777 (prompt (format "%s folder%s" prompt default-string)) | 2050 (prompt (format "%s folder%s" prompt default-string)) |
2051 (mh-current-folder-name mh-current-folder) | |
1778 read-name folder-name) | 2052 read-name folder-name) |
1779 (while (and (setq read-name (mh-folder-completing-read prompt default)) | 2053 (while (and (setq read-name (mh-folder-completing-read |
2054 prompt default allow-root-folder-flag)) | |
1780 (equal read-name "") | 2055 (equal read-name "") |
1781 (equal default ""))) | 2056 (equal default ""))) |
1782 (cond ((or (equal read-name "") | 2057 (cond ((or (equal read-name "") |
1783 (and (equal read-name "+") (not allow-root-folder-flag))) | 2058 (and (equal read-name "+") (not allow-root-folder-flag))) |
1784 (setq read-name default)) | 2059 (setq read-name default)) |
1788 (error "No folder specified")) | 2063 (error "No folder specified")) |
1789 (setq folder-name read-name) | 2064 (setq folder-name read-name) |
1790 (cond ((and (> (length folder-name) 0) | 2065 (cond ((and (> (length folder-name) 0) |
1791 (eq (aref folder-name (1- (length folder-name))) ?/)) | 2066 (eq (aref folder-name (1- (length folder-name))) ?/)) |
1792 (setq folder-name (substring folder-name 0 -1)))) | 2067 (setq folder-name (substring folder-name 0 -1)))) |
2068 (let* ((last-slash (mh-search-from-end ?/ folder-name)) | |
2069 (parent (and last-slash (substring folder-name 0 last-slash))) | |
2070 (child (if last-slash | |
2071 (substring folder-name (1+ last-slash)) | |
2072 (substring folder-name 1)))) | |
2073 (unless (member child | |
2074 (mapcar #'car (gethash parent mh-sub-folders-cache))) | |
2075 (mh-remove-from-sub-folders-cache folder-name))) | |
1793 (let ((new-file-flag | 2076 (let ((new-file-flag |
1794 (not (file-exists-p (mh-expand-file-name folder-name))))) | 2077 (not (file-exists-p (mh-expand-file-name folder-name))))) |
1795 (cond ((and new-file-flag | 2078 (cond ((and new-file-flag |
1796 (y-or-n-p | 2079 (y-or-n-p |
1797 (format "Folder %s does not exist. Create it? " | 2080 (format "Folder %s does not exist. Create it? " |
1807 ((not (file-directory-p (mh-expand-file-name folder-name))) | 2090 ((not (file-directory-p (mh-expand-file-name folder-name))) |
1808 (error "\"%s\" is not a directory" | 2091 (error "\"%s\" is not a directory" |
1809 (mh-expand-file-name folder-name))))) | 2092 (mh-expand-file-name folder-name))))) |
1810 folder-name)) | 2093 folder-name)) |
1811 | 2094 |
2095 (defun mh-truncate-log-buffer () | |
2096 "If `mh-log-buffer' is too big then truncate it. | |
2097 If the number of lines in `mh-log-buffer' exceeds `mh-log-buffer-lines' then | |
2098 keep only the last `mh-log-buffer-lines'. As a side effect the point is set to | |
2099 the end of the log buffer. | |
2100 | |
2101 The function returns the size of the final size of the log buffer." | |
2102 (with-current-buffer (get-buffer-create mh-log-buffer) | |
2103 (goto-char (point-max)) | |
2104 (save-excursion | |
2105 (when (equal (forward-line (- mh-log-buffer-lines)) 0) | |
2106 (delete-region (point-min) (point)))) | |
2107 (unless (or (bobp) | |
2108 (save-excursion | |
2109 (and (equal (forward-line -1) 0) (equal (char-after) ?)))) | |
2110 (insert "\n\n")) | |
2111 (buffer-size))) | |
2112 | |
1812 ;;; Issue commands to MH. | 2113 ;;; Issue commands to MH. |
1813 | 2114 |
1814 (defun mh-exec-cmd (command &rest args) | 2115 (defun mh-exec-cmd (command &rest args) |
1815 "Execute mh-command COMMAND with ARGS. | 2116 "Execute mh-command COMMAND with ARGS. |
1816 The side effects are what is desired. | 2117 The side effects are what is desired. |
1817 Any output is assumed to be an error and is shown to the user. | 2118 Any output is assumed to be an error and is shown to the user. |
1818 The output is not read or parsed by MH-E." | 2119 The output is not read or parsed by MH-E." |
1819 (save-excursion | 2120 (save-excursion |
1820 (set-buffer (get-buffer-create mh-log-buffer)) | 2121 (set-buffer (get-buffer-create mh-log-buffer)) |
1821 (erase-buffer) | 2122 (let ((initial-size (mh-truncate-log-buffer))) |
1822 (apply 'call-process | 2123 (apply 'call-process |
1823 (expand-file-name command mh-progs) nil t nil | 2124 (expand-file-name command mh-progs) nil t nil |
1824 (mh-list-to-string args)) | 2125 (mh-list-to-string args)) |
1825 (if (> (buffer-size) 0) | 2126 (if (> (buffer-size) initial-size) |
1826 (save-window-excursion | 2127 (save-window-excursion |
1827 (switch-to-buffer-other-window mh-log-buffer) | 2128 (switch-to-buffer-other-window mh-log-buffer) |
1828 (sit-for 5))))) | 2129 (sit-for 5)))))) |
1829 | 2130 |
1830 (defun mh-exec-cmd-error (env command &rest args) | 2131 (defun mh-exec-cmd-error (env command &rest args) |
1831 "In environment ENV, execute mh-command COMMAND with ARGS. | 2132 "In environment ENV, execute mh-command COMMAND with ARGS. |
1832 ENV is nil or a string of space-separated \"var=value\" elements. | 2133 ENV is nil or a string of space-separated \"var=value\" elements. |
1833 Signals an error if process does not complete successfully." | 2134 Signals an error if process does not complete successfully." |
1834 (save-excursion | 2135 (save-excursion |
1835 (set-buffer (get-buffer-create mh-temp-buffer)) | 2136 (set-buffer (get-buffer-create mh-temp-buffer)) |
1836 (erase-buffer) | 2137 (erase-buffer) |
1837 (let ((status | 2138 (let ((process-environment process-environment)) |
1838 (if env | 2139 ;; XXX: We should purge the list that split-string returns of empty |
1839 ;; the shell hacks necessary here shows just how broken Unix is | 2140 ;; strings. This can happen in XEmacs if leading or trailing spaces |
1840 (apply 'call-process "/bin/sh" nil t nil "-c" | 2141 ;; are present. |
1841 (format "%s %s ${1+\"$@\"}" | 2142 (dolist (elem (if (stringp env) (split-string env " ") ())) |
1842 env | 2143 (push elem process-environment)) |
1843 (expand-file-name command mh-progs)) | 2144 (mh-handle-process-error |
1844 command | 2145 command (apply #'call-process (expand-file-name command mh-progs) |
1845 (mh-list-to-string args)) | 2146 nil t nil (mh-list-to-string args)))))) |
1846 (apply 'call-process | |
1847 (expand-file-name command mh-progs) nil t nil | |
1848 (mh-list-to-string args))))) | |
1849 (mh-handle-process-error command status)))) | |
1850 | 2147 |
1851 (defun mh-exec-cmd-daemon (command filter &rest args) | 2148 (defun mh-exec-cmd-daemon (command filter &rest args) |
1852 "Execute MH command COMMAND in the background. | 2149 "Execute MH command COMMAND in the background. |
1853 | 2150 |
1854 If FILTER is non-nil then it is used to process the output otherwise the | 2151 If FILTER is non-nil then it is used to process the output otherwise the |
1856 details of FILTER. | 2153 details of FILTER. |
1857 | 2154 |
1858 ARGS are passed to COMMAND as command line arguments." | 2155 ARGS are passed to COMMAND as command line arguments." |
1859 (save-excursion | 2156 (save-excursion |
1860 (set-buffer (get-buffer-create mh-log-buffer)) | 2157 (set-buffer (get-buffer-create mh-log-buffer)) |
1861 (erase-buffer)) | 2158 (mh-truncate-log-buffer)) |
1862 (let* ((process-connection-type nil) | 2159 (let* ((process-connection-type nil) |
1863 (process (apply 'start-process | 2160 (process (apply 'start-process |
1864 command nil | 2161 command nil |
1865 (expand-file-name command mh-progs) | 2162 (expand-file-name command mh-progs) |
1866 (mh-list-to-string args)))) | 2163 (mh-list-to-string args)))) |
1867 (set-process-filter process (or filter 'mh-process-daemon)))) | 2164 (set-process-filter process (or filter 'mh-process-daemon)))) |
2165 | |
2166 (defun mh-exec-cmd-env-daemon (env command filter &rest args) | |
2167 "In ennvironment ENV, execute mh-command COMMAND in the background. | |
2168 | |
2169 ENV is nil or a string of space-separated \"var=value\" elements. | |
2170 Signals an error if process does not complete successfully. | |
2171 | |
2172 If FILTER is non-nil then it is used to process the output otherwise the | |
2173 default filter `mh-process-daemon' is used. See `set-process-filter' for more | |
2174 details of FILTER. | |
2175 | |
2176 ARGS are passed to COMMAND as command line arguments." | |
2177 (let ((process-environment process-environment)) | |
2178 (dolist (elem (if (stringp env) (split-string env " ") ())) | |
2179 (push elem process-environment)) | |
2180 (apply #'mh-exec-cmd-daemon command filter args))) | |
1868 | 2181 |
1869 (defun mh-process-daemon (process output) | 2182 (defun mh-process-daemon (process output) |
1870 "PROCESS daemon that puts OUTPUT into a temporary buffer. | 2183 "PROCESS daemon that puts OUTPUT into a temporary buffer. |
1871 Any output from the process is displayed in an asynchronous pop-up window." | 2184 Any output from the process is displayed in an asynchronous pop-up window." |
1872 (set-buffer (get-buffer-create mh-log-buffer)) | 2185 (set-buffer (get-buffer-create mh-log-buffer)) |
1931 "Execute MH library command COMMAND with ARGS. | 2244 "Execute MH library command COMMAND with ARGS. |
1932 Put the output into buffer after point. Set mark after inserted text." | 2245 Put the output into buffer after point. Set mark after inserted text." |
1933 (apply 'mh-exec-cmd-output (expand-file-name command mh-lib-progs) nil args)) | 2246 (apply 'mh-exec-cmd-output (expand-file-name command mh-lib-progs) nil args)) |
1934 | 2247 |
1935 (defun mh-handle-process-error (command status) | 2248 (defun mh-handle-process-error (command status) |
1936 "Raise error if COMMAND returned non-zero STATUS, otherwise return STATUS. | 2249 "Raise error if COMMAND returned non-zero STATUS, otherwise return STATUS." |
1937 STATUS is return value from `call-process'. | 2250 (if (equal status 0) |
1938 Program output is in current buffer. | 2251 status |
1939 If output is too long to include in error message, display the buffer." | 2252 (goto-char (point-min)) |
1940 (cond ((eq status 0) ;success | 2253 (insert (if (integerp status) |
1941 status) | 2254 (format "%s: exit code %d\n" command status) |
1942 ((stringp status) ;kill string | 2255 (format "%s: %s\n" command status))) |
1943 (error "%s: %s" command status)) | 2256 (save-excursion |
1944 (t ;exit code | 2257 (let ((error-message (buffer-substring (point-min) (point-max)))) |
1945 (cond | 2258 (set-buffer (get-buffer-create mh-log-buffer)) |
1946 ((= (buffer-size) 0) ;program produced no error message | 2259 (mh-truncate-log-buffer) |
1947 (error "%s: exit code %d" command status)) | 2260 (insert error-message))) |
1948 (t | 2261 (error "%s failed, check %s buffer for error message" |
1949 ;; will error message fit on one line? | 2262 command mh-log-buffer))) |
1950 (goto-line 2) | |
1951 (if (and (< (buffer-size) (frame-width)) | |
1952 (eobp)) | |
1953 (error "%s" | |
1954 (buffer-substring 1 (progn (goto-char 1) | |
1955 (end-of-line) | |
1956 (point)))) | |
1957 (display-buffer (current-buffer)) | |
1958 (error "%s failed with status %d. See error message in other window" | |
1959 command status))))))) | |
1960 | 2263 |
1961 (defun mh-list-to-string (l) | 2264 (defun mh-list-to-string (l) |
1962 "Flatten the list L and make every element of the new list into a string." | 2265 "Flatten the list L and make every element of the new list into a string." |
1963 (nreverse (mh-list-to-string-1 l))) | 2266 (nreverse (mh-list-to-string-1 l))) |
1964 | 2267 |