summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien <bzg@gnu.org>2020-02-11 23:55:53 +0100
committerBastien <bzg@gnu.org>2020-02-11 23:55:53 +0100
commit561feb128d9d1067e613cce632c411ad6d9a4669 (patch)
tree509362fd7896233a5b5d2670446fb66eaf8b976a
parent8f59e01aa89fd416eabca5b5ce3c39e3dc2beeb6 (diff)
downloadorg-mode-561feb128d9d1067e613cce632c411ad6d9a4669.tar.gz
Add `org-toggle-radio-button' and related minor mode
* doc/org-manual.org (Checkboxes): Document the new minor mode and command. * lisp/org-keys.el (org-mode-map): Bind C-c C-x C-r to `org-toggle-radio-button'. * lisp/org-list.el (org-list-checkbox-radio-mode): New minor mode to let C-c C-c call `org-toggle-radio-button' instead of `org-toggle-checkbox'. (org-toggle-radio-button): New command. * lisp/org.el (org-ctrl-c-ctrl-c): Use `org-toggle-radio-button'. * etc/ORG-NEWS: Document the new minor mode and command. Thanks to Phil Sainty for sharing this idea and links to similar implementations.
-rw-r--r--doc/org-manual.org15
-rw-r--r--etc/ORG-NEWS10
-rw-r--r--lisp/org-keys.el2
-rw-r--r--lisp/org-list.el35
-rw-r--r--lisp/org.el140
5 files changed, 135 insertions, 67 deletions
diff --git a/doc/org-manual.org b/doc/org-manual.org
index f9f0406..fa6fad1 100644
--- a/doc/org-manual.org
+++ b/doc/org-manual.org
@@ -4531,6 +4531,21 @@ The following commands work with checkboxes:
- If there is no active region, just toggle the checkbox at point.
+- {{{kbd(C-c C-x C-r)}}} (~org-toggle-radio-button~) ::
+
+ #+kindex: C-c C-x C-r
+ #+findex: org-toggle-radio-button
+ #+cindex: radio button, checkbox as
+ 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
+ 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.
+
- {{{kbd(M-S-RET)}}} (~org-insert-todo-heading~) ::
#+kindex: M-S-RET
diff --git a/etc/ORG-NEWS b/etc/ORG-NEWS
index 5e50b96..06f0f74 100644
--- a/etc/ORG-NEWS
+++ b/etc/ORG-NEWS
@@ -44,6 +44,16 @@ You can activate this minor mode by default by setting the option
~org-table-header-line-p~ to =t=. You can also change the face for
the header line by customizing the ~org-table-header~ face.
+*** New minor mode ~org-list-checkbox-radio-mode~
+
+When this minor mode is on, checkboxes behave as radio buttons: if a
+checkbox is turned on, other checkboxes at the same level are turned
+off.
+
+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~.
+
*** Looping agenda commands over headlines
~org-agenda-loop-over-headlines-in-active-region~ allows you to loop
diff --git a/lisp/org-keys.el b/lisp/org-keys.el
index 7f86831..93e1bd7 100644
--- a/lisp/org-keys.el
+++ b/lisp/org-keys.el
@@ -196,6 +196,7 @@
(declare-function org-todo "org" (&optional arg1))
(declare-function org-toggle-archive-tag "org" (&optional find-done))
(declare-function org-toggle-checkbox "org" (&optional toggle-presence))
+(declare-function org-toggle-radio-button "org" (&optional arg))
(declare-function org-toggle-comment "org" ())
(declare-function org-toggle-fixed-width "org" ())
(declare-function org-toggle-inline-images "org" (&optional include-linked))
@@ -658,6 +659,7 @@ COMMANDS is a list of alternating OLDDEF NEWDEF command names."
(org-defkey org-mode-map (kbd "C-c C-x C-M-v") #'org-redisplay-inline-images)
(org-defkey org-mode-map (kbd "C-c C-x \\") #'org-toggle-pretty-entities)
(org-defkey org-mode-map (kbd "C-c C-x C-b") #'org-toggle-checkbox)
+(org-defkey org-mode-map (kbd "C-c C-x C-r") #'org-toggle-radio-button)
(org-defkey org-mode-map (kbd "C-c C-x p") #'org-set-property)
(org-defkey org-mode-map (kbd "C-c C-x P") #'org-set-property-and-value)
(org-defkey org-mode-map (kbd "C-c C-x e") #'org-set-effort)
diff --git a/lisp/org-list.el b/lisp/org-list.el
index b614089..edba22c 100644
--- a/lisp/org-list.el
+++ b/lisp/org-list.el
@@ -2302,6 +2302,41 @@ is an integer, 0 means `-', 1 means `+' etc. If WHICH is
(org-list-struct-fix-ind struct parents)
(org-list-struct-apply-struct struct old-struct)))))
+;;;###autoload
+(define-minor-mode org-list-checkbox-radio-mode
+ "When turned on, use list checkboxes as radio buttons."
+ nil " CheckBoxRadio" nil
+ (unless (eq major-mode 'org-mode)
+ (user-error "Cannot turn this mode outside org-mode buffers")))
+
+(defun org-toggle-radio-button (&optional arg)
+ "Toggle off all checkboxes and toggle on the one at point."
+ (interactive "P")
+ (if (not (org-at-item-p))
+ (user-error "Cannot toggle checkbox outside of a list")
+ (let* ((cpos (org-in-item-p))
+ (struct (org-list-struct))
+ (orderedp (org-entry-get nil "ORDERED"))
+ (parents (org-list-parents-alist struct))
+ (old-struct (copy-tree struct))
+ (cbox (org-list-get-checkbox cpos struct))
+ (prevs (org-list-prevs-alist struct))
+ (start (org-list-get-list-begin (point-at-bol) struct prevs))
+ (new (unless (and cbox (equal arg '(4)) (equal start cpos))
+ "[ ]")))
+ (dolist (pos (org-list-get-all-items
+ start struct (org-list-prevs-alist struct)))
+ (org-list-set-checkbox pos struct new))
+ (when new
+ (org-list-set-checkbox
+ cpos struct
+ (cond ((equal arg '(4)) (unless cbox "[ ]"))
+ ((equal arg '(16)) (unless cbox "[-]"))
+ (t (if (equal cbox "[X]") "[ ]" "[X]")))))
+ (org-list-struct-fix-box struct parents prevs orderedp)
+ (org-list-struct-apply-struct struct old-struct)
+ (org-update-checkbox-count-maybe))))
+
(defun org-toggle-checkbox (&optional toggle-presence)
"Toggle the checkbox in the current line.
diff --git a/lisp/org.el b/lisp/org.el
index f7fa915..f117401 100644
--- a/lisp/org.el
+++ b/lisp/org.el
@@ -17217,39 +17217,79 @@ 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.
- (let* ((box (org-element-property :checkbox context))
- (struct (org-element-property :structure context))
- (old-struct (copy-tree struct))
- (parents (org-list-parents-alist struct))
- (prevs (org-list-prevs-alist struct))
- (orderedp (org-not-nil (org-entry-get nil "ORDERED"))))
- (org-list-set-checkbox
- (org-element-property :begin context) struct
- (cond ((equal arg '(16)) "[-]")
- ((and (not box) (equal arg '(4))) "[ ]")
- ((or (not box) (equal arg '(4))) nil)
- ((eq box 'on) "[ ]")
- (t "[X]")))
- ;; Mimic `org-list-write-struct' but with grabbing a return
- ;; value from `org-list-struct-fix-box'.
- (org-list-struct-fix-ind struct parents 2)
- (org-list-struct-fix-item-end struct)
- (org-list-struct-fix-bul struct prevs)
- (org-list-struct-fix-ind struct parents)
- (let ((block-item
- (org-list-struct-fix-box struct parents prevs orderedp)))
- (if (and box (equal struct old-struct))
- (if (equal arg '(16))
- (message "Checkboxes already reset")
- (user-error "Cannot toggle this checkbox: %s"
- (if (eq box 'on)
- "all subitems checked"
- "unchecked subitems")))
- (org-list-struct-apply-struct struct old-struct)
- (org-update-checkbox-count-maybe))
- (when block-item
- (message "Checkboxes were removed due to empty box at line %d"
- (org-current-line block-item))))))
+ (if (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))
+ (old-struct (copy-tree struct))
+ (parents (org-list-parents-alist struct))
+ (prevs (org-list-prevs-alist struct))
+ (orderedp (org-not-nil (org-entry-get nil "ORDERED"))))
+ (org-list-set-checkbox
+ (org-element-property :begin context) struct
+ (cond ((equal arg '(16)) "[-]")
+ ((and (not box) (equal arg '(4))) "[ ]")
+ ((or (not box) (equal arg '(4))) nil)
+ ((eq box 'on) "[ ]")
+ (t "[X]")))
+ ;; Mimic `org-list-write-struct' but with grabbing a return
+ ;; value from `org-list-struct-fix-box'.
+ (org-list-struct-fix-ind struct parents 2)
+ (org-list-struct-fix-item-end struct)
+ (org-list-struct-fix-bul struct prevs)
+ (org-list-struct-fix-ind struct parents)
+ (let ((block-item
+ (org-list-struct-fix-box struct parents prevs orderedp)))
+ (if (and box (equal struct old-struct))
+ (if (equal arg '(16))
+ (message "Checkboxes already reset")
+ (user-error "Cannot toggle this checkbox: %s"
+ (if (eq box 'on)
+ "all subitems checked"
+ "unchecked subitems")))
+ (org-list-struct-apply-struct struct old-struct)
+ (org-update-checkbox-count-maybe))
+ (when block-item
+ (message "Checkboxes were removed due to empty box at line %d"
+ (org-current-line block-item)))))))
+ (`plain-list
+ ;; At a plain list, with a double C-u argument, set
+ ;; checkboxes of each item to "[-]", whereas a single one
+ ;; 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)
+ (org-toggle-radio-button arg)
+ (let* ((begin (org-element-property :contents-begin context))
+ (struct (org-element-property :structure context))
+ (old-struct (copy-tree struct))
+ (first-box (save-excursion
+ (goto-char begin)
+ (looking-at org-list-full-item-re)
+ (match-string-no-properties 3)))
+ (new-box (cond ((equal arg '(16)) "[-]")
+ ((equal arg '(4)) (unless first-box "[ ]"))
+ ((equal first-box "[X]") "[ ]")
+ (t "[X]"))))
+ (cond
+ (arg
+ (dolist (pos
+ (org-list-get-all-items
+ begin struct (org-list-prevs-alist struct)))
+ (org-list-set-checkbox pos struct new-box)))
+ ((and first-box (eq (point) begin))
+ ;; For convenience, when point is at bol on the first
+ ;; item of the list and no argument is provided, simply
+ ;; toggle checkbox of that item, if any.
+ (org-list-set-checkbox begin struct new-box)))
+ (when (equal
+ (org-list-write-struct
+ struct (org-list-parents-alist struct) old-struct)
+ old-struct)
+ (message "Cannot update this checkbox"))
+ (org-update-checkbox-count-maybe))))
(`keyword
(let ((org-inhibit-startup-visibility-stuff t)
(org-startup-align-all-tables nil))
@@ -17258,40 +17298,6 @@ This command does many different things, depending on context:
(setq org-table-coordinate-overlays nil))
(org-save-outline-visibility 'use-markers (org-mode-restart)))
(message "Local setup has been refreshed"))
- (`plain-list
- ;; At a plain list, with a double C-u argument, set
- ;; checkboxes of each item to "[-]", whereas a single one
- ;; will toggle their presence according to the state of the
- ;; first item in the list. Without an argument, repair the
- ;; list.
- (let* ((begin (org-element-property :contents-begin context))
- (struct (org-element-property :structure context))
- (old-struct (copy-tree struct))
- (first-box (save-excursion
- (goto-char begin)
- (looking-at org-list-full-item-re)
- (match-string-no-properties 3)))
- (new-box (cond ((equal arg '(16)) "[-]")
- ((equal arg '(4)) (unless first-box "[ ]"))
- ((equal first-box "[X]") "[ ]")
- (t "[X]"))))
- (cond
- (arg
- (dolist (pos
- (org-list-get-all-items
- begin struct (org-list-prevs-alist struct)))
- (org-list-set-checkbox pos struct new-box)))
- ((and first-box (eq (point) begin))
- ;; For convenience, when point is at bol on the first
- ;; item of the list and no argument is provided, simply
- ;; toggle checkbox of that item, if any.
- (org-list-set-checkbox begin struct new-box)))
- (when (equal
- (org-list-write-struct
- struct (org-list-parents-alist struct) old-struct)
- old-struct)
- (message "Cannot update this checkbox"))
- (org-update-checkbox-count-maybe)))
((or `property-drawer `node-property)
(call-interactively #'org-property-action))
(`radio-target