aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/mh-e/mh-e.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/mh-e/mh-e.el')
-rw-r--r--lisp/mh-e/mh-e.el581
1 files changed, 319 insertions, 262 deletions
diff --git a/lisp/mh-e/mh-e.el b/lisp/mh-e/mh-e.el
index 344a67f5725..e72304c4412 100644
--- a/lisp/mh-e/mh-e.el
+++ b/lisp/mh-e/mh-e.el
@@ -1,11 +1,11 @@
1;;; mh-e.el --- GNU Emacs interface to the MH mail system 1;;; mh-e.el --- GNU Emacs interface to the MH mail system
2 2
3;; Copyright (C) 1985, 86, 87, 88, 90, 92, 93, 94, 95, 97, 1999, 3;; Copyright (C) 1985, 86, 87, 88, 90, 92, 93, 94, 95, 97, 1999,
4;; 2000, 01, 02, 2003 Free Software Foundation, Inc. 4;; 2000, 01, 02, 03, 2004 Free Software Foundation, Inc.
5 5
6;; Author: Bill Wohler <wohler@newt.com> 6;; Author: Bill Wohler <wohler@newt.com>
7;; Maintainer: Bill Wohler <wohler@newt.com> 7;; Maintainer: Bill Wohler <wohler@newt.com>
8;; Version: 7.3 8;; Version: 7.4.4
9;; Keywords: mail 9;; Keywords: mail
10 10
11;; This file is part of GNU Emacs. 11;; This file is part of GNU Emacs.
@@ -82,7 +82,9 @@
82 82
83;;; Code: 83;;; Code:
84 84
85(require 'cl) 85(provide 'mh-e)
86(require 'mh-utils)
87(mh-require-cl)
86 88
87(defvar recursive-load-depth-limit) 89(defvar recursive-load-depth-limit)
88(eval-when (compile load eval) 90(eval-when (compile load eval)
@@ -92,17 +94,14 @@
92 (setq recursive-load-depth-limit 50))) 94 (setq recursive-load-depth-limit 50)))
93 95
94(require 'mh-inc) 96(require 'mh-inc)
95(require 'mh-utils)
96(require 'gnus-util) 97(require 'gnus-util)
97(require 'easymenu) 98(require 'easymenu)
98(if mh-xemacs-flag
99 (require 'mh-xemacs-compat))
100 99
101;; Shush the byte-compiler 100;; Shush the byte-compiler
102(defvar font-lock-auto-fontify) 101(defvar font-lock-auto-fontify)
103(defvar font-lock-defaults) 102(defvar font-lock-defaults)
104 103
105(defconst mh-version "7.3" "Version number of MH-E.") 104(defconst mh-version "7.4.4" "Version number of MH-E.")
106 105
107;;; Autoloads 106;;; Autoloads
108(autoload 'Info-goto-node "info") 107(autoload 'Info-goto-node "info")
@@ -283,9 +282,7 @@ third should match the user name.")
283 '(3 mh-folder-scan-format-face)) 282 '(3 mh-folder-scan-format-face))
284 ;; Current message line 283 ;; Current message line
285 (list mh-scan-cur-msg-regexp 284 (list mh-scan-cur-msg-regexp
286 '(1 mh-folder-cur-msg-face prepend t)) 285 '(1 mh-folder-cur-msg-face prepend t)))
287 ;; Unseen messages in bold
288 '(mh-folder-font-lock-unseen (1 'bold append t)))
289 "Regexp keywords used to fontify the MH-Folder buffer.") 286 "Regexp keywords used to fontify the MH-Folder buffer.")
290 287
291(defvar mh-scan-cmd-note-width 1 288(defvar mh-scan-cmd-note-width 1
@@ -399,50 +396,61 @@ On nmh systems.")
399 (goto-char (point-min)) 396 (goto-char (point-min))
400 (sort (mh-read-msg-list) '<))))))))) 397 (sort (mh-read-msg-list) '<)))))))))
401 398
402(defvar mh-folder-unseen-seq-cache nil 399(defmacro mh-generate-sequence-font-lock (seq prefix face)
403 "Internal cache variable used for font-lock in MH-E. 400 "Generate the appropriate code to fontify messages in SEQ.
401PREFIX is used to generate unique names for the variables and functions
402defined by the macro. So a different prefix should be provided for every
403invocation.
404FACE is the font-lock face used to display the matching scan lines."
405 (let ((cache (intern (format "mh-folder-%s-seq-cache" prefix)))
406 (func (intern (format "mh-folder-font-lock-%s" prefix))))
407 `(progn
408 (defvar ,cache nil
409 "Internal cache variable used for font-lock in MH-E.
404Should only be non-nil through font-lock stepping, and nil once font-lock 410Should only be non-nil through font-lock stepping, and nil once font-lock
405is done highlighting.") 411is done highlighting.")
406(make-variable-buffer-local 'mh-folder-unseen-seq-cache) 412 (make-variable-buffer-local ',cache)
407 413
408(defun mh-folder-font-lock-unseen (limit) 414 (defun ,func (limit)
409 "Return unseen message lines to font-lock between point and LIMIT." 415 "Return unseen message lines to font-lock between point and LIMIT."
410 (if (not mh-folder-unseen-seq-cache) 416 (if (not ,cache) (setq ,cache (mh-seq-msgs (mh-find-seq ,seq))))
411 (setq mh-folder-unseen-seq-cache (mh-folder-unseen-seq-list))) 417 (let ((cur-msg (mh-get-msg-num nil)))
412 (let ((cur-msg (mh-get-msg-num nil))) 418 (cond ((not ,cache)
413 (cond 419 nil)
414 ((not mh-folder-unseen-seq-cache) 420 ((>= (point) limit) ;Presumably at end of buffer
415 nil) 421 (setq ,cache nil)
416 ((>= (point) limit) ;Presumably at end of buffer 422 nil)
417 (setq mh-folder-unseen-seq-cache nil) 423 ((member cur-msg ,cache)
418 nil) 424 (let ((bpoint (progn (beginning-of-line)(point)))
419 ((member cur-msg mh-folder-unseen-seq-cache) 425 (epoint (progn (forward-line 1)(point))))
420 (let ((bpoint (progn (beginning-of-line)(point))) 426 (if (<= limit (point)) (setq ,cache nil))
421 (epoint (progn (forward-line 1)(point)))) 427 (set-match-data (list bpoint epoint bpoint epoint))
422 (if (<= limit (point)) 428 t))
423 (setq mh-folder-unseen-seq-cache nil)) 429 (t
424 (set-match-data (list bpoint epoint bpoint epoint)) 430 ;; move forward one line at a time, checking each message
425 t)) 431 (while (and (= 0 (forward-line 1))
426 (t 432 (> limit (point))
427 ;; move forward one line at a time, checking each message number. 433 (not (member (mh-get-msg-num nil) ,cache))))
428 (while (and 434 ;; Examine how we must have exited the loop...
429 (= 0 (forward-line 1)) 435 (let ((cur-msg (mh-get-msg-num nil)))
430 (> limit (point)) 436 (cond ((or (<= limit (point))
431 (not (member (mh-get-msg-num nil) mh-folder-unseen-seq-cache)))) 437 (not (member cur-msg ,cache)))
432 ;; Examine how we must have exited the loop... 438 (setq ,cache nil)
433 (let ((cur-msg (mh-get-msg-num nil))) 439 nil)
434 (cond 440 ((member cur-msg ,cache)
435 ((or (<= limit (point)) 441 (let ((bpoint (progn (beginning-of-line) (point)))
436 (not (member cur-msg mh-folder-unseen-seq-cache))) 442 (epoint (progn (forward-line 1) (point))))
437 (setq mh-folder-unseen-seq-cache nil) 443 (if (<= limit (point)) (setq ,cache nil))
438 nil) 444 (set-match-data
439 ((member cur-msg mh-folder-unseen-seq-cache) 445 (list bpoint epoint bpoint epoint))
440 (let ((bpoint (progn (beginning-of-line)(point))) 446 t))))))))
441 (epoint (progn (forward-line 1)(point)))) 447
442 (if (<= limit (point)) 448 (setq mh-folder-font-lock-keywords
443 (setq mh-folder-unseen-seq-cache nil)) 449 (append mh-folder-font-lock-keywords
444 (set-match-data (list bpoint epoint bpoint epoint)) 450 (list (list ',func (list 1 '',face 'prepend t))))))))
445 t)))))))) 451
452(mh-generate-sequence-font-lock mh-unseen-seq unseen bold)
453(mh-generate-sequence-font-lock mh-tick-seq tick mh-folder-tick-face)
446 454
447 455
448 456
@@ -464,20 +472,15 @@ is done highlighting.")
464 472
465(defvar mh-next-direction 'forward) ;Direction to move to next message. 473(defvar mh-next-direction 'forward) ;Direction to move to next message.
466 474
467(defvar mh-narrowed-to-seq nil) ;Sequence display is narrowed to or
468 ;nil if not narrowed.
469
470(defvar mh-tick-seq-changed-when-narrowed-flag nil)
471 ;Has tick sequence changed while the
472 ;folder was narrowed to it?
473
474(defvar mh-view-ops ()) ;Stack of ops that change the folder 475(defvar mh-view-ops ()) ;Stack of ops that change the folder
475 ;view (such as narrowing or threading). 476 ;view (such as narrowing or threading).
477(defvar mh-folder-view-stack ()) ;Stack of previous folder views.
476 478
477(defvar mh-index-data nil) ;Info about index search results 479(defvar mh-index-data nil) ;Info about index search results
478(defvar mh-index-previous-search nil) 480(defvar mh-index-previous-search nil)
479(defvar mh-index-msg-checksum-map nil) 481(defvar mh-index-msg-checksum-map nil)
480(defvar mh-index-checksum-origin-map nil) 482(defvar mh-index-checksum-origin-map nil)
483(defvar mh-index-sequence-search-flag nil)
481 484
482(defvar mh-first-msg-num nil) ;Number of first msg in buffer. 485(defvar mh-first-msg-num nil) ;Number of first msg in buffer.
483 486
@@ -485,6 +488,10 @@ is done highlighting.")
485 488
486(defvar mh-mode-line-annotation nil) ;Message range displayed in buffer. 489(defvar mh-mode-line-annotation nil) ;Message range displayed in buffer.
487 490
491(defvar mh-sequence-notation-history nil)
492 ;Rememeber original notation that
493 ;is overwritten by `mh-note-seq'.
494
488;;; Macros and generic functions: 495;;; Macros and generic functions:
489 496
490(defun mh-mapc (function list) 497(defun mh-mapc (function list)
@@ -494,7 +501,7 @@ is done highlighting.")
494 (setq list (cdr list)))) 501 (setq list (cdr list))))
495 502
496(defun mh-scan-format () 503(defun mh-scan-format ()
497 "Return \"-format\" argument for the scan program." 504 "Return the output format argument for the scan program."
498 (if (equal mh-scan-format-file t) 505 (if (equal mh-scan-format-file t)
499 (list "-format" (if mh-nmh-flag 506 (list "-format" (if mh-nmh-flag
500 (list (mh-update-scan-format 507 (list (mh-update-scan-format
@@ -502,7 +509,7 @@ is done highlighting.")
502 (list (mh-update-scan-format 509 (list (mh-update-scan-format
503 mh-scan-format-mh mh-cmd-note)))) 510 mh-scan-format-mh mh-cmd-note))))
504 (if (not (equal mh-scan-format-file nil)) 511 (if (not (equal mh-scan-format-file nil))
505 (list "-format" mh-scan-format-file)))) 512 (list "-form" mh-scan-format-file))))
506 513
507 514
508 515
@@ -536,34 +543,29 @@ the Emacs front end to the MH mail system."
536 543
537;;; User executable MH-E commands: 544;;; User executable MH-E commands:
538 545
539(defun mh-delete-msg (msg-or-seq) 546(defun mh-delete-msg (range)
540 "Mark the specified MSG-OR-SEQ for subsequent deletion and move to the next. 547 "Mark the specified RANGE for subsequent deletion and move to the next.
541Default is the displayed message. 548Default is the displayed message.
542If optional prefix argument is provided, then prompt for the message sequence. 549
543If variable `transient-mark-mode' is non-nil and the mark is active, then the 550Check the documentation of `mh-interactive-range' to see how RANGE is read in
544selected region is marked for deletion. 551interactive use."
545In a program, MSG-OR-SEQ can be a message number, a list of message numbers, a 552 (interactive (list (mh-interactive-range "Delete")))
546region in a cons cell, or a sequence." 553 (mh-delete-msg-no-motion range)
547 (interactive (list (mh-interactive-msg-or-seq "Delete"))) 554 (if (looking-at mh-scan-deleted-msg-regexp) (mh-next-msg)))
548 (mh-delete-msg-no-motion msg-or-seq) 555
549 (mh-next-msg)) 556(defun mh-delete-msg-no-motion (range)
550 557 "Mark the specified RANGE for subsequent deletion.
551(defun mh-delete-msg-no-motion (msg-or-seq) 558
552 "Mark the specified MSG-OR-SEQ for subsequent deletion. 559Check the documentation of `mh-interactive-range' to see how RANGE is read in
553Default is the displayed message. 560interactive use."
554If optional prefix argument is provided, then prompt for the message sequence. 561 (interactive (list (mh-interactive-range "Delete")))
555If variable `transient-mark-mode' is non-nil and the mark is active, then the 562 (mh-iterate-on-range () range
556selected region is marked for deletion.
557In a program, MSG-OR-SEQ can be a message number, a list of message numbers, a
558region in a cons cell, or a sequence."
559 (interactive (list (mh-interactive-msg-or-seq "Delete")))
560 (mh-iterate-on-msg-or-seq () msg-or-seq
561 (mh-delete-a-msg nil))) 563 (mh-delete-a-msg nil)))
562 564
563(defun mh-execute-commands () 565(defun mh-execute-commands ()
564 "Process outstanding delete and refile requests." 566 "Process outstanding delete and refile requests."
565 (interactive) 567 (interactive)
566 (if mh-narrowed-to-seq (mh-widen)) 568 (if mh-folder-view-stack (mh-widen t))
567 (mh-process-commands mh-current-folder) 569 (mh-process-commands mh-current-folder)
568 (mh-set-scan-mode) 570 (mh-set-scan-mode)
569 (mh-goto-cur-msg) ; after mh-set-scan-mode for efficiency 571 (mh-goto-cur-msg) ; after mh-set-scan-mode for efficiency
@@ -626,7 +628,7 @@ Do not call this function from outside MH-E; use \\[mh-rmail] instead."
626 (save-excursion 628 (save-excursion
627 (goto-char (point-min)) 629 (goto-char (point-min))
628 (or (null mh-large-folder) 630 (or (null mh-large-folder)
629 (not (equal (forward-line mh-large-folder) 0)) 631 (not (equal (forward-line (1+ mh-large-folder)) 0))
630 (and (message "Not threading since the number of messages exceeds `mh-large-folder'") 632 (and (message "Not threading since the number of messages exceeds `mh-large-folder'")
631 nil)))) 633 nil))))
632 (mh-toggle-threads)) 634 (mh-toggle-threads))
@@ -673,31 +675,19 @@ Takes the address in the From: header field, and returns one of:
673Returns nil if the address was not found in either place or if the variable 675Returns nil if the address was not found in either place or if the variable
674`mh-default-folder-must-exist-flag' is nil and the folder does not exist." 676`mh-default-folder-must-exist-flag' is nil and the folder does not exist."
675 ;; Loop for all entries in mh-default-folder-list 677 ;; Loop for all entries in mh-default-folder-list
676 (save-excursion 678 (save-restriction
677 (let ((folder-name 679 (goto-char (point-min))
678 (car 680 (re-search-forward "\n\n" nil t)
679 (delq nil 681 (narrow-to-region (point-min) (point))
680 (mapcar 682 (let ((to/cc (concat (or (message-fetch-field "to") "") ", "
681 (lambda (list) 683 (or (message-fetch-field "cc") "")))
682 (let ((address-regexp (nth 0 list)) 684 (from (or (message-fetch-field "from") ""))
683 (folder (nth 1 list)) 685 folder-name)
684 (to-flag (nth 2 list))) 686 (setq folder-name
685 (when (or 687 (loop for list in mh-default-folder-list
686 (mh-goto-header-field (if to-flag "To:" "From:")) 688 when (string-match (nth 0 list) (if (nth 2 list) to/cc from))
687 ; if the To: field is missing, try Cc: 689 return (nth 1 list)
688 (and to-flag (mh-goto-header-field "cc:"))) 690 finally return nil))
689 (let ((endfield (save-excursion
690 (mh-header-field-end)(point))))
691 (if (re-search-forward address-regexp endfield t)
692 folder
693 (when to-flag ;Try Cc: as well
694 (mh-goto-header-field "cc:")
695 (let ((endfield (save-excursion
696 (mh-header-field-end)(point))))
697 (when (re-search-forward
698 address-regexp endfield t)
699 folder))))))))
700 mh-default-folder-list)))))
701 691
702 ;; Make sure a result from `mh-default-folder-list' begins with "+" 692 ;; Make sure a result from `mh-default-folder-list' begins with "+"
703 ;; since 'mh-expand-file-name below depends on it 693 ;; since 'mh-expand-file-name below depends on it
@@ -746,27 +736,23 @@ Otherwise, a default folder name is generated by `mh-folder-from-address'."
746 ""))) 736 "")))
747 t)) 737 t))
748 738
749(defun mh-refile-msg (msg-or-seq folder 739(defun mh-refile-msg (range folder &optional dont-update-last-destination-flag)
750 &optional dont-update-last-destination-flag) 740 "Refile RANGE into FOLDER.
751 "Refile MSG-OR-SEQ into FOLDER. 741
752Default is the displayed message. 742Check the documentation of `mh-interactive-range' to see how RANGE is read in
753If optional prefix argument is provided, then prompt for the message sequence. 743interactive use.
754If variable `transient-mark-mode' is non-nil and the mark is active, then the
755selected region is marked for refiling.
756In a program, MSG-OR-SEQ can be a message number, a list of message numbers, a
757region in a cons cell, or a sequence.
758 744
759If optional argument DONT-UPDATE-LAST-DESTINATION-FLAG is non-nil then the 745If optional argument DONT-UPDATE-LAST-DESTINATION-FLAG is non-nil then the
760variables `mh-last-destination' and `mh-last-destination-folder' are not 746variables `mh-last-destination' and `mh-last-destination-folder' are not
761updated." 747updated."
762 (interactive (list (mh-interactive-msg-or-seq "Refile") 748 (interactive (list (mh-interactive-range "Refile")
763 (intern (mh-prompt-for-refile-folder)))) 749 (intern (mh-prompt-for-refile-folder))))
764 (unless dont-update-last-destination-flag 750 (unless dont-update-last-destination-flag
765 (setq mh-last-destination (cons 'refile folder) 751 (setq mh-last-destination (cons 'refile folder)
766 mh-last-destination-folder mh-last-destination)) 752 mh-last-destination-folder mh-last-destination))
767 (mh-iterate-on-msg-or-seq () msg-or-seq 753 (mh-iterate-on-range () range
768 (mh-refile-a-msg nil folder)) 754 (mh-refile-a-msg nil folder))
769 (mh-next-msg)) 755 (when (looking-at mh-scan-refiled-msg-regexp) (mh-next-msg)))
770 756
771(defun mh-refile-or-write-again (message) 757(defun mh-refile-or-write-again (message)
772 "Re-execute the last refile or write command on the given MESSAGE. 758 "Re-execute the last refile or write command on the given MESSAGE.
@@ -1015,11 +1001,14 @@ end of buffer is reached) and save it."
1015 (when (consp part-index) (setq part-index (car part-index))) 1001 (when (consp part-index) (setq part-index (car part-index)))
1016 (mh-folder-mime-action part-index #'mh-mime-save-part nil)) 1002 (mh-folder-mime-action part-index #'mh-mime-save-part nil))
1017 1003
1004(defvar mh-thread-scan-line-map-stack)
1005
1018(defun mh-reset-threads-and-narrowing () 1006(defun mh-reset-threads-and-narrowing ()
1019 "Reset all variables pertaining to threads and narrowing. 1007 "Reset all variables pertaining to threads and narrowing.
1020Also removes all content from the folder buffer." 1008Also removes all content from the folder buffer."
1021 (setq mh-view-ops ()) 1009 (setq mh-view-ops ())
1022 (setq mh-narrowed-to-seq nil) 1010 (setq mh-folder-view-stack ())
1011 (setq mh-thread-scan-line-map-stack ())
1023 (let ((buffer-read-only nil)) (erase-buffer))) 1012 (let ((buffer-read-only nil)) (erase-buffer)))
1024 1013
1025(defun mh-rescan-folder (&optional range dont-exec-pending) 1014(defun mh-rescan-folder (&optional range dont-exec-pending)
@@ -1029,7 +1018,8 @@ messages to display. Otherwise show the entire folder.
1029If optional argument DONT-EXEC-PENDING is non-nil then pending deletes and 1018If optional argument DONT-EXEC-PENDING is non-nil then pending deletes and
1030refiles aren't carried out." 1019refiles aren't carried out."
1031 (interactive (list (if current-prefix-arg 1020 (interactive (list (if current-prefix-arg
1032 (mh-read-msg-range mh-current-folder t) 1021 (mh-read-range "Rescan" mh-current-folder t nil t
1022 mh-interpret-number-as-range-flag)
1033 nil))) 1023 nil)))
1034 (setq mh-next-direction 'forward) 1024 (setq mh-next-direction 'forward)
1035 (let ((threaded-flag (memq 'unthread mh-view-ops))) 1025 (let ((threaded-flag (memq 'unthread mh-view-ops)))
@@ -1073,16 +1063,13 @@ Otherwise send the entire message including the headers."
1073 (mh-set-scan-mode) 1063 (mh-set-scan-mode)
1074 (mh-show))) 1064 (mh-show)))
1075 1065
1076(defun mh-undo (msg-or-seq) 1066(defun mh-undo (range)
1077 "Undo the pending deletion or refile of the specified MSG-OR-SEQ. 1067 "Undo the pending deletion or refile of the specified RANGE.
1078Default is the displayed message. 1068
1079If optional prefix argument is provided, then prompt for the message sequence. 1069Check the documentation of `mh-interactive-range' to see how RANGE is read in
1080If variable `transient-mark-mode' is non-nil and the mark is active, then the 1070interactive use."
1081selected region is unmarked. 1071 (interactive (list (mh-interactive-range "Undo")))
1082In a program, MSG-OR-SEQ can be a message number, a list of message numbers, a 1072 (cond ((numberp range)
1083region in a cons cell, or a sequence."
1084 (interactive (list (mh-interactive-msg-or-seq "Undo")))
1085 (cond ((numberp msg-or-seq)
1086 (let ((original-position (point))) 1073 (let ((original-position (point)))
1087 (beginning-of-line) 1074 (beginning-of-line)
1088 (while (not (or (looking-at mh-scan-deleted-msg-regexp) 1075 (while (not (or (looking-at mh-scan-deleted-msg-regexp)
@@ -1098,7 +1085,7 @@ region in a cons cell, or a sequence."
1098 (mh-maybe-show)) 1085 (mh-maybe-show))
1099 (goto-char original-position) 1086 (goto-char original-position)
1100 (error "Nothing to undo")))) 1087 (error "Nothing to undo"))))
1101 (t (mh-iterate-on-msg-or-seq () msg-or-seq 1088 (t (mh-iterate-on-range () range
1102 (mh-undo-msg nil)))) 1089 (mh-undo-msg nil))))
1103 (if (not (mh-outstanding-commands-p)) 1090 (if (not (mh-outstanding-commands-p))
1104 (mh-set-folder-modified-p nil))) 1091 (mh-set-folder-modified-p nil)))
@@ -1200,8 +1187,20 @@ used to avoid problems in corner cases involving folders whose names end with a
1200 (setq folder (substring folder 0 (1- (length folder))))) 1187 (setq folder (substring folder 0 (1- (length folder)))))
1201 (values (format "+%s" folder) (car unseen) (car total)))))))) 1188 (values (format "+%s" folder) (car unseen) (car total))))))))
1202 1189
1203(defun mh-folder-size (folder) 1190(defun mh-folder-size-folder (folder)
1204 "Find size of FOLDER." 1191 "Find size of FOLDER using `folder'."
1192 (with-temp-buffer
1193 (let ((u (length (cdr (assoc mh-unseen-seq
1194 (mh-read-folder-sequences folder nil))))))
1195 (call-process (expand-file-name "folder" mh-progs) nil t nil
1196 "-norecurse" folder)
1197 (goto-char (point-min))
1198 (if (re-search-forward " has \\([0-9]+\\) " nil t)
1199 (values (car (read-from-string (match-string 1))) u folder)
1200 (values 0 u folder)))))
1201
1202(defun mh-folder-size-flist (folder)
1203 "Find size of FOLDER using `flist'."
1205 (with-temp-buffer 1204 (with-temp-buffer
1206 (call-process (expand-file-name "flist" mh-progs) nil t nil 1205 (call-process (expand-file-name "flist" mh-progs) nil t nil
1207 "-norecurse" folder "-sequence" (symbol-name mh-unseen-seq)) 1206 "-norecurse" folder "-sequence" (symbol-name mh-unseen-seq))
@@ -1211,6 +1210,12 @@ used to avoid problems in corner cases involving folders whose names end with a
1211 (buffer-substring (point) (line-end-position))) 1210 (buffer-substring (point) (line-end-position)))
1212 (values total unseen folder)))) 1211 (values total unseen folder))))
1213 1212
1213(defun mh-folder-size (folder)
1214 "Find size of FOLDER."
1215 (if mh-flists-present-flag
1216 (mh-folder-size-flist folder)
1217 (mh-folder-size-folder folder)))
1218
1214(defun mh-visit-folder (folder &optional range index-data) 1219(defun mh-visit-folder (folder &optional range index-data)
1215 "Visit FOLDER and display RANGE of messages. 1220 "Visit FOLDER and display RANGE of messages.
1216Do not call this function from outside MH-E; see \\[mh-rmail] instead. 1221Do not call this function from outside MH-E; see \\[mh-rmail] instead.
@@ -1225,7 +1230,9 @@ A prefix argument will cause a prompt for the RANGE of messages
1225regardless of the size of the `mh-large-folder' variable." 1230regardless of the size of the `mh-large-folder' variable."
1226 (interactive (let ((folder-name (mh-prompt-for-folder "Visit" mh-inbox t))) 1231 (interactive (let ((folder-name (mh-prompt-for-folder "Visit" mh-inbox t)))
1227 (list folder-name 1232 (list folder-name
1228 (mh-read-msg-range folder-name current-prefix-arg)))) 1233 (mh-read-range "Scan" folder-name t nil
1234 current-prefix-arg
1235 mh-interpret-number-as-range-flag))))
1229 (let ((config (current-window-configuration)) 1236 (let ((config (current-window-configuration))
1230 (current-buffer (current-buffer)) 1237 (current-buffer (current-buffer))
1231 (threaded-view-flag mh-show-threads-flag)) 1238 (threaded-view-flag mh-show-threads-flag))
@@ -1238,13 +1245,14 @@ regardless of the size of the `mh-large-folder' variable."
1238 (setq mh-index-data (car index-data) 1245 (setq mh-index-data (car index-data)
1239 mh-index-msg-checksum-map (make-hash-table :test #'equal) 1246 mh-index-msg-checksum-map (make-hash-table :test #'equal)
1240 mh-index-checksum-origin-map (make-hash-table :test #'equal)) 1247 mh-index-checksum-origin-map (make-hash-table :test #'equal))
1241 (mh-index-update-maps folder (cadr index-data))) 1248 (mh-index-update-maps folder (cadr index-data))
1249 (mh-index-create-sequences))
1242 (mh-scan-folder folder (or range "all")) 1250 (mh-scan-folder folder (or range "all"))
1243 (cond ((and threaded-view-flag 1251 (cond ((and threaded-view-flag
1244 (save-excursion 1252 (save-excursion
1245 (goto-char (point-min)) 1253 (goto-char (point-min))
1246 (or (null mh-large-folder) 1254 (or (null mh-large-folder)
1247 (not (equal (forward-line mh-large-folder) 0)) 1255 (not (equal (forward-line (1+ mh-large-folder)) 0))
1248 (and (message "Not threading since the number of messages exceeds `mh-large-folder'") 1256 (and (message "Not threading since the number of messages exceeds `mh-large-folder'")
1249 nil)))) 1257 nil))))
1250 (mh-toggle-threads)) 1258 (mh-toggle-threads))
@@ -1405,6 +1413,9 @@ If MSG is nil then act on the message at point"
1405 1413
1406;;; The folder data abstraction. 1414;;; The folder data abstraction.
1407 1415
1416(defvar mh-index-data-file ".mhe_index"
1417 "MH-E specific file where index seach info is stored.")
1418
1408(defun mh-make-folder (name) 1419(defun mh-make-folder (name)
1409 "Create a new mail folder called NAME. 1420 "Create a new mail folder called NAME.
1410Make it the current folder." 1421Make it the current folder."
@@ -1417,6 +1428,9 @@ Make it the current folder."
1417 (mh-folder-mode) 1428 (mh-folder-mode)
1418 (mh-set-folder-modified-p nil) 1429 (mh-set-folder-modified-p nil)
1419 (setq buffer-file-name mh-folder-filename) 1430 (setq buffer-file-name mh-folder-filename)
1431 (when (and (not mh-index-data)
1432 (file-exists-p (concat buffer-file-name mh-index-data-file)))
1433 (mh-index-read-data))
1420 (mh-make-folder-mode-line)) 1434 (mh-make-folder-mode-line))
1421 1435
1422;;; Ensure new buffers won't get this mode if default-major-mode is nil. 1436;;; Ensure new buffers won't get this mode if default-major-mode is nil.
@@ -1437,7 +1451,7 @@ Make it the current folder."
1437 ["List Sequences in Folder..." mh-list-sequences t] 1451 ["List Sequences in Folder..." mh-list-sequences t]
1438 ["Delete Sequence..." mh-delete-seq t] 1452 ["Delete Sequence..." mh-delete-seq t]
1439 ["Narrow to Sequence..." mh-narrow-to-seq t] 1453 ["Narrow to Sequence..." mh-narrow-to-seq t]
1440 ["Widen from Sequence" mh-widen mh-narrowed-to-seq] 1454 ["Widen from Sequence" mh-widen mh-folder-view-stack]
1441 "--" 1455 "--"
1442 ["Narrow to Subject Sequence" mh-narrow-to-subject t] 1456 ["Narrow to Subject Sequence" mh-narrow-to-subject t]
1443 ["Narrow to Tick Sequence" mh-narrow-to-tick 1457 ["Narrow to Tick Sequence" mh-narrow-to-tick
@@ -1512,9 +1526,6 @@ Make it the current folder."
1512 (set-specifier horizontal-scrollbar-visible-p nil 1526 (set-specifier horizontal-scrollbar-visible-p nil
1513 (cons (current-buffer) nil))))) 1527 (cons (current-buffer) nil)))))
1514 1528
1515;; Avoid compiler warnings in XEmacs and GNU Emacs 20
1516(eval-when-compile (defvar tool-bar-mode))
1517
1518(defmacro mh-write-file-functions-compat () 1529(defmacro mh-write-file-functions-compat ()
1519 "Return `write-file-functions' if it exists. 1530 "Return `write-file-functions' if it exists.
1520Otherwise return `local-write-file-hooks'. This macro exists purely for 1531Otherwise return `local-write-file-hooks'. This macro exists purely for
@@ -1524,8 +1535,11 @@ is used in previous versions and XEmacs."
1524 ''write-file-functions ;Emacs 21.4 1535 ''write-file-functions ;Emacs 21.4
1525 ''local-write-file-hooks)) ;<Emacs 21.4, XEmacs 1536 ''local-write-file-hooks)) ;<Emacs 21.4, XEmacs
1526 1537
1527;; Avoid compiler warning 1538;; Avoid compiler warnings in non-bleeding edge versions of Emacs.
1528(defvar tool-bar-map) 1539(eval-when-compile
1540 (defvar tool-bar-mode)
1541 (defvar tool-bar-map)
1542 (defvar desktop-save-buffer)) ;Emacs 21.4
1529 1543
1530(define-derived-mode mh-folder-mode fundamental-mode "MH-Folder" 1544(define-derived-mode mh-folder-mode fundamental-mode "MH-Folder"
1531 "Major MH-E mode for \"editing\" an MH folder scan listing.\\<mh-folder-mode-map> 1545 "Major MH-E mode for \"editing\" an MH folder scan listing.\\<mh-folder-mode-map>
@@ -1564,22 +1578,25 @@ When a folder is visited, the hook `mh-folder-mode-hook' is run.
1564 'mh-seq-list nil ; Alist of (seq . msgs) nums 1578 'mh-seq-list nil ; Alist of (seq . msgs) nums
1565 'mh-seen-list nil ; List of displayed messages 1579 'mh-seen-list nil ; List of displayed messages
1566 'mh-next-direction 'forward ; Direction to move to next message 1580 'mh-next-direction 'forward ; Direction to move to next message
1567 'mh-narrowed-to-seq nil ; Sequence display is narrowed to
1568 'mh-tick-seq-changed-when-narrowed-flag nil
1569 ; Tick seq changed while narrowed
1570 'mh-view-ops () ; Stack that keeps track of the order 1581 'mh-view-ops () ; Stack that keeps track of the order
1571 ; in which narrowing/threading has been 1582 ; in which narrowing/threading has been
1572 ; carried out. 1583 ; carried out.
1584 'mh-folder-view-stack () ; Stack of previous views of the
1585 ; folder.
1573 'mh-index-data nil ; If the folder was created by a call 1586 'mh-index-data nil ; If the folder was created by a call
1574 ; to mh-index-search this contains info 1587 ; to mh-index-search this contains info
1575 ; about the search results. 1588 ; about the search results.
1576 'mh-index-previous-search nil ; Previous folder and search-regexp 1589 'mh-index-previous-search nil ; Previous folder and search-regexp
1577 'mh-index-msg-checksum-map nil ; msg -> checksum map 1590 'mh-index-msg-checksum-map nil ; msg -> checksum map
1578 'mh-index-checksum-origin-map nil ; checksum -> ( orig-folder, orig-msg ) 1591 'mh-index-checksum-origin-map nil ; checksum -> ( orig-folder, orig-msg )
1592 'mh-index-sequence-search-flag nil ; folder resulted from sequence search
1579 'mh-first-msg-num nil ; Number of first msg in buffer 1593 'mh-first-msg-num nil ; Number of first msg in buffer
1580 'mh-last-msg-num nil ; Number of last msg in buffer 1594 'mh-last-msg-num nil ; Number of last msg in buffer
1581 'mh-msg-count nil ; Number of msgs in buffer 1595 'mh-msg-count nil ; Number of msgs in buffer
1582 'mh-mode-line-annotation nil ; Indicates message range 1596 'mh-mode-line-annotation nil ; Indicates message range
1597 'mh-sequence-notation-history (make-hash-table)
1598 ; Remember what is overwritten by
1599 ; mh-note-seq.
1583 'mh-previous-window-config nil) ; Previous window configuration 1600 'mh-previous-window-config nil) ; Previous window configuration
1584 (mh-remove-xemacs-horizontal-scrollbar) 1601 (mh-remove-xemacs-horizontal-scrollbar)
1585 (setq truncate-lines t) 1602 (setq truncate-lines t)
@@ -1597,8 +1614,7 @@ When a folder is visited, the hook `mh-folder-mode-hook' is run.
1597 (easy-menu-add mh-folder-sequence-menu) 1614 (easy-menu-add mh-folder-sequence-menu)
1598 (easy-menu-add mh-folder-message-menu) 1615 (easy-menu-add mh-folder-message-menu)
1599 (easy-menu-add mh-folder-folder-menu) 1616 (easy-menu-add mh-folder-folder-menu)
1600 (if (and (boundp 'tool-bar-mode) tool-bar-mode) 1617 (set (make-local-variable 'tool-bar-map) mh-folder-tool-bar-map)
1601 (set (make-local-variable 'tool-bar-map) mh-folder-tool-bar-map))
1602 (mh-funcall-if-exists mh-toolbar-init :folder) 1618 (mh-funcall-if-exists mh-toolbar-init :folder)
1603 (if (and mh-xemacs-flag 1619 (if (and mh-xemacs-flag
1604 font-lock-auto-fontify) 1620 font-lock-auto-fontify)
@@ -1611,6 +1627,15 @@ When a folder is visited, the hook `mh-folder-mode-hook' is run.
1611 (set (make-local-variable (car pairs)) (car (cdr pairs))) 1627 (set (make-local-variable (car pairs)) (car (cdr pairs)))
1612 (setq pairs (cdr (cdr pairs))))) 1628 (setq pairs (cdr (cdr pairs)))))
1613 1629
1630;;;###autoload
1631(defun mh-restore-desktop-buffer (desktop-buffer-file-name
1632 desktop-buffer-name
1633 desktop-buffer-misc)
1634 "Restore an MH folder buffer specified in a desktop file."
1635 (mh-find-path)
1636 (mh-visit-folder desktop-buffer-name)
1637 (current-buffer))
1638
1614(defun mh-scan-folder (folder range &optional dont-exec-pending) 1639(defun mh-scan-folder (folder range &optional dont-exec-pending)
1615 "Scan the FOLDER over the RANGE. 1640 "Scan the FOLDER over the RANGE.
1616If the optional argument DONT-EXEC-PENDING is non-nil then pending deletes and 1641If the optional argument DONT-EXEC-PENDING is non-nil then pending deletes and
@@ -1651,6 +1676,7 @@ If UPDATE, append the scan lines, otherwise replace."
1651 (range (if (and range (atom range)) (list range) range)) 1676 (range (if (and range (atom range)) (list range) range))
1652 scan-start) 1677 scan-start)
1653 (message "Scanning %s..." folder) 1678 (message "Scanning %s..." folder)
1679 (mh-remove-all-notation)
1654 (with-mh-folder-updating (nil) 1680 (with-mh-folder-updating (nil)
1655 (if update 1681 (if update
1656 (goto-char (point-max)) 1682 (goto-char (point-max))
@@ -1742,8 +1768,8 @@ Return in the current buffer."
1742 (message "inc %s..." folder)) 1768 (message "inc %s..." folder))
1743 (setq mh-next-direction 'forward) 1769 (setq mh-next-direction 'forward)
1744 (goto-char (point-max)) 1770 (goto-char (point-max))
1771 (mh-remove-all-notation)
1745 (let ((start-of-inc (point))) 1772 (let ((start-of-inc (point)))
1746 (mh-remove-cur-notation)
1747 (if maildrop-name 1773 (if maildrop-name
1748 ;; I think MH 5 used "-ms-file" instead of "-file", 1774 ;; I think MH 5 used "-ms-file" instead of "-file",
1749 ;; which would make inc'ing from maildrops fail. 1775 ;; which would make inc'ing from maildrops fail.
@@ -1763,11 +1789,12 @@ Return in the current buffer."
1763 (re-search-forward "^inc: no mail" nil t)) 1789 (re-search-forward "^inc: no mail" nil t))
1764 (message "No new mail%s%s" (if maildrop-name " in " "") 1790 (message "No new mail%s%s" (if maildrop-name " in " "")
1765 (if maildrop-name maildrop-name ""))) 1791 (if maildrop-name maildrop-name "")))
1766 ((and (when mh-narrowed-to-seq 1792 ((and (when mh-folder-view-stack
1767 (let ((saved-text (buffer-substring-no-properties 1793 (let ((saved-text (buffer-substring-no-properties
1768 start-of-inc (point-max)))) 1794 start-of-inc (point-max))))
1769 (delete-region start-of-inc (point-max)) 1795 (delete-region start-of-inc (point-max))
1770 (unwind-protect (mh-widen) 1796 (unwind-protect (mh-widen t)
1797 (mh-remove-all-notation)
1771 (goto-char (point-max)) 1798 (goto-char (point-max))
1772 (setq start-of-inc (point)) 1799 (setq start-of-inc (point))
1773 (insert saved-text) 1800 (insert saved-text)
@@ -1789,7 +1816,6 @@ Return in the current buffer."
1789 (setq mh-seq-list (mh-read-folder-sequences folder t)) 1816 (setq mh-seq-list (mh-read-folder-sequences folder t))
1790 (when (equal (point-max) start-of-inc) 1817 (when (equal (point-max) start-of-inc)
1791 (mh-notate-cur)) 1818 (mh-notate-cur))
1792 (mh-notate-user-sequences)
1793 (if new-mail-flag 1819 (if new-mail-flag
1794 (progn 1820 (progn
1795 (mh-make-folder-mode-line) 1821 (mh-make-folder-mode-line)
@@ -1798,7 +1824,9 @@ Return in the current buffer."
1798 (when (memq 'unthread mh-view-ops) 1824 (when (memq 'unthread mh-view-ops)
1799 (mh-thread-inc folder start-of-inc)) 1825 (mh-thread-inc folder start-of-inc))
1800 (mh-goto-cur-msg)) 1826 (mh-goto-cur-msg))
1801 (goto-char point-before-inc)))))) 1827 (goto-char point-before-inc))
1828 (mh-notate-user-sequences)
1829 (mh-notate-deleted-and-refiled)))))
1802 1830
1803(defun mh-make-folder-mode-line (&optional ignored) 1831(defun mh-make-folder-mode-line (&optional ignored)
1804 "Set the fields of the mode line for a folder buffer. 1832 "Set the fields of the mode line for a folder buffer.
@@ -1841,10 +1869,13 @@ in what is now stored in the buffer-local variable `mh-mode-line-annotation'."
1841 ("")))))) 1869 (""))))))
1842 (mh-logo-display)))) 1870 (mh-logo-display))))
1843 1871
1872;;; XXX: Remove this function, if no one uses it any more...
1844(defun mh-unmark-all-headers (remove-all-flags) 1873(defun mh-unmark-all-headers (remove-all-flags)
1845 "Remove all '+' flags from the folder listing. 1874 "Remove all '+' flags from the folder listing.
1846With non-nil argument REMOVE-ALL-FLAGS, remove all 'D', '^' and '%' flags too. 1875With non-nil argument REMOVE-ALL-FLAGS, remove all 'D', '^' and '%' flags too.
1847Optimized for speed (i.e., no regular expressions)." 1876Optimized for speed (i.e., no regular expressions).
1877
1878This function is deprecated. Use `mh-remove-all-notation' instead."
1848 (save-excursion 1879 (save-excursion
1849 (let ((case-fold-search nil) 1880 (let ((case-fold-search nil)
1850 (last-line (1- (point-max))) 1881 (last-line (1- (point-max)))
@@ -1869,6 +1900,39 @@ Optimized for speed (i.e., no regular expressions)."
1869 (insert " "))))) 1900 (insert " ")))))
1870 (forward-line))))) 1901 (forward-line)))))
1871 1902
1903(defun mh-add-sequence-notation (msg internal-seq-flag)
1904 "Add sequence notation to the MSG on the current line.
1905If INTERNAL-SEQ-FLAG is non-nil, then just remove text properties from the
1906current line, so that font-lock would automatically refontify it."
1907 (with-mh-folder-updating (t)
1908 (save-excursion
1909 (beginning-of-line)
1910 (if internal-seq-flag
1911 (mh-notate nil nil mh-cmd-note)
1912 (forward-char (1+ mh-cmd-note))
1913 (let ((stack (gethash msg mh-sequence-notation-history)))
1914 (setf (gethash msg mh-sequence-notation-history)
1915 (cons (char-after) stack)))
1916 (mh-notate nil mh-note-seq (1+ mh-cmd-note))))))
1917
1918(defun mh-remove-sequence-notation (msg internal-seq-flag &optional all)
1919 "Remove sequence notation from the MSG on the current line.
1920If INTERNAL-SEQ-FLAG is non-nil, then `font-lock' was used to highlight the
1921sequence. In that case, no notation needs to be removed. Otherwise the effect
1922of inserting `mh-note-seq' needs to be reversed.
1923If ALL is non-nil, then all sequence marks on the scan line are removed."
1924 (with-mh-folder-updating (t)
1925 ;; This takes care of internal sequences...
1926 (mh-notate nil nil mh-cmd-note)
1927 (unless internal-seq-flag
1928 ;; ... and this takes care of user sequences.
1929 (let ((stack (gethash msg mh-sequence-notation-history)))
1930 (while (and all (cdr stack))
1931 (setq stack (cdr stack)))
1932 (when stack
1933 (mh-notate nil (car stack) (1+ mh-cmd-note)))
1934 (setf (gethash msg mh-sequence-notation-history) (cdr stack))))))
1935
1872(defun mh-remove-cur-notation () 1936(defun mh-remove-cur-notation ()
1873 "Remove old cur notation." 1937 "Remove old cur notation."
1874 (let ((cur-msg (car (mh-seq-to-msgs 'cur)))) 1938 (let ((cur-msg (car (mh-seq-to-msgs 'cur))))
@@ -1884,12 +1948,10 @@ Optimized for speed (i.e., no regular expressions)."
1884 (save-excursion 1948 (save-excursion
1885 (setq overlay-arrow-position nil) 1949 (setq overlay-arrow-position nil)
1886 (goto-char (point-min)) 1950 (goto-char (point-min))
1887 (while (not (eobp)) 1951 (mh-iterate-on-range msg (cons (point-min) (point-max))
1888 (unless (or (equal (char-after) ?+) (eolp)) 1952 (mh-notate nil ? mh-cmd-note)
1889 (mh-notate nil ? mh-cmd-note) 1953 (mh-remove-sequence-notation msg nil t))
1890 (when (eq (char-after (+ (point) mh-cmd-note 1)) (elt mh-note-seq 0)) 1954 (clrhash mh-sequence-notation-history)))
1891 (mh-notate nil ? (1+ mh-cmd-note))))
1892 (forward-line))))
1893 1955
1894;;;###mh-autoload 1956;;;###mh-autoload
1895(defun mh-goto-cur-msg (&optional minimal-changes-flag) 1957(defun mh-goto-cur-msg (&optional minimal-changes-flag)
@@ -1934,22 +1996,47 @@ with no arguments, before the commands are processed."
1934 ;; Update the unseen sequence if it exists 1996 ;; Update the unseen sequence if it exists
1935 (mh-update-unseen) 1997 (mh-update-unseen)
1936 1998
1937 (let ((redraw-needed-flag mh-index-data)) 1999 (let ((redraw-needed-flag mh-index-data)
2000 (folders-changed (list mh-current-folder))
2001 (seq-map (and mh-refile-list mh-refile-preserves-sequences-flag
2002 (mh-create-sequence-map mh-seq-list)))
2003 (dest-map (and mh-refile-list mh-refile-preserves-sequences-flag
2004 (make-hash-table))))
1938 ;; Remove invalid scan lines if we are in an index folder and then remove 2005 ;; Remove invalid scan lines if we are in an index folder and then remove
1939 ;; the real messages 2006 ;; the real messages
1940 (when mh-index-data 2007 (when mh-index-data
1941 (mh-index-delete-folder-headers) 2008 (mh-index-delete-folder-headers)
1942 (mh-index-execute-commands)) 2009 (setq folders-changed
2010 (append folders-changed (mh-index-execute-commands))))
1943 2011
1944 ;; Then refile messages 2012 ;; Then refile messages
1945 (mh-mapc #'(lambda (folder-msg-list) 2013 (mh-mapc #'(lambda (folder-msg-list)
1946 (let ((dest-folder (symbol-name (car folder-msg-list))) 2014 (let* ((dest-folder (symbol-name (car folder-msg-list)))
1947 (msgs (cdr folder-msg-list))) 2015 (last (car (mh-translate-range dest-folder "last")))
2016 (msgs (cdr folder-msg-list)))
2017 (push dest-folder folders-changed)
1948 (setq redraw-needed-flag t) 2018 (setq redraw-needed-flag t)
1949 (apply #'mh-exec-cmd 2019 (apply #'mh-exec-cmd
1950 "refile" "-src" folder dest-folder 2020 "refile" "-src" folder dest-folder
1951 (mh-coalesce-msg-list msgs)) 2021 (mh-coalesce-msg-list msgs))
1952 (mh-delete-scan-msgs msgs))) 2022 (mh-delete-scan-msgs msgs)
2023 ;; Preserve sequences in destination folder...
2024 (when (and mh-refile-preserves-sequences-flag
2025 (numberp last))
2026 (clrhash dest-map)
2027 (loop for i from (1+ last)
2028 for msg in (sort (copy-sequence msgs) #'<)
2029 do (loop for seq-name in (gethash msg seq-map)
2030 do (push i (gethash seq-name dest-map))))
2031 (maphash
2032 #'(lambda (seq msgs)
2033 ;; Run it in the background, since we don't care
2034 ;; about the results.
2035 (apply #'mh-exec-cmd-daemon "mark" #'ignore
2036 "-sequence" (symbol-name seq) dest-folder
2037 "-add" (mapcar #'(lambda (x) (format "%s" x))
2038 (mh-coalesce-msg-list msgs))))
2039 dest-map))))
1953 mh-refile-list) 2040 mh-refile-list)
1954 (setq mh-refile-list ()) 2041 (setq mh-refile-list ())
1955 2042
@@ -1969,7 +2056,7 @@ with no arguments, before the commands are processed."
1969 ;; Redraw folder buffer if needed 2056 ;; Redraw folder buffer if needed
1970 (when (and redraw-needed-flag) 2057 (when (and redraw-needed-flag)
1971 (when (mh-speed-flists-active-p) 2058 (when (mh-speed-flists-active-p)
1972 (mh-speed-flists t mh-current-folder)) 2059 (apply #'mh-speed-flists t folders-changed))
1973 (cond ((memq 'unthread mh-view-ops) (mh-thread-inc folder (point-max))) 2060 (cond ((memq 'unthread mh-view-ops) (mh-thread-inc folder (point-max)))
1974 (mh-index-data (mh-index-insert-folder-headers))))) 2061 (mh-index-data (mh-index-insert-folder-headers)))))
1975 2062
@@ -1980,7 +2067,7 @@ with no arguments, before the commands are processed."
1980 (mh-invalidate-show-buffer)) 2067 (mh-invalidate-show-buffer))
1981 2068
1982 (setq mh-seq-list (mh-read-folder-sequences mh-current-folder nil)) 2069 (setq mh-seq-list (mh-read-folder-sequences mh-current-folder nil))
1983 (mh-unmark-all-headers t) 2070 (mh-remove-all-notation)
1984 (mh-notate-user-sequences) 2071 (mh-notate-user-sequences)
1985 (message "Processing deletes and refiles for %s...done" folder))) 2072 (message "Processing deletes and refiles for %s...done" folder)))
1986 2073
@@ -2115,55 +2202,67 @@ Expands ranges into set of individual numbers."
2115 (setq msgs (cons num msgs))))) 2202 (setq msgs (cons num msgs)))))
2116 msgs)) 2203 msgs))
2117 2204
2118(defun mh-notate-user-sequences (&optional msg-or-seq) 2205(defun mh-notate-user-sequences (&optional range)
2119 "Mark user-defined sequences in the messages specified by MSG-OR-SEQ. 2206 "Mark user-defined sequences in the messages specified by RANGE.
2120The optional argument MSG-OR-SEQ can be a message number, a list of message 2207The optional argument RANGE can be a message number, a list of message
2121numbers, a sequence, a region in a cons cell, or nil in which case all 2208numbers, a sequence, a region in a cons cell. If nil all messages are notated."
2122messages in the folder buffer are notated." 2209 (unless range
2123 (unless msg-or-seq 2210 (setq range (cons (point-min) (point-max))))
2124 (setq msg-or-seq (cons (point-min) (point-max))))
2125 (let ((seqs mh-seq-list) 2211 (let ((seqs mh-seq-list)
2126 (msg-hash (make-hash-table)) 2212 (msg-hash (make-hash-table)))
2127 (tick-msgs (and mh-tick-seq (mh-seq-msgs (mh-find-seq mh-tick-seq)))))
2128 (dolist (seq seqs) 2213 (dolist (seq seqs)
2129 (unless (mh-internal-seq (mh-seq-name seq)) 2214 (dolist (msg (mh-seq-msgs seq))
2130 (dolist (msg (mh-seq-msgs seq)) 2215 (push (car seq) (gethash msg msg-hash))))
2131 (setf (gethash msg msg-hash) t)))) 2216 (mh-iterate-on-range msg range
2132 (mh-iterate-on-msg-or-seq msg msg-or-seq 2217 (loop for seq in (gethash msg msg-hash)
2133 (when (gethash msg msg-hash) 2218 do (mh-add-sequence-notation msg (mh-internal-seq seq))))))
2134 (mh-notate nil mh-note-seq (1+ mh-cmd-note))) 2219
2135 (mh-notate-tick msg tick-msgs)))) 2220(defvar mh-internal-seqs '(answered cur deleted forwarded printed))
2136 2221
2137(defun mh-internal-seq (name) 2222(defun mh-internal-seq (name)
2138 "Return non-nil if NAME is the name of an internal MH-E sequence." 2223 "Return non-nil if NAME is the name of an internal MH-E sequence."
2139 (or (memq name '(answered cur deleted forwarded printed)) 2224 (or (memq name mh-internal-seqs)
2140 (eq name mh-unseen-seq) 2225 (eq name mh-unseen-seq)
2141 (and mh-tick-seq (eq name mh-tick-seq)) 2226 (and mh-tick-seq (eq name mh-tick-seq))
2142 (eq name mh-previous-seq) 2227 (eq name mh-previous-seq)
2143 (mh-folder-name-p name))) 2228 (mh-folder-name-p name)))
2144 2229
2145(defun mh-delete-msg-from-seq (msg-or-seq sequence &optional internal-flag) 2230(defun mh-valid-seq-p (name)
2146 "Delete MSG-OR-SEQ from SEQUENCE. 2231 "Return non-nil if NAME is a valid MH sequence name."
2147Default value of MSG-OR-SEQ is the displayed message. 2232 (and (symbolp name)
2148If optional prefix argument is provided, then prompt for the message sequence. 2233 (string-match "^[a-zA-Z][a-zA-Z0-9]*$" (symbol-name name))))
2149If variable `transient-mark-mode' is non-nil and the mark is active, then the 2234
2150selected region is deleted from SEQUENCE.. 2235(defun mh-delete-msg-from-seq (range sequence &optional internal-flag)
2151In a program, MSG-OR-SEQ can be a message number, a list of message numbers, a 2236 "Delete RANGE from SEQUENCE.
2152region in a cons cell, or a sequence; optional third arg INTERNAL-FLAG non-nil 2237
2153means do not inform MH of the change." 2238Check the documentation of `mh-interactive-range' to see how RANGE is read in
2154 (interactive (list (mh-interactive-msg-or-seq "Delete") 2239interactive use.
2240
2241Optional third arg INTERNAL-FLAG non-nil means do not inform MH of the
2242change."
2243 (interactive (list (mh-interactive-range "Delete")
2155 (mh-read-seq-default "Delete from" t) 2244 (mh-read-seq-default "Delete from" t)
2156 nil)) 2245 nil))
2157 (let ((entry (mh-find-seq sequence))) 2246 (let ((entry (mh-find-seq sequence))
2247 (user-sequence-flag (not (mh-internal-seq sequence)))
2248 (folders-changed (list mh-current-folder))
2249 (msg-list ()))
2158 (when entry 2250 (when entry
2159 (mh-iterate-on-msg-or-seq msg msg-or-seq 2251 (mh-iterate-on-range msg range
2160 (when (memq msg (mh-seq-msgs entry)) 2252 (push msg msg-list)
2161 (mh-notate nil ? (1+ mh-cmd-note))) 2253 ;; Calling "mark" repeatedly takes too long. So we will pretend here
2162 (mh-delete-a-msg-from-seq msg sequence internal-flag) 2254 ;; that we are just modifying an internal sequence...
2163 (mh-clear-text-properties nil)) 2255 (when (memq msg (cdr entry))
2164 (mh-notate-user-sequences msg-or-seq) 2256 (mh-remove-sequence-notation msg (not user-sequence-flag)))
2257 (mh-delete-a-msg-from-seq msg sequence t))
2258 ;; ... and here we will "mark" all the messages at one go.
2259 (unless internal-flag (mh-undefine-sequence sequence msg-list))
2260 (when (and mh-index-data (not internal-flag))
2261 (setq folders-changed
2262 (append folders-changed
2263 (mh-index-delete-from-sequence sequence msg-list))))
2165 (when (and (eq sequence mh-unseen-seq) (mh-speed-flists-active-p)) 2264 (when (and (eq sequence mh-unseen-seq) (mh-speed-flists-active-p))
2166 (mh-speed-flists t mh-current-folder))))) 2265 (apply #'mh-speed-flists t folders-changed)))))
2167 2266
2168(defun mh-delete-a-msg-from-seq (msg sequence internal-flag) 2267(defun mh-delete-a-msg-from-seq (msg sequence internal-flag)
2169 "Delete MSG from SEQUENCE. 2268 "Delete MSG from SEQUENCE.
@@ -2174,31 +2273,18 @@ If INTERNAL-FLAG is non-nil, then do not inform MH of the change."
2174 (mh-undefine-sequence sequence (list msg))) 2273 (mh-undefine-sequence sequence (list msg)))
2175 (setcdr entry (delq msg (mh-seq-msgs entry)))))) 2274 (setcdr entry (delq msg (mh-seq-msgs entry))))))
2176 2275
2177(defun mh-clear-text-properties (message)
2178 "Clear all text properties (except mh-tick) from the scan line for MESSAGE."
2179 (save-excursion
2180 (with-mh-folder-updating (t)
2181 (when (or (not message) (mh-goto-msg message t t))
2182 (beginning-of-line)
2183 (let ((tick-property (get-text-property (point) 'mh-tick)))
2184 (set-text-properties (point) (line-end-position) nil)
2185 (when tick-property
2186 (add-text-properties (point) (line-end-position)
2187 `(mh-tick ,tick-property))))))))
2188
2189(defun mh-undefine-sequence (seq msgs) 2276(defun mh-undefine-sequence (seq msgs)
2190 "Remove from the SEQ the list of MSGS." 2277 "Remove from the SEQ the list of MSGS."
2191 (prog1 (mh-exec-cmd "mark" mh-current-folder "-delete" 2278 (when (and (mh-valid-seq-p seq) msgs)
2192 "-sequence" (symbol-name seq) 2279 (apply #'mh-exec-cmd "mark" mh-current-folder "-delete"
2193 (mh-coalesce-msg-list msgs)) 2280 "-sequence" (symbol-name seq) (mh-coalesce-msg-list msgs))))
2194 (when (and (eq seq mh-unseen-seq) (mh-speed-flists-active-p))
2195 (mh-speed-flists t mh-current-folder))))
2196 2281
2197(defun mh-define-sequence (seq msgs) 2282(defun mh-define-sequence (seq msgs)
2198 "Define the SEQ to contain the list of MSGS. 2283 "Define the SEQ to contain the list of MSGS.
2199Do not mark pseudo-sequences or empty sequences. 2284Do not mark pseudo-sequences or empty sequences.
2200Signals an error if SEQ is an illegal name." 2285Signals an error if SEQ is an illegal name."
2201 (if (and msgs 2286 (if (and msgs
2287 (mh-valid-seq-p seq)
2202 (not (mh-folder-name-p seq))) 2288 (not (mh-folder-name-p seq)))
2203 (save-excursion 2289 (save-excursion
2204 (mh-exec-cmd-error nil "mark" mh-current-folder "-add" "-zero" 2290 (mh-exec-cmd-error nil "mark" mh-current-folder "-add" "-zero"
@@ -2237,31 +2323,6 @@ If INCLUDE-INTERNAL-FLAG non-nil, include MH-E internal sequences in list."
2237 2323
2238 2324
2239 2325
2240;;; User prompting commands.
2241
2242(defun mh-read-msg-range (folder &optional always-prompt-flag)
2243 "Prompt for message range from FOLDER.
2244If optional second argument ALWAYS-PROMPT-FLAG is non-nil then always ask for
2245range."
2246 (multiple-value-bind (total unseen) (mh-folder-size folder)
2247 (cond
2248 ((and (not always-prompt-flag) (numberp unseen) (> unseen 0))
2249 (list (symbol-name mh-unseen-seq)))
2250 ((or (null mh-large-folder) (not (numberp total)))
2251 (list "all"))
2252 ((and (numberp total) (or always-prompt-flag (> total mh-large-folder)))
2253 (let* ((prompt
2254 (format "Range or number of messages to read (default: %s): "
2255 total))
2256 (in (read-string prompt nil nil (number-to-string total))))
2257 (cond ((string-match "^[ \f\t\n\r\v]*[0-9]+[ \f\t\n\r\v]*$" in)
2258 (list (format "last:%s" (car (read-from-string in)))))
2259 ((equal in "") (list "all"))
2260 (t (split-string in)))))
2261 (t (list "all")))))
2262
2263
2264
2265;;; Build the folder-mode keymap: 2326;;; Build the folder-mode keymap:
2266 2327
2267(suppress-keymap mh-folder-mode-map) 2328(suppress-keymap mh-folder-mode-map)
@@ -2319,6 +2380,7 @@ range."
2319 2380
2320(gnus-define-keys (mh-folder-map "F" mh-folder-mode-map) 2381(gnus-define-keys (mh-folder-map "F" mh-folder-mode-map)
2321 "?" mh-prefix-help 2382 "?" mh-prefix-help
2383 "'" mh-index-ticked-messages
2322 "S" mh-sort-folder 2384 "S" mh-sort-folder
2323 "f" mh-alt-visit-folder 2385 "f" mh-alt-visit-folder
2324 "i" mh-index-search 2386 "i" mh-index-search
@@ -2327,6 +2389,7 @@ range."
2327 "n" mh-index-new-messages 2389 "n" mh-index-new-messages
2328 "o" mh-alt-visit-folder 2390 "o" mh-alt-visit-folder
2329 "p" mh-pack-folder 2391 "p" mh-pack-folder
2392 "q" mh-index-sequenced-messages
2330 "r" mh-rescan-folder 2393 "r" mh-rescan-folder
2331 "s" mh-search-folder 2394 "s" mh-search-folder
2332 "u" mh-undo-folder 2395 "u" mh-undo-folder
@@ -2340,6 +2403,7 @@ range."
2340 "w" mh-junk-whitelist) 2403 "w" mh-junk-whitelist)
2341 2404
2342(gnus-define-keys (mh-sequence-map "S" mh-folder-mode-map) 2405(gnus-define-keys (mh-sequence-map "S" mh-folder-mode-map)
2406 "'" mh-narrow-to-tick
2343 "?" mh-prefix-help 2407 "?" mh-prefix-help
2344 "d" mh-delete-msg-from-seq 2408 "d" mh-delete-msg-from-seq
2345 "k" mh-delete-seq 2409 "k" mh-delete-seq
@@ -2361,7 +2425,11 @@ range."
2361(gnus-define-keys (mh-limit-map "/" mh-folder-mode-map) 2425(gnus-define-keys (mh-limit-map "/" mh-folder-mode-map)
2362 "'" mh-narrow-to-tick 2426 "'" mh-narrow-to-tick
2363 "?" mh-prefix-help 2427 "?" mh-prefix-help
2428 "c" mh-narrow-to-cc
2429 "f" mh-narrow-to-from
2430 "r" mh-narrow-to-range
2364 "s" mh-narrow-to-subject 2431 "s" mh-narrow-to-subject
2432 "t" mh-narrow-to-to
2365 "w" mh-widen) 2433 "w" mh-widen)
2366 2434
2367(gnus-define-keys (mh-extract-map "X" mh-folder-mode-map) 2435(gnus-define-keys (mh-extract-map "X" mh-folder-mode-map)
@@ -2411,16 +2479,16 @@ range."
2411 "[d]elete, [o]refile, e[x]ecute,\n" 2479 "[d]elete, [o]refile, e[x]ecute,\n"
2412 "[s]end, [r]eply.\n" 2480 "[s]end, [r]eply.\n"
2413 "Prefix characters:\n [F]older, [S]equence, [J]unk, MIME [K]eys," 2481 "Prefix characters:\n [F]older, [S]equence, [J]unk, MIME [K]eys,"
2414 "\n [T]hread, / Limit, e[X]tract, [D]igest, [I]nc spools.") 2482 "\n [T]hread, [/]limit, e[X]tract, [D]igest, [I]nc spools.")
2415 2483
2416 (?F "[l]ist, [v]isit folder;\n" 2484 (?F "[l]ist; [v]isit folder;\n"
2417 "[t]hread; [s]earch; [i]ndexed search;\n" 2485 "[n]ew messages; [']ticked messages; [s]earch; [i]ndexed search;\n"
2418 "[p]ack; [S]ort; [r]escan; [k]ill") 2486 "[p]ack; [S]ort; [r]escan; [k]ill")
2419 (?S "[p]ut message in sequence, [n]arrow, [w]iden,\n" 2487 (?S "[p]ut message in sequence, [n]arrow, [']narrow to ticked, [w]iden,\n"
2420 "[s]equences, [l]ist,\n" 2488 "[s]equences, [l]ist,\n"
2421 "[d]elete message from sequence, [k]ill sequence") 2489 "[d]elete message from sequence, [k]ill sequence")
2422 (?T "[t]oggle, [d]elete, [o]refile thread") 2490 (?T "[t]oggle, [d]elete, [o]refile thread")
2423 (?/ "Limit to [s]ubject; [w]iden") 2491 (?/ "Limit to [c]c, [f]rom, [r]ange, [s]ubject, [t]o; [w]iden")
2424 (?X "un[s]har, [u]udecode message") 2492 (?X "un[s]har, [u]udecode message")
2425 (?D "[b]urst digest") 2493 (?D "[b]urst digest")
2426 (?K "[v]iew, [i]nline, [o]utput/save MIME part; save [a]ll parts; \n" 2494 (?K "[v]iew, [i]nline, [o]utput/save MIME part; save [a]ll parts; \n"
@@ -2443,17 +2511,6 @@ well.")
2443 "^There is no other window$")) 2511 "^There is no other window$"))
2444 (add-to-list 'debug-ignored-errors mess)) 2512 (add-to-list 'debug-ignored-errors mess))
2445 2513
2446;;;; Desktop support
2447
2448;;;###autoload
2449(defun mh-restore-desktop-buffer (desktop-buffer-file-name
2450 desktop-buffer-name
2451 desktop-buffer-misc)
2452 "Restore an mh folder buffer specified in a desktop file."
2453 (mh-find-path)
2454 (mh-visit-folder desktop-buffer-name)
2455 (current-buffer))
2456
2457(provide 'mh-e) 2514(provide 'mh-e)
2458 2515
2459;;; Local Variables: 2516;;; Local Variables: