Fix explanation, if state change is blocked by contained checkboxes

* lisp/org.el (org-todo): Fix explanation, if state change is blocked
  by contained checkboxes.

Consider a node, which contains unchecked checkboxes; if you have set
org-enforce-todo-checkbox-dependencies and try to change the node to
DONE, you will be denied with a message explaining why. However in this
special case the explanation would be wrong in talking of an unrelated
node instead of the checkboxes.

The fix uses the already existing variable org-blocked-by-checkboxes
(which is handled in org-block-todo-from-checkboxes).  Similar code is
already present in org-agenda-dim-blocked-tasks within org-agenda.el.
This commit is contained in:
Marc Ihm 2017-04-14 17:49:50 +02:00 committed by Nicolas Goaziou
parent 6bd5210535
commit 165f7c3950
1 changed files with 31 additions and 27 deletions

View File

@ -12585,7 +12585,8 @@ nil or a string to be used for the todo mark." )
bound1 t)) bound1 t))
(replace-match "0" t nil nil 1))))) (replace-match "0" t nil nil 1)))))
(defvar org-state) ;; dynamically scoped into this function (defvar org-state)
(defvar org-blocked-by-checkboxes)
(defun org-todo (&optional arg) (defun org-todo (&optional arg)
"Change the TODO state of an item. "Change the TODO state of an item.
@ -12675,12 +12676,12 @@ When called through ELisp, arg is also interpreted in the following way:
(and (not arg) org-use-fast-todo-selection (and (not arg) org-use-fast-todo-selection
(not (eq org-use-fast-todo-selection (not (eq org-use-fast-todo-selection
'prefix))))) 'prefix)))))
;; Use fast selection ;; Use fast selection.
(org-fast-todo-selection)) (org-fast-todo-selection))
((and (equal arg '(4)) ((and (equal arg '(4))
(or (not org-use-fast-todo-selection) (or (not org-use-fast-todo-selection)
(not org-todo-key-trigger))) (not org-todo-key-trigger)))
;; Read a state with completion ;; Read a state with completion.
(completing-read (completing-read
"State: " (mapcar #'list org-todo-keywords-1) "State: " (mapcar #'list org-todo-keywords-1)
nil t)) nil t))
@ -12696,9 +12697,9 @@ When called through ELisp, arg is also interpreted in the following way:
org-todo-keywords-1) org-todo-keywords-1)
(org-last org-todo-keywords-1)))) (org-last org-todo-keywords-1))))
((and (eq org-use-fast-todo-selection t) (equal arg '(4)) ((and (eq org-use-fast-todo-selection t) (equal arg '(4))
(setq arg nil))) ; hack to fall back to cycling (setq arg nil))) ;hack to fall back to cycling
(arg (arg
;; user or caller requests a specific state ;; User or caller requests a specific state.
(cond (cond
((equal arg "") nil) ((equal arg "") nil)
((eq arg 'none) nil) ((eq arg 'none) nil)
@ -12716,8 +12717,8 @@ When called through ELisp, arg is also interpreted in the following way:
((nth (1- (prefix-numeric-value arg)) ((nth (1- (prefix-numeric-value arg))
org-todo-keywords-1)))) org-todo-keywords-1))))
((null member) (or head (car org-todo-keywords-1))) ((null member) (or head (car org-todo-keywords-1)))
((equal this final-done-word) nil) ;; -> make empty ((equal this final-done-word) nil) ;-> make empty
((null tail) nil) ;; -> first entry ((null tail) nil) ;-> first entry
((memq interpret '(type priority)) ((memq interpret '(type priority))
(if (eq this-command last-command) (if (eq this-command last-command)
(car tail) (car tail)
@ -12735,20 +12736,24 @@ When called through ELisp, arg is also interpreted in the following way:
:position startpos)) :position startpos))
dolog now-done-p) dolog now-done-p)
(when org-blocker-hook (when org-blocker-hook
(setq org-last-todo-state-is-todo (let (org-blocked-by-checkboxes block-reason)
(not (member this org-done-keywords))) (setq org-last-todo-state-is-todo
(unless (save-excursion (not (member this org-done-keywords)))
(save-match-data (unless (save-excursion
(org-with-wide-buffer (save-match-data
(run-hook-with-args-until-failure (org-with-wide-buffer
'org-blocker-hook change-plist)))) (run-hook-with-args-until-failure
(if (called-interactively-p 'interactive) 'org-blocker-hook change-plist))))
(user-error "TODO state change from %s to %s blocked (by \"%s\")" (setq block-reason (if org-blocked-by-checkboxes
this org-state org-block-entry-blocking) "contained checkboxes"
;; fail silently (format "\"%s\"" org-block-entry-blocking)))
(message "TODO state change from %s to %s blocked (by \"%s\")" (if (called-interactively-p 'interactive)
this org-state org-block-entry-blocking) (user-error "TODO state change from %s to %s blocked (by %s)"
(throw 'exit nil)))) this org-state block-reason)
;; Fail silently.
(message "TODO state change from %s to %s blocked (by %s)"
this org-state block-reason)
(throw 'exit nil)))))
(store-match-data match-data) (store-match-data match-data)
(replace-match next t t) (replace-match next t t)
(cond ((equal this org-state) (cond ((equal this org-state)
@ -12775,7 +12780,7 @@ When called through ELisp, arg is also interpreted in the following way:
(when (and (or org-todo-log-states org-log-done) (when (and (or org-todo-log-states org-log-done)
(not (eq org-inhibit-logging t)) (not (eq org-inhibit-logging t))
(not (memq arg '(nextset previousset)))) (not (memq arg '(nextset previousset))))
;; we need to look at recording a time and note ;; We need to look at recording a time and note.
(setq dolog (or (nth 1 (assoc org-state org-todo-log-states)) (setq dolog (or (nth 1 (assoc org-state org-todo-log-states))
(nth 2 (assoc this org-todo-log-states)))) (nth 2 (assoc this org-todo-log-states))))
(when (and (eq dolog 'note) (eq org-inhibit-logging 'note)) (when (and (eq dolog 'note) (eq org-inhibit-logging 'note))
@ -12788,14 +12793,14 @@ When called through ELisp, arg is also interpreted in the following way:
;; If there was a CLOSED time stamp, get rid of it. ;; If there was a CLOSED time stamp, get rid of it.
(org-add-planning-info nil nil 'closed)) (org-add-planning-info nil nil 'closed))
(when (and now-done-p org-log-done) (when (and now-done-p org-log-done)
;; 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-effective-time)) (org-add-planning-info 'closed (org-current-effective-time))
(when (and (not dolog) (eq 'note org-log-done)) (when (and (not dolog) (eq 'note org-log-done))
(org-add-log-setup 'done org-state this 'note))) (org-add-log-setup 'done org-state this 'note)))
(when (and org-state dolog) (when (and org-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-setup 'state org-state this dolog))) (org-add-log-setup 'state org-state this dolog)))
;; Fixup tag positioning ;; Fixup tag positioning.
(org-todo-trigger-tag-changes org-state) (org-todo-trigger-tag-changes org-state)
(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))
(when org-provide-todo-statistics (when org-provide-todo-statistics
@ -12812,7 +12817,7 @@ When called through ELisp, arg is also interpreted in the following way:
(setq org-agenda-headline-snapshot-before-repeat (setq org-agenda-headline-snapshot-before-repeat
(org-get-heading)))) (org-get-heading))))
(org-auto-repeat-maybe org-state)) (org-auto-repeat-maybe org-state))
;; Fixup cursor location if close to the keyword ;; Fixup cursor location if close to the keyword.
(when (and (outline-on-heading-p) (when (and (outline-on-heading-p)
(not (bolp)) (not (bolp))
(save-excursion (beginning-of-line 1) (save-excursion (beginning-of-line 1)
@ -12931,7 +12936,6 @@ See variable `org-track-ordered-property-with-tag'."
(and tag (org-toggle-tag tag 'on)) (and tag (org-toggle-tag tag 'on))
(message "Subtasks must be completed in sequence"))))) (message "Subtasks must be completed in sequence")))))
(defvar org-blocked-by-checkboxes) ; dynamically scoped
(defun org-block-todo-from-checkboxes (change-plist) (defun org-block-todo-from-checkboxes (change-plist)
"Block turning an entry into a TODO, using checkboxes. "Block turning an entry into a TODO, using checkboxes.
This checks whether the current task should be blocked from state This checks whether the current task should be blocked from state