1
0
mirror of https://git.savannah.gnu.org/git/emacs/org-mode.git synced 2025-01-20 19:24:20 +00:00

Agenda: Refile and bulk action

This commit implements refiling directly from the agenda.

It also implements a mechanism for selecting a number of entries in
the agenda and then executing a command on all of them.  Possible
actions include archive, refile, todo state setting, and more.
This commit is contained in:
Carsten Dominik 2009-06-22 21:54:42 +02:00
parent 7040e46738
commit 2e3a26ae35
6 changed files with 216 additions and 17 deletions

View File

@ -1,3 +1,10 @@
2009-06-24 Carsten Dominik <carsten.dominik@gmail.com>
* orgcard.tex: Document bulk action.
* org.texi (Agenda commands): Document bulk action and remote
refiling.
2009-06-17 Carsten Dominik <carsten.dominik@gmail.com>
* org.texi (Clocking work time): Document the key to update effort

View File

@ -7075,6 +7075,10 @@ to it in the original Org file. If the text to be deleted remotely
is longer than one line, the kill needs to be confirmed by the user. See
variable @code{org-agenda-confirm-kill}.
@c
@kindex C-c C-w
@item C-c C-w
Refile the entry at point.
@c
@kindex a
@item a
Toggle the ARCHIVE tag for the current headline.
@ -7200,6 +7204,31 @@ Cancel the currently running clock.
@item J
Jump to the running clock in another window.
@tsubheading{Bulk remote editing selected entries}
@cindex remote editing, bulk, from agenda
@kindex s
@item s
Mark the entry at point for bulk editing. If the entry is already marked,
remove the mark from it. With double prefix, remove all marks.
@kindex B
@item B
Bulk action: Act on all marked entries in the agenda. This will prompt for
another key to select the action to be applied:
@example
r @r{Prompt for a single refile target and move all entries. The entries}
@r{will no longer be in the agenda, refresh (@kbd{g}) to bring them back.}
$ @r{Archive all selected entries.}
A @r{Archive entries by moving them to their respective archive siblings.}
t @r{Change TODO state. This prompts for a single TODO keyword and}
@r{changes the state of all selected entries, bypasing blocking and}
@r{suppressig logging notes (but not time stamps).}
+ @r{Add a tag to all selected entries.}
- @r{Remove a tag from all selected entries.}
@end example
@tsubheading{Calendar commands}
@cindex calendar commands, from agenda
@kindex c

View File

@ -650,20 +650,20 @@ after ``{\tt :}'', and dictionary words elsewhere.
\key{change state of current TODO item}{t}
\key{kill item and source}{C-k}
\key{archive the subtree (file/tag/sibling)}{\$ / a / A}
\key{refile the subtree}{C-c C-w}
\key{show tags of current headline}{T}
\key{set tags for current headline/region}{:}
\key{set priority of current item}{p}
\key{set / compute priority of current item}{p / P}
\key{raise/lower priority of current item}{S-UP/DOWN$^3$}
\key{display weighted priority of current item}{P}
\key{run an attachment command}{C-c C-a}
\key{schedule/set deadline for this item}{C-c C-s/d}
\key{change timestamp to one day earlier/later}{S-LEFT/RIGHT$^3$}
\key{change timestamp to today}{>}
\key{insert new entry into diary}{i}
\newcolumn
\key{start/stop/cancel the clock on current item}{I / O / X}
\key{jump to running clock entry}{J}
\key{select for / execute bulk action}{s / B}
{\bf Misc}

View File

@ -1,3 +1,15 @@
2009-06-24 Carsten Dominik <carsten.dominik@gmail.com>
* org-agenda.el (org-agenda-mode): Reset list of marks.
(org-agenda-mode-map): Define new keys for refile and bulk action.
(org-agenda-menu): Add menu itesm for refile and bulk action.
(org-agenda-refile): New function.
(org-agenda-set-tags): Optional arguments TAG and ONOFF.
(org-agenda-marked-entries): New variable.
(org-agenda-bulk-select, org-agenda-remove-bulk-action-overlays)
(org-agenda-remove-all-bulk-action-marks)
(org-agenda-bulk-action): New functions/commands.
2009-06-23 Carsten Dominik <carsten.dominik@gmail.com>
* org-exp.el (org-get-file-contents): Protect org-like lines in

