summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien <bzg@gnu.org>2020-02-12 01:51:46 +0100
committerBastien <bzg@gnu.org>2020-02-12 01:51:46 +0100
commit4028cc731b34a6783a411a36f27e6c44f612679c (patch)
tree0252a2edfb2a33be80596c7a630d20b5ef2da9fa
parent561feb128d9d1067e613cce632c411ad6d9a4669 (diff)
downloadorg-mode-4028cc731b34a6783a411a36f27e6c44f612679c.tar.gz
Use "#+attr_org: :radio" before lists to enable radio buttons
* doc/org-manual.org (Checkboxes): Document the use of "#+attr_org". * lisp/org.el (org-ctrl-c-ctrl-c): When the list at point is preceded by "#+attr_org: :radio" use `org-toggle-radio-button' instead of `org-toggle-checkbox'. * lisp/org-list.el (org-at-radio-list-p): New defsubst. (org-toggle-checkbox): Use it. * etc/ORG-NEWS: Document the use of "#+attr_org".
-rw-r--r--doc/org-manual.org7
-rw-r--r--etc/ORG-NEWS3
-rw-r--r--lisp/org-list.el182
-rw-r--r--lisp/org.el11
4 files changed, 111 insertions, 92 deletions
diff --git a/doc/org-manual.org b/doc/org-manual.org
index fa6fad1..1487089 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -4539,12 +4539,13 @@ The following commands work with checkboxes:
Toggle checkbox status by using the checkbox of the item at point as
a radio button: when turned on, all other checkboxes on the same
level will be turned off. With a universal prefix argument, toggle
- the presence of the checkbox. With double prefix argument, set it
+ the presence of the checkbox. With a double prefix argument, set it
to =[-]=.
#+findex: org-list-checkbox-radio-mode
- {{{kdb(C-c C-c)}}} can be told to consider checkboxes as radio buttons
- by calling {{{kbd(M-x org-list-checkbox-radio-mode)}}}, as minor mode.
+ {{{kdb(C-c C-c)}}} can be told to consider checkboxes as radio buttons by
+ setting =#+ATTR_ORG: :radio= right before the list or by calling
+ {{{kbd(M-x org-list-checkbox-radio-mode)}}} to activate this minor mode.
- {{{kbd(M-S-RET)}}} (~org-insert-todo-heading~) ::
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 06f0f74..8ed6a1a 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -54,6 +54,9 @@ If you want to occasionally toggle a checkbox as a radio button
without turning this minor mode on, you can use =<C-c C-x C-r>= to
call ~org-toggle-radio-button~.
+You can also add =#+ATTR_ORG: :radio= right before the list to tell
+Org to use radio buttons for this list only.
+
*** Looping agenda commands over headlines
~org-agenda-loop-over-headlines-in-active-region~ allows you to loop
diff --git a/lisp/org-list.el b/lisp/org-list.el
index edba22c..6d50ea0 100644
--- a/lisp/org-list.el
+++ b/lisp/org-list.el
@@ -2337,6 +2337,16 @@ is an integer, 0 means `-', 1 means `+' etc. If WHICH is
(org-list-struct-apply-struct struct old-struct)
(org-update-checkbox-count-maybe))))
+(defsubst org-at-radio-list-p ()
+ "Is point in a list with radio buttons?"
+ (let (attr)
+ (save-excursion
+ (org-at-item-p)
+ (goto-char (caar (org-list-struct)))
+ (org-backward-element)
+ (setq attr (car (org-element-property :attr_org (org-element-at-point))))
+ (when attr (string-match-p ":radio" attr)))))
+
(defun org-toggle-checkbox (&optional toggle-presence)
"Toggle the checkbox in the current line.
@@ -2351,92 +2361,94 @@ If point is on a headline, apply this to all checkbox items in
the text below the heading, taking as reference the first item in
subtree, ignoring planning line and any drawer following it."
(interactive "P")
- (save-excursion
- (let* (singlep
- block-item
- lim-up
- lim-down
- (orderedp (org-entry-get nil "ORDERED"))
- (_bounds
- ;; In a region, start at first item in region.
+ (if (org-at-radio-list-p)
+ (org-toggle-radio-button toggle-presence)
+ (save-excursion
+ (let* (singlep
+ block-item
+ lim-up
+ lim-down
+ (orderedp (org-entry-get nil "ORDERED"))
+ (_bounds
+ ;; In a region, start at first item in region.
+ (cond
+ ((org-region-active-p)
+ (let ((limit (region-end)))
+ (goto-char (region-beginning))
+ (if (org-list-search-forward (org-item-beginning-re) limit t)
+ (setq lim-up (point-at-bol))
+ (error "No item in region"))
+ (setq lim-down (copy-marker limit))))
+ ((org-at-heading-p)
+ ;; On a heading, start at first item after drawers and
+ ;; time-stamps (scheduled, etc.).
+ (let ((limit (save-excursion (outline-next-heading) (point))))
+ (org-end-of-meta-data t)
+ (if (org-list-search-forward (org-item-beginning-re) limit t)
+ (setq lim-up (point-at-bol))
+ (error "No item in subtree"))
+ (setq lim-down (copy-marker limit))))
+ ;; Just one item: set SINGLEP flag.
+ ((org-at-item-p)
+ (setq singlep t)
+ (setq lim-up (point-at-bol)
+ lim-down (copy-marker (point-at-eol))))
+ (t (error "Not at an item or heading, and no active region"))))
+ ;; Determine the checkbox going to be applied to all items
+ ;; within bounds.
+ (ref-checkbox
+ (progn
+ (goto-char lim-up)
+ (let ((cbox (and (org-at-item-checkbox-p) (match-string 1))))
+ (cond
+ ((equal toggle-presence '(16)) "[-]")
+ ((equal toggle-presence '(4))
+ (unless cbox "[ ]"))
+ ((equal "[X]" cbox) "[ ]")
+ (t "[X]"))))))
+ ;; When an item is found within bounds, grab the full list at
+ ;; point structure, then: (1) set check-box of all its items
+ ;; within bounds to REF-CHECKBOX, (2) fix check-boxes of the
+ ;; whole list, (3) move point after the list.
+ (goto-char lim-up)
+ (while (and (< (point) lim-down)
+ (org-list-search-forward (org-item-beginning-re)
+ lim-down 'move))
+ (let* ((struct (org-list-struct))
+ (struct-copy (copy-tree struct))
+ (parents (org-list-parents-alist struct))
+ (prevs (org-list-prevs-alist struct))
+ (bottom (copy-marker (org-list-get-bottom-point struct)))
+ (items-to-toggle (cl-remove-if
+ (lambda (e) (or (< e lim-up) (> e lim-down)))
+ (mapcar #'car struct))))
+ (mapc (lambda (e) (org-list-set-checkbox
+ e struct
+ ;; If there is no box at item, leave as-is
+ ;; unless function was called with C-u prefix.
+ (let ((cur-box (org-list-get-checkbox e struct)))
+ (if (or cur-box (equal toggle-presence '(4)))
+ ref-checkbox
+ cur-box))))
+ items-to-toggle)
+ (setq block-item (org-list-struct-fix-box
+ struct parents prevs orderedp))
+ ;; Report some problems due to ORDERED status of subtree.
+ ;; If only one box was being checked, throw an error, else,
+ ;; only signal problems.
(cond
- ((org-region-active-p)
- (let ((limit (region-end)))
- (goto-char (region-beginning))
- (if (org-list-search-forward (org-item-beginning-re) limit t)
- (setq lim-up (point-at-bol))
- (error "No item in region"))
- (setq lim-down (copy-marker limit))))
- ((org-at-heading-p)
- ;; On a heading, start at first item after drawers and
- ;; time-stamps (scheduled, etc.).
- (let ((limit (save-excursion (outline-next-heading) (point))))
- (org-end-of-meta-data t)
- (if (org-list-search-forward (org-item-beginning-re) limit t)
- (setq lim-up (point-at-bol))
- (error "No item in subtree"))
- (setq lim-down (copy-marker limit))))
- ;; Just one item: set SINGLEP flag.
- ((org-at-item-p)
- (setq singlep t)
- (setq lim-up (point-at-bol)
- lim-down (copy-marker (point-at-eol))))
- (t (error "Not at an item or heading, and no active region"))))
- ;; Determine the checkbox going to be applied to all items
- ;; within bounds.
- (ref-checkbox
- (progn
- (goto-char lim-up)
- (let ((cbox (and (org-at-item-checkbox-p) (match-string 1))))
- (cond
- ((equal toggle-presence '(16)) "[-]")
- ((equal toggle-presence '(4))
- (unless cbox "[ ]"))
- ((equal "[X]" cbox) "[ ]")
- (t "[X]"))))))
- ;; When an item is found within bounds, grab the full list at
- ;; point structure, then: (1) set check-box of all its items
- ;; within bounds to REF-CHECKBOX, (2) fix check-boxes of the
- ;; whole list, (3) move point after the list.
- (goto-char lim-up)
- (while (and (< (point) lim-down)
- (org-list-search-forward (org-item-beginning-re)
- lim-down 'move))
- (let* ((struct (org-list-struct))
- (struct-copy (copy-tree struct))
- (parents (org-list-parents-alist struct))
- (prevs (org-list-prevs-alist struct))
- (bottom (copy-marker (org-list-get-bottom-point struct)))
- (items-to-toggle (cl-remove-if
- (lambda (e) (or (< e lim-up) (> e lim-down)))
- (mapcar #'car struct))))
- (mapc (lambda (e) (org-list-set-checkbox
- e struct
- ;; If there is no box at item, leave as-is
- ;; unless function was called with C-u prefix.
- (let ((cur-box (org-list-get-checkbox e struct)))
- (if (or cur-box (equal toggle-presence '(4)))
- ref-checkbox
- cur-box))))
- items-to-toggle)
- (setq block-item (org-list-struct-fix-box
- struct parents prevs orderedp))
- ;; Report some problems due to ORDERED status of subtree.
- ;; If only one box was being checked, throw an error, else,
- ;; only signal problems.
- (cond
- ((and singlep block-item (> lim-up block-item))
- (error
- "Checkbox blocked because of unchecked box at line %d"
- (org-current-line block-item)))
- (block-item
- (message
- "Checkboxes were removed due to unchecked box at line %d"
- (org-current-line block-item))))
- (goto-char bottom)
- (move-marker bottom nil)
- (org-list-struct-apply-struct struct struct-copy)))
- (move-marker lim-down nil)))
+ ((and singlep block-item (> lim-up block-item))
+ (error
+ "Checkbox blocked because of unchecked box at line %d"
+ (org-current-line block-item)))
+ (block-item
+ (message
+ "Checkboxes were removed due to unchecked box at line %d"
+ (org-current-line block-item))))
+ (goto-char bottom)
+ (move-marker bottom nil)
+ (org-list-struct-apply-struct struct struct-copy)))
+ (move-marker lim-down nil))))
(org-update-checkbox-count-maybe))
(defun org-reset-checkbox-state-subtree ()
diff --git a/lisp/org.el b/lisp/org.el
index f117401..464387b 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -17171,6 +17171,7 @@ This command does many different things, depending on context:
src-block statistics-cookie table table-cell table-row
timestamp)
t))
+ (radio-list-p (org-at-radio-list-p))
(type (org-element-type context)))
;; For convenience: at the first line of a paragraph on the same
;; line as an item, apply function on that item instead.
@@ -17217,8 +17218,9 @@ This command does many different things, depending on context:
;; unconditionally, whereas `C-u' will toggle its presence.
;; Without a universal argument, if the item has a checkbox,
;; toggle it. Otherwise repair the list.
- (if (and (boundp org-list-checkbox-radio-mode)
- org-list-checkbox-radio-mode)
+ (if (or radio-list-p
+ (and (boundp org-list-checkbox-radio-mode)
+ org-list-checkbox-radio-mode))
(org-toggle-radio-button arg)
(let* ((box (org-element-property :checkbox context))
(struct (org-element-property :structure context))
@@ -17259,8 +17261,9 @@ This command does many different things, depending on context:
;; will toggle their presence according to the state of the
;; first item in the list. Without an argument, repair the
;; list.
- (if (and (boundp org-list-checkbox-radio-mode)
- org-list-checkbox-radio-mode)
+ (if (or radio-list-p
+ (and (boundp org-list-checkbox-radio-mode)
+ org-list-checkbox-radio-mode))
(org-toggle-radio-button arg)
(let* ((begin (org-element-property :contents-begin context))
(struct (org-element-property :structure context))