summaryrefslogtreecommitdiff
path: root/lisp/mail/mh-e.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/mail/mh-e.el')
-rw-r--r--lisp/mail/mh-e.el1170
1 files changed, 851 insertions, 319 deletions
diff --git a/lisp/mail/mh-e.el b/lisp/mail/mh-e.el
index 386a9acf996..e309a37b5ee 100644
--- a/lisp/mail/mh-e.el
+++ b/lisp/mail/mh-e.el
@@ -1,11 +1,11 @@
;;; mh-e.el --- GNU Emacs interface to the MH mail system
-;; Copyright (C) 1985,86,87,88,90,92,93,94,95,97,2000 Free Software Foundation, Inc.
+;; Copyright (C) 1985,86,87,88,90,92,93,94,95,97,2000,2001,2002 Free Software Foundation, Inc.
+;; Author: Bill Wohler <wohler@newt.com>
;; Maintainer: Bill Wohler <wohler@newt.com>
-;; Version: 5.0.2
+;; Version: 6.1.1
;; Keywords: mail
-;; Bug-reports: include `M-x mh-version' output in any correspondence
;; This file is part of GNU Emacs.
@@ -26,60 +26,89 @@
;;; Commentary:
-;; HOW TO USE:
-;; M-x mh-rmail to read mail. Type C-h m there for a list of commands.
-;; C-u M-x mh-rmail to visit any folder.
-;; M-x mh-smail to send mail. From within the mail reader, "m" works, too.
+;; How to Use:
+;; M-x mh-rmail to read mail. Type C-h m there for a list of commands.
+;; C-u M-x mh-rmail to visit any folder.
+;; M-x mh-smail to send mail. From within the mail reader, "m" works, too.
-;; MH (Message Handler) is a powerful mail reader. The MH newsgroup
-;; is comp.mail.mh; the mailing list is mh-users@ics.uci.edu (send to
-;; mh-users-request to be added). See the monthly Frequently Asked
-;; Questions posting there for information on getting MH and mh-e.
+;; Your .emacs might benefit from these bindings:
+;; (global-set-key "\C-cr" 'mh-rmail)
+;; (global-set-key "\C-xm" 'mh-smail)
+;; (global-set-key "\C-x4m" 'mh-smail-other-window)
-;; mh-e is an Emacs interface to the MH mail system.
-;; There is a mailing list for discussion of mh-e and
-;; announcements of new versions. Send a "subscribe" message to
-;; mh-e-request@gnu.org to be added. Do not report bugs here; mail
-;; them directly to the maintainer (see top of mh-e.el source).
-;; Include the output of M-x mh-version in any bug report.
+;; MH (Message Handler) is a powerful mail reader.
-;; mh-e works with GNU Emacs 18 or 19, and MH 6.
+;; The MH newsgroup is comp.mail.mh; the mailing list is mh-users@ics.uci.edu
+;; (send to mh-users-request to be added). See the monthly Frequently Asked
+;; Questions posting there for information on getting MH and mh-e:
+;; http://www.faqs.org/faqs/mail/mh-faq/part1/preamble.html
-;; NB. MH must have been compiled with the MHE compiler flag or several
+;; N.B. MH must have been compiled with the MHE compiler flag or several
;; features necessary for mh-e will be missing from MH commands, specifically
;; the -build switch to repl and forw.
-;; Your .emacs might benefit from these bindings:
-;; (global-set-key "\C-cr" 'mh-rmail)
-;; (global-set-key "\C-xm" 'mh-smail)
-;; (global-set-key "\C-x4m" 'mh-smail-other-window)
+;; mh-e is an Emacs interface to the MH mail system.
+
+;; mh-e is supported in GNU Emacs 20 and 21, with MH 6.8.4 and nmh 1.0.4.
+
+;; Mailing Lists:
+;; mh-e-users@lists.sourceforge.net
+;; mh-e-announce@lists.sourceforge.net
+;; mh-e-devel@lists.sourceforge.net
+;;
+;; Subscribe by sending a "subscribe" message to
+;; <list>-request@lists.sourceforge.net, or by using the web interface at
+;; https://sourceforge.net/mail/?group_id=13357
+
+;; Bug Reports:
+;; https://sourceforge.net/tracker/?group_id=13357&atid=113357
+;; Include the output of M-x mh-version in any bug report.
+
+;; Feature Requests:
+;; https://sourceforge.net/tracker/?atid=363357&group_id=13357&func=browse
+
+;; Support:
+;; https://sourceforge.net/tracker/?group_id=13357&atid=213357
;;; Change Log:
;; Original version for Gosling emacs by Brian Reid, Stanford, 1982.
;; Modified by James Larus, BBN, July 1984 and UCB, 1984 & 1985.
;; Rewritten for GNU Emacs, James Larus 1985. larus@ginger.berkeley.edu
-;; Modified by Stephen Gildea 1988. gildea@stop.mail-abuse.org
-(defconst mh-e-RCS-id "$Id: mh-e.el,v 1.30 2001/09/23 17:38:22 eliz Exp $")
+;; Modified by Stephen Gildea 1988. gildea@lcs.mit.edu
+;; Maintenance picked up by Bill Wohler <wohler@newt.com> and the
+;; SourceForge Crew <http://mh-e.sourceforge.net/>. 2001.
+
+;; $Id: mh-e.el,v 1.99.1.1 2002/10/01 19:41:43 wohler Exp $
;;; Code:
(provide 'mh-e)
(require 'mh-utils)
+(require 'gnus-util)
+(require 'easymenu)
+(if (save-match-data (string-match "XEmacs\\|Lucid" emacs-version))
+ (require 'mh-xemacs-compat))
+(eval-when-compile (require 'cl))
+
+(defconst mh-version "6.1.1" "Version number of mh-e.")
+
+;;; Initial Autoloads
+
+(autoload 'Info-goto-node "info")
;;; Hooks:
(defgroup mh nil
- "Emacs interface to the MH mail system"
+ "Emacs interface to the MH mail system."
:group 'mail)
(defgroup mh-hook nil
- "Hooks to mh-e mode"
+ "Hooks to mh-e mode."
:prefix "mh-"
:group 'mh)
-
(defcustom mh-folder-mode-hook nil
"Invoked in MH-Folder mode on a new folder."
:type 'hook
@@ -90,6 +119,14 @@
:type 'hook
:group 'mh-hook)
+(defcustom mh-folder-updated-hook nil
+ "Invoked when the folder actions (such as moves and deletes) are performed.
+Variables that are useful in this hook include `mh-delete-list' and
+`mh-refile-list' which can be used to see which changes are being made to
+current folder, `mh-current-folder'."
+ :type 'hook
+ :group 'mh-hook)
+
(defcustom mh-show-hook nil
"Invoked after \\<mh-folder-mode-map>`\\[mh-show]' shows a message."
:type 'hook
@@ -111,16 +148,23 @@
:group 'mh-hook)
(defcustom mh-before-quit-hook nil
- "Invoked by \\<mh-folder-mode-map>`\\[mh-quit]' before quitting mh-e. See also mh-quit-hook."
+ "Invoked by \\<mh-folder-mode-map>`\\[mh-quit]' before quitting mh-e.
+See also `mh-quit-hook'."
:type 'hook
:group 'mh-hook)
(defcustom mh-quit-hook nil
- "Invoked after \\<mh-folder-mode-map>`\\[mh-quit]' quits mh-e. See also mh-before-quit-hook."
+ "Invoked after \\<mh-folder-mode-map>`\\[mh-quit]' quits mh-e.
+See also `mh-before-quit-hook'."
:type 'hook
:group 'mh-hook)
-
+(defcustom mh-unseen-updated-hook nil
+ "Invoked after the unseen sequence has been updated.
+The variable `mh-seen-list' can be used to obtain the list of messages which
+will be removed from the unseen sequence."
+ :type 'hook
+ :group 'mh-hook)
;;; Personal preferences:
@@ -135,8 +179,7 @@ message text is piped to this command when you type \\<mh-folder-mode-map>`\\[mh
(defcustom mh-scan-prog "scan"
"*Program to run to generate one-line-per-message listing of a folder.
Normally \"scan\" or a file name linked to scan. This file is searched
-for relative to the mh-progs directory unless it is an absolute pathname.
-Automatically becomes buffer-local when set in any fashion."
+for relative to the mh-progs directory unless it is an absolute pathname."
:type 'string
:group 'mh)
(make-variable-buffer-local 'mh-scan-prog)
@@ -161,8 +204,11 @@ otherwise, your output may be truncated."
:group 'mh)
(defcustom mh-do-not-confirm nil
- "*Non-nil means do not prompt for confirmation before some mh-e commands.
-Affects non-recoverable commands such as `mh-kill-folder' and `mh-undo-folder'."
+ "*Non-nil means do not prompt for confirmation.
+Commands such as `mh-pack-folder' prompt to confirm whether to process
+outstanding moves and deletes or not before continuing. A non-nil setting will
+perform the action--which is usually desired but cannot be retracted--without
+question."
:type 'boolean
:group 'mh)
@@ -173,25 +219,6 @@ A directory name string, or nil to use current directory."
directory)
:group 'mh)
-;;; Parameterize mh-e to work with different scan formats. The defaults work
-;;; with the standard MH scan listings, in which the first 4 characters on
-;;; the line are the message number, followed by two places for notations.
-
-(defvar mh-good-msg-regexp "^....[^D^]"
- "Regexp specifying the scan lines that are 'good' messages.")
-
-(defvar mh-deleted-msg-regexp "^....D"
- "Regexp matching scan lines of deleted messages.")
-
-(defvar mh-refiled-msg-regexp "^....\\^"
- "Regexp matching scan lines of refiled messages.")
-
-(defvar mh-valid-scan-line "^ *[0-9]"
- "Regexp matching scan lines for messages (not error messages).")
-
-(defvar mh-cur-scan-msg-regexp "^....\\+"
- "Regexp matching scan line for the cur message.")
-
(defvar mh-note-deleted "D"
"String whose first character is used to notate deleted messages.")
@@ -203,8 +230,358 @@ A directory name string, or nil to use current directory."
(defvar mh-partial-folder-mode-line-annotation "select"
"Annotation when displaying part of a folder.
-The string is displayed after the folder's name. nil for no annotation.")
+The string is displayed after the folder's name. NIL for no annotation.")
+
+;;; Parameterize mh-e to work with different scan formats. The defaults work
+;;; with the standard MH scan listings, in which the first 4 characters on
+;;; the line are the message number, followed by two places for notations.
+
+(defcustom mh-scan-format-file t
+ "Specifies the format file to pass to the scan program.
+If t, the format string will be taken from the either `mh-scan-format-mh'
+or `mh-scan-format-nmh' depending on whether MH or nmh is in use.
+If nil, the default scan output will be used.
+
+If you customize the scan format, you may need to modify a few variables
+containing regexps that mh-e uses to identify specific portions of the output.
+Use `M-x apropos RET mh-scan.*regexp' to obtain a list of these variables."
+ :type '(choice (const :tag "Use mh-e scan format" t)
+ (const :tag "Use default scan format" nil)
+ (file :tag "Specify a scan format file"))
+ :group 'mh)
+
+;; The following scan formats are passed to the scan program if the
+;; setting of `mh-scan-format-file' above is nil. They are identical
+;; except the later one makes use of the nmh `decode' function to
+;; decode RFC 2047 encodings.
+
+(defvar mh-scan-format-mh
+ (concat
+ "%4(msg)"
+ "%<(cur)+%| %>"
+ "%<{replied}-"
+ "%?(nonnull(comp{to}))%<(mymbox{to})t%>"
+ "%?(nonnull(comp{cc}))%<(mymbox{cc})c%>"
+ "%?(nonnull(comp{bcc}))%<(mymbox{bcc})b%>"
+ "%?(nonnull(comp{newsgroups}))n%>"
+ "%<(zero) %>"
+ "%02(mon{date})/%02(mday{date})%<{date} %|*%>"
+ "%<(mymbox{from})%<{to}To:%14(friendly{to})%>%>"
+ "%<(zero)%17(friendly{from})%> "
+ "%{subject}%<{body}<<%{body}%>")
+ "*Scan format string for MH, provided to the scan program via the -format arg.
+This format is identical to the default except that additional hints for
+fontification have been added to the sixth column.
+
+The values of the sixth column, in priority order, are: `-' if the
+message has been replied to, t if an address on the To: line matches
+one of the mailboxes of the current user, `c' if the Cc: line matches,
+`b' if the Bcc: line matches, and `n' if a non-empty Newsgroups: header
+is present.")
+
+(defvar mh-scan-format-nmh
+ (concat
+ "%4(msg)"
+ "%<(cur)+%| %>"
+ "%<{replied}-"
+ "%?(nonnull(comp{to}))%<(mymbox{to})t%>"
+ "%?(nonnull(comp{cc}))%<(mymbox{cc})c%>"
+ "%?(nonnull(comp{bcc}))%<(mymbox{bcc})b%>"
+ "%?(nonnull(comp{newsgroups}))n%>"
+ "%<(zero) %>"
+ "%02(mon{date})/%02(mday{date})%<{date} %|*%>"
+ "%<(mymbox{from})%<{to}To:%14(decode(friendly{to}))%>%>"
+ "%<(zero)%17(decode(friendly{from}))%> "
+ "%(decode{subject})%<{body}<<%{body}%>")
+ "*Scan format string for nmh, provided to the scan program via the -format arg.
+This format is identical to the default except that additional hints for
+fontification have been added to the sixth column.
+
+The values of the sixth column, in priority order, are: `-' if the
+message has been replied to, t if an address on the To: line matches
+one of the mailboxes of the current user, `c' if the Cc: line matches,
+`b' if the Bcc: line matches, and `n' if a non-empty Newsgroups: header
+is present.")
+
+(defvar mh-scan-good-msg-regexp "^\\(....\\)[^D^]"
+ "Regexp specifying the scan lines that are 'good' messages.
+The default `mh-folder-font-lock-keywords' expects this expression to contain
+at least one parenthesized expression which matches the message number.")
+
+(defvar mh-scan-deleted-msg-regexp "^\\(....\\)D"
+ "Regexp matching scan lines of deleted messages.
+The default `mh-folder-font-lock-keywords' expects this expression to contain
+at least one parenthesized expression which matches the message number.")
+
+(defvar mh-scan-refiled-msg-regexp "^\\(....\\)\\^"
+ "Regexp matching scan lines of refiled messages.
+The default `mh-folder-font-lock-keywords' expects this expression to contain
+at least one parenthesized expression which matches the message number.")
+
+(defvar mh-scan-valid-regexp "^ *[0-9]"
+ "Regexp matching scan lines for messages (not error messages).")
+
+(defvar mh-scan-cur-msg-number-regexp "^\\(....\\+\\).*"
+ "Regexp matching scan line for the current message.
+The default `mh-folder-font-lock-keywords' expects this expression to contain
+at least one parenthesized expression which matches the message number.
+Don't disable this regexp as it's needed by non fontifying functions.")
+
+(defvar mh-scan-cur-msg-regexp "^\\(....\\+DISABLED.*\\)"
+ "Regexp matching scan line for the current message.
+The default `mh-folder-font-lock-keywords' expects this expression to contain
+at least one parenthesized expression which matches the whole line.
+To enable this feature, remove the string DISABLED from the regexp.")
+
+(defvar mh-scan-date-regexp "\\([0-9][0-9]/[0-9][0-9]\\)"
+ "Regexp matching a valid date in scan lines.
+The default `mh-folder-font-lock-keywords' expects this expression to contain
+only one parenthesized expression which matches the date field
+\(see `mh-scan-format-regexp').")
+
+(defvar mh-scan-rcpt-regexp "\\(To:\\)\\(..............\\)"
+ "Regexp specifying the recipient in scan lines for messages we sent.
+The default `mh-folder-font-lock-keywords' expects this expression to contain
+two parenthesized expressions. The first is expected to match the To:
+that the default scan format file generates. The second is expected to match
+the recipient's name.")
+
+(defvar mh-scan-body-regexp "\\(<<\\([^\n]+\\)?\\)"
+ "Regexp matching the message body beginning displayed in scan lines.
+The default `mh-folder-font-lock-keywords' expects this expression to contain
+at least one parenthesized expression which matches the body text.")
+
+(defvar mh-scan-subject-regexp
+ "^...............................\\([Rr][Ee]:\\s-*\\)*\\([^<\n]*\\)"
+ "*Regexp matching the subject string in MH folder mode.
+The default `mh-folder-font-lock-keywords' expects this expression to contain
+at least two parenthesized expressions. The first is expected to match the Re:
+string, if any. The second is expected to match the subject line itself.")
+
+(defvar mh-scan-format-regexp
+ (concat "\\([bct]\\)" mh-scan-date-regexp " \\(..................\\)")
+ "Regexp matching the output of scan using `mh-scan-format-mh' or `mh-scan-format-nmh'.
+The default `mh-folder-font-lock-keywords' expects this expression to contain
+at least three parenthesized expressions. The first should match the
+fontification hint, the second is found in `mh-scan-date-regexp', and the
+third should match the user name.")
+
+(defvar mh-folder-followup-face 'mh-folder-followup-face
+ "Face for highlighting Re: (followup) subject text in MH-Folder buffers.")
+(defface mh-folder-followup-face
+ '((((class color) (background light))
+ (:foreground "blue3"))
+ (((class color) (background dark))
+ (:foreground "LightGoldenRod"))
+ (t
+ (:bold t)))
+ "Face for highlighting Re: (followup) subject text in MH-Folder buffers."
+ :group 'mh)
+(defvar mh-folder-address-face 'mh-folder-address-face
+ "Face for highlighting the address in MH-Folder buffers.")
+(copy-face 'mh-folder-subject-face 'mh-folder-address-face)
+(defvar mh-folder-scan-format-face 'mh-folder-scan-format-face
+ "Face for highlighting `mh-scan-format-regexp' matches in MH-Folder buffers.")
+(copy-face 'mh-folder-followup-face 'mh-folder-scan-format-face)
+
+(defvar mh-folder-date-face 'mh-folder-date-face
+ "Face for highlighting the date in MH-Folder buffers.")
+(defface mh-folder-date-face
+ '((((class color) (background light))
+ (:foreground "snow4"))
+ (((class color) (background dark))
+ (:foreground "snow3"))
+ (t
+ (:bold t)))
+ "Face for highlighting the date in MH-Folder buffers."
+ :group 'mh)
+
+(defvar mh-folder-msg-number-face 'mh-folder-msg-number-face
+ "Face for highlighting the message number in MH-Folder buffers.")
+(defface mh-folder-msg-number-face
+ '((((class color) (background light))
+ (:foreground "snow4"))
+ (((class color) (background dark))
+ (:foreground "snow3"))
+ (t
+ (:bold t)))
+ "Face for highlighting the message number in MH-Folder buffers."
+ :group 'mh)
+(defvar mh-folder-deleted-face 'mh-folder-deleted-face
+ "Face for highlighting deleted messages in MH-Folder buffers.")
+(copy-face 'mh-folder-msg-number-face 'mh-folder-deleted-face)
+
+(defvar mh-folder-cur-msg-face 'mh-folder-cur-msg-face
+ "Face for the current message line in MH-Folder buffers.")
+(defface mh-folder-cur-msg-face
+ '((((type tty pc) (class color))
+ (:background "LightGreen"))
+ (((class color) (background light))
+ (:background "LightGreen") ;Use this for solid background colour
+;;; (:underline t) ;Use this for underlining
+ )
+ (((class color) (background dark))
+ (:background "DarkOliveGreen4"))
+ (t (:underline t)))
+ "Face for the current message line in MH-Folder buffers."
+ :group 'mh)
+
+;;mh-folder-subject-face is defined in mh-utils since it's needed there
+;;for mh-show-subject-face.
+
+(eval-after-load "font-lock"
+ '(progn
+ (defvar mh-folder-refiled-face 'mh-folder-refiled-face
+ "Face for highlighting refiled messages in MH-Folder buffers.")
+ (copy-face 'font-lock-variable-name-face 'mh-folder-refiled-face)
+ (defvar mh-folder-cur-msg-number-face 'mh-folder-cur-msg-number-face
+ "Face for highlighting the current message in MH-Folder buffers.")
+ (copy-face 'font-lock-keyword-face 'mh-folder-cur-msg-number-face)
+ (defvar mh-folder-to-face 'mh-folder-to-face
+ "Face for highlighting the To: string in MH-Folder buffers.")
+ (copy-face 'font-lock-string-face 'mh-folder-to-face)
+ (defvar mh-folder-body-face 'mh-folder-body-face
+ "Face for highlighting body text in MH-Folder buffers.")
+ (copy-face 'font-lock-string-face 'mh-folder-body-face)
+
+ (defvar mh-folder-font-lock-keywords
+ (list
+ ;; Marked for deletion
+ (list (concat mh-scan-deleted-msg-regexp ".*")
+ '(0 mh-folder-deleted-face))
+ ;; Marked for refile
+ (list (concat mh-scan-refiled-msg-regexp ".*")
+ '(0 mh-folder-refiled-face))
+ ;;after subj
+ (list mh-scan-body-regexp '(1 mh-folder-body-face nil t))
+ '(mh-folder-font-lock-subject
+ (1 mh-folder-followup-face append t)
+ (2 mh-folder-subject-face append t))
+ ;;current msg
+ (list mh-scan-cur-msg-number-regexp
+ '(1 mh-folder-cur-msg-number-face))
+ (list mh-scan-good-msg-regexp
+ '(1 mh-folder-msg-number-face)) ;; Msg number
+ (list mh-scan-date-regexp '(1 mh-folder-date-face)) ;; Date
+ (list mh-scan-rcpt-regexp
+ '(1 mh-folder-to-face) ;; To:
+ '(2 mh-folder-address-face)) ;; address
+ ;; scan font-lock name
+ (list mh-scan-format-regexp
+ '(1 mh-folder-date-face)
+ '(3 mh-folder-scan-format-face))
+ ;; Current message line
+ (list mh-scan-cur-msg-regexp
+ '(1 mh-folder-cur-msg-face prepend t))
+ ;; Unseen messages in bold
+ '(mh-folder-font-lock-unseen (1 'bold append t))
+ )
+ "Regexp keywords used to fontify the MH-Folder buffer.")
+ ))
+
+(defun mh-folder-font-lock-subject (limit)
+ "Return mh-e scan subject strings to font-lock between point and LIMIT."
+ (if (not (re-search-forward mh-scan-subject-regexp limit t))
+ nil
+ (if (match-beginning 1)
+ (set-match-data (list (match-beginning 1) (match-end 2)
+ (match-beginning 1) (match-end 2) nil nil))
+ (set-match-data (list (match-beginning 2) (match-end 2)
+ nil nil (match-beginning 2) (match-end 2))))
+ t))
+
+;; Fontifify unseen mesages in bold. - Peter S Galbraith <psg@debian.org>
+(defvar mh-folder-unseen-seq-name nil
+ "Name of unseen sequence.
+The default for this is provided by the function `mh-folder-unseen-seq-name'
+On nmh systems.")
+
+(defun mh-folder-unseen-seq-name ()
+ "Provide name of unseen sequence from mhparam."
+ (or mh-progs (mh-find-path))
+ (save-excursion
+ (let ((tmp-buffer (get-buffer-create mh-temp-buffer))
+ (unseen-seq-name "unseen"))
+ (set-buffer tmp-buffer)
+ (unwind-protect
+ (progn
+ (call-process (expand-file-name "mhparam" mh-progs)
+ nil '(t t) nil "-component" "Unseen-Sequence")
+ (goto-char (point-min))
+ (if (re-search-forward "Unseen-Sequence: \\(.*\\)$" nil t)
+ (setq unseen-seq-name (match-string 1))))
+ (kill-buffer tmp-buffer))
+ unseen-seq-name)))
+
+(defun mh-folder-unseen-seq-list ()
+ "Return a list of unseen message numbers for current folder."
+ (if (not mh-folder-unseen-seq-name)
+ (setq mh-folder-unseen-seq-name (mh-folder-unseen-seq-name)))
+ (cond
+ ((not mh-folder-unseen-seq-name)
+ nil)
+ (t
+ (let ((folder mh-current-folder))
+ (save-excursion
+ (let ((tmp-buffer (get-buffer-create mh-temp-buffer)))
+ (set-buffer tmp-buffer)
+ (unwind-protect
+ (progn
+ (call-process (expand-file-name "mark" mh-progs)
+ nil '(t t) nil
+ folder "-seq" mh-folder-unseen-seq-name
+ "-list")
+ (goto-char (point-min))
+ (sort (mh-read-msg-list) '<))
+ (kill-buffer tmp-buffer))))))))
+
+(defvar mh-folder-unseen-seq-cache nil
+ "Internal cache variable used for font-lock in mh-e.
+Should only be non-nil through font-lock stepping, and nil once font-lock
+is done highlighting.")
+(make-variable-buffer-local 'mh-folder-unseen-seq-cache)
+
+(defun mh-folder-font-lock-unseen (limit)
+ "Return unseen message lines to font-lock between point and LIMIT."
+ (if (not mh-folder-unseen-seq-cache)
+ (setq mh-folder-unseen-seq-cache (mh-folder-unseen-seq-list)))
+ (let ((cur-msg (mh-get-msg-num nil)))
+ (cond
+ ((not mh-folder-unseen-seq-cache)
+ nil)
+ ((not cur-msg) ;Presumably at end of buffer
+ (setq mh-folder-unseen-seq-cache nil)
+ nil)
+ ((member cur-msg mh-folder-unseen-seq-cache)
+ (let ((bpoint (progn (beginning-of-line)(point)))
+ (epoint (progn (forward-line 1)(point))))
+ (if (<= limit (point))
+ (setq mh-folder-unseen-seq-cache nil))
+ (set-match-data (list bpoint epoint bpoint epoint))
+ t))
+ (t
+ ;; move forward one line at a time, checking each message number.
+ (while (and
+ (= 0 (forward-line 1))
+ (> limit (point))
+ (not (member (mh-get-msg-num nil) mh-folder-unseen-seq-cache))))
+ ;; Examine how we must have exited the loop...
+ (let ((cur-msg (mh-get-msg-num nil)))
+ (cond
+ ((or (not cur-msg)
+ (<= limit (point))
+ (not (member cur-msg mh-folder-unseen-seq-cache)))
+ (setq mh-folder-unseen-seq-cache nil)
+ nil)
+ ((member cur-msg mh-folder-unseen-seq-cache)
+ (let ((bpoint (progn (beginning-of-line)(point)))
+ (epoint (progn (forward-line 1)(point))))
+ (if (<= limit (point))
+ (setq mh-folder-unseen-seq-cache nil))
+ (set-match-data (list bpoint epoint bpoint epoint))
+ t))))))))
+;; fontifify unseen mesages in bold. - end
;;; Internal variables:
@@ -225,7 +602,7 @@ The string is displayed after the folder's name. nil for no annotation.")
(defvar mh-last-msg-num nil) ;Number of last msg in buffer.
-(defvar mh-mode-line-annotation nil) ;Indiction this is not the full folder.
+(defvar mh-mode-line-annotation nil) ;Message range displayed in buffer.
;;; Macros and generic functions:
@@ -234,44 +611,40 @@ The string is displayed after the folder's name. nil for no annotation.")
(funcall func (car list))
(setq list (cdr list))))
+(defun mh-scan-format ()
+ "Generate arguments to the scan program to specify which format string should be used."
+ (if (equal mh-scan-format-file t)
+ (list "-format" (if mh-nmh-p
+ (list mh-scan-format-nmh)
+ (list mh-scan-format-mh)))
+ (if (not (equal mh-scan-format-file nil))
+ (list "-form" mh-scan-format-file))))
+
;;; Entry points:
;;;###autoload
(defun mh-rmail (&optional arg)
- "Inc(orporate) new mail with MH, or, with arg, scan an MH mail folder.
-This function is an entry point to mh-e, the Emacs front end
-to the MH mail system."
+ "Inc(orporate) new mail with MH.
+Scan an MH folder if ARG is non-nil. This function is an entry point to mh-e,
+the Emacs front end to the MH mail system."
(interactive "P")
(mh-find-path)
(if arg
(call-interactively 'mh-visit-folder)
(mh-inc-folder)))
-
-;;; mh-smail and mh-smail-other-window have been moved to the new file
-;;; mh-comp.el, but Emacs 18 still looks for them here, so provide a
-;;; definition here, too, for a while.
-
-(defun mh-smail ()
- "Compose and send mail with the MH mail system.
-This function is an entry point to mh-e, the Emacs front end
-to the MH mail system."
- (interactive)
- (mh-find-path)
- (require 'mh-comp)
- (call-interactively 'mh-send))
-
-
-(defun mh-smail-other-window ()
- "Compose and send mail in other window with the MH mail system.
-This function is an entry point to mh-e, the Emacs front end
-to the MH mail system."
- (interactive)
- (mh-find-path)
- (require 'mh-comp)
- (call-interactively 'mh-send-other-window))
+;;;###autoload
+(defun mh-nmail (&optional arg)
+ "Check for new mail in inbox folder.
+Scan an MH folder if ARG is non-nil. This function is an entry point to mh-e,
+the Emacs front end to the MH mail system."
+ (interactive "P")
+ (mh-find-path) ; init mh-inbox
+ (if arg
+ (call-interactively 'mh-visit-folder)
+ (mh-visit-folder mh-inbox)))
@@ -279,18 +652,26 @@ to the MH mail system."
(defun mh-delete-msg (msg-or-seq)
- "Mark the specified MESSAGE(s) for subsequent deletion and move to the next.
-Default is the displayed message. If optional prefix argument is
-given then prompt for the message sequence."
- (interactive (list (if current-prefix-arg
- (mh-read-seq-default "Delete" t)
- (mh-get-msg-num t))))
+ "Mark the specified MSG-OR-SEQ for subsequent deletion and move to the next.
+
+Default is the displayed message. If optional prefix argument is given then
+prompt for the message sequence. If variable `transient-mark-mode' is non-nil
+and the mark is active, then the selected region is marked for deletion."
+ (interactive (list (cond
+ ((and (boundp 'transient-mark-mode)
+ transient-mark-mode mark-active)
+ (mh-region-to-sequence (region-beginning)(region-end))
+ 'region)
+ (current-prefix-arg
+ (mh-read-seq-default "Delete" t))
+ (t
+ (mh-get-msg-num t)))))
(mh-delete-msg-no-motion msg-or-seq)
(mh-next-msg))
(defun mh-delete-msg-no-motion (msg-or-seq)
- "Mark the specified MESSAGE(s) for subsequent deletion.
+ "Mark the specified MSG-OR-SEQ for subsequent deletion.
Default is the displayed message. If optional prefix argument is
provided, then prompt for the message sequence."
(interactive (list (if current-prefix-arg
@@ -316,7 +697,7 @@ provided, then prompt for the message sequence."
"Move to the first message."
(interactive)
(goto-char (point-min))
- (while (and (not (eobp)) (not (looking-at mh-valid-scan-line)))
+ (while (and (not (eobp)) (not (looking-at mh-scan-valid-regexp)))
(forward-line 1)))
@@ -341,8 +722,8 @@ Type \"\\[mh-show]\" to show the message normally again."
(defun mh-inc-folder (&optional maildrop-name)
"Inc(orporate)s new mail into the Inbox folder.
-Optional prefix argument specifies an alternate maildrop from the default.
-If the prefix argument is given, incorporates mail into the current
+Optional argument MAILDROP-NAME specifies an alternate maildrop from the
+default. If the prefix argument is given, incorporates mail into the current
folder, otherwise uses the folder named by `mh-inbox'.
Runs `mh-inc-folder-hook' after incorporating new mail.
Do not call this function from outside mh-e; use \\[mh-rmail] instead."
@@ -359,6 +740,7 @@ Do not call this function from outside mh-e; use \\[mh-rmail] instead."
(switch-to-buffer mh-inbox)
(setq mh-previous-window-config config)))))
(mh-get-new-mail maildrop-name)
+ (if mh-showing-mode (mh-show))
(run-hooks 'mh-inc-folder-hook))
@@ -371,11 +753,11 @@ Do not call this function from outside mh-e; use \\[mh-rmail] instead."
(defun mh-next-undeleted-msg (&optional arg)
- "Move to the NTH next undeleted message in window."
+ "Move to the next undeleted message ARG in window."
(interactive "p")
(setq mh-next-direction 'forward)
(forward-line 1)
- (cond ((re-search-forward mh-good-msg-regexp nil 0 arg)
+ (cond ((re-search-forward mh-scan-good-msg-regexp nil 0 arg)
(beginning-of-line)
(mh-maybe-show))
(t
@@ -385,12 +767,19 @@ Do not call this function from outside mh-e; use \\[mh-rmail] instead."
(defun mh-refile-msg (msg-or-seq folder)
- "Refile MESSAGE(s) (default: displayed message) into FOLDER.
-If optional prefix argument provided, then prompt for message sequence."
+ "Refile MSG-OR-SEQ (default: displayed message) into FOLDER.
+If optional prefix argument provided, then prompt for message sequence.
+If variable `transient-mark-mode' is non-nil and the mark is active, then the
+selected region is marked for refiling."
(interactive
- (list (if current-prefix-arg
- (mh-read-seq-default "Refile" t)
- (mh-get-msg-num t))
+ (list (cond
+ ((and (boundp 'transient-mark-mode) transient-mark-mode mark-active)
+ (mh-region-to-sequence (region-beginning)(region-end))
+ 'region)
+ (current-prefix-arg
+ (mh-read-seq-default "Refile" t))
+ (t
+ (mh-get-msg-num t)))
(intern
(mh-prompt-for-folder
"Destination"
@@ -430,24 +819,48 @@ previous refile or write command."
(defun mh-quit ()
"Quit the current mh-e folder.
-Start by running mh-before-quit-hook. Restore the previous window
-configuration, if one exists. Finish by running mh-quit-hook."
+Start by running `mh-before-quit-hook'. Restore the previous window
+configuration, if one exists. Finish by running `mh-quit-hook'."
(interactive)
- (run-hooks 'mh-before-quit-hook)
+ (run-hooks 'mh-before-quit-hook)
(mh-update-sequences)
(mh-invalidate-show-buffer)
(bury-buffer (current-buffer))
(if (get-buffer mh-show-buffer)
(bury-buffer mh-show-buffer))
+ (if (get-buffer mh-temp-buffer)
+ (kill-buffer mh-temp-buffer))
+ (if (get-buffer mh-temp-folders-buffer)
+ (kill-buffer mh-temp-folders-buffer))
+ (if (get-buffer mh-temp-sequences-buffer)
+ (kill-buffer mh-temp-sequences-buffer))
(if mh-previous-window-config
(set-window-configuration mh-previous-window-config))
(run-hooks 'mh-quit-hook))
(defun mh-page-msg (&optional arg)
"Page the displayed message forwards.
-Scrolls ARG lines or a full screen if no argument is supplied."
+Scrolls ARG lines or a full screen if no argument is supplied. Show buffer
+first if not displayed. Show the next undeleted message if looking at the
+bottom of the current message."
(interactive "P")
- (scroll-other-window arg))
+ (if mh-showing-mode
+ (if mh-page-to-next-msg-p
+ (if (equal mh-next-direction 'backward)
+ (mh-previous-undeleted-msg)
+ (mh-next-undeleted-msg))
+ (if (mh-in-show-buffer (mh-show-buffer)
+ (pos-visible-in-window-p (point-max)))
+ (progn
+ (message (format
+ "End of message (Type %s to read %s undeleted message)"
+ (single-key-description last-input-event)
+ (if (equal mh-next-direction 'backward)
+ "previous"
+ "next")))
+ (setq mh-page-to-next-msg-p t))
+ (scroll-other-window arg)))
+ (mh-show)))
(defun mh-previous-page (&optional arg)
@@ -459,11 +872,11 @@ Scrolls ARG lines or a full screen if no argument is supplied."
(defun mh-previous-undeleted-msg (&optional arg)
- "Move to the NTH previous undeleted message in window."
+ "Move to the previous undeleted message ARG in window."
(interactive "p")
(setq mh-next-direction 'backward)
(beginning-of-line)
- (cond ((re-search-backward mh-good-msg-regexp nil 0 arg)
+ (cond ((re-search-backward mh-scan-good-msg-regexp nil 0 arg)
(mh-maybe-show))
(t
(if (get-buffer mh-show-buffer)
@@ -472,7 +885,7 @@ Scrolls ARG lines or a full screen if no argument is supplied."
(defun mh-rescan-folder (&optional range)
"Rescan a folder after optionally processing the outstanding commands.
-If optional prefix argument is provided, prompt for the range of
+If optional prefix argument RANGE is provided, prompt for the range of
messages to display. Otherwise show the entire folder."
(interactive (list (if current-prefix-arg
(mh-read-msg-range "Range to scan [all]? ")
@@ -482,8 +895,8 @@ messages to display. Otherwise show the entire folder."
(defun mh-write-msg-to-file (msg file no-headers)
- "Append MESSAGE to the end of a FILE.
-If NO-HEADERS (prefix argument) is provided, write only the message body.
+ "Append MSG to the end of a FILE.
+If prefix argument NO-HEADERS is provided, write only the message body.
Otherwise send the entire message including the headers."
(interactive
(list (mh-get-msg-num t)
@@ -512,29 +925,37 @@ Otherwise send the entire message including the headers."
(defun mh-toggle-showing ()
"Toggle the scanning mode/showing mode of displaying messages."
(interactive)
- (if mh-showing
+ (if mh-showing-mode
(mh-set-scan-mode)
- (mh-show)))
+ (mh-show)))
(defun mh-undo (msg-or-seq)
- "Undo the pending deletion or refile of the specified MESSAGE(s).
+ "Undo the pending deletion or refile of the specified MSG-OR-SEQ.
Default is the displayed message. If optional prefix argument is
-provided, then prompt for the message sequence."
- (interactive (list (if current-prefix-arg
- (mh-read-seq-default "Undo" t)
- (mh-get-msg-num t))))
+provided, then prompt for the message sequence.
+If variable `transient-mark-mode' is non-nil and the mark is active, then the
+selected region is unmarked."
+ (interactive (list (cond
+ ((and (boundp 'transient-mark-mode)
+ transient-mark-mode mark-active)
+ (mh-region-to-sequence (region-beginning)(region-end))
+ 'region)
+ (current-prefix-arg
+ (mh-read-seq-default "Undo" t))
+ (t
+ (mh-get-msg-num t)))))
(cond ((numberp msg-or-seq)
(let ((original-position (point)))
(beginning-of-line)
- (while (not (or (looking-at mh-deleted-msg-regexp)
- (looking-at mh-refiled-msg-regexp)
+ (while (not (or (looking-at mh-scan-deleted-msg-regexp)
+ (looking-at mh-scan-refiled-msg-regexp)
(and (eq mh-next-direction 'forward) (bobp))
(and (eq mh-next-direction 'backward)
(save-excursion (forward-line) (eobp)))))
(forward-line (if (eq mh-next-direction 'forward) -1 1)))
- (if (or (looking-at mh-deleted-msg-regexp)
- (looking-at mh-refiled-msg-regexp))
+ (if (or (looking-at mh-scan-deleted-msg-regexp)
+ (looking-at mh-scan-refiled-msg-regexp))
(progn
(mh-undo-msg (mh-get-msg-num t))
(mh-maybe-show))
@@ -559,21 +980,27 @@ provided, then prompt for the message sequence."
(mh-find-progs)
(set-buffer (get-buffer-create mh-temp-buffer))
(erase-buffer)
- (insert " mh-e info:\n\nversion: " mh-e-RCS-id
- "\nEmacs: " emacs-version " on " (symbol-name system-type) " ")
- (condition-case ()
- (call-process "uname" nil t nil "-a")
- (file-error))
- (insert "\n\n MH info:\n\n" (expand-file-name "inc" mh-progs) ":\n")
+ ;; mh-e and Emacs versions.
+ (insert "mh-e " mh-version "\n\n" (emacs-version) "\n\n")
+ ;; MH version.
(let ((help-start (point)))
(condition-case err-data
- (mh-exec-cmd-output "inc" nil "-help")
- (file-error (insert (mapconcat 'concat (cdr err-data) ": "))))
+ (mh-exec-cmd-output "inc" nil (if mh-nmh-p "-version" "-help"))
+ (file-error (insert (mapconcat 'concat (cdr err-data) ": ") "\n")))
(goto-char help-start)
- (search-forward "version: " nil t)
- (beginning-of-line)
- (delete-region help-start (point))
- (goto-char (point-min)))
+ (if mh-nmh-p
+ (search-forward "inc -- " nil t)
+ (search-forward "version: " nil t))
+ (delete-region help-start (point)))
+ (goto-char (point-max))
+ (insert "mh-progs:\t" mh-progs "\n"
+ "mh-lib:\t\t" mh-lib "\n"
+ "mh-lib-progs:\t" mh-lib-progs "\n\n")
+ ;; Linux version.
+ (condition-case ()
+ (call-process "uname" nil t nil "-a")
+ (file-error))
+ (goto-char (point-min))
(display-buffer mh-temp-buffer))
@@ -588,17 +1015,6 @@ Do not call this function from outside mh-e; see \\[mh-rmail] instead."
nil)
-(defun mh-compat-quit ()
- "The \"b\" key is obsolescent; will assume you want \"\\[mh-quit]\" ..."
- ;; Was going to make it run mh-burst-digest, but got complaint that
- ;; 'b' should mean 'back', as it does in info, less, and rn.
- ;; This is a temporary compatibility function.
- (interactive)
- (message "%s" (documentation this-command))
- (sit-for 1)
- (call-interactively 'mh-quit))
-
-
(defun mh-update-sequences ()
"Update MH's Unseen sequence and current folder and message.
Flush mh-e's state out to MH. The message at the cursor becomes current."
@@ -613,11 +1029,15 @@ Flush mh-e's state out to MH. The message at the cursor becomes current."
(setcdr seq-entry (list new-cur)) ;delete-seq-locally, add-msgs-to-seq
(mh-define-sequence 'cur (list new-cur))
(beginning-of-line)
- (if (looking-at mh-good-msg-regexp)
+ (if (looking-at mh-scan-good-msg-regexp)
(mh-notate nil mh-note-cur mh-cmd-note)))
(or folder-set
(save-excursion
- (mh-exec-cmd-quiet t "folder" mh-current-folder "-fast"))))))
+ ;; psg - mh-current-folder is nil if mh-summary-height < 4 !
+ ;; So I added this sanity check.
+ (if (stringp mh-current-folder)
+ (mh-exec-cmd-quiet t "folder" mh-current-folder "-fast")
+ (mh-exec-cmd-quiet t "folder" "-fast")))))))
@@ -628,9 +1048,9 @@ Flush mh-e's state out to MH. The message at the cursor becomes current."
;; Delete the MESSAGE.
(save-excursion
(mh-goto-msg msg nil t)
- (if (looking-at mh-refiled-msg-regexp)
+ (if (looking-at mh-scan-refiled-msg-regexp)
(error "Message %d is refiled. Undo refile before deleting" msg))
- (if (looking-at mh-deleted-msg-regexp)
+ (if (looking-at mh-scan-deleted-msg-regexp)
nil
(mh-set-folder-modified-p t)
(setq mh-delete-list (cons msg mh-delete-list))
@@ -642,9 +1062,9 @@ Flush mh-e's state out to MH. The message at the cursor becomes current."
;; Refile MESSAGE in FOLDER. FOLDER is a symbol, not a string.
(save-excursion
(mh-goto-msg msg nil t)
- (cond ((looking-at mh-deleted-msg-regexp)
+ (cond ((looking-at mh-scan-deleted-msg-regexp)
(error "Message %d is deleted. Undo delete before moving" msg))
- ((looking-at mh-refiled-msg-regexp)
+ ((looking-at mh-scan-refiled-msg-regexp)
(if (y-or-n-p
(format "Message %d already refiled. Copy to %s as well? "
msg destination))
@@ -673,7 +1093,7 @@ Flush mh-e's state out to MH. The message at the cursor becomes current."
;; Display the scan listing buffer, but do not show a message.
(if (get-buffer mh-show-buffer)
(delete-windows-on mh-show-buffer))
- (setq mh-showing nil)
+ (mh-showing-mode 0)
(force-mode-line-update)
(if mh-recenter-summary-p
(mh-recenter nil)))
@@ -711,110 +1131,33 @@ Flush mh-e's state out to MH. The message at the cursor becomes current."
;;; Ensure new buffers won't get this mode if default-major-mode is nil.
(put 'mh-folder-mode 'mode-class 'special)
-(defun mh-folder-mode ()
+(define-derived-mode mh-folder-mode fundamental-mode "MH-Folder"
"Major mh-e mode for \"editing\" an MH folder scan listing.\\<mh-folder-mode-map>
+
You can show the message the cursor is pointing to, and step through the
messages. Messages can be marked for deletion or refiling into another
folder; these commands are executed all at once with a separate command.
A prefix argument (\\[universal-argument]) to delete, refile, list, or undo
-applies the action to a message sequence.
-
-Here is a list of the standard keys for mh-e commands, grouped by function.
-This list is purposefully not customized; mh-e has a long history, and many
-alternate key bindings as a result. This list is to encourage users to use
-standard keys so the other keys can perhaps someday be put to new uses.
-
-t toggle show or scan-only mode
-RET show message, or back to top if already showing
-
-SPC page message forward
-DEL page message back
-
-n next message
-p previous message
-g go to message by number
-
-d mark for deletion
-o, ^ mark for output (refile) to another folder
-? show folder of pending refile
-u undo delete or refile marking
-
-x execute marked deletes and refiles
-i incorporate new mail
-
-m mail a new message
-r reply to a message
-f forward a message
-
-q quit mh-e
-
-M-f visit new folder
-M-r rescan this folder
-
-Here are all the commands with their current binding, listed in key order:
-\\{mh-folder-mode-map}
-
-Variables controlling mh-e operation are (defaults in parentheses):
-
- `mh-recursive-folders' (nil)
- Non-nil means commands which operate on folders do so recursively.
+applies the action to a message sequence. If `transient-mark-mode',
+is non-nil, the action is applied to the region.
- `mh-bury-show-buffer' (t)
- Non-nil means that the buffer used to display message is buried.
- It will never be offered as the default other buffer.
+Options that control this mode can be changed with \\[customize-group];
+specify the \"mh\" group. In particular, please see the `mh-scan-format-file'
+option if you wish to modify scan's format.
- `mh-clean-message-header' (nil)
- Non-nil means remove header lines matching the regular expression
- specified in mh-invisible-headers from messages.
+When a folder is visited, the hook `mh-folder-mode-hook' is run.
- `mh-visible-headers' (nil)
- If non-nil, it contains a regexp specifying the headers that are shown in
- a message if mh-clean-message-header is non-nil. Setting this variable
- overrides `mh-invisible-headers'.
+\\{mh-folder-mode-map}"
- `mh-do-not-confirm' (nil)
- Non-nil means do not prompt for confirmation before executing some
- non-recoverable commands such as `mh-kill-folder' and `mh-undo-folder'.
-
- `mhl-formfile' (nil)
- Name of format file to be used by mhl to show messages.
- A value of t means use the default format file.
- nil means don't use mhl to format messages.
-
- `mh-lpr-command-format' (\"lpr -p -J '%s'\")
- Format for command used to print a message on a system printer.
-
- `mh-scan-prog' (\"scan\")
- Program to run to generate one-line-per-message listing of a folder.
- Normally \"scan\" or a file name linked to scan. This file is searched
- for relative to the mh-progs directory unless it is an absolute pathname.
- Automatically becomes buffer-local when set in any fashion.
-
- `mh-print-background' (nil)
- Print messages in the background if non-nil.
- WARNING: do not delete the messages until printing is finished;
- otherwise, your output may be truncated.
-
- `mh-recenter-summary-p' (nil)
- If non-nil, then the scan listing is recentered when the window displaying
- a messages is toggled off.
-
- `mh-summary-height' (4)
- Number of lines in the summary window including the mode line.
-
-The value of mh-folder-mode-hook is called when a new folder is set up."
-
- (kill-all-local-variables)
- (use-local-map mh-folder-mode-map)
- (setq major-mode 'mh-folder-mode)
- (mh-set-mode-name "MH-Folder")
+ (make-local-variable 'font-lock-defaults)
+ (setq font-lock-defaults '(mh-folder-font-lock-keywords t))
(mh-make-local-vars
'mh-current-folder (buffer-name) ; Name of folder, a string
'mh-show-buffer (format "show-%s" (buffer-name)) ; Buffer that displays msgs
'mh-folder-filename ; e.g. "/usr/foobar/Mail/inbox/"
(file-name-as-directory (mh-expand-file-name (buffer-name)))
- 'mh-showing nil ; Show message also?
+ 'mh-showing-mode nil ; Show message also?
'mh-delete-list nil ; List of msgs nums to delete
'mh-refile-list nil ; List of folder names in mh-seq-list
'mh-seq-list nil ; Alist of (seq . msgs) nums
@@ -834,11 +1177,18 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(make-local-variable 'write-file-hooks)
(setq write-file-hooks '(mh-execute-commands))) ;Emacs 18
(make-local-variable 'revert-buffer-function)
+ (make-local-variable 'hl-line-mode) ; avoid pollution
+ (if (fboundp 'hl-line-mode)
+ (hl-line-mode 1))
(setq revert-buffer-function 'mh-undo-folder)
- (or (assq 'mh-showing minor-mode-alist)
+ (or (assq 'mh-showing-mode minor-mode-alist)
(setq minor-mode-alist
- (cons '(mh-showing " Show") minor-mode-alist)))
- (run-hooks 'mh-folder-mode-hook))
+ (cons '(mh-showing-mode " Show") minor-mode-alist)))
+ (easy-menu-add mh-folder-sequence-menu)
+ (easy-menu-add mh-folder-message-menu)
+ (easy-menu-add mh-folder-folder-menu)
+ (if (and (boundp 'tool-bar-mode) tool-bar-mode)
+ (set (make-local-variable 'tool-bar-map) mh-folder-tool-bar-map)))
(defun mh-make-local-vars (&rest pairs)
@@ -857,12 +1207,11 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(mh-process-or-undo-commands folder)
(switch-to-buffer folder)))
(mh-regenerate-headers range)
- (cond ((zerop (buffer-size))
+ (if (zerop (buffer-size))
(if (equal range "all")
(message "Folder %s is empty" folder)
(message "No messages in %s, range %s" folder range))
- (sit-for 5)))
- (mh-goto-cur-msg))
+ (mh-goto-cur-msg)))
(defun mh-regenerate-headers (range &optional update)
@@ -877,15 +1226,18 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(erase-buffer))
(setq scan-start (point))
(mh-exec-cmd-output mh-scan-prog nil
+ (mh-scan-format)
"-noclear" "-noheader"
"-width" (window-width)
folder range)
(goto-char scan-start)
(cond ((looking-at "scan: no messages in")
- (keep-lines mh-valid-scan-line)) ; Flush random scan lines
+ (keep-lines mh-scan-valid-regexp)) ; Flush random scan lines
+ ((looking-at "scan: bad message list ")
+ (keep-lines mh-scan-valid-regexp))
((looking-at "scan: ")) ; Keep error messages
(t
- (keep-lines mh-valid-scan-line))) ; Flush random scan lines
+ (keep-lines mh-scan-valid-regexp))) ; Flush random scan lines
(setq mh-seq-list (mh-read-folder-sequences folder nil))
(mh-notate-user-sequences)
(or update
@@ -914,10 +1266,12 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
;; I think MH 5 used "-ms-file" instead of "-file",
;; which would make inc'ing from maildrops fail.
(mh-exec-cmd-output mh-inc-prog nil folder
+ (mh-scan-format)
"-file" (expand-file-name maildrop-name)
"-width" (window-width)
"-truncate")
(mh-exec-cmd-output mh-inc-prog nil
+ (mh-scan-format)
"-width" (window-width)))
(if maildrop-name
(message "inc %s -file %s...done" folder maildrop-name)
@@ -928,11 +1282,11 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(message "No new mail%s%s" (if maildrop-name " in " "")
(if maildrop-name maildrop-name "")))
((re-search-forward "^inc:" nil t) ; Error messages
- (error "inc error"))
+ (error "Error incorporating mail"))
(t
(mh-remove-cur-notation)
(setq new-mail-p t)))
- (keep-lines mh-valid-scan-line) ; Flush random scan lines
+ (keep-lines mh-scan-valid-regexp) ; Flush random scan lines
(setq mh-seq-list (mh-read-folder-sequences folder t))
(mh-notate-user-sequences)
(if new-mail-p
@@ -952,20 +1306,25 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(setq mh-first-msg-num (mh-get-msg-num nil))
(mh-last-msg)
(setq mh-last-msg-num (mh-get-msg-num nil))
- (setq mh-msg-count (count-lines (point-min) (point-max)))
+ (setq mh-msg-count (if mh-first-msg-num
+ (count-lines (point-min) (point-max))
+ 0))
(setq mode-line-buffer-identification
- (list (format "{%%b%s} %d msg%s"
+ (list (format "{%%b%s} %s msg%s"
(if mh-mode-line-annotation
(format "/%s" mh-mode-line-annotation)
"")
- mh-msg-count
+ (if (zerop mh-msg-count)
+ "no"
+ (format "%d" mh-msg-count))
(if (zerop mh-msg-count)
"s"
- (if (> mh-msg-count 1)
- (format "s (%d-%d)" mh-first-msg-num
- mh-last-msg-num)
- (format " (%d)" mh-first-msg-num))))))))
-
+ (cond ((> mh-msg-count 1)
+ (format "s (%d-%d)" mh-first-msg-num
+ mh-last-msg-num))
+ (mh-first-msg-num
+ (format " (%d)" mh-first-msg-num))
+ (""))))))))
(defun mh-unmark-all-headers (remove-all-flags)
;; Remove all '+' flags from the headers, and if called with a non-nil
@@ -1002,7 +1361,7 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(save-excursion
(and cur-msg
(mh-goto-msg cur-msg t t)
- (looking-at mh-cur-scan-msg-regexp)
+ (looking-at mh-scan-cur-msg-number-regexp)
(mh-notate nil ? mh-cmd-note)))))
(defun mh-goto-cur-msg ()
@@ -1025,9 +1384,9 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(if (mh-outstanding-commands-p)
(if (or mh-do-not-confirm
(y-or-n-p
- "Process outstanding deletes and refiles (or lose them)? "))
+ "Process outstanding deletes and refiles (or lose them)? "))
(mh-process-commands folder)
- (mh-undo-folder)))
+ (mh-undo-folder)))
(mh-update-unseen)
(mh-invalidate-show-buffer))
@@ -1037,6 +1396,9 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(message "Processing deletes and refiles for %s..." folder)
(set-buffer folder)
(with-mh-folder-updating (nil)
+ ;; Run the hook while the lists are still valid
+ (run-hooks 'mh-folder-updated-hook)
+
;; Update the unseen sequence if it exists
(mh-update-unseen)
@@ -1080,13 +1442,14 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(defun mh-update-unseen ()
;; Flush updates to the Unseen sequence out to MH.
- ;; Return non-nil iff set the MH folder.
+ ;; Return non-NIL iff set the MH folder.
(if mh-seen-list
(let* ((unseen-seq (mh-find-seq mh-unseen-seq))
(unseen-msgs (mh-seq-msgs unseen-seq)))
(if unseen-msgs
(progn
(mh-undefine-sequence mh-unseen-seq mh-seen-list)
+ (run-hooks 'mh-unseen-updated-hook)
(while mh-seen-list
(setq unseen-msgs (delq (car mh-seen-list) unseen-msgs))
(setq mh-seen-list (cdr mh-seen-list)))
@@ -1144,6 +1507,8 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
nil
(string-lessp msg2 msg1))))
+(defun mh-lessp (msg1 msg2)
+ (not (mh-greaterp msg1 msg2)))
;;; Basic sequence handling
@@ -1211,7 +1576,7 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(defun mh-internal-seq (name)
- ;; Return non-nil if NAME is the name of an internal mh-e sequence.
+ ;; Return non-NIL if NAME is the name of an internal mh-e sequence.
(or (memq name '(answered cur deleted forwarded printed))
(eq name mh-unseen-seq)
(eq name mh-previous-seq)
@@ -1219,9 +1584,9 @@ The value of mh-folder-mode-hook is called when a new folder is set up."
(defun mh-delete-msg-from-seq (message sequence &optional internal-flag)
- "Delete MESSAGE from SEQUENCE. MESSAGE defaults to displayed message.
-From Lisp, optional third arg INTERNAL-FLAG non-nil means do not
-inform MH of the change."
+ "Delete MESSAGE from SEQUENCE.
+MESSAGE defaults to displayed message. From Lisp, optional third arg
+INTERNAL-FLAG non-nil means do not inform MH of the change."
(interactive (list (mh-get-msg-num t)
(mh-read-seq-default "Delete from" t)
nil))
@@ -1305,65 +1670,220 @@ inform MH of the change."
;;; Build the folder-mode keymap:
(suppress-keymap mh-folder-mode-map)
-(define-key mh-folder-mode-map "q" 'mh-quit)
-(define-key mh-folder-mode-map "b" 'mh-compat-quit)
-(define-key mh-folder-mode-map "?" 'mh-msg-is-in-seq)
-(define-key mh-folder-mode-map "%" 'mh-put-msg-in-seq)
-(define-key mh-folder-mode-map "|" 'mh-pipe-msg)
-(define-key mh-folder-mode-map "\ea" 'mh-edit-again)
-(define-key mh-folder-mode-map "\e%" 'mh-delete-msg-from-seq)
-(define-key mh-folder-mode-map "\e#" 'mh-delete-seq)
-(define-key mh-folder-mode-map "\C-xn" 'mh-narrow-to-seq)
-(define-key mh-folder-mode-map "\C-xw" 'mh-widen)
-(define-key mh-folder-mode-map "\eb" 'mh-burst-digest)
-(define-key mh-folder-mode-map "\eu" 'mh-undo-folder)
-(define-key mh-folder-mode-map "\e " 'mh-page-digest)
-(define-key mh-folder-mode-map "\e\177" 'mh-page-digest-backwards)
-(define-key mh-folder-mode-map "\ed" 'mh-redistribute)
-(define-key mh-folder-mode-map "\ee" 'mh-extract-rejected-mail)
-(define-key mh-folder-mode-map "\ef" 'mh-visit-folder)
-(define-key mh-folder-mode-map "\ek" 'mh-kill-folder)
-(define-key mh-folder-mode-map "\el" 'mh-list-folders)
-(define-key mh-folder-mode-map "\en" 'mh-store-msg)
-(define-key mh-folder-mode-map "\ep" 'mh-pack-folder)
-(define-key mh-folder-mode-map "\eq" 'mh-list-sequences)
-(define-key mh-folder-mode-map "\es" 'mh-search-folder)
-(define-key mh-folder-mode-map "\er" 'mh-rescan-folder)
-(define-key mh-folder-mode-map "l" 'mh-print-msg)
-(define-key mh-folder-mode-map "t" 'mh-toggle-showing)
-(define-key mh-folder-mode-map "c" 'mh-copy-msg)
-(define-key mh-folder-mode-map "i" 'mh-inc-folder)
-(define-key mh-folder-mode-map "x" 'mh-execute-commands)
-(define-key mh-folder-mode-map "e" 'mh-execute-commands)
-(define-key mh-folder-mode-map "f" 'mh-forward)
-(define-key mh-folder-mode-map "m" 'mh-send)
-(define-key mh-folder-mode-map "s" 'mh-send)
-(define-key mh-folder-mode-map "r" 'mh-reply)
-(define-key mh-folder-mode-map "a" 'mh-reply)
-(define-key mh-folder-mode-map "j" 'mh-goto-msg)
-(define-key mh-folder-mode-map "g" 'mh-goto-msg)
-(define-key mh-folder-mode-map "\e<" 'mh-first-msg)
-(define-key mh-folder-mode-map "\e>" 'mh-last-msg)
-(define-key mh-folder-mode-map "\177" 'mh-previous-page)
-(define-key mh-folder-mode-map " " 'mh-page-msg)
-(define-key mh-folder-mode-map "\r" 'mh-show)
-(define-key mh-folder-mode-map "." 'mh-show)
-(define-key mh-folder-mode-map "," 'mh-header-display)
-(define-key mh-folder-mode-map "u" 'mh-undo)
-(define-key mh-folder-mode-map "d" 'mh-delete-msg)
-(define-key mh-folder-mode-map "\C-d" 'mh-delete-msg-no-motion)
-(define-key mh-folder-mode-map "p" 'mh-previous-undeleted-msg)
-(define-key mh-folder-mode-map "n" 'mh-next-undeleted-msg)
-(define-key mh-folder-mode-map "o" 'mh-refile-msg)
-(define-key mh-folder-mode-map "^" 'mh-refile-msg)
-(define-key mh-folder-mode-map "\C-o" 'mh-write-msg-to-file)
-(define-key mh-folder-mode-map ">" 'mh-write-msg-to-file)
-(define-key mh-folder-mode-map "!" 'mh-refile-or-write-again)
+
+;; Save the `b' binding for a future `back'. Maybe?
+(gnus-define-keys mh-folder-mode-map
+ " " mh-page-msg
+ "!" mh-refile-or-write-again
+ "," mh-header-display
+ "." mh-show ;alias
+ ">" mh-write-msg-to-file
+ "E" mh-extract-rejected-mail
+ "\177" mh-previous-page
+ "\C-d" mh-delete-msg-no-motion
+ "\e<" mh-first-msg
+ "\e>" mh-last-msg
+ "\ed" mh-redistribute
+ "\r" mh-show
+ "^" mh-refile-msg ;alias
+ "c" mh-copy-msg
+ "d" mh-delete-msg
+ "e" mh-edit-again
+ "f" mh-forward
+ "g" mh-goto-msg
+ "i" mh-inc-folder
+ "k" mh-delete-subject-thread
+ "l" mh-print-msg
+ "m" mh-send ;alias
+ "n" mh-next-undeleted-msg
+ "o" mh-refile-msg
+ "p" mh-previous-undeleted-msg
+ "q" mh-quit
+ "r" mh-reply
+ "s" mh-send
+ "t" mh-toggle-showing
+ "u" mh-undo
+ "x" mh-execute-commands
+ "|" mh-pipe-msg)
+
+(gnus-define-keys (mh-folder-map "F" mh-folder-mode-map)
+ "S" mh-sort-folder
+ "f" mh-visit-folder ;alias
+ "k" mh-kill-folder
+ "l" mh-list-folders
+ "o" mh-visit-folder ;alias
+ "p" mh-pack-folder
+ "r" mh-rescan-folder
+ "s" mh-search-folder
+ "u" mh-undo-folder
+ "v" mh-visit-folder)
+
+(gnus-define-keys (mh-sequence-map "S" mh-folder-mode-map)
+ "d" mh-delete-msg-from-seq
+ "k" mh-delete-seq
+ "l" mh-list-sequences
+ "n" mh-narrow-to-seq
+ "p" mh-put-msg-in-seq
+ "s" mh-msg-is-in-seq
+ "w" mh-widen)
+
+(gnus-define-keys (mh-thread-map "T" mh-folder-mode-map)
+ "d" mh-delete-subject-thread
+ "k" mh-delete-subject-thread
+ "s" mh-narrow-to-subject-thread
+ "t" mh-toggle-subject-thread
+ "u" mh-next-unseen-subject-thread)
+
+(gnus-define-keys (mh-extract-map "X" mh-folder-mode-map)
+ "s" mh-store-msg ;shar
+ "u" mh-store-msg) ;uuencode
+
+(gnus-define-keys (mh-digest-map "D" mh-folder-mode-map)
+ " " mh-page-digest
+ "\177" mh-page-digest-backwards
+ "b" mh-burst-digest)
+
+(cond
+ ((not (null (save-match-data (string-match "XEmacs\\|Lucid" emacs-version))))
+ (define-key mh-folder-mode-map [button2] 'mh-show-mouse))
+ (t
+ (define-key mh-folder-mode-map [mouse-2] 'mh-show-mouse)))
;; "C-c /" prefix is used in mh-folder-mode by pgp.el and mailcrypt
+;;; Menu extracted from mh-menubar.el V1.1 (31 July 2001)
+;;; Menus for folder mode: folder, message, sequence (in that order)
+;;; folder-mode "Sequence" menu
+(easy-menu-define
+ mh-folder-sequence-menu mh-folder-mode-map "Menu for mh-e folder-sequence."
+ '("Sequence"
+ ["Add Msg to Seq..." mh-put-msg-in-seq (mh-get-msg-num nil)]
+ ["List Seq's for Msg" mh-msg-is-in-seq (mh-get-msg-num nil)]
+ ["Delete Msg from Seq..." mh-delete-msg-from-seq (mh-get-msg-num nil)]
+ ["List Seq's in Folder..." mh-list-sequences t]
+ ["Delete Seq..." mh-delete-seq t]
+ ["Show Only Msgs in Seq..." mh-narrow-to-seq t]
+ ["Show All Msgs in Folder" mh-widen mh-narrowed-to-seq]
+ "--"
+ ["Toggle Subject Thread" mh-toggle-subject-thread t]
+ ["Narrow to Subject Thread" mh-narrow-to-subject-thread t]
+ ["Delete Rest of Subject Thread" mh-delete-subject-thread t]
+ ["Next Unseen Subject Thread" mh-next-unseen-subject-thread t]
+ "--"
+ ["Push State Out to MH" mh-update-sequences t]))
+
+;;; folder-mode "Message" menu
+(easy-menu-define
+ mh-folder-message-menu mh-folder-mode-map "Menu for mh-e folder-message."
+ '("Message"
+ ["Show Msg" mh-show (mh-get-msg-num nil)]
+ ["Next Msg" mh-next-undeleted-msg t]
+ ["Previous Msg" mh-previous-undeleted-msg t]
+ ["Go to First Msg" mh-first-msg t]
+ ["Go to Last Msg" mh-last-msg t]
+ ["Go to Msg by Number..." mh-goto-msg t]
+ ["Delete Msg" mh-delete-msg (mh-get-msg-num nil)]
+ ["Refile Msg" mh-refile-msg (mh-get-msg-num nil)]
+ ["Undo Delete/Refile" mh-undo t]
+ ["Process Delete/Refile" mh-execute-commands
+ (or mh-refile-list mh-delete-list)]
+ "--"
+ ["Compose a New Msg" mh-send t]
+ ["Reply to Msg..." mh-reply (mh-get-msg-num nil)]
+ ["Forward Msg..." mh-forward (mh-get-msg-num nil)]
+ ["Redistribute Msg..." mh-redistribute (mh-get-msg-num nil)]
+ ["Edit Msg Again" mh-edit-again (mh-get-msg-num nil)]
+ ["Re-edit a Bounced Msg" mh-extract-rejected-mail t]
+ "--"
+ ["Refile Msg in Folder..." mh-refile-msg (mh-get-msg-num nil)]
+ ["Copy Msg to Folder..." mh-copy-msg (mh-get-msg-num nil)]
+ ["Print Msg" mh-print-msg (mh-get-msg-num nil)]
+ ["Write Msg to File..." mh-write-msg-to-file (mh-get-msg-num nil)]
+ ["Pipe Msg to Command..." mh-pipe-msg (mh-get-msg-num nil)]
+ ["Unpack Uuencoded Msg..." mh-store-msg (mh-get-msg-num nil)]
+ ["Show Msg with Header" mh-header-display (mh-get-msg-num nil)]
+ ["Burst Digest Msg" mh-burst-digest (mh-get-msg-num nil)]))
+
+;;; folder-mode "Folder" menu
+(easy-menu-define
+ mh-folder-folder-menu mh-folder-mode-map "Menu for mh-e folder."
+ '("Folder"
+ ["Incorporate New Mail" mh-inc-folder t]
+ ["Toggle Show/Folder" mh-toggle-showing t]
+ ["Execute Delete/Refile" mh-execute-commands
+ (or mh-refile-list mh-delete-list)]
+ ["Rescan Folder" mh-rescan-folder t]
+ ["Pack Folder" mh-pack-folder t]
+ ["Sort Folder" mh-sort-folder t]
+ "--"
+ ["Search a Folder..." mh-search-folder t]
+ ["Visit a Folder..." mh-visit-folder t]
+ ["List Folders" mh-list-folders t]
+ ["Quit MH-E" mh-quit t]))
+;;; Support for emacs21 toolbar using gnus/message.el icons (and code).
+(eval-when-compile (defvar tool-bar-map))
+(when (and (fboundp 'tool-bar-add-item)
+ tool-bar-mode)
+ (defvar mh-folder-tool-bar-map
+ (let ((tool-bar-map (make-sparse-keymap)))
+ (tool-bar-add-item "mail" 'mh-inc-folder 'mh-folder-inc-folder
+ :help "Incorporate new mail in Inbox")
+
+ (tool-bar-add-item "left_arrow" 'mh-previous-undeleted-msg
+ 'mh-folder-prev :help "Previous message")
+ (tool-bar-add-item "page-down" 'mh-page-msg 'mh-folder-page
+ :help "Page this message")
+ (tool-bar-add-item "right_arrow" 'mh-next-undeleted-msg 'mh-folder-next
+ :help "Next message")
+
+ (tool-bar-add-item "close" 'mh-delete-msg 'mh-folder-delete
+ :help "Mark for deletion")
+ (tool-bar-add-item "refile" 'mh-refile-msg 'mh-folder-refile
+ :help "Refile this message")
+ (tool-bar-add-item "undo" 'mh-undo 'mh-folder-undo
+ :help "Undo this mark")
+ (tool-bar-add-item "execute" 'mh-execute-commands 'mh-folder-exec
+ :help "Perform moves and deletes")
+
+ (tool-bar-add-item "show" 'mh-toggle-showing 'mh-folder-toggle-show
+ :help "Toggle showing message")
+
+ (tool-bar-add-item "mail/reply2" 'mh-reply 'mh-folder-reply
+ :help "Reply to this message")
+ (tool-bar-add-item "mail_compose" 'mh-send 'mh-folder-compose
+ :help "Compose new message")
+
+ (tool-bar-add-item "rescan" 'mh-rescan-folder 'mh-folder-rescan
+ :help "Rescan this folder")
+ (tool-bar-add-item "repack" 'mh-pack-folder 'mh-folder-pack
+ :help "Repack this folder")
+
+ (tool-bar-add-item "search" 'mh-search-folder 'mh-folder-search
+ :help "Search this folder")
+ (tool-bar-add-item "fld_open" 'mh-visit-folder 'mh-folder-visit
+ :help "Visit other folder")
+
+ (tool-bar-add-item "preferences" (lambda ()
+ (interactive)
+ (customize-group "mh"))
+ 'mh-folder-customize
+ :help "mh-e preferences")
+ (tool-bar-add-item "help" (lambda ()
+ (interactive)
+ (Info-goto-node "(mh-e)Top"))
+ 'mh-folder-help :help "Help")
+ tool-bar-map))
+
+ (defvar mh-folder-seq-tool-bar-map
+ (let ((tool-bar-map (copy-keymap mh-folder-tool-bar-map)))
+ (tool-bar-add-item "widen" 'mh-widen 'mh-folder-widen
+ :help "Widen from this sequence")
+ tool-bar-map)
+ "Tool-bar to use when narrowed to a sequence in MH-Folder buffers.")
+ )
+
;;;autoload the other mh-e parts
;;; mh-comp
@@ -1503,6 +2023,8 @@ Add the messages found to the sequence named `search'." t)
;;; mh-seq
+(autoload 'mh-region-to-sequence "mh-seq"
+ "Define sequence 'region as the messages in selected region." t)
(autoload 'mh-delete-seq "mh-seq"
"Delete the SEQUENCE." t)
(autoload 'mh-list-sequences "mh-seq"
@@ -1519,6 +2041,16 @@ If optional prefix argument provided, then prompt for the message sequence." t)
"Remove restrictions from current folder, thereby showing all messages." t)
(autoload 'mh-rename-seq "mh-seq"
"Rename SEQUENCE to have NEW-NAME." t)
+(autoload 'mh-narrow-to-subject-thread "mh-seq"
+ "Narrow to a sequence containing all following messages with same subject."
+ t)
+(autoload 'mh-toggle-subject-thread "mh-seq"
+ "Narrow to or widen from a sequence containing current subject sequence." t)
+(autoload 'mh-delete-subject-thread "mh-seq"
+ "Mark all following messages with same subject to be deleted." t)
+(autoload 'mh-next-unseen-subject-thread "mh-seq"
+ "Get the next unseen subject thread." t)
+
(dolist (mess '("^Cursor not pointing to message$"
"^There is no other window$"))