summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Wiegley <johnw@newartisans.com>2009-10-17 04:56:45 -0400
committerJohn Wiegley <johnw@newartisans.com>2009-10-18 23:43:43 -0400
commitabfc6babcaf6e20a4c12e5c4754ce107ddc0dc7b (patch)
tree4b3fb7a9e92c3d38f4f70eda1464b0cfa4f4f91a
parent57a7a4c15b7d3d04966ebe9f855e41bd5d84c768 (diff)
downloadorg-mode-abfc6babcaf6e20a4c12e5c4754ce107ddc0dc7b.tar.gz
Added feature for resolving clocks due to idleness
See the new manual section on "Resolving idle time". (org-clock-resolve-clock): If keeping or subtracting time results in a clock out at a time in the past, and if the resolution occurred due to idleness or invoking `M-x org-resolve-clocks', remember that past moment in time. On the next clock in, the user will be prompted to see if they want to back-date their new clock to then. (org-clock-resolve): Do not jump the user to the location of a dangling clock if the resolution is occuring due to an idle timeout. In that case there is typically only one dangling clock, the active one, and there is no value gained by shuffling their windows around to show it to them. Being prompted to resolve an idle clock should be as inobtrusive as possible. (org-resolve-clocks-if-idle): New function that resolves only the currently active clock if the user has exceeded the time returned by `org-user-idle-seconds', based on the value of `org-clock-idle-time'. (org-clock-in): If, after resolving clocks, (org-clock-out): Cancel the `org-clock-idle-timer' on clock out.
-rw-r--r--doc/ChangeLog5
-rw-r--r--doc/org.texi71
-rwxr-xr-xlisp/ChangeLog19
-rw-r--r--lisp/org-clock.el140
4 files changed, 187 insertions, 48 deletions
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 66c0c51..4ae3099 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -3,6 +3,11 @@
* org.texi (Pushing to MobileOrg): Mention that `org-directory'
should be set.
+2009-10-17 John Wiegley <johnw@newartisans.com>
+
+ * org.texi (Resolving idle time): Added a section on how idle and
+ dangling clocks are resolved.
+
2009-10-14 Carsten Dominik <carsten.dominik@gmail.com>
* org.texi (Agenda commands): Document that SPC is a filter for
diff --git a/doc/org.texi b/doc/org.texi
index 29a3e9f..67a3a10 100644
--- a/doc/org.texi
+++ b/doc/org.texi
@@ -231,6 +231,7 @@ Dates and Times
* Creating timestamps:: Commands which insert timestamps
* Deadlines and scheduling:: Planning your work
* Clocking work time:: Tracking how long you spend on a task
+* Resolving idle time::
* Effort estimates:: Planning work effort in advance
* Relative timer:: Notes with a running timer
@@ -4703,6 +4704,7 @@ is used in a much wider sense.
* Creating timestamps:: Commands which insert timestamps
* Deadlines and scheduling:: Planning your work
* Clocking work time:: Tracking how long you spend on a task
+* Resolving idle time::
* Effort estimates:: Planning work effort in advance
* Relative timer:: Notes with a running timer
@end menu
@@ -5220,7 +5222,7 @@ subtree, with dates shifted in each copy. The command @kbd{C-c C-x c} was
created for this purpose, it is described in @ref{Structure editing}.
-@node Clocking work time, Effort estimates, Deadlines and scheduling, Dates and Times
+@node Clocking work time, Resolving idle time, Deadlines and scheduling, Dates and Times
@section Clocking work time
Org mode allows you to clock the time you spend on specific tasks in a
@@ -5405,7 +5407,72 @@ The @kbd{l} key may be used in the timeline (@pxref{Timeline}) and in
the agenda (@pxref{Weekly/daily agenda}) to show which tasks have been
worked on or closed during a day.
-@node Effort estimates, Relative timer, Clocking work time, Dates and Times
+@node Resolving idle time, Effort estimates, Clocking work time, Dates and Times
+@section Resolving idle time
+@cindex resolve idle time
+
+@cindex idle, resolve, dangling
+If you clock in on a work item, and then walk away from your
+computer---perhaps to take a phone call---you often need to ``resolve'' the
+time you were away by either subtracting it from the current clock, or
+applying it to another one.
+
+@vindex org-clock-idle-time
+By customizing the variable @code{org-clock-idle-time} to some integer, such
+as 10 or 15, Emacs can alert you when you get back to your computer after
+being idle for that many minutes@footnote{On computers using Mac OS X,
+idleness is based on actual user idleness, not just Emacs' idle time.}, and
+ask what you want to do with the idle time. There will be a question waiting
+for you when you get back, indicating how much idle time has passed
+(constantly updated with the current amount), as well as a set of choices to
+correct the discrepancy:
+
+@table @kbd
+@item k
+To keep some or all of the minutes and stay clocked in, press @key{k}. Org
+will ask how many of the minutes to keep. Press @key{RET} to keep them all,
+effectively changing nothing, or enter a number to keep that many minutes.
+@item K
+If you use the shift key and press @key{K}, it will keep however many minutes
+you request and then immediately clock out of that task. If you keep all of
+the minutes, this is the same as just clocking out of the current task.
+@item s
+To keep none of the minutes, use @key{s} to subtract all the away time from
+the clock, and then check back in from the moment you returned.
+@item S
+To keep none of the minutes and just clock out at the start of the away time,
+use the shift key and press @key{S}. Remember that using shift will always
+leave you clocked out, no matter which option you choose.
+@item C
+To cancel the clock altogether, use @key{C}. Note that if instead of
+cancelling you subtract the away time, and the resulting clock amount is less
+than a minute, the clock will still be cancelled rather than clutter up the
+log with an empty entry.
+@end table
+
+What if you subtracted those away minutes from the current clock, and now
+want to apply them to a new clock? Simply clock in to any task immediately
+after the subtraction. Org will notice that you have subtracted time ``on
+the books'', so to speak, and will ask if you want to apply those minutes to
+the next task you clock in on.
+
+There is one other instance when this clock resolution magic occurs. Say you
+were clocked in and hacking away, and suddenly your cat chased a mouse who
+scared a hamster that crashed into your UPS's power button! You suddenly
+lose all your buffers, but thanks to auto-save you still have your recent Org
+mode changes, including your last clock in.
+
+If you restart Emacs and clock into any task, Org will notice that you have a
+dangling clock which was never clocked out from your last session. Using
+that clock's starting time as the beginning of the unaccounted-for period,
+Org will ask how you want to resolve that time. The logic and behavior is
+identical to dealing with away time due to idleness, it's just happening due
+to a recovery event rather than a set amount of idle time.
+
+You can also check all the files visited by your Org agenda for dangling
+clocks at any time using @kbd{M-x org-resolve-clocks}.
+
+@node Effort estimates, Relative timer, Resolving idle time, Dates and Times
@section Effort estimates
@cindex effort estimates
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 7cb0d5a..ee4dbe5 100755
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -31,6 +31,25 @@
2009-10-17 John Wiegley <johnw@newartisans.com>
+ * org-clock.el (org-clock-resolve-clock): If keeping or
+ subtracting time results in a clock out at a time in the past, and
+ if the resolution occurred due to idleness or invoking `M-x
+ org-resolve-clocks', remember that past moment in time. On the
+ next clock in, the user will be prompted to see if they want to
+ back-date their new clock to then.
+ (org-clock-resolve): Do not jump the user to the location of a
+ dangling clock if the resolution is occuring due to an idle
+ timeout. In that case there is typically only one dangling clock,
+ the active one, and there is no value gained by shuffling their
+ windows around to show it to them. Being prompted to resolve an
+ idle clock should be as inobtrusive as possible.
+ (org-resolve-clocks-if-idle): New function that resolves only the
+ currently active clock if the user has exceeded the time returned
+ by `org-user-idle-seconds', based on the value of
+ `org-clock-idle-time'.
+ (org-clock-in): If, after resolving clocks,
+ (org-clock-out): Cancel the `org-clock-idle-timer' on clock out.
+
* org-clock.el (org-clock-resolve-clock): New function that
resolves a clock to a specific time, closing or resuming as need
be, and possibly even starting a new clock.
diff --git a/lisp/org-clock.el b/lisp/org-clock.el
index 89a87e8..298331e 100644
--- a/lisp/org-clock.el
+++ b/lisp/org-clock.el
@@ -235,10 +235,14 @@ to add an effort property.")
(put 'org-mode-line-string 'risky-local-variable t)
(defvar org-clock-mode-line-timer nil)
+(defvar org-clock-idle-timer nil)
(defvar org-clock-heading "")
(defvar org-clock-heading-for-remember "")
(defvar org-clock-start-time "")
+(defvar org-clock-left-over-time nil
+ "If non-nil, user cancelled a clock; this is when leftover time started.")
+
(defvar org-clock-effort ""
"Effort estimate of the currently clocking task")
@@ -576,6 +580,8 @@ If necessary, clock-out of the currently active clock."
(setcar clock temp)))
(defvar org-clock-clocking-in nil)
+(defvar org-clock-resolving-clocks nil)
+(defvar org-clock-resolving-clocks-due-to-idleness nil)
(defun org-clock-resolve-clock (clock resolve-to &optional close-p
restart-p fail-quietly)
@@ -610,30 +616,32 @@ This routine can do one of many things:
start a new clock for the same item
else just enter a closing time for this clock
and then start a new clock for the same item"
- (cond
- ((null resolve-to)
- (org-clock-clock-cancel clock)
- (if (and restart-p (not org-clock-clocking-in))
- (org-clock-clock-in clock)))
-
- ((eq resolve-to 'now)
- (if restart-p
- (error "RESTART-P is not valid here"))
- (if (or close-p org-clock-clocking-in)
- (org-clock-clock-out clock fail-quietly)
- (unless (org-is-active-clock clock)
- (org-clock-clock-in clock t))))
-
- ((not (time-less-p resolve-to (current-time)))
- (error "RESOLVE-TO must refer to a time in the past"))
-
- (t
- (if restart-p
- (error "RESTART-P is not valid here"))
- (org-clock-clock-out clock fail-quietly resolve-to)
- (unless org-clock-clocking-in
- (if (not close-p)
- (org-clock-clock-in clock))))))
+ (let ((org-clock-resolving-clocks t))
+ (cond
+ ((null resolve-to)
+ (org-clock-clock-cancel clock)
+ (if (and restart-p (not org-clock-clocking-in))
+ (org-clock-clock-in clock)))
+
+ ((eq resolve-to 'now)
+ (if restart-p
+ (error "RESTART-P is not valid here"))
+ (if (or close-p org-clock-clocking-in)
+ (org-clock-clock-out clock fail-quietly)
+ (unless (org-is-active-clock clock)
+ (org-clock-clock-in clock t))))
+
+ ((not (time-less-p resolve-to (current-time)))
+ (error "RESOLVE-TO must refer to a time in the past"))
+
+ (t
+ (if restart-p
+ (error "RESTART-P is not valid here"))
+ (org-clock-clock-out clock fail-quietly resolve-to)
+ (unless org-clock-clocking-in
+ (if close-p
+ (setq org-clock-left-over-time last-valid)
+ (org-clock-clock-in clock)))))))
(defun org-clock-resolve (clock &optional prompt-fn last-valid fail-quietly)
"Resolve an open org-mode clock.
@@ -658,22 +666,23 @@ was started."
(let* ((ch
(save-window-excursion
(save-excursion
- (org-with-clock clock
- (org-clock-goto))
- (with-current-buffer (marker-buffer (car clock))
- (goto-char (car clock))
- (if org-clock-into-drawer
- (ignore-errors
- (outline-flag-region (save-excursion
- (outline-back-to-heading t)
- (search-forward ":LOGBOOK:")
- (goto-char (match-beginning 0)))
- (save-excursion
- (outline-back-to-heading t)
- (search-forward ":LOGBOOK:")
- (search-forward ":END:")
- (goto-char (match-end 0)))
- nil))))
+ (unless org-clock-resolving-clocks-due-to-idleness
+ (org-with-clock clock
+ (org-clock-goto))
+ (with-current-buffer (marker-buffer (car clock))
+ (goto-char (car clock))
+ (if org-clock-into-drawer
+ (ignore-errors
+ (outline-flag-region (save-excursion
+ (outline-back-to-heading t)
+ (search-forward ":LOGBOOK:")
+ (goto-char (match-beginning 0)))
+ (save-excursion
+ (outline-back-to-heading t)
+ (search-forward ":LOGBOOK:")
+ (search-forward ":END:")
+ (goto-char (match-end 0)))
+ nil)))))
(let (char-pressed)
(while (null char-pressed)
(setq char-pressed
@@ -711,8 +720,6 @@ was started."
(not (memq ch '(?K ?S ?C))))
fail-quietly))))
-(defvar org-clock-resolving-clocks nil)
-
(defun org-resolve-clocks (&optional also-non-dangling-p prompt-fn last-valid)
"Resolve all currently open org-mode clocks.
If `also-non-dangling-p' is non-nil, also ask to resolve
@@ -763,6 +770,26 @@ This routine returns a floating point number."
emacs-idle))
(org-emacs-idle-seconds)))
+(defun org-resolve-clocks-if-idle ()
+ "Resolve all currently open org-mode clocks.
+This is performed after `org-clock-idle-time' minutes, to check
+if the user really wants to stay clocked in after being idle for
+so long."
+ (when (and org-clock-idle-time (not org-clock-resolving-clocks)
+ org-clock-marker)
+ (let ((idle (org-user-idle-seconds))
+ (org-clock-resolving-clocks-due-to-idleness t))
+ (if (> idle (* 60 org-clock-idle-time))
+ (org-clock-resolve
+ (cons org-clock-marker
+ org-clock-start-time)
+ (function
+ (lambda (clock)
+ (format "Clocked in & idle for %d mins"
+ (/ (org-user-idle-seconds) 60))))
+ (time-subtract (current-time)
+ (seconds-to-time (org-user-idle-seconds))))))))
+
(defun org-clock-in (&optional select)
"Start the clock on the current item.
If necessary, clock-out of the currently active clock.
@@ -773,9 +800,14 @@ the clocking selection, associated with the letter `d'."
(interactive "P")
(setq org-clock-notification-was-shown nil)
(catch 'abort
- (let ((interrupting (marker-buffer org-clock-marker))
- ts selected-task target-pos (msg-extra ""))
- (unless org-clock-clocking-in
+ (let ((interrupting (and (not org-clock-resolving-clocks-due-to-idleness)
+ (marker-buffer org-clock-marker)))
+ ts selected-task target-pos (msg-extra "")
+ (left-over (and (not org-clock-resolving-clocks)
+ org-clock-left-over-time)))
+ (unless (or org-clock-clocking-in
+ org-clock-resolving-clocks)
+ (setq org-clock-left-over-time nil)
(let ((org-clock-clocking-in t))
(org-resolve-clocks))) ; check if any clocks are dangling
(when (equal select '(4))
@@ -875,7 +907,15 @@ the clocking selection, associated with the letter `d'."
(setq org-clock-effort (org-get-effort))
(setq org-clock-total-time (org-clock-sum-current-item
(org-clock-get-sum-start)))
- (setq org-clock-start-time (current-time))
+ (setq org-clock-start-time
+ (or (and left-over
+ (y-or-n-p
+ (format
+ "You stopped another clock %d mins ago; start this one from then? "
+ (/ (- (time-to-seconds (current-time))
+ (time-to-seconds left-over)) 60)))
+ left-over)
+ (current-time)))
(setq ts (org-insert-time-stamp org-clock-start-time
'with-hm 'inactive))))
(move-marker org-clock-marker (point) (buffer-base-buffer))
@@ -892,6 +932,11 @@ the clocking selection, associated with the letter `d'."
(setq org-clock-mode-line-timer nil))
(setq org-clock-mode-line-timer
(run-with-timer 60 60 'org-clock-update-mode-line))
+ (when org-clock-idle-timer
+ (cancel-timer org-clock-idle-timer)
+ (setq org-clock-idle-timer nil))
+ (setq org-clock-idle-timer
+ (run-with-timer 60 60 'org-resolve-clocks-if-idle))
(message "Clock starts at %s - %s" ts msg-extra)
(run-hooks 'org-clock-in-hook)))))))
@@ -1066,6 +1111,9 @@ If there is no running clock, throw an error, unless FAIL-QUIETLY is set."
(when org-clock-mode-line-timer
(cancel-timer org-clock-mode-line-timer)
(setq org-clock-mode-line-timer nil))
+ (when org-clock-idle-timer
+ (cancel-timer org-clock-idle-timer)
+ (setq org-clock-idle-timer nil))
(setq global-mode-string
(delq 'org-mode-line-string global-mode-string))
(when org-clock-out-switch-to-state