Browse Source

Cleaning up the progress logging code.

New STARTUP option "logstate" for logging only time stamps.
New character "!" for per-keyword setting of this.
Carsten Dominik 17 years ago
parent
commit
cb56b6684e
4 changed files with 225 additions and 83 deletions
  1. 23 0
      ChangeLog
  2. 42 0
      ORGWEBPAGE/Changes.org
  3. 110 59
      org.el
  4. 50 24
      org.texi

+ 23 - 0
ChangeLog

@@ -1,3 +1,26 @@
+2008-02-15  Carsten Dominik  <dominik@science.uva.nl>
+
+	* org.el (org-todo): Make sure that LOGGING properties are
+	honoured.
+
+2008-02-14  Carsten Dominik  <dominik@science.uva.nl>
+
+	* org.el (org-log-progress): Renamed from `org-log-done'.
+	(org-log-done): New defvaralias.
+	(org-todo-keywords): Improve docstring.
+	(org-startup-options): Define the new startup option `logstate'.
+	(org-set-regexps-and-options): Process the "!" markers.
+	(org-todo): Respect the new logging stuff.
+	(org-log-note-how): New variable.
+	(org-add-log-maybe): New parameter HOW that defines how logging
+	should be done and also overrides PURPOSE.  Add a docstring.
+	(org-add-log-note): Check if we really need to ask for a note.
+	(org-get-current-options): Digest the new keyword.
+
+	* org.texi (Progress logging): Document the new variable
+	`org-log-progress' and the new possibilities for per-keyword
+	settings.
+
 2008-02-13  Carsten Dominik  <dominik@science.uva.nl>
 2008-02-13  Carsten Dominik  <dominik@science.uva.nl>
 
 
 	* org.el (org-agenda-reset-markers): Renamed from
 	* org.el (org-agenda-reset-markers): Renamed from

+ 42 - 0
ORGWEBPAGE/Changes.org

@@ -7,8 +7,50 @@
 
 
 * Version 5.22
 * Version 5.22
 
 
+** Incompatible changes
+
+   - The variable `org-log-done' is obsolete.  Please use
+     `org-log-progress' instead.  For Emacs 21 users this is a
+     must, for the rest of us, `org-log-done' can still be used
+     as an alias.
+
 ** Details
 ** Details
 
 
+*** Changes to logging progress
+
+    There is now more control over which state changes are being
+    logged in what way.  You can now request that the change to a
+    state does one of three options:
+    - No extre record of this state change.
+    - Record a time stamp only, without further user interaction
+    - Prompt for a note and record a time stamp.
+
+    First of all, the variable `org-log-done' is obsolete and
+    will be replaced by `org-log-progress', which is used in
+    exactly the same way but has a more appropriate
+    name. `org-log-done' only exists as a variable alias now.
+
+    `org-log-progress' may have these values:
+
+    - 'done    Record a time stamp when the entry is done.
+    - t        backward compatibility for `done', should not be used
+    - 'state   Record a time stamp for each state change.
+    - list     symbols indicating where a note should be taken,
+               may contain the symbols `done', `state', and
+               `clock-out'
+
+    However, the better way to configure this on a per-keyword
+    basis, using the marker characters "!" and "@".  For example
+
+    #+TODO: TODO(t) STARTED(s!) WAIT(w@) | DONE(d!) CANCELLED(c@)
+
+    Now a time stamp will be recorded for STARTED, WAIT, DONE,
+    and CANCELLED.  You will be prompted for an additional note
+    for WAIT and CANCELLED.  Nothing will be recorded when you
+    switch to TODO. 
+
+*** Misc
+
    - M-RET no longer brakes a line in the middle, it will make a
    - M-RET no longer brakes a line in the middle, it will make a
      new line ofter the current or (if cursor is at the beginning
      new line ofter the current or (if cursor is at the beginning
      of the line) before the current line.
      of the line) before the current line.

+ 110 - 59
org.el

@@ -1641,6 +1641,15 @@ cycling, see the manual.
 TODO keywords and interpretation can also be set on a per-file basis with
 TODO keywords and interpretation can also be set on a per-file basis with
 the special #+SEQ_TODO and #+TYP_TODO lines.
 the special #+SEQ_TODO and #+TYP_TODO lines.
 
 
+Each keyword can optionally specify a character for fast state selection
+\(in combination with the variable `org-use-fast-todo-selection')
+and a specifier for state change logging, using the same syntax
+that is used in the \"#+TODO:\" lines.  For example, \"WAIT(w)\" says
+that the WAIT state can be selected with the \"w\" key. \"WAIT(w!)\"
+indicates to record a time stamp each time this state is selected.
+\"WAIT(w@)\" says that the user should in addition be prompted for a
+note.
+
 For backward compatibility, this variable may also be just a list
 For backward compatibility, this variable may also be just a list
 of keywords - in this case the interptetation (sequence or type) will be
 of keywords - in this case the interptetation (sequence or type) will be
 taken from the (otherwise obsolete) variable `org-todo-interpretation'."
 taken from the (otherwise obsolete) variable `org-todo-interpretation'."
