commit | author | age
|
76bbd0
|
1 |
;;; org-clock.el --- The time clocking code for Org mode -*- lexical-binding: t; -*- |
C |
2 |
|
|
3 |
;; Copyright (C) 2004-2018 Free Software Foundation, Inc. |
|
4 |
|
|
5 |
;; Author: Carsten Dominik <carsten at orgmode dot org> |
|
6 |
;; Keywords: outlines, hypermedia, calendar, wp |
|
7 |
;; Homepage: https://orgmode.org |
|
8 |
;; |
|
9 |
;; This file is part of GNU Emacs. |
|
10 |
;; |
|
11 |
;; GNU Emacs is free software: you can redistribute it and/or modify |
|
12 |
;; it under the terms of the GNU General Public License as published by |
|
13 |
;; the Free Software Foundation, either version 3 of the License, or |
|
14 |
;; (at your option) any later version. |
|
15 |
|
|
16 |
;; GNU Emacs is distributed in the hope that it will be useful, |
|
17 |
;; but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
18 |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
19 |
;; GNU General Public License for more details. |
|
20 |
|
|
21 |
;; You should have received a copy of the GNU General Public License |
|
22 |
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. |
|
23 |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
|
24 |
;; |
|
25 |
;;; Commentary: |
|
26 |
|
|
27 |
;; This file contains the time clocking code for Org mode |
|
28 |
|
|
29 |
;;; Code: |
|
30 |
|
|
31 |
(require 'cl-lib) |
|
32 |
(require 'org) |
|
33 |
|
|
34 |
(declare-function calendar-iso-to-absolute "cal-iso" (date)) |
|
35 |
(declare-function notifications-notify "notifications" (&rest params)) |
|
36 |
(declare-function org-element-property "org-element" (property element)) |
|
37 |
(declare-function org-element-type "org-element" (element)) |
|
38 |
(declare-function org-table-goto-line "org-table" (n)) |
|
39 |
|
|
40 |
(defvar org-frame-title-format-backup frame-title-format) |
|
41 |
(defvar org-time-stamp-formats) |
|
42 |
|
|
43 |
|
|
44 |
(defgroup org-clock nil |
|
45 |
"Options concerning clocking working time in Org mode." |
|
46 |
:tag "Org Clock" |
|
47 |
:group 'org-progress) |
|
48 |
|
|
49 |
(defcustom org-clock-into-drawer t |
|
50 |
"Non-nil when clocking info should be wrapped into a drawer. |
|
51 |
|
|
52 |
When non-nil, clocking info will be inserted into the same drawer |
|
53 |
as log notes (see variable `org-log-into-drawer'), if it exists, |
|
54 |
or \"LOGBOOK\" otherwise. If necessary, the drawer will be |
|
55 |
created. |
|
56 |
|
|
57 |
When an integer, the drawer is created only when the number of |
|
58 |
clocking entries in an item reaches or exceeds this value. |
|
59 |
|
|
60 |
When a string, it becomes the name of the drawer, ignoring the |
|
61 |
log notes drawer altogether. |
|
62 |
|
|
63 |
Do not check directly this variable in a Lisp program. Call |
|
64 |
function `org-clock-into-drawer' instead." |
|
65 |
:group 'org-todo |
|
66 |
:group 'org-clock |
|
67 |
:version "26.1" |
|
68 |
:package-version '(Org . "8.3") |
|
69 |
:type '(choice |
|
70 |
(const :tag "Always" t) |
|
71 |
(const :tag "Only when drawer exists" nil) |
|
72 |
(integer :tag "When at least N clock entries") |
|
73 |
(const :tag "Into LOGBOOK drawer" "LOGBOOK") |
|
74 |
(string :tag "Into Drawer named..."))) |
|
75 |
|
|
76 |
(defun org-clock-into-drawer () |
|
77 |
"Value of `org-clock-into-drawer'. but let properties overrule. |
|
78 |
|
|
79 |
If the current entry has or inherits a CLOCK_INTO_DRAWER |
|
80 |
property, it will be used instead of the default value. |
|
81 |
|
|
82 |
Return value is either a string, an integer, or nil." |
|
83 |
(let ((p (org-entry-get nil "CLOCK_INTO_DRAWER" 'inherit t))) |
|
84 |
(cond ((equal p "nil") nil) |
|
85 |
((equal p "t") (or (org-log-into-drawer) "LOGBOOK")) |
|
86 |
((org-string-nw-p p) |
|
87 |
(if (string-match-p "\\`[0-9]+\\'" p) (string-to-number p) p)) |
|
88 |
((org-string-nw-p org-clock-into-drawer)) |
|
89 |
((integerp org-clock-into-drawer) org-clock-into-drawer) |
|
90 |
((not org-clock-into-drawer) nil) |
|
91 |
((org-log-into-drawer)) |
|
92 |
(t "LOGBOOK")))) |
|
93 |
|
|
94 |
(defcustom org-clock-out-when-done t |
|
95 |
"When non-nil, clock will be stopped when the clocked entry is marked DONE. |
|
96 |
\\<org-mode-map>\ |
|
97 |
DONE here means any DONE-like state. |
|
98 |
A nil value means clock will keep running until stopped explicitly with |
|
99 |
`\\[org-clock-out]', or until the clock is started in a different item. |
|
100 |
Instead of t, this can also be a list of TODO states that should trigger |
|
101 |
clocking out." |
|
102 |
:group 'org-clock |
|
103 |
:type '(choice |
|
104 |
(const :tag "No" nil) |
|
105 |
(const :tag "Yes, when done" t) |
|
106 |
(repeat :tag "State list" |
|
107 |
(string :tag "TODO keyword")))) |
|
108 |
|
|
109 |
(defcustom org-clock-rounding-minutes 0 |
|
110 |
"Rounding minutes when clocking in or out. |
|
111 |
The default value is 0 so that no rounding is done. |
|
112 |
When set to a non-integer value, use the car of |
|
113 |
`org-time-stamp-rounding-minutes', like for setting a time-stamp. |
|
114 |
|
|
115 |
E.g. if `org-clock-rounding-minutes' is set to 5, time is 14:47 |
|
116 |
and you clock in: then the clock starts at 14:45. If you clock |
|
117 |
out within the next 5 minutes, the clock line will be removed; |
|
118 |
if you clock out 8 minutes after your clocked in, the clock |
|
119 |
out time will be 14:50." |
|
120 |
:group 'org-clock |
|
121 |
:version "24.4" |
|
122 |
:package-version '(Org . "8.0") |
|
123 |
:type '(choice |
|
124 |
(integer :tag "Minutes (0 for no rounding)") |
|
125 |
(symbol :tag "Use `org-time-stamp-rounding-minutes'" 'same-as-time-stamp))) |
|
126 |
|
|
127 |
(defcustom org-clock-out-remove-zero-time-clocks nil |
|
128 |
"Non-nil means remove the clock line when the resulting time is zero." |
|
129 |
:group 'org-clock |
|
130 |
:type 'boolean) |
|
131 |
|
|
132 |
(defcustom org-clock-in-switch-to-state nil |
|
133 |
"Set task to a special todo state while clocking it. |
|
134 |
The value should be the state to which the entry should be |
|
135 |
switched. If the value is a function, it must take one |
|
136 |
parameter (the current TODO state of the item) and return the |
|
137 |
state to switch it to." |
|
138 |
:group 'org-clock |
|
139 |
:group 'org-todo |
|
140 |
:type '(choice |
|
141 |
(const :tag "Don't force a state" nil) |
|
142 |
(string :tag "State") |
|
143 |
(symbol :tag "Function"))) |
|
144 |
|
|
145 |
(defcustom org-clock-out-switch-to-state nil |
|
146 |
"Set task to a special todo state after clocking out. |
|
147 |
The value should be the state to which the entry should be |
|
148 |
switched. If the value is a function, it must take one |
|
149 |
parameter (the current TODO state of the item) and return the |
|
150 |
state to switch it to." |
|
151 |
:group 'org-clock |
|
152 |
:group 'org-todo |
|
153 |
:type '(choice |
|
154 |
(const :tag "Don't force a state" nil) |
|
155 |
(string :tag "State") |
|
156 |
(symbol :tag "Function"))) |
|
157 |
|
|
158 |
(defcustom org-clock-history-length 5 |
|
159 |
"Number of clock tasks to remember in history." |
|
160 |
:group 'org-clock |
|
161 |
:type 'integer) |
|
162 |
|
|
163 |
(defcustom org-clock-goto-may-find-recent-task t |
|
164 |
"Non-nil means `org-clock-goto' can go to recent task if no active clock." |
|
165 |
:group 'org-clock |
|
166 |
:type 'boolean) |
|
167 |
|
|
168 |
(defcustom org-clock-heading-function nil |
|
169 |
"When non-nil, should be a function to create `org-clock-heading'. |
|
170 |
This is the string shown in the mode line when a clock is running. |
|
171 |
The function is called with point at the beginning of the headline." |
|
172 |
:group 'org-clock |
|
173 |
:type '(choice (const nil) (function))) |
|
174 |
|
|
175 |
(defcustom org-clock-string-limit 0 |
|
176 |
"Maximum length of clock strings in the mode line. 0 means no limit." |
|
177 |
:group 'org-clock |
|
178 |
:type 'integer) |
|
179 |
|
|
180 |
(defcustom org-clock-in-resume nil |
|
181 |
"If non-nil, resume clock when clocking into task with open clock. |
|
182 |
When clocking into a task with a clock entry which has not been closed, |
|
183 |
the clock can be resumed from that point." |
|
184 |
:group 'org-clock |
|
185 |
:type 'boolean) |
|
186 |
|
|
187 |
(defcustom org-clock-persist nil |
|
188 |
"When non-nil, save the running clock when Emacs is closed. |
|
189 |
The clock is resumed when Emacs restarts. |
|
190 |
When this is t, both the running clock, and the entire clock |
|
191 |
history are saved. When this is the symbol `clock', only the |
|
192 |
running clock is saved. When this is the symbol `history', only |
|
193 |
the clock history is saved. |
|
194 |
|
|
195 |
When Emacs restarts with saved clock information, the file containing |
|
196 |
the running clock as well as all files mentioned in the clock history |
|
197 |
will be visited. |
|
198 |
|
|
199 |
All this depends on running `org-clock-persistence-insinuate' in your |
|
200 |
Emacs initialization file." |
|
201 |
:group 'org-clock |
|
202 |
:type '(choice |
|
203 |
(const :tag "Just the running clock" clock) |
|
204 |
(const :tag "Just the history" history) |
|
205 |
(const :tag "Clock and history" t) |
|
206 |
(const :tag "No persistence" nil))) |
|
207 |
|
|
208 |
(defcustom org-clock-persist-file (convert-standard-filename |
|
209 |
(concat user-emacs-directory "org-clock-save.el")) |
|
210 |
"File to save clock data to." |
|
211 |
:group 'org-clock |
|
212 |
:type 'string) |
|
213 |
|
|
214 |
(defcustom org-clock-persist-query-save nil |
|
215 |
"When non-nil, ask before saving the current clock on exit." |
|
216 |
:group 'org-clock |
|
217 |
:type 'boolean) |
|
218 |
|
|
219 |
(defcustom org-clock-persist-query-resume t |
|
220 |
"When non-nil, ask before resuming any stored clock during load." |
|
221 |
:group 'org-clock |
|
222 |
:type 'boolean) |
|
223 |
|
|
224 |
(defcustom org-clock-sound nil |
|
225 |
"Sound to use for notifications. |
|
226 |
Possible values are: |
|
227 |
|
|
228 |
nil No sound played |
|
229 |
t Standard Emacs beep |
|
230 |
file name Play this sound file, fall back to beep" |
|
231 |
:group 'org-clock |
|
232 |
:type '(choice |
|
233 |
(const :tag "No sound" nil) |
|
234 |
(const :tag "Standard beep" t) |
|
235 |
(file :tag "Play sound file"))) |
|
236 |
|
|
237 |
(defcustom org-clock-mode-line-total 'auto |
|
238 |
"Default setting for the time included for the mode line clock. |
|
239 |
This can be overruled locally using the CLOCK_MODELINE_TOTAL property. |
|
240 |
Allowed values are: |
|
241 |
|
|
242 |
current Only the time in the current instance of the clock |
|
243 |
today All time clocked into this task today |
|
244 |
repeat All time clocked into this task since last repeat |
|
245 |
all All time ever recorded for this task |
|
246 |
auto Automatically, either `all', or `repeat' for repeating tasks" |
|
247 |
:group 'org-clock |
|
248 |
:type '(choice |
|
249 |
(const :tag "Current clock" current) |
|
250 |
(const :tag "Today's task time" today) |
|
251 |
(const :tag "Since last repeat" repeat) |
|
252 |
(const :tag "All task time" all) |
|
253 |
(const :tag "Automatically, `all' or since `repeat'" auto))) |
|
254 |
|
|
255 |
(defvaralias 'org-task-overrun-text 'org-clock-task-overrun-text) |
|
256 |
(defcustom org-clock-task-overrun-text nil |
|
257 |
"Extra mode line text to indicate that the clock is overrun. |
|
258 |
The can be nil to indicate that instead of adding text, the clock time |
|
259 |
should get a different face (`org-mode-line-clock-overrun'). |
|
260 |
When this is a string, it is prepended to the clock string as an indication, |
|
261 |
also using the face `org-mode-line-clock-overrun'." |
|
262 |
:group 'org-clock |
|
263 |
:version "24.1" |
|
264 |
:type '(choice |
|
265 |
(const :tag "Just mark the time string" nil) |
|
266 |
(string :tag "Text to prepend"))) |
|
267 |
|
|
268 |
(defcustom org-show-notification-handler nil |
|
269 |
"Function or program to send notification with. |
|
270 |
The function or program will be called with the notification |
|
271 |
string as argument." |
|
272 |
:group 'org-clock |
|
273 |
:type '(choice |
|
274 |
(const nil) |
|
275 |
(string :tag "Program") |
|
276 |
(function :tag "Function"))) |
|
277 |
|
|
278 |
(defgroup org-clocktable nil |
|
279 |
"Options concerning the clock table in Org mode." |
|
280 |
:tag "Org Clock Table" |
|
281 |
:group 'org-clock) |
|
282 |
|
|
283 |
(defcustom org-clocktable-defaults |
|
284 |
(list |
|
285 |
:maxlevel 2 |
|
286 |
:lang (or (bound-and-true-p org-export-default-language) "en") |
|
287 |
:scope 'file |
|
288 |
:block nil |
|
289 |
:wstart 1 |
|
290 |
:mstart 1 |
|
291 |
:tstart nil |
|
292 |
:tend nil |
|
293 |
:step nil |
|
294 |
:stepskip0 nil |
|
295 |
:fileskip0 nil |
|
296 |
:tags nil |
|
297 |
:emphasize nil |
|
298 |
:link nil |
|
299 |
:narrow '40! |
|
300 |
:indent t |
|
301 |
:formula nil |
|
302 |
:timestamp nil |
|
303 |
:level nil |
|
304 |
:tcolumns nil |
|
305 |
:formatter nil) |
|
306 |
"Default properties for clock tables." |
|
307 |
:group 'org-clock |
|
308 |
:version "24.1" |
|
309 |
:type 'plist) |
|
310 |
|
|
311 |
(defcustom org-clock-clocktable-formatter 'org-clocktable-write-default |
|
312 |
"Function to turn clocking data into a table. |
|
313 |
For more information, see `org-clocktable-write-default'." |
|
314 |
:group 'org-clocktable |
|
315 |
:version "24.1" |
|
316 |
:type 'function) |
|
317 |
|
|
318 |
;; FIXME: translate es and nl last string "Clock summary at" |
|
319 |
(defcustom org-clock-clocktable-language-setup |
|
320 |
'(("en" "File" "L" "Timestamp" "Headline" "Time" "ALL" "Total time" "File time" "Clock summary at") |
|
321 |
("es" "Archivo" "N" "Fecha y hora" "Tarea" "Tiempo" "TODO" "Tiempo total" "Tiempo archivo" "Clock summary at") |
|
322 |
("fr" "Fichier" "N" "Horodatage" "En-tête" "Durée" "TOUT" "Durée totale" "Durée fichier" "Horodatage sommaire à ") |
|
323 |
("nl" "Bestand" "N" "Tijdstip" "Hoofding" "Duur" "ALLES" "Totale duur" "Bestandstijd" "Clock summary at") |
|
324 |
("de" "Datei" "E" "Zeitstempel" "Kopfzeile" "Dauer" "GESAMT" |
|
325 |
"Gesamtdauer" "Dateizeit" "Erstellt am")) |
|
326 |
"Terms used in clocktable, translated to different languages." |
|
327 |
:group 'org-clocktable |
|
328 |
:version "24.1" |
|
329 |
:type 'alist) |
|
330 |
|
|
331 |
(defcustom org-clock-clocktable-default-properties '(:maxlevel 2 :scope file) |
|
332 |
"Default properties for new clocktables. |
|
333 |
These will be inserted into the BEGIN line, to make it easy for users to |
|
334 |
play with them." |
|
335 |
:group 'org-clocktable |
|
336 |
:type 'plist) |
|
337 |
|
|
338 |
(defcustom org-clock-idle-time nil |
|
339 |
"When non-nil, resolve open clocks if the user is idle more than X minutes." |
|
340 |
:group 'org-clock |
|
341 |
:type '(choice |
|
342 |
(const :tag "Never" nil) |
|
343 |
(integer :tag "After N minutes"))) |
|
344 |
|
|
345 |
(defcustom org-clock-auto-clock-resolution 'when-no-clock-is-running |
|
346 |
"When to automatically resolve open clocks found in Org buffers." |
|
347 |
:group 'org-clock |
|
348 |
:type '(choice |
|
349 |
(const :tag "Never" nil) |
|
350 |
(const :tag "Always" t) |
|
351 |
(const :tag "When no clock is running" when-no-clock-is-running))) |
|
352 |
|
|
353 |
(defcustom org-clock-report-include-clocking-task nil |
|
354 |
"When non-nil, include the current clocking task time in clock reports." |
|
355 |
:group 'org-clock |
|
356 |
:version "24.1" |
|
357 |
:type 'boolean) |
|
358 |
|
|
359 |
(defcustom org-clock-resolve-expert nil |
|
360 |
"Non-nil means do not show the splash buffer with the clock resolver." |
|
361 |
:group 'org-clock |
|
362 |
:version "24.1" |
|
363 |
:type 'boolean) |
|
364 |
|
|
365 |
(defcustom org-clock-continuously nil |
|
366 |
"Non-nil means to start clocking from the last clock-out time, if any." |
|
367 |
:type 'boolean |
|
368 |
:version "24.1" |
|
369 |
:group 'org-clock) |
|
370 |
|
|
371 |
(defcustom org-clock-total-time-cell-format "*%s*" |
|
372 |
"Format string for the total time cells." |
|
373 |
:group 'org-clock |
|
374 |
:version "24.1" |
|
375 |
:type 'string) |
|
376 |
|
|
377 |
(defcustom org-clock-file-time-cell-format "*%s*" |
|
378 |
"Format string for the file time cells." |
|
379 |
:group 'org-clock |
|
380 |
:version "24.1" |
|
381 |
:type 'string) |
|
382 |
|
|
383 |
(defcustom org-clock-clocked-in-display 'mode-line |
|
384 |
"When clocked in for a task, Org can display the current |
|
385 |
task and accumulated time in the mode line and/or frame title. |
|
386 |
Allowed values are: |
|
387 |
|
|
388 |
both displays in both mode line and frame title |
|
389 |
mode-line displays only in mode line (default) |
|
390 |
frame-title displays only in frame title |
|
391 |
nil current clock is not displayed" |
|
392 |
:group 'org-clock |
|
393 |
:type '(choice |
|
394 |
(const :tag "Mode line" mode-line) |
|
395 |
(const :tag "Frame title" frame-title) |
|
396 |
(const :tag "Both" both) |
|
397 |
(const :tag "None" nil))) |
|
398 |
|
|
399 |
(defcustom org-clock-frame-title-format '(t org-mode-line-string) |
|
400 |
"The value for `frame-title-format' when clocking in. |
|
401 |
|
|
402 |
When `org-clock-clocked-in-display' is set to `frame-title' |
|
403 |
or `both', clocking in will replace `frame-title-format' with |
|
404 |
this value. Clocking out will restore `frame-title-format'. |
|
405 |
|
|
406 |
`org-frame-title-string' is a format string using the same |
|
407 |
specifications than `frame-title-format', which see." |
|
408 |
:version "24.1" |
|
409 |
:group 'org-clock |
|
410 |
:type 'sexp) |
|
411 |
|
|
412 |
(defcustom org-clock-x11idle-program-name "x11idle" |
|
413 |
"Name of the program which prints X11 idle time in milliseconds. |
|
414 |
|
|
415 |
You can find x11idle.c in the contrib/scripts directory of the |
|
416 |
Org git distribution. Or, you can do: |
|
417 |
|
|
418 |
sudo apt-get install xprintidle |
|
419 |
|
|
420 |
if you are using Debian." |
|
421 |
:group 'org-clock |
|
422 |
:version "24.4" |
|
423 |
:package-version '(Org . "8.0") |
|
424 |
:type 'string) |
|
425 |
|
|
426 |
(defcustom org-clock-goto-before-context 2 |
|
427 |
"Number of lines of context to display before currently clocked-in entry. |
|
428 |
This applies when using `org-clock-goto'." |
|
429 |
:group 'org-clock |
|
430 |
:type 'integer) |
|
431 |
|
|
432 |
(defcustom org-clock-display-default-range 'thisyear |
|
433 |
"Default range when displaying clocks with `org-clock-display'. |
|
434 |
Valid values are: `today', `yesterday', `thisweek', `lastweek', |
|
435 |
`thismonth', `lastmonth', `thisyear', `lastyear' and `untilnow'." |
|
436 |
:group 'org-clock |
|
437 |
:type '(choice (const today) |
|
438 |
(const yesterday) |
|
439 |
(const thisweek) |
|
440 |
(const lastweek) |
|
441 |
(const thismonth) |
|
442 |
(const lastmonth) |
|
443 |
(const thisyear) |
|
444 |
(const lastyear) |
|
445 |
(const untilnow) |
|
446 |
(const :tag "Select range interactively" interactive)) |
|
447 |
:safe #'symbolp) |
|
448 |
|
|
449 |
(defvar org-clock-in-prepare-hook nil |
|
450 |
"Hook run when preparing the clock. |
|
451 |
This hook is run before anything happens to the task that |
|
452 |
you want to clock in. For example, you can use this hook |
|
453 |
to add an effort property.") |
|
454 |
(defvar org-clock-in-hook nil |
|
455 |
"Hook run when starting the clock.") |
|
456 |
(defvar org-clock-out-hook nil |
|
457 |
"Hook run when stopping the current clock.") |
|
458 |
|
|
459 |
(defvar org-clock-cancel-hook nil |
|
460 |
"Hook run when canceling the current clock.") |
|
461 |
(defvar org-clock-goto-hook nil |
|
462 |
"Hook run when selecting the currently clocked-in entry.") |
|
463 |
(defvar org-clock-has-been-used nil |
|
464 |
"Has the clock been used during the current Emacs session?") |
|
465 |
|
|
466 |
(defvar org-clock-stored-history nil |
|
467 |
"Clock history, populated by `org-clock-load'") |
|
468 |
(defvar org-clock-stored-resume-clock nil |
|
469 |
"Clock to resume, saved by `org-clock-load'") |
|
470 |
|
|
471 |
;;; The clock for measuring work time. |
|
472 |
|
|
473 |
(defvar org-mode-line-string "") |
|
474 |
(put 'org-mode-line-string 'risky-local-variable t) |
|
475 |
|
|
476 |
(defvar org-clock-mode-line-timer nil) |
|
477 |
(defvar org-clock-idle-timer nil) |
|
478 |
(defvar org-clock-heading) ; defined in org.el |
|
479 |
(defvar org-clock-start-time "") |
|
480 |
|
|
481 |
(defvar org-clock-leftover-time nil |
|
482 |
"If non-nil, user canceled a clock; this is when leftover time started.") |
|
483 |
|
|
484 |
(defvar org-clock-effort "" |
|
485 |
"Effort estimate of the currently clocking task.") |
|
486 |
|
|
487 |
(defvar org-clock-total-time nil |
|
488 |
"Holds total time, spent previously on currently clocked item. |
|
489 |
This does not include the time in the currently running clock.") |
|
490 |
|
|
491 |
(defvar org-clock-history nil |
|
492 |
"List of marker pointing to recent clocked tasks.") |
|
493 |
|
|
494 |
(defvar org-clock-default-task (make-marker) |
|
495 |
"Marker pointing to the default task that should clock time. |
|
496 |
The clock can be made to switch to this task after clocking out |
|
497 |
of a different task.") |
|
498 |
|
|
499 |
(defvar org-clock-interrupted-task (make-marker) |
|
500 |
"Marker pointing to the task that has been interrupted by the current clock.") |
|
501 |
|
|
502 |
(defvar org-clock-mode-line-map (make-sparse-keymap)) |
|
503 |
(define-key org-clock-mode-line-map [mode-line mouse-2] 'org-clock-goto) |
|
504 |
(define-key org-clock-mode-line-map [mode-line mouse-1] 'org-clock-menu) |
|
505 |
|
|
506 |
(defun org-clock--translate (s language) |
|
507 |
"Translate string S into using string LANGUAGE. |
|
508 |
Assume S in the English term to translate. Return S as-is if it |
|
509 |
cannot be translated." |
|
510 |
(or (nth (pcase s |
|
511 |
("File" 1) ("L" 2) ("Timestamp" 3) ("Headline" 4) ("Time" 5) |
|
512 |
("ALL" 6) ("Total time" 7) ("File time" 8) ("Clock summary at" 9)) |
|
513 |
(assoc-string language org-clock-clocktable-language-setup t)) |
|
514 |
s)) |
|
515 |
|
|
516 |
(defun org-clock--mode-line-heading () |
|
517 |
"Return currently clocked heading, formatted for mode line." |
|
518 |
(cond ((functionp org-clock-heading-function) |
|
519 |
(funcall org-clock-heading-function)) |
|
520 |
((org-before-first-heading-p) "???") |
|
521 |
(t (replace-regexp-in-string |
|
522 |
org-bracket-link-analytic-regexp "\\5" |
|
523 |
(org-no-properties (org-get-heading t t t t)))))) |
|
524 |
|
|
525 |
(defun org-clock-menu () |
|
526 |
(interactive) |
|
527 |
(popup-menu |
|
528 |
'("Clock" |
|
529 |
["Clock out" org-clock-out t] |
|
530 |
["Change effort estimate" org-clock-modify-effort-estimate t] |
|
531 |
["Go to clock entry" org-clock-goto t] |
|
532 |
["Switch task" (lambda () (interactive) (org-clock-in '(4))) :active t :keys "C-u C-c C-x C-i"]))) |
|
533 |
|
|
534 |
(defun org-clock-history-push (&optional pos buffer) |
|
535 |
"Push a marker to the clock history." |
|
536 |
(setq org-clock-history-length (max 1 (min 35 org-clock-history-length))) |
|
537 |
(let ((m (move-marker (make-marker) |
|
538 |
(or pos (point)) (org-base-buffer |
|
539 |
(or buffer (current-buffer))))) |
|
540 |
n l) |
|
541 |
(while (setq n (member m org-clock-history)) |
|
542 |
(move-marker (car n) nil)) |
|
543 |
(setq org-clock-history |
|
544 |
(delq nil |
|
545 |
(mapcar (lambda (x) (if (marker-buffer x) x nil)) |
|
546 |
org-clock-history))) |
|
547 |
(when (>= (setq l (length org-clock-history)) org-clock-history-length) |
|
548 |
(setq org-clock-history |
|
549 |
(nreverse |
|
550 |
(nthcdr (- l org-clock-history-length -1) |
|
551 |
(nreverse org-clock-history))))) |
|
552 |
(push m org-clock-history))) |
|
553 |
|
|
554 |
(defun org-clock-save-markers-for-cut-and-paste (beg end) |
|
555 |
"Save relative positions of markers in region." |
|
556 |
(org-check-and-save-marker org-clock-marker beg end) |
|
557 |
(org-check-and-save-marker org-clock-hd-marker beg end) |
|
558 |
(org-check-and-save-marker org-clock-default-task beg end) |
|
559 |
(org-check-and-save-marker org-clock-interrupted-task beg end) |
|
560 |
(dolist (m org-clock-history) |
|
561 |
(org-check-and-save-marker m beg end))) |
|
562 |
|
|
563 |
(defun org-clock-drawer-name () |
|
564 |
"Return clock drawer's name for current entry, or nil." |
|
565 |
(let ((drawer (org-clock-into-drawer))) |
|
566 |
(cond ((integerp drawer) |
|
567 |
(let ((log-drawer (org-log-into-drawer))) |
|
568 |
(if (stringp log-drawer) log-drawer "LOGBOOK"))) |
|
569 |
((stringp drawer) drawer) |
|
570 |
(t nil)))) |
|
571 |
|
|
572 |
(defun org-clocking-buffer () |
|
573 |
"Return the clocking buffer if we are currently clocking a task or nil." |
|
574 |
(marker-buffer org-clock-marker)) |
|
575 |
|
|
576 |
(defun org-clocking-p () |
|
577 |
"Return t when clocking a task." |
|
578 |
(not (equal (org-clocking-buffer) nil))) |
|
579 |
|
|
580 |
(defvar org-clock-before-select-task-hook nil |
|
581 |
"Hook called in task selection just before prompting the user.") |
|
582 |
|
|
583 |
(defun org-clock-select-task (&optional prompt) |
|
584 |
"Select a task that was recently associated with clocking. |
|
585 |
Return marker position of the selected task. Raise an error if |
|
586 |
there is no recent clock to choose from." |
|
587 |
(let (och chl sel-list rpl (i 0) s) |
|
588 |
;; Remove successive dups from the clock history to consider |
|
589 |
(dolist (c org-clock-history) |
|
590 |
(unless (equal c (car och)) (push c och))) |
|
591 |
(setq och (reverse och) chl (length och)) |
|
592 |
(if (zerop chl) |
|
593 |
(user-error "No recent clock") |
|
594 |
(save-window-excursion |
|
595 |
(org-switch-to-buffer-other-window |
|
596 |
(get-buffer-create "*Clock Task Select*")) |
|
597 |
(erase-buffer) |
|
598 |
(when (marker-buffer org-clock-default-task) |
|
599 |
(insert (org-add-props "Default Task\n" nil 'face 'bold)) |
|
600 |
(setq s (org-clock-insert-selection-line ?d org-clock-default-task)) |
|
601 |
(push s sel-list)) |
|
602 |
(when (marker-buffer org-clock-interrupted-task) |
|
603 |
(insert (org-add-props "The task interrupted by starting the last one\n" nil 'face 'bold)) |
|
604 |
(setq s (org-clock-insert-selection-line ?i org-clock-interrupted-task)) |
|
605 |
(push s sel-list)) |
|
606 |
(when (org-clocking-p) |
|
607 |
(insert (org-add-props "Current Clocking Task\n" nil 'face 'bold)) |
|
608 |
(setq s (org-clock-insert-selection-line ?c org-clock-marker)) |
|
609 |
(push s sel-list)) |
|
610 |
(insert (org-add-props "Recent Tasks\n" nil 'face 'bold)) |
|
611 |
(dolist (m och) |
|
612 |
(when (marker-buffer m) |
|
613 |
(setq i (1+ i) |
|
614 |
s (org-clock-insert-selection-line |
|
615 |
(if (< i 10) |
|
616 |
(+ i ?0) |
|
617 |
(+ i (- ?A 10))) m)) |
|
618 |
(if (fboundp 'int-to-char) (setf (car s) (int-to-char (car s)))) |
|
619 |
(push s sel-list))) |
|
620 |
(run-hooks 'org-clock-before-select-task-hook) |
|
621 |
(goto-char (point-min)) |
|
622 |
;; Set min-height relatively to circumvent a possible but in |
|
623 |
;; `fit-window-to-buffer' |
|
624 |
(fit-window-to-buffer nil nil (if (< chl 10) chl (+ 5 chl))) |
|
625 |
(message (or prompt "Select task for clocking:")) |
|
626 |
(setq cursor-type nil rpl (read-char-exclusive)) |
|
627 |
(kill-buffer) |
|
628 |
(cond |
|
629 |
((eq rpl ?q) nil) |
|
630 |
((eq rpl ?x) nil) |
|
631 |
((assoc rpl sel-list) (cdr (assoc rpl sel-list))) |
|
632 |
(t (user-error "Invalid task choice %c" rpl))))))) |
|
633 |
|
|
634 |
(defun org-clock-insert-selection-line (i marker) |
|
635 |
"Insert a line for the clock selection menu. |
|
636 |
And return a cons cell with the selection character integer and the marker |
|
637 |
pointing to it." |
|
638 |
(when (marker-buffer marker) |
|
639 |
(let (cat task heading prefix) |
|
640 |
(with-current-buffer (org-base-buffer (marker-buffer marker)) |
|
641 |
(org-with-wide-buffer |
|
642 |
(ignore-errors |
|
643 |
(goto-char marker) |
|
644 |
(setq cat (org-get-category) |
|
645 |
heading (org-get-heading 'notags) |
|
646 |
prefix (save-excursion |
|
647 |
(org-back-to-heading t) |
|
648 |
(looking-at org-outline-regexp) |
|
649 |
(match-string 0)) |
|
650 |
task (substring |
|
651 |
(org-fontify-like-in-org-mode |
|
652 |
(concat prefix heading) |
|
653 |
org-odd-levels-only) |
|
654 |
(length prefix)))))) |
|
655 |
(when (and cat task) |
|
656 |
(insert (format "[%c] %-12s %s\n" i cat task)) |
|
657 |
(cons i marker))))) |
|
658 |
|
|
659 |
(defvar org-clock-task-overrun nil |
|
660 |
"Internal flag indicating if the clock has overrun the planned time.") |
|
661 |
(defvar org-clock-update-period 60 |
|
662 |
"Number of seconds between mode line clock string updates.") |
|
663 |
|
|
664 |
(defun org-clock-get-clock-string () |
|
665 |
"Form a clock-string, that will be shown in the mode line. |
|
666 |
If an effort estimate was defined for the current item, use |
|
667 |
01:30/01:50 format (clocked/estimated). |
|
668 |
If not, show simply the clocked time like 01:50." |
|
669 |
(let ((clocked-time (org-clock-get-clocked-time))) |
|
670 |
(if org-clock-effort |
|
671 |
(let* ((effort-in-minutes (org-duration-to-minutes org-clock-effort)) |
|
672 |
(work-done-str |
|
673 |
(propertize (org-duration-from-minutes clocked-time) |
|
674 |
'face |
|
675 |
(if (and org-clock-task-overrun |
|
676 |
(not org-clock-task-overrun-text)) |
|
677 |
'org-mode-line-clock-overrun |
|
678 |
'org-mode-line-clock))) |
|
679 |
(effort-str (org-duration-from-minutes effort-in-minutes))) |
|
680 |
(format (propertize " [%s/%s] (%s)" 'face 'org-mode-line-clock) |
|
681 |
work-done-str effort-str org-clock-heading)) |
|
682 |
(format (propertize " [%s] (%s)" 'face 'org-mode-line-clock) |
|
683 |
(org-duration-from-minutes clocked-time) |
|
684 |
org-clock-heading)))) |
|
685 |
|
|
686 |
(defun org-clock-get-last-clock-out-time () |
|
687 |
"Get the last clock-out time for the current subtree." |
|
688 |
(save-excursion |
|
689 |
(let ((end (save-excursion (org-end-of-subtree)))) |
|
690 |
(when (re-search-forward (concat org-clock-string |
|
691 |
".*\\]--\\(\\[[^]]+\\]\\)") end t) |
|
692 |
(org-time-string-to-time (match-string 1)))))) |
|
693 |
|
|
694 |
(defun org-clock-update-mode-line (&optional refresh) |
|
695 |
"Update mode line with clock information. |
|
696 |
When optional argument is non-nil, refresh cached heading." |
|
697 |
(if org-clock-effort |
|
698 |
(org-clock-notify-once-if-expired) |
|
699 |
(setq org-clock-task-overrun nil)) |
|
700 |
(when refresh (setq org-clock-heading (org-clock--mode-line-heading))) |
|
701 |
(setq org-mode-line-string |
|
702 |
(propertize |
|
703 |
(let ((clock-string (org-clock-get-clock-string)) |
|
704 |
(help-text "Org mode clock is running.\nmouse-1 shows a \ |
|
705 |
menu\nmouse-2 will jump to task")) |
|
706 |
(if (and (> org-clock-string-limit 0) |
|
707 |
(> (length clock-string) org-clock-string-limit)) |
|
708 |
(propertize |
|
709 |
(substring clock-string 0 org-clock-string-limit) |
|
710 |
'help-echo (concat help-text ": " org-clock-heading)) |
|
711 |
(propertize clock-string 'help-echo help-text))) |
|
712 |
'local-map org-clock-mode-line-map |
|
713 |
'mouse-face 'mode-line-highlight)) |
|
714 |
(if (and org-clock-task-overrun org-clock-task-overrun-text) |
|
715 |
(setq org-mode-line-string |
|
716 |
(concat (propertize |
|
717 |
org-clock-task-overrun-text |
|
718 |
'face 'org-mode-line-clock-overrun) org-mode-line-string))) |
|
719 |
(force-mode-line-update)) |
|
720 |
|
|
721 |
(defun org-clock-get-clocked-time () |
|
722 |
"Get the clocked time for the current item in minutes. |
|
723 |
The time returned includes the time spent on this task in |
|
724 |
previous clocking intervals." |
|
725 |
(let ((currently-clocked-time |
|
726 |
(floor (- (float-time) |
|
727 |
(float-time org-clock-start-time)) 60))) |
|
728 |
(+ currently-clocked-time (or org-clock-total-time 0)))) |
|
729 |
|
|
730 |
(defun org-clock-modify-effort-estimate (&optional value) |
|
731 |
"Add to or set the effort estimate of the item currently being clocked. |
|
732 |
VALUE can be a number of minutes, or a string with format hh:mm or mm. |
|
733 |
When the string starts with a + or a - sign, the current value of the effort |
|
734 |
property will be changed by that amount. If the effort value is expressed |
|
735 |
as an `org-effort-durations' (e.g. \"3h\"), the modified value will be |
|
736 |
converted to a hh:mm duration. |
|
737 |
|
|
738 |
This command will update the \"Effort\" property of the currently |
|
739 |
clocked item, and the value displayed in the mode line." |
|
740 |
(interactive) |
|
741 |
(if (org-clock-is-active) |
|
742 |
(let ((current org-clock-effort) sign) |
|
743 |
(unless value |
|
744 |
;; Prompt user for a value or a change |
|
745 |
(setq value |
|
746 |
(read-string |
|
747 |
(format "Set effort (hh:mm or mm%s): " |
|
748 |
(if current |
|
749 |
(format ", prefix + to add to %s" org-clock-effort) |
|
750 |
""))))) |
|
751 |
(when (stringp value) |
|
752 |
;; A string. See if it is a delta |
|
753 |
(setq sign (string-to-char value)) |
|
754 |
(if (member sign '(?- ?+)) |
|
755 |
(setq current (org-duration-to-minutes current) |
|
756 |
value (substring value 1)) |
|
757 |
(setq current 0)) |
|
758 |
(setq value (org-duration-to-minutes value)) |
|
759 |
(if (equal ?- sign) |
|
760 |
(setq value (- current value)) |
|
761 |
(if (equal ?+ sign) (setq value (+ current value))))) |
|
762 |
(setq value (max 0 value) |
|
763 |
org-clock-effort (org-duration-from-minutes value)) |
|
764 |
(org-entry-put org-clock-marker "Effort" org-clock-effort) |
|
765 |
(org-clock-update-mode-line) |
|
766 |
(message "Effort is now %s" org-clock-effort)) |
|
767 |
(message "Clock is not currently active"))) |
|
768 |
|
|
769 |
(defvar org-clock-notification-was-shown nil |
|
770 |
"Shows if we have shown notification already.") |
|
771 |
|
|
772 |
(defun org-clock-notify-once-if-expired () |
|
773 |
"Show notification if we spent more time than we estimated before. |
|
774 |
Notification is shown only once." |
|
775 |
(when (org-clocking-p) |
|
776 |
(let ((effort-in-minutes (org-duration-to-minutes org-clock-effort)) |
|
777 |
(clocked-time (org-clock-get-clocked-time))) |
|
778 |
(if (setq org-clock-task-overrun |
|
779 |
(if (or (null effort-in-minutes) (zerop effort-in-minutes)) |
|
780 |
nil |
|
781 |
(>= clocked-time effort-in-minutes))) |
|
782 |
(unless org-clock-notification-was-shown |
|
783 |
(setq org-clock-notification-was-shown t) |
|
784 |
(org-notify |
|
785 |
(format-message "Task `%s' should be finished by now. (%s)" |
|
786 |
org-clock-heading org-clock-effort) |
|
787 |
org-clock-sound)) |
|
788 |
(setq org-clock-notification-was-shown nil))))) |
|
789 |
|
|
790 |
(defun org-notify (notification &optional play-sound) |
|
791 |
"Send a NOTIFICATION and maybe PLAY-SOUND. |
|
792 |
If PLAY-SOUND is non-nil, it overrides `org-clock-sound'." |
|
793 |
(org-show-notification notification) |
|
794 |
(if play-sound (org-clock-play-sound play-sound))) |
|
795 |
|
|
796 |
(defun org-show-notification (notification) |
|
797 |
"Show notification. |
|
798 |
Use `org-show-notification-handler' if defined, |
|
799 |
use libnotify if available, or fall back on a message." |
|
800 |
(cond ((functionp org-show-notification-handler) |
|
801 |
(funcall org-show-notification-handler notification)) |
|
802 |
((stringp org-show-notification-handler) |
|
803 |
(start-process "emacs-timer-notification" nil |
|
804 |
org-show-notification-handler notification)) |
|
805 |
((fboundp 'notifications-notify) |
|
806 |
(notifications-notify |
|
807 |
:title "Org mode message" |
|
808 |
:body notification |
|
809 |
;; FIXME how to link to the Org icon? |
|
810 |
;; :app-icon "~/.emacs.d/icons/mail.png" |
|
811 |
:urgency 'low)) |
|
812 |
((executable-find "notify-send") |
|
813 |
(start-process "emacs-timer-notification" nil |
|
814 |
"notify-send" notification)) |
|
815 |
;; Maybe the handler will send a message, so only use message as |
|
816 |
;; a fall back option |
|
817 |
(t (message "%s" notification)))) |
|
818 |
|
|
819 |
(defun org-clock-play-sound (&optional clock-sound) |
|
820 |
"Play sound as configured by `org-clock-sound'. |
|
821 |
Use alsa's aplay tool if available. |
|
822 |
If CLOCK-SOUND is non-nil, it overrides `org-clock-sound'." |
|
823 |
(let ((org-clock-sound (or clock-sound org-clock-sound))) |
|
824 |
(cond |
|
825 |
((not org-clock-sound)) |
|
826 |
((eq org-clock-sound t) (beep t) (beep t)) |
|
827 |
((stringp org-clock-sound) |
|
828 |
(let ((file (expand-file-name org-clock-sound))) |
|
829 |
(if (file-exists-p file) |
|
830 |
(if (executable-find "aplay") |
|
831 |
(start-process "org-clock-play-notification" nil |
|
832 |
"aplay" file) |
|
833 |
(condition-case nil |
|
834 |
(play-sound-file file) |
|
835 |
(error (beep t) (beep t)))))))))) |
|
836 |
|
|
837 |
(defvar org-clock-mode-line-entry nil |
|
838 |
"Information for the mode line about the running clock.") |
|
839 |
|
|
840 |
(defun org-find-open-clocks (file) |
|
841 |
"Search through the given file and find all open clocks." |
|
842 |
(let ((buf (or (get-file-buffer file) |
|
843 |
(find-file-noselect file))) |
|
844 |
(org-clock-re (concat org-clock-string " \\(\\[.*?\\]\\)$")) |
|
845 |
clocks) |
|
846 |
(with-current-buffer buf |
|
847 |
(save-excursion |
|
848 |
(goto-char (point-min)) |
|
849 |
(while (re-search-forward org-clock-re nil t) |
|
850 |
(push (cons (copy-marker (match-end 1) t) |
|
851 |
(org-time-string-to-time (match-string 1))) clocks)))) |
|
852 |
clocks)) |
|
853 |
|
|
854 |
(defsubst org-is-active-clock (clock) |
|
855 |
"Return t if CLOCK is the currently active clock." |
|
856 |
(and (org-clock-is-active) |
|
857 |
(= org-clock-marker (car clock)))) |
|
858 |
|
|
859 |
(defmacro org-with-clock-position (clock &rest forms) |
|
860 |
"Evaluate FORMS with CLOCK as the current active clock." |
|
861 |
`(with-current-buffer (marker-buffer (car ,clock)) |
|
862 |
(org-with-wide-buffer |
|
863 |
(goto-char (car ,clock)) |
|
864 |
(beginning-of-line) |
|
865 |
,@forms))) |
|
866 |
(def-edebug-spec org-with-clock-position (form body)) |
|
867 |
(put 'org-with-clock-position 'lisp-indent-function 1) |
|
868 |
|
|
869 |
(defmacro org-with-clock (clock &rest forms) |
|
870 |
"Evaluate FORMS with CLOCK as the current active clock. |
|
871 |
This macro also protects the current active clock from being altered." |
|
872 |
`(org-with-clock-position ,clock |
|
873 |
(let ((org-clock-start-time (cdr ,clock)) |
|
874 |
(org-clock-total-time) |
|
875 |
(org-clock-history) |
|
876 |
(org-clock-effort) |
|
877 |
(org-clock-marker (car ,clock)) |
|
878 |
(org-clock-hd-marker (save-excursion |
|
879 |
(org-back-to-heading t) |
|
880 |
(point-marker)))) |
|
881 |
,@forms))) |
|
882 |
(def-edebug-spec org-with-clock (form body)) |
|
883 |
(put 'org-with-clock 'lisp-indent-function 1) |
|
884 |
|
|
885 |
(defsubst org-clock-clock-in (clock &optional resume start-time) |
|
886 |
"Clock in to the clock located by CLOCK. |
|
887 |
If necessary, clock-out of the currently active clock." |
|
888 |
(org-with-clock-position clock |
|
889 |
(let ((org-clock-in-resume (or resume org-clock-in-resume))) |
|
890 |
(org-clock-in nil start-time)))) |
|
891 |
|
|
892 |
(defsubst org-clock-clock-out (clock &optional fail-quietly at-time) |
|
893 |
"Clock out of the clock located by CLOCK." |
|
894 |
(let ((temp (copy-marker (car clock) |
|
895 |
(marker-insertion-type (car clock))))) |
|
896 |
(if (org-is-active-clock clock) |
|
897 |
(org-clock-out nil fail-quietly at-time) |
|
898 |
(org-with-clock clock |
|
899 |
(org-clock-out nil fail-quietly at-time))) |
|
900 |
(setcar clock temp))) |
|
901 |
|
|
902 |
(defsubst org-clock-clock-cancel (clock) |
|
903 |
"Cancel the clock located by CLOCK." |
|
904 |
(let ((temp (copy-marker (car clock) |
|
905 |
(marker-insertion-type (car clock))))) |
|
906 |
(if (org-is-active-clock clock) |
|
907 |
(org-clock-cancel) |
|
908 |
(org-with-clock clock |
|
909 |
(org-clock-cancel))) |
|
910 |
(setcar clock temp))) |
|
911 |
|
|
912 |
(defvar org-clock-clocking-in nil) |
|
913 |
(defvar org-clock-resolving-clocks nil) |
|
914 |
(defvar org-clock-resolving-clocks-due-to-idleness nil) |
|
915 |
|
|
916 |
(defun org-clock-resolve-clock (clock resolve-to clock-out-time |
|
917 |
&optional close-p restart-p fail-quietly) |
|
918 |
"Resolve `CLOCK' given the time `RESOLVE-TO', and the present. |
|
919 |
`CLOCK' is a cons cell of the form (MARKER START-TIME)." |
|
920 |
(let ((org-clock-resolving-clocks t)) |
|
921 |
(cond |
|
922 |
((null resolve-to) |
|
923 |
(org-clock-clock-cancel clock) |
|
924 |
(if (and restart-p (not org-clock-clocking-in)) |
|
925 |
(org-clock-clock-in clock))) |
|
926 |
|
|
927 |
((eq resolve-to 'now) |
|
928 |
(if restart-p |
|
929 |
(error "RESTART-P is not valid here")) |
|
930 |
(if (or close-p org-clock-clocking-in) |
|
931 |
(org-clock-clock-out clock fail-quietly) |
|
932 |
(unless (org-is-active-clock clock) |
|
933 |
(org-clock-clock-in clock t)))) |
|
934 |
|
|
935 |
((not (time-less-p resolve-to (current-time))) |
|
936 |
(error "RESOLVE-TO must refer to a time in the past")) |
|
937 |
|
|
938 |
(t |
|
939 |
(if restart-p |
|
940 |
(error "RESTART-P is not valid here")) |
|
941 |
(org-clock-clock-out clock fail-quietly (or clock-out-time |
|
942 |
resolve-to)) |
|
943 |
(unless org-clock-clocking-in |
|
944 |
(if close-p |
|
945 |
(setq org-clock-leftover-time (and (null clock-out-time) |
|
946 |
resolve-to)) |
|
947 |
(org-clock-clock-in clock nil (and clock-out-time |
|
948 |
resolve-to)))))))) |
|
949 |
|
|
950 |
(defun org-clock-jump-to-current-clock (&optional effective-clock) |
|
951 |
(interactive) |
|
952 |
(let ((drawer (org-clock-into-drawer)) |
|
953 |
(clock (or effective-clock (cons org-clock-marker |
|
954 |
org-clock-start-time)))) |
|
955 |
(unless (marker-buffer (car clock)) |
|
956 |
(error "No clock is currently running")) |
|
957 |
(org-with-clock clock (org-clock-goto)) |
|
958 |
(with-current-buffer (marker-buffer (car clock)) |
|
959 |
(goto-char (car clock)) |
|
960 |
(when drawer |
|
961 |
(org-with-wide-buffer |
|
962 |
(let ((drawer-re (format "^[ \t]*:%s:[ \t]*$" |
|
963 |
(regexp-quote (if (stringp drawer) drawer "LOGBOOK")))) |
|
964 |
(beg (save-excursion (org-back-to-heading t) (point)))) |
|
965 |
(catch 'exit |
|
966 |
(while (re-search-backward drawer-re beg t) |
|
967 |
(let ((element (org-element-at-point))) |
|
968 |
(when (eq (org-element-type element) 'drawer) |
|
969 |
(when (> (org-element-property :end element) (car clock)) |
|
970 |
(org-flag-drawer nil element)) |
|
971 |
(throw 'exit nil))))))))))) |
|
972 |
|
|
973 |
(defun org-clock-resolve (clock &optional prompt-fn last-valid fail-quietly) |
|
974 |
"Resolve an open Org clock. |
|
975 |
An open clock was found, with `dangling' possibly being non-nil. |
|
976 |
If this function was invoked with a prefix argument, non-dangling |
|
977 |
open clocks are ignored. The given clock requires some sort of |
|
978 |
user intervention to resolve it, either because a clock was left |
|
979 |
dangling or due to an idle timeout. The clock resolution can |
|
980 |
either be: |
|
981 |
|
|
982 |
(a) deleted, the user doesn't care about the clock |
|
983 |
(b) restarted from the current time (if no other clock is open) |
|
984 |
(c) closed, giving the clock X minutes |
|
985 |
(d) closed and then restarted |
|
986 |
(e) resumed, as if the user had never left |
|
987 |
|
|
988 |
The format of clock is (CONS MARKER START-TIME), where MARKER |
|
989 |
identifies the buffer and position the clock is open at (and |
|
990 |
thus, the heading it's under), and START-TIME is when the clock |
|
991 |
was started." |
|
992 |
(cl-assert clock) |
|
993 |
(let* ((ch |
|
994 |
(save-window-excursion |
|
995 |
(save-excursion |
|
996 |
(unless org-clock-resolving-clocks-due-to-idleness |
|
997 |
(org-clock-jump-to-current-clock clock)) |
|
998 |
(unless org-clock-resolve-expert |
|
999 |
(with-output-to-temp-buffer "*Org Clock*" |
|
1000 |
(princ (format-message "Select a Clock Resolution Command: |
|
1001 |
|
|
1002 |
i/q Ignore this question; the same as keeping all the idle time. |
|
1003 |
|
|
1004 |
k/K Keep X minutes of the idle time (default is all). If this |
|
1005 |
amount is less than the default, you will be clocked out |
|
1006 |
that many minutes after the time that idling began, and then |
|
1007 |
clocked back in at the present time. |
|
1008 |
|
|
1009 |
g/G Indicate that you \"got back\" X minutes ago. This is quite |
|
1010 |
different from `k': it clocks you out from the beginning of |
|
1011 |
the idle period and clock you back in X minutes ago. |
|
1012 |
|
|
1013 |
s/S Subtract the idle time from the current clock. This is the |
|
1014 |
same as keeping 0 minutes. |
|
1015 |
|
|
1016 |
C Cancel the open timer altogether. It will be as though you |
|
1017 |
never clocked in. |
|
1018 |
|
|
1019 |
j/J Jump to the current clock, to make manual adjustments. |
|
1020 |
|
|
1021 |
For all these options, using uppercase makes your final state |
|
1022 |
to be CLOCKED OUT.")))) |
|
1023 |
(org-fit-window-to-buffer (get-buffer-window "*Org Clock*")) |
|
1024 |
(let (char-pressed) |
|
1025 |
(while (or (null char-pressed) |
|
1026 |
(and (not (memq char-pressed |
|
1027 |
'(?k ?K ?g ?G ?s ?S ?C |
|
1028 |
?j ?J ?i ?q))) |
|
1029 |
(or (ding) t))) |
|
1030 |
(setq char-pressed |
|
1031 |
(read-char (concat (funcall prompt-fn clock) |
|
1032 |
" [jkKgGSscCiq]? ") |
|
1033 |
nil 45))) |
|
1034 |
(and (not (memq char-pressed '(?i ?q))) char-pressed))))) |
|
1035 |
(default |
|
1036 |
(floor (/ (float-time |
|
1037 |
(time-subtract (current-time) last-valid)) 60))) |
|
1038 |
(keep |
|
1039 |
(and (memq ch '(?k ?K)) |
|
1040 |
(read-number "Keep how many minutes? " default))) |
|
1041 |
(gotback |
|
1042 |
(and (memq ch '(?g ?G)) |
|
1043 |
(read-number "Got back how many minutes ago? " default))) |
|
1044 |
(subtractp (memq ch '(?s ?S))) |
|
1045 |
(barely-started-p (< (- (float-time last-valid) |
|
1046 |
(float-time (cdr clock))) 45)) |
|
1047 |
(start-over (and subtractp barely-started-p))) |
|
1048 |
(cond |
|
1049 |
((memq ch '(?j ?J)) |
|
1050 |
(if (eq ch ?J) |
|
1051 |
(org-clock-resolve-clock clock 'now nil t nil fail-quietly)) |
|
1052 |
(org-clock-jump-to-current-clock clock)) |
|
1053 |
((or (null ch) |
|
1054 |
(not (memq ch '(?k ?K ?g ?G ?s ?S ?C)))) |
|
1055 |
(message "")) |
|
1056 |
(t |
|
1057 |
(org-clock-resolve-clock |
|
1058 |
clock (cond |
|
1059 |
((or (eq ch ?C) |
|
1060 |
;; If the time on the clock was less than a minute before |
|
1061 |
;; the user went away, and they've ask to subtract all the |
|
1062 |
;; time... |
|
1063 |
start-over) |
|
1064 |
nil) |
|
1065 |
((or subtractp |
|
1066 |
(and gotback (= gotback 0))) |
|
1067 |
last-valid) |
|
1068 |
((or (and keep (= keep default)) |
|
1069 |
(and gotback (= gotback default))) |
|
1070 |
'now) |
|
1071 |
(keep |
|
1072 |
(time-add last-valid (seconds-to-time (* 60 keep)))) |
|
1073 |
(gotback |
|
1074 |
(time-subtract (current-time) |
|
1075 |
(seconds-to-time (* 60 gotback)))) |
|
1076 |
(t |
|
1077 |
(error "Unexpected, please report this as a bug"))) |
|
1078 |
(and gotback last-valid) |
|
1079 |
(memq ch '(?K ?G ?S)) |
|
1080 |
(and start-over |
|
1081 |
(not (memq ch '(?K ?G ?S ?C)))) |
|
1082 |
fail-quietly))))) |
|
1083 |
|
|
1084 |
;;;###autoload |
|
1085 |
(defun org-resolve-clocks (&optional only-dangling-p prompt-fn last-valid) |
|
1086 |
"Resolve all currently open Org clocks. |
|
1087 |
If `only-dangling-p' is non-nil, only ask to resolve dangling |
|
1088 |
\(i.e., not currently open and valid) clocks." |
|
1089 |
(interactive "P") |
|
1090 |
(unless org-clock-resolving-clocks |
|
1091 |
(let ((org-clock-resolving-clocks t)) |
|
1092 |
(dolist (file (org-files-list)) |
|
1093 |
(let ((clocks (org-find-open-clocks file))) |
|
1094 |
(dolist (clock clocks) |
|
1095 |
(let ((dangling (or (not (org-clock-is-active)) |
|
1096 |
(/= (car clock) org-clock-marker)))) |
|
1097 |
(if (or (not only-dangling-p) dangling) |
|
1098 |
(org-clock-resolve |
|
1099 |
clock |
|
1100 |
(or prompt-fn |
|
1101 |
(function |
|
1102 |
(lambda (clock) |
|
1103 |
(format |
|
1104 |
"Dangling clock started %d mins ago" |
|
1105 |
(floor (- (float-time) |
|
1106 |
(float-time (cdr clock))) |
|
1107 |
60))))) |
|
1108 |
(or last-valid |
|
1109 |
(cdr clock))))))))))) |
|
1110 |
|
|
1111 |
(defun org-emacs-idle-seconds () |
|
1112 |
"Return the current Emacs idle time in seconds, or nil if not idle." |
|
1113 |
(let ((idle-time (current-idle-time))) |
|
1114 |
(if idle-time |
|
1115 |
(float-time idle-time) |
|
1116 |
0))) |
|
1117 |
|
|
1118 |
(defun org-mac-idle-seconds () |
|
1119 |
"Return the current Mac idle time in seconds." |
|
1120 |
(string-to-number (shell-command-to-string "ioreg -c IOHIDSystem | perl -ane 'if (/Idle/) {$idle=(pop @F)/1000000000; print $idle; last}'"))) |
|
1121 |
|
|
1122 |
(defvar org-x11idle-exists-p |
|
1123 |
;; Check that x11idle exists |
|
1124 |
(and (eq window-system 'x) |
|
1125 |
(eq 0 (call-process-shell-command |
|
1126 |
(format "command -v %s" org-clock-x11idle-program-name))) |
|
1127 |
;; Check that x11idle can retrieve the idle time |
|
1128 |
;; FIXME: Why "..-shell-command" rather than just `call-process'? |
|
1129 |
(eq 0 (call-process-shell-command org-clock-x11idle-program-name)))) |
|
1130 |
|
|
1131 |
(defun org-x11-idle-seconds () |
|
1132 |
"Return the current X11 idle time in seconds." |
|
1133 |
(/ (string-to-number (shell-command-to-string org-clock-x11idle-program-name)) 1000)) |
|
1134 |
|
|
1135 |
(defun org-user-idle-seconds () |
|
1136 |
"Return the number of seconds the user has been idle for. |
|
1137 |
This routine returns a floating point number." |
|
1138 |
(cond |
|
1139 |
((eq system-type 'darwin) |
|
1140 |
(org-mac-idle-seconds)) |
|
1141 |
((and (eq window-system 'x) org-x11idle-exists-p) |
|
1142 |
(org-x11-idle-seconds)) |
|
1143 |
(t |
|
1144 |
(org-emacs-idle-seconds)))) |
|
1145 |
|
|
1146 |
(defvar org-clock-user-idle-seconds) |
|
1147 |
|
|
1148 |
(defun org-resolve-clocks-if-idle () |
|
1149 |
"Resolve all currently open Org clocks. |
|
1150 |
This is performed after `org-clock-idle-time' minutes, to check |
|
1151 |
if the user really wants to stay clocked in after being idle for |
|
1152 |
so long." |
|
1153 |
(when (and org-clock-idle-time (not org-clock-resolving-clocks) |
|
1154 |
org-clock-marker (marker-buffer org-clock-marker)) |
|
1155 |
(let* ((org-clock-user-idle-seconds (org-user-idle-seconds)) |
|
1156 |
(org-clock-user-idle-start |
|
1157 |
(time-subtract (current-time) |
|
1158 |
(seconds-to-time org-clock-user-idle-seconds))) |
|
1159 |
(org-clock-resolving-clocks-due-to-idleness t)) |
|
1160 |
(if (> org-clock-user-idle-seconds (* 60 org-clock-idle-time)) |
|
1161 |
(org-clock-resolve |
|
1162 |
(cons org-clock-marker |
|
1163 |
org-clock-start-time) |
|
1164 |
(lambda (_) |
|
1165 |
(format "Clocked in & idle for %.1f mins" |
|
1166 |
(/ (float-time |
|
1167 |
(time-subtract (current-time) |
|
1168 |
org-clock-user-idle-start)) |
|
1169 |
60.0))) |
|
1170 |
org-clock-user-idle-start))))) |
|
1171 |
|
|
1172 |
(defvar org-clock-current-task nil "Task currently clocked in.") |
|
1173 |
(defvar org-clock-out-time nil) ; store the time of the last clock-out |
|
1174 |
(defvar org--msg-extra) |
|
1175 |
|
|
1176 |
;;;###autoload |
|
1177 |
(defun org-clock-in (&optional select start-time) |
|
1178 |
"Start the clock on the current item. |
|
1179 |
|
|
1180 |
If necessary, clock-out of the currently active clock. |
|
1181 |
|
|
1182 |
With a `\\[universal-argument]' prefix argument SELECT, offer a list of \ |
|
1183 |
recently clocked |
|
1184 |
tasks to clock into. |
|
1185 |
|
|
1186 |
When SELECT is `\\[universal-argument] \ \\[universal-argument]', \ |
|
1187 |
clock into the current task and mark it as |
|
1188 |
the default task, a special task that will always be offered in the |
|
1189 |
clocking selection, associated with the letter `d'. |
|
1190 |
|
|
1191 |
When SELECT is `\\[universal-argument] \\[universal-argument] \ |
|
1192 |
\\[universal-argument]', clock in by using the last clock-out |
|
1193 |
time as the start time. See `org-clock-continuously' to make this |
|
1194 |
the default behavior." |
|
1195 |
(interactive "P") |
|
1196 |
(setq org-clock-notification-was-shown nil) |
|
1197 |
(org-refresh-effort-properties) |
|
1198 |
(catch 'abort |
|
1199 |
(let ((interrupting (and (not org-clock-resolving-clocks-due-to-idleness) |
|
1200 |
(org-clocking-p))) |
|
1201 |
ts selected-task target-pos (org--msg-extra "") |
|
1202 |
(leftover (and (not org-clock-resolving-clocks) |
|
1203 |
org-clock-leftover-time))) |
|
1204 |
|
|
1205 |
(when (and org-clock-auto-clock-resolution |
|
1206 |
(or (not interrupting) |
|
1207 |
(eq t org-clock-auto-clock-resolution)) |
|
1208 |
(not org-clock-clocking-in) |
|
1209 |
(not org-clock-resolving-clocks)) |
|
1210 |
(setq org-clock-leftover-time nil) |
|
1211 |
(let ((org-clock-clocking-in t)) |
|
1212 |
(org-resolve-clocks))) ; check if any clocks are dangling |
|
1213 |
|
|
1214 |
(when (equal select '(64)) |
|
1215 |
;; Set start-time to `org-clock-out-time' |
|
1216 |
(let ((org-clock-continuously t)) |
|
1217 |
(org-clock-in nil org-clock-out-time) |
|
1218 |
(throw 'abort nil))) |
|
1219 |
|
|
1220 |
(when (equal select '(4)) |
|
1221 |
(setq selected-task (org-clock-select-task "Clock-in on task: ")) |
|
1222 |
(if selected-task |
|
1223 |
(setq selected-task (copy-marker selected-task)) |
|
1224 |
(error "Abort"))) |
|
1225 |
|
|
1226 |
(when (equal select '(16)) |
|
1227 |
;; Mark as default clocking task |
|
1228 |
(org-clock-mark-default-task)) |
|
1229 |
|
|
1230 |
(when interrupting |
|
1231 |
;; We are interrupting the clocking of a different task. |
|
1232 |
;; Save a marker to this task, so that we can go back. |
|
1233 |
;; First check if we are trying to clock into the same task! |
|
1234 |
(when (save-excursion |
|
1235 |
(unless selected-task |
|
1236 |
(org-back-to-heading t)) |
|
1237 |
(and (equal (marker-buffer org-clock-hd-marker) |
|
1238 |
(if selected-task |
|
1239 |
(marker-buffer selected-task) |
|
1240 |
(current-buffer))) |
|
1241 |
(= (marker-position org-clock-hd-marker) |
|
1242 |
(if selected-task |
|
1243 |
(marker-position selected-task) |
|
1244 |
(point))) |
|
1245 |
(equal org-clock-current-task (nth 4 (org-heading-components))))) |
|
1246 |
(message "Clock continues in \"%s\"" org-clock-heading) |
|
1247 |
(throw 'abort nil)) |
|
1248 |
(move-marker org-clock-interrupted-task |
|
1249 |
(marker-position org-clock-marker) |
|
1250 |
(marker-buffer org-clock-marker)) |
|
1251 |
(let ((org-clock-clocking-in t)) |
|
1252 |
(org-clock-out nil t))) |
|
1253 |
|
|
1254 |
;; Clock in at which position? |
|
1255 |
(setq target-pos |
|
1256 |
(if (and (eobp) (not (org-at-heading-p))) |
|
1257 |
(point-at-bol 0) |
|
1258 |
(point))) |
|
1259 |
(save-excursion |
|
1260 |
(when (and selected-task (marker-buffer selected-task)) |
|
1261 |
;; There is a selected task, move to the correct buffer |
|
1262 |
;; and set the new target position. |
|
1263 |
(set-buffer (org-base-buffer (marker-buffer selected-task))) |
|
1264 |
(setq target-pos (marker-position selected-task)) |
|
1265 |
(move-marker selected-task nil)) |
|
1266 |
(org-with-wide-buffer |
|
1267 |
(goto-char target-pos) |
|
1268 |
(org-back-to-heading t) |
|
1269 |
(or interrupting (move-marker org-clock-interrupted-task nil)) |
|
1270 |
(run-hooks 'org-clock-in-prepare-hook) |
|
1271 |
(org-clock-history-push) |
|
1272 |
(setq org-clock-current-task (nth 4 (org-heading-components))) |
|
1273 |
(cond ((functionp org-clock-in-switch-to-state) |
|
1274 |
(let ((case-fold-search nil)) |
|
1275 |
(looking-at org-complex-heading-regexp)) |
|
1276 |
(let ((newstate (funcall org-clock-in-switch-to-state |
|
1277 |
(match-string 2)))) |
|
1278 |
(when newstate (org-todo newstate)))) |
|
1279 |
((and org-clock-in-switch-to-state |
|
1280 |
(not (looking-at (concat org-outline-regexp "[ \t]*" |
|
1281 |
org-clock-in-switch-to-state |
|
1282 |
"\\>")))) |
|
1283 |
(org-todo org-clock-in-switch-to-state))) |
|
1284 |
(setq org-clock-heading (org-clock--mode-line-heading)) |
|
1285 |
(org-clock-find-position org-clock-in-resume) |
|
1286 |
(cond |
|
1287 |
((and org-clock-in-resume |
|
1288 |
(looking-at |
|
1289 |
(concat "^[ \t]*" org-clock-string |
|
1290 |
" \\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" |
|
1291 |
" *\\sw+.? +[012][0-9]:[0-5][0-9]\\)\\][ \t]*$"))) |
|
1292 |
(message "Matched %s" (match-string 1)) |
|
1293 |
(setq ts (concat "[" (match-string 1) "]")) |
|
1294 |
(goto-char (match-end 1)) |
|
1295 |
(setq org-clock-start-time |
|
1296 |
(apply 'encode-time |
|
1297 |
(org-parse-time-string (match-string 1)))) |
|
1298 |
(setq org-clock-effort (org-entry-get (point) org-effort-property)) |
|
1299 |
(setq org-clock-total-time (org-clock-sum-current-item |
|
1300 |
(org-clock-get-sum-start)))) |
|
1301 |
((eq org-clock-in-resume 'auto-restart) |
|
1302 |
;; called from org-clock-load during startup, |
|
1303 |
;; do not interrupt, but warn! |
|
1304 |
(message "Cannot restart clock because task does not contain unfinished clock") |
|
1305 |
(ding) |
|
1306 |
(sit-for 2) |
|
1307 |
(throw 'abort nil)) |
|
1308 |
(t |
|
1309 |
(insert-before-markers "\n") |
|
1310 |
(backward-char 1) |
|
1311 |
(org-indent-line) |
|
1312 |
(when (and (save-excursion |
|
1313 |
(end-of-line 0) |
|
1314 |
(org-in-item-p))) |
|
1315 |
(beginning-of-line 1) |
|
1316 |
(indent-line-to (- (org-get-indentation) 2))) |
|
1317 |
(insert org-clock-string " ") |
|
1318 |
(setq org-clock-effort (org-entry-get (point) org-effort-property)) |
|
1319 |
(setq org-clock-total-time (org-clock-sum-current-item |
|
1320 |
(org-clock-get-sum-start))) |
|
1321 |
(setq org-clock-start-time |
|
1322 |
(or (and org-clock-continuously org-clock-out-time) |
|
1323 |
(and leftover |
|
1324 |
(y-or-n-p |
|
1325 |
(format |
|
1326 |
"You stopped another clock %d mins ago; start this one from then? " |
|
1327 |
(/ (- (float-time |
|
1328 |
(org-current-time org-clock-rounding-minutes t)) |
|
1329 |
(float-time leftover)) |
|
1330 |
60))) |
|
1331 |
leftover) |
|
1332 |
start-time |
|
1333 |
(org-current-time org-clock-rounding-minutes t))) |
|
1334 |
(setq ts (org-insert-time-stamp org-clock-start-time |
|
1335 |
'with-hm 'inactive)))) |
|
1336 |
(move-marker org-clock-marker (point) (buffer-base-buffer)) |
|
1337 |
(move-marker org-clock-hd-marker |
|
1338 |
(save-excursion (org-back-to-heading t) (point)) |
|
1339 |
(buffer-base-buffer)) |
|
1340 |
(setq org-clock-has-been-used t) |
|
1341 |
;; add to mode line |
|
1342 |
(when (or (eq org-clock-clocked-in-display 'mode-line) |
|
1343 |
(eq org-clock-clocked-in-display 'both)) |
|
1344 |
(or global-mode-string (setq global-mode-string '(""))) |
|
1345 |
(or (memq 'org-mode-line-string global-mode-string) |
|
1346 |
(setq global-mode-string |
|
1347 |
(append global-mode-string '(org-mode-line-string))))) |
|
1348 |
;; add to frame title |
|
1349 |
(when (or (eq org-clock-clocked-in-display 'frame-title) |
|
1350 |
(eq org-clock-clocked-in-display 'both)) |
|
1351 |
(setq frame-title-format org-clock-frame-title-format)) |
|
1352 |
(org-clock-update-mode-line) |
|
1353 |
(when org-clock-mode-line-timer |
|
1354 |
(cancel-timer org-clock-mode-line-timer) |
|
1355 |
(setq org-clock-mode-line-timer nil)) |
|
1356 |
(when org-clock-clocked-in-display |
|
1357 |
(setq org-clock-mode-line-timer |
|
1358 |
(run-with-timer org-clock-update-period |
|
1359 |
org-clock-update-period |
|
1360 |
'org-clock-update-mode-line))) |
|
1361 |
(when org-clock-idle-timer |
|
1362 |
(cancel-timer org-clock-idle-timer) |
|
1363 |
(setq org-clock-idle-timer nil)) |
|
1364 |
(setq org-clock-idle-timer |
|
1365 |
(run-with-timer 60 60 'org-resolve-clocks-if-idle)) |
|
1366 |
(message "Clock starts at %s - %s" ts org--msg-extra) |
|
1367 |
(run-hooks 'org-clock-in-hook)))))) |
|
1368 |
|
|
1369 |
;;;###autoload |
|
1370 |
(defun org-clock-in-last (&optional arg) |
|
1371 |
"Clock in the last closed clocked item. |
|
1372 |
When already clocking in, send a warning. |
|
1373 |
With a universal prefix argument, select the task you want to |
|
1374 |
clock in from the last clocked in tasks. |
|
1375 |
With two universal prefix arguments, start clocking using the |
|
1376 |
last clock-out time, if any. |
|
1377 |
With three universal prefix arguments, interactively prompt |
|
1378 |
for a todo state to switch to, overriding the existing value |
|
1379 |
`org-clock-in-switch-to-state'." |
|
1380 |
(interactive "P") |
|
1381 |
(if (equal arg '(4)) (org-clock-in arg) |
|
1382 |
(let ((start-time (if (or org-clock-continuously (equal arg '(16))) |
|
1383 |
(or org-clock-out-time |
|
1384 |
(org-current-time org-clock-rounding-minutes t)) |
|
1385 |
(org-current-time org-clock-rounding-minutes t)))) |
|
1386 |
(if (null org-clock-history) |
|
1387 |
(message "No last clock") |
|
1388 |
(let ((org-clock-in-switch-to-state |
|
1389 |
(if (and (not org-clock-current-task) (equal arg '(64))) |
|
1390 |
(completing-read "Switch to state: " |
|
1391 |
(and org-clock-history |
|
1392 |
(with-current-buffer |
|
1393 |
(marker-buffer (car org-clock-history)) |
|
1394 |
org-todo-keywords-1))) |
|
1395 |
org-clock-in-switch-to-state)) |
|
1396 |
(already-clocking org-clock-current-task)) |
|
1397 |
(org-clock-clock-in (list (car org-clock-history)) nil start-time) |
|
1398 |
(or already-clocking |
|
1399 |
;; Don't display a message if we are already clocking in |
|
1400 |
(message "Clocking back: %s (in %s)" |
|
1401 |
org-clock-current-task |
|
1402 |
(buffer-name (marker-buffer org-clock-marker))))))))) |
|
1403 |
|
|
1404 |
(defun org-clock-mark-default-task () |
|
1405 |
"Mark current task as default task." |
|
1406 |
(interactive) |
|
1407 |
(save-excursion |
|
1408 |
(org-back-to-heading t) |
|
1409 |
(move-marker org-clock-default-task (point)))) |
|
1410 |
|
|
1411 |
(defun org-clock-get-sum-start () |
|
1412 |
"Return the time from which clock times should be counted. |
|
1413 |
|
|
1414 |
This is for the currently running clock as it is displayed in the |
|
1415 |
mode line. This function looks at the properties LAST_REPEAT and |
|
1416 |
in particular CLOCK_MODELINE_TOTAL and the corresponding variable |
|
1417 |
`org-clock-mode-line-total' and then decides which time to use. |
|
1418 |
|
|
1419 |
The time is always returned as UTC." |
|
1420 |
(let ((cmt (or (org-entry-get nil "CLOCK_MODELINE_TOTAL" 'selective) |
|
1421 |
(symbol-name org-clock-mode-line-total))) |
|
1422 |
(lr (org-entry-get nil "LAST_REPEAT"))) |
|
1423 |
(cond |
|
1424 |
((equal cmt "current") |
|
1425 |
(setq org--msg-extra "showing time in current clock instance") |
|
1426 |
(current-time)) |
|
1427 |
((equal cmt "today") |
|
1428 |
(setq org--msg-extra "showing today's task time.") |
|
1429 |
(let* ((dt (decode-time)) |
|
1430 |
(hour (nth 2 dt)) |
|
1431 |
(day (nth 3 dt))) |
|
1432 |
(if (< hour org-extend-today-until) (setf (nth 3 dt) (1- day))) |
|
1433 |
(setf (nth 2 dt) org-extend-today-until) |
|
1434 |
(apply #'encode-time (append (list 0 0) (nthcdr 2 dt))))) |
|
1435 |
((or (equal cmt "all") |
|
1436 |
(and (or (not cmt) (equal cmt "auto")) |
|
1437 |
(not lr))) |
|
1438 |
(setq org--msg-extra "showing entire task time.") |
|
1439 |
nil) |
|
1440 |
((or (equal cmt "repeat") |
|
1441 |
(and (or (not cmt) (equal cmt "auto")) |
|
1442 |
lr)) |
|
1443 |
(setq org--msg-extra "showing task time since last repeat.") |
|
1444 |
(and lr (org-time-string-to-time lr))) |
|
1445 |
(t nil)))) |
|
1446 |
|
|
1447 |
(defun org-clock-find-position (find-unclosed) |
|
1448 |
"Find the location where the next clock line should be inserted. |
|
1449 |
When FIND-UNCLOSED is non-nil, first check if there is an unclosed clock |
|
1450 |
line and position cursor in that line." |
|
1451 |
(org-back-to-heading t) |
|
1452 |
(catch 'exit |
|
1453 |
(let* ((beg (line-beginning-position)) |
|
1454 |
(end (save-excursion (outline-next-heading) (point))) |
|
1455 |
(org-clock-into-drawer (org-clock-into-drawer)) |
|
1456 |
(drawer (org-clock-drawer-name))) |
|
1457 |
;; Look for a running clock if FIND-UNCLOSED in non-nil. |
|
1458 |
(when find-unclosed |
|
1459 |
(let ((open-clock-re |
|
1460 |
(concat "^[ \t]*" |
|
1461 |
org-clock-string |
|
1462 |
" \\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}" |
|
1463 |
" *\\sw+ +[012][0-9]:[0-5][0-9]\\)\\][ \t]*$"))) |
|
1464 |
(while (re-search-forward open-clock-re end t) |
|
1465 |
(let ((element (org-element-at-point))) |
|
1466 |
(when (and (eq (org-element-type element) 'clock) |
|
1467 |
(eq (org-element-property :status element) 'running)) |
|
1468 |
(beginning-of-line) |
|
1469 |
(throw 'exit t)))))) |
|
1470 |
;; Look for an existing clock drawer. |
|
1471 |
(when drawer |
|
1472 |
(goto-char beg) |
|
1473 |
(let ((drawer-re (concat "^[ \t]*:" (regexp-quote drawer) ":[ \t]*$"))) |
|
1474 |
(while (re-search-forward drawer-re end t) |
|
1475 |
(let ((element (org-element-at-point))) |
|
1476 |
(when (eq (org-element-type element) 'drawer) |
|
1477 |
(let ((cend (org-element-property :contents-end element))) |
|
1478 |
(if (and (not org-log-states-order-reversed) cend) |
|
1479 |
(goto-char cend) |
|
1480 |
(forward-line)) |
|
1481 |
(throw 'exit t))))))) |
|
1482 |
(goto-char beg) |
|
1483 |
(let ((clock-re (concat "^[ \t]*" org-clock-string)) |
|
1484 |
(count 0) |
|
1485 |
positions) |
|
1486 |
;; Count the CLOCK lines and store their positions. |
|
1487 |
(save-excursion |
|
1488 |
(while (re-search-forward clock-re end t) |
|
1489 |
(let ((element (org-element-at-point))) |
|
1490 |
(when (eq (org-element-type element) 'clock) |
|
1491 |
(setq positions (cons (line-beginning-position) positions) |
|
1492 |
count (1+ count)))))) |
|
1493 |
(cond |
|
1494 |
((null positions) |
|
1495 |
;; Skip planning line and property drawer, if any. |
|
1496 |
(org-end-of-meta-data) |
|
1497 |
(unless (bolp) (insert "\n")) |
|
1498 |
;; Create a new drawer if necessary. |
|
1499 |
(when (and org-clock-into-drawer |
|
1500 |
(or (not (wholenump org-clock-into-drawer)) |
|
1501 |
(< org-clock-into-drawer 2))) |
|
1502 |
(let ((beg (point))) |
|
1503 |
(insert ":" drawer ":\n:END:\n") |
|
1504 |
(org-indent-region beg (point)) |
|
1505 |
(goto-char beg) |
|
1506 |
(org-flag-drawer t) |
|
1507 |
(forward-line)))) |
|
1508 |
;; When a clock drawer needs to be created because of the |
|
1509 |
;; number of clock items or simply if it is missing, collect |
|
1510 |
;; all clocks in the section and wrap them within the drawer. |
|
1511 |
((if (wholenump org-clock-into-drawer) |
|
1512 |
(>= (1+ count) org-clock-into-drawer) |
|
1513 |
drawer) |
|
1514 |
;; Skip planning line and property drawer, if any. |
|
1515 |
(org-end-of-meta-data) |
|
1516 |
(let ((beg (point))) |
|
1517 |
(insert |
|
1518 |
(mapconcat |
|
1519 |
(lambda (p) |
|
1520 |
(save-excursion |
|
1521 |
(goto-char p) |
|
1522 |
(org-trim (delete-and-extract-region |
|
1523 |
(save-excursion (skip-chars-backward " \r\t\n") |
|
1524 |
(line-beginning-position 2)) |
|
1525 |
(line-beginning-position 2))))) |
|
1526 |
positions "\n") |
|
1527 |
"\n:END:\n") |
|
1528 |
(let ((end (point-marker))) |
|
1529 |
(goto-char beg) |
|
1530 |
(save-excursion (insert ":" drawer ":\n")) |
|
1531 |
(org-flag-drawer t) |
|
1532 |
(org-indent-region (point) end) |
|
1533 |
(forward-line) |
|
1534 |
(unless org-log-states-order-reversed |
|
1535 |
(goto-char end) |
|
1536 |
(beginning-of-line -1)) |
|
1537 |
(set-marker end nil)))) |
|
1538 |
(org-log-states-order-reversed (goto-char (car (last positions)))) |
|
1539 |
(t (goto-char (car positions)))))))) |
|
1540 |
|
|
1541 |
;;;###autoload |
|
1542 |
(defun org-clock-out (&optional switch-to-state fail-quietly at-time) |
|
1543 |
"Stop the currently running clock. |
|
1544 |
Throw an error if there is no running clock and FAIL-QUIETLY is nil. |
|
1545 |
With a universal prefix, prompt for a state to switch the clocked out task |
|
1546 |
to, overriding the existing value of `org-clock-out-switch-to-state'." |
|
1547 |
(interactive "P") |
|
1548 |
(catch 'exit |
|
1549 |
(when (not (org-clocking-p)) |
|
1550 |
(setq global-mode-string |
|
1551 |
(delq 'org-mode-line-string global-mode-string)) |
|
1552 |
(setq frame-title-format org-frame-title-format-backup) |
|
1553 |
(force-mode-line-update) |
|
1554 |
(if fail-quietly (throw 'exit t) (user-error "No active clock"))) |
|
1555 |
(let ((org-clock-out-switch-to-state |
|
1556 |
(if switch-to-state |
|
1557 |
(completing-read "Switch to state: " |
|
1558 |
(with-current-buffer |
|
1559 |
(marker-buffer org-clock-marker) |
|
1560 |
org-todo-keywords-1) |
|
1561 |
nil t "DONE") |
|
1562 |
org-clock-out-switch-to-state)) |
|
1563 |
(now (org-current-time org-clock-rounding-minutes)) |
|
1564 |
ts te s h m remove) |
|
1565 |
(setq org-clock-out-time now) |
|
1566 |
(save-excursion ; Do not replace this with `with-current-buffer'. |
|
1567 |
(with-no-warnings (set-buffer (org-clocking-buffer))) |
|
1568 |
(save-restriction |
|
1569 |
(widen) |
|
1570 |
(goto-char org-clock-marker) |
|
1571 |
(beginning-of-line 1) |
|
1572 |
(if (and (looking-at (concat "[ \t]*" org-keyword-time-regexp)) |
|
1573 |
(equal (match-string 1) org-clock-string)) |
|
1574 |
(setq ts (match-string 2)) |
|
1575 |
(if fail-quietly (throw 'exit nil) (error "Clock start time is gone"))) |
|
1576 |
(goto-char (match-end 0)) |
|
1577 |
(delete-region (point) (point-at-eol)) |
|
1578 |
(insert "--") |
|
1579 |
(setq te (org-insert-time-stamp (or at-time now) 'with-hm 'inactive)) |
|
1580 |
(setq s (- (float-time |
|
1581 |
(apply #'encode-time (org-parse-time-string te))) |
|
1582 |
(float-time |
|
1583 |
(apply #'encode-time (org-parse-time-string ts)))) |
|
1584 |
h (floor (/ s 3600)) |
|
1585 |
s (- s (* 3600 h)) |
|
1586 |
m (floor (/ s 60)) |
|
1587 |
s (- s (* 60 s))) |
|
1588 |
(insert " => " (format "%2d:%02d" h m)) |
|
1589 |
(move-marker org-clock-marker nil) |
|
1590 |
(move-marker org-clock-hd-marker nil) |
|
1591 |
;; Possibly remove zero time clocks. However, do not add |
|
1592 |
;; a note associated to the CLOCK line in this case. |
|
1593 |
(cond ((and org-clock-out-remove-zero-time-clocks |
|
1594 |
(= (+ h m) 0)) |
|
1595 |
(setq remove t) |
|
1596 |
(delete-region (line-beginning-position) |
|
1597 |
(line-beginning-position 2))) |
|
1598 |
(org-log-note-clock-out |
|
1599 |
(org-add-log-setup |
|
1600 |
'clock-out nil nil nil |
|
1601 |
(concat "# Task: " (org-get-heading t) "\n\n")))) |
|
1602 |
(when org-clock-mode-line-timer |
|
1603 |
(cancel-timer org-clock-mode-line-timer) |
|
1604 |
(setq org-clock-mode-line-timer nil)) |
|
1605 |
(when org-clock-idle-timer |
|
1606 |
(cancel-timer org-clock-idle-timer) |
|
1607 |
(setq org-clock-idle-timer nil)) |
|
1608 |
(setq global-mode-string |
|
1609 |
(delq 'org-mode-line-string global-mode-string)) |
|
1610 |
(setq frame-title-format org-frame-title-format-backup) |
|
1611 |
(when org-clock-out-switch-to-state |
|
1612 |
(save-excursion |
|
1613 |
(org-back-to-heading t) |
|
1614 |
(let ((org-clock-out-when-done nil)) |
|
1615 |
(cond |
|
1616 |
((functionp org-clock-out-switch-to-state) |
|
1617 |
(let ((case-fold-search nil)) |
|
1618 |
(looking-at org-complex-heading-regexp)) |
|
1619 |
(let ((newstate (funcall org-clock-out-switch-to-state |
|
1620 |
(match-string 2)))) |
|
1621 |
(when newstate (org-todo newstate)))) |
|
1622 |
((and org-clock-out-switch-to-state |
|
1623 |
(not (looking-at (concat org-outline-regexp "[ \t]*" |
|
1624 |
org-clock-out-switch-to-state |
|
1625 |
"\\>")))) |
|
1626 |
(org-todo org-clock-out-switch-to-state)))))) |
|
1627 |
(force-mode-line-update) |
|
1628 |
(message (concat "Clock stopped at %s after " |
|
1629 |
(org-duration-from-minutes (+ (* 60 h) m)) "%s") |
|
1630 |
te (if remove " => LINE REMOVED" "")) |
|
1631 |
(run-hooks 'org-clock-out-hook) |
|
1632 |
(unless (org-clocking-p) |
|
1633 |
(setq org-clock-current-task nil))))))) |
|
1634 |
|
|
1635 |
(add-hook 'org-clock-out-hook 'org-clock-remove-empty-clock-drawer) |
|
1636 |
|
|
1637 |
(defun org-clock-remove-empty-clock-drawer () |
|
1638 |
"Remove empty clock drawers in current subtree." |
|
1639 |
(save-excursion |
|
1640 |
(org-back-to-heading t) |
|
1641 |
(org-map-tree |
|
1642 |
(lambda () |
|
1643 |
(let ((drawer (org-clock-drawer-name)) |
|
1644 |
(case-fold-search t)) |
|
1645 |
(when drawer |
|
1646 |
(let ((re (format "^[ \t]*:%s:[ \t]*$" (regexp-quote drawer))) |
|
1647 |
(end (save-excursion (outline-next-heading)))) |
|
1648 |
(while (re-search-forward re end t) |
|
1649 |
(org-remove-empty-drawer-at (point)))))))))) |
|
1650 |
|
|
1651 |
(defun org-clock-timestamps-up (&optional n) |
|
1652 |
"Increase CLOCK timestamps at cursor. |
|
1653 |
Optional argument N tells to change by that many units." |
|
1654 |
(interactive "P") |
|
1655 |
(org-clock-timestamps-change 'up n)) |
|
1656 |
|
|
1657 |
(defun org-clock-timestamps-down (&optional n) |
|
1658 |
"Increase CLOCK timestamps at cursor. |
|
1659 |
Optional argument N tells to change by that many units." |
|
1660 |
(interactive "P") |
|
1661 |
(org-clock-timestamps-change 'down n)) |
|
1662 |
|
|
1663 |
(defun org-clock-timestamps-change (updown &optional n) |
|
1664 |
"Change CLOCK timestamps synchronously at cursor. |
|
1665 |
UPDOWN tells whether to change `up' or `down'. |
|
1666 |
Optional argument N tells to change by that many units." |
|
1667 |
(let ((tschange (if (eq updown 'up) 'org-timestamp-up |
|
1668 |
'org-timestamp-down)) |
|
1669 |
(timestamp? (org-at-timestamp-p 'lax)) |
|
1670 |
ts1 begts1 ts2 begts2 updatets1 tdiff) |
|
1671 |
(when timestamp? |
|
1672 |
(save-excursion |
|
1673 |
(move-beginning-of-line 1) |
|
1674 |
(re-search-forward org-ts-regexp3 nil t) |
|
1675 |
(setq ts1 (match-string 0) begts1 (match-beginning 0)) |
|
1676 |
(when (re-search-forward org-ts-regexp3 nil t) |
|
1677 |
(setq ts2 (match-string 0) begts2 (match-beginning 0)))) |
|
1678 |
;; Are we on the second timestamp? |
|
1679 |
(if (<= begts2 (point)) (setq updatets1 t)) |
|
1680 |
(if (not ts2) |
|
1681 |
;; fall back on org-timestamp-up if there is only one |
|
1682 |
(funcall tschange n) |
|
1683 |
(funcall tschange n) |
|
1684 |
(let ((ts (if updatets1 ts2 ts1)) |
|
1685 |
(begts (if updatets1 begts1 begts2))) |
|
1686 |
(setq tdiff |
|
1687 |
(time-subtract |
|
1688 |
(org-time-string-to-time org-last-changed-timestamp) |
|
1689 |
(org-time-string-to-time ts))) |
|
1690 |
(save-excursion |
|
1691 |
(goto-char begts) |
|
1692 |
(org-timestamp-change |
|
1693 |
(round (/ (float-time tdiff) |
|
1694 |
(pcase timestamp? |
|
1695 |
(`minute 60) |
|
1696 |
(`hour 3600) |
|
1697 |
(`day (* 24 3600)) |
|
1698 |
(`month (* 24 3600 31)) |
|
1699 |
(`year (* 24 3600 365.2))))) |
|
1700 |
timestamp? 'updown))))))) |
|
1701 |
|
|
1702 |
;;;###autoload |
|
1703 |
(defun org-clock-cancel () |
|
1704 |
"Cancel the running clock by removing the start timestamp." |
|
1705 |
(interactive) |
|
1706 |
(when (not (org-clocking-p)) |
|
1707 |
(setq global-mode-string |
|
1708 |
(delq 'org-mode-line-string global-mode-string)) |
|
1709 |
(setq frame-title-format org-frame-title-format-backup) |
|
1710 |
(force-mode-line-update) |
|
1711 |
(error "No active clock")) |
|
1712 |
(save-excursion ; Do not replace this with `with-current-buffer'. |
|
1713 |
(with-no-warnings (set-buffer (org-clocking-buffer))) |
|
1714 |
(goto-char org-clock-marker) |
|
1715 |
(if (looking-back (concat "^[ \t]*" org-clock-string ".*") |
|
1716 |
(line-beginning-position)) |
|
1717 |
(progn (delete-region (1- (point-at-bol)) (point-at-eol)) |
|
1718 |
(org-remove-empty-drawer-at (point))) |
|
1719 |
(message "Clock gone, cancel the timer anyway") |
|
1720 |
(sit-for 2))) |
|
1721 |
(move-marker org-clock-marker nil) |
|
1722 |
(move-marker org-clock-hd-marker nil) |
|
1723 |
(setq global-mode-string |
|
1724 |
(delq 'org-mode-line-string global-mode-string)) |
|
1725 |
(setq frame-title-format org-frame-title-format-backup) |
|
1726 |
(force-mode-line-update) |
|
1727 |
(message "Clock canceled") |
|
1728 |
(run-hooks 'org-clock-cancel-hook)) |
|
1729 |
|
|
1730 |
;;;###autoload |
|
1731 |
(defun org-clock-goto (&optional select) |
|
1732 |
"Go to the currently clocked-in entry, or to the most recently clocked one. |
|
1733 |
With prefix arg SELECT, offer recently clocked tasks for selection." |
|
1734 |
(interactive "@P") |
|
1735 |
(let* ((recent nil) |
|
1736 |
(m (cond |
|
1737 |
(select |
|
1738 |
(or (org-clock-select-task "Select task to go to: ") |
|
1739 |
(error "No task selected"))) |
|
1740 |
((org-clocking-p) org-clock-marker) |
|
1741 |
((and org-clock-goto-may-find-recent-task |
|
1742 |
(car org-clock-history) |
|
1743 |
(marker-buffer (car org-clock-history))) |
|
1744 |
(setq recent t) |
|
1745 |
(car org-clock-history)) |
|
1746 |
(t (error "No active or recent clock task"))))) |
|
1747 |
(pop-to-buffer-same-window (marker-buffer m)) |
|
1748 |
(if (or (< m (point-min)) (> m (point-max))) (widen)) |
|
1749 |
(goto-char m) |
|
1750 |
(org-show-entry) |
|
1751 |
(org-back-to-heading t) |
|
1752 |
(org-cycle-hide-drawers 'children) |
|
1753 |
(recenter org-clock-goto-before-context) |
|
1754 |
(org-reveal) |
|
1755 |
(if recent |
|
1756 |
(message "No running clock, this is the most recently clocked task")) |
|
1757 |
(run-hooks 'org-clock-goto-hook))) |
|
1758 |
|
|
1759 |
(defvar-local org-clock-file-total-minutes nil |
|
1760 |
"Holds the file total time in minutes, after a call to `org-clock-sum'.") |
|
1761 |
|
|
1762 |
;;;###autoload |
|
1763 |
(defun org-clock-sum-today (&optional headline-filter) |
|
1764 |
"Sum the times for each subtree for today." |
|
1765 |
(let ((range (org-clock-special-range 'today))) |
|
1766 |
(org-clock-sum (car range) (cadr range) |
|
1767 |
headline-filter :org-clock-minutes-today))) |
|
1768 |
|
|
1769 |
(defun org-clock-sum-custom (&optional headline-filter range propname) |
|
1770 |
"Sum the times for each subtree for today." |
|
1771 |
(let ((r (or (and (symbolp range) (org-clock-special-range range)) |
|
1772 |
(org-clock-special-range |
|
1773 |
(intern (completing-read |
|
1774 |
"Range: " |
|
1775 |
'("today" "yesterday" "thisweek" "lastweek" |
|
1776 |
"thismonth" "lastmonth" "thisyear" "lastyear" |
|
1777 |
"interactive") |
|
1778 |
nil t)))))) |
|
1779 |
(org-clock-sum (car r) (cadr r) |
|
1780 |
headline-filter (or propname :org-clock-minutes-custom)))) |
|
1781 |
|
|
1782 |
;;;###autoload |
|
1783 |
(defun org-clock-sum (&optional tstart tend headline-filter propname) |
|
1784 |
"Sum the times for each subtree. |
|
1785 |
Puts the resulting times in minutes as a text property on each headline. |
|
1786 |
TSTART and TEND can mark a time range to be considered. |
|
1787 |
HEADLINE-FILTER is a zero-arg function that, if specified, is called for |
|
1788 |
each headline in the time range with point at the headline. Headlines for |
|
1789 |
which HEADLINE-FILTER returns nil are excluded from the clock summation. |
|
1790 |
PROPNAME lets you set a custom text property instead of :org-clock-minutes." |
|
1791 |
(org-with-silent-modifications |
|
1792 |
(let* ((re (concat "^\\(\\*+\\)[ \t]\\|^[ \t]*" |
|
1793 |
org-clock-string |
|
1794 |
"[ \t]*\\(?:\\(\\[.*?\\]\\)-+\\(\\[.*?\\]\\)\\|=>[ \t]+\\([0-9]+\\):\\([0-9]+\\)\\)")) |
|
1795 |
(lmax 30) |
|
1796 |
(ltimes (make-vector lmax 0)) |
|
1797 |
(level 0) |
|
1798 |
(tstart (cond ((stringp tstart) (org-time-string-to-seconds tstart)) |
|
1799 |
((consp tstart) (float-time tstart)) |
|
1800 |
(t tstart))) |
|
1801 |
(tend (cond ((stringp tend) (org-time-string-to-seconds tend)) |
|
1802 |
((consp tend) (float-time tend)) |
|
1803 |
(t tend))) |
|
1804 |
(t1 0) |
|
1805 |
time) |
|
1806 |
(remove-text-properties (point-min) (point-max) |
|
1807 |
`(,(or propname :org-clock-minutes) t |
|
1808 |
:org-clock-force-headline-inclusion t)) |
|
1809 |
(save-excursion |
|
1810 |
(goto-char (point-max)) |
|
1811 |
(while (re-search-backward re nil t) |
|
1812 |
(cond |
|
1813 |
((match-end 2) |
|
1814 |
;; Two time stamps. |
|
1815 |
(let* ((ts (float-time |
|
1816 |
(apply #'encode-time |
|
1817 |
(save-match-data |
|
1818 |
(org-parse-time-string (match-string 2)))))) |
|
1819 |
(te (float-time |
|
1820 |
(apply #'encode-time |
|
1821 |
(org-parse-time-string (match-string 3))))) |
|
1822 |
(dt (- (if tend (min te tend) te) |
|
1823 |
(if tstart (max ts tstart) ts)))) |
|
1824 |
(when (> dt 0) (cl-incf t1 (floor (/ dt 60)))))) |
|
1825 |
((match-end 4) |
|
1826 |
;; A naked time. |
|
1827 |
(setq t1 (+ t1 (string-to-number (match-string 5)) |
|
1828 |
(* 60 (string-to-number (match-string 4)))))) |
|
1829 |
(t ;A headline |
|
1830 |
;; Add the currently clocking item time to the total. |
|
1831 |
(when (and org-clock-report-include-clocking-task |
|
1832 |
(eq (org-clocking-buffer) (current-buffer)) |
|
1833 |
(eq (marker-position org-clock-hd-marker) (point)) |
|
1834 |
tstart |
|
1835 |
tend |
|
1836 |
(>= (float-time org-clock-start-time) tstart) |
|
1837 |
(<= (float-time org-clock-start-time) tend)) |
|
1838 |
(let ((time (floor (- (float-time) |
|
1839 |
(float-time org-clock-start-time)) |
|
1840 |
60))) |
|
1841 |
(setq t1 (+ t1 time)))) |
|
1842 |
(let* ((headline-forced |
|
1843 |
(get-text-property (point) |
|
1844 |
:org-clock-force-headline-inclusion)) |
|
1845 |
(headline-included |
|
1846 |
(or (null headline-filter) |
|
1847 |
(save-excursion |
|
1848 |
(save-match-data (funcall headline-filter)))))) |
|
1849 |
(setq level (- (match-end 1) (match-beginning 1))) |
|
1850 |
(when (>= level lmax) |
|
1851 |
(setq ltimes (vconcat ltimes (make-vector lmax 0)) lmax (* 2 lmax))) |
|
1852 |
(when (or (> t1 0) (> (aref ltimes level) 0)) |
|
1853 |
(when (or headline-included headline-forced) |
|
1854 |
(if headline-included |
|
1855 |
(cl-loop for l from 0 to level do |
|
1856 |
(aset ltimes l (+ (aref ltimes l) t1)))) |
|
1857 |
(setq time (aref ltimes level)) |
|
1858 |
(goto-char (match-beginning 0)) |
|
1859 |
(put-text-property (point) (point-at-eol) |
|
1860 |
(or propname :org-clock-minutes) time) |
|
1861 |
(when headline-filter |
|
1862 |
(save-excursion |
|
1863 |
(save-match-data |
|
1864 |
(while (org-up-heading-safe) |
|
1865 |
(put-text-property |
|
1866 |
(point) (line-end-position) |
|
1867 |
:org-clock-force-headline-inclusion t)))))) |
|
1868 |
(setq t1 0) |
|
1869 |
(cl-loop for l from level to (1- lmax) do |
|
1870 |
(aset ltimes l 0))))))) |
|
1871 |
(setq org-clock-file-total-minutes (aref ltimes 0)))))) |
|
1872 |
|
|
1873 |
(defun org-clock-sum-current-item (&optional tstart) |
|
1874 |
"Return time, clocked on current item in total." |
|
1875 |
(save-excursion |
|
1876 |
(save-restriction |
|
1877 |
(org-narrow-to-subtree) |
|
1878 |
(org-clock-sum tstart) |
|
1879 |
org-clock-file-total-minutes))) |
|
1880 |
|
|
1881 |
;;;###autoload |
|
1882 |
(defun org-clock-display (&optional arg) |
|
1883 |
"Show subtree times in the entire buffer. |
|
1884 |
|
|
1885 |
By default, show the total time for the range defined in |
|
1886 |
`org-clock-display-default-range'. With `\\[universal-argument]' \ |
|
1887 |
prefix, show |
|
1888 |
the total time for today instead. |
|
1889 |
|
|
1890 |
With `\\[universal-argument] \\[universal-argument]' prefix, \ |
|
1891 |
use a custom range, entered at prompt. |
|
1892 |
|
|
1893 |
With `\\[universal-argument] \ \\[universal-argument] \ |
|
1894 |
\\[universal-argument]' prefix, display the total time in the |
|
1895 |
echo area. |
|
1896 |
|
|
1897 |
Use `\\[org-clock-remove-overlays]' to remove the subtree times." |
|
1898 |
(interactive "P") |
|
1899 |
(org-clock-remove-overlays) |
|
1900 |
(let* ((todayp (equal arg '(4))) |
|
1901 |
(customp (member arg '((16) today yesterday |
|
1902 |
thisweek lastweek thismonth |
|
1903 |
lastmonth thisyear lastyear |
|
1904 |
untilnow interactive))) |
|
1905 |
(prop (cond ((not arg) :org-clock-minutes-default) |
|
1906 |
(todayp :org-clock-minutes-today) |
|
1907 |
(customp :org-clock-minutes-custom) |
|
1908 |
(t :org-clock-minutes)))) |
|
1909 |
(cond ((not arg) (org-clock-sum-custom |
|
1910 |
nil org-clock-display-default-range prop)) |
|
1911 |
(todayp (org-clock-sum-today)) |
|
1912 |
(customp (org-clock-sum-custom nil arg)) |
|
1913 |
(t (org-clock-sum))) |
|
1914 |
(unless (equal arg '(64)) |
|
1915 |
(save-excursion |
|
1916 |
(goto-char (point-min)) |
|
1917 |
(let ((p nil)) |
|
1918 |
(while (or (and (equal (setq p (point)) (point-min)) |
|
1919 |
(get-text-property p prop)) |
|
1920 |
(setq p (next-single-property-change (point) prop))) |
|
1921 |
(goto-char p) |
|
1922 |
(let ((time (get-text-property p prop))) |
|
1923 |
(when time (org-clock-put-overlay time))))) |
|
1924 |
;; Arrange to remove the overlays upon next change. |
|
1925 |
(when org-remove-highlights-with-change |
|
1926 |
(add-hook 'before-change-functions 'org-clock-remove-overlays |
|
1927 |
nil 'local)))) |
|
1928 |
(let* ((h (/ org-clock-file-total-minutes 60)) |
|
1929 |
(m (- org-clock-file-total-minutes (* 60 h)))) |
|
1930 |
(message (concat (format "Total file time%s: " |
|
1931 |
(cond (todayp " for today") |
|
1932 |
(customp " (custom)") |
|
1933 |
(t ""))) |
|
1934 |
(org-duration-from-minutes |
|
1935 |
org-clock-file-total-minutes) |
|
1936 |
" (%d hours and %d minutes)") |
|
1937 |
h m)))) |
|
1938 |
|
|
1939 |
(defvar-local org-clock-overlays nil) |
|
1940 |
|
|
1941 |
(defun org-clock-put-overlay (time) |
|
1942 |
"Put an overlays on the current line, displaying TIME. |
|
1943 |
This creates a new overlay and stores it in `org-clock-overlays', so that it |
|
1944 |
will be easy to remove." |
|
1945 |
(let (ov tx) |
|
1946 |
(beginning-of-line) |
|
1947 |
(let ((case-fold-search nil)) |
|
1948 |
(when (looking-at org-complex-heading-regexp) |
|
1949 |
(goto-char (match-beginning 4)))) |
|
1950 |
(setq ov (make-overlay (point) (point-at-eol)) |
|
1951 |
tx (concat (buffer-substring-no-properties (point) (match-end 4)) |
|
1952 |
(org-add-props |
|
1953 |
(make-string |
|
1954 |
(max 0 (- (- 60 (current-column)) |
|
1955 |
(- (match-end 4) (match-beginning 4)) |
|
1956 |
(length (org-get-at-bol 'line-prefix)))) |
|
1957 |
?\·) |
|
1958 |
'(face shadow)) |
|
1959 |
(org-add-props |
|
1960 |
(format " %9s " (org-duration-from-minutes time)) |
|
1961 |
'(face org-clock-overlay)) |
|
1962 |
"")) |
|
1963 |
(overlay-put ov 'display tx) |
|
1964 |
(push ov org-clock-overlays))) |
|
1965 |
|
|
1966 |
;;;###autoload |
|
1967 |
(defun org-clock-remove-overlays (&optional _beg _end noremove) |
|
1968 |
"Remove the occur highlights from the buffer. |
|
1969 |
If NOREMOVE is nil, remove this function from the |
|
1970 |
`before-change-functions' in the current buffer." |
|
1971 |
(interactive) |
|
1972 |
(unless org-inhibit-highlight-removal |
|
1973 |
(mapc #'delete-overlay org-clock-overlays) |
|
1974 |
(setq org-clock-overlays nil) |
|
1975 |
(unless noremove |
|
1976 |
(remove-hook 'before-change-functions |
|
1977 |
'org-clock-remove-overlays 'local)))) |
|
1978 |
|
|
1979 |
(defvar org-state) ;; dynamically scoped into this function |
|
1980 |
(defun org-clock-out-if-current () |
|
1981 |
"Clock out if the current entry contains the running clock. |
|
1982 |
This is used to stop the clock after a TODO entry is marked DONE, |
|
1983 |
and is only done if the variable `org-clock-out-when-done' is not nil." |
|
1984 |
(when (and (org-clocking-p) |
|
1985 |
org-clock-out-when-done |
|
1986 |
(marker-buffer org-clock-marker) |
|
1987 |
(or (and (eq t org-clock-out-when-done) |
|
1988 |
(member org-state org-done-keywords)) |
|
1989 |
(and (listp org-clock-out-when-done) |
|
1990 |
(member org-state org-clock-out-when-done))) |
|
1991 |
(equal (or (buffer-base-buffer (org-clocking-buffer)) |
|
1992 |
(org-clocking-buffer)) |
|
1993 |
(or (buffer-base-buffer (current-buffer)) |
|
1994 |
(current-buffer))) |
|
1995 |
(< (point) org-clock-marker) |
|
1996 |
(> (save-excursion (outline-next-heading) (point)) |
|
1997 |
org-clock-marker)) |
|
1998 |
;; Clock out, but don't accept a logging message for this. |
|
1999 |
(let ((org-log-note-clock-out nil) |
|
2000 |
(org-clock-out-switch-to-state nil)) |
|
2001 |
(org-clock-out)))) |
|
2002 |
|
|
2003 |
(add-hook 'org-after-todo-state-change-hook |
|
2004 |
'org-clock-out-if-current) |
|
2005 |
|
|
2006 |
;;;###autoload |
|
2007 |
(defun org-clock-get-clocktable (&rest props) |
|
2008 |
"Get a formatted clocktable with parameters according to PROPS. |
|
2009 |
The table is created in a temporary buffer, fully formatted and |
|
2010 |
fontified, and then returned." |
|
2011 |
;; Set the defaults |
|
2012 |
(setq props (plist-put props :name "clocktable")) |
|
2013 |
(unless (plist-member props :maxlevel) |
|
2014 |
(setq props (plist-put props :maxlevel 2))) |
|
2015 |
(unless (plist-member props :scope) |
|
2016 |
(setq props (plist-put props :scope 'agenda))) |
|
2017 |
(with-temp-buffer |
|
2018 |
(org-mode) |
|
2019 |
(org-create-dblock props) |
|
2020 |
(org-update-dblock) |
|
2021 |
(org-font-lock-ensure) |
|
2022 |
(forward-line 2) |
|
2023 |
(buffer-substring (point) (progn |
|
2024 |
(re-search-forward "^[ \t]*#\\+END" nil t) |
|
2025 |
(point-at-bol))))) |
|
2026 |
|
|
2027 |
;;;###autoload |
|
2028 |
(defun org-clock-report (&optional arg) |
|
2029 |
"Update or create a table containing a report about clocked time. |
|
2030 |
|
|
2031 |
If point is inside an existing clocktable block, update it. |
|
2032 |
Otherwise, insert a new one. |
|
2033 |
|
|
2034 |
The new table inherits its properties from the variable |
|
2035 |
`org-clock-clocktable-default-properties'. The scope of the |
|
2036 |
clocktable, when not specified in the previous variable, is |
|
2037 |
`subtree' when the function is called from within a subtree, and |
|
2038 |
`file' elsewhere. |
|
2039 |
|
|
2040 |
When called with a prefix argument, move to the first clock table |
|
2041 |
in the buffer and update it." |
|
2042 |
(interactive "P") |
|
2043 |
(org-clock-remove-overlays) |
|
2044 |
(when arg |
|
2045 |
(org-find-dblock "clocktable") |
|
2046 |
(org-show-entry)) |
|
2047 |
(pcase (org-in-clocktable-p) |
|
2048 |
(`nil |
|
2049 |
(org-create-dblock |
|
2050 |
(org-combine-plists |
|
2051 |
(list :scope (if (org-before-first-heading-p) 'file 'subtree)) |
|
2052 |
org-clock-clocktable-default-properties |
|
2053 |
'(:name "clocktable")))) |
|
2054 |
(start (goto-char start))) |
|
2055 |
(org-update-dblock)) |
|
2056 |
|
|
2057 |
(defun org-day-of-week (day month year) |
|
2058 |
"Returns the day of the week as an integer." |
|
2059 |
(nth 6 |
|
2060 |
(decode-time |
|
2061 |
(date-to-time |
|
2062 |
(format "%d-%02d-%02dT00:00:00" year month day))))) |
|
2063 |
|
|
2064 |
(defun org-quarter-to-date (quarter year) |
|
2065 |
"Get the date (week day year) of the first day of a given quarter." |
|
2066 |
(let (startday) |
|
2067 |
(cond |
|
2068 |
((= quarter 1) |
|
2069 |
(setq startday (org-day-of-week 1 1 year)) |
|
2070 |
(cond |
|
2071 |
((= startday 0) |
|
2072 |
(list 52 7 (- year 1))) |
|
2073 |
((= startday 6) |
|
2074 |
(list 52 6 (- year 1))) |
|
2075 |
((<= startday 4) |
|
2076 |
(list 1 startday year)) |
|
2077 |
((> startday 4) |
|
2078 |
(list 53 startday (- year 1))) |
|
2079 |
) |
|
2080 |
) |
|
2081 |
((= quarter 2) |
|
2082 |
(setq startday (org-day-of-week 1 4 year)) |
|
2083 |
(cond |
|
2084 |
((= startday 0) |
|
2085 |
(list 13 startday year)) |
|
2086 |
((< startday 4) |
|
2087 |
(list 14 startday year)) |
|
2088 |
((>= startday 4) |
|
2089 |
(list 13 startday year)) |
|
2090 |
) |
|
2091 |
) |
|
2092 |
((= quarter 3) |
|
2093 |
(setq startday (org-day-of-week 1 7 year)) |
|
2094 |
(cond |
|
2095 |
((= startday 0) |
|
2096 |
(list 26 startday year)) |
|
2097 |
((< startday 4) |
|
2098 |
(list 27 startday year)) |
|
2099 |
((>= startday 4) |
|
2100 |
(list 26 startday year)) |
|
2101 |
) |
|
2102 |
) |
|
2103 |
((= quarter 4) |
|
2104 |
(setq startday (org-day-of-week 1 10 year)) |
|
2105 |
(cond |
|
2106 |
((= startday 0) |
|
2107 |
(list 39 startday year)) |
|
2108 |
((<= startday 4) |
|
2109 |
(list 40 startday year)) |
|
2110 |
((> startday 4) |
|
2111 |
(list 39 startday year))))))) |
|
2112 |
|
|
2113 |
(defun org-clock-special-range (key &optional time as-strings wstart mstart) |
|
2114 |
"Return two times bordering a special time range. |
|
2115 |
|
|
2116 |
KEY is a symbol specifying the range and can be one of `today', |
|
2117 |
`yesterday', `thisweek', `lastweek', `thismonth', `lastmonth', |
|
2118 |
`thisyear', `lastyear' or `untilnow'. If set to `interactive', |
|
2119 |
user is prompted for range boundaries. It can be a string or an |
|
2120 |
integer. |
|
2121 |
|
|
2122 |
By default, a week starts Monday 0:00 and ends Sunday 24:00. The |
|
2123 |
range is determined relative to TIME, which defaults to current |
|
2124 |
time. |
|
2125 |
|
|
2126 |
The return value is a list containing two internal times, one for |
|
2127 |
the beginning of the range and one for its end, like the ones |
|
2128 |
returned by `current time' or `encode-time' and a string used to |
|
2129 |
display information. If AS-STRINGS is non-nil, the returned |
|
2130 |
times will be formatted strings. |
|
2131 |
|
|
2132 |
If WSTART is non-nil, use this number to specify the starting day |
|
2133 |
of a week (monday is 1). If MSTART is non-nil, use this number |
|
2134 |
to specify the starting day of a month (1 is the first day of the |
|
2135 |
month). If you can combine both, the month starting day will |
|
2136 |
have priority." |
|
2137 |
(let* ((tm (decode-time time)) |
|
2138 |
(m (nth 1 tm)) |
|
2139 |
(h (nth 2 tm)) |
|
2140 |
(d (nth 3 tm)) |
|
2141 |
(month (nth 4 tm)) |
|
2142 |
(y (nth 5 tm)) |
|
2143 |
(dow (nth 6 tm)) |
|
2144 |
(skey (format "%s" key)) |
|
2145 |
(shift 0) |
|
2146 |
(q (cond ((>= month 10) 4) |
|
2147 |
((>= month 7) 3) |
|
2148 |
((>= month 4) 2) |
|
2149 |
(t 1))) |
|
2150 |
m1 h1 d1 month1 y1 shiftedy shiftedm shiftedq) |
|
2151 |
(cond |
|
2152 |
((string-match "\\`[0-9]+\\'" skey) |
|
2153 |
(setq y (string-to-number skey) month 1 d 1 key 'year)) |
|
2154 |
((string-match "\\`\\([0-9]+\\)-\\([0-9]\\{1,2\\}\\)\\'" skey) |
|
2155 |
(setq y (string-to-number (match-string 1 skey)) |
|
2156 |
month (string-to-number (match-string 2 skey)) |
|
2157 |
d 1 |
|
2158 |
key 'month)) |
|
2159 |
((string-match "\\`\\([0-9]+\\)-[wW]\\([0-9]\\{1,2\\}\\)\\'" skey) |
|
2160 |
(require 'cal-iso) |
|
2161 |
(let ((date (calendar-gregorian-from-absolute |
|
2162 |
(calendar-iso-to-absolute |
|
2163 |
(list (string-to-number (match-string 2 skey)) |
|
2164 |
1 |
|
2165 |
(string-to-number (match-string 1 skey))))))) |
|
2166 |
(setq d (nth 1 date) |
|
2167 |
month (car date) |
|
2168 |
y (nth 2 date) |
|
2169 |
dow 1 |
|
2170 |
key 'week))) |
|
2171 |
((string-match "\\`\\([0-9]+\\)-[qQ]\\([1-4]\\)\\'" skey) |
|
2172 |
(require 'cal-iso) |
|
2173 |
(setq q (string-to-number (match-string 2 skey))) |
|
2174 |
(let ((date (calendar-gregorian-from-absolute |
|
2175 |
(calendar-iso-to-absolute |
|
2176 |
(org-quarter-to-date |
|
2177 |
q (string-to-number (match-string 1 skey))))))) |
|
2178 |
(setq d (nth 1 date) |
|
2179 |
month (car date) |
|
2180 |
y (nth 2 date) |
|
2181 |
dow 1 |
|
2182 |
key 'quarter))) |
|
2183 |
((string-match |
|
2184 |
"\\`\\([0-9]+\\)-\\([0-9]\\{1,2\\}\\)-\\([0-9]\\{1,2\\}\\)\\'" |
|
2185 |
skey) |
|
2186 |
(setq y (string-to-number (match-string 1 skey)) |
|
2187 |
month (string-to-number (match-string 2 skey)) |
|
2188 |
d (string-to-number (match-string 3 skey)) |
|
2189 |
key 'day)) |
|
2190 |
((string-match "\\([-+][0-9]+\\)\\'" skey) |
|
2191 |
(setq shift (string-to-number (match-string 1 skey)) |
|
2192 |
key (intern (substring skey 0 (match-beginning 1)))) |
|
2193 |
(when (and (memq key '(quarter thisq)) (> shift 0)) |
|
2194 |
(error "Looking forward with quarters isn't implemented")))) |
|
2195 |
(when (= shift 0) |
|
2196 |
(pcase key |
|
2197 |
(`yesterday (setq key 'today shift -1)) |
|
2198 |
(`lastweek (setq key 'week shift -1)) |
|
2199 |
(`lastmonth (setq key 'month shift -1)) |
|
2200 |
(`lastyear (setq key 'year shift -1)) |
|
2201 |
(`lastq (setq key 'quarter shift -1)))) |
|
2202 |
;; Prepare start and end times depending on KEY's type. |
|
2203 |
(pcase key |
|
2204 |
((or `day `today) (setq m 0 h 0 h1 24 d (+ d shift))) |
|
2205 |
((or `week `thisweek) |
|
2206 |
(let* ((ws (or wstart 1)) |
|
2207 |
(diff (+ (* -7 shift) (if (= dow 0) (- 7 ws) (- dow ws))))) |
|
2208 |
(setq m 0 h 0 d (- d diff) d1 (+ 7 d)))) |
|
2209 |
((or `month `thismonth) |
|
2210 |
(setq h 0 m 0 d (or mstart 1) month (+ month shift) month1 (1+ month))) |
|
2211 |
((or `quarter `thisq) |
|
2212 |
;; Compute if this shift remains in this year. If not, compute |
|
2213 |
;; how many years and quarters we have to shift (via floor*) and |
|
2214 |
;; compute the shifted years, months and quarters. |
|
2215 |
(cond |
|
2216 |
((< (+ (- q 1) shift) 0) ; Shift not in this year. |
|
2217 |
(let* ((interval (* -1 (+ (- q 1) shift))) |
|
2218 |
;; Set tmp to ((years to shift) (quarters to shift)). |
|
2219 |
(tmp (cl-floor interval 4))) |
|
2220 |
;; Due to the use of floor, 0 quarters actually means 4. |
|
2221 |
(if (= 0 (nth 1 tmp)) |
|
2222 |
(setq shiftedy (- y (nth 0 tmp)) |
|
2223 |
shiftedm 1 |
|
2224 |
shiftedq 1) |
|
2225 |
(setq shiftedy (- y (+ 1 (nth 0 tmp))) |
|
2226 |
shiftedm (- 13 (* 3 (nth 1 tmp))) |
|
2227 |
shiftedq (- 5 (nth 1 tmp))))) |
|
2228 |
(setq m 0 h 0 d 1 month shiftedm month1 (+ 3 shiftedm) y shiftedy)) |
|
2229 |
((> (+ q shift) 0) ; Shift is within this year. |
|
2230 |
(setq shiftedq (+ q shift)) |
|
2231 |
(setq shiftedy y) |
|
2232 |
(let ((qshift (* 3 (1- (+ q shift))))) |
|
2233 |
(setq m 0 h 0 d 1 month (+ 1 qshift) month1 (+ 4 qshift)))))) |
|
2234 |
((or `year `thisyear) |
|
2235 |
(setq m 0 h 0 d 1 month 1 y (+ y shift) y1 (1+ y))) |
|
2236 |
((or `interactive `untilnow)) ; Special cases, ignore them. |
|
2237 |
(_ (user-error "No such time block %s" key))) |
|
2238 |
;; Format start and end times according to AS-STRINGS. |
|
2239 |
(let* ((start (pcase key |
|
2240 |
(`interactive (org-read-date nil t nil "Range start? ")) |
|
2241 |
;; In theory, all clocks started after the dawn of |
|
2242 |
;; humanity. |
|
2243 |
(`untilnow (encode-time 0 0 0 0 0 -50000)) |
|
2244 |
(_ (encode-time 0 m h d month y)))) |
|
2245 |
(end (pcase key |
|
2246 |
(`interactive (org-read-date nil t nil "Range end? ")) |
|
2247 |
(`untilnow (current-time)) |
|
2248 |
(_ (encode-time 0 |
|
2249 |
(or m1 m) |
|
2250 |
(or h1 h) |
|
2251 |
(or d1 d) |
|
2252 |
(or month1 month) |
|
2253 |
(or y1 y))))) |
|
2254 |
(text |
|
2255 |
(pcase key |
|
2256 |
((or `day `today) (format-time-string "%A, %B %d, %Y" start)) |
|
2257 |
((or `week `thisweek) (format-time-string "week %G-W%V" start)) |
|
2258 |
((or `month `thismonth) (format-time-string "%B %Y" start)) |
|
2259 |
((or `year `thisyear) (format-time-string "the year %Y" start)) |
|
2260 |
((or `quarter `thisq) |
|
2261 |
(concat (org-count-quarter shiftedq) |
|
2262 |
" quarter of " (number-to-string shiftedy))) |
|
2263 |
(`interactive "(Range interactively set)") |
|
2264 |
(`untilnow "now")))) |
|
2265 |
(if (not as-strings) (list start end text) |
|
2266 |
(let ((f (cdr org-time-stamp-formats))) |
|
2267 |
(list (format-time-string f start) |
|
2268 |
(format-time-string f end) |
|
2269 |
text)))))) |
|
2270 |
|
|
2271 |
(defun org-count-quarter (n) |
|
2272 |
(cond |
|
2273 |
((= n 1) "1st") |
|
2274 |
((= n 2) "2nd") |
|
2275 |
((= n 3) "3rd") |
|
2276 |
((= n 4) "4th"))) |
|
2277 |
|
|
2278 |
;;;###autoload |
|
2279 |
(defun org-clocktable-shift (dir n) |
|
2280 |
"Try to shift the :block date of the clocktable at point. |
|
2281 |
Point must be in the #+BEGIN: line of a clocktable, or this function |
|
2282 |
will throw an error. |
|
2283 |
DIR is a direction, a symbol `left', `right', `up', or `down'. |
|
2284 |
Both `left' and `down' shift the block toward the past, `up' and `right' |
|
2285 |
push it toward the future. |
|
2286 |
N is the number of shift steps to take. The size of the step depends on |
|
2287 |
the currently selected interval size." |
|
2288 |
(setq n (prefix-numeric-value n)) |
|
2289 |
(and (memq dir '(left down)) (setq n (- n))) |
|
2290 |
(save-excursion |
|
2291 |
(goto-char (point-at-bol)) |
|
2292 |
(if (not (looking-at "^[ \t]*#\\+BEGIN:[ \t]+clocktable\\>.*?:block[ \t]+\\(\\S-+\\)")) |
|
2293 |
(error "Line needs a :block definition before this command works") |
|
2294 |
(let* ((b (match-beginning 1)) (e (match-end 1)) |
|
2295 |
(s (match-string 1)) |
|
2296 |
block shift ins y mw d date wp m) |
|
2297 |
(cond |
|
2298 |
((equal s "yesterday") (setq s "today-1")) |
|
2299 |
((equal s "lastweek") (setq s "thisweek-1")) |
|
2300 |
((equal s "lastmonth") (setq s "thismonth-1")) |
|
2301 |
((equal s "lastyear") (setq s "thisyear-1")) |
|
2302 |
((equal s "lastq") (setq s "thisq-1"))) |
|
2303 |
|
|
2304 |
(cond |
|
2305 |
((string-match "^\\(today\\|thisweek\\|thismonth\\|thisyear\\|thisq\\)\\([-+][0-9]+\\)?$" s) |
|
2306 |
(setq block (match-string 1 s) |
|
2307 |
shift (if (match-end 2) |
|
2308 |
(string-to-number (match-string 2 s)) |
|
2309 |
0)) |
|
2310 |
(setq shift (+ shift n)) |
|
2311 |
(setq ins (if (= shift 0) block (format "%s%+d" block shift)))) |
|
2312 |
((string-match "\\([0-9]+\\)\\(-\\([wWqQ]?\\)\\([0-9]\\{1,2\\}\\)\\(-\\([0-9]\\{1,2\\}\\)\\)?\\)?" s) |
|
2313 |
;; 1 1 2 3 3 4 4 5 6 6 5 2 |
|
2314 |
(setq y (string-to-number (match-string 1 s)) |
|
2315 |
wp (and (match-end 3) (match-string 3 s)) |
|
2316 |
mw (and (match-end 4) (string-to-number (match-string 4 s))) |
|
2317 |
d (and (match-end 6) (string-to-number (match-string 6 s)))) |
|
2318 |
(cond |
|
2319 |
(d (setq ins (format-time-string |
|
2320 |
"%Y-%m-%d" |
|
2321 |
(encode-time 0 0 0 (+ d n) m y)))) |
|
2322 |
((and wp (string-match "w\\|W" wp) mw (> (length wp) 0)) |
|
2323 |
(require 'cal-iso) |
|
2324 |
(setq date (calendar-gregorian-from-absolute |
|
2325 |
(calendar-iso-to-absolute (list (+ mw n) 1 y)))) |
|
2326 |
(setq ins (format-time-string |
|
2327 |
"%G-W%V" |
|
2328 |
(encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date))))) |
|
2329 |
((and wp (string-match "q\\|Q" wp) mw (> (length wp) 0)) |
|
2330 |
(require 'cal-iso) |
|
2331 |
; if the 4th + 1 quarter is requested we flip to the 1st quarter of the next year |
|
2332 |
(if (> (+ mw n) 4) |
|
2333 |
(setq mw 0 |
|
2334 |
y (+ 1 y)) |
|
2335 |
()) |
|
2336 |
; if the 1st - 1 quarter is requested we flip to the 4th quarter of the previous year |
|
2337 |
(if (= (+ mw n) 0) |
|
2338 |
(setq mw 5 |
|
2339 |
y (- y 1)) |
|
2340 |
()) |
|
2341 |
(setq date (calendar-gregorian-from-absolute |
|
2342 |
(calendar-iso-to-absolute (org-quarter-to-date (+ mw n) y)))) |
|
2343 |
(setq ins (format-time-string |
|
2344 |
(concat (number-to-string y) "-Q" (number-to-string (+ mw n))) |
|
2345 |
(encode-time 0 0 0 (nth 1 date) (car date) (nth 2 date))))) |
|
2346 |
(mw |
|
2347 |
(setq ins (format-time-string |
|
2348 |
"%Y-%m" |
|
2349 |
(encode-time 0 0 0 1 (+ mw n) y)))) |
|
2350 |
(y |
|
2351 |
(setq ins (number-to-string (+ y n)))))) |
|
2352 |
(t (error "Cannot shift clocktable block"))) |
|
2353 |
(when ins |
|
2354 |
(goto-char b) |
|
2355 |
(insert ins) |
|
2356 |
(delete-region (point) (+ (point) (- e b))) |
|
2357 |
(beginning-of-line 1) |
|
2358 |
(org-update-dblock) |
|
2359 |
t))))) |
|
2360 |
|
|
2361 |
;;;###autoload |
|
2362 |
(defun org-dblock-write:clocktable (params) |
|
2363 |
"Write the standard clocktable." |
|
2364 |
(setq params (org-combine-plists org-clocktable-defaults params)) |
|
2365 |
(catch 'exit |
|
2366 |
(let* ((scope (plist-get params :scope)) |
|
2367 |
(files (pcase scope |
|
2368 |
(`agenda |
|
2369 |
(org-agenda-files t)) |
|
2370 |
(`agenda-with-archives |
|
2371 |
(org-add-archive-files (org-agenda-files t))) |
|
2372 |
(`file-with-archives |
|
2373 |
(and buffer-file-name |
|
2374 |
(org-add-archive-files (list buffer-file-name)))) |
|
2375 |
((pred functionp) (funcall scope)) |
|
2376 |
((pred consp) scope) |
|
2377 |
(_ (or (buffer-file-name) (current-buffer))))) |
|
2378 |
(block (plist-get params :block)) |
|
2379 |
(ts (plist-get params :tstart)) |
|
2380 |
(te (plist-get params :tend)) |
|
2381 |
(ws (plist-get params :wstart)) |
|
2382 |
(ms (plist-get params :mstart)) |
|
2383 |
(step (plist-get params :step)) |
|
2384 |
(formatter (or (plist-get params :formatter) |
|
2385 |
org-clock-clocktable-formatter |
|
2386 |
'org-clocktable-write-default)) |
|
2387 |
cc) |
|
2388 |
;; Check if we need to do steps |
|
2389 |
(when block |
|
2390 |
;; Get the range text for the header |
|
2391 |
(setq cc (org-clock-special-range block nil t ws ms) |
|
2392 |
ts (car cc) |
|
2393 |
te (nth 1 cc))) |
|
2394 |
(when step |
|
2395 |
;; Write many tables, in steps |
|
2396 |
(unless (or block (and ts te)) |
|
2397 |
(error "Clocktable `:step' can only be used with `:block' or `:tstart,:end'")) |
|
2398 |
(org-clocktable-steps params) |
|
2399 |
(throw 'exit nil)) |
|
2400 |
|
|
2401 |
(org-agenda-prepare-buffers (if (consp files) files (list files))) |
|
2402 |
|
|
2403 |
(let ((origin (point)) |
|
2404 |
(tables |
|
2405 |
(if (consp files) |
|
2406 |
(mapcar (lambda (file) |
|
2407 |
(with-current-buffer (find-buffer-visiting file) |
|
2408 |
(save-excursion |
|
2409 |
(save-restriction |
|
2410 |
(org-clock-get-table-data file params))))) |
|
2411 |
files) |
|
2412 |
;; Get the right restriction for the scope. |
|
2413 |
(save-restriction |
|
2414 |
(cond |
|
2415 |
((not scope)) ;use the restriction as it is now |
|
2416 |
((eq scope 'file) (widen)) |
|
2417 |
((eq scope 'subtree) (org-narrow-to-subtree)) |
|
2418 |
((eq scope 'tree) |
|
2419 |
(while (org-up-heading-safe)) |
|
2420 |
(org-narrow-to-subtree)) |
|
2421 |
((and (symbolp scope) |
|
2422 |
(string-match "\\`tree\\([0-9]+\\)\\'" |
|
2423 |
(symbol-name scope))) |
|
2424 |
(let ((level (string-to-number |
|
2425 |
(match-string 1 (symbol-name scope))))) |
|
2426 |
(catch 'exit |
|
2427 |
(while (org-up-heading-safe) |
|
2428 |
(looking-at org-outline-regexp) |
|
2429 |
(when (<= (org-reduced-level (funcall outline-level)) |
|
2430 |
level) |
|
2431 |
(throw 'exit nil)))) |
|
2432 |
(org-narrow-to-subtree)))) |
|
2433 |
(list (org-clock-get-table-data nil params))))) |
|
2434 |
(multifile |
|
2435 |
;; Even though `file-with-archives' can consist of |
|
2436 |
;; multiple files, we consider this is one extended file |
|
2437 |
;; instead. |
|
2438 |
(and (consp files) (not (eq scope 'file-with-archives))))) |
|
2439 |
|
|
2440 |
(funcall formatter |
|
2441 |
origin |
|
2442 |
tables |
|
2443 |
(org-combine-plists params `(:multifile ,multifile))))))) |
|
2444 |
|
|
2445 |
(defun org-clocktable-write-default (ipos tables params) |
|
2446 |
"Write out a clock table at position IPOS in the current buffer. |
|
2447 |
TABLES is a list of tables with clocking data as produced by |
|
2448 |
`org-clock-get-table-data'. PARAMS is the parameter property list obtained |
|
2449 |
from the dynamic block definition." |
|
2450 |
;; This function looks quite complicated, mainly because there are a |
|
2451 |
;; lot of options which can add or remove columns. I have massively |
|
2452 |
;; commented this function, the I hope it is understandable. If |
|
2453 |
;; someone wants to write their own special formatter, this maybe |
|
2454 |
;; much easier because there can be a fixed format with a |
|
2455 |
;; well-defined number of columns... |
|
2456 |
(let* ((lang (or (plist-get params :lang) "en")) |
|
2457 |
(multifile (plist-get params :multifile)) |
|
2458 |
(block (plist-get params :block)) |
|
2459 |
(sort (plist-get params :sort)) |
|
2460 |
(header (plist-get params :header)) |
|
2461 |
(link (plist-get params :link)) |
|
2462 |
(maxlevel (or (plist-get params :maxlevel) 3)) |
|
2463 |
(emph (plist-get params :emphasize)) |
|
2464 |
(compact? (plist-get params :compact)) |
|
2465 |
(narrow (or (plist-get params :narrow) (and compact? '40!))) |
|
2466 |
(level? (and (not compact?) (plist-get params :level))) |
|
2467 |
(timestamp (plist-get params :timestamp)) |
|
2468 |
(properties (plist-get params :properties)) |
|
2469 |
(time-columns |
|
2470 |
(if (or compact? (< maxlevel 2)) 1 |
|
2471 |
;; Deepest headline level is a hard limit for the number |
|
2472 |
;; of time columns. |
|
2473 |
(let ((levels |
|
2474 |
(cl-mapcan |
|
2475 |
(lambda (table) |
|
2476 |
(pcase table |
|
2477 |
(`(,_ ,(and (pred wholenump) (pred (/= 0))) ,entries) |
|
2478 |
(mapcar #'car entries)))) |
|
2479 |
tables))) |
|
2480 |
(min maxlevel |
|
2481 |
(or (plist-get params :tcolumns) 100) |
|
2482 |
(if (null levels) 1 (apply #'max levels)))))) |
|
2483 |
(indent (or compact? (plist-get params :indent))) |
|
2484 |
(formula (plist-get params :formula)) |
|
2485 |
(case-fold-search t) |
|
2486 |
(total-time (apply #'+ (mapcar #'cadr tables))) |
|
2487 |
recalc narrow-cut-p) |
|
2488 |
|
|
2489 |
(when (and narrow (integerp narrow) link) |
|
2490 |
;; We cannot have both integer narrow and link. |
|
2491 |
(message "Using hard narrowing in clocktable to allow for links") |
|
2492 |
(setq narrow (intern (format "%d!" narrow)))) |
|
2493 |
|
|
2494 |
(pcase narrow |
|
2495 |
((or `nil (pred integerp)) nil) ;nothing to do |
|
2496 |
((and (pred symbolp) |
|
2497 |
(guard (string-match-p "\\`[0-9]+!\\'" (symbol-name narrow)))) |
|
2498 |
(setq narrow-cut-p t) |
|
2499 |
(setq narrow (string-to-number (symbol-name narrow)))) |
|
2500 |
(_ (error "Invalid value %s of :narrow property in clock table" narrow))) |
|
2501 |
|
|
2502 |
;; Now we need to output this table stuff. |
|
2503 |
(goto-char ipos) |
|
2504 |
|
|
2505 |
;; Insert the text *before* the actual table. |
|
2506 |
(insert-before-markers |
|
2507 |
(or header |
|
2508 |
;; Format the standard header. |
|
2509 |
(format "#+CAPTION: %s %s%s\n" |
|
2510 |
(org-clock--translate "Clock summary at" lang) |
|
2511 |
(format-time-string (org-time-stamp-format t t)) |
|
2512 |
(if block |
|
2513 |
(let ((range-text |
|
2514 |
(nth 2 (org-clock-special-range |
|
2515 |
block nil t |
|
2516 |
(plist-get params :wstart) |
|
2517 |
(plist-get params :mstart))))) |
|
2518 |
(format ", for %s." range-text)) |
|
2519 |
"")))) |
|
2520 |
|
|
2521 |
;; Insert the narrowing line |
|
2522 |
(when (and narrow (integerp narrow) (not narrow-cut-p)) |
|
2523 |
(insert-before-markers |
|
2524 |
"|" ;table line starter |
|
2525 |
(if multifile "|" "") ;file column, maybe |
|
2526 |
(if level? "|" "") ;level column, maybe |
|
2527 |
(if timestamp "|" "") ;timestamp column, maybe |
|
2528 |
(if properties ;properties columns, maybe |
|
2529 |
(make-string (length properties) ?|) |
|
2530 |
"") |
|
2531 |
(format "<%d>| |\n" narrow))) ;headline and time columns |
|
2532 |
|
|
2533 |
;; Insert the table header line |
|
2534 |
(insert-before-markers |
|
2535 |
"|" ;table line starter |
|
2536 |
(if multifile ;file column, maybe |
|
2537 |
(concat (org-clock--translate "File" lang) "|") |
|
2538 |
"") |
|
2539 |
(if level? ;level column, maybe |
|
2540 |
(concat (org-clock--translate "L" lang) "|") |
|
2541 |
"") |
|
2542 |
(if timestamp ;timestamp column, maybe |
|
2543 |
(concat (org-clock--translate "Timestamp" lang) "|") |
|
2544 |
"") |
|
2545 |
(if properties ;properties columns, maybe |
|
2546 |
(concat (mapconcat #'identity properties "|") "|") |
|
2547 |
"") |
|
2548 |
(concat (org-clock--translate "Headline" lang)"|") |
|
2549 |
(concat (org-clock--translate "Time" lang) "|") |
|
2550 |
(make-string (max 0 (1- time-columns)) ?|) ;other time columns |
|
2551 |
(if (eq formula '%) "%|\n" "\n")) |
|
2552 |
|
|
2553 |
;; Insert the total time in the table |
|
2554 |
(insert-before-markers |
|
2555 |
"|-\n" ;a hline |
|
2556 |
"|" ;table line starter |
|
2557 |
(if multifile (format "| %s " (org-clock--translate "ALL" lang)) "") |
|
2558 |
;file column, maybe |
|
2559 |
(if level? "|" "") ;level column, maybe |
|
2560 |
(if timestamp "|" "") ;timestamp column, maybe |
|
2561 |
(make-string (length properties) ?|) ;properties columns, maybe |
|
2562 |
(concat (format org-clock-total-time-cell-format |
|
2563 |
(org-clock--translate "Total time" lang)) |
|
2564 |
"| ") |
|
2565 |
(format org-clock-total-time-cell-format |
|
2566 |
(org-duration-from-minutes (or total-time 0))) ;time |
|
2567 |
"|" |
|
2568 |
(make-string (max 0 (1- time-columns)) ?|) |
|
2569 |
(cond ((not (eq formula '%)) "") |
|
2570 |
((or (not total-time) (= total-time 0)) "0.0|") |
|
2571 |
(t "100.0|")) |
|
2572 |
"\n") |
|
2573 |
|
|
2574 |
;; Now iterate over the tables and insert the data but only if any |
|
2575 |
;; time has been collected. |
|
2576 |
(when (and total-time (> total-time 0)) |
|
2577 |
(pcase-dolist (`(,file-name ,file-time ,entries) tables) |
|
2578 |
(when (or (and file-time (> file-time 0)) |
|
2579 |
(not (plist-get params :fileskip0))) |
|
2580 |
(insert-before-markers "|-\n") ;hline at new file |
|
2581 |
;; First the file time, if we have multiple files. |
|
2582 |
(when multifile |
|
2583 |
;; Summarize the time collected from this file. |
|
2584 |
(insert-before-markers |
|
2585 |
(format (concat "| %s %s | %s%s" |
|
2586 |
(format org-clock-file-time-cell-format |
|
2587 |
(org-clock--translate "File time" lang)) |
|
2588 |
" | *%s*|\n") |
|
2589 |
(file-name-nondirectory file-name) |
|
2590 |
(if level? "| " "") ;level column, maybe |
|
2591 |
(if timestamp "| " "") ;timestamp column, maybe |
|
2592 |
(if properties ;properties columns, maybe |
|
2593 |
(make-string (length properties) ?|) |
|
2594 |
"") |
|
2595 |
(org-duration-from-minutes file-time)))) ;time |
|
2596 |
|
|
2597 |
;; Get the list of node entries and iterate over it |
|
2598 |
(when (> maxlevel 0) |
|
2599 |
(pcase-dolist (`(,level ,headline ,ts ,time ,props) entries) |
|
2600 |
(when narrow-cut-p |
|
2601 |
(setq headline |
|
2602 |
(if (and (string-match |
|
2603 |
(format "\\`%s\\'" org-bracket-link-regexp) |
|
2604 |
headline) |
|
2605 |
(match-end 3)) |
|
2606 |
(format "[[%s][%s]]" |
|
2607 |
(match-string 1 headline) |
|
2608 |
(org-shorten-string (match-string 3 headline) |
|
2609 |
narrow)) |
|
2610 |
(org-shorten-string headline narrow)))) |
|
2611 |
(cl-flet ((format-field (f) (format (cond ((not emph) "%s |") |
|
2612 |
((= level 1) "*%s* |") |
|
2613 |
((= level 2) "/%s/ |") |
|
2614 |
(t "%s |")) |
|
2615 |
f))) |
|
2616 |
(insert-before-markers |
|
2617 |
"|" ;start the table line |
|
2618 |
(if multifile "|" "") ;free space for file name column? |
|
2619 |
(if level? (format "%d|" level) "") ;level, maybe |
|
2620 |
(if timestamp (concat ts "|") "") ;timestamp, maybe |
|
2621 |
(if properties ;properties columns, maybe |
|
2622 |
(concat (mapconcat (lambda (p) (or (cdr (assoc p props)) "")) |
|
2623 |
properties |
|
2624 |
"|") |
|
2625 |
"|") |
|
2626 |
"") |
|
2627 |
(if indent ;indentation |
|
2628 |
(org-clocktable-indent-string level) |
|
2629 |
"") |
|
2630 |
(format-field headline) |
|
2631 |
;; Empty fields for higher levels. |
|
2632 |
(make-string (max 0 (1- (min time-columns level))) ?|) |
|
2633 |
(format-field (org-duration-from-minutes time)) |
|
2634 |
(make-string (max 0 (- time-columns level)) ?|) |
|
2635 |
(if (eq formula '%) |
|
2636 |
(format "%.1f |" (* 100 (/ time (float total-time)))) |
|
2637 |
"") |
|
2638 |
"\n"))))))) |
|
2639 |
(delete-char -1) |
|
2640 |
(cond |
|
2641 |
;; Possibly rescue old formula? |
|
2642 |
((or (not formula) (eq formula '%)) |
|
2643 |
(let ((contents (org-string-nw-p (plist-get params :content)))) |
|
2644 |
(when (and contents (string-match "^\\([ \t]*#\\+tblfm:.*\\)" contents)) |
|
2645 |
(setq recalc t) |
|
2646 |
(insert "\n" (match-string 1 contents)) |
|
2647 |
(beginning-of-line 0)))) |
|
2648 |
;; Insert specified formula line. |
|
2649 |
((stringp formula) |
|
2650 |
(insert "\n#+TBLFM: " formula) |
|
2651 |
(setq recalc t)) |
|
2652 |
(t |
|
2653 |
(user-error "Invalid :formula parameter in clocktable"))) |
|
2654 |
;; Back to beginning, align the table, recalculate if necessary. |
|
2655 |
(goto-char ipos) |
|
2656 |
(skip-chars-forward "^|") |
|
2657 |
(org-table-align) |
|
2658 |
(when org-hide-emphasis-markers |
|
2659 |
;; We need to align a second time. |
|
2660 |
(org-table-align)) |
|
2661 |
(when sort |
|
2662 |
(save-excursion |
|
2663 |
(org-table-goto-line 3) |
|
2664 |
(org-table-goto-column (car sort)) |
|
2665 |
(org-table-sort-lines nil (cdr sort)))) |
|
2666 |
(when recalc (org-table-recalculate 'all)) |
|
2667 |
total-time)) |
|
2668 |
|
|
2669 |
(defun org-clocktable-indent-string (level) |
|
2670 |
"Return indentation string according to LEVEL. |
|
2671 |
LEVEL is an integer. Indent by two spaces per level above 1." |
|
2672 |
(if (= level 1) "" |
|
2673 |
(concat "\\_" (make-string (* 2 (1- level)) ?\s)))) |
|
2674 |
|
|
2675 |
(defun org-clocktable-steps (params) |
|
2676 |
"Step through the range to make a number of clock tables." |
|
2677 |
(let* ((ts (plist-get params :tstart)) |
|
2678 |
(te (plist-get params :tend)) |
|
2679 |
(ws (plist-get params :wstart)) |
|
2680 |
(ms (plist-get params :mstart)) |
|
2681 |
(step0 (plist-get params :step)) |
|
2682 |
(step (cdr (assq step0 '((day . 86400) (week . 604800))))) |
|
2683 |
(stepskip0 (plist-get params :stepskip0)) |
|
2684 |
(block (plist-get params :block)) |
|
2685 |
cc tsb) |
|
2686 |
(when block |
|
2687 |
(setq cc (org-clock-special-range block nil t ws ms) |
|
2688 |
ts (car cc) |
|
2689 |
te (nth 1 cc))) |
|
2690 |
(cond |
|
2691 |
((numberp ts) |
|
2692 |
;; If ts is a number, it's an absolute day number from |
|
2693 |
;; org-agenda. |
|
2694 |
(pcase-let ((`(,month ,day ,year) (calendar-gregorian-from-absolute ts))) |
|
2695 |
(setq ts (float-time (encode-time 0 0 0 day month year))))) |
|
2696 |
(ts |
|
2697 |
(setq ts (float-time (apply #'encode-time (org-parse-time-string ts)))))) |
|
2698 |
(cond |
|
2699 |
((numberp te) |
|
2700 |
;; Likewise for te. |
|
2701 |
(pcase-let ((`(,month ,day ,year) (calendar-gregorian-from-absolute te))) |
|
2702 |
(setq te (float-time (encode-time 0 0 0 day month year))))) |
|
2703 |
(te |
|
2704 |
(setq te (float-time (apply #'encode-time (org-parse-time-string te)))))) |
|
2705 |
(setq tsb |
|
2706 |
(if (eq step0 'week) |
|
2707 |
(let ((dow (nth 6 (decode-time (seconds-to-time ts))))) |
|
2708 |
(if (<= dow ws) ts |
|
2709 |
(- ts (* 86400 (- dow ws))))) |
|
2710 |
ts)) |
|
2711 |
(while (< tsb te) |
|
2712 |
(unless (bolp) (insert "\n")) |
|
2713 |
(let ((start-time (seconds-to-time (max tsb ts)))) |
|
2714 |
(cl-incf tsb (let ((dow (nth 6 (decode-time (seconds-to-time tsb))))) |
|
2715 |
(if (or (eq step0 'day) |
|
2716 |
(= dow ws)) |
|
2717 |
step |
|
2718 |
(* 86400 (- ws dow))))) |
|
2719 |
(insert "\n" |
|
2720 |
(if (eq step0 'day) "Daily report: " |
|
2721 |
"Weekly report starting on: ") |
|
2722 |
(format-time-string (org-time-stamp-format nil t) start-time) |
|
2723 |
"\n") |
|
2724 |
(let ((table-begin (line-beginning-position 0)) |
|
2725 |
(step-time |
|
2726 |
(org-dblock-write:clocktable |
|
2727 |
(org-combine-plists |
|
2728 |
params |
|
2729 |
(list |
|
2730 |
:header "" :step nil :block nil |
|
2731 |
:tstart (format-time-string (org-time-stamp-format t t) |
|
2732 |
start-time) |
|
2733 |
:tend (format-time-string (org-time-stamp-format t t) |
|
2734 |
(seconds-to-time (min te tsb)))))))) |
|
2735 |
(re-search-forward "^[ \t]*#\\+END:") |
|
2736 |
(when (and stepskip0 (equal step-time 0)) |
|
2737 |
;; Remove the empty table |
|
2738 |
(delete-region (line-beginning-position) table-begin)))) |
|
2739 |
(end-of-line 0)))) |
|
2740 |
|
|
2741 |
(defun org-clock-get-table-data (file params) |
|
2742 |
"Get the clocktable data for file FILE, with parameters PARAMS. |
|
2743 |
FILE is only for identification - this function assumes that |
|
2744 |
the correct buffer is current, and that the wanted restriction is |
|
2745 |
in place. |
|
2746 |
The return value will be a list with the file name and the total |
|
2747 |
file time (in minutes) as 1st and 2nd elements. The third element |
|
2748 |
of this list will be a list of headline entries. Each entry has the |
|
2749 |
following structure: |
|
2750 |
|
|
2751 |
(LEVEL HEADLINE TIMESTAMP TIME PROPERTIES) |
|
2752 |
|
|
2753 |
LEVEL: The level of the headline, as an integer. This will be |
|
2754 |
the reduced level, so 1,2,3,... even if only odd levels |
|
2755 |
are being used. |
|
2756 |
HEADLINE: The text of the headline. Depending on PARAMS, this may |
|
2757 |
already be formatted like a link. |
|
2758 |
TIMESTAMP: If PARAMS require it, this will be a time stamp found in the |
|
2759 |
entry, any of SCHEDULED, DEADLINE, NORMAL, or first inactive, |
|
2760 |
in this sequence. |
|
2761 |
TIME: The sum of all time spend in this tree, in minutes. This time |
|
2762 |
will of cause be restricted to the time block and tags match |
|
2763 |
specified in PARAMS. |
|
2764 |
PROPERTIES: The list properties specified in the `:properties' parameter |
|
2765 |
along with their value, as an alist following the pattern |
|
2766 |
(NAME . VALUE)." |
|
2767 |
(let* ((maxlevel (or (plist-get params :maxlevel) 3)) |
|
2768 |
(timestamp (plist-get params :timestamp)) |
|
2769 |
(ts (plist-get params :tstart)) |
|
2770 |
(te (plist-get params :tend)) |
|
2771 |
(ws (plist-get params :wstart)) |
|
2772 |
(ms (plist-get params :mstart)) |
|
2773 |
(block (plist-get params :block)) |
|
2774 |
(link (plist-get params :link)) |
|
2775 |
(tags (plist-get params :tags)) |
|
2776 |
(properties (plist-get params :properties)) |
|
2777 |
(inherit-property-p (plist-get params :inherit-props)) |
|
2778 |
(matcher (and tags (cdr (org-make-tags-matcher tags)))) |
|
2779 |
cc st p tbl) |
|
2780 |
|
|
2781 |
(setq org-clock-file-total-minutes nil) |
|
2782 |
(when block |
|
2783 |
(setq cc (org-clock-special-range block nil t ws ms) |
|
2784 |
ts (car cc) |
|
2785 |
te (nth 1 cc))) |
|
2786 |
(when (integerp ts) (setq ts (calendar-gregorian-from-absolute ts))) |
|
2787 |
(when (integerp te) (setq te (calendar-gregorian-from-absolute te))) |
|
2788 |
(when (and ts (listp ts)) |
|
2789 |
(setq ts (format "%4d-%02d-%02d" (nth 2 ts) (car ts) (nth 1 ts)))) |
|
2790 |
(when (and te (listp te)) |
|
2791 |
(setq te (format "%4d-%02d-%02d" (nth 2 te) (car te) (nth 1 te)))) |
|
2792 |
;; Now the times are strings we can parse. |
|
2793 |
(if ts (setq ts (org-matcher-time ts))) |
|
2794 |
(if te (setq te (org-matcher-time te))) |
|
2795 |
(save-excursion |
|
2796 |
(org-clock-sum ts te |
|
2797 |
(when matcher |
|
2798 |
`(lambda () |
|
2799 |
(let* ((tags-list (org-get-tags-at)) |
|
2800 |
(org-scanner-tags tags-list) |
|
2801 |
(org-trust-scanner-tags t)) |
|
2802 |
(funcall ,matcher nil tags-list nil))))) |
|
2803 |
(goto-char (point-min)) |
|
2804 |
(setq st t) |
|
2805 |
(while (or (and (bobp) (prog1 st (setq st nil)) |
|
2806 |
(get-text-property (point) :org-clock-minutes) |
|
2807 |
(setq p (point-min))) |
|
2808 |
(setq p (next-single-property-change |
|
2809 |
(point) :org-clock-minutes))) |
|
2810 |
(goto-char p) |
|
2811 |
(let ((time (get-text-property p :org-clock-minutes))) |
|
2812 |
(when (and time (> time 0) (org-at-heading-p)) |
|
2813 |
(let ((level (org-reduced-level (org-current-level)))) |
|
2814 |
(when (<= level maxlevel) |
|
2815 |
(let* ((headline (org-get-heading t t t t)) |
|
2816 |
(hdl |
|
2817 |
(if (not link) headline |
|
2818 |
(let ((search |
|
2819 |
(org-make-org-heading-search-string headline))) |
|
2820 |
(org-make-link-string |
|
2821 |
(if (not (buffer-file-name)) search |
|
2822 |
(format "file:%s::%s" (buffer-file-name) search)) |
|
2823 |
;; Prune statistics cookies. Replace |
|
2824 |
;; links with their description, or |
|
2825 |
;; a plain link if there is none. |
|
2826 |
(org-trim |
|
2827 |
(org-link-display-format |
|
2828 |
(replace-regexp-in-string |
|
2829 |
"\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" "" |
|
2830 |
headline))))))) |
|
2831 |
(tsp |
|
2832 |
(and timestamp |
|
2833 |
(cl-some (lambda (p) (org-entry-get (point) p)) |
|
2834 |
'("SCHEDULED" "DEADLINE" "TIMESTAMP" |
|
2835 |
"TIMESTAMP_IA")))) |
|
2836 |
(props |
|
2837 |
(and properties |
|
2838 |
(delq nil |
|
2839 |
(mapcar |
|
2840 |
(lambda (p) |
|
2841 |
(let ((v (org-entry-get |
|
2842 |
(point) p inherit-property-p))) |
|
2843 |
(and v (cons p v)))) |
|
2844 |
properties))))) |
|
2845 |
(push (list level hdl tsp time props) tbl))))))) |
|
2846 |
(list file org-clock-file-total-minutes (nreverse tbl))))) |
|
2847 |
|
|
2848 |
;; Saving and loading the clock |
|
2849 |
|
|
2850 |
(defvar org-clock-loaded nil |
|
2851 |
"Was the clock file loaded?") |
|
2852 |
|
|
2853 |
;;;###autoload |
|
2854 |
(defun org-clock-update-time-maybe () |
|
2855 |
"If this is a CLOCK line, update it and return t. |
|
2856 |
Otherwise, return nil." |
|
2857 |
(interactive) |
|
2858 |
(save-excursion |
|
2859 |
(beginning-of-line 1) |
|
2860 |
(skip-chars-forward " \t") |
|
2861 |
(when (looking-at org-clock-string) |
|
2862 |
(let ((re (concat "[ \t]*" org-clock-string |
|
2863 |
" *[[<]\\([^]>]+\\)[]>]\\(-+[[<]\\([^]>]+\\)[]>]" |
|
2864 |
"\\([ \t]*=>.*\\)?\\)?")) |
|
2865 |
ts te h m s neg) |
|
2866 |
(cond |
|
2867 |
((not (looking-at re)) |
|
2868 |
nil) |
|
2869 |
((not (match-end 2)) |
|
2870 |
(when (and (equal (marker-buffer org-clock-marker) (current-buffer)) |
|
2871 |
(> org-clock-marker (point)) |
|
2872 |
(<= org-clock-marker (point-at-eol))) |
|
2873 |
;; The clock is running here |
|
2874 |
(setq org-clock-start-time |
|
2875 |
(apply 'encode-time |
|
2876 |
(org-parse-time-string (match-string 1)))) |
|
2877 |
(org-clock-update-mode-line))) |
|
2878 |
(t |
|
2879 |
(and (match-end 4) (delete-region (match-beginning 4) (match-end 4))) |
|
2880 |
(end-of-line 1) |
|
2881 |
(setq ts (match-string 1) |
|
2882 |
te (match-string 3)) |
|
2883 |
(setq s (- (float-time |
|
2884 |
(apply #'encode-time (org-parse-time-string te))) |
|
2885 |
(float-time |
|
2886 |
(apply #'encode-time (org-parse-time-string ts)))) |
|
2887 |
neg (< s 0) |
|
2888 |
s (abs s) |
|
2889 |
h (floor (/ s 3600)) |
|
2890 |
s (- s (* 3600 h)) |
|
2891 |
m (floor (/ s 60)) |
|
2892 |
s (- s (* 60 s))) |
|
2893 |
(insert " => " (format (if neg "-%d:%02d" "%2d:%02d") h m)) |
|
2894 |
t)))))) |
|
2895 |
|
|
2896 |
(defun org-clock-save () |
|
2897 |
"Persist various clock-related data to disk. |
|
2898 |
The details of what will be saved are regulated by the variable |
|
2899 |
`org-clock-persist'." |
|
2900 |
(when (and org-clock-persist |
|
2901 |
(or org-clock-loaded |
|
2902 |
org-clock-has-been-used |
|
2903 |
(not (file-exists-p org-clock-persist-file)))) |
|
2904 |
(with-temp-file org-clock-persist-file |
|
2905 |
(insert (format ";; %s - %s at %s\n" |
|
2906 |
(file-name-nondirectory org-clock-persist-file) |
|
2907 |
(system-name) |
|
2908 |
(format-time-string (org-time-stamp-format t)))) |
|
2909 |
;; Store clock to be resumed. |
|
2910 |
(when (and (memq org-clock-persist '(t clock)) |
|
2911 |
(let ((b (org-base-buffer (org-clocking-buffer)))) |
|
2912 |
(and (buffer-live-p b) |
|
2913 |
(buffer-file-name b) |
|
2914 |
(or (not org-clock-persist-query-save) |
|
2915 |
(y-or-n-p (format "Save current clock (%s) " |
|
2916 |
org-clock-heading)))))) |
|
2917 |
(insert |
|
2918 |
(format "(setq org-clock-stored-resume-clock '(%S . %d))\n" |
|
2919 |
(buffer-file-name (org-base-buffer (org-clocking-buffer))) |
|
2920 |
(marker-position org-clock-marker)))) |
|
2921 |
;; Store clocked task history. Tasks are stored reversed to |
|
2922 |
;; make reading simpler. |
|
2923 |
(when (and (memq org-clock-persist '(t history)) |
|
2924 |
org-clock-history) |
|
2925 |
(insert |
|
2926 |
(format "(setq org-clock-stored-history '(%s))\n" |
|
2927 |
(mapconcat |
|
2928 |
(lambda (m) |
|
2929 |
(let ((b (org-base-buffer (marker-buffer m)))) |
|
2930 |
(when (and (buffer-live-p b) |
|
2931 |
(buffer-file-name b)) |
|
2932 |
(format "(%S . %d)" |
|
2933 |
(buffer-file-name b) |
|
2934 |
(marker-position m))))) |
|
2935 |
(reverse org-clock-history) |
|
2936 |
" "))))))) |
|
2937 |
|
|
2938 |
(defun org-clock-load () |
|
2939 |
"Load clock-related data from disk, maybe resuming a stored clock." |
|
2940 |
(when (and org-clock-persist (not org-clock-loaded)) |
|
2941 |
(if (not (file-readable-p org-clock-persist-file)) |
|
2942 |
(message "Not restoring clock data; %S not found" org-clock-persist-file) |
|
2943 |
(message "Restoring clock data") |
|
2944 |
;; Load history. |
|
2945 |
(load-file org-clock-persist-file) |
|
2946 |
(setq org-clock-loaded t) |
|
2947 |
(pcase-dolist (`(,(and file (pred file-exists-p)) . ,position) |
|
2948 |
org-clock-stored-history) |
|
2949 |
(org-clock-history-push position (find-file-noselect file))) |
|
2950 |
;; Resume clock. |
|
2951 |
(pcase org-clock-stored-resume-clock |
|
2952 |
(`(,(and file (pred file-exists-p)) . ,position) |
|
2953 |
(with-current-buffer (find-file-noselect file) |
|
2954 |
(when (or (not org-clock-persist-query-resume) |
|
2955 |
(y-or-n-p (format "Resume clock (%s) " |
|
2956 |
(save-excursion |
|
2957 |
(goto-char position) |
|
2958 |
(org-get-heading t t))))) |
|
2959 |
(goto-char position) |
|
2960 |
(let ((org-clock-in-resume 'auto-restart) |
|
2961 |
(org-clock-auto-clock-resolution nil)) |
|
2962 |
(org-clock-in) |
|
2963 |
(when (org-invisible-p) (org-show-context)))))) |
|
2964 |
(_ nil))))) |
|
2965 |
|
|
2966 |
;; Suggested bindings |
|
2967 |
(org-defkey org-mode-map "\C-c\C-x\C-e" 'org-clock-modify-effort-estimate) |
|
2968 |
|
|
2969 |
(provide 'org-clock) |
|
2970 |
|
|
2971 |
;; Local variables: |
|
2972 |
;; generated-autoload-file: "org-loaddefs.el" |
|
2973 |
;; coding: utf-8 |
|
2974 |
;; End: |
|
2975 |
|
|
2976 |
;;; org-clock.el ends here |