瀏覽代碼

Merge commit 'johnw/master'

Carsten Dominik 15 年之前
父節點
當前提交
1ad1de18be
共有 3 個文件被更改,包括 190 次插入2 次删除
  1. 59 0
      lisp/ChangeLog
  2. 110 2
      lisp/org-clock.el
  3. 21 0
      lisp/org.el

+ 59 - 0
lisp/ChangeLog

@@ -1,3 +1,62 @@
+2009-10-17  John Wiegley  <johnw@newartisans.com>
+
+	* org-clock.el (org-clock-idle-time): New user customizable option
+	for detecting whether the user has left a clock idle.  Note: it is
+	only used in this commit to test whether it's worthwhile to check
+	OS X to get the Mac user's current idle time.  If the Emacs idle
+	time is less than the value, the user hasn't been away long enough
+	to be worth checking (a more expensive test than just getting
+	Emacs idle time).
+	(org-user-idle-seconds, org-mac-idle-seconds)
+	(org-emacs-idle-seconds): This three functions, in conjunction
+	with the user customization variable `org-clock-idle-time', return
+	the number of seconds (as a floating point) that the user has been
+	away from their Emacs (or, if running on OS X, their computer).
+
+	* org-clock.el (org-find-open-clocks): New function that returns a
+	list of all open clocks in the given FILE.  Note that each clock
+	it returns is a cons cell of the format (MARKER . START-TIME).
+	This "clock" value is used by several of the new clock module
+	utility functions.
+	(org-is-active-clock): New inline function which tests whether the
+	given clock value is the same as the currently active clock.
+	Returns non-nil if this is the case.
+	(org-with-clock-position): New macro that evaluates FORMS with
+	point in the buffer and at the position of the given clock.
+	Changes to the current clock are global.
+	(org-with-clock): New macro that evaluates FORMS with point in the
+	buffer and at the position of the given clock.  However, changes
+	to the current clock are local and have no effect on the user's
+	active clock.  This allows, for example, far any clock to be
+	cancelled without cancelling the active clock.
+	(org-clock-clock-in): New inline function that switches the active
+	clock to the given clock.  If either the argument RESUME, or the
+	global `org-clock-in-resume', are non-nil, it will resume a clock
+	that was previously left open.
+	(org-clock-clock-out): New inline function that clocks out the
+	given clock value without affecting the currently active clock.
+	(org-clock-clock-cancel): New inline function that cancels the
+	given clock value without affecting the currently active clock.
+
+	* org-clock.el (org-clock-in): Before creating
+	`org-clock-mode-line-timer', check to make sure an older timer is
+	not currently running.
+	(org-clock-out): Accept new third parameter `at-time', which
+	permits a clock to be clocked out at a specific time.  Note that
+	no attempt is made to verify that the clock out time is later than
+	the clock in time.
+
+	* org.el (org-files-list): New utility function for returning a
+	list of all open org-mode buffers, plus all files used to build
+	the agenda buffer.  Note that not all the files will necessarily
+	be visited by a buffer at time of call.
+	(org-entry-beginning-position): Like the function
+	`line-beginning-position', this inline function returns the
+	beginning position of the current heading/entry.
+	(org-entry-end-position): Like the function `line-end-position',
+	this inline function returns the end position of the current
+	heading/entry.
+
 2009-10-16  Carsten Dominik  <carsten.dominik@gmail.com>
 
 	* org-agenda.el (org-agenda-list): Mark the all-todo items line as

+ 110 - 2
lisp/org-clock.el

@@ -206,6 +206,13 @@ string as argument."
   :group 'org-clock
   :type 'plist)
 
