]> code.delx.au - gnu-emacs/blobdiff - lisp/org/org-clock.el
2008-11-23 Carsten Dominik <carsten.dominik@gmail.com>
[gnu-emacs] / lisp / org / org-clock.el
index d54a490047669410244e7ce983981510f492012e..82064e0cdb02061c56976ad70e3dbc9ccafcf191 100644 (file)
@@ -5,7 +5,7 @@
 ;; Author: Carsten Dominik <carsten at orgmode dot org>
 ;; Keywords: outlines, hypermedia, calendar, wp
 ;; Homepage: http://orgmode.org
-;; Version: 6.12a
+;; Version: 6.13
 ;;
 ;; This file is part of GNU Emacs.
 ;;
@@ -104,9 +104,19 @@ has not been closed, resume the clock from that point"
 
 (defcustom org-clock-persist nil
   "When non-nil, save the running clock when emacs is closed, and
-  resume it next time emacs is started."
+  resume it next time emacs is started.
+When this is t, both the running clock, and the entire clock
+history are saved.  When this is the symbol `clock', only the
+running clock is saved.
+
+When Emacs restarts with saved clock information, the file containing the
+running clock as well as all files mentioned in the clock history will
+be visited."
   :group 'org-clock
-  :type 'boolean)
+  :type '(choice
+         (const :tag "Just the running clock" clock)
+         (const :tag "Clock and history" t)
+         (const :tag "No persistence" nil)))
 
 (defcustom org-clock-persist-file "~/.emacs.d/org-clock-save.el"
   "File to save clock data to"
@@ -246,7 +256,7 @@ of a different task.")
                             'help-echo (concat help-text ": " org-clock-heading))
               (org-propertize clock-string 'help-echo help-text)))
           'local-map org-clock-mode-map
-          'mouse-face '(face mode-line-highlight)))
+          'mouse-face (if (featurep 'xemacs) 'highlight 'mode-line-highlight)))
     (force-mode-line-update)))
 
 (defvar org-clock-mode-line-entry nil
@@ -260,88 +270,102 @@ clock into.  When SELECT is `C-u C-u', clock into the current task and mark
 is as the default task, a special task that will always be offered in
 the clocking selection, associated with the letter `d'."
   (interactive "P")
-  (let ((interrupting (marker-buffer org-clock-marker))
-       ts selected-task target-pos)
-    (when (equal select '(4))
-      (setq selected-task (org-clock-select-task "Clock-in on task: "))
-      (if selected-task
-         (setq selected-task (copy-marker selected-task))
-       (error "Abort")))
-    (when interrupting
-      ;; We are interrupting the clocking of a differnt task.
-      ;; Save a marker to this task, so that we can go back.
-      (move-marker org-clock-interrupted-task
-                  (marker-position org-clock-marker)
-                  (marker-buffer org-clock-marker))
-      (org-clock-out t))
-    
-    (when (equal select '(16))
-      ;; Mark as default clocking task
-      (save-excursion
-       (org-back-to-heading t)
-       (move-marker org-clock-default-task (point))))
-    
-    (setq target-pos (point))  ;; we want to clock in at this location
-    (save-excursion
-      (when (and selected-task (marker-buffer selected-task))
-       ;; There is a selected task, move to the correct buffer
-       ;; and set the new target position.
-       (set-buffer (org-base-buffer (marker-buffer selected-task)))
-       (setq target-pos (marker-position selected-task))
-       (move-marker selected-task nil))
-      (save-excursion
-       (save-restriction
-         (widen)
-         (goto-char target-pos)
+  (catch 'abort
+    (let ((interrupting (marker-buffer org-clock-marker))
+         ts selected-task target-pos)
+      (when (equal select '(4))
+       (setq selected-task (org-clock-select-task "Clock-in on task: "))
+       (if selected-task
+           (setq selected-task (copy-marker selected-task))
+         (error "Abort")))
+      (when interrupting
+       ;; We are interrupting the clocking of a differnt task.
+       ;; Save a marker to this task, so that we can go back.
+       (move-marker org-clock-interrupted-task
+                    (marker-position org-clock-marker)
+                    (marker-buffer org-clock-marker))
+       (org-clock-out t))
+      
+      (when (equal select '(16))
+       ;; Mark as default clocking task
+       (save-excursion
          (org-back-to-heading t)
-         (or interrupting (move-marker org-clock-interrupted-task nil))
-         (org-clock-history-push)
-         (cond ((functionp org-clock-in-switch-to-state)
-                (looking-at org-complex-heading-regexp)
-                (let ((newstate (funcall org-clock-in-switch-to-state (match-string 2))))
-                  (if newstate (org-todo newstate))))
-               ((and org-clock-in-switch-to-state
-                     (not (looking-at (concat outline-regexp "[ \t]*"
-                                              org-clock-in-switch-to-state
-                                              "\\>"))))
-                (org-todo org-clock-in-switch-to-state)))
-         (setq org-clock-heading-for-remember
-               (and (looking-at org-complex-heading-regexp)
-                    (match-end 4)
-                    (org-trim (buffer-substring (match-end 1) (match-end 4)))))
-         (setq org-clock-heading
-               (cond ((and org-clock-heading-function
-                           (functionp org-clock-heading-function))
-                      (funcall org-clock-heading-function))
-                     ((looking-at org-complex-heading-regexp)
-                      (match-string 4))
-                     (t "???")))
-         (setq org-clock-heading (org-propertize org-clock-heading 'face nil))
-         (org-clock-find-position)
-         (if (and org-clock-in-resume
-                  (looking-at (concat "^[ \\t]* " org-clock-string
-                                      " \\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}"
-                                      " +\\sw+ +[012][0-9]:[0-5][0-9]\\)\\]$")))
-             (progn (message "Matched %s" (match-string 1))
-                    (setq ts (concat "[" (match-string 1) "]"))
-                    (goto-char (match-end 1))
-                    (setq org-clock-start-time
-                          (apply 'encode-time (org-parse-time-string (match-string 1)))))
-           (progn
+         (move-marker org-clock-default-task (point))))
+      
+      (setq target-pos (point))  ;; we want to clock in at this location
+      (save-excursion
+       (when (and selected-task (marker-buffer selected-task))
+         ;; There is a selected task, move to the correct buffer
+         ;; and set the new target position.
+         (set-buffer (org-base-buffer (marker-buffer selected-task)))
+         (setq target-pos (marker-position selected-task))
+         (move-marker selected-task nil))
+       (save-excursion
+         (save-restriction
+           (widen)
+           (goto-char target-pos)
+           (org-back-to-heading t)
+           (or interrupting (move-marker org-clock-interrupted-task nil))
+           (org-clock-history-push)
+           (cond ((functionp org-clock-in-switch-to-state)
+                  (looking-at org-complex-heading-regexp)
+                  (let ((newstate (funcall org-clock-in-switch-to-state
+                                           (match-string 2))))
+                    (if newstate (org-todo newstate))))
+                 ((and org-clock-in-switch-to-state
+                       (not (looking-at (concat outline-regexp "[ \t]*"
+                                                org-clock-in-switch-to-state
+                                                "\\>"))))
+                  (org-todo org-clock-in-switch-to-state)))
+           (setq org-clock-heading-for-remember
+                 (and (looking-at org-complex-heading-regexp)
+                      (match-end 4)
+                      (org-trim (buffer-substring (match-end 1)
+                                                  (match-end 4)))))
+           (setq org-clock-heading
+                 (cond ((and org-clock-heading-function
+                             (functionp org-clock-heading-function))
+                        (funcall org-clock-heading-function))
+                       ((looking-at org-complex-heading-regexp)
+                        (match-string 4))
+                       (t "???")))
+           (setq org-clock-heading (org-propertize org-clock-heading
+                                                   'face nil))
+           (org-clock-find-position)
+           (cond
+            ((and org-clock-in-resume
+                  (looking-at
+                   (concat "^[ \\t]* " org-clock-string
+                           " \\[\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}"
+                           " +\\sw+ +[012][0-9]:[0-5][0-9]\\)\\][ \t]*$")))
+             (message "Matched %s" (match-string 1))
+             (setq ts (concat "[" (match-string 1) "]"))
+             (goto-char (match-end 1))
+             (setq org-clock-start-time
+                   (apply 'encode-time
+                          (org-parse-time-string (match-string 1)))))
+            ((eq org-clock-in-resume 'auto-restart)
+             ;; called from org-clock-load during startup,
+             ;; do not interrupt, but warn!
+             (message "Cannot restart clock because task does not contain unfinished clock")
+             (ding)
+             (sit-for 2)
+             (throw 'abort nil))
+            (t
              (insert "\n") (backward-char 1)
              (org-indent-line-function)
              (insert org-clock-string " ")
              (setq org-clock-start-time (current-time))
              (setq ts (org-insert-time-stamp org-clock-start-time 'with-hm 'inactive))))
-         (move-marker org-clock-marker (point) (buffer-base-buffer))
-         (or global-mode-string (setq global-mode-string '("")))
-         (or (memq 'org-mode-line-string global-mode-string)
-             (setq global-mode-string
-                   (append global-mode-string '(org-mode-line-string))))
-         (org-update-mode-line)
-         (setq org-mode-line-timer
-               (run-with-timer 60 60 'org-update-mode-line))
-         (message "Clock started at %s" ts))))))
+           (move-marker org-clock-marker (point) (buffer-base-buffer))
+           (or global-mode-string (setq global-mode-string '("")))
+           (or (memq 'org-mode-line-string global-mode-string)
+               (setq global-mode-string
+                     (append global-mode-string '(org-mode-line-string))))
+           (org-update-mode-line)
+           (setq org-mode-line-timer
+                 (run-with-timer 60 60 'org-update-mode-line))
+           (message "Clock started at %s" ts)))))))
 
 (defun org-clock-find-position ()
   "Find the location where the next clock line should be inserted."
@@ -820,7 +844,7 @@ the currently selected interval size."
           (block (plist-get params :block))
           (link (plist-get params :link))
           ipos time p level hlc hdl content recalc formula pcol
-          cc beg end pos tbl tbl1 range-text rm-file-column scope-is-list)
+          cc beg end pos tbl tbl1 range-text rm-file-column scope-is-list st)
       (setq org-clock-file-total-minutes nil)
       (when step
        (unless (or block (and ts te))
@@ -902,7 +926,11 @@ the currently selected interval size."
        (unless scope-is-list
          (org-clock-sum ts te)
          (goto-char (point-min))
-         (while (setq p (next-single-property-change (point) :org-clock-minutes))
+         (setq st t)
+         (while (or (and (bobp) (prog1 st (setq st nil))
+                         (get-text-property (point) :org-clock-minutes)
+                         (setq p (point-min)))
+                    (setq p (next-single-property-change (point) :org-clock-minutes)))
            (goto-char p)
            (when (setq time (get-text-property p :org-clock-minutes))
              (save-excursion
@@ -1062,71 +1090,92 @@ This function is made for clock tables."
        0))))
 
 (defun org-clock-save ()
-  "Persist various clock-related data to disk"
-  (with-current-buffer (find-file (expand-file-name org-clock-persist-file))
-    (progn (delete-region (point-min) (point-max))
-          ;;Store clock
-          (insert (format ";; org-persist.el - %s at %s\n"
-                          system-name (format-time-string
-                                       (cdr org-time-stamp-formats))))
-          (if (and org-clock-persist (marker-buffer org-clock-marker)
-                   (or (not org-clock-persist-query-save)
-                       (y-or-n-p (concat "Save current clock ("
-                                         (substring-no-properties org-clock-heading)
-                                         ")"))))
-              (insert "(setq resume-clock '(\""
-                      (buffer-file-name (marker-buffer org-clock-marker))
-                      "\" . " (int-to-string (marker-position org-clock-marker))
-                      "))\n"))
-          ;;Store clocked task history. Tasks are stored reversed to make
-          ;;reading simpler
-          (if org-clock-history
-              (insert "(setq stored-clock-history '("
-                      (mapconcat
-                       (lambda (m)
-                         (when (marker-buffer m)
-                           (concat "(\"" (buffer-file-name (marker-buffer m))
-                                   "\" . " (int-to-string (marker-position m))
-                               ")")))
-                       (reverse org-clock-history) " ") "))\n"))
-          (save-buffer)
-          (kill-buffer (current-buffer)))))
-
-(defvar org-clock-loaded nil)
+  "Persist various clock-related data to disk.
+The details of what will be saved are regulated by the variable
+`org-clock-persist'."
+  (when org-clock-persist
+    (let (b)
+      (with-current-buffer (find-file (expand-file-name org-clock-persist-file))
+       (progn
+         (delete-region (point-min) (point-max))
+         ;;Store clock
+         (insert (format ";; org-persist.el - %s at %s\n"
+                         system-name (format-time-string
+                                      (cdr org-time-stamp-formats))))
+         (if (and (setq b (marker-buffer org-clock-marker))
+                  (setq b (or (buffer-base-buffer b) b))
+                  (buffer-live-p b)
+                  (buffer-file-name b)
+                  (or (not org-clock-persist-query-save)
+                      (y-or-n-p (concat "Save current clock ("
+                                        (substring-no-properties org-clock-heading)
+                                        ") "))))
+             (insert "(setq resume-clock '(\""
+                     (buffer-file-name (marker-buffer org-clock-marker))
+                     "\" . " (int-to-string (marker-position org-clock-marker))
+                     "))\n"))
+         ;; Store clocked task history. Tasks are stored reversed to make
+         ;; reading simpler
+         (when (and org-clock-history (eq org-clock-persist t))
+           (insert
+            "(setq stored-clock-history '("
+            (mapconcat
+             (lambda (m)
+               (when (and (setq b (marker-buffer m))
+                          (setq b (or (buffer-base-buffer b) b))
+                          (buffer-live-p b)
+                          (buffer-file-name b))
+                 (concat "(\"" (buffer-file-name b)
+                         "\" . " (int-to-string (marker-position m))
+                         ")")))
+             (reverse org-clock-history) " ") "))\n"))
+         (save-buffer)
+         (kill-buffer (current-buffer)))))))
+
+(defvar org-clock-loaded nil
+  "Was the clock file loaded?")
 
 (defun org-clock-load ()
   "Load various clock-related data from disk, optionally resuming
 a stored clock"
-  (if (not org-clock-loaded)
-      (let ((filename (expand-file-name org-clock-persist-file))
-           (org-clock-in-resume t))
-       (if (file-readable-p filename)
-           (progn
-             (message "%s" "Restoring clock data")
-             (setq org-clock-loaded t)
-             (load-file filename)
-             ;; load history
-             (if (boundp 'stored-clock-history)
-                 (save-window-excursion
-                   (mapc (lambda (task)
-                           (org-clock-history-push (cdr task)
-                                                   (find-file (car task))))
-                         stored-clock-history)))
-             ;; resume clock
-             (if (and (boundp 'resume-clock) org-clock-persist
-                      (or (not org-clock-persist-query-resume)
-                          (y-or-n-p 
-                           (concat
-                            "Resume clock ("
-                            (with-current-buffer (find-file (car resume-clock))
-                              (progn (goto-char (cdr resume-clock))
-                                     (looking-at org-complex-heading-regexp)
-                                     (match-string 4))) ")"))))
-                 (with-current-buffer (find-file (car resume-clock))
-                   (progn (goto-char (cdr resume-clock))
-                          (org-clock-in)))))
+  (when (and org-clock-persist (not org-clock-loaded))
+    (let ((filename (expand-file-name org-clock-persist-file))
+         (org-clock-in-resume 'auto-restart)
+         resume-clock stored-clock-history)
+      (if (not (file-readable-p filename))
          (message "Not restoring clock data; %s not found"
-                  org-clock-persist-file)))))
+                  org-clock-persist-file)
+       (message "%s" "Restoring clock data")
+       (setq org-clock-loaded t)
+       (load-file filename)
+       ;; load history
+       (when stored-clock-history
+         (save-window-excursion
+           (mapc (lambda (task)
+                   (if (file-exists-p (car task))
+                       (org-clock-history-push (cdr task)
+                                               (find-file (car task)))))
+                 stored-clock-history)))
+       ;; resume clock
+       (when (and resume-clock org-clock-persist
+                  (file-exists-p (car resume-clock))
+                  (or (not org-clock-persist-query-resume)
+                      (y-or-n-p 
+                       (concat
+                        "Resume clock ("
+                        (with-current-buffer (find-file (car resume-clock))
+                          (save-excursion
+                            (goto-char (cdr resume-clock))
+                            (org-back-to-heading t)
+                            (and (looking-at org-complex-heading-regexp)
+                                 (match-string 4))))
+                        ") "))))
+         (when (file-exists-p (car resume-clock))
+           (with-current-buffer (find-file (car resume-clock))
+             (goto-char (cdr resume-clock))
+             (org-clock-in)
+             (if (org-invisible-p)
+                 (org-show-context)))))))))
 
 ;;;###autoload
 (defun org-clock-persistence-insinuate ()