瀏覽代碼

Merge branch 'filtering'

Conflicts:
	lisp/ChangeLog
Carsten Dominik 16 年之前
父節點
當前提交
a086743480
共有 4 個文件被更改,包括 233 次插入52 次删除
  1. 46 0
      ORGWEBPAGE/Changes.org
  2. 47 14
      doc/org.texi
  3. 9 0
      lisp/ChangeLog
  4. 131 38
      lisp/org-agenda.el

+ 46 - 0
ORGWEBPAGE/Changes.org

@@ -17,6 +17,52 @@
 :END:
 
 ** 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=
 

+ 47 - 14
doc/org.texi

@@ -4863,10 +4863,10 @@ you want to clock your time).  For a specific buffer you can use
 @end example
 
 @noindent
-or you can set up these values globally by customizing the variables
-@code{org-global-properties} and @code{org-columns-default-format}.  In
-particular if you want to use this setup also in the agenda, a global setup
-may be advised.
+or, even better, you can set up these values globally by customizing the
+variables @code{org-global-properties} and @code{org-columns-default-format}.
+In particular if you want to use this setup also in the agenda, a global
+setup may be advised.
 
 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
@@ -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
 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
 @chapter Capture
@@ -5835,9 +5839,7 @@ sequence in which they are found in the agenda files.
 
 Sorting can be customized using the variable
 @code{org-agenda-sorting-strategy}, and may also include criteria based on
-the estimated effort of an entry.
-@c FIXME: link!!!!!!!!
-
+the estimated effort of an entry (@pxref{Effort estimates}).
 
 @node Agenda commands, Custom agenda views, Presentation and sorting, Agenda Views
 @section Commands in the agenda buffer
@@ -5994,17 +5996,48 @@ that entry would be in the original buffer (taken from a property, from a
 @code{org-columns-default-format}), will be used in the agenda.
 
 @tsubheading{Secondary filtering and query editing}
+@cindex filtering, by tag and effort, in agenda
+@cindex tag filtering, in agenda
+@cindex effort filtering, in agenda
 @cindex query editing, in agenda
 
 @kindex /
 @item /
-Filter the current agenda view with respect to a tag.  You will be prompted
-for a tag selection letter.  Pressing @key{TAB} at that prompt will offer use
-completion to select a tag (including any tags that do not have a selection
-character).  The command then hides all entries that do not contain or
-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
-hidden entries.
+Filter the current agenda view with respect to a tag and/or effort estimates.
+The difference between this and a custom agenda commands is that filtering is
+very fast, so that you can switch quickly between different filters without
+having to recreate the agenda.
+
+You will be prompted for a tag selection letter.  Pressing @key{TAB} at that
+prompt will offer use completion to select a tag (including any tags that do
+not have a selection character).  The command then hides all entries that do
+not contain or inherit this tag.  When called with prefix arg, remove the
+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 \
+@item \
+Narrow the current agenda filter by an additional condition.  When called with
+prefix arg, remove the entries that @emph{do} have the tag, or that do match
+the effort criterion.  You can achieve the same effect by pressing @kbd{+} or
+@kbd{-} as the first key after the @kbd{/} command.
 
 @kindex [
 @kindex ]

+ 9 - 0
lisp/ChangeLog

@@ -5,6 +5,9 @@
 	(org-add-log-note): Mask prefix argument when immediately storing
 	the note.
 
+	* org-agenda.el (org-agenda-filter-effort-default-operator): New
+	option.
+
 2008-10-19  James TD Smith  <ahktenzero@mohorovi.cc>
 
 	* org.el (org-add-log-setup): Bugfix; code to find insertion point
@@ -13,6 +16,12 @@
 
 2008-10-18  Carsten Dominik  <dominik@science.uva.nl>
 
+	* org-agenda.el (org-agenda-filter-tags,org-agenda-filter-form):
+	New variables.
+	(org-prepare-agenda): Reset the filter tags.
+	(org-agenda-filter-by-tag, org-agenda-filter-by-tag-show-all):
+	Show filter tags in mode line.
+
 	* org-table.el (orgtbl-to-html): Bind `html-table-tag' for the
 	formatter.
 

+ 131 - 38
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))
 	  (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
  "Options concerning skipping parts of agenda files."
@@ -1169,6 +1177,7 @@ The following commands are available:
 (org-defkey org-agenda-mode-map "{" 'org-agenda-manipulate-query-add-re)
 (org-defkey org-agenda-mode-map "}" 'org-agenda-manipulate-query-subtract-re)
 (org-defkey org-agenda-mode-map "/" 'org-agenda-filter-by-tag)
+(org-defkey org-agenda-mode-map "\\" 'org-agenda-filter-by-tag-refine)
 
 (defvar org-agenda-keymap (copy-keymap org-agenda-mode-map)
   "Local keymap for agenda entries from Org-mode.")
@@ -1958,6 +1967,7 @@ higher priority settings."
 (defun org-prepare-agenda (&optional name)
   (setq org-todo-keywords-for-agenda nil)
   (setq org-done-keywords-for-agenda nil)
+  (setq org-agenda-filter-tags nil)
   (if org-agenda-multi
       (progn
 	(setq buffer-read-only nil)
@@ -4117,48 +4127,125 @@ When this is the global TODO list, a prefix argument will be interpreted."
     (goto-line line)
     (recenter window-line)))
 
+
 (defvar org-global-tags-completion-table nil)
-(defun org-agenda-filter-by-tag (strip &optional char)
+(defvar org-agenda-filter-tags nil)
+(defvar org-agenda-filter-form nil)
+(defun org-agenda-filter-by-tag (strip &optional char narrow)
   "Keep only those lines in the agenda buffer that have a specific tag.
 The tag is selected with its fast selection letter, as configured.
-With prefix argument STRIP, remove all lines that do have the tag."
+With prefix argument STRIP, remove all lines that do have the tag.
+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 `+'
+to switch to narrowing."
   (interactive "P")