+(defcustom org-clock-idle-time nil
+  "When non-nil, resolve open clocks if the user is idle more than X minutes."
+  :group 'org-clock
+  :type '(choice
+	  (const :tag "Never" nil)
+	  (integer :tag "After N minutes")))
+
 
 (defvar org-clock-in-prepare-hook nil
   "Hook run when preparing the clock.
@@ -495,6 +502,103 @@ Use alsa's aplay tool if available."
 (defvar org-clock-mode-line-entry nil
   "Information for the modeline about the running clock.")
 
+(defun org-find-open-clocks (file)
+  "Search through the given file and find all open clocks."
+  (let ((buf (or (get-file-buffer file)
+		 (find-file-noselect file)))
+	clocks)
+    (with-current-buffer buf
+      (save-excursion
+	(goto-char (point-min))
+	(while (re-search-forward "CLOCK: \\(\\[.*?\\]\\)$" nil t)
+	  (push (cons (copy-marker (1- (match-end 1)) t)
+		      (org-time-string-to-time (match-string 1))) clocks))))
+    clocks))
+
+(defsubst org-is-active-clock (clock)
+  "Return t if CLOCK is the currently active clock."
+  (and (org-clock-is-active)
+       (= org-clock-marker (car clock))))
+
+(defmacro org-with-clock-position (clock &rest forms)
+  "Evaluate FORMS with CLOCK as the current active clock."
+  `(with-current-buffer (marker-buffer (car ,clock))
+     (save-excursion
+       (save-restriction
+	 (widen)
+	 (goto-char (car ,clock))
+	 (beginning-of-line)
+	 ,@forms))))
+
+(put 'org-with-clock-position 'lisp-indent-function 1)
+
+(defmacro org-with-clock (clock &rest forms)
+  "Evaluate FORMS with CLOCK as the current active clock.
+This macro also protects the current active clock from being altered."
+  `(org-with-clock-position ,clock
+     (let ((org-clock-start-time (cdr ,clock))
+	   (org-clock-total-time)
+	   (org-clock-history)
+	   (org-clock-effort)
+	   (org-clock-marker (car ,clock))
+	   (org-clock-hd-marker (save-excursion
+				  (outline-back-to-heading t)
+				  (point-marker))))
+       ,@forms)))
+
+(put 'org-with-clock 'lisp-indent-function 1)
+
+(defsubst org-clock-clock-in (clock &optional resume)
+  "Clock in to the clock located by CLOCK.
+If necessary, clock-out of the currently active clock."
+  (org-with-clock-position clock
+    (let ((org-clock-in-resume (or resume org-clock-in-resume)))
+      (org-clock-in))))
+
+(defsubst org-clock-clock-out (clock &optional fail-quietly at-time)
+  "Clock out of the clock located by CLOCK."
+  (let ((temp (copy-marker (car clock)
+			   (marker-insertion-type (car clock)))))
+    (if (org-is-active-clock clock)
+	(org-clock-out fail-quietly at-time)
+      (org-with-clock clock
+	(org-clock-out fail-quietly at-time)))
+    (setcar clock temp)))
+
+(defsubst org-clock-clock-cancel (clock)
+  "Cancel the clock located by CLOCK."
+  (let ((temp (copy-marker (car clock)
+			   (marker-insertion-type (car clock)))))
+    (if (org-is-active-clock clock)
+	(org-clock-cancel)
+      (org-with-clock clock
+	(org-clock-cancel)))
+    (setcar clock temp)))
+
+(defun org-emacs-idle-seconds ()
+  "Return the current Emacs idle time in seconds, or nil if not idle."
+  (let ((idle-time (current-idle-time)))
+    (if idle-time
+	(time-to-seconds idle-time)
+      0)))
+
+(defun org-mac-idle-seconds ()
+  "Return the current Mac idle time in seconds"
+  (string-to-number (shell-command-to-string "ioreg -c IOHIDSystem | perl -ane 'if (/Idle/) {$idle=(pop @F)/1000000000; print $idle; last}'")))
+
+(defun org-user-idle-seconds ()
+  "Return the number of seconds the user has been idle for.
+This routine returns a floating point number."
+  (if (eq system-type 'darwin)
+      (let ((emacs-idle (org-emacs-idle-seconds)))
+	;; If Emacs has been idle for longer than the user's
+	;; `org-clock-idle-time' value, check whether the whole system has
+	;; really been idle for that long.
+	(if (> emacs-idle (* 60 org-clock-idle-time))
+	    (min emacs-idle (org-mac-idle-seconds))
+	  emacs-idle))
+    (org-emacs-idle-seconds)))
+
 (defun org-clock-in (&optional select)
   "Start the clock on the current item.
 If necessary, clock-out of the currently active clock.
@@ -616,6 +720,9 @@ the clocking selection, associated with the letter `d'."
 		(setq global-mode-string
 		      (append global-mode-string '(org-mode-line-string))))
 	    (org-clock-update-mode-line)
+	    (when org-clock-mode-line-timer
+	      (cancel-timer org-clock-mode-line-timer)
+	      (setq org-clock-mode-line-timer nil))
 	    (setq org-clock-mode-line-timer
 		  (run-with-timer 60 60 'org-clock-update-mode-line))
 	    (message "Clock starts at %s - %s" ts msg-extra)
@@ -748,7 +855,7 @@ line and position cursor in that line."
 	    (and (re-search-forward org-property-end-re nil t)
 		 (goto-char (match-beginning 0))))))))
 
-(defun org-clock-out (&optional fail-quietly)
+(defun org-clock-out (&optional fail-quietly at-time)
   "Stop the currently running clock.
 If there is no running clock, throw an error, unless FAIL-QUIETLY is set."
   (interactive)
@@ -769,7 +876,8 @@ If there is no running clock, throw an error, unless FAIL-QUIETLY is set."
 	  (goto-char (match-end 0))
 	  (delete-region (point) (point-at-eol))
 	  (insert "--")
-	  (setq te (org-insert-time-stamp (current-time) 'with-hm 'inactive))
+	  (setq te (org-insert-time-stamp (or at-time (current-time))
+					  'with-hm 'inactive))
 	  (setq s (- (org-float-time (apply 'encode-time (org-parse-time-string te)))
 		     (org-float-time (apply 'encode-time (org-parse-time-string ts))))
 		h (floor (/ s 3600))

+ 21 - 0
lisp/org.el

@@ -5335,6 +5335,27 @@ are at least `org-cycle-separator-lines' empty lines before the headline."
     (let ((context (if (org-up-heading-safe) 'children 'overview)))
       (org-cycle-show-empty-lines context))))
 
+(defun org-files-list ()
+  "Return `org-agenda-files' list, plus all open org-mode files.
+This is useful for operations that need to scan all of a user's
+open and agenda-wise Org files."
+  (let ((files (mapcar 'expand-file-name org-agenda-files)))
+    (dolist (buf (buffer-list))
+      (with-current-buffer buf
+	(if (eq major-mode 'org-mode)
+	    (let ((file (expand-file-name (buffer-file-name))))
+	      (unless (member file files)
+		(push file files))))))
+    files))
+
+(defsubst org-entry-beginning-position ()
+  "Return the beginning position of the current entry."
+  (save-excursion (outline-back-to-heading t) (point)))
+
+(defsubst org-entry-end-position ()
+  "Return the end position of the current entry."
+  (save-excursion (outline-next-heading) (point)))
+
 (defun org-cycle-hide-drawers (state)
   "Re-hide all drawers after a visibility state change."
   (when (and (org-mode-p)