@@ -1721,20 +1730,20 @@ Lisp variable `state'."
   :group 'org-todo
   :group 'org-todo
   :type 'hook)
   :type 'hook)
 
 
-(defcustom org-log-done nil
+(defcustom org-log-progress nil
   "When set, insert a (non-active) time stamp when TODO entry is marked DONE.
   "When set, insert a (non-active) time stamp when TODO entry is marked DONE.
 When the state of an entry is changed from nothing or a DONE state to
 When the state of an entry is changed from nothing or a DONE state to
 a not-done TODO state, remove a previous closing date.
 a not-done TODO state, remove a previous closing date.
 
 
+If this is the symbol `state', that means log the time of each state change.
+
 This can also be a list of symbols indicating under which conditions
 This can also be a list of symbols indicating under which conditions
 the time stamp recording the action should be annotated with a short note.
 the time stamp recording the action should be annotated with a short note.
 Valid members of this list are
 Valid members of this list are
 
 
   done       Offer to record a note when marking entries done
   done       Offer to record a note when marking entries done
   state      Offer to record a note whenever changing the TODO state
   state      Offer to record a note whenever changing the TODO state
-             of an item.  This is only relevant if TODO keywords are
-             interpreted as sequence, see variable `org-todo-interpretation'.
-             When `state' is set, this includes tracking `done'.
+             of an item.
   clock-out  Offer to record a note when clocking out of an item.
   clock-out  Offer to record a note when clocking out of an item.
 
 
 A separate window will then pop up and allow you to type a note.
 A separate window will then pop up and allow you to type a note.
@@ -1742,27 +1751,36 @@ After finishing with C-c C-c, the note will be added directly after the
 timestamp, as a plain list item.  See also the variable
 timestamp, as a plain list item.  See also the variable
 `org-log-note-headings'.
 `org-log-note-headings'.
 
 
-Logging can also be configured on a per-file basis by adding one of
-the following lines anywhere in the buffer:
+Logging can also be configured on a per-file basis by adding one
+or several the following lines anywhere in the buffer:
 
 
    #+STARTUP: logdone
    #+STARTUP: logdone
+   #+STARTUP: logstate
    #+STARTUP: nologging
    #+STARTUP: nologging
    #+STARTUP: lognotedone
    #+STARTUP: lognotedone
    #+STARTUP: lognotestate
    #+STARTUP: lognotestate
    #+STARTUP: lognoteclock-out
    #+STARTUP: lognoteclock-out
 
 
 You can have local logging settings for a subtree by setting the LOGGING
 You can have local logging settings for a subtree by setting the LOGGING
-property to one or more of these keywords."
+property to one or more of these keywords.
+
+Finally, state logging can be configured on a per-state basis.
+See the variable `org-todo-keywords'."
   :group 'org-todo
   :group 'org-todo
   :group 'org-progress
   :group 'org-progress
   :type '(choice
   :type '(choice
 	  (const :tag "off" nil)
 	  (const :tag "off" nil)
-	  (const :tag "on" t)
+	  (const :tag "when moving to a DONE state" done)
+	  (const :tag "when moving to a DONE state" t)
+	  (const :tag "all state changes" state)
 	  (set :tag "on, with notes, detailed control" :greedy t :value (done)
 	  (set :tag "on, with notes, detailed control" :greedy t :value (done)
 	       (const :tag "when item is marked DONE" done)
 	       (const :tag "when item is marked DONE" done)
 	       (const :tag "when TODO state changes" state)
 	       (const :tag "when TODO state changes" state)
 	       (const :tag "when clocking out" clock-out))))
 	       (const :tag "when clocking out" clock-out))))
 
 
+(if (fboundp 'defvaralias)
+    (defvaralias 'org-log-done 'org-log-progress))
+
 (defcustom org-log-done-with-time t
 (defcustom org-log-done-with-time t
   "Non-nil means, the CLOSED time stamp will contain date and time.
   "Non-nil means, the CLOSED time stamp will contain date and time.
 When nil, only the date will be recorded."
 When nil, only the date will be recorded."
@@ -4391,12 +4409,13 @@ we turn off invisibility temporarily.  Use this in a `let' form."
     ("align" org-startup-align-all-tables t)
     ("align" org-startup-align-all-tables t)
     ("noalign" org-startup-align-all-tables nil)
     ("noalign" org-startup-align-all-tables nil)
     ("customtime" org-display-custom-times t)
     ("customtime" org-display-custom-times t)
-    ("logging" org-log-done t)
-    ("logdone" org-log-done t)
-    ("nologging" org-log-done nil)
-    ("lognotedone" org-log-done done push)
-    ("lognotestate" org-log-done state push)
-    ("lognoteclock-out" org-log-done clock-out push)
+    ("logging" org-log-progress t)
+    ("logdone" org-log-progress t)
+    ("logstate" org-log-progress state)
+    ("nologging" org-log-progress nil)
+    ("lognotedone" org-log-progress done push)
+    ("lognotestate" org-log-progress state push)
+    ("lognoteclock-out" org-log-progress clock-out push)
     ("logrepeat" org-log-repeat t)
     ("logrepeat" org-log-repeat t)
     ("nologrepeat" org-log-repeat nil)
     ("nologrepeat" org-log-repeat nil)
     ("constcgs" constants-unit-system cgs)
     ("constcgs" constants-unit-system cgs)
@@ -4425,7 +4444,7 @@ means to push this value onto the list in the variable.")
 	  (splitre "[ \t]+")
 	  (splitre "[ \t]+")
 	  kwds kws0 kwsa key value cat arch tags const links hw dws
 	  kwds kws0 kwsa key value cat arch tags const links hw dws
 	  tail sep kws1 prio props drawers
 	  tail sep kws1 prio props drawers
-	  ex log)
+	  ex note time)
       (save-excursion
       (save-excursion
 	(save-restriction
 	(save-restriction
 	  (widen)
 	  (widen)
@@ -4511,11 +4530,14 @@ means to push this value onto the list in the variable.")
 			    (progn
 			    (progn
 			      (setq kw (match-string 1 x)
 			      (setq kw (match-string 1 x)
 				    ex (and (match-end 2) (match-string 2 x))
 				    ex (and (match-end 2) (match-string 2 x))
-				    log (and ex (string-match "@" ex))
+				    note (and ex (string-match "@" ex))
+				    time (or note (and ex (string-match "!" ex)))
 				    key (and ex (substring ex 0 1)))
 				    key (and ex (substring ex 0 1)))
 			      (if (equal key "@") (setq key nil))
 			      (if (equal key "@") (setq key nil))
 			      (push (cons kw (and key (string-to-char key))) kwsa)
 			      (push (cons kw (and key (string-to-char key))) kwsa)
-			      (and log (push kw org-todo-log-states))
+			      (and (or note time)
+				   (push (cons kw (if note 'note 'time))
+					 org-todo-log-states))
 			      kw)
 			      kw)
 			  (error "Invalid TODO keyword %s" x)))
 			  (error "Invalid TODO keyword %s" x)))
 		      kws0)
 		      kws0)
@@ -7928,7 +7950,7 @@ this heading."
 		     (looking-at org-todo-line-regexp)
 		     (looking-at org-todo-line-regexp)
 		     (or (not (match-end 2))
 		     (or (not (match-end 2))
 			 (not (member (match-string 2) org-done-keywords))))
 			 (not (member (match-string 2) org-done-keywords))))
-	    (let (org-log-done)
+	    (let (org-log-progress)
 	      (org-todo
 	      (org-todo
 	       (car (or (member org-archive-mark-done org-done-keywords)
 	       (car (or (member org-archive-mark-done org-done-keywords)
 			org-done-keywords)))))
 			org-done-keywords)))))
@@ -13631,7 +13653,7 @@ from that hook."
   (when (and org-clock-marker
   (when (and org-clock-marker
 	     (equal (marker-buffer org-clock-marker) (current-buffer)))
 	     (equal (marker-buffer org-clock-marker) (current-buffer)))
     ;; FIXME: test this, this is w/o notetaking!
     ;; FIXME: test this, this is w/o notetaking!
-    (let (org-log-done) (org-clock-out)))
+    (let (org-log-progress) (org-clock-out)))
   (when buffer-file-name
   (when buffer-file-name
     (save-buffer)
     (save-buffer)
     (setq buffer-file-name nil))
     (setq buffer-file-name nil))
