(sh-shell): Mark as safe.
[bpt/emacs.git] / lisp / mh-e / mh-folder.el
CommitLineData
dda00b2c
BW
1;;; mh-folder.el --- MH-Folder mode
2
3;; Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
4
5;; Author: Bill Wohler <wohler@newt.com>
6;; Maintainer: Bill Wohler <wohler@newt.com>
7;; Keywords: mail
8;; See: mh-e.el
9
10;; This file is part of GNU Emacs.
11
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 2, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25;; Boston, MA 02110-1301, USA.
26
27;;; Commentary:
28
29;; Mode for browsing folders
30
31;;; Change Log:
32
33;;; Code:
34
35(require 'mh-e)
36(require 'mh-scan)
37(mh-require-cl)
38
efc27af6 39;; Dynamically-created functions not found in mh-loaddefs.el.
dda00b2c 40(autoload 'mh-tool-bar-folder-buttons-init "mh-tool-bar")
efc27af6 41(autoload 'mh-tool-bar-init "mh-tool-bar")
dda00b2c
BW
42
43(require 'gnus-util)
44(autoload 'message-fetch-field "message")
45
46\f
47
48;;; MH-E Entry Points
49
50;;;###autoload
51(defun mh-rmail (&optional arg)
52 "Incorporate new mail with MH.
53Scan an MH folder if ARG is non-nil.
54
55This function is an entry point to MH-E, the Emacs interface to
56the MH mail system."
57 (interactive "P")
58 (mh-find-path)
59 (if arg
60 (call-interactively 'mh-visit-folder)
61 (unless (get-buffer mh-inbox)
62 (mh-visit-folder mh-inbox (symbol-name mh-unseen-seq)))
63 (mh-inc-folder)))
64
65;;;###autoload
66(defun mh-nmail (&optional arg)
67 "Check for new mail in inbox folder.
68Scan an MH folder if ARG is non-nil.
69
70This function is an entry point to MH-E, the Emacs interface to
71the MH mail system."
72 (interactive "P")
73 (mh-find-path) ; init mh-inbox
74 (if arg
75 (call-interactively 'mh-visit-folder)
76 (mh-visit-folder mh-inbox)))
77\f
78
79;;; Desktop Integration
80
81;; desktop-buffer-mode-handlers appeared in Emacs 22.
82(if (fboundp 'desktop-buffer-mode-handlers)
83 (add-to-list 'desktop-buffer-mode-handlers
84 '(mh-folder-mode . mh-restore-desktop-buffer)))
85
86(defun mh-restore-desktop-buffer (desktop-buffer-file-name
87 desktop-buffer-name
88 desktop-buffer-misc)
89 "Restore an MH folder buffer specified in a desktop file.
90When desktop creates a buffer, DESKTOP-BUFFER-FILE-NAME holds the
91file name to visit, DESKTOP-BUFFER-NAME holds the desired buffer
92name, and DESKTOP-BUFFER-MISC holds a list of miscellaneous info
93used by the `desktop-buffer-handlers' functions."
94 (mh-find-path)
95 (mh-visit-folder desktop-buffer-name)
96 (current-buffer))
97
98\f
99
100;;; Variables
101
102(defvar mh-folder-filename nil
103 "Full path of directory for this folder.")
104
105(defvar mh-partial-folder-mode-line-annotation "select"
106 "Annotation when displaying part of a folder.
107The string is displayed after the folder's name. nil for no
108annotation.")
109
110(defvar mh-last-destination nil
111 "Destination of last refile or write command.")
112
113(defvar mh-last-destination-folder nil
114 "Destination of last refile command.")
115
116(defvar mh-last-destination-write nil
117 "Destination of last write command.")
118
119(defvar mh-first-msg-num nil
120 "Number of first message in buffer.")
121
122(defvar mh-last-msg-num nil
123 "Number of last msg in buffer.")
124
125(defvar mh-msg-count nil
126 "Number of msgs in buffer.")
127
128\f
129
130;;; Sequence Menu
131
132(easy-menu-define
133 mh-folder-sequence-menu mh-folder-mode-map "Menu for MH-E folder-sequence."
134 '("Sequence"
135 ["Add Message to Sequence..." mh-put-msg-in-seq (mh-get-msg-num nil)]
136 ["List Sequences for Message" mh-msg-is-in-seq (mh-get-msg-num nil)]
137 ["Delete Message from Sequence..." mh-delete-msg-from-seq
138 (mh-get-msg-num nil)]
139 ["List Sequences in Folder..." mh-list-sequences t]
140 ["Delete Sequence..." mh-delete-seq t]
141 ["Narrow to Sequence..." mh-narrow-to-seq t]
142 ["Widen from Sequence" mh-widen mh-folder-view-stack]
143 "--"
144 ["Narrow to Subject Sequence" mh-narrow-to-subject t]
145 ["Narrow to Tick Sequence" mh-narrow-to-tick
146 (and mh-tick-seq (mh-seq-msgs (mh-find-seq mh-tick-seq)))]
147 ["Delete Rest of Same Subject" mh-delete-subject t]
148 ["Toggle Tick Mark" mh-toggle-tick t]
149 "--"
150 ["Push State Out to MH" mh-update-sequences t]))
151
152;;; Message Menu
153
154(easy-menu-define
155 mh-folder-message-menu mh-folder-mode-map "Menu for MH-E folder-message."
156 '("Message"
157 ["Show Message" mh-show (mh-get-msg-num nil)]
158 ["Show Message with Header" mh-header-display (mh-get-msg-num nil)]
159 ["Next Message" mh-next-undeleted-msg t]
160 ["Previous Message" mh-previous-undeleted-msg t]
161 ["Go to First Message" mh-first-msg t]
162 ["Go to Last Message" mh-last-msg t]
163 ["Go to Message by Number..." mh-goto-msg t]
164 ["Modify Message" mh-modify t]
165 ["Delete Message" mh-delete-msg (mh-get-msg-num nil)]
166 ["Refile Message" mh-refile-msg (mh-get-msg-num nil)]
167 ["Undo Delete/Refile" mh-undo (mh-outstanding-commands-p)]
168 ["Execute Delete/Refile" mh-execute-commands
169 (mh-outstanding-commands-p)]
170 "--"
171 ["Compose a New Message" mh-send t]
172 ["Reply to Message..." mh-reply (mh-get-msg-num nil)]
173 ["Forward Message..." mh-forward (mh-get-msg-num nil)]
174 ["Redistribute Message..." mh-redistribute (mh-get-msg-num nil)]
175 ["Edit Message Again" mh-edit-again (mh-get-msg-num nil)]
176 ["Re-edit a Bounced Message" mh-extract-rejected-mail t]
177 "--"
178 ["Copy Message to Folder..." mh-copy-msg (mh-get-msg-num nil)]
179 ["Print Message" mh-print-msg (mh-get-msg-num nil)]
180 ["Write Message to File..." mh-write-msg-to-file
181 (mh-get-msg-num nil)]
182 ["Pipe Message to Command..." mh-pipe-msg (mh-get-msg-num nil)]
183 ["Unpack Uuencoded Message..." mh-store-msg (mh-get-msg-num nil)]
184 ["Burst Digest Message" mh-burst-digest (mh-get-msg-num nil)]))
185
186;;; Folder Menu
187
188(easy-menu-define
189 mh-folder-folder-menu mh-folder-mode-map "Menu for MH-E folder."
190 '("Folder"
191 ["Incorporate New Mail" mh-inc-folder t]
192 ["Toggle Show/Folder" mh-toggle-showing t]
193 ["Execute Delete/Refile" mh-execute-commands
194 (mh-outstanding-commands-p)]
195 ["Rescan Folder" mh-rescan-folder t]
196 ["Thread Folder" mh-toggle-threads
197 (not (memq 'unthread mh-view-ops))]
198 ["Pack Folder" mh-pack-folder t]
199 ["Sort Folder" mh-sort-folder t]
200 "--"
201 ["List Folders" mh-list-folders t]
202 ["Visit a Folder..." mh-visit-folder t]
203 ["View New Messages" mh-index-new-messages t]
204 ["Search..." mh-search t]
205 "--"
206 ["Quit MH-E" mh-quit t]))
207
208\f
209
210;;; MH-Folder Keys
211
212(suppress-keymap mh-folder-mode-map)
213
214;; Use defalias to make sure the documented primary key bindings
215;; appear in menu lists.
216(defalias 'mh-alt-show 'mh-show)
217(defalias 'mh-alt-refile-msg 'mh-refile-msg)
218(defalias 'mh-alt-send 'mh-send)
219(defalias 'mh-alt-visit-folder 'mh-visit-folder)
220
221;; Save the "b" binding for a future `back'. Maybe?
222(gnus-define-keys mh-folder-mode-map
223 " " mh-page-msg
224 "!" mh-refile-or-write-again
225 "'" mh-toggle-tick
226 "," mh-header-display
227 "." mh-alt-show
228 ";" mh-toggle-mh-decode-mime-flag
229 ">" mh-write-msg-to-file
230 "?" mh-help
231 "E" mh-extract-rejected-mail
232 "M" mh-modify
233 "\177" mh-previous-page
234 "\C-d" mh-delete-msg-no-motion
235 "\t" mh-index-next-folder
236 [backtab] mh-index-previous-folder
237 "\M-\t" mh-index-previous-folder
238 "\e<" mh-first-msg
239 "\e>" mh-last-msg
240 "\ed" mh-redistribute
241 "\r" mh-show
242 "^" mh-alt-refile-msg
243 "c" mh-copy-msg
244 "d" mh-delete-msg
245 "e" mh-edit-again
246 "f" mh-forward
247 "g" mh-goto-msg
248 "i" mh-inc-folder
249 "k" mh-delete-subject-or-thread
250 "m" mh-alt-send
251 "n" mh-next-undeleted-msg
252 "\M-n" mh-next-unread-msg
253 "o" mh-refile-msg
254 "p" mh-previous-undeleted-msg
255 "\M-p" mh-previous-unread-msg
256 "q" mh-quit
257 "r" mh-reply
258 "s" mh-send
259 "t" mh-toggle-showing
260 "u" mh-undo
261 "v" mh-index-visit-folder
262 "x" mh-execute-commands
263 "|" mh-pipe-msg)
264
265(gnus-define-keys (mh-folder-map "F" mh-folder-mode-map)
266 "?" mh-prefix-help
267 "'" mh-index-ticked-messages
268 "S" mh-sort-folder
269 "c" mh-catchup
270 "f" mh-alt-visit-folder
271 "k" mh-kill-folder
272 "l" mh-list-folders
273 "n" mh-index-new-messages
274 "o" mh-alt-visit-folder
275 "p" mh-pack-folder
276 "q" mh-index-sequenced-messages
277 "r" mh-rescan-folder
278 "s" mh-search
279 "u" mh-undo-folder
280 "v" mh-visit-folder)
281
282(define-key mh-folder-mode-map "I" mh-inc-spool-map)
283
284(gnus-define-keys (mh-junk-map "J" mh-folder-mode-map)
285 "?" mh-prefix-help
286 "b" mh-junk-blacklist
287 "w" mh-junk-whitelist)
288
289(gnus-define-keys (mh-ps-print-map "P" mh-folder-mode-map)
290 "?" mh-prefix-help
291 "C" mh-ps-print-toggle-color
292 "F" mh-ps-print-toggle-faces
293 "f" mh-ps-print-msg-file
294 "l" mh-print-msg
295 "p" mh-ps-print-msg)
296
297(gnus-define-keys (mh-sequence-map "S" mh-folder-mode-map)
298 "'" mh-narrow-to-tick
299 "?" mh-prefix-help
300 "d" mh-delete-msg-from-seq
301 "k" mh-delete-seq
302 "l" mh-list-sequences
303 "n" mh-narrow-to-seq
304 "p" mh-put-msg-in-seq
305 "s" mh-msg-is-in-seq
306 "w" mh-widen)
307
308(gnus-define-keys (mh-thread-map "T" mh-folder-mode-map)
309 "?" mh-prefix-help
310 "u" mh-thread-ancestor
311 "p" mh-thread-previous-sibling
312 "n" mh-thread-next-sibling
313 "t" mh-toggle-threads
314 "d" mh-thread-delete
315 "o" mh-thread-refile)
316
317(gnus-define-keys (mh-limit-map "/" mh-folder-mode-map)
318 "'" mh-narrow-to-tick
319 "?" mh-prefix-help
320 "c" mh-narrow-to-cc
321 "g" mh-narrow-to-range
322 "m" mh-narrow-to-from
323 "s" mh-narrow-to-subject
324 "t" mh-narrow-to-to
325 "w" mh-widen)
326
327(gnus-define-keys (mh-extract-map "X" mh-folder-mode-map)
328 "?" mh-prefix-help
329 "s" mh-store-msg ;shar
330 "u" mh-store-msg) ;uuencode
331
332(gnus-define-keys (mh-digest-map "D" mh-folder-mode-map)
333 " " mh-page-digest
334 "?" mh-prefix-help
335 "\177" mh-page-digest-backwards
336 "b" mh-burst-digest)
337
338(gnus-define-keys (mh-mime-map "K" mh-folder-mode-map)
339 "?" mh-prefix-help
340 "a" mh-mime-save-parts
341 "e" mh-display-with-external-viewer
342 "i" mh-folder-inline-mime-part
343 "o" mh-folder-save-mime-part
344 "t" mh-toggle-mime-buttons
345 "v" mh-folder-toggle-mime-part
346 "\t" mh-next-button
347 [backtab] mh-prev-button
348 "\M-\t" mh-prev-button)
349
350(cond
351 (mh-xemacs-flag
352 (define-key mh-folder-mode-map [button2] 'mh-show-mouse))
353 (t
354 (define-key mh-folder-mode-map [mouse-2] 'mh-show-mouse)))
355
356;; "C-c /" prefix is used in mh-folder-mode by pgp.el and mailcrypt
357
358\f
359
360;;; MH-Folder Help Messages
361
362;; If you add a new prefix, add appropriate text to the nil key.
363
364;; In general, messages are grouped logically. Taking the main commands for
365;; example, the first line is "ways to view messages," the second line is
366;; "things you can do with messages", and the third is "composing" messages.
367
368;; When adding a new prefix, ensure that the help message contains "what" the
369;; prefix is for. For example, if the word "folder" were not present in the
370;; "F" entry, it would not be clear what these commands operated upon.
371(defvar mh-folder-mode-help-messages
372 '((nil "[i]nc, [.]show, [,]show all, [n]ext, [p]revious,\n"
373 "[d]elete, [o]refile, e[x]ecute,\n"
374 "[s]end, [r]eply,\n"
375 "[;]toggle MIME decoding.\n"
376 "Prefix characters:\n [F]older, [S]equence, [J]unk, MIME [K]eys,"
377 "\n [T]hread, [/]limit, e[X]tract, [D]igest, [I]nc spools.")
378
379 (?F "[l]ist; [v]isit folder;\n"
380 "[n]ew messages; [']ticked messages; [s]earch;\n"
381 "[p]ack; [S]ort; [r]escan; [k]ill")
382 (?P "[p]rint message to [f]ile; old-style [l]pr printing;\n"
383 "Toggle printing of [C]olors, [F]aces")
384 (?S "[p]ut message in sequence, [n]arrow, [']narrow to ticked, [w]iden,\n"
385 "[s]equences, [l]ist,\n"
386 "[d]elete message from sequence, [k]ill sequence")
387 (?T "[t]oggle, [d]elete, [o]refile thread")
388 (?/ "Limit to [c]c, ran[g]e, fro[m], [s]ubject, [t]o; [w]iden")
389 (?X "un[s]har, [u]udecode message")
390 (?D "[b]urst digest")
391 (?K "[v]iew, [i]nline, [o]utput/save MIME part; save [a]ll parts; \n"
392 "[TAB] next; [SHIFT-TAB] previous")
393 (?J "[b]lacklist, [w]hitelist message"))
394 "Key binding cheat sheet.
395See `mh-set-help'.")
396
397\f
398
399;;; MH-Folder Font Lock
400
401(defvar mh-folder-font-lock-keywords
402 (list
403 ;; Folders when displaying index buffer
404 (list "^\\+.*"
405 '(0 'mh-search-folder))
406 ;; Marked for deletion
407 (list (concat mh-scan-deleted-msg-regexp ".*")
408 '(0 'mh-folder-deleted))
409 ;; Marked for refile
410 (list (concat mh-scan-refiled-msg-regexp ".*")
411 '(0 'mh-folder-refiled))
412 ;; After subject
413 (list mh-scan-body-regexp
414 '(1 'mh-folder-body nil t))
415 ;; Subject
416 '(mh-folder-font-lock-subject
417 (1 'mh-folder-followup append t)
418 (2 'mh-folder-subject append t))
419 ;; Current message number
420 (list mh-scan-cur-msg-number-regexp
421 '(1 'mh-folder-cur-msg-number))
422 ;; Message number
423 (list mh-scan-good-msg-regexp
424 '(1 'mh-folder-msg-number))
425 ;; Date
426 (list mh-scan-date-regexp
427 '(1 'mh-folder-date))
428 ;; Messages from me (To:)
429 (list mh-scan-rcpt-regexp
430 '(1 'mh-folder-to)
431 '(2 'mh-folder-address))
432 ;; Messages to me
433 (list mh-scan-sent-to-me-sender-regexp
434 '(1 'mh-folder-sent-to-me-hint)
435 '(2 'mh-folder-sent-to-me-sender)))
436 "Keywords (regular expressions) used to fontify the MH-Folder buffer.")
437
438(defun mh-folder-font-lock-subject (limit)
439 "Return MH-E scan subject strings to font-lock between point and LIMIT."
440 (if (not (re-search-forward mh-scan-subject-regexp limit t))
441 nil
442 (if (match-beginning 1)
443 (set-match-data (list (match-beginning 1) (match-end 3)
444 (match-beginning 1) (match-end 3) nil nil))
445 (set-match-data (list (match-beginning 3) (match-end 3)
446 nil nil (match-beginning 3) (match-end 3))))
447 t))
448
449;; Fontify unseen messages in bold.
450
451(defmacro mh-generate-sequence-font-lock (seq prefix face)
452 "Generate the appropriate code to fontify messages in SEQ.
453PREFIX is used to generate unique names for the variables and
454functions defined by the macro. So a different prefix should be
455provided for every invocation.
456FACE is the font-lock face used to display the matching scan lines."
457 (let ((cache (intern (format "mh-folder-%s-seq-cache" prefix)))
458 (func (intern (format "mh-folder-font-lock-%s" prefix))))
459 `(progn
460 (defvar ,cache nil
461 "Internal cache variable used for font-lock in MH-E.
462Should only be non-nil through font-lock stepping, and nil once
463font-lock is done highlighting.")
464 (make-variable-buffer-local ',cache)
465
466 (defun ,func (limit)
467 "Return unseen message lines to font-lock between point and LIMIT."
468 (if (not ,cache) (setq ,cache (mh-seq-msgs (mh-find-seq ,seq))))
469 (let ((cur-msg (mh-get-msg-num nil)))
470 (cond ((not ,cache)
471 nil)
472 ((>= (point) limit) ;Presumably at end of buffer
473 (setq ,cache nil)
474 nil)
475 ((member cur-msg ,cache)
476 (let ((bpoint (progn (beginning-of-line)(point)))
477 (epoint (progn (forward-line 1)(point))))
478 (if (<= limit (point)) (setq ,cache nil))
479 (set-match-data (list bpoint epoint bpoint epoint))
480 t))
481 (t
482 ;; move forward one line at a time, checking each message
483 (while (and (= 0 (forward-line 1))
484 (> limit (point))
485 (not (member (mh-get-msg-num nil) ,cache))))
486 ;; Examine how we must have exited the loop...
487 (let ((cur-msg (mh-get-msg-num nil)))
488 (cond ((or (<= limit (point))
489 (not (member cur-msg ,cache)))
490 (setq ,cache nil)
491 nil)
492 ((member cur-msg ,cache)
493 (let ((bpoint (progn (beginning-of-line) (point)))
494 (epoint (progn (forward-line 1) (point))))
495 (if (<= limit (point)) (setq ,cache nil))
496 (set-match-data
497 (list bpoint epoint bpoint epoint))
498 t))))))))
499
500 (setq mh-folder-font-lock-keywords
501 (append mh-folder-font-lock-keywords
502 (list (list ',func (list 1 '',face 'prepend t))))))))
503
504(mh-generate-sequence-font-lock mh-unseen-seq unseen bold)
505(mh-generate-sequence-font-lock mh-tick-seq tick mh-folder-tick)
506
507\f
508
509;;; MH-Folder Mode
510
511(defmacro mh-remove-xemacs-horizontal-scrollbar ()
512 "Get rid of the horizontal scrollbar that XEmacs insists on putting in."
513 (when mh-xemacs-flag
514 `(if (and (featurep 'scrollbar)
515 (fboundp 'set-specifier))
516 (set-specifier horizontal-scrollbar-visible-p nil
517 (cons (current-buffer) nil)))))
518
dda00b2c 519;; Register mh-folder-mode as supporting which-function-mode...
d5dc8c56 520(mh-require 'which-func nil t)
dda00b2c
BW
521(when (boundp 'which-func-modes)
522 (add-to-list 'which-func-modes 'mh-folder-mode))
523
524;; Shush compiler.
42f8c37f
BW
525(defvar desktop-save-buffer)
526(defvar font-lock-auto-fontify)
527(defvar image-load-path)
528(defvar font-lock-defaults) ; XEmacs
dda00b2c
BW
529
530(defvar mh-folder-buttons-init-flag nil)
531
532;; Ensure new buffers won't get this mode if default-major-mode is nil.
533(put 'mh-folder-mode 'mode-class 'special)
534
535;; Autoload cookie needed by desktop.el
536;;;###autoload
537(define-derived-mode mh-folder-mode fundamental-mode "MH-Folder"
538 "Major MH-E mode for \"editing\" an MH folder scan listing.\\<mh-folder-mode-map>
539
540You can show the message the cursor is pointing to, and step through
541the messages. Messages can be marked for deletion or refiling into
542another folder; these commands are executed all at once with a
543separate command.
544
545Options that control this mode can be changed with
546\\[customize-group]; specify the \"mh\" group. In particular, please
547see the `mh-scan-format-file' option if you wish to modify scan's
548format.
549
550When a folder is visited, the hook `mh-folder-mode-hook' is run.
551
552Ranges
553======
554Many commands that operate on individual messages, such as
555`mh-forward' or `mh-refile-msg' take a RANGE argument. This argument
556can be used in several ways.
557
558If you provide the prefix argument (\\[universal-argument]) to
559these commands, then you will be prompted for the message range.
560This can be any valid MH range which can include messages,
561sequences, and the abbreviations (described in the mh(1) man
562page):
563
564<num1>-<num2>
565 Indicates all messages in the range <num1> to <num2>, inclusive.
566 The range must be nonempty.
567
568<num>:N
569<num>:+N
570<num>:-N
571 Up to N messages beginning with (or ending with) message num. Num
572 may be any of the predefined symbols: first, prev, cur, next or
573 last.
574
575first:N
576prev:N
577next:N
578last:N
579 The first, previous, next or last messages, if they exist.
580
581all
582 All of the messages.
583
584For example, a range that shows all of these things is `1 2 3
5855-10 last:5 unseen'.
586
587If the option `transient-mark-mode' is set to t and you set a
588region in the MH-Folder buffer, then the MH-E command will
589perform the operation on all messages in that region.
590
591\\{mh-folder-mode-map}"
592 (mh-do-in-gnu-emacs
efc27af6 593 (unless mh-folder-buttons-init-flag
44e3f440 594 (let* ((load-path (mh-image-load-path-for-library "mh-e" "mh-logo.xpm"))
f875b154
BW
595 (image-load-path (cons (car load-path)
596 (when (boundp 'image-load-path)
597 image-load-path))))
efc27af6
BW
598 (mh-tool-bar-folder-buttons-init)
599 (setq mh-folder-buttons-init-flag t)))
600 (set (make-local-variable 'tool-bar-map) mh-folder-tool-bar-map))
601 (mh-do-in-xemacs
602 (mh-tool-bar-init :folder))
dda00b2c
BW
603 (make-local-variable 'font-lock-defaults)
604 (setq font-lock-defaults '(mh-folder-font-lock-keywords t))
605 (make-local-variable 'desktop-save-buffer)
606 (setq desktop-save-buffer t)
607 (mh-make-local-vars
608 'mh-colors-available-flag (mh-colors-available-p)
609 ; Do we have colors available
610 'mh-current-folder (buffer-name) ; Name of folder, a string
611 'mh-show-buffer (format "show-%s" (buffer-name)) ; Buffer that displays msgs
612 'mh-folder-filename ; e.g. "/usr/foobar/Mail/inbox/"
613 (file-name-as-directory (mh-expand-file-name (buffer-name)))
614 'mh-display-buttons-for-inline-parts-flag
615 mh-display-buttons-for-inline-parts-flag ; Allow for display of buttons to
616 ; be toggled.
617 'mh-arrow-marker (make-marker) ; Marker where arrow is displayed
618 'overlay-arrow-position nil ; Allow for simultaneous display in
619 'overlay-arrow-string ">" ; different MH-E buffers.
620 'mh-showing-mode nil ; Show message also?
621 'mh-delete-list nil ; List of msgs nums to delete
622 'mh-refile-list nil ; List of folder names in mh-seq-list
623 'mh-seq-list nil ; Alist of (seq . msgs) nums
624 'mh-seen-list nil ; List of displayed messages
625 'mh-next-direction 'forward ; Direction to move to next message
626 'mh-view-ops () ; Stack that keeps track of the order
627 ; in which narrowing/threading has been
628 ; carried out.
629 'mh-folder-view-stack () ; Stack of previous views of the
630 ; folder.
631 'mh-index-data nil ; If the folder was created by a call
632 ; to mh-search, this contains info
633 ; about the search results.
634 'mh-index-previous-search nil ; folder, indexer, search-regexp
635 'mh-index-msg-checksum-map nil ; msg -> checksum map
636 'mh-index-checksum-origin-map nil ; checksum -> ( orig-folder, orig-msg )
637 'mh-index-sequence-search-flag nil ; folder resulted from sequence search
638 'mh-first-msg-num nil ; Number of first msg in buffer
639 'mh-last-msg-num nil ; Number of last msg in buffer
640 'mh-msg-count nil ; Number of msgs in buffer
641 'mh-mode-line-annotation nil ; Indicates message range
642 'mh-sequence-notation-history (make-hash-table)
643 ; Remember what is overwritten by
644 ; mh-note-seq.
645 'imenu-create-index-function 'mh-index-create-imenu-index
646 ; Setup imenu support
647 'mh-previous-window-config nil) ; Previous window configuration
648 (mh-remove-xemacs-horizontal-scrollbar)
649 (setq truncate-lines t)
650 (auto-save-mode -1)
651 (setq buffer-offer-save t)
06e7028b
BW
652 (mh-make-local-hook (mh-write-file-functions))
653 (add-hook (mh-write-file-functions) 'mh-execute-commands nil t)
dda00b2c
BW
654 (make-local-variable 'revert-buffer-function)
655 (make-local-variable 'hl-line-mode) ; avoid pollution
656 (mh-funcall-if-exists hl-line-mode 1)
657 (setq revert-buffer-function 'mh-undo-folder)
4f3a7d02 658 (add-to-list 'minor-mode-alist '(mh-showing-mode " Show"))
dda00b2c
BW
659 (easy-menu-add mh-folder-sequence-menu)
660 (easy-menu-add mh-folder-message-menu)
661 (easy-menu-add mh-folder-folder-menu)
662 (mh-inc-spool-make)
dda00b2c
BW
663 (mh-set-help mh-folder-mode-help-messages)
664 (if (and mh-xemacs-flag
665 font-lock-auto-fontify)
666 (turn-on-font-lock))) ; Force font-lock in XEmacs.
667
668\f
669
670;;; MH-Folder Commands
671
672;; Alphabetical.
673;; See also mh-comp.el, mh-junk.el, mh-mime.el, mh-print.el,
674;; mh-search.el, and mh-seq.el.
675
676;;;###mh-autoload
677(defun mh-delete-msg (range)
678 "Delete RANGE\\<mh-folder-mode-map>.
679
680To mark a message for deletion, use this command. A \"D\" is
681placed by the message in the scan window, and the next undeleted
682message is displayed. If the previous command had been
683\\[mh-previous-undeleted-msg], then the next message displayed is
684the first undeleted message previous to the message just deleted.
685Use \\[mh-next-undeleted-msg] to force subsequent
686\\[mh-delete-msg] commands to move forward to the next undeleted
687message after deleting the message under the cursor.
688
689The hook `mh-delete-msg-hook' is called after you mark a message
690for deletion. For example, a past maintainer of MH-E used this
691once when he kept statistics on his mail usage.
692
693Check the documentation of `mh-interactive-range' to see how
694RANGE is read in interactive use."
695 (interactive (list (mh-interactive-range "Delete")))
696 (mh-delete-msg-no-motion range)
697 (if (looking-at mh-scan-deleted-msg-regexp)
698 (mh-next-msg)))
699
700;;;###mh-autoload
701(defun mh-delete-msg-no-motion (range)
702 "Delete RANGE, don't move to next message.
703
704This command marks the RANGE for deletion but leaves the cursor
705at the current message in case you wish to perform other
706operations on the message.
707
708Check the documentation of `mh-interactive-range' to see how
709RANGE is read in interactive use."
710 (interactive (list (mh-interactive-range "Delete")))
711 (mh-iterate-on-range () range
712 (mh-delete-a-msg nil)))
713
714;;;###mh-autoload
715(defun mh-execute-commands ()
716 "Process outstanding delete and refile requests\\<mh-folder-mode-map>.
717
718If you've marked messages to be deleted or refiled and you want
719to go ahead and delete or refile the messages, use this command.
720Many MH-E commands that may affect the numbering of the
721messages (such as \\[mh-rescan-folder] or \\[mh-pack-folder])
722will ask if you want to process refiles or deletes first and then
723either run this command for you or undo the pending refiles and
798b73dd 724deletes.
dda00b2c
BW
725
726This function runs `mh-before-commands-processed-hook' before the
727commands are processed and `mh-after-commands-processed-hook'
728after the commands are processed."
729 (interactive)
730 (if mh-folder-view-stack (mh-widen t))
731 (mh-process-commands mh-current-folder)
732 (mh-set-scan-mode)
733 (mh-goto-cur-msg) ; after mh-set-scan-mode for efficiency
734 (mh-make-folder-mode-line)
735 t) ; return t for write-file-functions
736
737;;;###mh-autoload
738(defun mh-first-msg ()
739 "Display first message."
740 (interactive)
741 (goto-char (point-min))
742 (while (and (not (eobp)) (not (looking-at mh-scan-valid-regexp)))
743 (forward-line 1)))
744
745;;;###mh-autoload
746(defun mh-goto-msg (number &optional no-error-if-no-message dont-show)
747 "Go to a message\\<mh-folder-mode-map>.
748
749You can enter the message NUMBER either before or after typing
750\\[mh-goto-msg]. In the latter case, Emacs prompts you.
751
752In a program, optional non-nil second argument NO-ERROR-IF-NO-MESSAGE
753means return nil instead of signaling an error if message does not
754exist\; in this case, the cursor is positioned near where the message
755would have been. Non-nil third argument DONT-SHOW means not to show
756the message."
757 (interactive "NGo to message: ")
758 (setq number (prefix-numeric-value number))
759 (let ((point (point))
760 (return-value t))
761 (goto-char (point-min))
762 (unless (re-search-forward (format (mh-scan-msg-search-regexp) number)
763 nil t)
764 (goto-char point)
765 (unless no-error-if-no-message
766 (error "No message %d" number))
767 (setq return-value nil))
768 (beginning-of-line)
769 (or dont-show (not return-value) (mh-maybe-show number))
770 return-value))
771
772;;;###mh-autoload
773(defun mh-inc-folder (&optional file folder)
774 "Incorporate new mail into a folder.
775
776You can incorporate mail from any file into the current folder by
777specifying a prefix argument; you'll be prompted for the name of
778the FILE to use as well as the destination FOLDER
779
780The hook `mh-inc-folder-hook' is run after incorporating new
781mail.
782
783Do not call this function from outside MH-E; use \\[mh-rmail]
784instead."
785 (interactive (list (if current-prefix-arg
786 (expand-file-name
787 (read-file-name "inc mail from file: "
788 mh-user-path)))
789 (if current-prefix-arg
790 (mh-prompt-for-folder "inc mail into" mh-inbox t))))
791 (if (not folder)
792 (setq folder mh-inbox))
793 (let ((threading-needed-flag nil))
794 (let ((config (current-window-configuration)))
795 (when (and mh-show-buffer (get-buffer mh-show-buffer))
796 (delete-windows-on mh-show-buffer))
797 (cond ((not (get-buffer folder))
798 (mh-make-folder folder)
799 (setq threading-needed-flag mh-show-threads-flag)
800 (setq mh-previous-window-config config))
801 ((not (eq (current-buffer) (get-buffer folder)))
802 (switch-to-buffer folder)
803 (setq mh-previous-window-config config))))
804 (mh-get-new-mail file)
805 (when (and threading-needed-flag
806 (save-excursion
807 (goto-char (point-min))
808 (or (null mh-large-folder)
809 (not (equal (forward-line (1+ mh-large-folder)) 0))
810 (and (message "Not threading since the number of messages exceeds `mh-large-folder'")
811 nil))))
812 (mh-toggle-threads))
813 (beginning-of-line)
814 (if (and mh-showing-mode (looking-at mh-scan-valid-regexp)) (mh-show))
815 (run-hooks 'mh-inc-folder-hook)))
816
817;;;###mh-autoload
818(defun mh-last-msg ()
819 "Display last message."
820 (interactive)
821 (goto-char (point-max))
822 (while (and (not (bobp)) (not (looking-at mh-scan-valid-regexp)))
823 (forward-line -1))
824 (mh-recenter nil))
825
826;;;###mh-autoload
827(defun mh-modify (&optional message)
828 "Edit message.
829
830There are times when you need to edit a message. For example, you
831may need to fix a broken Content-Type header field. You can do
832this with this command. It displays the raw message in an
833editable buffer. When you are done editing, save and kill the
834buffer as you would any other.
835
836From a program, edit MESSAGE; nil means edit current message."
837 (interactive)
838 (let* ((message (or message (mh-get-msg-num t)))
839 (msg-filename (mh-msg-filename message))
840 edit-buffer)
841 (when (not (file-exists-p msg-filename))
842 (error "Message %d does not exist" message))
843
844 ;; Invalidate the show buffer if it is showing the same message that is
845 ;; to be edited.
846 (when (and (buffer-live-p (get-buffer mh-show-buffer))
847 (equal (save-excursion (set-buffer mh-show-buffer)
848 buffer-file-name)
849 msg-filename))
850 (mh-invalidate-show-buffer))
851
852 ;; Edit message
853 (find-file msg-filename)
854 (setq edit-buffer (current-buffer))
855
856 ;; Set buffer properties
857 (mh-letter-mode)
858 (use-local-map text-mode-map)
859
860 ;; Just show the edit buffer...
861 (delete-other-windows)
862 (switch-to-buffer edit-buffer)))
863
864;;;###mh-autoload
865(defun mh-next-button (&optional backward-flag)
866 "Go to the next button.
867
868If the end of the buffer is reached then the search wraps over to
869the start of the buffer.
870
871If an optional prefix argument BACKWARD-FLAG is given, the cursor
872will move to the previous button."
873 (interactive (list current-prefix-arg))
874 (unless mh-showing-mode
875 (mh-show))
876 (mh-in-show-buffer (mh-show-buffer)
877 (mh-goto-next-button backward-flag)))
878
879;;;###mh-autoload
880(defun mh-next-undeleted-msg (&optional count wait-after-complaining-flag)
881 "Display next message.
882
883This command can be given a prefix argument COUNT to specify how
884many unread messages to skip.
885
886In a program, pause for a second after printing message if we are
887at the last undeleted message and optional argument
888WAIT-AFTER-COMPLAINING-FLAG is non-nil."
889 (interactive "p")
890 (setq mh-next-direction 'forward)
891 (forward-line 1)
892 (cond ((re-search-forward mh-scan-good-msg-regexp nil t count)
893 (beginning-of-line)
894 (mh-maybe-show))
895 (t (forward-line -1)
896 (message "No more undeleted messages")
897 (if wait-after-complaining-flag (sit-for 1)))))
898
899;;;###mh-autoload
900(defun mh-next-unread-msg (&optional count)
901 "Display next unread message.
902
903This command can be given a prefix argument COUNT to specify how
904many unread messages to skip."
905 (interactive "p")
906 (unless (> count 0)
907 (error "The function `mh-next-unread-msg' expects positive argument"))
908 (setq count (1- count))
909 (let ((unread-sequence (reverse (cdr (assoc mh-unseen-seq mh-seq-list))))
910 (cur-msg (mh-get-msg-num nil)))
911 (cond ((and (not cur-msg) (not (bobp))
912 ;; If we are at the end of the buffer back up one line and go
913 ;; to unread message after that.
914 (progn
915 (forward-line -1)
916 (setq cur-msg (mh-get-msg-num nil)))
917 nil))
918 ((or (null unread-sequence) (not cur-msg))
919 ;; No unread message or there aren't any messages in buffer...
920 (message "No more unread messages"))
921 ((progn
922 ;; Skip messages
923 (while (and unread-sequence (>= cur-msg (car unread-sequence)))
924 (setq unread-sequence (cdr unread-sequence)))
925 (while (> count 0)
926 (setq unread-sequence (cdr unread-sequence))
927 (setq count (1- count)))
928 (not (car unread-sequence)))
929 (message "No more unread messages"))
930 (t (loop for msg in unread-sequence
931 when (mh-goto-msg msg t) return nil
932 finally (message "No more unread messages"))))))
933
934;;;###mh-autoload
935(defun mh-page-msg (&optional lines)
936 "Display next page in message.
937
938You can give this command a prefix argument that specifies the
939number of LINES to scroll. This command will also show the next
940undeleted message if it is used at the bottom of a message."
941 (interactive "P")
942 (if mh-showing-mode
943 (if mh-page-to-next-msg-flag
944 (if (equal mh-next-direction 'backward)
945 (mh-previous-undeleted-msg)
946 (mh-next-undeleted-msg))
947 (if (mh-in-show-buffer (mh-show-buffer)
948 (pos-visible-in-window-p (point-max)))
949 (progn
950 (message
951 "End of message (Type %s to read %s undeleted message)"
952 (single-key-description last-input-event)
953 (if (equal mh-next-direction 'backward)
954 "previous"
955 "next"))
956 (setq mh-page-to-next-msg-flag t))
957 (scroll-other-window lines)))
958 (mh-show)))
959
960;;;###mh-autoload
961(defun mh-prev-button ()
962 "Go to the previous button.
963
964If the beginning of the buffer is reached then the search wraps
965over to the end of the buffer."
966 (interactive)
967 (mh-next-button t))
968
969;;;###mh-autoload
970(defun mh-previous-page (&optional lines)
971 "Display next page in message.
972
973You can give this command a prefix argument that specifies the
974number of LINES to scroll."
975 (interactive "P")
976 (mh-in-show-buffer (mh-show-buffer)
977 (scroll-down lines)))
978
979;;;###mh-autoload
980(defun mh-previous-undeleted-msg (&optional count wait-after-complaining-flag)
981 "Display previous message.
982
983This command can be given a prefix argument COUNT to specify how
984many unread messages to skip.
985
986In a program, pause for a second after printing message if we are
987at the last undeleted message and optional argument
988WAIT-AFTER-COMPLAINING-FLAG is non-nil."
989 (interactive "p")
990 (setq mh-next-direction 'backward)
991 (beginning-of-line)
992 (cond ((re-search-backward mh-scan-good-msg-regexp nil t count)
993 (mh-maybe-show))
994 (t (message "No previous undeleted message")
995 (if wait-after-complaining-flag (sit-for 1)))))
996
997;;;###mh-autoload
998(defun mh-previous-unread-msg (&optional count)
999 "Display previous unread message.
1000
1001This command can be given a prefix argument COUNT to specify how
1002many unread messages to skip."
1003 (interactive "p")
1004 (unless (> count 0)
1005 (error "The function `mh-previous-unread-msg' expects positive argument"))
1006 (setq count (1- count))
1007 (let ((unread-sequence (cdr (assoc mh-unseen-seq mh-seq-list)))
1008 (cur-msg (mh-get-msg-num nil)))
1009 (cond ((and (not cur-msg) (not (bobp))
1010 ;; If we are at the end of the buffer back up one line and go
1011 ;; to unread message after that.
1012 (progn
1013 (forward-line -1)
1014 (setq cur-msg (mh-get-msg-num nil)))
1015 nil))
1016 ((or (null unread-sequence) (not cur-msg))
1017 ;; No unread message or there aren't any messages in buffer...
1018 (message "No more unread messages"))
1019 ((progn
1020 ;; Skip count messages...
1021 (while (and unread-sequence (>= (car unread-sequence) cur-msg))
1022 (setq unread-sequence (cdr unread-sequence)))
1023 (while (> count 0)
1024 (setq unread-sequence (cdr unread-sequence))
1025 (setq count (1- count)))
1026 (not (car unread-sequence)))
1027 (message "No more unread messages"))
1028 (t (loop for msg in unread-sequence
1029 when (mh-goto-msg msg t) return nil
1030 finally (message "No more unread messages"))))))
1031
1032;;;###mh-autoload
1033(defun mh-quit ()
1034 "Quit the current MH-E folder.
1035
1036When you want to quit using MH-E and go back to editing, you can use
1037this command. This buries the buffers of the current MH-E folder and
1038restores the buffers that were present when you first ran
1039\\[mh-rmail]. It also removes any MH-E working buffers whose name
1040begins with \" *mh-\" or \"*MH-E \". You can later restore your MH-E
1041session by selecting the \"+inbox\" buffer or by running \\[mh-rmail]
1042again.
1043
1044The two hooks `mh-before-quit-hook' and `mh-quit-hook' are called by
1045this function. The former one is called before the quit occurs, so you
1046might use it to perform any MH-E operations; you could perform some
1047query and abort the quit or call `mh-execute-commands', for example.
1048The latter is not run in an MH-E context, so you might use it to
1049modify the window setup."
1050 (interactive)
1051 (run-hooks 'mh-before-quit-hook)
1052 (let ((show-buffer (get-buffer mh-show-buffer)))
1053 (when show-buffer
1054 (kill-buffer show-buffer)))
1055 (mh-update-sequences)
1056 (mh-destroy-postponed-handles)
1057 (bury-buffer (current-buffer))
1058
1059 ;; Delete all MH-E temporary and working buffers.
1060 (dolist (buffer (buffer-list))
1061 (when (or (string-match "^ \\*mh-" (buffer-name buffer))
1062 (string-match "^\\*MH-E " (buffer-name buffer)))
1063 (kill-buffer buffer)))
1064
1065 (if mh-previous-window-config
1066 (set-window-configuration mh-previous-window-config))
1067 (run-hooks 'mh-quit-hook))
1068
1069;;;###mh-autoload
1070(defun mh-refile-msg (range folder &optional dont-update-last-destination-flag)
1071 "Refile (output) RANGE into FOLDER.
1072
1073You are prompted for the folder name. Note that this command can also
1074be used to create folders. If you specify a folder that does not
1075exist, you will be prompted to create it.
1076
1077The hook `mh-refile-msg-hook' is called after a message is marked to
1078be refiled.
1079
1080Check the documentation of `mh-interactive-range' to see how RANGE is
1081read in interactive use.
1082
1083In a program, the variables `mh-last-destination' and
1084`mh-last-destination-folder' are not updated if
1085DONT-UPDATE-LAST-DESTINATION-FLAG is non-nil."
1086 (interactive (list (mh-interactive-range "Refile")
1087 (intern (mh-prompt-for-refile-folder))))
1088 (unless dont-update-last-destination-flag
1089 (setq mh-last-destination (cons 'refile folder)
1090 mh-last-destination-folder mh-last-destination))
1091 (mh-iterate-on-range () range
1092 (mh-refile-a-msg nil folder))
1093 (when (looking-at mh-scan-refiled-msg-regexp) (mh-next-msg)))
1094
1095;;;###mh-autoload
1096(defun mh-refile-or-write-again (range &optional interactive-flag)
1097 "Repeat last output command.
1098
1099If you are refiling several messages into the same folder, you
1100can use this command to repeat the last
1101refile (\\[mh-refile-msg]) or write (\\[mh-write-msg-to-file]).
1102You can use a range.
1103
1104Check the documentation of `mh-interactive-range' to see how RANGE is
1105read in interactive use.
1106
1107In a program, a non-nil INTERACTIVE-FLAG means that the function was
1108called interactively."
1109 (interactive (list (mh-interactive-range "Redo") t))
1110 (if (null mh-last-destination)
1111 (error "No previous refile or write"))
1112 (cond ((eq (car mh-last-destination) 'refile)
1113 (mh-refile-msg range (cdr mh-last-destination))
1114 (message "Destination folder: %s" (cdr mh-last-destination)))
1115 (t
1116 (mh-iterate-on-range msg range
1117 (apply 'mh-write-msg-to-file msg (cdr mh-last-destination)))
1118 (mh-next-msg interactive-flag))))
1119
1120;;;###mh-autoload
1121(defun mh-rescan-folder (&optional range dont-exec-pending)
1122 "Rescan folder\\<mh-folder-mode-map>.
1123
1124This command is useful to grab all messages in your \"+inbox\" after
1125processing your new mail for the first time. If you don't want to
1126rescan the entire folder, this command will accept a RANGE. Check the
1127documentation of `mh-interactive-range' to see how RANGE is read in
1128interactive use.
1129
1130This command will ask if you want to process refiles or deletes first
1131and then either run \\[mh-execute-commands] for you or undo the
798b73dd 1132pending refiles and deletes.
dda00b2c
BW
1133
1134In a program, the processing of outstanding commands is not performed
1135if DONT-EXEC-PENDING is non-nil."
1136 (interactive (list (if current-prefix-arg
1137 (mh-read-range "Rescan" mh-current-folder t nil t
1138 mh-interpret-number-as-range-flag)
1139 nil)))
1140 (setq mh-next-direction 'forward)
1141 (let ((threaded-flag (memq 'unthread mh-view-ops))
1142 (msg-num (mh-get-msg-num nil)))
1143 (mh-scan-folder mh-current-folder (or range "all") dont-exec-pending)
1144 ;; If there isn't a cur sequence, mh-scan-folder goes to the first message.
1145 ;; Try to stay where we were.
1146 (if (null (car (mh-seq-to-msgs 'cur)))
1147 (mh-goto-msg msg-num t t))
1148 (cond (threaded-flag (mh-toggle-threads))
1149 (mh-index-data (mh-index-insert-folder-headers)))))
1150
1151(defun mh-show-mouse (event)
1152 "Move point to mouse EVENT and show message."
1153 (interactive "e")
1154 (mouse-set-point event)
1155 (mh-show))
1156
1157;;;###mh-autoload
1158(defun mh-toggle-showing ()
1159 "Toggle between MH-Folder and MH-Folder Show modes.
1160
1161This command switches between MH-Folder mode and MH-Folder Show
1162mode. MH-Folder mode turns off the associated show buffer so that
1163you can perform operations on the messages quickly without
1164reading them. This is an excellent way to prune out your junk
1165mail or to refile a group of messages to another folder for later
1166examination."
1167 (interactive)
1168 (if mh-showing-mode
1169 (mh-set-scan-mode)
1170 (mh-show)))
1171
1172;;;###mh-autoload
1173(defun mh-undo (range)
1174 "Undo pending deletes or refiles in RANGE.
1175
1176If you've deleted a message or refiled it, but changed your mind,
1177you can cancel the action before you've executed it. Use this
1178command to undo a refile on or deletion of a single message. You
1179can also undo refiles and deletes for messages that are found in
1180a given RANGE.
1181
1182Check the documentation of `mh-interactive-range' to see how
1183RANGE is read in interactive use."
1184 (interactive (list (mh-interactive-range "Undo")))
1185 (cond ((numberp range)
1186 (let ((original-position (point)))
1187 (beginning-of-line)
1188 (while (not (or (looking-at mh-scan-deleted-msg-regexp)
1189 (looking-at mh-scan-refiled-msg-regexp)
1190 (and (eq mh-next-direction 'forward) (bobp))
1191 (and (eq mh-next-direction 'backward)
1192 (save-excursion (forward-line) (eobp)))))
1193 (forward-line (if (eq mh-next-direction 'forward) -1 1)))
1194 (if (or (looking-at mh-scan-deleted-msg-regexp)
1195 (looking-at mh-scan-refiled-msg-regexp))
1196 (progn
1197 (mh-undo-msg (mh-get-msg-num t))
1198 (mh-maybe-show))
1199 (goto-char original-position)
1200 (error "Nothing to undo"))))
1201 (t (mh-iterate-on-range () range
1202 (mh-undo-msg nil))))
1203 (if (not (mh-outstanding-commands-p))
1204 (mh-set-folder-modified-p nil)))
1205
1206;;;###mh-autoload
1207(defun mh-visit-folder (folder &optional range index-data)
1208 "Visit FOLDER.
1209
1210When you want to read the messages that you have refiled into folders,
1211use this command to visit the folder. You are prompted for the folder
1212name.
1213
1214The folder buffer will show just unseen messages if there are any;
1215otherwise, it will show all the messages in the buffer as long there
1216are fewer than `mh-large-folder' messages. If there are more, then you
1217are prompted for a range of messages to scan.
1218
1219You can provide a prefix argument in order to specify a RANGE of
1220messages to show when you visit the folder. In this case, regions are
1221not used to specify the range and `mh-large-folder' is ignored. Check
1222the documentation of `mh-interactive-range' to see how RANGE is read
1223in interactive use.
1224
1225Note that this command can also be used to create folders. If you
1226specify a folder that does not exist, you will be prompted to create
1227it.
1228
1229Do not call this function from outside MH-E; use \\[mh-rmail] instead.
1230
1231If, in a program, RANGE is nil (the default), then all messages in
1232FOLDER are displayed. If an index buffer is being created then
1233INDEX-DATA is used to initialize the index buffer specific data
1234structures."
1235 (interactive (let ((folder-name (mh-prompt-for-folder "Visit" mh-inbox t)))
1236 (list folder-name
1237 (mh-read-range "Scan" folder-name t nil
1238 current-prefix-arg
1239 mh-interpret-number-as-range-flag))))
1240 (let ((config (current-window-configuration))
1241 (current-buffer (current-buffer))
1242 (threaded-view-flag mh-show-threads-flag))
1243 (delete-other-windows)
1244 (save-excursion
1245 (when (get-buffer folder)
1246 (set-buffer folder)
1247 (setq threaded-view-flag (memq 'unthread mh-view-ops))))
1248 (when index-data
1249 (mh-make-folder folder)
1250 (setq mh-index-data (car index-data)
1251 mh-index-msg-checksum-map (make-hash-table :test #'equal)
1252 mh-index-checksum-origin-map (make-hash-table :test #'equal))
1253 (mh-index-update-maps folder (cadr index-data))
1254 (mh-index-create-sequences))
1255 (mh-scan-folder folder (or range "all"))
1256 (cond ((and threaded-view-flag
1257 (save-excursion
1258 (goto-char (point-min))
1259 (or (null mh-large-folder)
1260 (not (equal (forward-line (1+ mh-large-folder)) 0))
1261 (and (message "Not threading since the number of messages exceeds `mh-large-folder'")
1262 nil))))
1263 (mh-toggle-threads))
1264 (mh-index-data
1265 (mh-index-insert-folder-headers)))
1266 (unless (eq current-buffer (current-buffer))
1267 (setq mh-previous-window-config config)))
1268 nil)
1269
1270;;;###mh-autoload
1271(defun mh-write-msg-to-file (message file no-header)
1272 "Append MESSAGE to end of FILE\\<mh-folder-mode-map>.
1273
1274You are prompted for the filename. If the file already exists,
1275the message is appended to it. You can also write the message to
1276the file without the header by specifying a prefix argument
1277NO-HEADER. Subsequent writes to the same file can be made with
1278the command \\[mh-refile-or-write-again]."
1279 (interactive
1280 (list (mh-get-msg-num t)
1281 (let ((default-dir (if (eq 'write (car mh-last-destination-write))
1282 (file-name-directory
1283 (car (cdr mh-last-destination-write)))
1284 default-directory)))
1285 (read-file-name (format "Save message%s in file: "
1286 (if current-prefix-arg " body" ""))
1287 default-dir
1288 (if (eq 'write (car mh-last-destination-write))
1289 (car (cdr mh-last-destination-write))
1290 (expand-file-name "mail.out" default-dir))))
1291 current-prefix-arg))
1292 (let ((msg-file-to-output (mh-msg-filename message))
1293 (output-file (mh-expand-file-name file)))
1294 (setq mh-last-destination (list 'write file (if no-header 'no-header))
1295 mh-last-destination-write mh-last-destination)
1296 (save-excursion
1297 (set-buffer (get-buffer-create mh-temp-buffer))
1298 (erase-buffer)
1299 (insert-file-contents msg-file-to-output)
1300 (goto-char (point-min))
1301 (if no-header (search-forward "\n\n"))
1302 (append-to-file (point) (point-max) output-file))))
1303
1304;;;###mh-autoload
1305(defun mh-update-sequences ()
1306 "Flush MH-E's state out to MH.
1307
1308This function updates the sequence specified by your
1309\"Unseen-Sequence:\" profile component, \"cur\", and the sequence
1310listed by the `mh-tick-seq' option which is \"tick\" by default.
1311The message at the cursor is used for \"cur\"."
1312 (interactive)
1313 ;; mh-update-sequences is the opposite of mh-read-folder-sequences,
1314 ;; which updates MH-E's state from MH.
1315 (let ((folder-set (mh-update-unseen))
1316 (new-cur (mh-get-msg-num nil)))
1317 (if new-cur
1318 (let ((seq-entry (mh-find-seq 'cur)))
1319 (mh-remove-cur-notation)
1320 (setcdr seq-entry
1321 (list new-cur)) ;delete-seq-locally, add-msgs-to-seq
1322 (mh-define-sequence 'cur (list new-cur))
1323 (beginning-of-line)
1324 (if (looking-at mh-scan-good-msg-regexp)
1325 (mh-notate-cur)))
1326 (or folder-set
1327 (save-excursion
1328 ;; psg - mh-current-folder is nil if mh-summary-height < 4 !
1329 ;; So I added this sanity check.
1330 (if (stringp mh-current-folder)
1331 (mh-exec-cmd-quiet t "folder" mh-current-folder "-fast")
1332 (mh-exec-cmd-quiet t "folder" "-fast")))))))
1333
1334\f
1335
1336;;; Support Routines
1337
1338(defun mh-get-new-mail (maildrop-name)
1339 "Read new mail from MAILDROP-NAME into the current buffer.
1340Return in the current buffer."
1341 (let ((point-before-inc (point))
1342 (folder mh-current-folder)
1343 (new-mail-flag nil))
1344 (with-mh-folder-updating (t)
1345 (if maildrop-name
1346 (message "inc %s -file %s..." folder maildrop-name)
1347 (message "inc %s..." folder))
1348 (setq mh-next-direction 'forward)
1349 (goto-char (point-max))
1350 (mh-remove-cur-notation)
1351 (let ((start-of-inc (point)))
1352 (if maildrop-name
1353 ;; I think MH 5 used "-ms-file" instead of "-file",
1354 ;; which would make inc'ing from maildrops fail.
1355 (mh-exec-cmd-output mh-inc-prog nil folder
1356 (mh-scan-format)
1357 "-file" (expand-file-name maildrop-name)
1358 "-width" (window-width)
1359 "-truncate")
1360 (mh-exec-cmd-output mh-inc-prog nil
1361 (mh-scan-format)
1362 "-width" (window-width)))
1363 (if maildrop-name
1364 (message "inc %s -file %s...done" folder maildrop-name)
1365 (message "inc %s...done" folder))
1366 (goto-char start-of-inc)
1367 (cond ((save-excursion
1368 (re-search-forward "^inc: no mail" nil t))
1369 (message "No new mail%s%s" (if maildrop-name " in " "")
1370 (if maildrop-name maildrop-name "")))
1371 ((and (when mh-folder-view-stack
1372 (let ((saved-text (buffer-substring-no-properties
1373 start-of-inc (point-max))))
1374 (delete-region start-of-inc (point-max))
1375 (unwind-protect (mh-widen t)
1376 (mh-remove-cur-notation)
1377 (goto-char (point-max))
1378 (setq start-of-inc (point))
1379 (insert saved-text)
1380 (goto-char start-of-inc))))
1381 nil))
1382 ((re-search-forward "^inc:" nil t) ; Error messages
1383 (error "Error incorporating mail"))
1384 ((and
1385 (equal mh-scan-format-file t)
1386 mh-adaptive-cmd-note-flag
1387 ;; Have we reached an edge condition?
1388 (save-excursion
1389 (re-search-forward mh-scan-msg-overflow-regexp nil 0 1))
1390 (setq start-of-inc (mh-generate-new-cmd-note folder))
1391 nil))
1392 (t
1393 (setq new-mail-flag t)))
1394 (keep-lines mh-scan-valid-regexp) ; Flush random scan lines
1395 (let* ((sequences (mh-read-folder-sequences folder t))
1396 (new-cur (assoc 'cur sequences))
1397 (new-unseen (assoc mh-unseen-seq sequences)))
1398 (unless (assoc 'cur mh-seq-list)
1399 (push (list 'cur) mh-seq-list))
1400 (unless (assoc mh-unseen-seq mh-seq-list)
1401 (push (list mh-unseen-seq) mh-seq-list))
1402 (setcdr (assoc 'cur mh-seq-list) (cdr new-cur))
1403 (setcdr (assoc mh-unseen-seq mh-seq-list) (cdr new-unseen)))
1404 (when (equal (point-max) start-of-inc)
1405 (mh-notate-cur))
1406 (if new-mail-flag
1407 (progn
1408 (mh-make-folder-mode-line)
1409 (when (mh-speed-flists-active-p)
1410 (mh-speed-flists t mh-current-folder))
1411 (when (memq 'unthread mh-view-ops)
1412 (mh-thread-inc folder start-of-inc))
1413 (mh-goto-cur-msg))
1414 (goto-char point-before-inc))
1415 (mh-notate-user-sequences (cons start-of-inc (point-max)))))))
1416
1417(defun mh-generate-new-cmd-note (folder)
1418 "Fix the `mh-cmd-note' value for this FOLDER.
1419
1420After doing an `mh-get-new-mail' operation in this FOLDER, at least
1421one line that looks like a truncated message number was found.
1422
1423Remove the text added by the last `mh-inc' command. It should be the
1424messages cur-last. Call `mh-set-cmd-note', adjusting the notation
1425column with the width of the largest message number in FOLDER.
1426
1427Reformat the message number width on each line in the buffer and trim
1428the line length to fit in the window.
1429
1430Rescan the FOLDER in the range cur-last in order to display the
1431messages that were removed earlier. They should all fit in the scan
1432line now with no message truncation."
1433 (save-excursion
1434 (let ((maxcol (1- (window-width)))
1435 (old-cmd-note mh-cmd-note)
1436 mh-cmd-note-fmt
1437 msgnum)
1438 ;; Nuke all of the lines just added by the last inc
1439 (delete-char (- (point-max) (point)))
1440 ;; Update the current buffer to reflect the new mh-cmd-note
1441 ;; value needed to display messages.
1442 (mh-set-cmd-note (mh-msg-num-width-to-column (mh-msg-num-width folder)))
1443 (setq mh-cmd-note-fmt (concat "%" (format "%d" mh-cmd-note) "d"))
1444 ;; Cleanup the messages that are in the buffer right now
1445 (goto-char (point-min))
1446 (cond ((memq 'unthread mh-view-ops)
1447 (mh-thread-add-spaces (- mh-cmd-note old-cmd-note)))
1448 (t (while (re-search-forward (mh-scan-msg-number-regexp) nil 0 1)
1449 ;; reformat the number to fix in mh-cmd-note columns
1450 (setq msgnum (string-to-number
1451 (buffer-substring
1452 (match-beginning 1) (match-end 1))))
1453 (replace-match (format mh-cmd-note-fmt msgnum))
1454 ;; trim the line to fix in the window
1455 (end-of-line)
1456 (let ((eol (point)))
1457 (move-to-column maxcol)
1458 (if (<= (point) eol)
1459 (delete-char (- eol (point))))))))
1460 ;; now re-read the lost messages
1461 (goto-char (point-max))
1462 (prog1 (point)
1463 (mh-regenerate-headers "cur-last" t)))))
1464
1465;;;###mh-autoload
1466(defun mh-goto-cur-msg (&optional minimal-changes-flag)
1467 "Position the cursor at the current message.
1468When optional argument MINIMAL-CHANGES-FLAG is non-nil, the
1469function doesn't recenter the folder buffer."
1470 (let ((cur-msg (car (mh-seq-to-msgs 'cur))))
1471 (cond ((and cur-msg
1472 (mh-goto-msg cur-msg t t))
1473 (unless minimal-changes-flag
1474 (mh-notate-cur)
1475 (mh-recenter 0)
1476 (mh-maybe-show cur-msg)))
1477 (t
1478 (setq overlay-arrow-position nil)
1479 (message "No current message")))))
1480
1481;;;###mh-autoload
1482(defun mh-recenter (arg)
1483 "Like recenter but with three improvements:
1484
1485- At the end of the buffer it tries to show fewer empty lines.
1486
1487- operates only if the current buffer is in the selected window.
1488 (Commands like `save-some-buffers' can make this false.)
1489
1490- nil ARG means recenter as if prefix argument had been given."
1491 (cond ((not (eq (get-buffer-window (current-buffer)) (selected-window)))
1492 nil)
1493 ((= (point-max) (save-excursion
1494 (forward-line (- (/ (window-height) 2) 2))
1495 (point)))
1496 (let ((lines-from-end 2))
1497 (save-excursion
1498 (while (> (point-max) (progn (forward-line) (point)))
1499 (incf lines-from-end)))
1500 (recenter (- lines-from-end))))
1501 ;; '(4) is the same as C-u prefix argument.
1502 (t (recenter (or arg '(4))))))
1503
1504(defun mh-update-unseen ()
1505 "Synchronize the unseen sequence with MH.
1506Return non-nil iff the MH folder was set.
1507The hook `mh-unseen-updated-hook' is called after the unseen sequence
1508is updated."
1509 (if mh-seen-list
1510 (let* ((unseen-seq (mh-find-seq mh-unseen-seq))
1511 (unseen-msgs (mh-seq-msgs unseen-seq)))
1512 (if unseen-msgs
1513 (progn
1514 (mh-undefine-sequence mh-unseen-seq mh-seen-list)
1515 (run-hooks 'mh-unseen-updated-hook)
1516 (while mh-seen-list
1517 (setq unseen-msgs (delq (car mh-seen-list) unseen-msgs))
1518 (setq mh-seen-list (cdr mh-seen-list)))
1519 (setcdr unseen-seq unseen-msgs)
1520 t) ;since we set the folder
1521 (setq mh-seen-list nil)))))
1522
1523;;;###mh-autoload
1524(defun mh-outstanding-commands-p ()
1525 "Return non-nil if there are outstanding deletes or refiles."
1526 (save-excursion
1527 (when (eq major-mode 'mh-show-mode)
1528 (set-buffer mh-show-folder-buffer))
1529 (or mh-delete-list mh-refile-list)))
1530
1531;;;###mh-autoload
1532(defun mh-set-folder-modified-p (flag)
1533 "Mark current folder as modified or unmodified according to FLAG."
1534 (set-buffer-modified-p flag))
1535
1536(defun mh-process-commands (folder)
1537 "Process outstanding commands for FOLDER.
1538
1539This function runs `mh-before-commands-processed-hook' before the
1540commands are processed and `mh-after-commands-processed-hook'
1541after the commands are processed."
1542 (message "Processing deletes and refiles for %s..." folder)
1543 (set-buffer folder)
1544 (with-mh-folder-updating (nil)
1545 ;; Run the before hook -- the refile and delete lists are still valid
1546 (run-hooks 'mh-before-commands-processed-hook)
1547
1548 ;; Update the unseen sequence if it exists
1549 (mh-update-unseen)
1550
1551 (let ((redraw-needed-flag mh-index-data)
1552 (folders-changed (list mh-current-folder))
1553 (seq-map (and mh-refile-list mh-refile-preserves-sequences-flag
1554 (mh-create-sequence-map mh-seq-list)))
1555 (dest-map (and mh-refile-list mh-refile-preserves-sequences-flag
1556 (make-hash-table))))
1557 ;; Remove invalid scan lines if we are in an index folder and then remove
1558 ;; the real messages
1559 (when mh-index-data
1560 (mh-index-delete-folder-headers)
1561 (setq folders-changed
1562 (append folders-changed (mh-index-execute-commands))))
1563
1564 ;; Then refile messages
1565 (mh-mapc #'(lambda (folder-msg-list)
1566 (let* ((dest-folder (symbol-name (car folder-msg-list)))
1567 (last (car (mh-translate-range dest-folder "last")))
1568 (msgs (cdr folder-msg-list)))
1569 (push dest-folder folders-changed)
1570 (setq redraw-needed-flag t)
1571 (apply #'mh-exec-cmd
1572 "refile" "-src" folder dest-folder
1573 (mh-coalesce-msg-list msgs))
1574 (mh-delete-scan-msgs msgs)
1575 ;; Preserve sequences in destination folder...
1576 (when mh-refile-preserves-sequences-flag
1577 (clrhash dest-map)
1578 (loop for i from (1+ (or last 0))
1579 for msg in (sort (copy-sequence msgs) #'<)
1580 do (loop for seq-name in (gethash msg seq-map)
1581 do (push i (gethash seq-name dest-map))))
1582 (maphash
1583 #'(lambda (seq msgs)
1584 ;; Can't be run in the background, since the
1585 ;; current folder is changed by mark this could
1586 ;; lead to a race condition with the next refile.
1587 (apply #'mh-exec-cmd "mark"
1588 "-sequence" (symbol-name seq) dest-folder
1589 "-add" (mapcar #'(lambda (x) (format "%s" x))
1590 (mh-coalesce-msg-list msgs))))
1591 dest-map))))
1592 mh-refile-list)
1593 (setq mh-refile-list ())
1594
1595 ;; Now delete messages
1596 (cond (mh-delete-list
1597 (setq redraw-needed-flag t)
1598 (apply 'mh-exec-cmd "rmm" folder
1599 (mh-coalesce-msg-list mh-delete-list))
1600 (mh-delete-scan-msgs mh-delete-list)
1601 (setq mh-delete-list nil)))
1602
1603 ;; Don't need to remove sequences since delete and refile do so.
1604 ;; Mark cur message
1605 (if (> (buffer-size) 0)
1606 (mh-define-sequence 'cur (list (or (mh-get-msg-num nil) "last"))))
1607
1608 ;; Redraw folder buffer if needed
1609 (when (and redraw-needed-flag)
1610 (when (mh-speed-flists-active-p)
1611 (apply #'mh-speed-flists t folders-changed))
1612 (cond ((memq 'unthread mh-view-ops) (mh-thread-inc folder (point-max)))
1613 (mh-index-data (mh-index-insert-folder-headers))))
1614
1615 (and (buffer-file-name (get-buffer mh-show-buffer))
1616 (not (file-exists-p (buffer-file-name (get-buffer mh-show-buffer))))
1617 ;; If "inc" were to put a new msg in this file,
1618 ;; we would not notice, so mark it invalid now.
1619 (mh-invalidate-show-buffer))
1620
1621 (setq mh-seq-list (mh-read-folder-sequences mh-current-folder nil))
1622 (mh-remove-all-notation)
1623 (mh-notate-user-sequences)
1624
1625 ;; Run the after hook -- now folders-changed is valid,
1626 ;; but not the lists of specific messages.
1627 (let ((mh-folders-changed folders-changed))
1628 (run-hooks 'mh-after-commands-processed-hook)))
1629
1630 (message "Processing deletes and refiles for %s...done" folder)))
1631
1632(defun mh-delete-scan-msgs (msgs)
1633 "Delete the scan listing lines for MSGS."
1634 (save-excursion
1635 (while msgs
1636 (when (mh-goto-msg (car msgs) t t)
1637 (when (memq 'unthread mh-view-ops)
1638 (mh-thread-forget-message (car msgs)))
1639 (mh-delete-line 1))
1640 (setq msgs (cdr msgs)))))
1641
1642(defun mh-set-scan-mode ()
1643 "Display the scan listing buffer, but do not show a message."
1644 (if (get-buffer mh-show-buffer)
1645 (delete-windows-on mh-show-buffer))
1646 (mh-showing-mode 0)
1647 (force-mode-line-update)
1648 (if mh-recenter-summary-flag
1649 (mh-recenter nil)))
1650
1651;;;###mh-autoload
1652(defun mh-make-folder-mode-line (&optional ignored)
1653 "Set the fields of the mode line for a folder buffer.
1654The optional argument is now obsolete and IGNORED. It used to be
1655used to pass in what is now stored in the buffer-local variable
1656`mh-mode-line-annotation'."
1657 (save-excursion
1658 (save-window-excursion
1659 (mh-first-msg)
1660 (let ((new-first-msg-num (mh-get-msg-num nil)))
1661 (when (or (not (memq 'unthread mh-view-ops))
1662 (null mh-first-msg-num)
1663 (null new-first-msg-num)
1664 (< new-first-msg-num mh-first-msg-num))
1665 (setq mh-first-msg-num new-first-msg-num)))
1666 (mh-last-msg)
1667 (let ((new-last-msg-num (mh-get-msg-num nil)))
1668 (when (or (not (memq 'unthread mh-view-ops))
1669 (null mh-last-msg-num)
1670 (null new-last-msg-num)
1671 (> new-last-msg-num mh-last-msg-num))
1672 (setq mh-last-msg-num new-last-msg-num)))
1673 (setq mh-msg-count (if mh-first-msg-num
1674 (count-lines (point-min) (point-max))
1675 0))
1676 (setq mode-line-buffer-identification
1677 (list (format " {%%b%s} %s msg%s"
1678 (if mh-mode-line-annotation
1679 (format "/%s" mh-mode-line-annotation)
1680 "")
1681 (if (zerop mh-msg-count)
1682 "no"
1683 (format "%d" mh-msg-count))
1684 (if (zerop mh-msg-count)
1685 "s"
1686 (cond ((> mh-msg-count 1)
1687 (format "s (%d-%d)" mh-first-msg-num
1688 mh-last-msg-num))
1689 (mh-first-msg-num
1690 (format " (%d)" mh-first-msg-num))
1691 (""))))))
1692 (mh-logo-display))))
1693
1694;;;###mh-autoload
1695(defun mh-scan-folder (folder range &optional dont-exec-pending)
1696 "Scan FOLDER over RANGE.
1697
1698After the scan is performed, switch to the buffer associated with
1699FOLDER.
1700
1701Check the documentation of `mh-interactive-range' to see how RANGE is
1702read in interactive use.
1703
1704The processing of outstanding commands is not performed if
1705DONT-EXEC-PENDING is non-nil."
1706 (when (stringp range)
1707 (setq range (delete "" (split-string range "[ \t\n]"))))
1708 (cond ((null (get-buffer folder))
1709 (mh-make-folder folder))
1710 (t
1711 (unless dont-exec-pending
1712 (mh-process-or-undo-commands folder)
1713 (mh-reset-threads-and-narrowing))
1714 (switch-to-buffer folder)))
1715 (mh-regenerate-headers range)
1716 (if (zerop (buffer-size))
1717 (if (equal range "all")
1718 (message "Folder %s is empty" folder)
1719 (message "No messages in %s, range %s" folder range))
1720 (mh-goto-cur-msg))
1721 (when (mh-outstanding-commands-p)
1722 (mh-notate-deleted-and-refiled)))
1723
1724;;;###mh-autoload
1725(defun mh-process-or-undo-commands (folder)
1726 "If FOLDER has outstanding commands, then either process or discard them.
1727Called by functions like `mh-sort-folder', so also invalidate
1728show buffer."
1729 (set-buffer folder)
1730 (if (mh-outstanding-commands-p)
1731 (if (or mh-do-not-confirm-flag
1732 (y-or-n-p
1733 "Process outstanding deletes and refiles? "))
1734 (mh-process-commands folder)
1735 (set-buffer folder)
1736 (mh-undo-folder)))
1737 (mh-update-unseen)
1738 (mh-invalidate-show-buffer))
1739
1740;;;###mh-autoload
1741(defun mh-regenerate-headers (range &optional update)
1742 "Scan folder over RANGE.
1743If UPDATE, append the scan lines, otherwise replace."
1744 (let ((folder mh-current-folder)
1745 (range (if (and range (atom range)) (list range) range))
1746 scan-start)
1747 (message "Scanning %s..." folder)
1748 (mh-remove-all-notation)
1749 (with-mh-folder-updating (nil)
1750 (if update
1751 (goto-char (point-max))
1752 (delete-region (point-min) (point-max))
1753 (if mh-adaptive-cmd-note-flag
1754 (mh-set-cmd-note (mh-msg-num-width-to-column (mh-msg-num-width
1755 folder)))))
1756 (setq scan-start (point))
1757 (apply #'mh-exec-cmd-output
1758 mh-scan-prog nil
1759 (mh-scan-format)
1760 "-noclear" "-noheader"
1761 "-width" (window-width)
1762 folder range)
1763 (goto-char scan-start)
1764 (cond ((looking-at "scan: no messages in")
1765 (keep-lines mh-scan-valid-regexp)) ; Flush random scan lines
1766 ((looking-at (if (mh-variant-p 'mu-mh)
1767 "scan: message set .* does not exist"
1768 "scan: bad message list "))
1769 (keep-lines mh-scan-valid-regexp))
1770 ((looking-at "scan: ")) ; Keep error messages
1771 (t
1772 (keep-lines mh-scan-valid-regexp))) ; Flush random scan lines
1773 (setq mh-seq-list (mh-read-folder-sequences folder nil))
1774 (mh-notate-user-sequences)
1775 (or update
1776 (setq mh-mode-line-annotation
1777 (if (equal range '("all"))
1778 nil
1779 mh-partial-folder-mode-line-annotation)))
1780 (mh-make-folder-mode-line))
1781 (message "Scanning %s...done" folder)))
1782
1783;;;###mh-autoload
1784(defun mh-reset-threads-and-narrowing ()
1785 "Reset all variables pertaining to threads and narrowing.
1786Also removes all content from the folder buffer."
1787 (setq mh-view-ops ())
1788 (setq mh-folder-view-stack ())
1789 (setq mh-thread-scan-line-map-stack ())
1790 (let ((buffer-read-only nil)) (erase-buffer)))
1791
1792(defun mh-make-folder (name)
1793 "Create a new mail folder called NAME.
1794Make it the current folder."
1795 (switch-to-buffer name)
1796 (setq buffer-read-only nil)
1797 (erase-buffer)
1798 (if mh-adaptive-cmd-note-flag
1799 (mh-set-cmd-note (mh-msg-num-width-to-column (mh-msg-num-width name))))
1800 (setq buffer-read-only t)
1801 (mh-folder-mode)
1802 (mh-set-folder-modified-p nil)
1803 (setq buffer-file-name mh-folder-filename)
1804 (when (and (not mh-index-data)
1805 (file-exists-p (concat buffer-file-name mh-index-data-file)))
1806 (mh-index-read-data))
1807 (mh-make-folder-mode-line))
1808
1809;;;###mh-autoload
1810(defun mh-next-msg (&optional wait-after-complaining-flag)
1811 "Move backward or forward to the next undeleted message in the buffer.
1812If optional argument WAIT-AFTER-COMPLAINING-FLAG is non-nil and
1813we are at the last message, then wait for a second after telling
1814the user that there aren't any more unread messages."
1815 (if (eq mh-next-direction 'forward)
1816 (mh-next-undeleted-msg 1 wait-after-complaining-flag)
1817 (mh-previous-undeleted-msg 1 wait-after-complaining-flag)))
1818
1819;;;###mh-autoload
1820(defun mh-prompt-for-refile-folder ()
1821 "Prompt the user for a folder in which the message should be filed.
1822The folder is returned as a string.
1823
1824The default folder name is generated by the option
1825`mh-default-folder-for-message-function' if it is non-nil or
1826`mh-folder-from-address'."
1827 (mh-prompt-for-folder
1828 "Destination"
1829 (let ((refile-file (ignore-errors (mh-msg-filename (mh-get-msg-num t)))))
1830 (if (null refile-file) ""
1831 (save-excursion
1832 (set-buffer (get-buffer-create mh-temp-buffer))
1833 (erase-buffer)
1834 (insert-file-contents refile-file)
1835 (or (and mh-default-folder-for-message-function
1836 (let ((buffer-file-name refile-file))
1837 (funcall mh-default-folder-for-message-function)))
1838 (mh-folder-from-address)
1839 (and (eq 'refile (car mh-last-destination-folder))
1840 (symbol-name (cdr mh-last-destination-folder)))
1841 ""))))
1842 t))
1843
1844;;;###mh-autoload
1845(defun mh-folder-from-address ()
1846 "Derive folder name from sender.
1847
1848The name of the folder is derived as follows:
1849
1850 a) The folder name associated with the first address found in
1851 the list `mh-default-folder-list' is used. Each element in
1852 this list contains a \"Check Recipient\" item. If this item is
1853 turned on, then the address is checked against the recipient
1854 instead of the sender. This is useful for mailing lists.
1855
1856 b) An alias prefixed by `mh-default-folder-prefix'
1857 corresponding to the address is used. The prefix is used to
1858 prevent clutter in your mail directory.
1859
1860Return nil if a folder name was not derived, or if the variable
1861`mh-default-folder-must-exist-flag' is t and the folder does not
1862exist."
1863 ;; Loop for all entries in mh-default-folder-list
1864 (save-restriction
1865 (goto-char (point-min))
1866 (re-search-forward "\n\n" nil 'limit)
1867 (narrow-to-region (point-min) (point))
1868 (let ((to/cc (concat (or (message-fetch-field "to") "") ", "
1869 (or (message-fetch-field "cc") "")))
1870 (from (or (message-fetch-field "from") ""))
1871 folder-name)
1872 (setq folder-name
1873 (loop for list in mh-default-folder-list
1874 when (string-match (nth 0 list) (if (nth 2 list) to/cc from))
1875 return (nth 1 list)
1876 finally return nil))
1877
1878 ;; Make sure a result from `mh-default-folder-list' begins with "+"
1879 ;; since 'mh-expand-file-name below depends on it
1880 (when (and folder-name (not (eq (aref folder-name 0) ?+)))
1881 (setq folder-name (concat "+" folder-name)))
1882
1883 ;; If not, is there an alias for the address?
1884 (when (not folder-name)
1885 (let* ((from-header (mh-extract-from-header-value))
1886 (address (and from-header
1887 (nth 1 (mail-extract-address-components
1888 from-header))))
1889 (alias (and address (mh-alias-address-to-alias address))))
1890 (when alias
1891 (setq folder-name
1892 (and alias (concat "+" mh-default-folder-prefix alias))))))
1893
1894 ;; If mh-default-folder-must-exist-flag set, check that folder exists.
1895 (if (and folder-name
1896 (or (not mh-default-folder-must-exist-flag)
1897 (file-exists-p (mh-expand-file-name folder-name))))
1898 folder-name))))
1899
1900;;;###mh-autoload
1901(defun mh-delete-a-msg (message)
1902 "Delete MESSAGE.
1903If MESSAGE is nil then the message at point is deleted.
1904The hook `mh-delete-msg-hook' is called after you mark a message
1905for deletion. For example, a past maintainer of MH-E used this
1906once when he kept statistics on his mail usage."
1907 (save-excursion
1908 (if (numberp message)
1909 (mh-goto-msg message nil t)
1910 (beginning-of-line)
1911 (setq message (mh-get-msg-num t)))
1912 (if (looking-at mh-scan-refiled-msg-regexp)
1913 (error "Message %d is refiled; undo refile before deleting" message))
1914 (if (looking-at mh-scan-deleted-msg-regexp)
1915 nil
1916 (mh-set-folder-modified-p t)
1917 (setq mh-delete-list (cons message mh-delete-list))
1918 (mh-notate nil mh-note-deleted mh-cmd-note)
1919 (run-hooks 'mh-delete-msg-hook))))
1920
1921;;;###mh-autoload
1922(defun mh-refile-a-msg (message folder)
1923 "Refile MESSAGE in FOLDER.
1924If MESSAGE is nil then the message at point is refiled.
1925Folder is a symbol, not a string.
1926The hook `mh-refile-msg-hook' is called after a message is marked to
1927be refiled."
1928 (save-excursion
1929 (if (numberp message)
1930 (mh-goto-msg message nil t)
1931 (beginning-of-line)
1932 (setq message (mh-get-msg-num t)))
1933 (cond ((looking-at mh-scan-deleted-msg-regexp)
1934 (error "Message %d is deleted; undo delete before moving" message))
1935 ((looking-at mh-scan-refiled-msg-regexp)
1936 (if (y-or-n-p
1937 (format "Message %d already refiled; copy to %s as well? "
1938 message folder))
1939 (mh-exec-cmd "refile" (mh-get-msg-num t) "-link"
1940 "-src" mh-current-folder
1941 (symbol-name folder))
1942 (message "Message not copied")))
1943 (t
1944 (mh-set-folder-modified-p t)
1945 (cond ((null (assoc folder mh-refile-list))
1946 (push (list folder message) mh-refile-list))
1947 ((not (member message (cdr (assoc folder mh-refile-list))))
1948 (push message (cdr (assoc folder mh-refile-list)))))
1949 (mh-notate nil mh-note-refiled mh-cmd-note)
1950 (run-hooks 'mh-refile-msg-hook)))))
1951
1952(defun mh-undo-msg (msg)
1953 "Undo the deletion or refile of one MSG.
1954If MSG is nil then act on the message at point"
1955 (save-excursion
1956 (if (numberp msg)
1957 (mh-goto-msg msg t t)
1958 (beginning-of-line)
1959 (setq msg (mh-get-msg-num t)))
1960 (cond ((memq msg mh-delete-list)
1961 (setq mh-delete-list (delq msg mh-delete-list)))
1962 (t
1963 (dolist (folder-msg-list mh-refile-list)
1964 (setf (cdr folder-msg-list) (remove msg (cdr folder-msg-list))))
1965 (setq mh-refile-list (loop for x in mh-refile-list
1966 unless (null (cdr x)) collect x))))
1967 (mh-notate nil ? mh-cmd-note)))
1968
1969;;;###mh-autoload
1970(defun mh-msg-filename (msg &optional folder)
1971 "Return the file name of MSG in FOLDER (default current folder)."
1972 (expand-file-name (int-to-string msg)
1973 (if folder
1974 (mh-expand-file-name folder)
1975 mh-folder-filename)))
1976
1977(provide 'mh-folder)
1978
1979;; Local Variables:
1980;; indent-tabs-mode: nil
1981;; sentence-end-double-space: nil
1982;; End:
1983
a1ab640d 1984;; arch-tag: aa97b758-d4f6-4c86-bc5a-1950921da1e7
dda00b2c 1985;;; mh-folder.el ends here