-  (let (char a tag tags (inhibit-read-only t))
-      (message "Select tag [%s] or no tag [ ], [TAB] to complete, [/] to restore: "
-	       (mapconcat
-		(lambda (x) (if (cdr x) (char-to-string (cdr x)) ""))
-		org-tag-alist-for-agenda ""))
+  (let* ((alist org-tag-alist-for-agenda)
+	(tag-chars (mapconcat 
+		    (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
+      (message 
+       "%s by tag [%s ], [TAB], [/]:off, [+-]:narrow, [>=<]:effort: "
+       (if narrow "Narrow" "Filter") tag-chars)
+      (setq char (read-char)))
+    (when (member char '(?+ ?-))
+      ;; Narrowing down
+      (cond ((equal char ?-) (setq strip t narrow t))
+	    ((equal char ?+) (setq strip nil narrow t)))
+      (message 
+       "Narrow by tag [%s ], [TAB], [/]:off, [>=<]:effort: " tag-chars)
+      (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 (equal char ?\t)
-	(unless (local-variable-p 'org-global-tags-completion-table (current-buffer))
-	  (org-set-local 'org-global-tags-completion-table
-			 (org-global-tags-completion-table)))
-	(let ((completion-ignore-case t))
-	  (setq tag (completing-read
-		     "Tag: " org-global-tags-completion-table))))
-      (cond
-       ((equal char ?/) (org-agenda-filter-by-tag-show-all))
-       ((or (equal char ?\ )
-	    (setq a (rassoc char org-tag-alist-for-agenda))
-	    (and tag (setq a (cons tag nil))))
-	(org-agenda-filter-by-tag-show-all)
-	(setq tag (car a))
-	(save-excursion
-	  (goto-char (point-min))
-	  (while (not (eobp))
-	    (if (get-text-property (point) 'org-marker)
-		(progn
-		  (setq tags (get-text-property (point) 'tags))
-		  (if (not tag)
-		      (if (or (and strip (not tags))
-			      (and (not strip) tags))
-			  (org-agenda-filter-by-tag-hide-line))
-		    (if (or (and (member tag tags) strip)
-			    (and (not (member tag tags)) (not strip)))
-			(org-agenda-filter-by-tag-hide-line)))
-		  (beginning-of-line 2))
-	      (beginning-of-line 2)))))
-       (t (error "Invalid tag selection character %c" char)))))
+      (when (or (< char ?0) (> char ?9))
+	(error "Need 1-9,0 to select effort" )))
+    (when (equal char ?\t)
+      (unless (local-variable-p 'org-global-tags-completion-table (current-buffer))
+	(org-set-local 'org-global-tags-completion-table
+		       (org-global-tags-completion-table)))
+      (let ((completion-ignore-case t))
+	(setq tag (completing-read
+		   "Tag: " org-global-tags-completion-table))))
+    (cond
+     ((equal char ?/) (org-agenda-filter-by-tag-show-all))
+     ((or (equal char ?\ )
+	  (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))))
+      (org-agenda-filter-by-tag-show-all)
+      (setq tag (car a))
+      (setq org-agenda-filter-tags
+	    (cons (concat (if strip "-" "+") tag)
+		  (if narrow current nil)))
+      (setq org-agenda-filter-form (org-agenda-filter-make-matcher))
+      (org-agenda-set-mode-name)
+      (save-excursion
+	(goto-char (point-min))
+	(while (not (eobp))
+	  (if (get-text-property (point) 'org-marker)
+	      (progn
+		(setq tags (get-text-property (point) 'tags))
+		(if (not (eval org-agenda-filter-form))
+		    (org-agenda-filter-by-tag-hide-line))
+		(beginning-of-line 2))
+	    (beginning-of-line 2)))))
+     (t (error "Invalid tag selection character %c" char)))))
+
+(defun org-agenda-filter-by-tag-refine (strip &optional char)
+  "Refine the current filter.  See `org-agenda-filter-by-tag."
+  (interactive "P")
+  (org-agenda-filter-by-tag strip char 'refine))
+
+(defun org-agenda-filter-make-matcher ()
+  (let (f f1)
+    (dolist (x org-agenda-filter-tags)
+      (if (member x '("-" "+"))
+	  (setq f1 '(not 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) ?-)
+	    (setq f1 (list 'not f1))))
+      (push f1 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)
 
@@ -4183,7 +4270,10 @@ With prefix argument STRIP, remove all lines that do have the tag."
 
 (defun org-agenda-filter-by-tag-show-all ()
   (mapc 'org-delete-overlay org-agenda-filter-overlays)
-  (setq org-agenda-filter-overlays nil))
+  (setq org-agenda-filter-overlays nil)
+  (setq org-agenda-filter-tags nil)
+  (setq org-agenda-filter-form nil)
+  (org-agenda-set-mode-name))
 
 (defun org-agenda-manipulate-query-add ()
   "Manipulate the query by adding a search term with positive selection.
@@ -4522,6 +4612,9 @@ so that the date SD will be in that range."
 		(if org-agenda-include-diary   " Diary"  "")
 		(if org-agenda-use-time-grid   " Grid"   "")
 		(if org-agenda-show-log        " Log"    "")
+		(if org-agenda-filter-tags
+		    (concat " {" (mapconcat 'identity org-agenda-filter-tags "") "}")
+		  "")
 		(if org-agenda-archives-mode
 		    (if (eq org-agenda-archives-mode t)
 			" Archives"