View File

@ -1246,7 +1246,8 @@ The following commands are available:
(interactive)
(kill-all-local-variables)
(setq org-agenda-undo-list nil
org-agenda-pending-undo-list nil)
org-agenda-pending-undo-list nil
org-agenda-marked-entries nil)
(setq major-mode 'org-agenda-mode)
;; Keep global-font-lock-mode from turning on font-lock-mode
(org-set-local 'font-lock-global-modes (list 'not major-mode))
@ -1292,6 +1293,9 @@ The following commands are available:
(org-defkey org-agenda-mode-map "\C-k" 'org-agenda-kill)
(org-defkey org-agenda-mode-map "\C-c$" 'org-agenda-archive)
(org-defkey org-agenda-mode-map "\C-c\C-x\C-s" 'org-agenda-archive)
(org-defkey org-agenda-mode-map "\C-c\C-w" 'org-agenda-refile)
(org-defkey org-agenda-mode-map "s" 'org-agenda-bulk-select)
(org-defkey org-agenda-mode-map "B" 'org-agenda-bulk-action)
(org-defkey org-agenda-mode-map "\C-c\C-x!" 'org-reload)
(org-defkey org-agenda-mode-map "$" 'org-agenda-archive)
(org-defkey org-agenda-mode-map "A" 'org-agenda-archive-to-archive-sibling)
@ -1342,7 +1346,6 @@ The following commands are available:
(org-defkey org-agenda-mode-map "q" 'org-agenda-quit)
(org-defkey org-agenda-mode-map "x" 'org-agenda-exit)
(org-defkey org-agenda-mode-map "\C-x\C-w" 'org-write-agenda)
(org-defkey org-agenda-mode-map "s" 'org-save-all-org-buffers)
(org-defkey org-agenda-mode-map "\C-x\C-s" 'org-save-all-org-buffers)
(org-defkey org-agenda-mode-map "P" 'org-agenda-show-priority)
(org-defkey org-agenda-mode-map "T" 'org-agenda-show-tags)
@ -1407,11 +1410,17 @@ The following commands are available:
["Tree to indirect frame" org-agenda-tree-to-indirect-buffer t]
"--"
["Cycle TODO" org-agenda-todo t]
("Archive"
("Archive and Refile"
["Toggle ARCHIVE tag" org-agenda-toggle-archive-tag t]
["Move to archive sibling" org-agenda-archive-to-archive-sibling t]
["Archive subtree" org-agenda-archive t])
["Archive subtree" org-agenda-archive t]
["Refile" org-agenda-refile t])
["Delete subtree" org-agenda-kill t]
("Bulk action"
["Toggle mark entry" org-agenda-bulk-select t]
["Act on all marked" org-agenda-bulk-action t]
["Unmark all entries" org-agenda-remove-all-bulk-action-marks :active t :keys "C-u s"])
"--"
["Add note" org-agenda-add-note t]
"--"
["Goto Today" org-agenda-goto-today (org-agenda-check-type nil 'agenda 'timeline)]
@ -2826,9 +2835,9 @@ given in `org-agenda-start-on-weekday'."
'org-agenda-date))
(put-text-property s (1- (point)) 'org-date-line t)
(put-text-property s (1- (point)) 'org-day-cnt day-cnt)
(when todayp
(put-text-property s (1- (point)) 'org-today t)
(put-text-property s (1- (point)) 'face 'org-agenda-date-today))
(when todayp
(put-text-property s (1- (point)) 'org-today t)
(put-text-property s (1- (point)) 'face 'org-agenda-date-today))
(if rtnall (insert
(org-finalize-agenda-entries
(org-agenda-add-time-grid-maybe
@ -5361,6 +5370,28 @@ If this information is not given, the function uses the tree at point."
(delete-region (point-at-bol) (1+ (point-at-eol)))))
(beginning-of-line 0))))))
(defun org-agenda-refile (&optional goto rfloc)
"Refile the item at point."
(interactive "P")
(let* ((marker (or (get-text-property (point) 'org-hd-marker)
(org-agenda-error)))
(buffer (marker-buffer marker))
(pos (marker-position marker))
(rfloc (or rfloc
(org-refile-get-location
(if goto "Goto: " "Refile to: ") buffer
org-refile-allow-creating-parent-nodes))))
(with-current-buffer buffer
(save-excursion
(save-restriction
(widen)
(goto-char marker)
(org-remove-subtree-entries-from-agenda)
(org-refile goto buffer rfloc))))))
(defun org-agenda-open-link ()
"Follow the link in the current line, if any."
(interactive)
@ -5721,7 +5752,7 @@ the same tree node, and the headline of the tree node in the Org-mode file."
(beginning-of-line 1))))
;; FIXME: should fix the tags property of the agenda line.
(defun org-agenda-set-tags ()
(defun org-agenda-set-tags (&optional tag onoff)
"Set tags for the current headline."
(interactive)
(org-agenda-check-no-diary)
@ -5744,7 +5775,9 @@ the same tree node, and the headline of the tree node in the Org-mode file."
(and (outline-next-heading)
(org-flag-heading nil))) ; show the next heading
(goto-char pos)
(call-interactively 'org-set-tags)
(if tag
(org-toggle-tag tag onoff)
(call-interactively 'org-set-tags))
(end-of-line 1)
(setq newhead (org-get-heading)))
(org-agenda-change-all-lines newhead hdmarker)
@ -6197,6 +6230,119 @@ This is a command that has to be installed in `calendar-mode-map'."
(princ s))
(org-fit-window-to-buffer (get-buffer-window "*Dates*"))))
;;; Bulk commands
(defvar org-agenda-marked-entries nil
"List of markers that refer to marked entries in the agenda.")
(defun org-agenda-bulk-select (&optional remove-all)
"Toggle marking the entry at point for future bulk action.
With `C-u' prefix arg, run an action for all marked entries.
With double `C-u C-u' prefix arg, remove all marks."
(interactive "P")
(if remove-all
(org-agenda-remove-all-bulk-action-marks)
(org-agenda-check-no-diary)
(let* ((m (get-text-property (point) 'org-hd-marker))
ov)
(if (eq (get-char-property (point-at-bol) 'type)
'org-marked-entry-overlay)
(progn
(org-agenda-remove-bulk-action-overlays
(point-at-bol) (+ 2 (point-at-bol)))
(setq org-agenda-marked-entries
(delete (get-text-property (point-at-bol) 'org-hd-marker)
org-agenda-marked-entries)))
(unless m (error "Nothing to mark at point"))
(push m org-agenda-marked-entries)
(setq ov (org-make-overlay (point-at-bol) (+ 2 (point-at-bol))))
(org-overlay-display ov ">>"
(org-get-todo-face "TODO")
'evaporate)
(org-overlay-put ov 'type 'org-marked-entry-overlay)
(beginning-of-line 2)))))
(defun org-agenda-remove-bulk-action-overlays (&optional beg end)
"Remove the mark overlays between BEG and END in the agenda buffer.
BEG and END default to the buffer limits.
This only removes the overlays, it does not remove the markers
from the list in `org-agenda-marked-entries'."
(interactive)
(mapc (lambda (ov)
(and (eq (org-overlay-get ov 'type) 'org-marked-entry-overlay)
(org-delete-overlay ov)))
(org-overlays-in (or beg (point-min)) (or end (point-max)))))
(defun org-agenda-remove-all-bulk-action-marks ()
"Remove all marks in the agenda buffer.
This will remove the markers, and the overlays."
(interactive)
(mapc (lambda (m) (move-marker m nil)) org-agenda-marked-entries)
(setq org-agenda-marked-entries nil)
(org-agenda-remove-bulk-action-overlays (point-min) (point-max)))
(defun org-agenda-bulk-action ()
"Execute an remote-editing action on all marked entries."
(interactive)
(unless org-agenda-marked-entries
(error "No entries are marked"))
(message "Action: [r]efile [$]archive [A]rch-to-sib [t]odo [+]tag [-]tag")
(let* ((action (read-char-exclusive))
(entries (reverse org-agenda-marked-entries))
cmd rfloc state e (cnt 0))
(cond
((equal action ?$)
(setq cmd '(org-agenda-archive)))
((equal action ?A)
(setq cmd '(org-agenda-archive-to-archive-sibling)))
((member action '(?r ?w))
(setq rfloc (org-refile-get-location
"Refile to: "
(marker-buffer (car org-agenda-marked-entries))
org-refile-allow-creating-parent-nodes))
(setcar (nthcdr 3 rfloc)
(move-marker (make-marker) (nth 3 rfloc)
(or (get-file-buffer (nth 1 rfloc))
(find-buffer-visiting (nth 1 rfloc))
(error "This should not happen"))))
(setq cmd (list 'org-agenda-refile nil (list 'quote rfloc))))
((equal action ?t)
(setq state (org-ido-completing-read
"Todo state: "
(with-current-buffer (marker-buffer (car entries))
(mapcar 'list org-todo-keywords-1))))
(setq cmd `(let ((org-inhibit-blocking t)
(org-inhibit-logging 'note))
(org-agenda-todo ,state))))
((memq action '(?- ?+))
(setq tag (org-ido-completing-read
(format "Tag to %s: " (if (eq action ?+) "add" "remove"))
(with-current-buffer (marker-buffer (car entries))
(delq nil
(mapcar (lambda (x)
(if (stringp (car x)) x)) org-tag-alist)))))
(setq cmd `(org-agenda-set-tags ,tag ,(if (eq action ?+) ''on ''off))))
(t (error "Invalid bulk action")))
;; Now loop over all markers and apply cmd
(while (setq e (pop entries))
(goto-char
(or (text-property-any (point-min) (point-max) 'org-hd-marker e)
(error "Cannot find entry for marker %s" e)))
(eval cmd)
(setq org-agenda-marked-entries (delete e org-agenda-marked-entries))
(setq cnt (1+ cnt)))
(setq org-agenda-marked-entries nil)
(org-agenda-remove-all-bulk-action-marks)
(message "Acted on %d entries" cnt)))
;;; Appointment reminders
(defvar appt-time-msg-list)

View File

@ -8384,7 +8384,7 @@ on the system \"/user@host:\"."
Note that this is still *before* the stuff will be removed from
the *old* location.")
(defun org-refile (&optional goto default-buffer)
(defun org-refile (&optional goto default-buffer rfloc)
"Move the entry at point to another heading.
The list of target headings is compiled using the information in
`org-refile-targets', which see. This list is created before each use
@ -8404,6 +8404,8 @@ not actually move anything.
With a double prefix `C-u C-u', go to the location where the last refiling
operation has put the subtree.
RFLOC can be a refile location obtained in a different way.
See also `org-refile-use-outline-path' and `org-completion-use-ido'"
(interactive "P")
(let* ((cbuf (current-buffer))
@ -8419,9 +8421,10 @@ See also `org-refile-use-outline-path' and `org-completion-use-ido'"
(error "The region is not a (sequence of) subtree(s)")))
(if (equal goto '(16))
(org-refile-goto-last-stored)
(when (setq it (org-refile-get-location
(if goto "Goto: " "Refile to: ") default-buffer
org-refile-allow-creating-parent-nodes))
(when (setq it (or rfloc
(org-refile-get-location
(if goto "Goto: " "Refile to: ") default-buffer
org-refile-allow-creating-parent-nodes)))
(setq file (nth 1 it)
re (nth 2 it)
pos (nth 3 it))
@ -9141,11 +9144,13 @@ For calling through lisp, arg is also interpreted in the following way:
(not (member this org-done-keywords))))
(and logging (org-local-logging logging))
(when (and (or org-todo-log-states org-log-done)
(not org-inhibit-logging)
(not (eq org-inhibit-logging t))
(not (memq arg '(nextset previousset))))
;; we need to look at recording a time and note
(setq dolog (or (nth 1 (assoc state org-todo-log-states))
(nth 2 (assoc this org-todo-log-states))))
(if (and (eq dolog 'note) (eq org-inhibit-logging 'note))
(setq dolog 'time))
(when (and state
(member state org-not-done-keywords)
(not (member this org-not-done-keywords)))