Browse Source

Implement narrowing existing agenda filters, and effort filters.

Carsten Dominik 16 years ago
parent
commit
aead878b46
4 changed files with 155 additions and 29 deletions
  1. 46 0
      ORGWEBPAGE/Changes.org
  2. 41 21
      doc/org.texi
  3. 4 0
      lisp/ChangeLog
  4. 64 8
      lisp/org-agenda.el

+ 46 - 0
ORGWEBPAGE/Changes.org

@@ -17,6 +17,52 @@
 :END:
 :END:
 
 
 ** Details
 ** Details
+
+*** Enhancements to secondary agenda filtering
+
+**** You can now refining the current filter by an additional criterion
+      When filtering an existing agenda view with =/=, you can
+      now narrow down the existing selection by an additional
+      condition.  Do do this, use =\= instead of =/= to add the
+      aditional criterion.  You can also press =+= or =-= after
+      =/= to add a positive or negative condition.  A condition
+      can be a TAG, or an effort estimate limit, see below.
+
+**** It is now possible to filter for effort estimates
+     This means to filter the agenda for the value of the Effort
+     property.  For this you should best set up global allowed
+     values for effort estimates, with
+
+#+begin_src emacs-lisp
+(setq org-global-properties
+      '(("Effort_ALL" . "0 0:10 0:30 1:00 2:00 3:00 4:00")))
+#+end_src
+      
+     You may then select effort limits with single keys in the
+     filter.  It works like this:  After =/= or =\=, first select
+     the operater which you want to use to compare effort
+     estimates:
+
+     : <   Select entries with effort smaller than or equal to the limit
+     : >   Select entries with effort larger than or equal to the limit
+     : =   Select entries with effort equal to the limit
+
+     After that, you can press a single digit number which is
+     used as an index to the allowed effort estimates.
+
+     If you do not use digits to fast-select tags, you can even
+     skip the operator, which will then default to
+     `org-agenda-filter-effort-default-operator', which is by
+     default =<=.
+
+     Thanks to Manish for the great idea to include fast effort
+     filtering into the agenda filtering process.
+
+**** The mode line will show the active filter
+     For example, if there is a filter in place that does select
+     for HOME tags, against EMAILtags, and for tasks with an
+     estimated effort smaller than 30 minutes, the mode-line with
+     show =+HOME-EMAIL+<0:30=
    
    
 *** Setting tags has now its own binding, =C-c C-q=
 *** Setting tags has now its own binding, =C-c C-q=
 
 

+ 41 - 21
doc/org.texi

@@ -4863,10 +4863,10 @@ you want to clock your time).  For a specific buffer you can use
 @end example
 @end example
 
 
 @noindent
 @noindent
-or you can set up these values globally by customizing the variables
+or, even better, you can set up these values globally by customizing the
-@code{org-global-properties} and @code{org-columns-default-format}.  In
+variables @code{org-global-properties} and @code{org-columns-default-format}.
-particular if you want to use this setup also in the agenda, a global setup
+In particular if you want to use this setup also in the agenda, a global
-may be advised.
+setup may be advised.
 
 
 The way to assign estimates to individual items is then to switch to column
 The way to assign estimates to individual items is then to switch to column
 mode, and to use @kbd{S-@key{right}} and @kbd{S-@key{left}} to change the
 mode, and to use @kbd{S-@key{right}} and @kbd{S-@key{left}} to change the
@@ -4882,6 +4882,10 @@ option @code{org-agenda-columns-add-appointments-to-effort-sum}.  The
 appointments on a day that take place over a specified time interval will
 appointments on a day that take place over a specified time interval will
 then also be added to the load estimate of the day.
 then also be added to the load estimate of the day.
 
 
+Effort estimates can be used in secondary agenda filtering that is triggered
+with the @kbd{/} key in the agenda (@pxref{Agenda commands}).  If you have
+these estimates defined consistently, two or three key presses will narrow
+down the list to stuff that fits into an available time slot.
 
 
 @node Capture, Agenda Views, Dates and Times, Top
 @node Capture, Agenda Views, Dates and Times, Top
 @chapter Capture
 @chapter Capture
@@ -5835,9 +5839,7 @@ sequence in which they are found in the agenda files.
 
 
 Sorting can be customized using the variable
 Sorting can be customized using the variable
 @code{org-agenda-sorting-strategy}, and may also include criteria based on
 @code{org-agenda-sorting-strategy}, and may also include criteria based on
-the estimated effort of an entry.
+the estimated effort of an entry (@pxref{Effort estimates}).
-@c FIXME: link!!!!!!!!
-
 
 
 @node Agenda commands, Custom agenda views, Presentation and sorting, Agenda Views
 @node Agenda commands, Custom agenda views, Presentation and sorting, Agenda Views
 @section Commands in the agenda buffer
 @section Commands in the agenda buffer
@@ -6001,23 +6003,41 @@ that entry would be in the original buffer (taken from a property, from a
 
 
 @kindex /
 @kindex /
 @item /
 @item /
-Filter the current agenda view with respect to a tag.  You will be prompted
+Filter the current agenda view with respect to a tag and/or effort estimates.
-for a tag selection letter.  Pressing @key{TAB} at that prompt will offer use
+The difference between this and a custom agenda commands is that filtering is
-completion to select a tag (including any tags that do not have a selection
+very fast, so that you can switch quickly between different filters without
-character).  The command then hides all entries that do not contain or
+having to recreate the agenda.
-inherit this tag.  When called with prefix arg, remove the entries that
+
-@emph{do} have the tag.  A second @kbd{/} at the prompt will unhide any
+You will be prompted for a tag selection letter.  Pressing @key{TAB} at that
-hidden entries.  If the first key you press is either @kbd{+} or @kbd{-}, the
+prompt will offer use completion to select a tag (including any tags that do
-previous filter will be narrowed by requiring or forbidding the selected
+not have a selection character).  The command then hides all entries that do
-additional tag.  Instead of pressing @kbd{+} or {-}, you can also use the
+not contain or inherit this tag.  When called with prefix arg, remove the
-following command.
+entries that @emph{do} have the tag.  A second @kbd{/} at the prompt will
+turn off the filter and unhide any hidden entries.  If the first key you
+press is either @kbd{+} or @kbd{-}, the previous filter will be narrowed by
+requiring or forbidding the selected additional tag.  Instead of pressing
+@kbd{+} or {-}, you can also use the @kbd{\} command.
+
+In order to filter for effort estimates, you should set-up allowed
+efforts globally, for example
+@lisp
+(setq org-global-properties
+    '(("Effort_ALL". "0 0:10 0:30 1:00 2:00 3:00 4:00")))
+@end lisp
+You can then filter for an effort by first typing an operator, one of @kbd{<},
+@kbd{>}, and @kbd{=}, and then the one-digit index of an effort estimate in
+your array of allowed values, where @kbd{0} means the 10th value.  The filter
+will then restrict to entries with effort smaller-or-equal, equal, or
+larger-or-equal than the selected value.  If the digits 0-9 are not used as
+fast access keys to tags, you can also simply press the index digit directly
+without an operator.  In this case, @kbd{<} will be assumed.
 
 
 @kindex \
 @kindex \
 @item \
 @item \
-Narrow the curent agenda fiter by an additional condition.  When called with
+Narrow the current agenda filter by an additional condition.  When called with
-prefix arg, remove the entries that @emph{do} have the tag.  You can achive
+prefix arg, remove the entries that @emph{do} have the tag, or that do match
-the same effect by pressing @kbd{+} or @kbd{-} as the first key after the
+the effort criterion.  You can achieve the same effect by pressing @kbd{+} or
-@kbd{/} command.
+@kbd{-} as the first key after the @kbd{/} command.
 
 
 @kindex [
 @kindex [
 @kindex ]
 @kindex ]

+ 4 - 0
lisp/ChangeLog

@@ -1,3 +1,7 @@
+2008-10-20  Carsten Dominik  <dominik@science.uva.nl>
+
+	* org-agenda.el (org-agenda-filter-effort-default-operator): New
+	option.
 
 
 2008-10-18  Carsten Dominik  <dominik@science.uva.nl>
 2008-10-18  Carsten Dominik  <dominik@science.uva.nl>
 
 

+ 64 - 8
lisp/org-agenda.el

@@ -387,6 +387,14 @@ or `C-c a #' to produce the list."
 	  (repeat :tag "Projects are *not* stuck if they have an entry with TAG being any of" (string))
 	  (repeat :tag "Projects are *not* stuck if they have an entry with TAG being any of" (string))
 	  (regexp :tag "Projects are *not* stuck if this regexp matches\ninside the subtree")))
 	  (regexp :tag "Projects are *not* stuck if this regexp matches\ninside the subtree")))
 
 
