From add34418c88baf9d685ea05ba8a1551021e4105d Mon Sep 17 00:00:00 2001 From: Carsten Dominik Date: Fri, 25 Apr 2008 08:18:49 +0200 Subject: [PATCH] Implement a basic history system for recently clocked tasks. Recently clocked tasks are now remembered as well as a default task and an interrupted task. There is no automatic clock-in yet. Also, a number of bug fixes have sneaked into this patch, sorry for the mess. --- ORGWEBPAGE/Changes.org | 15 +++++ doc/org.texi | 11 +++- lisp/org-clock.el | 165 ++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 158 insertions(+), 33 deletions(-) diff --git a/ORGWEBPAGE/Changes.org b/ORGWEBPAGE/Changes.org index cef5f6d7b..59b5ee282 100644 --- a/ORGWEBPAGE/Changes.org +++ b/ORGWEBPAGE/Changes.org @@ -8,6 +8,21 @@ #+LINK_UP: index.html #+LINK_HOME: http://orgmode.org +* Version 6.02a +** Details +*** Clock task history + Org now remembers the last 5 tasks that you clocked into, to + make it easier to clock back into a task after interrupting + it for another task. + - `C-u C-u C-c C-x C-i' (or `C-u C-u I' from the agenda) wil + clock into that task and mark it as current default task. + - `C-u C-c C-x C-i' (or `C-u I' from the agenda) will offer a + list of recently clocked tasks, including the default task, + for selection. `d' selects the default task, `i' selects + the task that was interrupted by the task that is currently + being clocked. `1',... selects a recent task. When you + select a task, you will be clocked into it. + * Version 6.02 ** Overview diff --git a/doc/org.texi b/doc/org.texi index 4637e6811..982a1a051 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -4493,7 +4493,11 @@ Start the clock on the current item (clock-in). This inserts the CLOCK keyword together with a timestamp. If this is not the first clocking of this item, the multiple CLOCK lines will be wrapped into a @code{:CLOCK:} drawer (see also the variable -@code{org-clock-into-drawer}). +@code{org-clock-into-drawer}). When called with a @kbd{C-u} prefix argument, +select the task from a list of recently clocked tasks. With two @kbd{C-u +C-u} prefixes, clock into the task at point and mark it as the default task. +The default task will always be available when selecting a clocking task, +with letter @kbd{d}. @kindex C-c C-x C-o @item C-c C-x C-o Stop the clock (clock-out). The inserts another timestamp at the same @@ -4518,8 +4522,9 @@ Cancel the current clock. This is useful if a clock was started by mistake, or if you ended up working on something else. @kindex C-c C-x C-j @item C-c C-x C-j -Jump to the entry that contains the currently running clock, an another -window. +Jump to the entry that contains the currently running clock. With a +@kbd{C-u} prefix arg, select the target task from a list of recently clocked +tasks. @kindex C-c C-x C-d @item C-c C-x C-d Display time summaries for each subtree in the current buffer. This diff --git a/lisp/org-clock.el b/lisp/org-clock.el index 88029ae9f..9de924e7c 100644 --- a/lisp/org-clock.el +++ b/lisp/org-clock.el @@ -93,6 +93,82 @@ The function is called with point at the beginning of the headline." (defvar org-clock-heading "") (defvar org-clock-start-time "") +(defvar org-clock-history + (list (make-marker) (make-marker) (make-marker) (make-marker) (make-marker)) + "Marker pointing to the previous task teking clock time. +This is used to find back to the previous task after interrupting work. +When clocking into a task and the clock is currently running, this marker +is moved to the position of the currently running task and continues +to point there even after the task is clocked out.") + +(defun org-clock-history-push (&optional pos buffer) + (let ((m (org-last org-clock-history))) + (move-marker m (or pos (point)) buffer) + (setq org-clock-history + (reverse (cdr (reverse org-clock-history)))) + (while (member m org-clock-history) + (move-marker (car (member m org-clock-history)) nil)) + (setq org-clock-history (cons m org-clock-history)))) + +(defun org-clock-select-task (&optional prompt) + "Select a task that recently was associated with clocking." + (interactive) + (let (sel-list rpl file task (i 0)) + (save-window-excursion + (org-switch-to-buffer-other-window + (get-buffer-create "*Clock Task Select*")) + (erase-buffer) + (when (marker-buffer org-clock-default-task) + (insert (org-add-props "Default Task\n" nil 'face 'bold)) + (setq s (org-clock-insert-selection-line ?d org-clock-default-task)) + (push s sel-list)) + (when (marker-buffer org-clock-interrupted-task) + (insert (org-add-props "Interrupted Task\n" nil 'face 'bold)) + (setq s (org-clock-insert-selection-line ?i org-clock-interrupted-task)) + (push s sel-list)) + (insert (org-add-props "Recent Tasks\n" nil 'face 'bold)) + (mapc + (lambda (m) + (when (marker-buffer m) + (setq i (1+ i) + s (org-clock-insert-selection-line + (string-to-char (number-to-string i)) m)) + (push s sel-list))) + org-clock-history) + (shrink-window-if-larger-than-buffer) + (message (or prompt "Select task for clocking:")) + (setq rpl (read-char-exclusive)) + (cond + ((eq rpl ?q) nil) + ((eq rpl ?x) nil) + ((assoc rpl sel-list) (cdr (assoc rpl sel-list))) + (t (error "Invalid task choice %c" rpl)))))) + +(defun org-clock-insert-selection-line (i marker) + (when (marker-buffer marker) + (let (file cat task) + (with-current-buffer (marker-buffer marker) + (save-excursion + (goto-char marker) + (setq file (buffer-file-name (marker-buffer marker)) + cat (or (org-get-category) + (progn (org-refresh-category-properties) + (org-get-category))) + task (org-get-heading 'notags)))) + (when (and cat task) + (insert (format "[%c] %-15s %s\n" i cat task)) + (cons i marker))))) + +(defvar org-clock-default-task (make-marker) + "Marker pointing to the default task that should clock time. +The clock can be made to switch to this task after clocking out +of a different task.") + +(defvar org-clock-interrupted-task (make-marker) + "Marker pointing to the default task that should clock time. +The clock can be made to switch to this task after clocking out +of a different task.") + (defun org-update-mode-line () (let* ((delta (- (time-to-seconds (current-time)) (time-to-seconds org-clock-start-time))) @@ -106,14 +182,39 @@ The function is called with point at the beginning of the headline." (defvar org-clock-mode-line-entry nil "Information for the modeline about the running clock.") -(defun org-clock-in () +(defun org-clock-in (&optional select) "Start the clock on the current item. -If necessary, clock-out of the currently active clock." - (interactive) - (org-clock-out t) - (let (ts) +If necessary, clock-out of the currently active clock. +With prefix arg SELECT, offer a list of recently clocked tasks to +clock into. When SELECT is `C-u C-u', clock into the current task and mark +is as the default task, a special task that will always be offered in +the clocking selection, associated with the letter `d'." + (interactive "P") + (let (ts selected-task) + ;; Are we interrupting the clocking of a differnt task? + (if (marker-buffer org-clock-marker) + (progn + (move-marker org-clock-interrupted-task + (marker-position org-clock-marker) + (marker-buffer org-clock-marker)) + (let ((org-clock-inhibit-clock-restart t)) + (org-clock-out t))) + (move-marker org-clock-interrupted-task nil)) + + (cond + ((equal select '(16)) + (save-excursion + (org-back-to-heading t) + (move-marker org-clock-default-task (point)))) + ((equal select '(4)) + (setq selected-task (org-clock-select-task "Clock-in on task: ")))) + (save-excursion (org-back-to-heading t) + (when (and selected-task (marker-buffer selected-task)) + (set-buffer (marker-buffer selected-task)) + (goto-char selected-task)) + (org-clock-history-push) (when (and org-clock-in-switch-to-state (not (looking-at (concat outline-regexp "[ \t]*" org-clock-in-switch-to-state @@ -127,7 +228,7 @@ If necessary, clock-out of the currently active clock." (setq org-clock-heading "???"))) (setq org-clock-heading (propertize org-clock-heading 'face nil)) (org-clock-find-position) - + (insert "\n") (backward-char 1) (indent-relative) (insert org-clock-string " ") @@ -258,19 +359,23 @@ If there is no running clock, throw an error, unless FAIL-QUIETLY is set." (force-mode-line-update) (message "Clock canceled")) -(defun org-clock-goto (&optional delete-windows) - "Go to the currently clocked-in entry." +(defun org-clock-goto (&optional select) + "Go to the currently clocked-in entry. +With prefix arg SELECT, offer recently clocked tasks." (interactive "P") - (if (not (marker-buffer org-clock-marker)) - (error "No active clock")) - (switch-to-buffer-other-window - (marker-buffer org-clock-marker)) - (if delete-windows (delete-other-windows)) - (goto-char org-clock-marker) - (org-show-entry) - (org-back-to-heading) - (org-cycle-hide-drawers 'children) - (recenter)) + (let ((m (if select + (org-clock-select-task "Select task to go to: ") + org-clock-marker))) + (if (not (marker-buffer m)) + (if select + (error "No task selected") + (error "No active clock"))) + (switch-to-buffer (marker-buffer m)) + (goto-char m) + (org-show-entry) + (org-back-to-heading) + (org-cycle-hide-drawers 'children) + (recenter))) (defvar org-clock-file-total-minutes nil "Holds the file total time in minutes, after a call to `org-clock-sum'.") @@ -666,9 +771,9 @@ the currently selected interval size." (scope 'agenda) (p1 (copy-sequence params)) file) - (plist-put p1 :tostring t) - (plist-put p1 :multifile t) - (plist-put p1 :scope 'file) + (setq p1 (plist-put p1 :tostring t)) + (setq p1 (plist-put p1 :multifile t)) + (setq p1 (plist-put p1 :scope 'file)) (org-prepare-agenda-buffers files) (while (setq file (pop files)) (with-current-buffer (find-buffer-visiting file) @@ -767,17 +872,17 @@ the currently selected interval size." (apply 'encode-time (org-parse-time-string ts))))) (if te (setq te (time-to-seconds (apply 'encode-time (org-parse-time-string te))))) - (plist-put p1 :header "") - (plist-put p1 :step nil) - (plist-put p1 :block nil) + (setq p1 (plist-put p1 :header "")) + (setq p1 (plist-put p1 :step nil)) + (setq p1 (plist-put p1 :block nil)) (while (< ts te) (or (bolp) (insert "\n")) - (plist-put p1 :tstart (format-time-string - (car org-time-stamp-formats) - (seconds-to-time ts))) - (plist-put p1 :tend (format-time-string - (car org-time-stamp-formats) - (seconds-to-time (setq ts (+ ts step))))) + (setq p1 (plist-put p1 :tstart (format-time-string + (car org-time-stamp-formats) + (seconds-to-time ts)))) + (setq p1 (plist-put p1 :tend (format-time-string + (car org-time-stamp-formats) + (seconds-to-time (setq ts (+ ts step)))))) (insert "\n" (if (eq step0 'day) "Daily report: " "Weekly report starting on: ") (plist-get p1 :tstart) "\n") (org-dblock-write:clocktable p1) -- 2.11.4.GIT