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.el141
1 files changed, 112 insertions, 29 deletions
diff --git a/lisp/org/org-clock.el b/lisp/org/org-clock.el
index 06df2d49719..9efd99be826 100644
--- a/lisp/org/org-clock.el
+++ b/lisp/org/org-clock.el
@@ -35,11 +35,17 @@
(declare-function notifications-notify "notifications" (&rest params))
(declare-function org-element-property "org-element" (property element))
(declare-function org-element-type "org-element" (element))
+(declare-function org-inlinetask-at-task-p "org-inlinetask" ())
+(declare-function org-inlinetask-goto-beginning "org-inlinetask" ())
+(declare-function org-inlinetask-goto-end "org-inlinetask" ())
+(declare-function org-inlinetask-in-task-p "org-inlinetask" ())
(declare-function org-link-display-format "ol" (s))
(declare-function org-link-heading-search-string "ol" (&optional string))
(declare-function org-link-make-string "ol" (link &optional description))
(declare-function org-table-goto-line "org-table" (n))
(declare-function org-dynamic-block-define "org" (type func))
+(declare-function w32-notification-notify "w32fns.c" (&rest params))
+(declare-function w32-notification-close "w32fns.c" (&rest params))
(defvar org-frame-title-format-backup nil)
(defvar org-state)
@@ -273,6 +279,15 @@ also using the face `org-mode-line-clock-overrun'."
(const :tag "Just mark the time string" nil)
(string :tag "Text to prepend")))
+(defcustom org-show-notification-timeout 3
+ "Number of seconds to wait before closing Org notifications.
+This is applied to notifications sent with `notifications-notify'
+and `w32-notification-notify' only, not other mechanisms possibly
+set through `org-show-notification-handler'."
+ :group 'org-clock
+ :package-version '(Org . "9.4")
+ :type 'integer)
+
(defcustom org-show-notification-handler nil
"Function or program to send notification with.
The function or program will be called with the notification
@@ -457,6 +472,19 @@ Valid values are: `today', `yesterday', `thisweek', `lastweek',
(const :tag "Select range interactively" interactive))
:safe #'symbolp)
+(defcustom org-clock-auto-clockout-timer nil
+ "Timer for auto clocking out when Emacs is idle.
+When set to a number, auto clock out the currently clocked in
+task after this number of seconds of idle time.
+
+This is only effective when `org-clock-auto-clockout-insinuate'
+is added to the user configuration."
+ :group 'org-clock
+ :package-version '(Org . "9.4")
+ :type '(choice
+ (integer :tag "Clock out after Emacs is idle for X seconds")
+ (const :tag "Never auto clock out" nil)))
+
(defvar org-clock-in-prepare-hook nil
"Hook run when preparing the clock.
This hook is run before anything happens to the task that
@@ -698,7 +726,8 @@ If not, show simply the clocked time like 01:50."
(save-excursion
(let ((end (save-excursion (org-end-of-subtree))))
(when (re-search-forward (concat org-clock-string
- ".*\\]--\\(\\[[^]]+\\]\\)") end t)
+ ".*\\]--\\(\\[[^]]+\\]\\)")
+ end t)
(org-time-string-to-time (match-string 1))))))
(defun org-clock-update-mode-line (&optional refresh)
@@ -725,7 +754,8 @@ menu\nmouse-2 will jump to task"))
(setq org-mode-line-string
(concat (propertize
org-clock-task-overrun-text
- 'face 'org-mode-line-clock-overrun) org-mode-line-string)))
+ 'face 'org-mode-line-clock-overrun)
+ org-mode-line-string)))
(force-mode-line-update))
(defun org-clock-get-clocked-time ()
@@ -808,15 +838,26 @@ If PLAY-SOUND is non-nil, it overrides `org-clock-sound'."
"Show notification.
Use `org-show-notification-handler' if defined,
use libnotify if available, or fall back on a message."
+ (ignore-errors (require 'notifications))
(cond ((functionp org-show-notification-handler)
(funcall org-show-notification-handler notification))
((stringp org-show-notification-handler)
(start-process "emacs-timer-notification" nil
org-show-notification-handler notification))
+ ((fboundp 'w32-notification-notify)
+ (let ((id (w32-notification-notify
+ :title "Org mode message"
+ :body notification
+ :urgency 'low)))
+ (run-with-timer
+ org-show-notification-timeout
+ nil
+ (lambda () (w32-notification-close id)))))
((fboundp 'notifications-notify)
(notifications-notify
:title "Org mode message"
:body notification
+ :timeout (* org-show-notification-timeout 1000)
;; FIXME how to link to the Org icon?
;; :app-icon "~/.emacs.d/icons/mail.png"
:urgency 'low))
@@ -859,7 +900,8 @@ If CLOCK-SOUND is non-nil, it overrides `org-clock-sound'."
(goto-char (point-min))
(while (re-search-forward org-clock-re nil t)
(push (cons (copy-marker (match-end 1) t)
- (org-time-string-to-time (match-string 1))) clocks))))
+ (org-time-string-to-time (match-string 1)))
+ clocks))))
clocks))
(defsubst org-is-active-clock (clock)
@@ -983,7 +1025,7 @@ CLOCK is a cons cell of the form (MARKER START-TIME)."
(let ((element (org-element-at-point)))
(when (eq (org-element-type element) 'drawer)
(when (> (org-element-property :end element) (car clock))
- (org-flag-drawer nil element))
+ (org-hide-drawer-toggle 'off nil element))
(throw 'exit nil)))))))))))
(defun org-clock-resolve (clock &optional prompt-fn last-valid fail-quietly)
@@ -1022,6 +1064,9 @@ k/K Keep X minutes of the idle time (default is all). If this
that many minutes after the time that idling began, and then
clocked back in at the present time.
+t/T Like `k', but will ask you to specify a time (when you got
+ distracted away), instead of a number of minutes.
+
g/G Indicate that you \"got back\" X minutes ago. This is quite
different from `k': it clocks you out from the beginning of
the idle period and clock you back in X minutes ago.
@@ -1041,19 +1086,24 @@ to be CLOCKED OUT."))))
(while (or (null char-pressed)
(and (not (memq char-pressed
'(?k ?K ?g ?G ?s ?S ?C
- ?j ?J ?i ?q)))
+ ?j ?J ?i ?q ?t ?T)))
(or (ding) t)))
(setq char-pressed
(read-char (concat (funcall prompt-fn clock)
- " [jkKgGSscCiq]? ")
+ " [jkKtTgGSscCiq]? ")
nil 45)))
(and (not (memq char-pressed '(?i ?q))) char-pressed)))))
(default
(floor (org-time-convert-to-integer (org-time-since last-valid))
60))
(keep
- (and (memq ch '(?k ?K))
- (read-number "Keep how many minutes? " default)))
+ (or (and (memq ch '(?k ?K))
+ (read-number "Keep how many minutes? " default))
+ (and (memq ch '(?t ?T))
+ (floor
+ (/ (float-time
+ (org-time-subtract (org-read-date t t) last-valid))
+ 60)))))
(gotback
(and (memq ch '(?g ?G))
(read-number "Got back how many minutes ago? " default)))
@@ -1068,7 +1118,7 @@ to be CLOCKED OUT."))))
(org-clock-resolve-clock clock 'now nil t nil fail-quietly))
(org-clock-jump-to-current-clock clock))
((or (null ch)
- (not (memq ch '(?k ?K ?g ?G ?s ?S ?C))))
+ (not (memq ch '(?k ?K ?g ?G ?s ?S ?C ?t ?T))))
(message ""))
(t
(org-clock-resolve-clock
@@ -1092,7 +1142,7 @@ to be CLOCKED OUT."))))
(t
(error "Unexpected, please report this as a bug")))
(and gotback last-valid)
- (memq ch '(?K ?G ?S))
+ (memq ch '(?K ?G ?S ?T))
(and start-over
(not (memq ch '(?K ?G ?S ?C))))
fail-quietly)))))
@@ -1315,7 +1365,6 @@ the default behavior."
(t
(insert-before-markers "\n")
(backward-char 1)
- (org-indent-line)
(when (and (save-excursion
(end-of-line 0)
(org-in-item-p)))
@@ -1340,7 +1389,8 @@ the default behavior."
start-time
(org-current-time org-clock-rounding-minutes t)))
(setq ts (org-insert-time-stamp org-clock-start-time
- 'with-hm 'inactive))))
+ 'with-hm 'inactive))
+ (org-indent-line)))
(move-marker org-clock-marker (point) (buffer-base-buffer))
(move-marker org-clock-hd-marker
(save-excursion (org-back-to-heading t) (point))
@@ -1375,6 +1425,26 @@ the default behavior."
(message "Clock starts at %s - %s" ts org--msg-extra)
(run-hooks 'org-clock-in-hook))))))
+(defun org-clock-auto-clockout ()
+ "Clock out the currently clocked in task if Emacs is idle.
+See `org-clock-auto-clockout-timer' to set the idle time span.
+
+This is only effective when `org-clock-auto-clockout-insinuate'
+is present in the user configuration."
+ (when (and (numberp org-clock-auto-clockout-timer)
+ org-clock-current-task)
+ (run-with-idle-timer
+ org-clock-auto-clockout-timer nil #'org-clock-out)))
+
+;;;###autoload
+(defun org-clock-toggle-auto-clockout ()
+ (interactive)
+ (if (memq 'org-clock-auto-clockout org-clock-in-hook)
+ (progn (remove-hook 'org-clock-in-hook #'org-clock-auto-clockout)
+ (message "Auto clock-out after idle time turned off"))
+ (add-hook 'org-clock-in-hook #'org-clock-auto-clockout t)
+ (message "Auto clock-out after idle time turned on")))
+
;;;###autoload
(defun org-clock-in-last (&optional arg)
"Clock in the last closed clocked item.
@@ -1512,7 +1582,7 @@ line and position cursor in that line."
(insert ":" drawer ":\n:END:\n")
(org-indent-region beg (point))
(org-flag-region
- (line-end-position -1) (1- (point)) t 'org-hide-drawer)
+ (line-end-position -1) (1- (point)) t 'outline)
(forward-line -1))))
;; When a clock drawer needs to be created because of the
;; number of clock items or simply if it is missing, collect
@@ -1537,7 +1607,7 @@ line and position cursor in that line."
(let ((end (point-marker)))
(goto-char beg)
(save-excursion (insert ":" drawer ":\n"))
- (org-flag-region (line-end-position) (1- end) t 'org-hide-drawer)
+ (org-flag-region (line-end-position) (1- end) t 'outline)
(org-indent-region (point) end)
(forward-line)
(unless org-log-states-order-reversed
@@ -1579,7 +1649,7 @@ to, overriding the existing value of `org-clock-out-switch-to-state'."
org-clock-out-switch-to-state))
(now (org-current-time org-clock-rounding-minutes))
ts te s h m remove)
- (setq org-clock-out-time now)
+ (setq org-clock-out-time (or at-time now))
(save-excursion ; Do not replace this with `with-current-buffer'.
(with-no-warnings (set-buffer (org-clocking-buffer)))
(save-restriction
@@ -1724,7 +1794,7 @@ Optional argument N tells to change by that many units."
(delq 'org-mode-line-string global-mode-string))
(org-clock-restore-frame-title-format)
(force-mode-line-update)
- (error "No active clock"))
+ (user-error "No active clock"))
(save-excursion ; Do not replace this with `with-current-buffer'.
(with-no-warnings (set-buffer (org-clocking-buffer)))
(goto-char org-clock-marker)
@@ -1753,14 +1823,14 @@ With prefix arg SELECT, offer recently clocked tasks for selection."
(m (cond
(select
(or (org-clock-select-task "Select task to go to: ")
- (error "No task selected")))
+ (user-error "No task selected")))
((org-clocking-p) org-clock-marker)
((and org-clock-goto-may-find-recent-task
(car org-clock-history)
(marker-buffer (car org-clock-history)))
(setq recent t)
(car org-clock-history))
- (t (error "No active or recent clock task")))))
+ (t (user-error "No active or recent clock task")))))
(pop-to-buffer-same-window (marker-buffer m))
(if (or (< m (point-min)) (> m (point-max))) (widen))
(goto-char m)
@@ -1890,7 +1960,12 @@ PROPNAME lets you set a custom text property instead of :org-clock-minutes."
"Return time, clocked on current item in total."
(save-excursion
(save-restriction
- (org-narrow-to-subtree)
+ (if (and (featurep 'org-inlinetask)
+ (or (org-inlinetask-at-task-p)
+ (org-inlinetask-in-task-p)))
+ (narrow-to-region (save-excursion (org-inlinetask-goto-beginning) (point))
+ (save-excursion (org-inlinetask-goto-end) (point)))
+ (org-narrow-to-subtree))
(org-clock-sum tstart)
org-clock-file-total-minutes)))
@@ -2067,7 +2142,10 @@ in the buffer and update it."
(start (goto-char start)))
(org-update-dblock))
-(org-dynamic-block-define "clocktable" #'org-clock-report)
+;;;###autoload
+(eval-after-load 'org
+ '(progn
+ (org-dynamic-block-define "clocktable" #'org-clock-report)))
(defun org-day-of-week (day month year)
"Return the day of the week as an integer."
@@ -2310,7 +2388,7 @@ the currently selected interval size."
(save-excursion
(goto-char (point-at-bol))
(if (not (looking-at "^[ \t]*#\\+BEGIN:[ \t]+clocktable\\>.*?:block[ \t]+\\(\\S-+\\)"))
- (error "Line needs a :block definition before this command works")
+ (user-error "Line needs a :block definition before this command works")
(let* ((b (match-beginning 1)) (e (match-end 1))
(s (match-string 1))
block shift ins y mw d date wp m)
@@ -2369,7 +2447,7 @@ the currently selected interval size."
(encode-time 0 0 0 1 (+ mw n) y))))
(y
(setq ins (number-to-string (+ y n))))))
- (t (error "Cannot shift clocktable block")))
+ (t (user-error "Cannot shift clocktable block")))
(when ins
(goto-char b)
(insert ins)
@@ -2384,20 +2462,21 @@ the currently selected interval size."
(setq params (org-combine-plists org-clocktable-defaults params))
(catch 'exit
(let* ((scope (plist-get params :scope))
+ (base-buffer (org-base-buffer (current-buffer)))
(files (pcase scope
(`agenda
(org-agenda-files t))
(`agenda-with-archives
(org-add-archive-files (org-agenda-files t)))
(`file-with-archives
- (and buffer-file-name
- (org-add-archive-files (list buffer-file-name))))
+ (let ((base-file (buffer-file-name base-buffer)))
+ (and base-file
+ (org-add-archive-files (list base-file)))))
((or `nil `file `subtree `tree
(and (pred symbolp)
(guard (string-match "\\`tree\\([0-9]+\\)\\'"
(symbol-name scope)))))
- (or (buffer-file-name (buffer-base-buffer))
- (current-buffer)))
+ base-buffer)
((pred functionp) (funcall scope))
((pred consp) scope)
(_ (user-error "Unknown scope: %S" scope))))
@@ -2421,7 +2500,7 @@ the currently selected interval size."
(when step
;; Write many tables, in steps
(unless (or block (and ts te))
- (error "Clocktable `:step' can only be used with `:block' or `:tstart,:end'"))
+ (user-error "Clocktable `:step' can only be used with `:block' or `:tstart, :end'"))
(org-clocktable-steps params)
(throw 'exit nil))
@@ -2527,7 +2606,7 @@ from the dynamic block definition."
(guard (string-match-p "\\`[0-9]+!\\'" (symbol-name narrow))))
(setq narrow-cut-p t)
(setq narrow (string-to-number (symbol-name narrow))))
- (_ (error "Invalid value %s of :narrow property in clock table" narrow)))
+ (_ (user-error "Invalid value %s of :narrow property in clock table" narrow)))
;; Now we need to output this table stuff.
(goto-char ipos)
@@ -2718,6 +2797,7 @@ a number of clock tables."
(pcase step
(`day "Daily report: ")
(`week "Weekly report starting on: ")
+ (`semimonth "Semimonthly report starting on: ")
(`month "Monthly report starting on: ")
(`year "Annual report starting on: ")
(_ (user-error "Unknown `:step' specification: %S" step))))
@@ -2767,6 +2847,9 @@ a number of clock tables."
(let ((offset (if (= dow week-start) 7
(mod (- week-start dow) 7))))
(list 0 0 org-extend-today-until (+ d offset) m y)))
+ (`semimonth (list 0 0 0
+ (if (< d 16) 16 1)
+ (if (< d 16) m (1+ m)) y))
(`month (list 0 0 0 month-start (1+ m) y))
(`year (list 0 0 org-extend-today-until 1 1 (1+ y)))))))
(table-begin (line-beginning-position 0))
@@ -2883,7 +2966,7 @@ PROPERTIES: The list properties specified in the `:properties' parameter
(org-trim
(org-link-display-format
(replace-regexp-in-string
- "\\[[0-9]+%\\]\\|\\[[0-9]+/[0-9]+\\]" ""
+ "\\[[0-9]*\\(?:%\\|/[0-9]*\\)\\]" ""
headline)))))))
(tgs (and tags (org-get-tags)))
(tsp