1
0
mirror of https://git.savannah.gnu.org/git/emacs/org-mode.git synced 2024-11-23 07:18:53 +00:00

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.
This commit is contained in:
John Wiegley 2009-10-17 04:56:45 -04:00
parent 57a7a4c15b
commit abfc6babca
4 changed files with 184 additions and 45 deletions

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)))
(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))))
((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"))
((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))))))
(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