123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- ;;; bir-mercy.el --- Mercy interface for BIR -*- lexical-binding: t; -*-
- ;; Copyright (C) 2022 c1-g
- ;; Author: c1-g <char1iegordon@protonmail.com>
- ;; Keywords: extensions
- ;; This program is free software; you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation, either version 3 of the License, or
- ;; (at your option) any later version.
- ;; This program is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
- ;; You should have received a copy of the GNU General Public License
- ;; along with this program. If not, see <https://www.gnu.org/licenses/>.
- ;;; Commentary:
- ;;
- ;;; Code:
- (require 'widget)
- (eval-when-compile
- (require 'wid-edit))
- (defvar-local bir-mercy--last-edited-field nil)
- (defvar-local bir-mercy--total nil)
- (defvar-local bir-mercy--origin-buffer nil)
- (defun bir-mercy-time-in (days)
- (let ((seconds (* days 60 60 24))
- (now (time-to-seconds)))
- (format-time-string "%b %d, %Y" (seconds-to-time (+ now seconds)))))
- (defmacro bir-mercy-with-widget-deactivated (widget &rest body)
- (declare (debug (body)))
- `(let ((widget ,widget))
- (if (widget-apply ,widget :active)
- (progn ,@body)
- (widget-apply ,widget :activate)
- (progn ,@body)
- (widget-apply ,widget :deactivate))))
- (defun bir-mercy (&optional match scope &rest skip)
- (interactive)
- (let* ((buf (get-buffer-create "*Mercy scheduling*"))
- (ids (org-map-entries #'org-id-get-create match scope skip))
- (total (length ids))
- (origin (current-buffer))
- (inhibit-read-only t))
- (with-current-buffer buf
- (remove-overlays)
- (erase-buffer)
- (kill-all-local-variables)
- (setq bir-mercy--origin-buffer origin)
- (setq bir-mercy--total total)
- (add-hook 'after-change-functions #'bir-maybe-get-widget-after-change nil t)
- (add-hook 'after-change-functions #'widget-after-change nil t)
- (widget-insert (format "Elements to schedule %s"
- (propertize (make-string 1 ?\s) 'display '(space :align-to 45))))
- (widget-apply (widget-create 'integer
- :size 7
- :tag 'total
- :format "%v\n\n"
- total)
- :deactivate)
- (widget-insert (make-string fill-column ?-) "\n\n")
- (widget-create 'integer
- :size 10
- :valid-regexp "[[:digit:]]+"
- :tag 'elt-per-day
- :format (format "Number of elements per day: %s%%v"
- (propertize (make-string 1 ?\s) 'display '(space :align-to 45)))
- total)
- (widget-insert "\n\n")
- (widget-create 'integer
- :valid-regexp "[[:digit:]]+"
- :tag 'period
- :size 10
- :format (format "Scheduling period: %s%%v"
- (propertize (make-string 1 ?\s) 'display '(space :align-to 45)))
- 1)
- (widget-apply (widget-create 'text
- :tag 'date
- :size 12
- :format " %v\n\n"
- (bir-mercy-time-in 1))
- :deactivate)
- (widget-insert (make-string fill-column ?-) "\n\n")
- (widget-insert "Choosing OK will result in scheduling ")
- (widget-apply (widget-create 'integer
- :size 7
- :tag 'total
- :format "%v"
- total)
- :deactivate)
- (widget-insert " elements collected from\na collecting period of ")
- (widget-apply (widget-create 'integer
- :valid-regexp "[[:digit:]]+"
- :size 4
- :tag 'period
- :format "%v"
- 1)
- :deactivate)
- (widget-insert " days(s) in a period from today till ")
- (widget-apply (widget-create 'text
- :tag 'date
- :size 12
- :format "%v\n"
- (bir-mercy-time-in 1))
- :deactivate)
- (widget-insert "with ")
- (widget-apply (widget-create 'integer
- :valid-regexp "[[:digit:]]+"
- :size 5
- :tag 'elt-per-day
- :format "%v"
- total)
- :deactivate)
- (widget-insert " repetitions per day.")
- (widget-insert "\n\n")
- (widget-insert (propertize (make-string 1 ?\s) 'display '(space :align-to 40)))
- (widget-insert " ")
- (widget-create 'push-button
- :notify (apply-partially #'bir-mercy-complete ids)
- :button-face 'org-checkbox-statistics-done
- "✓ OK")
- (widget-insert " ")
- (widget-create 'push-button
- :notify (lambda (&rest ignore)
- (kill-buffer))
- :button-face 'org-checkbox-statistics-todo
- "❌ Cancel")
- (widget-insert " ")
- (widget-create 'push-button
- :notify (lambda (&rest ignore)
- (bir-mercy-update bir-mercy--last-edited-field))
- :button-face 'org-date
- "⭯ Update")
- (widget-insert " ")
- (use-local-map widget-keymap)
- (widget-setup)
- (display-buffer-in-side-window buf '((dedicated . t)
- (side . right)))
- (select-window (get-buffer-window buf))
- (let ((fit-window-to-buffer-horizontally t))
- (fit-window-to-buffer)))))
- (defun bir-maybe-get-widget-after-change (from to _old)
- (let ((field (widget-field-find from))
- (other (widget-field-find to)))
- (when field
- (unless (eq field other)
- (error "Change in different fields"))
- (setq-local bir-mercy--last-edited-field field))))
- (defun bir-mercy-complete (ids &rest _ignore)
- "docstring"
- (let* ((widgets (cl-remove-if-not
- (lambda (w)
- (widget-apply w :active))
- widget-field-list))
- (elt-per-day (widget-value (seq-find (lambda (w)
- (eq 'elt-per-day (widget-get w :tag)))
- widgets)))
- (period (widget-value (seq-find (lambda (w)
- (eq 'period (widget-get w :tag)))
- widgets)))
- (ids (seq-partition ids elt-per-day)))
- (with-current-buffer bir-mercy--origin-buffer
- (save-excursion
- (dotimes (i period)
- (dolist (id (nth i ids))
- (goto-char (org-find-entry-with-id id))
- (org-schedule nil (format "+%d" i))))))))
- (defun bir-mercy-update (field)
- (when field
- (let* ((widgets (seq-group-by (lambda (w)
- (widget-get w :tag))
- widget-field-list))
- (active-period-widget (car
- (cl-remove-if-not
- (lambda (w)
- (widget-apply w :active))
- (alist-get 'period widgets))))
- (tag (widget-get field :tag))
- (period))
- (when (memq tag '(elt-per-day period))
- (dolist (widget (alist-get tag widgets))
- (bir-mercy-with-widget-deactivated
- widget
- (widget-value-set
- widget
- (widget-value field))))
- (dolist (widget (alist-get (car (delq tag '(elt-per-day period))) widgets))
- (bir-mercy-with-widget-deactivated
- widget
- (widget-value-set
- widget
- (round (/ bir-mercy--total
- (widget-value field)))))))
- (when active-period-widget
- (dolist (date-widget (alist-get 'date widgets))
- (bir-mercy-with-widget-deactivated
- date-widget
- (widget-value-set
- date-widget
- (bir-mercy-time-in (widget-value active-period-widget))))))
- (widget-setup))))
- (provide 'bir-mercy)
- ;;; bir-mercy.el ends here
|