Browse Source

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.
Bastien Guerry 6 years ago
parent
commit
8ecc966292
2 changed files with 89 additions and 24 deletions
  1. 51 8
      lisp/org-agenda.el
  2. 38 16
      lisp/org.el

+ 51 - 8
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.

+ 38 - 16
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.