diff lisp/vm/vm-undo.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children 859a2309aef8
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/vm/vm-undo.el	Mon Aug 13 08:45:50 2007 +0200
@@ -0,0 +1,465 @@
+;;; Commands to undo message attribute changes in VM
+;;; Copyright (C) 1989, 1990, 1993, 1994 Kyle E. Jones
+;;;
+;;; This program is free software; you can redistribute it and/or modify
+;;; it under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 1, or (at your option)
+;;; any later version.
+;;;
+;;; This program is distributed in the hope that it will be useful,
+;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with this program; if not, write to the Free Software
+;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+(provide 'vm-undo)
+
+(defun vm-set-buffer-modified-p (flag &optional buffer)
+  (save-excursion
+    (and buffer (set-buffer buffer))
+    (set-buffer-modified-p flag)
+    (vm-increment vm-modification-counter)
+    (intern (buffer-name) vm-buffers-needing-display-update)
+    (if (null flag)
+	(setq vm-messages-not-on-disk 0))))
+
+(defun vm-undo-boundary ()
+  (if (car vm-undo-record-list)
+      (setq vm-undo-record-list (cons nil vm-undo-record-list))))
+
+(defun vm-clear-expunge-invalidated-undos ()
+  (let ((udp vm-undo-record-list) udp-prev)
+    (while udp
+      (cond ((null (car udp))
+	     (setq udp-prev udp))
+	    ((and (not (eq (car (car udp)) 'vm-set-buffer-modified-p))
+		  ;; delete flag == expunged is the
+		  ;; indicator of an expunged message
+		  (eq (vm-deleted-flag (car (cdr (car udp)))) 'expunged))
+	     (cond (udp-prev (setcdr udp-prev (cdr udp)))
+		   (t (setq vm-undo-record-list (cdr udp)))))
+	    (t (setq udp-prev udp)))
+      (setq udp (cdr udp))))
+  (vm-clear-modification-flag-undos))
+	    
+(defun vm-clear-virtual-quit-invalidated-undos ()
+  (let ((udp vm-undo-record-list) udp-prev)
+    (while udp
+      (cond ((null (car udp))
+	     (setq udp-prev udp))
+	    ((and (not (eq (car (car udp)) 'vm-set-buffer-modified-p))
+		  ;; message-id-number == "Q" is the
+		  ;; indicator of a dead message
+		  (equal (vm-message-id-number-of (car (cdr (car udp)))) "Q"))
+	     (cond (udp-prev (setcdr udp-prev (cdr udp)))
+		   (t (setq vm-undo-record-list (cdr udp)))))
+	    (t (setq udp-prev udp)))
+      (setq udp (cdr udp))))
+  (vm-clear-modification-flag-undos))
+	    
+(defun vm-clear-modification-flag-undos ()
+  (let ((udp vm-undo-record-list) udp-prev)
+    (while udp
+      (cond ((null (car udp))
+	     (setq udp-prev udp))
+	    ((eq (car (car udp)) 'vm-set-buffer-modified-p)
+	     (cond (udp-prev (setcdr udp-prev (cdr udp)))
+		   (t (setq vm-undo-record-list (cdr udp)))))
+	    (t (setq udp-prev udp)))
+      (setq udp (cdr udp)))
+    (vm-squeeze-consecutive-undo-boundaries)))
+
+;; squeeze out consecutive record separators left by record deletions
+(defun vm-squeeze-consecutive-undo-boundaries ()
+  (let ((udp vm-undo-record-list) udp-prev)
+    (while udp
+      (cond ((and (null (car udp)) udp-prev (null (car udp-prev)))
+	     (setcdr udp-prev (cdr udp)))
+	    (t (setq udp-prev udp)))
+      (setq udp (cdr udp)))
+    (if (equal '(nil) vm-undo-record-list)
+	(setq vm-undo-record-list nil)))
+  ;; for the Undo button on the menubar, if present
+  (and (null vm-undo-record-list)
+       (vm-menu-support-possible-p)
+       (vm-menu-xemacs-menus-p)
+       (vm-menu-set-menubar-dirty-flag)))
+	    
+(defun vm-undo-record (sexp)
+  ;; for the Undo button on the menubar, if present
+  (and (null vm-undo-record-list)
+       (vm-menu-support-possible-p)
+       (vm-menu-xemacs-menus-p)
+       (vm-menu-set-menubar-dirty-flag))
+  (setq vm-undo-record-list (cons sexp vm-undo-record-list)))
+
+(defun vm-undo-describe (record)
+  (let ((cell
+	 (assq (car record)
+	       '((vm-set-new-flag "new" "old")
+		 (vm-set-unread-flag "unread" "read")
+		 (vm-set-deleted-flag "deleted" "undeleted")
+		 (vm-set-forwarded-flag "forwarded" "unforwarded")
+		 (vm-set-replied-flag "answered" "unanswered")
+		 (vm-set-redistributed-flag "redistributed" "unredistributed")
+		 (vm-set-filed-flag "filed" "unfiled")
+		 (vm-set-written-flag "written" "unwritten"))))
+	(m (nth 1 record))
+	labels)
+    (cond (cell
+	   (message "VM Undo! %s/%s %s -> %s"
+		    (buffer-name (vm-buffer-of m))
+		    (vm-number-of m)
+		    (if (nth 2 record)
+			(nth 2 cell)
+		      (nth 1 cell))
+		    (if (nth 2 record)
+			(nth 1 cell)
+		      (nth 2 cell))))
+	  ((eq (car cell) 'vm-set-labels)
+	   (setq labels (nth 2 record))
+	   (message "VM Undo! %s/%s %s%s"
+		    (buffer-name (vm-buffer-of m))
+		    (vm-number-of m)
+		    (if (null labels)
+			"lost all its labels"
+		      "labels set to ")
+		    (if (null labels)
+			""
+		      (mapconcat 'identity labels ", ")))))))
+
+(defun vm-undo-set-message-pointer (record)
+  (if (and (not (eq (car record) 'vm-set-buffer-modified-p))
+	   (not (eq (nth 1 record) vm-message-pointer)))
+      (progn
+	(vm-record-and-change-message-pointer
+	 vm-message-pointer
+	 (or (cdr (vm-reverse-link-of (nth 1 record)))
+	     vm-message-list))
+	;; make folder read-only to avoid modifications when we
+	;; do this.
+	(let ((vm-folder-read-only t))
+	  (vm-preview-current-message)))))
+
+(defun vm-undo ()
+  "Undo last change to message attributes in the current folder.
+Consecutive invocations of this command cause sequentially earlier
+changes to be undone.  After an intervening command between undos,
+the undos themselves become undoable."
+  (interactive)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-display nil nil '(vm-undo) '(vm-undo))
+  (let ((modified (buffer-modified-p)))
+    (if (not (eq last-command 'vm-undo))
+	(setq vm-undo-record-pointer vm-undo-record-list))
+    (if (not vm-undo-record-pointer)
+	(error "No further VM undo information available"))
+    ;; skip current record boundary
+    (setq vm-undo-record-pointer (cdr vm-undo-record-pointer))
+    (while (car vm-undo-record-pointer)
+      (vm-undo-set-message-pointer (car vm-undo-record-pointer))
+      (vm-undo-describe (car vm-undo-record-pointer))
+      (eval (car vm-undo-record-pointer))
+      (setq vm-undo-record-pointer (cdr vm-undo-record-pointer)))
+    (and modified (not (buffer-modified-p))
+	 (delete-auto-save-file-if-necessary))
+    (vm-update-summary-and-mode-line)))
+
+(defun vm-set-message-attributes (string count)
+  "Set message attributes.
+Use this command to change attributes like `deleted' or
+`replied'.  Interactively you will be prompted for the attributes
+to be changed, and only the attributes you enter will be altered.
+You can use completion to expand the attribute names.  The names
+should be entered as a space separated list.
+
+A numeric prefix argument COUNT causes the current message and
+the next COUNT-1 message to have their attributes altered.  A
+negative COUNT arg causes the current message and the previous
+COUNT-1 messages to be altered.  COUNT defaults to one."
+  (interactive
+   (let ((last-command last-command)
+	 (this-command this-command))
+     ;; so the user can see what message they are about to
+     ;; modify.
+     (vm-follow-summary-cursor)
+     (list
+      (vm-read-string "Set attributes: " vm-supported-attribute-names t)
+      (prefix-numeric-value current-prefix-arg))))
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-read-only)
+  (vm-error-if-folder-empty)
+  (vm-display nil nil '(vm-set-message-attributes)
+	      '(vm-set-message-attributes))
+  (let ((name-list (vm-parse string "[ \t]*\\([^ \t]+\\)"))
+	(m-list (vm-select-marked-or-prefixed-messages count))
+	n-list name m)
+    (while m-list
+      (setq m (car m-list)
+	    n-list name-list)
+      (while n-list
+	(setq name (car n-list))
+	(cond ((string= name "new")
+	       (vm-set-new-flag m t))
+	      ((string= name "recent")
+	       (vm-set-new-flag m t))
+	      ((string= name "unread")
+	       (vm-set-unread-flag m t))
+	      ((string= name "unseen")
+	       (vm-set-unread-flag m t))
+	      ((string= name "read")
+	       (vm-set-new-flag m nil)
+	       (vm-set-unread-flag m nil))
+	      ((string= name "deleted")
+	       (vm-set-deleted-flag m t))
+	      ((string= name "replied")
+	       (vm-set-replied-flag m t))
+	      ((string= name "answered")
+	       (vm-set-replied-flag m t))
+	      ((string= name "forwarded")
+	       (vm-set-forwarded-flag m t))
+	      ((string= name "redistributed")
+	       (vm-set-redistributed-flag m t))
+	      ((string= name "filed")
+	       (vm-set-filed-flag m t))
+	      ((string= name "written")
+	       (vm-set-written-flag m t))
+	      ((string= name "edited")
+	       (vm-set-edited-flag-of m t))
+	      ((string= name "undeleted")
+	       (vm-set-deleted-flag m nil))
+	      ((string= name "unreplied")
+	       (vm-set-replied-flag m nil))
+	      ((string= name "unanswered")
+	       (vm-set-replied-flag m nil))
+	      ((string= name "unforwarded")
+	       (vm-set-forwarded-flag m nil))
+	      ((string= name "unredistributed")
+	       (vm-set-redistributed-flag m nil))
+	      ((string= name "unfiled")
+	       (vm-set-filed-flag m nil))
+	      ((string= name "unwritten")
+	       (vm-set-written-flag m nil))
+	      ((string= name "unedited")
+	       (vm-set-edited-flag-of m nil)))
+	(setq n-list (cdr n-list)))
+      (setq m-list (cdr m-list)))
+    (vm-update-summary-and-mode-line)))
+
+(defun vm-add-message-labels (string count)
+  "Attach some labels to a message.
+These are arbitrary user-defined labels, not to be confused with
+message attributes like `new' and `deleted'.  Interactively you
+will be prompted for the labels to be added.  You can use
+completion to expand the label names, with the completion list
+being all the labels that have ever been used in this folder.
+The names should be entered as a space separated list.  Label
+names are compared case-insensitively.
+
+A numeric prefix argument COUNT causes the current message and
+the next COUNT-1 message to have the labels added.  A
+negative COUNT arg causes the current message and the previous
+COUNT-1 messages to be altered.  COUNT defaults to one."
+  (interactive
+   (let ((last-command last-command)
+	 (this-command this-command)
+	 (vm-completion-auto-correct nil)
+	 (completion-ignore-case t))
+     ;; so the user can see what message they are about to
+     ;; modify.
+     (vm-follow-summary-cursor)
+     (vm-select-folder-buffer)
+     (list
+      (vm-read-string "Add labels: "
+		      (vm-obarray-to-string-list vm-label-obarray) t)
+      (prefix-numeric-value current-prefix-arg))))
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-read-only)
+  (vm-error-if-folder-empty)
+  (vm-add-or-delete-message-labels string count t))
+
+(defun vm-delete-message-labels (string count)
+  "Delete some labels from a message.
+These are arbitrary user-defined labels, not to be confused with
+message attributes like `new' and `deleted'.  Interactively you
+will be prompted for the labels to be deleted.  You can use
+completion to expand the label names, with the completion list
+being all the labels that have ever been used in this folder.
+The names should be entered as a space separated list.  Label
+names are compared case-insensitively.
+
+A numeric prefix argument COUNT causes the current message and
+the next COUNT-1 message to have the labels deleted.  A
+negative COUNT arg causes the current message and the previous
+COUNT-1 messages to be altered.  COUNT defaults to one."
+  (interactive
+   (let ((last-command last-command)
+	 (this-command this-command)
+	 (vm-completion-auto-correct nil)
+	 (completion-ignore-case t))
+     ;; so the user can see what message they are about to
+     ;; modify.
+     (vm-follow-summary-cursor)
+     (vm-select-folder-buffer)
+     (list
+      (vm-read-string "Delete labels: "
+		      (vm-obarray-to-string-list vm-label-obarray) t)
+      (prefix-numeric-value current-prefix-arg))))
+  (vm-follow-summary-cursor)
+  (vm-select-folder-buffer)
+  (vm-check-for-killed-summary)
+  (vm-error-if-folder-read-only)
+  (vm-error-if-folder-empty)
+  (vm-add-or-delete-message-labels string count nil))
+
+(defun vm-add-or-delete-message-labels (string count add)
+  (vm-display nil nil '(vm-add-message-labels vm-delete-message-labels)
+	      (list this-command))
+  (setq string (downcase string))
+  (let ((m-list (vm-select-marked-or-prefixed-messages count))
+	(action-labels (vm-parse string
+"[\000-\040,\177-\377]*\\([^\000-\040,\177-\377]+\\)[\000-\040,\177-\377]*"))
+	labels act-labels)
+    (if (and add m-list)
+	(progn
+	  (setq act-labels action-labels)
+	  (while act-labels
+	    (intern (car act-labels) vm-label-obarray)
+	    (setq act-labels (cdr act-labels)))))
+    (while m-list
+      (setq act-labels action-labels
+	    labels (copy-sequence (vm-labels-of (car m-list))))
+      (if add
+	  (while act-labels
+	    (setq labels (cons (car act-labels) labels)
+		  act-labels (cdr act-labels)))
+	(while act-labels
+	  (setq labels (vm-delqual (car act-labels) labels)
+		act-labels (cdr act-labels))))
+      (if add
+	  (setq labels (vm-delete-duplicates labels)))
+      (vm-set-labels (car m-list) labels)
+      (setq m-list (cdr m-list))))
+  (vm-update-summary-and-mode-line))
+
+(defun vm-set-xxxx-flag (m flag norecord function attr-index)
+  (let ((m-list nil) vmp)
+    (cond
+     ((and (not vm-folder-read-only)
+	   (or (not (vm-virtual-messages-of m))
+	       (not (save-excursion
+		      (set-buffer
+		       (vm-buffer-of
+			 (vm-real-message-of m)))
+		      vm-folder-read-only))))
+      (aset (vm-attributes-of m) attr-index flag)
+      (vm-mark-for-summary-update m)
+      (cond
+       ((not norecord)
+	(if (eq vm-flush-interval t)
+	    (vm-stuff-virtual-attributes m)
+	  (vm-set-modflag-of m t))
+	(setq vmp (cons (vm-real-message-of m) (vm-virtual-messages-of m)))
+	(while vmp
+	  (if (eq (vm-attributes-of m) (vm-attributes-of (car vmp)))
+	      (setq m-list (cons (car vmp) m-list)))
+	  (setq vmp (cdr vmp)))
+	(if (null m-list)
+	    (setq m-list (cons m m-list)))
+	(while m-list
+	  (save-excursion
+	    (set-buffer (vm-buffer-of (car m-list)))
+	    (cond ((not (buffer-modified-p))
+		   (vm-set-buffer-modified-p t)
+		   (vm-undo-record (list 'vm-set-buffer-modified-p nil))))
+	    (vm-undo-record (list function (car m-list) (not flag)))
+	    (vm-undo-boundary)
+	    (vm-increment vm-modification-counter))
+	  (setq m-list (cdr m-list)))))))))
+
+(defun vm-set-labels (m labels)
+  (let ((m-list nil)
+	(old-labels (vm-labels-of m))
+	vmp)
+    (cond
+     ((and (not vm-folder-read-only)
+	   (or (not (vm-virtual-messages-of m))
+	       (not (save-excursion
+		      (set-buffer
+		       (vm-buffer-of
+			 (vm-real-message-of m)))
+		      vm-folder-read-only))))
+      (vm-set-labels-of m labels)
+      (vm-set-label-string-of m nil)
+      (vm-mark-for-summary-update m)
+      (if (eq vm-flush-interval t)
+	  (vm-stuff-virtual-attributes m)
+	(vm-set-modflag-of m t))
+      (setq vmp (cons (vm-real-message-of m) (vm-virtual-messages-of m)))
+      (while vmp
+	(if (eq (vm-attributes-of m) (vm-attributes-of (car vmp)))
+	    (setq m-list (cons (car vmp) m-list)))
+	(setq vmp (cdr vmp)))
+      (if (null m-list)
+	  (setq m-list (cons m m-list)))
+      (while m-list
+	(save-excursion
+	  (set-buffer (vm-buffer-of (car m-list)))
+	  (cond ((not (buffer-modified-p))
+		 (vm-set-buffer-modified-p t)
+		 (vm-undo-record (list 'vm-set-buffer-modified-p nil))))
+	  (vm-undo-record (list 'vm-set-labels m old-labels))
+	  (vm-undo-boundary)
+	  (vm-increment vm-modification-counter))
+	(setq m-list (cdr m-list)))))))
+
+(defun vm-set-new-flag (m flag &optional norecord)
+  (vm-set-xxxx-flag m flag norecord 'vm-set-new-flag 0))
+
+(defun vm-set-unread-flag (m flag &optional norecord)
+  (vm-set-xxxx-flag m flag norecord 'vm-set-unread-flag 1))
+
+(defun vm-set-deleted-flag (m flag &optional norecord)
+  (vm-set-xxxx-flag m flag norecord 'vm-set-deleted-flag 2))
+
+(defun vm-set-filed-flag (m flag &optional norecord)
+  (vm-set-xxxx-flag m flag norecord 'vm-set-filed-flag 3))
+
+(defun vm-set-replied-flag (m flag &optional norecord)
+  (vm-set-xxxx-flag m flag norecord 'vm-set-replied-flag 4))
+
+(defun vm-set-written-flag (m flag &optional norecord)
+  (vm-set-xxxx-flag m flag norecord 'vm-set-written-flag 5))
+
+(defun vm-set-forwarded-flag (m flag &optional norecord)
+  (vm-set-xxxx-flag m flag norecord 'vm-set-forwarded-flag 6))
+
+(defun vm-set-redistributed-flag (m flag &optional norecord)
+  (vm-set-xxxx-flag m flag norecord 'vm-set-forwarded-flag 8))
+
+;; use these to avoid undo and summary update.
+(defun vm-set-new-flag-of (m flag) (aset (aref m 2) 0 flag))
+(defun vm-set-unread-flag-of (m flag) (aset (aref m 2) 1 flag))
+(defun vm-set-deleted-flag-of (m flag) (aset (aref m 2) 2 flag))
+(defun vm-set-filed-flag-of (m flag) (aset (aref m 2) 3 flag))
+(defun vm-set-replied-flag-of (m flag) (aset (aref m 2) 4 flag))
+(defun vm-set-written-flag-of (m flag) (aset (aref m 2) 5 flag))
+(defun vm-set-forwarded-flag-of (m flag) (aset (aref m 2) 6 flag))
+(defun vm-set-redistributed-flag-of (m flag) (aset (aref m 2) 8 flag))
+
+;; this is solely for the use of vm-stuff-attributes and appears here
+;; only because this function should be grouped with others of its kind
+;; for maintenance purposes.
+(defun vm-set-deleted-flag-in-vector (v flag)
+  (aset v 2 flag))
+;; ditto.  this is for vm-read-attributes.
+(defun vm-set-new-flag-in-vector (v flag)
+  (aset v 0 flag))