@@ -14458,8 +14480,14 @@ For calling through lisp, arg is also interpreted in the following way:
       (let* ((match-data (match-data))
       (let* ((match-data (match-data))
 	     (startpos (point-at-bol))
 	     (startpos (point-at-bol))
 	     (logging (save-match-data (org-entry-get nil "LOGGING" t)))
 	     (logging (save-match-data (org-entry-get nil "LOGGING" t)))
-	     (org-log-done (org-parse-local-options logging 'org-log-done))
+	     (old-progress org-log-progress)
+	     (org-log-progress
+	      (org-parse-local-options logging 'org-log-progress))
+	     (local-logging-p (not (equal old-progress org-log-progress)))
 	     (org-log-repeat (org-parse-local-options logging 'org-log-repeat))
 	     (org-log-repeat (org-parse-local-options logging 'org-log-repeat))
+	     (org-todo-log-states (if local-logging-p
+				      nil
+				    org-todo-log-states))
 	     (this (match-string 1))
 	     (this (match-string 1))
 	     (hl-pos (match-beginning 0))
 	     (hl-pos (match-beginning 0))
 	     (head (org-get-todo-sequence-head this))
 	     (head (org-get-todo-sequence-head this))
@@ -14529,7 +14557,7 @@ For calling through lisp, arg is also interpreted in the following way:
 	     (next (if state (concat " " state " ") " "))
 	     (next (if state (concat " " state " ") " "))
 	     (change-plist (list :type 'todo-state-change :from this :to state
 	     (change-plist (list :type 'todo-state-change :from this :to state
 				 :position startpos))
 				 :position startpos))
-	     dostates)
+	     dolog now-done-p)
 	(when org-blocker-hook
 	(when org-blocker-hook
 	  (unless (save-excursion
 	  (unless (save-excursion
 		    (save-match-data
 		    (save-match-data
@@ -14558,26 +14586,36 @@ For calling through lisp, arg is also interpreted in the following way:
 		   (mapconcat 'identity (assoc state org-todo-sets) " ")))
 		   (mapconcat 'identity (assoc state org-todo-sets) " ")))
 	(setq org-last-todo-state-is-todo
 	(setq org-last-todo-state-is-todo
 	      (not (member state org-done-keywords)))
 	      (not (member state org-done-keywords)))
-	(when (and org-log-done (not (memq arg '(nextset previousset))))
-	  (setq dostates (and (listp org-log-done) (memq 'state org-log-done)
-			      (or (not org-todo-log-states)
-				  (member state org-todo-log-states))))
-
+	(setq now-done-p (and (member state org-done-keywords)
+			      (not (member this org-done-keywords))))
+	(when (and (or org-todo-log-states org-log-progress)
+		   (not (memq arg '(nextset previousset))))
+	  ;; we need to look at recording a time and note
+	  (if org-todo-log-states
+	      ;; The type of logging has been defined per state
+	      (setq dolog (cdr (assoc state org-todo-log-states)))
+	    ;; we only have general rules
+	    (setq dolog
+		  (cond ((memq org-log-progress '(t done)) nil)
+			((eq org-log-progress 'state) 'time)
+			((listp org-log-progress)
+			 (and (member 'state org-log-progress)
+			      'note)))))
 	  (cond
 	  (cond
 	   ((and state (member state org-not-done-keywords)
 	   ((and state (member state org-not-done-keywords)
 		 (not (member this org-not-done-keywords)))
 		 (not (member this org-not-done-keywords)))
 	    ;; This is now a todo state and was not one before
 	    ;; This is now a todo state and was not one before
 	    ;; Remove any CLOSED timestamp, and possibly log the state change
 	    ;; Remove any CLOSED timestamp, and possibly log the state change
 	    (org-add-planning-info nil nil 'closed)
 	    (org-add-planning-info nil nil 'closed)
-	    (and dostates (org-add-log-maybe 'state state 'findpos)))
-	   ((and state dostates)
+	    (and dolog (org-add-log-maybe 'state state 'findpos dolog)))
+	   ((and state dolog)
 	    ;; This is a non-nil state, and we need to log it
 	    ;; This is a non-nil state, and we need to log it
-	    (org-add-log-maybe 'state state 'findpos))
-	   ((and (member state org-done-keywords)
-		 (not (member this org-done-keywords)))
+	    (if now-done-p (org-add-planning-info 'closed (org-current-time)))
+	    (org-add-log-maybe 'state state 'findpos dolog))
+	   (now-done-p
 	    ;; It is now done, and it was not done before
 	    ;; It is now done, and it was not done before
 	    (org-add-planning-info 'closed (org-current-time))
 	    (org-add-planning-info 'closed (org-current-time))
-	    (org-add-log-maybe 'done state 'findpos))))
+	    (org-add-log-maybe 'done state 'findpos dolog))))
 	;; Fixup tag positioning
 	;; Fixup tag positioning
 	(and org-auto-align-tags (not org-setting-tags) (org-set-tags nil t))
 	(and org-auto-align-tags (not org-setting-tags) (org-set-tags nil t))
 	(run-hooks 'org-after-todo-state-change-hook)
 	(run-hooks 'org-after-todo-state-change-hook)
@@ -14697,7 +14735,7 @@ This function should be run in the `org-after-todo-state-change-hook'."
 	 (done-word (nth 3 aa))
 	 (done-word (nth 3 aa))
 	 (whata '(("d" . day) ("m" . month) ("y" . year)))
 	 (whata '(("d" . day) ("m" . month) ("y" . year)))
 	 (msg "Entry repeats: ")
 	 (msg "Entry repeats: ")
-	 (org-log-done)
+	 (org-log-progress)
 	 re type n what ts)
 	 re type n what ts)
     (when repeat
     (when repeat
       (org-todo (if (eq interpret 'type) last-state head))
       (org-todo (if (eq interpret 'type) last-state head))
@@ -14705,7 +14743,8 @@ This function should be run in the `org-after-todo-state-change-hook'."
 		 (not (memq 'org-add-log-note
 		 (not (memq 'org-add-log-note
 			    (default-value 'post-command-hook))))
 			    (default-value 'post-command-hook))))
 	;; Make sure a note is taken
 	;; Make sure a note is taken
-	(let ((org-log-done '(done)))
+	(let ((org-log-progress '(done))
+	      (org-todo-log-states nil))
 	  (org-add-log-maybe 'done (or done-word (car org-done-keywords))
 	  (org-add-log-maybe 'done (or done-word (car org-done-keywords))
 			     'findpos)))
 			     'findpos)))
       (org-back-to-heading t)
       (org-back-to-heading t)
@@ -14856,17 +14895,24 @@ be removed."
 (defvar org-log-note-marker (make-marker))
 (defvar org-log-note-marker (make-marker))
 (defvar org-log-note-purpose nil)
 (defvar org-log-note-purpose nil)
 (defvar org-log-note-state nil)
 (defvar org-log-note-state nil)
+(defvar org-log-note-how nil)
 (defvar org-log-note-window-configuration nil)
 (defvar org-log-note-window-configuration nil)
 (defvar org-log-note-return-to (make-marker))
 (defvar org-log-note-return-to (make-marker))
 (defvar org-log-post-message nil
 (defvar org-log-post-message nil
   "Message to be displayed after a log note has been stored.
   "Message to be displayed after a log note has been stored.
 The auto-repeater uses this.")
 The auto-repeater uses this.")
 
 
-(defun org-add-log-maybe (&optional purpose state findpos)
-  "Set up the post command hook to take a note."
+(defun org-add-log-maybe (&optional purpose state findpos how)
+  "Set up the post command hook to take a note.
+For this to do anything, HOW must be set, or PURPOSE must
+be a member of the list in `org-log-progress'.
+If this is about to TODO state change, the new state is expected in STATE.
+When FINDPOS is non-nil, find the correct position for the note in
+the current entry.  If not, assume that it can be inserted at point."
   (save-excursion
   (save-excursion
-    (when (and (listp org-log-done)
-	       (memq purpose org-log-done))
+    (when (or how
+	      (and (listp org-log-progress)
+		   (memq purpose org-log-progress)))
       (when findpos
       (when findpos
 	(org-back-to-heading t)
 	(org-back-to-heading t)
 	(looking-at (concat outline-regexp "\\( *\\)[^\r\n]*"
 	(looking-at (concat outline-regexp "\\( *\\)[^\r\n]*"
@@ -14878,8 +14924,9 @@ The auto-repeater uses this.")
 	  (org-skip-over-state-notes)
 	  (org-skip-over-state-notes)
 	  (skip-chars-backward " \t\n\r")))
 	  (skip-chars-backward " \t\n\r")))
       (move-marker org-log-note-marker (point))
       (move-marker org-log-note-marker (point))
-      (setq org-log-note-purpose purpose)
-      (setq org-log-note-state state)
+      (setq org-log-note-purpose purpose
+	    org-log-note-state state
+	    org-log-note-how how)
       (add-hook 'post-command-hook 'org-add-log-note 'append))))
       (add-hook 'post-command-hook 'org-add-log-note 'append))))
 
 
 (defun org-skip-over-state-notes ()
 (defun org-skip-over-state-notes ()
@@ -14900,16 +14947,18 @@ The auto-repeater uses this.")
   (goto-char org-log-note-marker)
   (goto-char org-log-note-marker)
   (org-switch-to-buffer-other-window "*Org Note*")
   (org-switch-to-buffer-other-window "*Org Note*")
   (erase-buffer)
   (erase-buffer)
-  (let ((org-inhibit-startup t)) (org-mode))
-  (insert (format "# Insert note for %s.
+  (if (eq org-log-note-how 'time)
+      (org-store-log-note)
+    (let ((org-inhibit-startup t)) (org-mode))
+    (insert (format "# Insert note for %s.
 # Finish with C-c C-c, or cancel with C-c C-k.\n\n"
 # Finish with C-c C-c, or cancel with C-c C-k.\n\n"
-		  (cond
-		   ((eq org-log-note-purpose 'clock-out) "stopped clock")
-		   ((eq org-log-note-purpose 'done)  "closed todo item")
-		   ((eq org-log-note-purpose 'state)
-		    (format "state change to \"%s\"" org-log-note-state))
+		    (cond
+		     ((eq org-log-note-purpose 'clock-out) "stopped clock")
+		     ((eq org-log-note-purpose 'done)  "closed todo item")
+		     ((eq org-log-note-purpose 'state)
+		      (format "state change to \"%s\"" org-log-note-state))
 		   (t (error "This should not happen")))))
 		   (t (error "This should not happen")))))
-  (org-set-local 'org-finish-function 'org-store-log-note))
+    (org-set-local 'org-finish-function 'org-store-log-note)))
 
 
 (defun org-store-log-note ()
 (defun org-store-log-note ()
   "Finish taking a log note, and insert it to where it belongs."
   "Finish taking a log note, and insert it to where it belongs."
@@ -18652,7 +18701,7 @@ If there is no running clock, throw an error, unless FAIL-QUIETLY is set."
       (insert " => " (format "%2d:%02d" h m))
       (insert " => " (format "%2d:%02d" h m))
       (move-marker org-clock-marker nil)
       (move-marker org-clock-marker nil)
       (let* ((logging (save-match-data (org-entry-get nil "LOGGING" t)))
       (let* ((logging (save-match-data (org-entry-get nil "LOGGING" t)))
-	     (org-log-done (org-parse-local-options logging 'org-log-done))
+	     (org-log-progress (org-parse-local-options logging 'org-log-progress))
 	     (org-log-repeat (org-parse-local-options logging 'org-log-repeat)))
 	     (org-log-repeat (org-parse-local-options logging 'org-log-repeat)))
 	(org-add-log-maybe 'clock-out))
 	(org-add-log-maybe 'clock-out))
       (when org-mode-line-timer
       (when org-mode-line-timer
@@ -18820,10 +18869,10 @@ and is only done if the variable `org-clock-out-when-done' is not nil."
 	     (> (save-excursion (outline-next-heading) (point))
 	     (> (save-excursion (outline-next-heading) (point))
 		org-clock-marker))
 		org-clock-marker))
     ;; Clock out, but don't accept a logging message for this.
     ;; Clock out, but don't accept a logging message for this.
-    (let ((org-log-done (if (and (listp org-log-done)
-				 (member 'clock-out org-log-done))
+    (let ((org-log-progress (if (and (listp org-log-progress)
+				 (member 'clock-out org-log-progress))
 			    '(done)
 			    '(done)
-			  org-log-done)))
+			  org-log-progress)))
       (org-clock-out))))
       (org-clock-out))))
 
 
 (add-hook 'org-after-todo-state-change-hook
 (add-hook 'org-after-todo-state-change-hook
@@ -24687,11 +24736,13 @@ Does include HTML export options as well as TODO and CATEGORY stuff."
    (if org-odd-levels-only "odd" "oddeven")
    (if org-odd-levels-only "odd" "oddeven")
    (if org-hide-leading-stars "hidestars" "showstars")
    (if org-hide-leading-stars "hidestars" "showstars")
    (if org-startup-align-all-tables "align" "noalign")
    (if org-startup-align-all-tables "align" "noalign")
-   (cond ((eq t org-log-done) "logdone")
-	 ((not org-log-done) "nologging")
-	 ((listp org-log-done)
+   (cond ((eq t org-log-progress) "logdone")
+	 ((eq 'done org-log-progress) "logdone")
+	 ((eq 'state org-log-progress) "logstate")
+	 ((not org-log-progress) "nologging")
+	 ((listp org-log-progress)
 	  (mapconcat (lambda (x) (concat "lognote" (symbol-name x)))
 	  (mapconcat (lambda (x) (concat "lognote" (symbol-name x)))
-		     org-log-done " ")))
+		     org-log-progress " ")))
    (or (mapconcat (lambda (x)
    (or (mapconcat (lambda (x)
 		    (cond
 		    (cond
 		     ((equal '(:startgroup) x) "{")
 		     ((equal '(:startgroup) x) "{")
@@ -27228,11 +27279,11 @@ See the individual commands for more information."
      ["Create clock table" org-clock-report t]
      ["Create clock table" org-clock-report t]
      "--"
      "--"
      ["Record DONE time"
      ["Record DONE time"
-      (progn (setq org-log-done (not org-log-done))
+      (progn (setq org-log-progress (not org-log-progress))
 	     (message "Switching to %s will %s record a timestamp"
 	     (message "Switching to %s will %s record a timestamp"
 		      (car org-done-keywords)
 		      (car org-done-keywords)
-		      (if org-log-done "automatically" "not")))
-      :style toggle :selected org-log-done])
+		      (if org-log-progress "automatically" "not")))
+      :style toggle :selected org-log-progress])
     "--"
     "--"
     ["Agenda Command..." org-agenda t]
     ["Agenda Command..." org-agenda t]
     ["Set Restriction Lock" org-agenda-set-restriction-lock t]
     ["Set Restriction Lock" org-agenda-set-restriction-lock t]

+ 50 - 24
org.texi

@@ -2908,9 +2908,11 @@ special faces for some of them.  This can be done using the variable
 @cindex progress logging
 @cindex progress logging
 @cindex logging, of progress
 @cindex logging, of progress
 
 
-Org-mode can automatically record a time stamp and even a note when you
-mark a TODO item as DONE, or even each time you change the state of
-a TODO item.
+Org-mode can automatically record a time stamp and possibly a note when
+you mark a TODO item as DONE, or even each time you change the state of
+a TODO item.  This system is highly configurable, settings can be on a
+per-file and even on a per-keyword basis.  For information on how to
+clock working time for a task, see @ref{Clocking work time}.
 
 
 @menu
 @menu
 * Closing items::               When was this entry marked DONE?
 * Closing items::               When was this entry marked DONE?
@@ -2923,11 +2925,12 @@ a TODO item.
 If you want to keep track of @emph{when} a certain TODO item was
 If you want to keep track of @emph{when} a certain TODO item was
 finished, turn on logging with@footnote{The corresponding in-buffer
 finished, turn on logging with@footnote{The corresponding in-buffer
 setting is: @code{#+STARTUP: logdone}.  You may also set this for the
 setting is: @code{#+STARTUP: logdone}.  You may also set this for the
-scope of a subtree by adding a @code{:LOGGING:} property with one or more
-of the logging keywords in the value.}
+scope of a subtree by adding a @code{:LOGGING:} property with
+one or more of the STARTUP logging keywords in the value.  When set with
+a property, all per-state settings are overruled.}
 
 
 @lisp
 @lisp
-(setq org-log-done t)
+(setq org-log-progress 'done)
 @end lisp
 @end lisp
 
 
 @noindent
 @noindent
@@ -2943,7 +2946,7 @@ along with the timestamp, use@footnote{The corresponding in-buffer
 setting is: @code{#+STARTUP: lognotedone}}
 setting is: @code{#+STARTUP: lognotedone}}
 
 
 @lisp
 @lisp
-(setq org-log-done '(done))
+(setq org-log-progress '(done))
 @end lisp
 @end lisp
 
 
 @node Tracking TODO state changes,  , Closing items, Progress logging
 @node Tracking TODO state changes,  , Closing items, Progress logging
@@ -2952,34 +2955,53 @@ setting is: @code{#+STARTUP: lognotedone}}
 When TODO keywords are used as workflow states (@pxref{Workflow
 When TODO keywords are used as workflow states (@pxref{Workflow
 states}), you might want to keep track of when a state change occurred
 states}), you might want to keep track of when a state change occurred
 and record a note about this change.  With the setting@footnote{The
 and record a note about this change.  With the setting@footnote{The
-corresponding in-buffer setting is: @code{#+STARTUP: lognotestate}.}
+corresponding in-buffer setting is: @code{#+STARTUP: logstate}.}
 
 
 @lisp
 @lisp
-(setq org-log-done '(state))
+(setq org-log-progress 'state)
+@end lisp
+
+each state change will recorded with a time stamp.  With the
+setting@footnote{The corresponding in-buffer setting is:
+@code{#+STARTUP: lognotestate}.}
+
+@lisp
+(setq org-log-progress '(state))
 @end lisp
 @end lisp
 
 
 @noindent
 @noindent
 each state change will prompt you for a note that will be attached to
 each state change will prompt you for a note that will be attached to
-the current headline.  If you press @kbd{C-c C-c} without typing
-anything into the note buffer, only the time of the state change will be
-noted.  Very likely you do not want this verbose tracking all the time,
-so it is probably better to configure this behavior with in-buffer
-options.  For example, if you are tracking purchases, put these into a
-separate file that contains:
+the current headline.  If you exit without writing anything, a time
+stamp will still be recorded.
+
+Very likely you do not want this verbose tracking all the time, so it is
+probably better to configure this behavior on a per-keyword basis.  You
+can do this for a particular file with in-buffer settings, or globally
+by configuring org-todo-keywords.  For example, lets assume you are
+tracking purchases in a particular file with the following TODO states:
 
 
 @example
 @example
 #+SEQ_TODO: TODO(t) ORDERED(o) INVOICE(i) PAYED(p) | RECEIVED(r)
 #+SEQ_TODO: TODO(t) ORDERED(o) INVOICE(i) PAYED(p) | RECEIVED(r)
 #+STARTUP: lognotestate
 #+STARTUP: lognotestate
 @end example
 @end example
 
 
-If you only need to take a note for some of the states, mark those
-states with an additional @samp{@@}, like this:
+If you only need to take a time stamp or a note for some of the states,
+mark those states with an additional @samp{!} or @samp{@@},
+respectively, like this:
 
 
 @example
 @example
-#+SEQ_TODO: TODO(t) ORDERED(o@@) INVOICE(i@@) PAYED(p) | RECEIVED(r)
-#+STARTUP: lognotestate
+#+SEQ_TODO: TODO(t) ORDERED(o@@) INVOICE(i@@) PAYED(p!) | RECEIVED(r!)
 @end example
 @end example
 
 
+Then changing to a state PAYED or RECEIVED will record a time stamp, and
+changing to the state ORDERED or INVOICE will also record a note.
+@i{Note that specifying a per-keyword setting for logging causes the
+corresponding #+STARTUP option to be ignored.}
+
+If you would like to define fast access keys and per-state logging
+indicators globally, you can use the same syntax in the variable
+@code{org-todo-keywords}.
+
 @node Priorities, Breaking down tasks, Progress logging, TODO items
 @node Priorities, Breaking down tasks, Progress logging, TODO items
 @section Priorities
 @section Priorities
 @cindex priorities
 @cindex priorities
@@ -4340,7 +4362,7 @@ this item, the multiple CLOCK lines will be wrapped into a
 Stop the clock (clock-out).  The inserts another timestamp at the same
 Stop the clock (clock-out).  The inserts another timestamp at the same
 location where the clock was last started.  It also directly computes
 location where the clock was last started.  It also directly computes
 the resulting time in inserts it after the time range as @samp{=>
 the resulting time in inserts it after the time range as @samp{=>
-HH:MM}.  See the variable @code{org-log-done} for the possibility to
+HH:MM}.  See the variable @code{org-log-progress} for the possibility to
 record an additional note together with the clock-out time
 record an additional note together with the clock-out time
 stamp@footnote{The corresponding in-buffer setting is: @code{#+STARTUP:
 stamp@footnote{The corresponding in-buffer setting is: @code{#+STARTUP:
 lognoteclock-out}}.
 lognoteclock-out}}.
@@ -5273,7 +5295,7 @@ not remove the previously used indirect buffer.
 @kindex l
 @kindex l
 @item l
 @item l
 Toggle Logbook mode.  In Logbook mode, entries that where marked DONE while
 Toggle Logbook mode.  In Logbook mode, entries that where marked DONE while
-logging was on (variable @code{org-log-done}) are shown in the agenda,
+logging was on (variable @code{org-log-progress}) are shown in the agenda,
 as are entries that have been clocked on that day.
 as are entries that have been clocked on that day.
 
 
 @tsubheading{Change display}
 @tsubheading{Change display}
@@ -7336,19 +7358,23 @@ align      @r{align all tables}
 noalign    @r{don't align tables on startup}
 noalign    @r{don't align tables on startup}
 @end example
 @end example
 Logging TODO state changes and clock intervals (variables
 Logging TODO state changes and clock intervals (variables
-@code{org-log-done} and @code{org-log-repeat}) can be configured using
+@code{org-log-progress} and @code{org-log-repeat}) can be configured using
 these options.
 these options.
 @cindex @code{logdone}, STARTUP keyword
 @cindex @code{logdone}, STARTUP keyword
+@cindex @code{logging}, STARTUP keyword
 @cindex @code{nologging}, STARTUP keyword
 @cindex @code{nologging}, STARTUP keyword
 @cindex @code{lognotedone}, STARTUP keyword
 @cindex @code{lognotedone}, STARTUP keyword
-@cindex @code{lognoteclock-out}, STARTUP keyword
+@cindex @code{logstate}, STARTUP keyword
 @cindex @code{lognotestate}, STARTUP keyword
 @cindex @code{lognotestate}, STARTUP keyword
+@cindex @code{lognoteclock-out}, STARTUP keyword
 @cindex @code{logrepeat}, STARTUP keyword
 @cindex @code{logrepeat}, STARTUP keyword
 @cindex @code{nologrepeat}, STARTUP keyword
 @cindex @code{nologrepeat}, STARTUP keyword
 @example
 @example
-logging          @r{record a timestamp when an item is marked DONE}
+logdone          @r{record a timestamp when an item is marked DONE}
+logging          @r{alias for @code{logdone}}
 nologging        @r{don't record when items are marked DONE}
 nologging        @r{don't record when items are marked DONE}
 lognotedone      @r{record timestamp and a note when DONE}
 lognotedone      @r{record timestamp and a note when DONE}
+logstate         @r{record a time stamp when a state change occurs}
 lognotestate     @r{record timestamp and a note when TODO state changes}
 lognotestate     @r{record timestamp and a note when TODO state changes}
 logrepeat        @r{record a note when re-instating a repeating item}
 logrepeat        @r{record a note when re-instating a repeating item}
 nologrepeat      @r{do not record when re-instating repeating item}
 nologrepeat      @r{do not record when re-instating repeating item}