comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; Commands to undo message attribute changes in VM
2 ;;; Copyright (C) 1989, 1990, 1993, 1994 Kyle E. Jones
3 ;;;
4 ;;; This program is free software; you can redistribute it and/or modify
5 ;;; it under the terms of the GNU General Public License as published by
6 ;;; the Free Software Foundation; either version 1, or (at your option)
7 ;;; any later version.
8 ;;;
9 ;;; This program is distributed in the hope that it will be useful,
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ;;; GNU General Public License for more details.
13 ;;;
14 ;;; You should have received a copy of the GNU General Public License
15 ;;; along with this program; if not, write to the Free Software
16 ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 (provide 'vm-undo)
19
20 (defun vm-set-buffer-modified-p (flag &optional buffer)
21 (save-excursion
22 (and buffer (set-buffer buffer))
23 (set-buffer-modified-p flag)
24 (vm-increment vm-modification-counter)
25 (intern (buffer-name) vm-buffers-needing-display-update)
26 (if (null flag)
27 (setq vm-messages-not-on-disk 0))))
28
29 (defun vm-undo-boundary ()
30 (if (car vm-undo-record-list)
31 (setq vm-undo-record-list (cons nil vm-undo-record-list))))
32
33 (defun vm-clear-expunge-invalidated-undos ()
34 (let ((udp vm-undo-record-list) udp-prev)
35 (while udp
36 (cond ((null (car udp))
37 (setq udp-prev udp))
38 ((and (not (eq (car (car udp)) 'vm-set-buffer-modified-p))
39 ;; delete flag == expunged is the
40 ;; indicator of an expunged message
41 (eq (vm-deleted-flag (car (cdr (car udp)))) 'expunged))
42 (cond (udp-prev (setcdr udp-prev (cdr udp)))
43 (t (setq vm-undo-record-list (cdr udp)))))
44 (t (setq udp-prev udp)))
45 (setq udp (cdr udp))))
46 (vm-clear-modification-flag-undos))
47
48 (defun vm-clear-virtual-quit-invalidated-undos ()
49 (let ((udp vm-undo-record-list) udp-prev)
50 (while udp
51 (cond ((null (car udp))
52 (setq udp-prev udp))
53 ((and (not (eq (car (car udp)) 'vm-set-buffer-modified-p))
54 ;; message-id-number == "Q" is the
55 ;; indicator of a dead message
56 (equal (vm-message-id-number-of (car (cdr (car udp)))) "Q"))
57 (cond (udp-prev (setcdr udp-prev (cdr udp)))
58 (t (setq vm-undo-record-list (cdr udp)))))
59 (t (setq udp-prev udp)))
60 (setq udp (cdr udp))))
61 (vm-clear-modification-flag-undos))
62
63 (defun vm-clear-modification-flag-undos ()
64 (let ((udp vm-undo-record-list) udp-prev)
65 (while udp
66 (cond ((null (car udp))
67 (setq udp-prev udp))
68 ((eq (car (car udp)) 'vm-set-buffer-modified-p)
69 (cond (udp-prev (setcdr udp-prev (cdr udp)))
70 (t (setq vm-undo-record-list (cdr udp)))))
71 (t (setq udp-prev udp)))
72 (setq udp (cdr udp)))
73 (vm-squeeze-consecutive-undo-boundaries)))
74
75 ;; squeeze out consecutive record separators left by record deletions
76 (defun vm-squeeze-consecutive-undo-boundaries ()
77 (let ((udp vm-undo-record-list) udp-prev)
78 (while udp
79 (cond ((and (null (car udp)) udp-prev (null (car udp-prev)))
80 (setcdr udp-prev (cdr udp)))
81 (t (setq udp-prev udp)))
82 (setq udp (cdr udp)))
83 (if (equal '(nil) vm-undo-record-list)
84 (setq vm-undo-record-list nil)))
85 ;; for the Undo button on the menubar, if present
86 (and (null vm-undo-record-list)
87 (vm-menu-support-possible-p)
88 (vm-menu-xemacs-menus-p)
89 (vm-menu-set-menubar-dirty-flag)))
90
91 (defun vm-undo-record (sexp)
92 ;; for the Undo button on the menubar, if present
93 (and (null vm-undo-record-list)
94 (vm-menu-support-possible-p)
95 (vm-menu-xemacs-menus-p)
96 (vm-menu-set-menubar-dirty-flag))
97 (setq vm-undo-record-list (cons sexp vm-undo-record-list)))
98
99 (defun vm-undo-describe (record)
100 (let ((cell
101 (assq (car record)
102 '((vm-set-new-flag "new" "old")
103 (vm-set-unread-flag "unread" "read")
104 (vm-set-deleted-flag "deleted" "undeleted")
105 (vm-set-forwarded-flag "forwarded" "unforwarded")
106 (vm-set-replied-flag "answered" "unanswered")
107 (vm-set-redistributed-flag "redistributed" "unredistributed")
108 (vm-set-filed-flag "filed" "unfiled")
109 (vm-set-written-flag "written" "unwritten"))))
110 (m (nth 1 record))
111 labels)
112 (cond (cell
113 (message "VM Undo! %s/%s %s -> %s"
114 (buffer-name (vm-buffer-of m))
115 (vm-number-of m)
116 (if (nth 2 record)
117 (nth 2 cell)
118 (nth 1 cell))
119 (if (nth 2 record)
120 (nth 1 cell)
121 (nth 2 cell))))
122 ((eq (car cell) 'vm-set-labels)
123 (setq labels (nth 2 record))
124 (message "VM Undo! %s/%s %s%s"
125 (buffer-name (vm-buffer-of m))
126 (vm-number-of m)
127 (if (null labels)
128 "lost all its labels"
129 "labels set to ")
130 (if (null labels)
131 ""
132 (mapconcat 'identity labels ", ")))))))
133
134 (defun vm-undo-set-message-pointer (record)
135 (if (and (not (eq (car record) 'vm-set-buffer-modified-p))
136 (not (eq (nth 1 record) vm-message-pointer)))
137 (progn
138 (vm-record-and-change-message-pointer
139 vm-message-pointer
140 (or (cdr (vm-reverse-link-of (nth 1 record)))
141 vm-message-list))
142 ;; make folder read-only to avoid modifications when we
143 ;; do this.
144 (let ((vm-folder-read-only t))
145 (vm-preview-current-message)))))
146
147 (defun vm-undo ()
148 "Undo last change to message attributes in the current folder.
149 Consecutive invocations of this command cause sequentially earlier
150 changes to be undone. After an intervening command between undos,
151 the undos themselves become undoable."
152 (interactive)
153 (vm-select-folder-buffer)
154 (vm-check-for-killed-summary)
155 (vm-display nil nil '(vm-undo) '(vm-undo))
156 (let ((modified (buffer-modified-p)))
157 (if (not (eq last-command 'vm-undo))
158 (setq vm-undo-record-pointer vm-undo-record-list))
159 (if (not vm-undo-record-pointer)
160 (error "No further VM undo information available"))
161 ;; skip current record boundary
162 (setq vm-undo-record-pointer (cdr vm-undo-record-pointer))
163 (while (car vm-undo-record-pointer)
164 (vm-undo-set-message-pointer (car vm-undo-record-pointer))
165 (vm-undo-describe (car vm-undo-record-pointer))
166 (eval (car vm-undo-record-pointer))
167 (setq vm-undo-record-pointer (cdr vm-undo-record-pointer)))
168 (and modified (not (buffer-modified-p))
169 (delete-auto-save-file-if-necessary))
170 (vm-update-summary-and-mode-line)))
171
172 (defun vm-set-message-attributes (string count)
173 "Set message attributes.
174 Use this command to change attributes like `deleted' or
175 `replied'. Interactively you will be prompted for the attributes
176 to be changed, and only the attributes you enter will be altered.
177 You can use completion to expand the attribute names. The names
178 should be entered as a space separated list.
179
180 A numeric prefix argument COUNT causes the current message and
181 the next COUNT-1 message to have their attributes altered. A
182 negative COUNT arg causes the current message and the previous
183 COUNT-1 messages to be altered. COUNT defaults to one."
184 (interactive
185 (let ((last-command last-command)
186 (this-command this-command))
187 ;; so the user can see what message they are about to
188 ;; modify.
189 (vm-follow-summary-cursor)
190 (list
191 (vm-read-string "Set attributes: " vm-supported-attribute-names t)
192 (prefix-numeric-value current-prefix-arg))))
193 (vm-follow-summary-cursor)
194 (vm-select-folder-buffer)
195 (vm-check-for-killed-summary)
196 (vm-error-if-folder-read-only)
197 (vm-error-if-folder-empty)
198 (vm-display nil nil '(vm-set-message-attributes)
199 '(vm-set-message-attributes))
200 (let ((name-list (vm-parse string "[ \t]*\\([^ \t]+\\)"))
201 (m-list (vm-select-marked-or-prefixed-messages count))
202 n-list name m)
203 (while m-list
204 (setq m (car m-list)
205 n-list name-list)
206 (while n-list
207 (setq name (car n-list))
208 (cond ((string= name "new")
209 (vm-set-new-flag m t))
210 ((string= name "recent")
211 (vm-set-new-flag m t))
212 ((string= name "unread")
213 (vm-set-unread-flag m t))
214 ((string= name "unseen")
215 (vm-set-unread-flag m t))
216 ((string= name "read")
217 (vm-set-new-flag m nil)
218 (vm-set-unread-flag m nil))
219 ((string= name "deleted")
220 (vm-set-deleted-flag m t))
221 ((string= name "replied")
222 (vm-set-replied-flag m t))
223 ((string= name "answered")
224 (vm-set-replied-flag m t))
225 ((string= name "forwarded")
226 (vm-set-forwarded-flag m t))
227 ((string= name "redistributed")
228 (vm-set-redistributed-flag m t))
229 ((string= name "filed")
230 (vm-set-filed-flag m t))
231 ((string= name "written")
232 (vm-set-written-flag m t))
233 ((string= name "edited")
234 (vm-set-edited-flag-of m t))
235 ((string= name "undeleted")
236 (vm-set-deleted-flag m nil))
237 ((string= name "unreplied")
238 (vm-set-replied-flag m nil))
239 ((string= name "unanswered")
240 (vm-set-replied-flag m nil))
241 ((string= name "unforwarded")
242 (vm-set-forwarded-flag m nil))
243 ((string= name "unredistributed")
244 (vm-set-redistributed-flag m nil))
245 ((string= name "unfiled")
246 (vm-set-filed-flag m nil))
247 ((string= name "unwritten")
248 (vm-set-written-flag m nil))
249 ((string= name "unedited")
250 (vm-set-edited-flag-of m nil)))
251 (setq n-list (cdr n-list)))
252 (setq m-list (cdr m-list)))
253 (vm-update-summary-and-mode-line)))
254
255 (defun vm-add-message-labels (string count)
256 "Attach some labels to a message.
257 These are arbitrary user-defined labels, not to be confused with
258 message attributes like `new' and `deleted'. Interactively you
259 will be prompted for the labels to be added. You can use
260 completion to expand the label names, with the completion list
261 being all the labels that have ever been used in this folder.
262 The names should be entered as a space separated list. Label
263 names are compared case-insensitively.
264
265 A numeric prefix argument COUNT causes the current message and
266 the next COUNT-1 message to have the labels added. A
267 negative COUNT arg causes the current message and the previous
268 COUNT-1 messages to be altered. COUNT defaults to one."
269 (interactive
270 (let ((last-command last-command)
271 (this-command this-command)
272 (vm-completion-auto-correct nil)
273 (completion-ignore-case t))
274 ;; so the user can see what message they are about to
275 ;; modify.
276 (vm-follow-summary-cursor)
277 (vm-select-folder-buffer)
278 (list
279 (vm-read-string "Add labels: "
280 (vm-obarray-to-string-list vm-label-obarray) t)
281 (prefix-numeric-value current-prefix-arg))))
282 (vm-follow-summary-cursor)
283 (vm-select-folder-buffer)
284 (vm-check-for-killed-summary)
285 (vm-error-if-folder-read-only)
286 (vm-error-if-folder-empty)
287 (vm-add-or-delete-message-labels string count t))
288
289 (defun vm-delete-message-labels (string count)
290 "Delete some labels from a message.
291 These are arbitrary user-defined labels, not to be confused with
292 message attributes like `new' and `deleted'. Interactively you
293 will be prompted for the labels to be deleted. You can use
294 completion to expand the label names, with the completion list
295 being all the labels that have ever been used in this folder.
296 The names should be entered as a space separated list. Label
297 names are compared case-insensitively.
298
299 A numeric prefix argument COUNT causes the current message and
300 the next COUNT-1 message to have the labels deleted. A
301 negative COUNT arg causes the current message and the previous
302 COUNT-1 messages to be altered. COUNT defaults to one."
303 (interactive
304 (let ((last-command last-command)
305 (this-command this-command)
306 (vm-completion-auto-correct nil)
307 (completion-ignore-case t))
308 ;; so the user can see what message they are about to
309 ;; modify.
310 (vm-follow-summary-cursor)
311 (vm-select-folder-buffer)
312 (list
313 (vm-read-string "Delete labels: "
314 (vm-obarray-to-string-list vm-label-obarray) t)
315 (prefix-numeric-value current-prefix-arg))))
316 (vm-follow-summary-cursor)
317 (vm-select-folder-buffer)
318 (vm-check-for-killed-summary)
319 (vm-error-if-folder-read-only)
320 (vm-error-if-folder-empty)
321 (vm-add-or-delete-message-labels string count nil))
322
323 (defun vm-add-or-delete-message-labels (string count add)
324 (vm-display nil nil '(vm-add-message-labels vm-delete-message-labels)
325 (list this-command))
326 (setq string (downcase string))
327 (let ((m-list (vm-select-marked-or-prefixed-messages count))
328 (action-labels (vm-parse string
329 "[\000-\040,\177-\377]*\\([^\000-\040,\177-\377]+\\)[\000-\040,\177-\377]*"))
330 labels act-labels)
331 (if (and add m-list)
332 (progn
333 (setq act-labels action-labels)
334 (while act-labels
335 (intern (car act-labels) vm-label-obarray)
336 (setq act-labels (cdr act-labels)))))
337 (while m-list
338 (setq act-labels action-labels
339 labels (copy-sequence (vm-labels-of (car m-list))))
340 (if add
341 (while act-labels
342 (setq labels (cons (car act-labels) labels)
343 act-labels (cdr act-labels)))
344 (while act-labels
345 (setq labels (vm-delqual (car act-labels) labels)
346 act-labels (cdr act-labels))))
347 (if add
348 (setq labels (vm-delete-duplicates labels)))
349 (vm-set-labels (car m-list) labels)
350 (setq m-list (cdr m-list))))
351 (vm-update-summary-and-mode-line))
352
353 (defun vm-set-xxxx-flag (m flag norecord function attr-index)
354 (let ((m-list nil) vmp)
355 (cond
356 ((and (not vm-folder-read-only)
357 (or (not (vm-virtual-messages-of m))
358 (not (save-excursion
359 (set-buffer
360 (vm-buffer-of
361 (vm-real-message-of m)))
362 vm-folder-read-only))))
363 (aset (vm-attributes-of m) attr-index flag)
364 (vm-mark-for-summary-update m)
365 (cond
366 ((not norecord)
367 (if (eq vm-flush-interval t)
368 (vm-stuff-virtual-attributes m)
369 (vm-set-modflag-of m t))
370 (setq vmp (cons (vm-real-message-of m) (vm-virtual-messages-of m)))
371 (while vmp
372 (if (eq (vm-attributes-of m) (vm-attributes-of (car vmp)))
373 (setq m-list (cons (car vmp) m-list)))
374 (setq vmp (cdr vmp)))
375 (if (null m-list)
376 (setq m-list (cons m m-list)))
377 (while m-list
378 (save-excursion
379 (set-buffer (vm-buffer-of (car m-list)))
380 (cond ((not (buffer-modified-p))
381 (vm-set-buffer-modified-p t)
382 (vm-undo-record (list 'vm-set-buffer-modified-p nil))))
383 (vm-undo-record (list function (car m-list) (not flag)))
384 (vm-undo-boundary)
385 (vm-increment vm-modification-counter))
386 (setq m-list (cdr m-list)))))))))
387
388 (defun vm-set-labels (m labels)
389 (let ((m-list nil)
390 (old-labels (vm-labels-of m))
391 vmp)
392 (cond
393 ((and (not vm-folder-read-only)
394 (or (not (vm-virtual-messages-of m))
395 (not (save-excursion
396 (set-buffer
397 (vm-buffer-of
398 (vm-real-message-of m)))
399 vm-folder-read-only))))
400 (vm-set-labels-of m labels)
401 (vm-set-label-string-of m nil)
402 (vm-mark-for-summary-update m)
403 (if (eq vm-flush-interval t)
404 (vm-stuff-virtual-attributes m)
405 (vm-set-modflag-of m t))
406 (setq vmp (cons (vm-real-message-of m) (vm-virtual-messages-of m)))
407 (while vmp
408 (if (eq (vm-attributes-of m) (vm-attributes-of (car vmp)))
409 (setq m-list (cons (car vmp) m-list)))
410 (setq vmp (cdr vmp)))
411 (if (null m-list)
412 (setq m-list (cons m m-list)))
413 (while m-list
414 (save-excursion
415 (set-buffer (vm-buffer-of (car m-list)))
416 (cond ((not (buffer-modified-p))
417 (vm-set-buffer-modified-p t)
418 (vm-undo-record (list 'vm-set-buffer-modified-p nil))))
419 (vm-undo-record (list 'vm-set-labels m old-labels))
420 (vm-undo-boundary)
421 (vm-increment vm-modification-counter))
422 (setq m-list (cdr m-list)))))))
423
424 (defun vm-set-new-flag (m flag &optional norecord)
425 (vm-set-xxxx-flag m flag norecord 'vm-set-new-flag 0))
426
427 (defun vm-set-unread-flag (m flag &optional norecord)
428 (vm-set-xxxx-flag m flag norecord 'vm-set-unread-flag 1))
429
430 (defun vm-set-deleted-flag (m flag &optional norecord)
431 (vm-set-xxxx-flag m flag norecord 'vm-set-deleted-flag 2))
432
433 (defun vm-set-filed-flag (m flag &optional norecord)
434 (vm-set-xxxx-flag m flag norecord 'vm-set-filed-flag 3))
435
436 (defun vm-set-replied-flag (m flag &optional norecord)
437 (vm-set-xxxx-flag m flag norecord 'vm-set-replied-flag 4))
438
439 (defun vm-set-written-flag (m flag &optional norecord)
440 (vm-set-xxxx-flag m flag norecord 'vm-set-written-flag 5))
441
442 (defun vm-set-forwarded-flag (m flag &optional norecord)
443 (vm-set-xxxx-flag m flag norecord 'vm-set-forwarded-flag 6))
444
445 (defun vm-set-redistributed-flag (m flag &optional norecord)
446 (vm-set-xxxx-flag m flag norecord 'vm-set-forwarded-flag 8))
447
448 ;; use these to avoid undo and summary update.
449 (defun vm-set-new-flag-of (m flag) (aset (aref m 2) 0 flag))
450 (defun vm-set-unread-flag-of (m flag) (aset (aref m 2) 1 flag))
451 (defun vm-set-deleted-flag-of (m flag) (aset (aref m 2) 2 flag))
452 (defun vm-set-filed-flag-of (m flag) (aset (aref m 2) 3 flag))
453 (defun vm-set-replied-flag-of (m flag) (aset (aref m 2) 4 flag))
454 (defun vm-set-written-flag-of (m flag) (aset (aref m 2) 5 flag))
455 (defun vm-set-forwarded-flag-of (m flag) (aset (aref m 2) 6 flag))
456 (defun vm-set-redistributed-flag-of (m flag) (aset (aref m 2) 8 flag))
457
458 ;; this is solely for the use of vm-stuff-attributes and appears here
459 ;; only because this function should be grouped with others of its kind
460 ;; for maintenance purposes.
461 (defun vm-set-deleted-flag-in-vector (v flag)
462 (aset v 2 flag))
463 ;; ditto. this is for vm-read-attributes.
464 (defun vm-set-new-flag-in-vector (v flag)
465 (aset v 0 flag))