summaryrefslogtreecommitdiff
path: root/lisp/org/org-clock.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/org/org-clock.el')
-rw-r--r--lisp/org/org-clock.el255
1 files changed, 118 insertions, 137 deletions
diff --git a/lisp/org/org-clock.el b/lisp/org/org-clock.el
index 4c5fcc64b0f..ff32e28d1e8 100644
--- a/lisp/org/org-clock.el
+++ b/lisp/org/org-clock.el
@@ -4,7 +4,7 @@
;; Author: Carsten Dominik <carsten at orgmode dot org>
;; Keywords: outlines, hypermedia, calendar, wp
-;; Homepage: http://orgmode.org
+;; Homepage: https://orgmode.org
;;
;; This file is part of GNU Emacs.
;;
@@ -430,7 +430,9 @@ This applies when using `org-clock-goto'."
:type 'integer)
(defcustom org-clock-display-default-range 'thisyear
- "Default range when displaying clocks with `org-clock-display'."
+ "Default range when displaying clocks with `org-clock-display'.
+Valid values are: `today', `yesterday', `thisweek', `lastweek',
+`thismonth', `lastmonth', `thisyear', `lastyear' and `untilnow'."
:group 'org-clock
:type '(choice (const today)
(const yesterday)
@@ -441,7 +443,8 @@ This applies when using `org-clock-goto'."
(const thisyear)
(const lastyear)
(const untilnow)
- (const :tag "Select range interactively" interactive)))
+ (const :tag "Select range interactively" interactive))
+ :safe #'symbolp)
(defvar org-clock-in-prepare-hook nil
"Hook run when preparing the clock.
@@ -465,38 +468,6 @@ to add an effort property.")
(defvar org-clock-stored-resume-clock nil
"Clock to resume, saved by `org-clock-load'")
-(defconst org-clock--oldest-date
- (let* ((dichotomy
- (lambda (min max pred)
- (if (funcall pred min) min
- (cl-incf min)
- (while (> (- max min) 1)
- (let ((mean (+ (ash min -1) (ash max -1) (logand min max 1))))
- (if (funcall pred mean) (setq max mean) (setq min mean)))))
- max))
- (high
- (funcall dichotomy
- most-negative-fixnum
- 0
- (lambda (m)
- ;; libc in macOS 10.6 hangs when decoding times
- ;; around year -2**31. Limit `high' not to go
- ;; any earlier than that.
- (unless (and (eq system-type 'darwin)
- (string-match-p
- "10\\.6\\.[[:digit:]]"
- (shell-command-to-string
- "sw_vers -productVersion"))
- (<= m -1034058203135))
- (ignore-errors (decode-time (list m 0)))))))
- (low
- (funcall dichotomy
- most-negative-fixnum
- 0
- (lambda (m) (ignore-errors (decode-time (list high m)))))))
- (list high low))
- "Internal time for oldest date representable on the system.")
-
;;; The clock for measuring work time.
(defvar org-mode-line-string "")
@@ -542,6 +513,15 @@ cannot be translated."
(assoc-string language org-clock-clocktable-language-setup t))
s))
+(defun org-clock--mode-line-heading ()
+ "Return currently clocked heading, formatted for mode line."
+ (cond ((functionp org-clock-heading-function)
+ (funcall org-clock-heading-function))
+ ((org-before-first-heading-p) "???")
+ (t (replace-regexp-in-string
+ org-bracket-link-analytic-regexp "\\5"
+ (org-no-properties (org-get-heading t t t t))))))
+
(defun org-clock-menu ()
(interactive)
(popup-menu
@@ -690,19 +670,18 @@ If not, show simply the clocked time like 01:50."
(if org-clock-effort
(let* ((effort-in-minutes (org-duration-to-minutes org-clock-effort))
(work-done-str
- (propertize
- (org-duration-from-minutes clocked-time)
- 'face (if (and org-clock-task-overrun (not org-clock-task-overrun-text))
- 'org-mode-line-clock-overrun 'org-mode-line-clock)))
- (effort-str (org-duration-from-minutes effort-in-minutes))
- (clockstr (propertize
- (concat " [%s/" effort-str
- "] (" (replace-regexp-in-string "%" "%%" org-clock-heading) ")")
- 'face 'org-mode-line-clock)))
- (format clockstr work-done-str))
- (propertize (concat " [" (org-duration-from-minutes clocked-time)
- "]" (format " (%s)" org-clock-heading))
- 'face 'org-mode-line-clock))))
+ (propertize (org-duration-from-minutes clocked-time)
+ 'face
+ (if (and org-clock-task-overrun
+ (not org-clock-task-overrun-text))
+ 'org-mode-line-clock-overrun
+ 'org-mode-line-clock)))
+ (effort-str (org-duration-from-minutes effort-in-minutes)))
+ (format (propertize " [%s/%s] (%s)" 'face 'org-mode-line-clock)
+ work-done-str effort-str org-clock-heading))
+ (format (propertize " [%s] (%s)" 'face 'org-mode-line-clock)
+ (org-duration-from-minutes clocked-time)
+ org-clock-heading))))
(defun org-clock-get-last-clock-out-time ()
"Get the last clock-out time for the current subtree."
@@ -712,10 +691,13 @@ If not, show simply the clocked time like 01:50."
".*\\]--\\(\\[[^]]+\\]\\)") end t)
(org-time-string-to-time (match-string 1))))))
-(defun org-clock-update-mode-line ()
+(defun org-clock-update-mode-line (&optional refresh)
+ "Update mode line with clock information.
+When optional argument is non-nil, refresh cached heading."
(if org-clock-effort
(org-clock-notify-once-if-expired)
(setq org-clock-task-overrun nil))
+ (when refresh (setq org-clock-heading (org-clock--mode-line-heading)))
(setq org-mode-line-string
(propertize
(let ((clock-string (org-clock-get-clock-string))
@@ -1232,7 +1214,8 @@ the default behavior."
(when (equal select '(64))
;; Set start-time to `org-clock-out-time'
(let ((org-clock-continuously t))
- (org-clock-in nil org-clock-out-time)))
+ (org-clock-in nil org-clock-out-time)
+ (throw 'abort nil)))
(when (equal select '(4))
(setq selected-task (org-clock-select-task "Clock-in on task: "))
@@ -1298,15 +1281,7 @@ the default behavior."
org-clock-in-switch-to-state
"\\>"))))
(org-todo org-clock-in-switch-to-state)))
- (setq org-clock-heading
- (cond ((and org-clock-heading-function
- (functionp org-clock-heading-function))
- (funcall org-clock-heading-function))
- ((nth 4 (org-heading-components))
- (replace-regexp-in-string
- "\\[\\[.*?\\]\\[\\(.*?\\)\\]\\]" "\\1"
- (match-string-no-properties 4)))
- (t "???")))
+ (setq org-clock-heading (org-clock--mode-line-heading))
(org-clock-find-position org-clock-in-resume)
(cond
((and org-clock-in-resume
@@ -1442,7 +1417,7 @@ in particular CLOCK_MODELINE_TOTAL and the corresponding variable
`org-clock-mode-line-total' and then decides which time to use.
The time is always returned as UTC."
- (let ((cmt (or (org-entry-get nil "CLOCK_MODELINE_TOTAL")
+ (let ((cmt (or (org-entry-get nil "CLOCK_MODELINE_TOTAL" 'selective)
(symbol-name org-clock-mode-line-total)))
(lr (org-entry-get nil "LAST_REPEAT")))
(cond
@@ -1451,7 +1426,7 @@ The time is always returned as UTC."
(current-time))
((equal cmt "today")
(setq org--msg-extra "showing today's task time.")
- (let* ((dt (org-decode-time nil t))
+ (let* ((dt (decode-time))
(hour (nth 2 dt))
(day (nth 3 dt)))
(if (< hour org-extend-today-until) (setf (nth 3 dt) (1- day)))
@@ -1784,6 +1759,7 @@ With prefix arg SELECT, offer recently clocked tasks for selection."
(defvar-local org-clock-file-total-minutes nil
"Holds the file total time in minutes, after a call to `org-clock-sum'.")
+;;;###autoload
(defun org-clock-sum-today (&optional headline-filter)
"Sum the times for each subtree for today."
(let ((range (org-clock-special-range 'today)))
@@ -1929,37 +1905,36 @@ Use `\\[org-clock-remove-overlays]' to remove the subtree times."
(prop (cond ((not arg) :org-clock-minutes-default)
(todayp :org-clock-minutes-today)
(customp :org-clock-minutes-custom)
- (t :org-clock-minutes)))
- time h m p)
+ (t :org-clock-minutes))))
(cond ((not arg) (org-clock-sum-custom
nil org-clock-display-default-range prop))
(todayp (org-clock-sum-today))
(customp (org-clock-sum-custom nil arg))
(t (org-clock-sum)))
- (unless (eq arg '(64))
+ (unless (equal arg '(64))
(save-excursion
(goto-char (point-min))
- (while (or (and (equal (setq p (point)) (point-min))
- (get-text-property p prop))
- (setq p (next-single-property-change
- (point) prop)))
- (goto-char p)
- (when (setq time (get-text-property p prop))
- (org-clock-put-overlay time)))
- (setq h (/ org-clock-file-total-minutes 60)
- m (- org-clock-file-total-minutes (* 60 h)))
+ (let ((p nil))
+ (while (or (and (equal (setq p (point)) (point-min))
+ (get-text-property p prop))
+ (setq p (next-single-property-change (point) prop)))
+ (goto-char p)
+ (let ((time (get-text-property p prop)))
+ (when time (org-clock-put-overlay time)))))
;; Arrange to remove the overlays upon next change.
(when org-remove-highlights-with-change
(add-hook 'before-change-functions 'org-clock-remove-overlays
- nil 'local))))
- (message (concat (format "Total file time%s: "
- (cond (todayp " for today")
- (customp " (custom)")
- (t "")))
- (org-duration-from-minutes
- org-clock-file-total-minutes)
- " (%d hours and %d minutes)")
- h m)))
+ nil 'local))))
+ (let* ((h (/ org-clock-file-total-minutes 60))
+ (m (- org-clock-file-total-minutes (* 60 h))))
+ (message (concat (format "Total file time%s: "
+ (cond (todayp " for today")
+ (customp " (custom)")
+ (t "")))
+ (org-duration-from-minutes
+ org-clock-file-total-minutes)
+ " (%d hours and %d minutes)")
+ h m))))
(defvar-local org-clock-overlays nil)
@@ -2051,27 +2026,32 @@ fontified, and then returned."
;;;###autoload
(defun org-clock-report (&optional arg)
- "Create a table containing a report about clocked time.
-If the cursor is inside an existing clocktable block, then the table
-will be updated. If not, a new clocktable will be inserted. The scope
-of the new clock will be subtree when called from within a subtree, and
-file elsewhere.
-
-When called with a prefix argument, move to the first clock table in the
-buffer and update it."
+ "Update or create a table containing a report about clocked time.
+
+If point is inside an existing clocktable block, update it.
+Otherwise, insert a new one.
+
+The new table inherits its properties from the variable
+`org-clock-clocktable-default-properties'. The scope of the
+clocktable, when not specified in the previous variable, is
+`subtree' when the function is called from within a subtree, and
+`file' elsewhere.
+
+When called with a prefix argument, move to the first clock table
+in the buffer and update it."
(interactive "P")
(org-clock-remove-overlays)
(when arg
(org-find-dblock "clocktable")
(org-show-entry))
- (if (org-in-clocktable-p)
- (goto-char (org-in-clocktable-p))
- (let ((props (if (ignore-errors
- (save-excursion (org-back-to-heading)))
- (list :name "clocktable" :scope 'subtree)
- (list :name "clocktable"))))
- (org-create-dblock
- (org-combine-plists org-clock-clocktable-default-properties props))))
+ (pcase (org-in-clocktable-p)
+ (`nil
+ (org-create-dblock
+ (org-combine-plists
+ (list :scope (if (org-before-first-heading-p) 'file 'subtree))
+ org-clock-clocktable-default-properties
+ '(:name "clocktable"))))
+ (start (goto-char start)))
(org-update-dblock))
(defun org-day-of-week (day month year)
@@ -2258,7 +2238,9 @@ have priority."
;; Format start and end times according to AS-STRINGS.
(let* ((start (pcase key
(`interactive (org-read-date nil t nil "Range start? "))
- (`untilnow org-clock--oldest-date)
+ ;; In theory, all clocks started after the dawn of
+ ;; humanity.
+ (`untilnow (encode-time 0 0 0 0 0 -50000))
(_ (encode-time 0 m h d month y))))
(end (pcase key
(`interactive (org-read-date nil t nil "Range end? "))
@@ -2692,16 +2674,15 @@ LEVEL is an integer. Indent by two spaces per level above 1."
(defun org-clocktable-steps (params)
"Step through the range to make a number of clock tables."
- (let* ((p1 (copy-sequence params))
- (ts (plist-get p1 :tstart))
- (te (plist-get p1 :tend))
- (ws (plist-get p1 :wstart))
- (ms (plist-get p1 :mstart))
- (step0 (plist-get p1 :step))
- (step (cdr (assoc step0 '((day . 86400) (week . 604800)))))
- (stepskip0 (plist-get p1 :stepskip0))
- (block (plist-get p1 :block))
- cc step-time tsb)
+ (let* ((ts (plist-get params :tstart))
+ (te (plist-get params :tend))
+ (ws (plist-get params :wstart))
+ (ms (plist-get params :mstart))
+ (step0 (plist-get params :step))
+ (step (cdr (assq step0 '((day . 86400) (week . 604800)))))
+ (stepskip0 (plist-get params :stepskip0))
+ (block (plist-get params :block))
+ cc tsb)
(when block
(setq cc (org-clock-special-range block nil t ws ms)
ts (car cc)
@@ -2724,37 +2705,37 @@ LEVEL is an integer. Indent by two spaces per level above 1."
(setq tsb
(if (eq step0 'week)
(let ((dow (nth 6 (decode-time (seconds-to-time ts)))))
- (if (< dow ws) ts
+ (if (<= dow ws) ts
(- ts (* 86400 (- dow ws)))))
ts))
- (setq p1 (plist-put p1 :header ""))
- (setq p1 (plist-put p1 :step nil))
- (setq p1 (plist-put p1 :block nil))
(while (< tsb te)
- (or (bolp) (insert "\n"))
- (setq p1 (plist-put p1 :tstart (format-time-string
- (org-time-stamp-format nil t)
- (seconds-to-time (max tsb ts)))))
- (cl-incf tsb (let ((dow (nth 6 (decode-time (seconds-to-time tsb)))))
- (if (or (eq step0 'day)
- (= dow ws))
- step
- (* 86400 (- ws dow)))))
- (setq p1 (plist-put p1 :tend (format-time-string
- (org-time-stamp-format nil t)
- (seconds-to-time (min te tsb)))))
- (insert "\n" (if (eq step0 'day) "Daily report: "
- "Weekly report starting on: ")
- (plist-get p1 :tstart) "\n")
- (setq step-time (org-dblock-write:clocktable p1))
- (re-search-forward "^[ \t]*#\\+END:")
- (when (and (equal step-time 0) stepskip0)
- ;; Remove the empty table
- (delete-region (point-at-bol)
- (save-excursion
- (re-search-backward "^\\(Daily\\|Weekly\\) report"
- nil t)
- (point))))
+ (unless (bolp) (insert "\n"))
+ (let ((start-time (seconds-to-time (max tsb ts))))
+ (cl-incf tsb (let ((dow (nth 6 (decode-time (seconds-to-time tsb)))))
+ (if (or (eq step0 'day)
+ (= dow ws))
+ step
+ (* 86400 (- ws dow)))))
+ (insert "\n"
+ (if (eq step0 'day) "Daily report: "
+ "Weekly report starting on: ")
+ (format-time-string (org-time-stamp-format nil t) start-time)
+ "\n")
+ (let ((table-begin (line-beginning-position 0))
+ (step-time
+ (org-dblock-write:clocktable
+ (org-combine-plists
+ params
+ (list
+ :header "" :step nil :block nil
+ :tstart (format-time-string (org-time-stamp-format t t)
+ start-time)
+ :tend (format-time-string (org-time-stamp-format t t)
+ (seconds-to-time (min te tsb))))))))
+ (re-search-forward "^[ \t]*#\\+END:")
+ (when (and stepskip0 (equal step-time 0))
+ ;; Remove the empty table
+ (delete-region (line-beginning-position) table-begin))))
(end-of-line 0))))
(defun org-clock-get-table-data (file params)