New feature: toggle visibility of mime buttons.
[more-wl.git] / wl / wl-action.el
blobd82580ebad09d82ab1f0452a9be0eaa1d8d15e7d
1 ;;; wl-action.el --- Mark and actions in the Summary mode for Wanderlust.
3 ;; Copyright (C) 2003 Yuuichi Teranishi <teranisi@gohome.org>
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
8 ;; This file is part of Wanderlust (Yet Another Message Interface on Emacsen).
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING. If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
26 ;;; Commentary:
29 ;;; Code:
32 (require 'wl-summary)
34 (eval-when-compile
35 (defalias-maybe 'wl-summary-target-mark 'ignore)
36 (defalias-maybe 'wl-summary-target-mark-region 'ignore))
38 (defsubst wl-summary-action-mark (action)
39 (nth 0 action))
40 (defsubst wl-summary-action-symbol (action)
41 (nth 1 action))
42 (defsubst wl-summary-action-argument-function (action)
43 (nth 2 action))
44 (defsubst wl-summary-action-set-function (action)
45 (nth 3 action))
46 (defsubst wl-summary-action-exec-function (action)
47 (nth 4 action))
48 (defsubst wl-summary-action-face (action)
49 (nth 5 action))
50 (defsubst wl-summary-action-docstring (action)
51 (concat (nth 6 action)
52 "\nThis function is defined by `wl-summary-define-mark-action'."))
54 ;; Set mark
55 (defun wl-summary-set-mark (&optional set-mark number interactive data)
56 "Set temporary mark SET-MARK on the message with NUMBER.
57 NUMBER is the message number to set the mark on.
58 INTERACTIVE is set as t if it have to run interactively.
59 DATA is passed to the set-action function of the action as an argument.
60 Return number if put mark succeed"
61 (let* ((set-mark (or set-mark
62 (completing-read "Mark: " wl-summary-mark-action-list)))
63 (current (wl-summary-message-number))
64 (action (assoc set-mark wl-summary-mark-action-list))
65 visible mark cur-mark)
66 (when (zerop (elmo-folder-length wl-summary-buffer-elmo-folder))
67 (error "Set mark failed"))
68 (prog1
69 (save-excursion
70 ;; Put mark
71 (if number
72 ;; Jump to message if cursor is not on the message.
73 (when (and (setq visible (wl-summary-message-visible-p number))
74 (not (eq number current)))
75 (wl-summary-jump-to-msg number))
76 (setq visible t
77 number current))
78 (setq cur-mark (nth 1 (wl-summary-registered-temp-mark number)))
79 (unless number
80 (error "No message"))
81 (if (wl-summary-reserve-temp-mark-p cur-mark)
82 (when interactive
83 (error "Already marked as `%s'" cur-mark))
84 (when (and interactive
85 (null data)
86 (wl-summary-action-argument-function action))
87 (setq data (funcall (wl-summary-action-argument-function action)
88 (wl-summary-action-symbol action)
89 number)))
90 ;; Unset the current mark.
91 (wl-summary-unset-mark number)
92 ;; Set action.
93 (funcall (wl-summary-action-set-function action)
94 number
95 (wl-summary-action-mark action)
96 data)
97 (when visible
98 (wl-summary-put-temp-mark set-mark)
99 (when wl-summary-highlight
100 (wl-highlight-summary-current-line))
101 (when data
102 (wl-summary-print-argument number data)))
103 (when (and (eq wl-summary-buffer-view 'thread)
104 interactive)
105 (wl-thread-open-children number))
106 (set-buffer-modified-p nil)
107 ;; Return value.
108 number))
109 ;; Move the cursor.
110 (if interactive
111 (if (eq wl-summary-move-direction-downward nil)
112 (wl-summary-prev)
113 (wl-summary-next))))))
115 (defun wl-summary-register-target-mark (number mark data)
116 (or (memq number wl-summary-buffer-target-mark-list)
117 (setq wl-summary-buffer-target-mark-list
118 (cons number wl-summary-buffer-target-mark-list))))
120 (defun wl-summary-unregister-target-mark (number)
121 (setq wl-summary-buffer-target-mark-list
122 (delq number wl-summary-buffer-target-mark-list)))
124 (defun wl-summary-have-target-mark-p (number)
125 (memq number wl-summary-buffer-target-mark-list))
127 (defun wl-summary-target-mark-set-action (action)
128 (unless (eq (wl-summary-action-symbol action) 'target-mark)
129 (unless wl-summary-buffer-target-mark-list (error "no target"))
130 (save-excursion
131 (goto-char (point-min))
132 (let ((numlist wl-summary-buffer-number-list)
133 number mlist data)
134 ;; use firstly marked message.
135 (when (wl-summary-action-argument-function action)
136 (while numlist
137 (if (memq (car numlist) wl-summary-buffer-target-mark-list)
138 (setq number (car numlist)
139 numlist nil))
140 (setq numlist (cdr numlist)))
141 (wl-summary-jump-to-msg number)
142 (setq data (funcall (wl-summary-action-argument-function action)
143 (wl-summary-action-symbol action) number)))
144 (while (not (eobp))
145 (when (string= (wl-summary-temp-mark) "*")
146 (let (wl-summary-buffer-disp-msg)
147 (when (setq number (wl-summary-message-number))
148 (wl-summary-set-mark (wl-summary-action-mark action)
149 nil nil data)
150 (setq wl-summary-buffer-target-mark-list
151 (delq number wl-summary-buffer-target-mark-list)))))
152 (forward-line 1))
153 (setq mlist wl-summary-buffer-target-mark-list)
154 (while mlist
155 (wl-summary-register-temp-mark (car mlist)
156 (wl-summary-action-mark action) data)
157 (setq wl-summary-buffer-target-mark-list
158 (delq (car mlist) wl-summary-buffer-target-mark-list))
159 (setq mlist (cdr mlist)))))))
161 ;; wl-summary-buffer-temp-mark-list specification
162 ;; ((1 "D" nil)(2 "o" "+fuga")(3 "O" "+hoge"))
163 (defun wl-summary-register-temp-mark (number mark mark-info)
164 (let ((elem (assq number wl-summary-buffer-temp-mark-list)))
165 (setq wl-summary-buffer-temp-mark-list
166 (delq elem wl-summary-buffer-temp-mark-list)))
167 (setq wl-summary-buffer-temp-mark-list
168 (cons (list number mark mark-info) wl-summary-buffer-temp-mark-list)))
170 (defun wl-summary-unregister-temp-mark (number)
171 (let ((elem (assq number wl-summary-buffer-temp-mark-list)))
172 (setq wl-summary-buffer-temp-mark-list
173 (delq elem wl-summary-buffer-temp-mark-list))))
175 (defun wl-summary-registered-temp-mark (number)
176 (and wl-summary-buffer-temp-mark-list
177 (assq number wl-summary-buffer-temp-mark-list)))
179 (defun wl-summary-collect-temp-mark (mark &optional begin end)
180 (if (or begin end)
181 (save-excursion
182 (save-restriction
183 (let (mark-list)
184 (narrow-to-region (or begin (point-min))(or end (point-max)))
185 (goto-char (point-min))
186 ;; for thread...
187 (if (eq wl-summary-buffer-view 'thread)
188 (let (number entity mark-info)
189 (while (not (eobp))
190 (setq number (wl-summary-message-number)
191 entity (wl-thread-get-entity number)
192 mark-info (wl-summary-registered-temp-mark number))
193 ;; toplevel message mark.
194 (when (string= (nth 1 mark-info) mark)
195 (setq mark-list (cons mark-info mark-list)))
196 ;; When thread is closed...children should also be checked.
197 (unless (wl-thread-entity-get-opened entity)
198 (dolist (msg (wl-thread-get-children-msgs number))
199 (setq mark-info (wl-summary-registered-temp-mark
200 msg))
201 (when (string= (nth 1 mark-info) mark)
202 (setq mark-list (cons mark-info mark-list)))))
203 (forward-line 1)))
204 (let (number mark-info)
205 (while (not (eobp))
206 (setq number (wl-summary-message-number)
207 mark-info (wl-summary-registered-temp-mark number))
208 (when (string= (nth 1 mark-info) mark)
209 (setq mark-list (cons mark-info mark-list)))
210 (forward-line 1))))
211 mark-list)))
212 (let (mark-list)
213 (dolist (mark-info wl-summary-buffer-temp-mark-list)
214 (when (string= (nth 1 mark-info) mark)
215 (setq mark-list (cons mark-info mark-list))))
216 mark-list)))
218 ;; Unset mark
219 (defun wl-summary-unset-mark (&optional number interactive force)
220 "Unset temporary mark of the message with NUMBER.
221 NUMBER is the message number to unset the mark.
222 If not specified, the message on the cursor position is treated.
223 Optional INTERACTIVE is non-nil when it should be called interactively.
224 If optional FORCE is non-nil, remove scored mark too.
225 Return number if put mark succeed"
226 (interactive)
227 (save-excursion
228 (beginning-of-line)
229 (let ((buffer-read-only nil)
230 visible mark action)
231 (if number
232 ;; Jump to message
233 (when (and (setq visible (wl-summary-message-visible-p number))
234 (not (eq number (wl-summary-message-number))))
235 (wl-summary-jump-to-msg number))
236 (setq visible t
237 number (wl-summary-message-number)))
238 (setq mark (wl-summary-temp-mark))
239 ;; Remove from temporal mark structure.
240 (wl-summary-unregister-target-mark number)
241 (wl-summary-unregister-temp-mark number)
242 ;; Delete mark on buffer.
243 (when visible
244 (unless (string= mark " ")
245 (wl-summary-put-temp-mark
246 (or (unless force (wl-summary-get-score-mark number))
247 " "))
248 (setq action (assoc mark wl-summary-mark-action-list))
249 (when wl-summary-highlight
250 (wl-highlight-summary-current-line))
251 (when (wl-summary-action-argument-function action)
252 (wl-summary-remove-argument)))
253 (set-buffer-modified-p nil))))
254 ;; Move the cursor.
255 ;; (if (or interactive (interactive-p))
256 ;; (if (eq wl-summary-move-direction-downward nil)
257 ;; (wl-summary-prev)
258 ;; (wl-summary-next))))
261 (defun wl-summary-make-destination-numbers-list (mark-list)
262 (let (dest-numbers dest-number)
263 (dolist (elem mark-list)
264 (setq dest-number (assoc (nth 2 elem) dest-numbers))
265 (if dest-number
266 (unless (memq (car elem) (cdr dest-number))
267 (nconc dest-number (list (car elem))))
268 (setq dest-numbers (nconc dest-numbers
269 (list
270 (list (nth 2 elem)
271 (car elem)))))))
272 dest-numbers))
274 (defun wl-summary-move-mark-list-messages (mark-list folder-name message)
275 (if (null mark-list)
276 (message "No marks")
277 (save-excursion
278 (let ((start (point))
279 (refiles (mapcar 'car mark-list))
280 (refile-failures 0)
281 dst-msgs ; loop counter
282 result)
283 ;; begin refile...
284 (goto-char start) ; avoid moving cursor to
285 ; the bottom line.
286 (elmo-with-progress-display
287 (elmo-folder-move-messages (length refiles))
288 message
289 (setq result nil)
290 (condition-case nil
291 (setq result (elmo-folder-move-messages
292 wl-summary-buffer-elmo-folder
293 refiles
294 (if (eq folder-name 'null)
295 'null
296 (wl-folder-get-elmo-folder folder-name))))
297 (error nil))
298 (when result ; succeeded.
299 ;; update buffer.
300 (wl-summary-delete-messages-on-buffer refiles)
301 ;; update wl-summary-buffer-temp-mark-list.
302 (dolist (mark-info mark-list)
303 (setq wl-summary-buffer-temp-mark-list
304 (delq mark-info wl-summary-buffer-temp-mark-list)))))
305 (wl-summary-set-message-modified)
306 ;; Return the operation failed message numbers.
307 (if result
309 (length refiles))))))
311 (defun wl-summary-get-refile-destination-subr (action number learn)
312 (let* ((number (or number (wl-summary-message-number)))
313 (msgid (and number
314 (elmo-message-field wl-summary-buffer-elmo-folder
315 number 'message-id)))
316 (entity (and number
317 (elmo-message-entity wl-summary-buffer-elmo-folder
318 number)))
319 folder cur-mark tmp-folder)
320 (catch 'done
321 (when (null entity)
322 (message "Cannot decide destination.")
323 (throw 'done nil))
324 (when (null number)
325 (message "No message.")
326 (throw 'done nil))
327 (setq folder (wl-summary-read-folder
328 (or (wl-refile-guess entity) wl-trash-folder)
329 (format "for %s " action)))
330 ;; Cache folder hack by okada@opaopa.org
331 (when (and (eq (elmo-folder-type-internal
332 (wl-folder-get-elmo-folder
333 (wl-folder-get-realname folder))) 'cache)
334 (not (string= folder
335 (setq tmp-folder
336 (concat "'cache/"
337 (elmo-cache-get-path-subr
338 (elmo-msgid-to-cache msgid)))))))
339 (setq folder tmp-folder)
340 (message "Force refile to %s." folder))
341 (if (string= folder (wl-summary-buffer-folder-name))
342 (error "Same folder"))
343 (if (or (not (elmo-folder-writable-p (wl-folder-get-elmo-folder folder)))
344 (string= folder wl-queue-folder)
345 (string= folder wl-draft-folder))
346 (error "Don't set as target: %s" folder))
347 ;; learn for refile.
348 (when learn
349 (wl-refile-learn entity folder))
350 folder)))
352 ;;; Actions
353 (defun wl-summary-define-mark-action ()
354 (interactive)
355 (dolist (action wl-summary-mark-action-list)
356 (fset (intern (format "wl-summary-%s" (wl-summary-action-symbol action)))
357 `(lambda (&optional number data)
358 ,(wl-summary-action-docstring action)
359 (interactive)
360 (wl-summary-set-mark ,(wl-summary-action-mark action)
361 number (interactive-p) data)))
362 (fset (intern (format "wl-summary-%s-region"
363 (wl-summary-action-symbol action)))
364 `(lambda (beg end)
365 ,(wl-summary-action-docstring action)
366 (interactive "r")
367 (save-excursion
368 (goto-char beg)
369 (wl-summary-mark-region-subr
370 (quote ,(intern (format "wl-summary-%s"
371 (wl-summary-action-symbol action))))
372 beg end
373 (if (quote ,(wl-summary-action-argument-function action))
374 (funcall (function
375 ,(wl-summary-action-argument-function action))
376 (quote ,(wl-summary-action-symbol action))
377 (wl-summary-message-number)))))))
378 (fset (intern (format "wl-summary-target-mark-%s"
379 (wl-summary-action-symbol action)))
380 `(lambda ()
381 ,(wl-summary-action-docstring action)
382 (interactive)
383 (wl-summary-target-mark-set-action (quote ,action))))
384 (fset (intern (format "wl-thread-%s"
385 (wl-summary-action-symbol action)))
386 `(lambda (arg)
387 ,(wl-summary-action-docstring action)
388 (interactive "P")
389 (wl-thread-call-region-func
390 (quote ,(intern (format "wl-summary-%s-region"
391 (wl-summary-action-symbol action))))
392 arg)
393 (if arg
394 (wl-summary-goto-top-of-current-thread))
395 (if (not wl-summary-move-direction-downward)
396 (wl-summary-prev)
397 (wl-thread-goto-bottom-of-sub-thread)
398 (if wl-summary-buffer-disp-msg
399 (wl-summary-redisplay)))))))
401 (defun wl-summary-get-dispose-folder (folder)
402 (if (string= folder wl-trash-folder)
403 'null
404 (let* ((type (or (wl-get-assoc-list-value wl-dispose-folder-alist folder)
405 'trash)))
406 (cond ((stringp type)
407 type)
408 ((or (equal type 'remove) (equal type 'null))
409 'null)
410 (t;; (equal type 'trash)
411 (let ((trash-folder (wl-folder-get-elmo-folder wl-trash-folder)))
412 (unless (elmo-folder-exists-p trash-folder)
413 (if (y-or-n-p
414 (format "Trash Folder %s does not exist, create it? "
415 wl-trash-folder))
416 (elmo-folder-create trash-folder)
417 (error "Trash Folder is not created"))))
418 wl-trash-folder)))))
420 ;; Dispose action.
421 (defun wl-summary-exec-action-dispose (mark-list)
422 (wl-summary-move-mark-list-messages mark-list
423 (wl-summary-get-dispose-folder
424 (wl-summary-buffer-folder-name))
425 "Disposing messages"))
427 ;; Delete action.
428 (defun wl-summary-exec-action-delete (mark-list)
429 (wl-summary-move-mark-list-messages mark-list
430 'null
431 "Deleting messages"))
433 ;; Refile action
434 (defun wl-summary-set-action-refile (number mark data)
435 (when (null data)
436 (error "Destination folder is empty"))
437 (wl-summary-register-temp-mark number mark data)
438 (setq wl-summary-buffer-prev-refile-destination data))
440 (defun wl-summary-get-refile-destination (action number)
441 "Decide refile destination."
442 (wl-summary-get-refile-destination-subr action number t))
444 (defun wl-summary-exec-action-refile (mark-list)
445 (save-excursion
446 (let ((start (point))
447 (failures 0)
448 dst-msgs)
449 ;; begin refile...
450 (setq dst-msgs (wl-summary-make-destination-numbers-list mark-list))
451 (goto-char start) ; avoid moving cursor to the bottom line.
452 (elmo-with-progress-display
453 (elmo-folder-move-messages (length mark-list))
454 "Refiling messages"
455 (dolist (pair dst-msgs)
456 (if (condition-case nil
457 (elmo-folder-move-messages
458 wl-summary-buffer-elmo-folder
459 (cdr pair)
460 (wl-folder-get-elmo-folder (car pair)))
461 (error nil))
462 (progn
463 ;; update buffer.
464 (wl-summary-delete-messages-on-buffer (cdr pair))
465 (setq wl-summary-buffer-temp-mark-list
466 (wl-delete-associations
467 (cdr pair)
468 wl-summary-buffer-temp-mark-list)))
469 (setq failures (+ failures (length (cdr pair)))))))
470 failures)))
472 ;; Copy action
473 (defun wl-summary-get-copy-destination (action number)
474 (wl-summary-get-refile-destination-subr action number nil))
476 (defun wl-summary-exec-action-copy (mark-list)
477 (save-excursion
478 (let ((start (point))
479 (failures 0)
480 dst-msgs)
481 ;; begin refile...
482 (setq dst-msgs
483 (wl-summary-make-destination-numbers-list mark-list))
484 (goto-char start) ; avoid moving cursor to the bottom line.
485 (elmo-with-progress-display
486 (elmo-folder-move-messages (length mark-list))
487 "Copying messages"
488 (dolist (pair dst-msgs)
489 (if (condition-case nil
490 (elmo-folder-move-messages
491 wl-summary-buffer-elmo-folder
492 (cdr pair)
493 (wl-folder-get-elmo-folder (car pair))
494 'no-delete)
495 (error nil))
496 (progn
497 ;; update buffer.
498 (wl-summary-delete-copy-marks-on-buffer (cdr pair))
499 (setq wl-summary-buffer-temp-mark-list
500 (wl-delete-associations
501 (cdr pair)
502 wl-summary-buffer-temp-mark-list)))
503 (setq failures (+ failures (length (cdr pair)))))))
504 failures)))
506 ;; Prefetch.
507 (defun wl-summary-exec-action-prefetch (mark-list)
508 (save-excursion
509 (let* ((count 0)
510 (length (length mark-list))
511 (mark-list-copy (copy-sequence mark-list))
512 (pos (point))
513 (failures 0))
514 (dolist (mark-info mark-list-copy)
515 (message "Prefetching...(%d/%d)"
516 (setq count (+ 1 count)) length)
517 (if (wl-summary-prefetch-msg (car mark-info))
518 (progn
519 (wl-summary-unset-mark (car mark-info))
520 (sit-for 0))
521 (incf failures)))
522 (message "Prefetching...done")
523 0)))
525 ;; Resend.
526 (defun wl-summary-get-resend-address (action number)
527 "Decide resend address."
528 (wl-address-read-from-minibuffer "Resend message to: "))
530 (defun wl-summary-exec-action-resend (mark-list)
531 (let ((failure 0))
532 (dolist (mark-info mark-list)
533 (if (condition-case nil
534 (progn
535 (wl-summary-exec-action-resend-subr (car mark-info)
536 (nth 2 mark-info))
538 (error))
539 (wl-summary-unmark (car mark-info))
540 (incf failure)))
541 failure))
543 (defun wl-summary-exec-action-resend-subr (number address)
544 "Resend the message with NUMBER to ADDRESS."
545 (message "Resending message to %s..." address)
546 (let ((folder wl-summary-buffer-elmo-folder))
547 (save-excursion
548 ;; We first set up a normal mail buffer.
549 (set-buffer (get-buffer-create " *wl-draft-resend*"))
550 (set-buffer-multibyte nil)
551 (erase-buffer)
552 (setq wl-sent-message-via nil)
553 ;; Insert our usual headers.
554 (wl-draft-insert-from-field)
555 (wl-draft-insert-date-field)
556 (insert "To: " address "\n")
557 (goto-char (point-min))
558 ;; Rename them all to "Resent-*".
559 (while (re-search-forward "^[A-Za-z]" nil t)
560 (forward-char -1)
561 (insert "Resent-"))
562 (widen)
563 (forward-line)
564 (delete-region (point) (point-max))
565 (let ((beg (point)))
566 ;; Insert the message to be resent.
567 (insert
568 ;; elmo-message-fetch is erase current buffer before fetch message
569 (elmo-message-fetch-string folder number
570 (if wl-summary-resend-use-cache
571 (elmo-make-fetch-strategy
572 'entire 'maybe nil
573 (elmo-file-cache-get-path
574 (elmo-message-field
575 folder number 'message-id)))
576 (elmo-make-fetch-strategy 'entire))
577 'unread))
578 (goto-char (point-min))
579 (search-forward "\n\n")
580 (forward-char -1)
581 (save-restriction
582 (narrow-to-region beg (point))
583 (wl-draft-delete-fields wl-ignored-resent-headers)
584 (goto-char (point-max)))
585 (insert mail-header-separator)
586 ;; Rename all old ("Previous-")Resent headers.
587 (while (re-search-backward "^\\(Previous-\\)*Resent-" beg t)
588 (beginning-of-line)
589 (insert "Previous-"))
590 ;; Quote any "From " lines at the beginning.
591 (goto-char beg)
592 (when (looking-at "From ")
593 (replace-match "X-From-Line: ")))
594 (run-hooks 'wl-summary-resend-hook)
595 ;; Send it.
596 (wl-draft-dispatch-message)
597 (kill-buffer (current-buffer)))
598 (message "Resending message to %s...done" address)))
601 (defun wl-summary-remove-argument ()
602 (save-excursion
603 (let ((inhibit-read-only t)
604 (buffer-read-only nil)
605 (buf (current-buffer))
606 sol eol rs re)
607 (beginning-of-line)
608 (setq sol (point))
609 (search-forward "\r")
610 (forward-char -1)
611 (setq eol (point))
612 (setq rs (next-single-property-change sol 'wl-summary-action-argument
613 buf eol))
614 (setq re (next-single-property-change rs 'wl-summary-action-argument
615 buf eol))
616 (put-text-property rs re 'wl-summary-action-argument nil)
617 (put-text-property rs re 'invisible nil)
618 (goto-char re)
619 (delete-char (- eol re)))))
621 (defun wl-summary-collect-numbers-region (begin end)
622 "Return a list of message number in the region specified by BEGIN and END."
623 (save-excursion
624 (save-restriction
625 (let (numbers)
626 (wl-summary-narrow-to-region (or begin (point-min))(or end (point-max)))
627 (goto-char (point-min))
628 ;; for thread...
629 (if (eq wl-summary-buffer-view 'thread)
630 (let (number entity)
631 (while (not (eobp))
632 (setq numbers (cons (wl-summary-message-number) numbers)
633 entity (wl-thread-get-entity number))
634 ;; When thread is closed...children should also be checked.
635 (unless (wl-thread-entity-get-opened entity)
636 (dolist (msg (wl-thread-get-children-msgs number))
637 (setq numbers (cons msg numbers))))
638 (forward-line 1)))
639 (let (number)
640 (while (not (eobp))
641 (setq numbers (cons (wl-summary-message-number) numbers))
642 (forward-line 1))))
643 (nreverse (delq nil numbers))))))
645 (defun wl-summary-exec (&optional numbers)
646 (interactive)
647 (let ((failures 0)
648 collected pair action modified)
649 (dolist (action wl-summary-mark-action-list)
650 (setq collected (cons (cons
651 (wl-summary-action-mark action)
652 nil) collected)))
653 (dolist (mark-info wl-summary-buffer-temp-mark-list)
654 (setq pair
655 (when (or (null numbers)
656 (memq (nth 0 mark-info) numbers))
657 (assoc (nth 1 mark-info) collected)))
658 (if pair
659 (setcdr pair (cons mark-info (cdr pair)))))
660 ;; collected is a pair of
661 ;; mark-string and a list of mark-info
662 (dolist (pair collected)
663 (when (cdr pair)
664 (setq action (assoc (car pair) wl-summary-mark-action-list))
665 (when (wl-summary-action-exec-function action)
666 (setq modified t)
667 (setq failures (+ failures (funcall
668 (wl-summary-action-exec-function action)
669 (cdr pair)))))))
670 (when modified
671 (wl-summary-set-message-modified))
672 (run-hooks 'wl-summary-exec-hook)
673 ;; message buffer is not up-to-date
674 (unless (and wl-message-buffer
675 (eq (wl-summary-message-number)
676 (with-current-buffer wl-message-buffer
677 wl-message-buffer-cur-number)))
678 (wl-summary-toggle-disp-msg 'off)
679 (setq wl-message-buffer nil))
680 (set-buffer-modified-p nil)
681 (when (> failures 0)
682 (message "%d execution(s) were failed" failures))))
684 (defun wl-summary-exec-region (beg end)
685 (interactive "r")
686 (wl-summary-exec
687 (wl-summary-collect-numbers-region beg end)))
689 (defun wl-summary-read-folder (default &optional purpose ignore-error
690 no-create init)
691 (let ((fld (completing-read
692 (format "Folder name %s(%s): " (or purpose "")
693 default)
694 'wl-folder-complete-folder
695 nil nil (or init wl-default-spec)
696 'wl-read-folder-history)))
697 (if (or (string= fld wl-default-spec)
698 (string= fld ""))
699 (setq fld default))
700 (setq fld (elmo-string (wl-folder-get-realname fld)))
701 (if (string-match "\n" fld)
702 (error "Not supported folder name: %s" fld))
703 (unless no-create
704 (if ignore-error
705 (condition-case nil
706 (wl-folder-confirm-existence
707 (wl-folder-get-elmo-folder
708 fld))
709 (error))
710 (wl-folder-confirm-existence (wl-folder-get-elmo-folder
711 fld))))
712 fld))
714 (defun wl-summary-print-argument (msg-num data)
715 "Print action argument on line."
716 (when data
717 (wl-summary-remove-argument)
718 (save-excursion
719 (let ((inhibit-read-only t)
720 (data (copy-sequence data))
721 (buffer-read-only nil)
722 len rs re c)
723 (setq len (string-width data))
724 (if (< len 1) ()
725 ;;(end-of-line)
726 (beginning-of-line)
727 (search-forward "\r")
728 (forward-char -1)
729 (setq re (point))
730 (let ((width (cond (wl-summary-width
731 (1- wl-summary-width))
732 (wl-summary-print-argument-within-window
733 (1- (window-width)))))
734 (c (current-column))
735 (padding 0))
736 (if (and width
737 (> (+ c len) width))
738 (progn
739 (move-to-column width)
740 (setq c (current-column))
741 (while (> (+ c len) width)
742 (forward-char -1)
743 (setq c (current-column)))
744 (when (< (+ c len) width)
745 (setq data (concat " " data)))
746 (setq rs (point))
747 (put-text-property rs re 'invisible t))
748 (when (and width
749 (> (setq padding (- width len c)) 0))
750 (setq data (concat (make-string padding ?\ ) data)))
751 (setq rs (1- re))))
752 (put-text-property rs re 'wl-summary-action-argument t)
753 (goto-char re)
754 (wl-highlight-action-argument-string data)
755 (insert data)
756 (set-buffer-modified-p nil))))))
758 (defsubst wl-summary-reserve-temp-mark-p (mark)
759 "Return t if temporal MARK should be reserved."
760 (member mark wl-summary-reserve-mark-list))
762 ;; Refile prev destination
763 (defun wl-summary-refile-prev-destination ()
764 "Refile message to previously refiled destination."
765 (interactive)
766 (funcall (symbol-function 'wl-summary-refile)
767 (wl-summary-message-number)
768 wl-summary-buffer-prev-refile-destination)
769 (if (and (interactive-p)
770 (eq wl-summary-move-direction-downward nil))
771 (wl-summary-prev)
772 (wl-summary-next)))
774 (defun wl-summary-refile-prev-destination-region (beg end)
775 "Refile messages in the region to previously refiled destination."
776 (interactive "r")
777 (wl-summary-mark-region-subr 'wl-summary-refile
778 beg end
779 wl-summary-buffer-prev-refile-destination))
781 (defun wl-thread-refile-prev-destination (arg)
782 "Refile messages in the thread to previously refiled destination."
783 (interactive "P")
784 (wl-thread-call-region-func
785 'wl-summary-refile-prev-destination-region
786 arg))
788 (defun wl-summary-target-mark-refile-prev-destination ()
789 "Refile messages with target mark to previously refiled destination."
790 (interactive)
791 (let ((elem wl-summary-mark-action-list)
792 action)
793 (while elem
794 (when (eq (wl-summary-action-symbol (car elem)) 'refile)
795 (setq action (car elem))
796 (setq elem nil))
797 (setq elem (cdr elem)))
798 (wl-summary-target-mark-set-action
799 (list
800 (car action)
801 'refile-prev-destination
802 (lambda (&rest args) wl-summary-buffer-prev-refile-destination)
803 (nth 2 action)
804 (nth 3 action)
805 (nth 4 action)
806 (nth 6 action)))))
808 (defsubst wl-summary-no-auto-refile-message-p (number)
809 (member (wl-summary-message-mark wl-summary-buffer-elmo-folder number)
810 wl-summary-auto-refile-skip-marks))
812 (defvar wl-auto-refile-guess-functions
813 '(wl-refile-guess-by-rule)
814 "*List of functions which is used for guessing refile destination folder.")
816 (defun wl-summary-auto-refile (&optional open-all)
817 "Set refile mark automatically according to 'wl-refile-guess-by-rule'."
818 (interactive "P")
819 (message "Marking...")
820 (save-excursion
821 (if (and (eq wl-summary-buffer-view 'thread)
822 open-all)
823 (wl-thread-open-all))
824 (let* ((spec (wl-summary-buffer-folder-name))
825 checked-dsts
826 (count 0)
827 number dst thr-entity)
828 (goto-line 1)
829 (while (not (eobp))
830 (setq number (wl-summary-message-number))
831 (dolist (number (cons number
832 (and (eq wl-summary-buffer-view 'thread)
833 ;; process invisible children.
834 (not (wl-thread-entity-get-opened
835 (setq thr-entity
836 (wl-thread-get-entity number))))
837 (wl-thread-entity-get-descendant
838 thr-entity))))
839 (when (and (not (wl-summary-no-auto-refile-message-p number))
840 (not (wl-summary-reserve-temp-mark-p
841 (nth 1 (wl-summary-registered-temp-mark number))))
842 (setq dst
843 (wl-folder-get-realname
844 (wl-refile-guess
845 (elmo-message-entity wl-summary-buffer-elmo-folder
846 number)
847 wl-auto-refile-guess-functions)))
848 (not (equal dst spec))
849 (let ((pair (assoc dst checked-dsts))
850 ret)
851 (if pair
852 (cdr pair)
853 (setq ret
854 (condition-case nil
855 (progn
856 (wl-folder-confirm-existence
857 (wl-folder-get-elmo-folder dst))
859 (error)))
860 (setq checked-dsts (cons (cons dst ret) checked-dsts))
861 ret)))
862 (if (funcall (symbol-function 'wl-summary-refile) number dst)
863 (incf count))
864 (message "Marking...%d message(s)." count)))
865 (forward-line))
866 (if (eq count 0)
867 (message "No message was marked.")
868 (message "Marked %d message(s)." count)))))
870 (defun wl-summary-unmark (&optional number)
871 "Unmark marks (temporary, refile, copy, delete)of current line.
872 If optional argument NUMBER is specified, unmark message specified by NUMBER."
873 (interactive)
874 (wl-summary-unset-mark number (interactive-p)))
876 (defun wl-summary-unmark-region (beg end)
877 (interactive "r")
878 (save-excursion
879 (save-restriction
880 (wl-summary-narrow-to-region beg end)
881 (goto-char (point-min))
882 (if (eq wl-summary-buffer-view 'thread)
883 (progn
884 (while (not (eobp))
885 (let* ((number (wl-summary-message-number))
886 (entity (wl-thread-get-entity number)))
887 (if (wl-thread-entity-get-opened entity)
888 ;; opened...unmark line.
889 (wl-summary-unmark)
890 ;; closed
891 (wl-summary-delete-marks-on-buffer
892 (wl-thread-get-children-msgs number))))
893 (forward-line 1)))
894 (while (not (eobp))
895 (wl-summary-unmark)
896 (forward-line 1))))))
898 (defun wl-summary-mark-region-subr (function beg end data)
899 (save-excursion
900 (save-restriction
901 (wl-summary-narrow-to-region beg end)
902 (goto-char (point-min))
903 (if (eq wl-summary-buffer-view 'thread)
904 (progn
905 (while (not (eobp))
906 (let* ((number (wl-summary-message-number))
907 (entity (wl-thread-get-entity number))
908 (wl-summary-move-direction-downward t)
909 children)
910 (if (wl-thread-entity-get-opened entity)
911 ;; opened...delete line.
912 (funcall function nil data)
913 ;; closed
914 (setq children (wl-thread-get-children-msgs number))
915 (while children
916 (funcall function (pop children) data)))
917 (forward-line 1))))
918 (while (not (eobp))
919 (funcall function nil data)
920 (forward-line 1))))))
922 (defun wl-summary-target-mark-all ()
923 (interactive)
924 (wl-summary-target-mark-region (point-min) (point-max)))
926 (defun wl-summary-delete-all-mark (mark)
927 (goto-char (point-min))
928 (while (not (eobp))
929 (when (string= (wl-summary-temp-mark) mark)
930 (wl-summary-unmark))
931 (forward-line 1))
932 (if (string= mark "*")
933 (setq wl-summary-buffer-target-mark-list nil)
934 (let (deleted)
935 (dolist (mark-info wl-summary-buffer-temp-mark-list)
936 (when (string= (nth 1 mark-info) mark)
937 (setq deleted (cons mark-info deleted))))
938 (dolist (delete deleted)
939 (setq wl-summary-buffer-temp-mark-list
940 (delq delete wl-summary-buffer-temp-mark-list))))))
942 (defun wl-summary-unmark-all ()
943 "Unmark all according to what you input."
944 (interactive)
945 (let ((unmarks (string-to-char-list (read-from-minibuffer "Unmark: ")))
946 cur-mark)
947 (save-excursion
948 (while unmarks
949 (setq cur-mark (char-to-string (car unmarks)))
950 (wl-summary-delete-all-mark cur-mark)
951 (setq unmarks (cdr unmarks))))))
953 (defun wl-summary-target-mark-thread ()
954 (interactive)
955 (wl-thread-call-region-func 'wl-summary-target-mark-region t))
957 (require 'product)
958 (product-provide (provide 'wl-action) (require 'wl-version))
960 ;;; wl-action.el ends here