From 8ecc966292f322ec6d0d0fb29e1087a55d22975f Mon Sep 17 00:00:00 2001 From: Bastien Guerry Date: Tue, 5 Feb 2013 17:35:51 +0100 Subject: [PATCH] Implement "delay" cookies for scheduled items. * org-agenda.el (org-agenda-skip-scheduled-delay-if-deadline): New option. The structure of the possible values is copied from `org-agenda-skip-deadline-prewarning-if-scheduled'. (org-agenda-get-scheduled): Honor the two new option, `org-scheduled-delay-days' and `org-agenda-skip-deadline-prewarning-if-scheduled'. I.e. if a scheduled entry has a delay cookie like "-2d" (similar to the prewarning cookie for deadline), don't show the entry until needed. * org.el (org-deadline-warning-days): Small docstring fix. (org-scheduled-delay-days): New option (see `org-deadline-warning-days'.) (org-get-wdays): Use the new option. Thanks to Andrew M. Nuxoll and Michael Brand for this idea. You can now use a "delay cookie" in scheduled items. For example, * TODO Sleep SCHEDULED: <2013-02-06 mer. -3d> will not be shown on 06/02 but on 09/02, three days later. The value of the cookie overrides any value of `org-scheduled-delay-days', unless `org-scheduled-delay-days' is negative (same logic than for `org-deadline-warning-days'.) Also check org-agenda-skip-scheduled-delay-if-deadline, which does for delay cookies what `org-agenda-skip-deadline-prewarning-if-scheduled' does for prewarning deadline cookies. --- lisp/org-agenda.el | 59 +++++++++++++++++++++++++++++++++++++++------- lisp/org.el | 54 +++++++++++++++++++++++++++++------------- 2 files changed, 89 insertions(+), 24 deletions(-) diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el index 663d1b56b..6caa51116 100644 --- a/lisp/org-agenda.el +++ b/lisp/org-agenda.el @@ -843,6 +843,21 @@ because you will take care of it on the day when scheduled." (const :tag "Remove prewarning if entry is scheduled" t) (integer :tag "Restart prewarning N days before deadline"))) +(defcustom org-agenda-skip-scheduled-delay-if-deadline nil + "Non-nil means skip scheduled delay when entry also has a deadline. +This variable may be set to nil, t, the symbol `post-deadline', +or a number which will then give the number of days after the actual +scheduled date when the delay should expire. The symbol `post-deadline' +eliminates the schedule delay when the date is posterior to the deadline." + :group 'org-agenda-skip + :group 'org-agenda-daily/weekly + :version "24.3" + :type '(choice + (const :tag "Always honor delay" nil) + (const :tag "Ignore delay if posterior to the deadline" post-deadline) + (const :tag "Ignore delay if entry has a deadline" t) + (integer :tag "Honor delay up until N days after the scheduled date"))) + (defcustom org-agenda-skip-additional-timestamps-same-entry nil "When nil, multiple same-day timestamps in entry make multiple agenda lines. When non-nil, after the search for timestamps has matched once in an @@ -5331,7 +5346,13 @@ the documentation of `org-diary'." (setq results (append results rtn)))))))) results)))) +(defsubst org-em (x y list) + "Is X or Y a member of LIST?" + (or (memq x list) (memq y list))) + (defvar org-heading-keyword-regexp-format) ; defined in org.el +(defvar org-agenda-sorting-strategy-selected nil) + (defun org-agenda-get-todos () "Return the TODO information for agenda display." (let* ((props (list 'face nil @@ -6143,7 +6164,8 @@ FRACTION is what fraction of the head-warning time has passed." deadline-results)) d2 diff pos pos1 category category-pos level tags donep ee txt head pastschedp todo-state face timestr s habitp show-all - did-habit-check-p warntime inherited-tags ts-date) + did-habit-check-p warntime inherited-tags ts-date suppress-delay + ddays) (goto-char (point-min)) (while (re-search-forward regexp nil t) (catch :skip @@ -6162,12 +6184,38 @@ FRACTION is what fraction of the head-warning time has passed." warntime (get-text-property (point) 'org-appt-warntime)) (setq pastschedp (and todayp (< diff 0))) (setq did-habit-check-p nil) + (setq suppress-delay + (let ((ds (and org-agenda-skip-scheduled-delay-if-deadline + (let ((item (buffer-substring (point-at-bol) (point-at-eol)))) + (save-match-data + (and (string-match + org-deadline-time-regexp item) + (match-string 1 item))))))) + (cond + ((not ds) nil) + ;; The current item has a deadline date (in ds), so + ;; evaluate its delay time. + ((integerp org-agenda-skip-scheduled-delay-if-deadline) + ;; Use global delay time. + (- org-agenda-skip-scheduled-delay-if-deadline)) + ((eq org-agenda-skip-scheduled-delay-if-deadline + 'post-deadline) + ;; Set delay to no later than deadline. + (min (- d2 (org-time-string-to-absolute + ds d1 'past show-all (current-buffer) pos)) + org-scheduled-delay-days)) + (t 0)))) + (setq ddays (if suppress-delay + (let ((org-scheduled-delay-days suppress-delay)) + (org-get-wdays s t t)) + (org-get-wdays s t))) ;; When to show a scheduled item in the calendar: ;; If it is on or past the date. - (when (or (and (< diff 0) + (when (or (and (> ddays 0) (= diff (- ddays))) + (and (zerop ddays) (= diff 0)) + (and (< diff 0) (< (abs diff) org-scheduled-past-days) (and todayp (not org-agenda-only-exact-dates))) - (= diff 0) ;; org-is-habit-p uses org-entry-get, which is expansive ;; so we go extra mile to only call it once (and todayp @@ -6578,7 +6626,6 @@ The modified list may contain inherited tags, and tags matched by s)) (defvar org-agenda-sorting-strategy) ;; because the def is in a let form -(defvar org-agenda-sorting-strategy-selected nil) (defun org-agenda-add-time-grid-maybe (list ndays todayp) "Add a time-grid for agenda items which need it. @@ -6893,10 +6940,6 @@ without respect of their type." (cond ((and ha (not hb)) -1) ((and (not ha) hb) +1)))) -(defsubst org-em (x y list) - "Is X or Y a member of LIST?" - (or (memq x list) (memq y list))) - (defun org-entries-lessp (a b) "Predicate for sorting agenda entries." ;; The following variables will be used when the form is evaluated. diff --git a/lisp/org.el b/lisp/org.el index e9353f672..3e9ef8b15 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -2864,7 +2864,7 @@ is used." (string :tag "Format string"))))) (defcustom org-deadline-warning-days 14 - "No. of days before expiration during which a deadline becomes active. + "Number of days before expiration during which a deadline becomes active. This variable governs the display in sparse trees and in the agenda. When 0 or negative, it means use this number (the absolute value of it) even if a deadline has a different individual lead time specified. @@ -2874,6 +2874,20 @@ Custom commands can set this variable in the options section." :group 'org-agenda-daily/weekly :type 'integer) +(defcustom org-scheduled-delay-days 0 + "Number of days before a scheduled item becomes active. +This variable governs the display in sparse trees and in the agenda. +The default value (i.e. 0) means: don't delay scheduled item. +When negative, it means use this number (the absolute value of it) +even if a scheduled item has a different individual delay time +specified. + +Custom commands can set this variable in the options section." + :group 'org-time + :group 'org-agenda-daily/weekly + :version "24.3" + :type 'integer) + (defcustom org-read-date-prefer-future t "Non-nil means assume future for incomplete date input from user. This affects the following situations: @@ -16216,21 +16230,29 @@ If SECONDS is non-nil, return the difference in seconds." (and (< (org-time-stamp-to-now timestamp-string) ndays) (not (org-entry-is-done-p)))) -(defun org-get-wdays (ts) - "Get the deadline lead time appropriate for timestring TS." - (cond - ((<= org-deadline-warning-days 0) - ;; 0 or negative, enforce this value no matter what - (- org-deadline-warning-days)) - ((string-match "-\\([0-9]+\\)\\([hdwmy]\\)\\(\\'\\|>\\| \\)" ts) - ;; lead time is specified. - (floor (* (string-to-number (match-string 1 ts)) - (cdr (assoc (match-string 2 ts) - '(("d" . 1) ("w" . 7) - ("m" . 30.4) ("y" . 365.25) - ("h" . 0.041667))))))) - ;; go for the default. - (t org-deadline-warning-days))) +(defun org-get-wdays (ts &optional delay zero-delay) + "Get the deadline lead time appropriate for timestring TS. +When DELAY is non-nil, get the delay time for scheduled items +instead of the deadline lead time. When ZERO-DELAY is non-nil +and `org-scheduled-delay-days' is 0, enforce 0 as the delay, +don't try to find the delay cookie in the scheduled timestamp." + (let ((tv (if delay org-scheduled-delay-days + org-deadline-warning-days))) + (cond + ((or (and delay (< tv 0)) + (and delay zero-delay (<= tv 0)) + (and (not delay) (<= tv 0))) + ;; Enforce this value no matter what + (- tv)) + ((string-match "-\\([0-9]+\\)\\([hdwmy]\\)\\(\\'\\|>\\| \\)" ts) + ;; lead time is specified. + (floor (* (string-to-number (match-string 1 ts)) + (cdr (assoc (match-string 2 ts) + '(("d" . 1) ("w" . 7) + ("m" . 30.4) ("y" . 365.25) + ("h" . 0.041667))))))) + ;; go for the default. + (t tv)))) (defun org-calendar-select-mouse (ev) "Return to `org-read-date' with the date currently selected.