10

I have several tasks in my Org file, one of which has items starting with a checkbox (but only those). They all have a statistic cookie at their end ([n/m]).

I'm looking for a way to automatically update the parent task from TODO to DONE once all of the checkboxes are marked as done as well.

The documentation says:

If you would like a TODO entry to automatically change to DONE when all children are done, you can use the following setup [...]

Another possibility is the use of checkboxes to identify (a hierarchy of) a large number of subtasks (see Checkboxes).

The way I understand that last part is that if children items are checkboxes, parent state should already be automatically updated (which is not the case).

What am I missing?


Edit:

From org-after-todo-statistics-hook's documentation (org.el):

Hook that is called after a TODO statistics cookie has been updated. Each function is called with two arguments: the number of not-done entries and the number of done entries.

Which means that the proposed code from orgmode.org:

(defun org-summary-todo (n-done n-not-done)
  "Switch entry to DONE when all subentries are done, to TODO otherwise."
  (let (org-log-done org-log-states)   ; turn off logging
    (org-todo (if (= n-not-done 0) "DONE" "TODO"))))

(add-hook 'org-after-todo-statistics-hook 'org-summary-todo)

won't count checkbox items.

  • Do you have a statistics cookie in the headline? Like this: * Heading [0/1] – erikstokes Jan 26 '16 at 14:27
  • Yes, there is. Forgot to mention that bit my bad. – Mathieu Marques Jan 26 '16 at 14:30
  • 1
    I think this misunderstanding comes from the org manual not being so clear in this case. The line that starts "Another possibility is the use of checkboxes..." is a new paragraph under the heading "Breaking tasks down into subtasks". I think that "another possibility" in this case is referring to another possibility for breaking tasks into subtasks not another possibility for automatically changing the TODO state. Maybe try their suggested function and hook (though I couldn't get this to wokr) – elethan Jan 26 '16 at 15:09
  • @elethan It actually makes sense, I guess. Yes see my edit :) – Mathieu Marques Jan 26 '16 at 15:26
  • This is a FAQ. – NickD Feb 11 at 19:17
5

As you mentioned in your updated question, org-after-todo-statistics-hook isn't what you want. What you want is org-checkbox-statistics-hook.

I use this function to do what you describe (they should really get this functionality into org-mode):

(defun my/org-checkbox-todo ()
  "Switch header TODO state to DONE when all checkboxes are ticked, to TODO otherwise"
  (let ((todo-state (org-get-todo-state)) beg end)
    (unless (not todo-state)
      (save-excursion
    (org-back-to-heading t)
    (setq beg (point))
    (end-of-line)
    (setq end (point))
    (goto-char beg)
    (if (re-search-forward "\\[\\([0-9]*%\\)\\]\\|\\[\\([0-9]*\\)/\\([0-9]*\\)\\]"
                   end t)
        (if (match-end 1)
        (if (equal (match-string 1) "100%")
            (unless (string-equal todo-state "DONE")
              (org-todo 'done))
          (unless (string-equal todo-state "TODO")
            (org-todo 'todo)))
          (if (and (> (match-end 2) (match-beginning 2))
               (equal (match-string 2) (match-string 3)))
          (unless (string-equal todo-state "DONE")
            (org-todo 'done))
        (unless (string-equal todo-state "TODO")
          (org-todo 'todo)))))))))

(add-hook 'org-checkbox-statistics-hook 'my/org-checkbox-todo) will it call it every time you toggle a checkbox.

It does require you have a statistics cookie (the thing you get with [/] or [%] then C-c C-c) in the header.

  • 1
    Do you happen to have a version that, when the TODO is scheduled to repeat, unchecks the checklist for the scheduling again? :) – ctietze May 16 '18 at 17:40
0

I wanted a version that does what @ctietze is looking for--toggle the checkbox state for items in the checklist. I tried calling org-reset-checkbox-state-subtree but got errors about too many nested frames (I'm using Spacemacs).

Here is my working solution:

    (defun my/org-reset-checkbox-state-subtree ()
    "Simplified version of org-list builtin"
    ;; Begin copy from org-reset-checkbox-subtree
    (org-narrow-to-subtree)
      (org-show-subtree)
      (goto-char (point-min))
      (let ((end (point-max)))
        (while (< (point) end)
          (when (org-at-item-checkbox-p)
            (replace-match "[ ]" t t nil 1))
          (beginning-of-line 2)))
      (org-update-checkbox-count-maybe 'all)
    ;; End copy from org-reset-checkbox-subtree
    )

  (defun my/org-checkbox-todo ()
    "Switch header TODO state to DONE when all checkboxes are ticked, to TODO otherwise"
    (let ((todo-state (org-get-todo-state)) beg end)
      (unless (not todo-state)
        (save-excursion
          (org-back-to-heading t)
          (setq beg (point))
          (end-of-line)
          (setq end (point))
          (goto-char beg)
          (if (re-search-forward "\\[\\([0-9]*%\\)\\]\\|\\[\\([0-9]*\\)/\\([0-9]*\\)\\]"
                                 end t)
              (if (match-end 1)
                  (if (equal (match-string 1) "100%")
                      (unless (string-equal todo-state "DONE")
                        (my/org-reset-checkbox-state-subtree)
                        (org-todo 'done))
                    (unless (string-equal todo-state "TODO")
                      (org-todo 'todo)))
                (if (and (> (match-end 2) (match-beginning 2))
                         (equal (match-string 2) (match-string 3)))
                    (unless (string-equal todo-state "DONE")
                      (my/org-reset-checkbox-state-subtree)
                      (org-todo 'done))
                  (unless (string-equal todo-state "TODO")
                    (org-todo 'todo)))))))))
  (add-hook 'org-checkbox-statistics-hook 'my/org-checkbox-todo)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.