+(defcustom org-agenda-filter-effort-default-operator "<"
+  "The default operator for effort estimate filtering.
+If you select an effort estimate limit with first pressing an operator,
+this one will be used."
+  :group 'org-agenda-custom-commands
+  :type '(choice (const :tag "less or equal" "<")
+		 (const :tag "greater or equal"">")
+		 (const :tag "equal" "=")))
 
 
 (defgroup org-agenda-skip nil
 (defgroup org-agenda-skip nil
  "Options concerning skipping parts of agenda files."
  "Options concerning skipping parts of agenda files."
@@ -4119,6 +4127,7 @@ When this is the global TODO list, a prefix argument will be interpreted."
     (goto-line line)
     (goto-line line)
     (recenter window-line)))
     (recenter window-line)))
 
 
+
 (defvar org-global-tags-completion-table nil)
 (defvar org-global-tags-completion-table nil)
 (defvar org-agenda-filter-tags nil)
 (defvar org-agenda-filter-tags nil)
 (defvar org-agenda-filter-form nil)
 (defvar org-agenda-filter-form nil)
@@ -4130,20 +4139,45 @@ A lisp caller can specify CHAR.  NARROW means that the new tag should be
 used to narrow the search - the interactive user can also press `-' or `+'
 used to narrow the search - the interactive user can also press `-' or `+'
 to switch to narrowing."
 to switch to narrowing."
   (interactive "P")
   (interactive "P")
