aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Berman2012-12-12 20:53:49 +0100
committerStephen Berman2012-12-12 20:53:49 +0100
commitd16da867a96deafbfc9920e3f15970a8fe48161d (patch)
tree5b73cd5c0494574334106a399b0f6e741ea7b135
parentbbf95285a036398ed993e6051249f094b1c4d354 (diff)
downloademacs-d16da867a96deafbfc9920e3f15970a8fe48161d.tar.gz
emacs-d16da867a96deafbfc9920e3f15970a8fe48161d.zip
* calendar/todos.el: Extend and improve handling of item editing,
especially of date/time header. (todos-month-name-array, todos-month-abbrev-array): New defconsts. (todos-date-pattern): Use explicitly numbered groups. (todos-read-date): Optionally read and return just one of the date string components year, month or monthname, day. (todos-key-bindings): Add bindings for new item header editing commands. (todos-edit-item): Exclude date/time header from minibuffer by default; include it by passing a prefix argument. (todos-edit-item-header-1): New function containing the guts of the commands for editing item date/time headers. (todos-edit-item-header): Use it. Condition editing of time string on value of `todos-always-add-time-string'. (todos-edit-item-date-from-calendar): Use todos-edit-item-header-1. (todos-edit-item-date-to-today): Rename from todos-edit-item-date-is-today and use todos-edit-item-header-1. (todos-edit-item-date): Remove. (todos-edit-item-date-day-name, todos-edit-item-date-year) (todos-edit-item-date-month, todos-edit-item-date-day): New commands.
-rw-r--r--lisp/ChangeLog23
-rw-r--r--lisp/calendar/todos.el484
2 files changed, 374 insertions, 133 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 98df5d3fe92..bde403fe0d5 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,26 @@
12012-12-12 Stephen Berman <stephen.berman@gmx.net>
2
3 * calendar/todos.el: Extend and improve handling of item editing,
4 especially of date/time header.
5 (todos-month-name-array, todos-month-abbrev-array): New defconsts.
6 (todos-date-pattern): Use explicitly numbered groups.
7 (todos-read-date): Optionally read and return just one of the date
8 string components year, month or monthname, day.
9 (todos-key-bindings): Add bindings for new item header editing
10 commands.
11 (todos-edit-item): Exclude date/time header from minibuffer by
12 default; include it by passing a prefix argument.
13 (todos-edit-item-header-1): New function containing the guts of
14 the commands for editing item date/time headers.
15 (todos-edit-item-header): Use it. Condition editing of time
16 string on value of `todos-always-add-time-string'.
17 (todos-edit-item-date-from-calendar): Use todos-edit-item-header-1.
18 (todos-edit-item-date-to-today): Rename from
19 todos-edit-item-date-is-today and use todos-edit-item-header-1.
20 (todos-edit-item-date): Remove.
21 (todos-edit-item-date-day-name, todos-edit-item-date-year)
22 (todos-edit-item-date-month, todos-edit-item-date-day): New commands.
23
12012-12-02 Stephen Berman <stephen.berman@gmx.net> 242012-12-02 Stephen Berman <stephen.berman@gmx.net>
2 25
3 * calendar/todos.el (todos-show): Fix a comment. 26 * calendar/todos.el (todos-show): Fix a comment.
diff --git a/lisp/calendar/todos.el b/lisp/calendar/todos.el
index 70f144d8004..c7f2d5b8dee 100644
--- a/lisp/calendar/todos.el
+++ b/lisp/calendar/todos.el
@@ -1336,19 +1336,32 @@ it.")
1336 1336
1337;;; Global variables and helper functions for items 1337;;; Global variables and helper functions for items
1338 1338
1339(defconst todos-month-name-array
1340 (vconcat calendar-month-name-array (vector "*"))
1341 "Array of month names, in order.
1342The final element is \"*\", indicating an unspecified month.")
1343
1344(defconst todos-month-abbrev-array
1345 (vconcat calendar-month-abbrev-array (vector "*"))
1346 "Array of abbreviated month names, in order.
1347The final element is \"*\", indicating an unspecified month.")
1348
1339(defconst todos-date-pattern 1349(defconst todos-date-pattern
1340 (let ((dayname (diary-name-pattern calendar-day-name-array nil t))) 1350 (let ((dayname (diary-name-pattern calendar-day-name-array nil t)))
1341 (concat "\\(?:" dayname "\\|" 1351 (concat "\\(?5:" dayname "\\|"
1342 (let ((dayname) 1352 (let ((dayname)
1343 ;; FIXME: how to choose between abbreviated and unabbreviated 1353 ;; FIXME: how to choose between abbreviated and unabbreviated
1344 ;; month name? 1354 ;; month name?
1345 (monthname (format "\\(?:%s\\|\\*\\)" 1355 ;; (monthname (format "\\(?6:%s\\|\\*\\)"
1346 (diary-name-pattern 1356 ;; (diary-name-pattern
1347 calendar-month-name-array 1357 ;; calendar-month-name-array
1348 calendar-month-abbrev-array t))) 1358 ;; calendar-month-abbrev-array)))
1349 (month "\\(?:[0-9]+\\|\\*\\)") 1359 (monthname (format "\\(?6:%s\\)" (diary-name-pattern
1350 (day "\\(?:[0-9]+\\|\\*\\)") 1360 todos-month-name-array
1351 (year "-?\\(?:[0-9]+\\|\\*\\)")) 1361 todos-month-abbrev-array)))
1362 (month "\\(?7:[0-9]+\\|\\*\\)")
1363 (day "\\(?8:[0-9]+\\|\\*\\)")
1364 (year "-?\\(?9:[0-9]+\\|\\*\\)"))
1352 (mapconcat 'eval calendar-date-display-form "")) 1365 (mapconcat 'eval calendar-date-display-form ""))
1353 "\\)")) 1366 "\\)"))
1354 "Regular expression matching a Todos date header.") 1367 "Regular expression matching a Todos date header.")
@@ -1637,57 +1650,87 @@ TYPE can be either a file or a category"
1637 name)) 1650 name))
1638 1651
1639;; Adapted from calendar-read-date and calendar-date-string. 1652;; Adapted from calendar-read-date and calendar-date-string.
1640(defun todos-read-date () 1653(defun todos-read-date (&optional arg mo yr)
1641 "Prompt for Gregorian date and return it in the current format. 1654 "Prompt for Gregorian date and return it in the current format.
1642Also accepts `*' as an unspecified month, day, or year." 1655
1643 (let* ((year (let (x) 1656With non-nil ARG, prompt for and return only the date component
1644 (while (if (numberp x) (< x 0) (not (eq x '*))) 1657specified by ARG, which can be one of these symbols:
1645 (setq x (read-from-minibuffer 1658`month' (prompt for name, return name or number according to
1646 "Year (>0 or RET for this year or * for any year): " 1659value of `calendar-date-display-form'), `day' of month, or
1647 nil nil t nil (number-to-string 1660`year'. The value of each of these components can be `*',
1648 (calendar-extract-year 1661indicating an unspecified month, day, or year.
1649 (calendar-current-date)))))) 1662
1650 x)) 1663When ARG is `day', non-nil arguments MO and YR determine the
1651 (month-array (vconcat calendar-month-name-array (vector "*"))) 1664number of the last the day of the month."
1652 (abbrevs (vconcat calendar-month-abbrev-array (vector "*"))) 1665 (let (year monthname month day
1653 (completion-ignore-case todos-completion-ignore-case) 1666 dayname) ; Needed by calendar-date-display-form.
1654 (monthname (completing-read 1667 ;; FIXME: year can be omitted from Diary
1655 "Month name (RET for current month, * for any month): " 1668 (when (or (not arg) (eq arg 'year))
1656 (mapcar 'list (append month-array nil)) 1669 (while (if (natnump year) (< year 1) (not (eq year '*)))
1657 nil t nil nil 1670 (setq year (read-from-minibuffer
1658 (calendar-month-name (calendar-extract-month 1671 "Year (>0 or RET for this year or * for any year): "
1659 (calendar-current-date)) t))) 1672 nil nil t nil (number-to-string
1660 (month (cdr (assoc-string 1673 (calendar-extract-year
1661 monthname (calendar-make-alist month-array nil nil 1674 (calendar-current-date)))))))
1662 abbrevs)))) 1675 (when (or (not arg) (eq arg 'month))
1663 (last (if (= month 13) 1676 (let* ((marray todos-month-name-array)
1664 ;; Use longest possible month for checking day number 1677 (mlist (append marray nil))
1665 ;; input. Does Calendar do anything special when * is 1678 (mabarray todos-month-abbrev-array)
1666 ;; currently a shorter month? 1679 (mablist (append mabarray nil))
1667 31 1680 (completion-ignore-case todos-completion-ignore-case))
1668 (let ((yr (if (eq year '*) 1681 (setq monthname (completing-read
1669 ;; Use a leap year to allow Feb. 29. 1682 "Month name (RET for current month, * for any month): "
1670 2012 1683 ;; (mapcar 'list (append marray nil))
1671 year))) 1684 mlist nil t nil nil
1672 (calendar-last-day-of-month month yr)))) 1685 (calendar-month-name (calendar-extract-month
1673 (day (let (x) 1686 (calendar-current-date)) t))
1674 (while (if (numberp x) (or (< x 0) (< last x)) (not (eq x '*))) 1687 ;; month (cdr (assoc-string
1675 (setq x (read-from-minibuffer 1688 ;; monthname (calendar-make-alist marray nil nil
1676 (format 1689 ;; abbrevs))))))
1677 "Day (1-%d or RET for today or * for any day): " 1690 month (1+ (- (length mlist)
1678 last) nil nil t nil (number-to-string 1691 (length (or (member monthname mlist)
1679 (calendar-extract-day 1692 (member monthname mablist))))))
1680 (calendar-current-date)))))) 1693 ;; FIXME: We follow diary-insert-entry in using abbreviated
1681 x)) 1694 ;; month name (and no day name) in date string. Should this
1682 dayname) ; Needed by calendar-date-display-form. 1695 ;; be customizable?
1683 (setq year (if (eq year '*) (symbol-name '*) (number-to-string year))) 1696 (setq monthname (aref mabarray (1- month)))))
1684 (setq day (if (eq day '*) (symbol-name '*) (number-to-string day))) 1697 (when (or (not arg) (eq arg 'day))
1685 ;; FIXME: make abbreviation customizable 1698 (let ((last (let ((mm (or month mo))
1686 (setq monthname 1699 (yy (or year yr)))
1687 (or (and (= month 13) "*") 1700 ;; If month is unspecified, use a month with 31
1688 (calendar-month-name (calendar-extract-month (list month day year)) 1701 ;; days for checking day of month input. Does
1689 t))) 1702 ;; Calendar do anything special when * is
1690 (mapconcat 'eval calendar-date-display-form ""))) 1703 ;; currently a shorter month?
1704 (if (= mm 13) (setq mm 1))
1705 ;; If year is unspecified, use a leap year to
1706 ;; allow Feb. 29.
1707 (if (eq year '*) (setq yy 2012))
1708 (calendar-last-day-of-month mm yy))))
1709 (while (if (natnump day) (or (< day 1) (> day last)) (not (eq day '*)))
1710 (setq day (read-from-minibuffer
1711 (format "Day (1-%d or RET for today or * for any day): "
1712 last)
1713 nil nil t nil (number-to-string
1714 (calendar-extract-day
1715 (calendar-current-date))))))))
1716 ;; Stringify read values (monthname is already a string).
1717 (and year (setq year (if (eq year '*)
1718 (symbol-name '*)
1719 (number-to-string year))))
1720 (and day (setq day (if (eq day '*)
1721 (symbol-name '*)
1722 (number-to-string day))))
1723 (and month (setq month (if (eq month '*)
1724 (symbol-name '*)
1725 (number-to-string month))))
1726 (if arg
1727 (cond ((eq arg 'year) year)
1728 ((eq arg 'day) day)
1729 ((eq arg 'month)
1730 (if (memq 'month calendar-date-display-form)
1731 month
1732 monthname)))
1733 (mapconcat 'eval calendar-date-display-form ""))))
1691 1734
1692(defun todos-read-dayname () 1735(defun todos-read-dayname ()
1693 "Choose name of a day of the week with completion and return it." 1736 "Choose name of a day of the week with completion and return it."
@@ -1742,6 +1785,7 @@ the empty string (i.e., no time string)."
1742 :notify (lambda (widget &rest ignore) 1785 :notify (lambda (widget &rest ignore)
1743 (setq todos-multiple-filter-files 'quit) 1786 (setq todos-multiple-filter-files 'quit)
1744 (quit-window t) 1787 (quit-window t)
1788 ;; FIXME: use (throw 'exit nil) ?
1745 (exit-recursive-edit)) 1789 (exit-recursive-edit))
1746 "Cancel") 1790 "Cancel")
1747 (widget-insert " ") 1791 (widget-insert " ")
@@ -1754,6 +1798,7 @@ the empty string (i.e., no time string)."
1754 (widget-value 1798 (widget-value
1755 todos-multiple-filter-files-widget))) 1799 todos-multiple-filter-files-widget)))
1756 (quit-window t) 1800 (quit-window t)
1801 ;; FIXME: use (throw 'exit nil) ?
1757 (exit-recursive-edit)) 1802 (exit-recursive-edit))
1758 "Apply") 1803 "Apply")
1759 (use-local-map widget-keymap) 1804 (use-local-map widget-keymap)
@@ -2407,7 +2452,7 @@ which is the value of the user option
2407(defvar todos-key-bindings 2452(defvar todos-key-bindings
2408 `( 2453 `(
2409 ;; display 2454 ;; display
2410 ("Cd" . todos-display-categories) ;FIXME: Cs todos-show-categories? 2455 ("Cd" . todos-display-categories) ;FIXME: Fc todos-file-categories?
2411 ("H" . todos-highlight-item) 2456 ("H" . todos-highlight-item)
2412 ("N" . todos-hide-show-item-numbering) 2457 ("N" . todos-hide-show-item-numbering)
2413 ("D" . todos-hide-show-date-time) 2458 ("D" . todos-hide-show-date-time)
@@ -2420,7 +2465,7 @@ which is the value of the user option
2420 ("V" . todos-show-done-only) 2465 ("V" . todos-show-done-only)
2421 ("As" . todos-show-archive) 2466 ("As" . todos-show-archive)
2422 ("Ac" . todos-choose-archive) 2467 ("Ac" . todos-choose-archive)
2423 ("Y" . todos-diary-items) 2468 ;; ("Y" . todos-diary-items)
2424 ("Fe" . todos-edit-multiline) 2469 ("Fe" . todos-edit-multiline)
2425 ("Fh" . todos-highlight-item) 2470 ("Fh" . todos-highlight-item)
2426 ("Fn" . todos-hide-show-item-numbering) 2471 ("Fn" . todos-hide-show-item-numbering)
@@ -2453,9 +2498,12 @@ which is the value of the user option
2453 ("ee" . todos-edit-item) 2498 ("ee" . todos-edit-item)
2454 ("em" . todos-edit-multiline-item) 2499 ("em" . todos-edit-multiline-item)
2455 ("eh" . todos-edit-item-header) 2500 ("eh" . todos-edit-item-header)
2456 ("edd" . todos-edit-item-date)
2457 ("edc" . todos-edit-item-date-from-calendar) 2501 ("edc" . todos-edit-item-date-from-calendar)
2458 ("edt" . todos-edit-item-date-is-today) 2502 ("edt" . todos-edit-item-date-to-today)
2503 ("edn" . todos-edit-item-date-day-name)
2504 ("edy" . todos-edit-item-date-year)
2505 ("edm" . todos-edit-item-date-month)
2506 ("edd" . todos-edit-item-date-day)
2459 ("et" . todos-edit-item-time) 2507 ("et" . todos-edit-item-time)
2460 ("eyy" . todos-edit-item-diary-inclusion) 2508 ("eyy" . todos-edit-item-diary-inclusion)
2461 ;; ("" . todos-edit-category-diary-inclusion) 2509 ;; ("" . todos-edit-category-diary-inclusion)
@@ -4209,7 +4257,12 @@ the priority is not given by HERE but by prompting."
4209 (string-match todos-date-pattern date-type)) 4257 (string-match todos-date-pattern date-type))
4210 (setq todos-date-from-calendar date-type) 4258 (setq todos-date-from-calendar date-type)
4211 (todos-set-date-from-calendar)) 4259 (todos-set-date-from-calendar))
4212 (t (calendar-date-string (calendar-current-date) t t)))) 4260 (t
4261 ;; FIXME: We follow diary-insert-entry in
4262 ;; hardcoding abbreviated month name and no
4263 ;; day name in date string. Should this be
4264 ;; customizable?
4265 (calendar-date-string (calendar-current-date) t t))))
4213 (time-string (or (and time (todos-read-time)) 4266 (time-string (or (and time (todos-read-time))
4214 (and todos-always-add-time-string 4267 (and todos-always-add-time-string
4215 (substring (current-time-string) 11 16))))) 4268 (substring (current-time-string) 11 16)))))
@@ -4285,7 +4338,7 @@ the priority is not given by HERE but by prompting."
4285 (let (calendar-view-diary-initially-flag) 4338 (let (calendar-view-diary-initially-flag)
4286 (calendar)) 4339 (calendar))
4287 ;; *Calendar* is now current buffer. 4340 ;; *Calendar* is now current buffer.
4288 (local-set-key (kbd "RET") 'exit-recursive-edit) 4341 (local-set-key (kbd "RET") 'exit-recursive-edit) ; FIXME: (throw 'exit nil)?
4289 (message "Put cursor on a date and type <return> to set it.") 4342 (message "Put cursor on a date and type <return> to set it.")
4290 ;; FIXME: is there a better way than recursive-edit? 4343 ;; FIXME: is there a better way than recursive-edit?
4291 (recursive-edit) 4344 (recursive-edit)
@@ -4344,13 +4397,18 @@ the item at point."
4344 (todos-prefix-overlays))) 4397 (todos-prefix-overlays)))
4345 (if ov (delete-overlay ov))))) 4398 (if ov (delete-overlay ov)))))
4346 4399
4347(defun todos-edit-item () 4400(defun todos-edit-item (&optional arg)
4348 "Edit the Todo item at point. 4401 "Edit the Todo item at point.
4402
4403With non-nil prefix argument ARG, include the item's date/time
4404header, making it also editable; otherwise, include only the item
4405content.
4406
4349If the item consists of only one logical line, edit it in the 4407If the item consists of only one logical line, edit it in the
4350minibuffer; otherwise, edit it in Todos Edit mode." 4408minibuffer; otherwise, edit it in Todos Edit mode."
4351 (interactive) 4409 (interactive "P")
4352 (when (todos-item-string) 4410 (when (todos-item-string)
4353 (let* ((buffer-read-only) 4411 (let* ((opoint (point))
4354 (start (todos-item-start)) 4412 (start (todos-item-start))
4355 (item-beg (progn 4413 (item-beg (progn
4356 (re-search-forward 4414 (re-search-forward
@@ -4359,16 +4417,22 @@ minibuffer; otherwise, edit it in Todos Edit mode."
4359 (regexp-quote todos-nondiary-end) "?") 4417 (regexp-quote todos-nondiary-end) "?")
4360 (line-end-position) t) 4418 (line-end-position) t)
4361 (1+ (- (point) start)))) 4419 (1+ (- (point) start))))
4362 (item (todos-item-string)) 4420 (header (substring (todos-item-string) 0 item-beg))
4421 (item (if arg (todos-item-string)
4422 (substring (todos-item-string) item-beg)))
4363 (multiline (> (length (split-string item "\n")) 1)) 4423 (multiline (> (length (split-string item "\n")) 1))
4364 (opoint (point))) 4424 (buffer-read-only nil))
4365 (if multiline 4425 (if multiline
4366 (todos-edit-multiline t) 4426 (todos-edit-multiline-item)
4367 (let ((new (read-string "Edit: " (cons item item-beg)))) 4427 (let ((new (concat (if arg "" header)
4368 (while (not (string-match 4428 (read-string "Edit: " (if arg
4369 (concat todos-date-string-start todos-date-pattern) new)) 4429 (cons item item-beg)
4370 (setq new (read-from-minibuffer 4430 (cons item 0))))))
4371 "Item must start with a date: " new))) 4431 (when arg
4432 (while (not (string-match (concat todos-date-string-start
4433 todos-date-pattern) new))
4434 (setq new (read-from-minibuffer
4435 "Item must start with a date: " new))))
4372 ;; Indent newlines inserted by C-q C-j if nonspace char follows. 4436 ;; Indent newlines inserted by C-q C-j if nonspace char follows.
4373 (setq new (replace-regexp-in-string 4437 (setq new (replace-regexp-in-string
4374 "\\(\n\\)[^[:blank:]]" 4438 "\\(\n\\)[^[:blank:]]"
@@ -4422,91 +4486,245 @@ in the number or names of categories."
4422 ;; In case next buffer is not the one holding todos-current-todos-file. 4486 ;; In case next buffer is not the one holding todos-current-todos-file.
4423 (todos-show)) 4487 (todos-show))
4424 4488
4425(defun todos-edit-item-header (&optional what) 4489(defun todos-edit-item-header-1 (what &optional inc)
4426 "Edit date/time header of at least one item. 4490 "Underlying function to edit items' date/time headers.
4427 4491
4428Interactively, ask whether to edit year, month and day or day of 4492The argument WHAT (passed by invoking commands) specifies what
4429the week, as well as time. If there are marked items, apply the 4493part of the header to edit; possible values are these symbols:
4430changes to all of these; otherwise, edit just the item at point. 4494`date', to edit the year, month, and day of the date string;
4431 4495`time', to edit just the time string; `calendar', to select the
4432Non-interactively, argument WHAT specifies whether to set the 4496date from the Calendar; `today', to set the date to today's date;
4433date from the Calendar or to today, or whether to edit only the 4497`dayname', to set the date string to the name of a day or to
4434date or day, or only the time." 4498change the day name; and `year', `month' or `day', to edit only
4435 (interactive) 4499these respective parts of the date string (`day' is the number of
4500the given day of the month, and `month' is either the name of the
4501given month or its number, depending on the value of
4502`calendar-date-display-form').
4503
4504The optional argument INC is a positive or negative integer
4505\(passed by invoking commands as a numerical prefix argument)
4506that in conjunction with the WHAT values `year', `month' or
4507`day', increments or decrements the specified date string
4508component by the specified number of suitable units, i.e., years,
4509months, or days, with automatic adjustment of the other date
4510string components as necessary.
4511
4512If there are marked items, apply the same edit to all of these;
4513otherwise, edit just the item at point."
4436 (let* ((cat (todos-current-category)) 4514 (let* ((cat (todos-current-category))
4437 (marked (assoc cat todos-categories-with-marks)) 4515 (marked (assoc cat todos-categories-with-marks))
4438 (first t) ; Match only first of marked items. 4516 (first t)
4439 (todos-date-from-calendar t) 4517 (todos-date-from-calendar t)
4440 ndate ntime nheader) 4518 (buffer-read-only nil)
4519 ndate ntime year monthname month day
4520 dayname) ; Needed by calendar-date-display-form.
4441 (save-excursion 4521 (save-excursion
4442 (or (and marked (goto-char (point-min))) (todos-item-start)) 4522 (or (and marked (goto-char (point-min))) (todos-item-start))
4443 (catch 'stop 4523 (catch 'end
4444 (while (not (eobp)) 4524 (while (not (eobp))
4445 (and marked 4525 (and marked
4446 (while (not (todos-marked-item-p)) 4526 (while (not (todos-marked-item-p))
4447 (todos-forward-item) 4527 (todos-forward-item)
4448 (and (eobp) (throw 'stop nil)))) 4528 (and (eobp) (throw 'end nil))))
4449 (re-search-forward (concat todos-date-string-start "\\(?1:" 4529 (re-search-forward (concat todos-date-string-start "\\(?1:"
4450 todos-date-pattern 4530 todos-date-pattern
4451 "\\)\\(?2: " diary-time-regexp "\\)?") 4531 "\\)\\(?2: " diary-time-regexp "\\)?"
4532 (regexp-quote todos-nondiary-end) "?")
4452 (line-end-position) t) 4533 (line-end-position) t)
4453 (let* ((odate (match-string-no-properties 1)) 4534 (let* ((odate (match-string-no-properties 1))
4454 (otime (match-string-no-properties 2)) 4535 (otime (match-string-no-properties 2))
4455 (buffer-read-only)) 4536 (omonthname (match-string-no-properties 6))
4456 (cond ((eq what 'today) 4537 (omonth (match-string-no-properties 7))
4457 (progn 4538 (oday (match-string-no-properties 8))
4458 (setq ndate (calendar-date-string 4539 (oyear (match-string-no-properties 9))
4459 (calendar-current-date) t t)) 4540 (tmn-array todos-month-name-array)
4460 (replace-match ndate nil nil nil 1))) 4541 (mlist (append tmn-array nil))
4461 ((eq what 'calendar) 4542 (tma-array todos-month-abbrev-array)
4462 (setq ndate (save-match-data (todos-set-date-from-calendar))) 4543 (mablist (append tma-array nil))
4463 (replace-match ndate nil nil nil 1)) 4544 (yy (and oyear (unless (string= oyear "*")
4464 (t 4545 (string-to-number oyear))))
4465 (unless (eq what 'timeonly) 4546 (mm (or (and omonth (unless (string= omonth "*")
4466 (when first 4547 (string-to-number omonth)))
4467 (setq ndate (if (save-match-data 4548 (1+ (- (length mlist)
4468 (string-match "[0-9]+" odate)) 4549 (length (or (member omonthname mlist)
4469 (if (y-or-n-p "Change date? ") 4550 (member omonthname mablist)))))))
4470 (todos-read-date) 4551 (dd (and oday (unless (string= oday "*")
4471 (todos-read-dayname)) 4552 (string-to-number oday)))))
4472 (if (y-or-n-p "Change day? ") 4553 ;; If there are marked items, use only the first to set
4473 (todos-read-dayname) 4554 ;; header changes, and apply these to all marked items.
4474 (todos-read-date))))) 4555 (when first
4475 (replace-match ndate nil nil nil 1)) 4556 (cond
4476 (unless (eq what 'dateonly) 4557 ((eq what 'date)
4477 (when first 4558 (setq ndate (todos-read-date)))
4478 (setq ntime (save-match-data (todos-read-time))) 4559 ((eq what 'calendar)
4479 (when (< 0 (length ntime)) 4560 (setq ndate (save-match-data (todos-set-date-from-calendar))))
4480 (setq ntime (concat " " ntime)))) 4561 ((eq what 'today)
4481 (if otime 4562 (setq ndate (calendar-date-string (calendar-current-date) t t)))
4482 (replace-match ntime nil nil nil 2) 4563 ((eq what 'dayname)
4483 (goto-char (match-end 1)) 4564 (setq ndate (todos-read-dayname)))
4484 (insert ntime))))) 4565 ((eq what 'time)
4566 (setq ntime (save-match-data (todos-read-time)))
4567 (when (> (length ntime) 0)
4568 (setq ntime (concat " " ntime))))
4569 ;; When date string consists only of a day name,
4570 ;; passing other date components is a NOP.
4571 ((and (memq what '(year month day))
4572 (not (or oyear omonth oday))))
4573 ((eq what 'year)
4574 (setq day oday
4575 monthname omonthname
4576 month omonth
4577 year (cond ((not current-prefix-arg)
4578 (todos-read-date 'year))
4579 ((string= oyear "*")
4580 (error "Cannot increment *"))
4581 (t ; FIXME: handle negative years
4582 (number-to-string (+ yy inc))))))
4583 ((eq what 'month)
4584 (setf day oday
4585 year oyear
4586 (if (memq 'month calendar-date-display-form)
4587 month
4588 monthname)
4589 (cond ((not current-prefix-arg)
4590 (todos-read-date 'month))
4591 ((or (string= omonth "*") (= mm 13))
4592 (error "Cannot increment *"))
4593 (t
4594 (let ((mminc (+ mm inc)))
4595 ;; Increment or decrement month by INC
4596 ;; modulo 12.
4597 (setq mm (% mminc 12))
4598 ;; If result is 0, make month December.
4599 (setq mm (if (= mm 0) 12 (abs mm)))
4600 ;; Adjust year if necessary.
4601 (setq year (or (and (cond ((> mminc 12)
4602 (+ yy (/ mminc 12)))
4603 ((< mminc 1)
4604 (- yy (/ mminc 12) 1))
4605 (t yy))
4606 (number-to-string yy))
4607 oyear)))
4608 ;; Return the changed numerical month as
4609 ;; a string or the corresponding month name.
4610 (if omonth
4611 (number-to-string mm)
4612 (aref tma-array (1- mm))))))
4613 (let ((yy (string-to-number year)) ; 0 if year is "*".
4614 ;; When mm is 13 (corresponding to "*" as value
4615 ;; of month), this raises an args-out-of-range
4616 ;; error in calendar-last-day-of-month, so use 1
4617 ;; (corresponding to January) to get 31 days.
4618 (mm (if (= mm 13) 1 mm)))
4619 (if (> (string-to-number day)
4620 (calendar-last-day-of-month mm yy))
4621 (error "%s %s does not have %s days"
4622 (aref tmn-array (1- mm))
4623 (if (= mm 2) yy "") day))))
4624 ((eq what 'day)
4625 (setq year oyear
4626 month omonth
4627 monthname omonthname
4628 day (cond
4629 ((not current-prefix-arg)
4630 (todos-read-date 'day mm oyear))
4631 ((string= oday "*")
4632 (error "Cannot increment *"))
4633 ((or (string= omonth "*") (string= omonthname "*"))
4634 (setq dd (+ dd inc))
4635 (if (> dd 31)
4636 (error "A month cannot have more than 31 days")
4637 (number-to-string dd)))
4638 ;; Increment or decrement day by INC,
4639 ;; adjusting month and year if necessary
4640 ;; (if year is "*" assume current year to
4641 ;; calculate adjustment).
4642 (t
4643 (let* ((yy (or yy (calendar-extract-year
4644 (calendar-current-date))))
4645 (date (calendar-gregorian-from-absolute
4646 (+ (calendar-absolute-from-gregorian
4647 (list mm dd yy)) inc)))
4648 (adjmm (nth 0 date)))
4649 ;; Set year and month(name) to adjusted values.
4650 (unless (string= year "*")
4651 (setq year (number-to-string (nth 2 date))))
4652 (if month
4653 (setq month (number-to-string adjmm))
4654 (setq monthname (aref tma-array (1- adjmm))))
4655 ;; Return changed numerical day as a string.
4656 (number-to-string (nth 1 date)))))))))
4657 ;; If new year, month or day date string components were
4658 ;; calculated, rebuild the whole date string from them.
4659 (when (memq what '(year month day))
4660 (if (or oyear omonth omonthname oday)
4661 (setq ndate (mapconcat 'eval calendar-date-display-form ""))
4662 (message "Cannot edit date component of empty date string")))
4663 (when ndate (replace-match ndate nil nil nil 1))
4664 ;; Add new time string to the header, if it was supplied.
4665 (when ntime
4666 (if otime
4667 (replace-match ntime nil nil nil 2)
4668 (goto-char (match-end 1))
4669 (insert ntime)))
4485 (setq todos-date-from-calendar nil) 4670 (setq todos-date-from-calendar nil)
4486 (setq first nil)) 4671 (setq first nil))
4672 ;; Apply the changes to the first marked item header to the
4673 ;; remaining marked items. If there are no marked items,
4674 ;; we're finished.
4487 (if marked 4675 (if marked
4488 (todos-forward-item) 4676 (todos-forward-item)
4489 (goto-char (point-max)))))))) 4677 (goto-char (point-max))))))))
4490 4678
4491(defun todos-edit-item-date () 4679(defun todos-edit-item-header ()
4492 "Prompt for and apply changes to current item's date." 4680 "Interactively edit at least the date of item's date/time header.
4681If user option `todos-always-add-time-string' is non-nil, also
4682edit item's time string."
4683 (interactive)
4684 (todos-edit-item-header-1 'date)
4685 (when todos-always-add-time-string
4686 (todos-edit-item-time)))
4687
4688(defun todos-edit-item-time ()
4689 "Interactively edit the time string of item's date/time header."
4493 (interactive) 4690 (interactive)
4494 (todos-edit-item-header 'dateonly)) 4691 (todos-edit-item-header-1 'time))
4495 4692
4496(defun todos-edit-item-date-from-calendar () 4693(defun todos-edit-item-date-from-calendar ()
4497 "Prompt for changes to current item's date and apply from Calendar." 4694 "Interactively edit item's date using the Calendar."
4498 (interactive) 4695 (interactive)
4499 (todos-edit-item-header 'calendar)) 4696 (todos-edit-item-header-1 'calendar))
4500 4697
4501(defun todos-edit-item-date-is-today () 4698(defun todos-edit-item-date-to-today ()
4502 "Set item date to today's date." 4699 "Set item's date to today's date."
4503 (interactive) 4700 (interactive)
4504 (todos-edit-item-header 'today)) 4701 (todos-edit-item-header-1 'today))
4505 4702
4506(defun todos-edit-item-time () 4703(defun todos-edit-item-date-day-name ()
4507 "Prompt For and apply changes to current item's time." 4704 "Replace item's date with the name of a day of the week."
4508 (interactive) 4705 (interactive)
4509 (todos-edit-item-header 'timeonly)) 4706 (todos-edit-item-header-1 'dayname))
4707
4708(defun todos-edit-item-date-year (&optional inc)
4709 "Interactively edit the year of item's date string.
4710With prefix argument INC a positive or negative integer,
4711increment or decrement the year by INC."
4712 (interactive "p")
4713 (todos-edit-item-header-1 'year inc))
4714
4715(defun todos-edit-item-date-month (&optional inc)
4716 "Interactively edit the month of item's date string.
4717With prefix argument INC a positive or negative integer,
4718increment or decrement the month by INC."
4719 (interactive "p")
4720 (todos-edit-item-header-1 'month inc))
4721
4722(defun todos-edit-item-date-day (&optional inc)
4723 "Interactively edit the day of the month of item's date string.
4724With prefix argument INC a positive or negative integer,
4725increment or decrement the day by INC."
4726 (interactive "p")
4727 (todos-edit-item-header-1 'day inc))
4510 4728
4511(defun todos-edit-item-diary-inclusion () 4729(defun todos-edit-item-diary-inclusion ()
4512 "Change diary status of one or more todo items in this category. 4730 "Change diary status of one or more todo items in this category.