-  (let ((tag-chars (mapconcat (lambda (x) (if (cdr x) (char-to-string (cdr x)) ""))
+  (let* ((alist org-tag-alist-for-agenda)
-			      org-tag-alist-for-agenda ""))
+	(tag-chars (mapconcat 
-	char a tag tags (inhibit-read-only t) (current org-agenda-filter-tags))
+		    (lambda (x) (if (cdr x) (char-to-string (cdr x)) ""))
+		    alist ""))
+	(efforts (org-split-string
+		  (or (cdr (assoc (concat org-effort-property "_ALL")
+				  org-global-properties))
+		      "0 0:10 0:30 1:00 2:00 3:00 4:00 5:00 6:00 7:00 8:00"		      "")))
+	(effort-op org-agenda-filter-effort-default-operator)
+	(effort-prompt "")
+	(inhibit-read-only t)
+	(current org-agenda-filter-tags)
+	char a tag tags)
     (unless char
     (unless char
       (message 
       (message 
-       "%s by tag [%s ], [TAB] to complete, [/]:restore, [+-]:narrow, [>=<]:effort: "
+       "%s by tag [%s ], [TAB], [/]:off, [+-]:narrow, [>=<]:effort: "
-       (if narrow "Filter" "Narrow") tag-chars)
+       (if narrow "Narrow" "Filter") tag-chars)
       (setq char (read-char)))
       (setq char (read-char)))
     (when (member char '(?+ ?-))
     (when (member char '(?+ ?-))
+      ;; Narrowing down
       (cond ((equal char ?-) (setq strip t narrow t))
       (cond ((equal char ?-) (setq strip t narrow t))
 	    ((equal char ?+) (setq strip nil narrow t)))
 	    ((equal char ?+) (setq strip nil narrow t)))
       (message 
       (message 
-       "Narrow by tag [%s ], [TAB] to complete, [/]:restore, [>=<]:effort: " tag-chars)
+       "Narrow by tag [%s ], [TAB], [/]:off, [>=<]:effort: " tag-chars)
       (setq char (read-char)))
       (setq char (read-char)))
+    (when (member char '(?< ?> ?=))
+      ;; An effort operator
+      (setq effort-op (char-to-string char))
+      (loop for i from 0 to 9 do
+	    (setq effort-prompt
+		  (concat
+		   effort-prompt " ["
+		   (if (= i 9) "0" (int-to-string (1+ i)))
+		   "]" (nth i efforts))))
+      (setq alist nil) ; to make sure it will be interpreted as effort.
+      (message "Effort%s: %s " effort-op effort-prompt)
+      (setq char (read-char))
+      (when (or (< char ?0) (> char ?9))
+	(error "Need 1-9,0 to select effort" )))
     (when (equal char ?\t)
     (when (equal char ?\t)
       (unless (local-variable-p 'org-global-tags-completion-table (current-buffer))
       (unless (local-variable-p 'org-global-tags-completion-table (current-buffer))
 	(org-set-local 'org-global-tags-completion-table
 	(org-set-local 'org-global-tags-completion-table
@@ -4154,7 +4188,11 @@ to switch to narrowing."
     (cond
     (cond
      ((equal char ?/) (org-agenda-filter-by-tag-show-all))
      ((equal char ?/) (org-agenda-filter-by-tag-show-all))
      ((or (equal char ?\ )
      ((or (equal char ?\ )
-	  (setq a (rassoc char org-tag-alist-for-agenda))
+	  (setq a (rassoc char alist))
+	  (and (>= char ?0) (<= char ?9)
+	       (setq n (if (= char ?0) 9 (- char ?0 1))
+		     tag (concat effort-op (nth n efforts))
+		     a (cons tag nil)))
 	  (and tag (setq a (cons tag nil))))
 	  (and tag (setq a (cons tag nil))))
       (org-agenda-filter-by-tag-show-all)
       (org-agenda-filter-by-tag-show-all)
       (setq tag (car a))
       (setq tag (car a))
@@ -4185,12 +4223,30 @@ to switch to narrowing."
     (dolist (x org-agenda-filter-tags)
     (dolist (x org-agenda-filter-tags)
       (if (member x '("-" "+"))
       (if (member x '("-" "+"))
 	  (setq f1 '(not tags))
 	  (setq f1 '(not tags))
-	(setq f1 (list 'member (substring x 1) 'tags))
+	(if (string-match "[<=>]" x)
+	    (setq f1 (org-agenda-filter-effort-form x))
+	  (setq f1 (list 'member (substring x 1) 'tags)))
 	(if (equal (string-to-char x) ?-)
 	(if (equal (string-to-char x) ?-)
 	    (setq f1 (list 'not f1))))
 	    (setq f1 (list 'not f1))))
       (push f1 f))
       (push f1 f))
     (cons 'and (nreverse f))))
     (cons 'and (nreverse f))))
 
 
+(defun org-agenda-filter-effort-form (e)
+  "Return the form to compare the effort of the current line with what E says.
+E looks line \"+<2:25\"."
+  (let (op)
+    (setq e (substring e 1))
+    (setq op (string-to-char e) e (substring e 1))
+    (setq op (if (equal op ?<) '<= (if (equal op ?>) '>= '=)))
+    (list 'org-agenda-compare-effort (list 'quote op)
+	  (org-hh:mm-string-to-minutes e))))
+
+(defun org-agenda-compare-effort (op value)
+  (let ((eff (get-text-property 'effort-minutes)))
+    (if (not eff)
+	nil ; we don't have an effort defined
+      (funcall op eff value))))
+
 (defvar org-agenda-filter-overlays nil)
 (defvar org-agenda-filter-overlays nil)
 
 
 (defun org-agenda-filter-by-tag-hide-line ()
 (defun org-agenda-filter-by-tag-hide-line ()