Spelling fixes.
[bpt/emacs.git] / lisp / org / org-agenda.el
index aea284f..2b4a001 100644 (file)
@@ -1,11 +1,10 @@
 ;;; org-agenda.el --- Dynamic task and appointment lists for Org
 
-;; Copyright (C) 2004-201 Free Software Foundation, Inc.
+;; Copyright (C) 2004-2012 Free Software Foundation, Inc.
 
 ;; Author: Carsten Dominik <carsten at orgmode dot org>
 ;; Keywords: outlines, hypermedia, calendar, wp
 ;; Homepage: http://orgmode.org
-;; Version: 7.7
 ;;
 ;; This file is part of GNU Emacs.
 ;;
 ;;; Commentary:
 
 ;; This file contains the code for creating and using the Agenda for Org-mode.
+;;
+;; The functions `org-batch-agenda', `org-batch-agenda-csv', and
+;; `org-batch-store-agenda-views' are implemented as macros to provide
+;; a convenient way for extracting agenda information from the command
+;; line. The Lisp does not evaluate parameters of a macro call; thus
+;; it is not necessary to quote the parameters passed to one of those
+;; functions. E.g. you can write:
+;;
+;;   emacs -batch -l ~/.emacs -eval '(org-batch-agenda "a" org-agenda-span 7)'
+;;
+;; To export an agenda spanning 7 days. If `org-batch-agenda' would
+;; have been implemented as a regular function you'd have to quote the
+;; symbol org-agenda-span. Moreover: To use a symbol as parameter
+;; value you would have to double quote the symbol.
+;;
+;; This is a hack, but it works even when running Org byte-compiled.
+;;
 
 ;;; Code:
 
@@ -50,6 +66,8 @@
 (declare-function calendar-julian-date-string   "cal-julian" (&optional date))
 (declare-function calendar-mayan-date-string    "cal-mayan"  (&optional date))
 (declare-function calendar-persian-date-string  "cal-persia" (&optional date))
+(declare-function calendar-check-holidays       "holidays" (date))
+
 (declare-function org-datetree-find-date-create "org-datetree"
                  (date &optional keep-restriction))
 (declare-function org-columns-quit              "org-colview" ())
@@ -60,6 +78,8 @@
 (declare-function org-is-habit-p "org-habit" (&optional pom))
 (declare-function org-habit-parse-todo "org-habit" (&optional pom))
 (declare-function org-habit-get-priority "org-habit" (habit &optional moment))
+(declare-function org-pop-to-buffer-same-window "org-compat"
+                 (&optional buffer-or-name norecord label))
 
 (defvar calendar-mode-map)
 (defvar org-clock-current-task) ; defined in org-clock.el
@@ -225,6 +245,10 @@ you can \"misuse\" it to also add other text to the header.  However,
     (const user-defined-up) (const user-defined-down))
   "Sorting choices.")
 
+;; Keep custom values for `org-agenda-filter-preset' compatible with
+;; the new variable `org-agenda-tag-filter-preset'.
+(defvaralias 'org-agenda-filter-preset 'org-agenda-tag-filter-preset)
+
 (defconst org-agenda-custom-commands-local-options
   `(repeat :tag "Local settings for this command. Remember to quote values"
           (choice :tag "Setting"
@@ -266,19 +290,27 @@ you can \"misuse\" it to also add other text to the header.  However,
            (list :tag "Deadline Warning days"
                  (const org-deadline-warning-days)
                  (integer :value 1))
+           (list :tag "Category filter preset"
+                 (const org-agenda-category-filter-preset)
+                 (list
+                  (const :format "" quote)
+                  (repeat
+                   (string :tag "+category or -category"))))
            (list :tag "Tags filter preset"
-                 (const org-agenda-filter-preset)
+                 (const org-agenda-tag-filter-preset)
                  (list
                   (const :format "" quote)
                   (repeat
                    (string :tag "+tag or -tag"))))
            (list :tag "Set daily/weekly entry types"
                  (const org-agenda-entry-types)
-                 (set :greedy t :value (:deadline :scheduled :timestamp :sexp)
-                      (const :deadline)
-                      (const :scheduled)
-                      (const :timestamp)
-                      (const :sexp)))
+                 (list
+                  (const :format "" quote)
+                  (set :greedy t :value (:deadline :scheduled :timestamp :sexp)
+                       (const :deadline)
+                       (const :scheduled)
+                       (const :timestamp)
+                       (const :sexp))))
            (list :tag "Standard skipping condition"
                  :value (org-agenda-skip-function '(org-agenda-skip-entry-if))
                  (const org-agenda-skip-function)
@@ -330,7 +362,8 @@ This will be spliced into the custom type of
 `org-agenda-custom-commands'.")
 
 
-(defcustom org-agenda-custom-commands nil
+(defcustom org-agenda-custom-commands '(("n" "Agenda and all TODO's"
+                                        ((agenda "") (alltodo))))
   "Custom commands for the agenda.
 These commands will be offered on the splash screen displayed by the
 agenda dispatcher \\[org-agenda].  Each entry is a list like this:
@@ -375,7 +408,7 @@ where
 
 desc   A description string to be displayed in the dispatcher menu.
 cmd    An agenda command, similar to the above.  However, tree commands
-       are no allowed, but instead you can get agenda and global todo list.
+       are not allowed, but instead you can get agenda and global todo list.
        So valid commands for a set are:
        (agenda \"\" settings)
        (alltodo \"\" settings)
@@ -616,6 +649,7 @@ See also the variable `org-agenda-tags-todo-honor-ignore-options' if you want
 to make his option also apply to the tags-todo list."
   :group 'org-agenda-skip
   :group 'org-agenda-todo-list
+  :version "24.1"
   :type '(choice
          (const :tag "Ignore future timestamp todos" future)
          (const :tag "Ignore past or present timestamp todos" past)
@@ -763,12 +797,13 @@ trigger you to schedule it, and then you don't want to be reminded of it
 because you will take care of it on the day when scheduled."
   :group 'org-agenda-skip
   :group 'org-agenda-daily/weekly
+  :version "24.1"
   :type '(choice
-         (const :tag "Alwas show prewarning" nil)
+         (const :tag "Always show prewarning" nil)
          (const :tag "Remove prewarning if entry is scheduled" t)
          (integer :tag "Restart prewarning N days before deadline")))
 
-(defcustom org-agenda-skip-additional-timestamps-same-entry t
+(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
 entry, the rest of the entry will not be searched."
@@ -800,7 +835,7 @@ will only be dimmed."
   :group 'org-agenda-todo-list
   :type '(choice
          (const :tag "Do not dim" nil)
-         (const :tag "Dim to a grey face" t)
+         (const :tag "Dim to a gray face" t)
          (const :tag "Make invisible" invisible)))
 
 (defcustom org-timeline-show-empty-dates 3
@@ -827,6 +862,7 @@ property so than moving the mouse over the command shows it.
 Setting it to nil is good if matcher strings are very long and/or if
 you want to use two-column display (see `org-agenda-menu-two-column')."
   :group 'org-agenda
+  :version "24.1"
   :type 'boolean)
 
 (defcustom org-agenda-menu-two-column nil
@@ -834,6 +870,7 @@ you want to use two-column display (see `org-agenda-menu-two-column')."
 If you use this, you probably want to set `org-agenda-menu-show-matcher'
 to nil."
   :group 'org-agenda
+  :version "24.1"
   :type 'boolean)
 
 (defcustom org-finalize-agenda-hook nil
@@ -853,6 +890,13 @@ Needs to be set before org.el is loaded."
   :group 'org-agenda-startup
   :type 'boolean)
 
+(defcustom org-agenda-follow-indirect nil
+  "Non-nil means `org-agenda-follow-mode' displays only the
+current item's tree, in an indirect buffer."
+  :group 'org-agenda
+  :version "24.1"
+  :type 'boolean)
+
 (defcustom org-agenda-show-outline-path t
   "Non-nil means show outline path in echo area after line motion."
   :group 'org-agenda-startup
@@ -1003,11 +1047,13 @@ This function makes sure that dates are aligned for easy reading."
   "Non-nil means use leading zero for military times in agenda.
 For example, 9:30am would become 09:30 rather than  9:30."
   :group 'org-agenda-daily/weekly
+  :version "24.1"
   :type 'boolean)
 
 (defcustom org-agenda-timegrid-use-ampm nil
   "When set, show AM/PM style timestamps on the timegrid."
   :group 'org-agenda
+  :version "24.1"
   :type 'boolean)
 
 (defun org-agenda-time-of-day-to-ampm (time)
@@ -1048,6 +1094,16 @@ and timeline buffers."
              (const :tag "Saturday" 6)
              (const :tag "Sunday" 0)))
 
+(defcustom org-agenda-move-date-from-past-immediately-to-today t
+  "Non-nil means jump to today when moving a past date forward in time.
+When using S-right in the agenda to move a a date forward, and the date
+stamp currently points to the past, the first key press will move it
+to today.  WHen nil, just move one day forward even if the date stays
+in the past."
+  :group 'org-agenda-daily/weekly
+  :version "24.1"
+  :type 'boolean)
+
 (defcustom org-agenda-include-diary nil
   "If non-nil, include in the agenda entries from the Emacs Calendar's diary.
 Custom commands can set this variable in the options section."
@@ -1058,14 +1114,7 @@ Custom commands can set this variable in the options section."
   "If non-nil, include entries within their deadline warning period.
 Custom commands can set this variable in the options section."
   :group 'org-agenda-daily/weekly
-  :type 'boolean)
-
-(defcustom org-agenda-include-all-todo nil
-  "Set  means weekly/daily agenda will always contain all TODO entries.
-The TODO entries will be listed at the top of the agenda, before
-the entries for specific days.
-This option is deprecated, it is better to define a block agenda instead."
-  :group 'org-agenda-daily/weekly
+  :version "24.1"
   :type 'boolean)
 
 (defcustom org-agenda-repeating-timestamp-show-all t
@@ -1141,6 +1190,7 @@ issue display.
 :short-face           face for clock intervals that are too short"
   :group 'org-agenda-daily/weekly
   :group 'org-clock
+  :version "24.1"
   :type 'plist)
 
 (defcustom org-agenda-log-mode-add-notes t
@@ -1199,6 +1249,7 @@ by preceding the first snippet with \"+\" or \"-\".  If the first snippet
 is a regexp marked with braces like \"{abc}\", this will also switch to
 boolean search."
   :group 'org-agenda-search-view
+  :version "24.1"
   :type 'boolean)
 
 (if (fboundp 'defvaralias)
@@ -1209,6 +1260,7 @@ boolean search."
   "Non-nil means, search words must be matches as complete words.
 When nil, they may also match part of a word."
   :group 'org-agenda-search-view
+  :version "24.1"
   :type 'boolean)
 
 (defgroup org-agenda-time-grid nil
@@ -1272,12 +1324,14 @@ a grid line."
 (defcustom org-agenda-show-current-time-in-grid t
   "Non-nil means show the current time in the time grid."
   :group 'org-agenda-time-grid
+  :version "24.1"
   :type 'boolean)
 
 (defcustom org-agenda-current-time-string
   "now - - - - - - - - - - - - - - - - - - - - - - - - -"
   "The string for the current time marker in the agenda."
   :group 'org-agenda-time-grid
+  :version "24.1"
   :type 'string)
 
 (defgroup org-agenda-sorting nil
@@ -1388,14 +1442,15 @@ When nil, such items are sorted as 0 minutes effort."
     (tags  . " %i %-12:c")
     (search . " %i %-12:c"))
   "Format specifications for the prefix of items in the agenda views.
-An alist with five entries, each for the different agenda types.  The 
-keys of the sublists are `agenda', `timeline', `todo', `search' and `tags'.  
+An alist with five entries, each for the different agenda types.  The
+keys of the sublists are `agenda', `timeline', `todo', `search' and `tags'.
 The values are format strings.
 
 This format works similar to a printf format, with the following meaning:
 
-  %c   the category of the item, \"Diary\" for entries from the diary, 
+  %c   the category of the item, \"Diary\" for entries from the diary,
        or as given by the CATEGORY keyword or derived from the file name
+  %e   the effort required by the item
   %i   the icon category of the item, see `org-agenda-category-icon-alist'
   %T   the last tag of the item (ignore inherited tags, which come first)
   %t   the HH:MM time-of-day specification if one applies to the entry
@@ -1408,10 +1463,10 @@ contain two additional characters:  a question mark just after the `%'
 and a whitespace/punctuation character just before the final letter.
 
 If the first character after `%' is a question mark, the entire field
-will only be included if the corresponding value applies to the current 
-entry.  This is useful for fields which should have fixed width when 
-present, but zero width when absent.  For example, \"%?-12t\" will 
-result in a 12 character time field if a time of the day is specified, 
+will only be included if the corresponding value applies to the current
+entry.  This is useful for fields which should have fixed width when
+present, but zero width when absent.  For example, \"%?-12t\" will
+result in a 12 character time field if a time of the day is specified,
 but will completely disappear in entries which do not contain a time.
 
 If there is punctuation or whitespace character just before the final
@@ -1420,7 +1475,7 @@ the value is not empty.  For example, the format \"%-12:c\" leads to
 \"Diary: \" if the category is \"Diary\".  If the category were be
 empty, no additional colon would be inserted.
 
-The default value for the agenda sublist is \"  %-12:c%?-12t% s\", 
+The default value for the agenda sublist is \"  %-12:c%?-12t% s\",
 which means:
 
 - Indent the line with two space characters
@@ -1487,6 +1542,7 @@ that passed since this item was scheduled first."
   "Text preceding item pulled into the agenda by inactive time stamps.
 These entries are added to the agenda when pressing \"[\"."
   :group 'org-agenda-line-format
+  :version "24.1"
   :type '(list
          (string :tag "Scheduled today     ")
          (string :tag "Scheduled previously")))
@@ -1525,6 +1581,7 @@ the headline/diary entry."
   "Non-nil means remove time ranges specifications in agenda
 items that span on several days."
   :group 'org-agenda-line-format
+  :version "24.1"
   :type 'boolean)
 
 (defcustom org-agenda-default-appointment-duration nil
@@ -1606,6 +1663,7 @@ The only argument passed to that function is the day. It should
 returns a face, or nil if does not want to specify a face and let
 the normal rules apply."
   :group 'org-agenda-line-format
+  :version "24.1"
   :type 'function)
 
 (defcustom org-agenda-category-icon-alist nil
@@ -1619,7 +1677,7 @@ Where CATEGORY-REGEXP is a regexp matching the categories where
 the icon should be displayed.
 FILE-OR-DATA either a file path or a string containing image data.
 
-The other fields can be omited safely if not needed:
+The other fields can be omitted safely if not needed:
 TYPE indicates the image type.
 DATA-P is a boolean indicating whether the FILE-OR-DATA string is
 image data.
@@ -1638,6 +1696,7 @@ category, you can use:
 
   (\"Emacs\" '(space . (:width (16))))"
   :group 'org-agenda-line-format
+  :version "24.1"
   :type '(alist :key-type (string :tag "Regexp matching category")
                :value-type (choice (list :tag "Icon"
                                          (string :tag "File or data")
@@ -1697,9 +1756,10 @@ For example, this value makes those two functions available:
     (?C bulk-cut))
 
 With selected entries in an agenda buffer, `B R' will call
-the custom function `set-category' on the selected entries.  
+the custom function `set-category' on the selected entries.
 Note that functions in this alist don't need to be quoted."
   :type 'alist
+  :version "24.1"
   :group 'org-agenda)
 
 (eval-when-compile
@@ -1711,12 +1771,14 @@ Note that functions in this alist don't need to be quoted."
 If STRING is non-nil, the text property will be fetched from position 0
 in that string.  If STRING is nil, it will be fetched from the beginning
 of the current line."
-  `(let ((marker (get-text-property (if string 0 (point-at-bol))
-                                   'org-hd-marker string)))
-     (with-current-buffer (marker-buffer marker)
-       (save-excursion
-        (goto-char marker)
-        ,@body))))
+  (org-with-gensyms (marker)
+    `(let ((,marker (get-text-property (if string 0 (point-at-bol))
+                                      'org-hd-marker ,string)))
+       (with-current-buffer (marker-buffer ,marker)
+        (save-excursion
+          (goto-char ,marker)
+          ,@body)))))
+(def-edebug-spec org-agenda-with-point-at-orig-entry (form body))
 
 (defun org-add-agenda-custom-command (entry)
   "Replace or add a command in `org-agenda-custom-commands'.
@@ -1870,7 +1932,7 @@ The following commands are available:
 (org-defkey org-agenda-mode-map "\C-c\C-xp" 'org-agenda-set-property)
 (org-defkey org-agenda-mode-map "q" 'org-agenda-quit)
 (org-defkey org-agenda-mode-map "x" 'org-agenda-exit)
-(org-defkey org-agenda-mode-map "\C-x\C-w" 'org-write-agenda)
+(org-defkey org-agenda-mode-map "\C-x\C-w" 'org-agenda-write)
 (org-defkey org-agenda-mode-map "\C-x\C-s" 'org-save-all-org-buffers)
 (org-defkey org-agenda-mode-map "s" 'org-save-all-org-buffers)
 (org-defkey org-agenda-mode-map "P" 'org-agenda-show-priority)
@@ -1918,6 +1980,7 @@ The following commands are available:
 (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)
+(org-defkey org-agenda-mode-map "<" 'org-agenda-filter-by-category)
 (org-defkey org-agenda-mode-map ";" 'org-timer-set-timer)
 (define-key org-agenda-mode-map "?" 'org-agenda-show-the-flagging-note)
 (org-defkey org-agenda-mode-map "\C-c\C-x\C-mg"    'org-mobile-pull)
@@ -1984,7 +2047,7 @@ The following commands are available:
       :keys "v A"]
      "--"
      ["Remove Restriction" org-agenda-remove-restriction-lock org-agenda-restrict])
-    ["Write view to file" org-write-agenda t]
+    ["Write view to file" org-agenda-write t]
     ["Rebuild buffer" org-agenda-redo t]
     ["Save all Org-mode Buffers" org-save-all-org-buffers t]
     "--"
@@ -2295,7 +2358,7 @@ Pressing `<' twice means to restrict to the current subtree or region
                               nil 'face 'org-warning)))))))
         t t))
        ((equal keys "L")
-       (unless (org-mode-p)
+       (unless (eq major-mode 'org-mode)
          (error "This is not an Org-mode file"))
        (unless restriction
          (put 'org-agenda-files 'org-restrict (list bfn))
@@ -2330,7 +2393,7 @@ Agenda views are separated by `org-agenda-block-separator'."
   "The user interface for selecting an agenda command."
   (catch 'exit
     (let* ((bfn (buffer-file-name (buffer-base-buffer)))
-          (restrict-ok (and bfn (org-mode-p)))
+          (restrict-ok (and bfn (eq major-mode 'org-mode)))
           (region-p (org-region-active-p))
           (custom org-agenda-custom-commands)
           (selstring "")
@@ -2572,23 +2635,13 @@ If CMD-KEY is a string of length 1, it is used as a key in
 longer string it is used as a tags/todo match string.
 Parameters are alternating variable names and values that will be bound
 before running the agenda command."
-  (let (pars)
-    (while parameters
-      (push (list (pop parameters) (if parameters (pop parameters))) pars))
+  (org-eval-in-environment (org-make-parameter-alist parameters)
     (if (> (length cmd-key) 2)
-       (eval (list 'let (nreverse pars)
-                   (list 'org-tags-view nil cmd-key)))
-      (eval (list 'let (nreverse pars) (list 'org-agenda nil cmd-key))))
-    (set-buffer org-agenda-buffer-name)
-    (princ (org-encode-for-stdout (buffer-string)))))
-
-;(defun org-encode-for-stdout (string)
-;  (if (fboundp 'encode-coding-string)
-;      (encode-coding-string string buffer-file-coding-system)
-;    string))
-
-(defun org-encode-for-stdout (string)
-  string)
+       (org-tags-view nil cmd-key)
+      (org-agenda nil cmd-key)))
+  (set-buffer org-agenda-buffer-name)
+  (princ (buffer-string)))
+(def-edebug-spec org-batch-agenda (form &rest sexp))
 
 (defvar org-agenda-info nil)
 
@@ -2627,30 +2680,26 @@ extra        Sting with extra planning info
 priority-l   The priority letter if any was given
 priority-n   The computed numerical priority
 agenda-day   The day in the agenda where this is listed"
-
-  (let (pars)
-    (while parameters
-      (push (list (pop parameters) (if parameters (pop parameters))) pars))
-    (push (list 'org-agenda-remove-tags t) pars)
+  (org-eval-in-environment (append '((org-agenda-remove-tags t))
+                                  (org-make-parameter-alist parameters))
     (if (> (length cmd-key) 2)
-       (eval (list 'let (nreverse pars)
-                   (list 'org-tags-view nil cmd-key)))
-      (eval (list 'let (nreverse pars) (list 'org-agenda nil cmd-key))))
-    (set-buffer org-agenda-buffer-name)
-    (let* ((lines (org-split-string (buffer-string) "\n"))
-          line)
-      (while (setq line (pop lines))
-       (catch 'next
-         (if (not (get-text-property 0 'org-category line)) (throw 'next nil))
-         (setq org-agenda-info
-               (org-fix-agenda-info (text-properties-at 0 line)))
-         (princ
-          (org-encode-for-stdout
-           (mapconcat 'org-agenda-export-csv-mapper
-                      '(org-category txt type todo tags date time extra
-                                     priority-letter priority agenda-day)
-                     ",")))
-         (princ "\n"))))))
+       (org-tags-view nil cmd-key)
+      (org-agenda nil cmd-key)))
+  (set-buffer org-agenda-buffer-name)
+  (let* ((lines (org-split-string (buffer-string) "\n"))
+        line)
+    (while (setq line (pop lines))
+      (catch 'next
+       (if (not (get-text-property 0 'org-category line)) (throw 'next nil))
+       (setq org-agenda-info
+             (org-fix-agenda-info (text-properties-at 0 line)))
+       (princ
+        (mapconcat 'org-agenda-export-csv-mapper
+                   '(org-category txt type todo tags date time extra
+                                  priority-letter priority agenda-day)
+                   ","))
+       (princ "\n")))))
+(def-edebug-spec org-batch-agenda-csv (form &rest sexp))
 
 (defun org-fix-agenda-info (props)
   "Make sure all properties on an agenda item have a canonical form.
@@ -2700,17 +2749,14 @@ This ensures the export commands can easily use it."
   (interactive)
   (eval (list 'org-batch-store-agenda-views)))
 
-;; FIXME, why is this a macro?????
 ;;;###autoload
 (defmacro org-batch-store-agenda-views (&rest parameters)
   "Run all custom agenda commands that have a file argument."
   (let ((cmds (org-agenda-normalize-custom-commands org-agenda-custom-commands))
        (pop-up-frames nil)
        (dir default-directory)
-       pars cmd thiscmdkey files opts cmd-or-set)
-    (while parameters
-      (push (list (pop parameters) (if parameters (pop parameters))) pars))
-    (setq pars (reverse pars))
+       (pars (org-make-parameter-alist parameters))
+       cmd thiscmdkey files opts cmd-or-set)
     (save-window-excursion
       (while cmds
        (setq cmd (pop cmds)
@@ -2720,15 +2766,17 @@ This ensures the export commands can easily use it."
              files (nth (if (listp cmd-or-set) 4 5) cmd))
        (if (stringp files) (setq files (list files)))
        (when files
-         (eval (list 'let (append org-agenda-exporter-settings opts pars)
-                     (list 'org-agenda nil thiscmdkey)))
+         (org-eval-in-environment (append org-agenda-exporter-settings
+                                          opts pars)
+           (org-agenda nil thiscmdkey))
          (set-buffer org-agenda-buffer-name)
          (while files
-           (eval (list 'let (append org-agenda-exporter-settings opts pars)
-                       (list 'org-write-agenda
-                             (expand-file-name (pop files) dir) nil t))))
+           (org-eval-in-environment (append org-agenda-exporter-settings
+                                            opts pars)
+             (org-agenda-write (expand-file-name (pop files) dir) nil t)))
          (and (get-buffer org-agenda-buffer-name)
               (kill-buffer org-agenda-buffer-name)))))))
+(def-edebug-spec org-batch-store-agenda-views (&rest sexp))
 
 (defun org-agenda-mark-header-line (pos)
   "Mark the line at POS as an agenda structure header."
@@ -2741,7 +2789,8 @@ This ensures the export commands can easily use it."
                         'org-agenda-title-append org-agenda-title-append))))
 
 (defvar org-mobile-creating-agendas)
-(defun org-write-agenda (file &optional open nosettings)
+(defvar org-agenda-write-buffer-name "Agenda View")
+(defun org-agenda-write (file &optional open nosettings)
   "Write the current buffer (an agenda view) as a file.
 Depending on the extension of the file name, plain text (.txt),
 HTML (.html or .htm) or Postscript (.ps) is produced.
@@ -2763,7 +2812,7 @@ higher priority settings."
         (let ((bs (copy-sequence (buffer-string))) beg)
           (org-agenda-unmark-filtered-text)
           (with-temp-buffer
-            (rename-buffer "Agenda View" t)
+            (rename-buffer org-agenda-write-buffer-name t)
             (set-buffer-modified-p nil)
             (insert bs)
             (org-agenda-remove-marked-text 'org-filtered)
@@ -2824,7 +2873,8 @@ higher priority settings."
     (set-buffer org-agenda-buffer-name))
   (when open (org-open-file file)))
 
-(defvar org-agenda-filter-overlays nil)
+(defvar org-agenda-tag-filter-overlays nil)
+(defvar org-agenda-cat-filter-overlays nil)
 
 (defun org-agenda-mark-filtered-text ()
   "Mark all text hidden by filtering with a text property."
@@ -2835,7 +2885,8 @@ higher priority settings."
         (put-text-property
          (overlay-start o) (overlay-end o)
          'org-filtered t)))
-     org-agenda-filter-overlays)))
+     (append org-agenda-tag-filter-overlays
+            org-agenda-cat-filter-overlays))))
 
 (defun org-agenda-unmark-filtered-text ()
   "Remove the filtering text property."
@@ -2882,7 +2933,7 @@ removed from the entry content.  Currently only `planning' is allowed here."
   (let (txt drawer-re kwd-time-re ind)
     (save-excursion
       (with-current-buffer (marker-buffer marker)
-       (if (not (org-mode-p))
+       (if (not (eq major-mode 'org-mode))
            (setq txt "")
          (save-excursion
            (save-restriction
@@ -2998,7 +3049,7 @@ removed from the entry content.  Currently only `planning' is allowed here."
 
 (defun org-check-for-org-mode ()
   "Make sure current buffer is in org-mode.  Error if not."
-  (or (org-mode-p)
+  (or (eq major-mode 'org-mode)
       (error "Cannot execute org-mode agenda command on buffer in %s"
             major-mode)))
 
@@ -3018,9 +3069,10 @@ removed from the entry content.  Currently only `planning' is allowed here."
 (defvar org-pre-agenda-window-conf nil)
 (defvar org-agenda-columns-active nil)
 (defvar org-agenda-name nil)
-(defvar org-agenda-filter nil)
-(defvar org-agenda-filter-while-redo nil)
-(defvar org-agenda-filter-preset nil
+(defvar org-agenda-tag-filter nil)
+(defvar org-agenda-category-filter nil)
+(defvar org-agenda-tag-filter-while-redo nil)
+(defvar org-agenda-tag-filter-preset nil
   "A preset of the tags filter used for secondary agenda filtering.
 This must be a list of strings, each string must be a single tag preceded
 by \"+\" or \"-\".
@@ -3030,13 +3082,24 @@ the entire agenda view.  In a block agenda, it will not work reliably to
 define a filter for one of the individual blocks.  You need to set it in
 the global options and expect it to be applied to the entire view.")
 
+(defvar org-agenda-category-filter-preset nil
+  "A preset of the category filter used for secondary agenda filtering.
+This must be a list of strings, each string must be a single category
+preceded by \"+\" or \"-\".
+This variable should not be set directly, but agenda custom commands can
+bind it in the options section.  The preset filter is a global property of
+the entire agenda view.  In a block agenda, it will not work reliably to
+define a filter for one of the individual blocks.  You need to set it in
+the global options and expect it to be applied to the entire view.")
+
 (defun org-prepare-agenda (&optional name)
   (setq org-todo-keywords-for-agenda nil)
-  (setq org-done-keywords-for-agenda nil)
   (setq org-drawers-for-agenda nil)
   (unless org-agenda-persistent-filter
-    (setq org-agenda-filter nil))
-  (put 'org-agenda-filter :preset-filter org-agenda-filter-preset)
+    (setq org-agenda-tag-filter nil
+          org-agenda-category-filter nil))
+  (put 'org-agenda-tag-filter :preset-filter org-agenda-tag-filter-preset)
+  (put 'org-agenda-category-filter :preset-filter org-agenda-category-filter-preset)
   (if org-agenda-multi
       (progn
        (setq buffer-read-only nil)
@@ -3049,6 +3112,7 @@ the global options and expect it to be applied to the entire view.")
                    (make-string (window-width) org-agenda-block-separator))
                  "\n"))
        (narrow-to-region (point) (point-max)))
+    (setq org-done-keywords-for-agenda nil)
     (org-agenda-reset-markers)
     (setq org-agenda-contributing-files nil)
     (setq org-agenda-columns-active nil)
@@ -3065,7 +3129,7 @@ the global options and expect it to be applied to the entire view.")
        (awin (select-window awin))
        ((not (setq org-pre-agenda-window-conf (current-window-configuration))))
        ((equal org-agenda-window-setup 'current-window)
-       (switch-to-buffer abuf))
+       (org-pop-to-buffer-same-window abuf))
        ((equal org-agenda-window-setup 'other-window)
        (org-switch-to-buffer-other-window abuf))
        ((equal org-agenda-window-setup 'other-frame)
@@ -3076,7 +3140,7 @@ the global options and expect it to be applied to the entire view.")
       ;; additional test in case agenda is invoked from within agenda
       ;; buffer via elisp link
       (unless (equal (current-buffer) abuf)
-       (switch-to-buffer abuf)))
+       (org-pop-to-buffer-same-window abuf)))
     (setq buffer-read-only nil)
     (let ((inhibit-read-only t)) (erase-buffer))
     (org-agenda-mode)
@@ -3115,8 +3179,10 @@ the global options and expect it to be applied to the entire view.")
          (org-habit-insert-consistency-graphs))
       (run-hooks 'org-finalize-agenda-hook)
       (setq org-agenda-type (org-get-at-bol 'org-agenda-type))
-      (when (or org-agenda-filter (get 'org-agenda-filter :preset-filter))
-       (org-agenda-filter-apply org-agenda-filter))
+      (when (or org-agenda-tag-filter (get 'org-agenda-tag-filter :preset-filter))
+       (org-agenda-filter-apply org-agenda-tag-filter 'tag))
+      (when (or org-agenda-category-filter (get 'org-agenda-category-filter :preset-filter))
+       (org-agenda-filter-apply org-agenda-category-filter 'category))
       )))
 
 (defun org-agenda-mark-clocking-task ()
@@ -3190,17 +3256,9 @@ the global options and expect it to be applied to the entire view.")
        (setq org-blocked-by-checkboxes nil invis1 invis)
        (let ((marker (org-get-at-bol 'org-hd-marker)))
          (when (and marker
-                    (not (with-current-buffer (marker-buffer marker)
-                           (save-excursion
-                             (goto-char marker)
-                             (if (org-entry-get nil "NOBLOCKING")
-                                 t ;; Never block this entry
-                               (run-hook-with-args-until-failure
-                                'org-blocker-hook
-                                (list :type 'todo-state-change
-                                      :position marker
-                                      :from 'todo
-                                      :to 'done)))))))
+                    (with-current-buffer (marker-buffer marker)
+                      (save-excursion (goto-char marker)
+                                      (org-entry-blocked-p))))
            (if org-blocked-by-checkboxes (setq invis1 nil))
            (setq b (if invis1
                        (max (point-min) (1- (point-at-bol)))
@@ -3334,7 +3392,7 @@ no longer in use."
 
 (defvar org-agenda-only-exact-dates nil) ; dynamically scoped
 
-(defun org-timeline (&optional include-all)
+(defun org-timeline (&optional dotodo)
   "Show a time-sorted view of the entries in the current org file.
 Only entries with a time stamp of today or later will be listed.  With
 \\[universal-argument] prefix, all unfinished TODO items will also be shown,
@@ -3345,7 +3403,6 @@ dates."
   (org-compile-prefix-format 'timeline)
   (org-set-sorting-strategy 'timeline)
   (let* ((dopast t)
-        (dotodo include-all)
         (doclosed org-agenda-show-log)
         (entry (buffer-file-name (or (buffer-base-buffer (current-buffer))
                                      (current-buffer))))
@@ -3364,7 +3421,7 @@ dates."
     (setq org-agenda-redo-command
          (list 'progn
                (list 'org-switch-to-buffer-other-window (current-buffer))
-               (list 'org-timeline (list 'quote include-all))))
+               (list 'org-timeline (list 'quote dotodo))))
     (if (not dopast)
        ;; Remove past dates from the list of dates.
        (setq day-numbers (delq nil (mapcar (lambda(x)
@@ -3432,22 +3489,26 @@ When EMPTY is non-nil, also include days without any entries."
   (let ((re (concat
             (if pre-re pre-re "")
             (if inactive org-ts-regexp-both org-ts-regexp)))
-        dates dates1 date day day1 day2 ts1 ts2)
+        dates dates1 date day day1 day2 ts1 ts2 pos)
     (if force-today
        (setq dates (list (org-today))))
     (save-excursion
       (goto-char beg)
       (while (re-search-forward re end t)
        (setq day (time-to-days (org-time-string-to-time
-                                (substring (match-string 1) 0 10))))
+                                (substring (match-string 1) 0 10)
+                                (current-buffer) (match-beginning 0))))
        (or (memq day dates) (push day dates)))
       (unless no-ranges
        (goto-char beg)
        (while (re-search-forward org-tr-regexp end t)
+         (setq pos (match-beginning 0))
          (setq ts1 (substring (match-string 1) 0 10)
                ts2 (substring (match-string 2) 0 10)
-               day1 (time-to-days (org-time-string-to-time ts1))
-               day2 (time-to-days (org-time-string-to-time ts2)))
+               day1 (time-to-days (org-time-string-to-time
+                                   ts1 (current-buffer) pos))
+               day2 (time-to-days (org-time-string-to-time
+                                   ts2  (current-buffer) pos)))
          (while (< (setq day1 (1+ day1)) day2)
            (or (memq day1 dates) (push day1 dates)))))
       (setq dates (sort dates '<))
@@ -3472,7 +3533,7 @@ Custom commands can set this variable in the options section.")
 (defvar org-starting-day nil) ; local variable in the agenda buffer
 (defvar org-agenda-current-span nil
   "The current span used in the agenda view.") ; local variable in the agenda buffer
-(defvar org-include-all-loc nil) ; local variable
+(defvar org-arg-loc nil) ; local variable
 
 (defvar org-agenda-entry-types '(:deadline :scheduled :timestamp :sexp)
   "List of types searched for when creating the daily/weekly agenda.
@@ -3508,29 +3569,29 @@ somewhat less efficient) way of determining what is included in
 the daily/weekly agenda, see `org-agenda-skip-function'.")
 
 ;;;###autoload
-(defun org-agenda-list (&optional include-all start-day span)
+(defun org-agenda-list (&optional arg start-day span)
   "Produce a daily/weekly view from all files in variable `org-agenda-files'.
 The view will be for the current day or week, but from the overview buffer
 you will be able to go to other days/weeks.
 
 With a numeric prefix argument in an interactive call, the agenda will
-span INCLUDE-ALL days.  Lisp programs should instead specify SPAN to change
+span ARG days.  Lisp programs should instead specify SPAN to change
 the number of days.  SPAN defaults to `org-agenda-span'.
 
 START-DAY defaults to TODAY, or to the most recent match for the weekday
 given in `org-agenda-start-on-weekday'."
   (interactive "P")
-  (if (and (integerp include-all) (> include-all 0))
-      (setq span include-all include-all nil))
+  (if (and (integerp arg) (> arg 0))
+      (setq span arg arg nil))
   (setq start-day (or start-day org-agenda-start-day))
   (if org-agenda-overriding-arguments
-      (setq include-all (car org-agenda-overriding-arguments)
+      (setq arg (car org-agenda-overriding-arguments)
            start-day (nth 1 org-agenda-overriding-arguments)
            span (nth 2 org-agenda-overriding-arguments)))
   (if (stringp start-day)
       ;; Convert to an absolute day number
       (setq start-day (time-to-days (org-read-date nil t start-day))))
-  (setq org-agenda-last-arguments (list include-all start-day span))
+  (setq org-agenda-last-arguments (list arg start-day span))
   (org-compile-prefix-format 'agenda)
   (org-set-sorting-strategy 'agenda)
   (let* ((span (org-agenda-ndays-to-span
@@ -3557,7 +3618,7 @@ given in `org-agenda-start-on-weekday'."
         s e rtn rtnall file date d start-pos end-pos todayp
         clocktable-start clocktable-end filter)
     (setq org-agenda-redo-command
-         (list 'org-agenda-list (list 'quote include-all) start-day (list 'quote span)))
+         (list 'org-agenda-list (list 'quote arg) start-day (list 'quote span)))
     (dotimes (n (1- ndays))
       (push (1+ (car day-numbers)) day-numbers))
     (setq day-numbers (nreverse day-numbers))
@@ -3565,7 +3626,7 @@ given in `org-agenda-start-on-weekday'."
          clocktable-end (1+ (or (org-last day-numbers) 0)))
     (org-prepare-agenda "Day/Week")
     (org-set-local 'org-starting-day (car day-numbers))
-    (org-set-local 'org-include-all-loc include-all)
+    (org-set-local 'org-arg-loc arg)
     (org-set-local 'org-agenda-current-span (org-agenda-ndays-to-span span))
     (unless org-agenda-compact-blocks
       (let* ((d1 (car day-numbers))
@@ -3616,7 +3677,7 @@ given in `org-agenda-start-on-weekday'."
              (setq rtn (apply 'org-agenda-get-day-entries
                               file date
                               org-agenda-entry-types)))))
-         (setq rtnall (append rtnall rtn))))
+         (setq rtnall (append rtnall rtn)))) ;; all entries
       (if org-agenda-include-diary
          (let ((org-agenda-search-headline-for-time t))
            (require 'diary-lib)
@@ -3638,7 +3699,7 @@ given in `org-agenda-start-on-weekday'."
            (put-text-property s (1- (point)) 'org-day-cnt day-cnt)
            (when todayp
              (put-text-property s (1- (point)) 'org-today t))
-           (if rtnall (insert
+           (if rtnall (insert ;; all entries
                        (org-finalize-agenda-entries
                         (org-agenda-add-time-grid-maybe
                          rtnall ndays todayp))
@@ -3655,8 +3716,8 @@ given in `org-agenda-start-on-weekday'."
        (setq p (plist-put p :tend clocktable-end))
        (setq p (plist-put p :scope 'agenda))
        (when (and (eq org-agenda-clockreport-mode 'with-filter)
-                  (setq filter (or org-agenda-filter-while-redo
-                                   (get 'org-agenda-filter :preset-filter))))
+                  (setq filter (or org-agenda-tag-filter-while-redo
+                                   (get 'org-agenda-tag-filter :preset-filter))))
          (setq p (plist-put p :tags (mapconcat (lambda (x)
                                                  (if (string-match "[<>=]" x)
                                                      ""
@@ -3786,7 +3847,7 @@ in `org-agenda-text-search-extra-files'."
         (full-words org-agenda-search-view-force-full-words)
         (org-agenda-text-search-extra-files org-agenda-text-search-extra-files)
         regexp rtn rtnall files file pos
-        marker category tags c neg re boolean
+        marker category org-category-pos tags c neg re boolean
         ee txt beg end words regexps+ regexps- hdl-only buffer beg1 str)
     (unless (and (not edit-at)
                 (stringp string)
@@ -3869,7 +3930,7 @@ in `org-agenda-text-search-extra-files'."
     (if (not regexps+)
        (setq regexp org-outline-regexp-bol)
       (setq regexp (pop regexps+))
-      (if hdl-only (setq regexp (concat "^" org-outline-regexp ".*?"
+      (if hdl-only (setq regexp (concat org-outline-regexp-bol ".*?"
                                        regexp))))
     (setq files (org-agenda-files nil 'ifmode))
     (when (eq (car org-agenda-text-search-extra-files) 'agenda-archives)
@@ -3890,7 +3951,7 @@ in `org-agenda-text-search-extra-files'."
                                    file))))
        (with-current-buffer buffer
          (with-syntax-table (org-search-syntax-table)
-           (unless (org-mode-p)
+           (unless (eq major-mode 'org-mode)
              (error "Agenda file %s is not in `org-mode'" file))
            (let ((case-fold-search t))
              (save-excursion
@@ -3900,7 +3961,7 @@ in `org-agenda-text-search-extra-files'."
                                        org-agenda-restrict-end)
                    (widen))
                  (goto-char (point-min))
-                 (unless (or (org-on-heading-p)
+                 (unless (or (org-at-heading-p)
                              (outline-next-heading))
                    (throw 'nextfile t))
                  (goto-char (max (point-min) (1- (point))))
@@ -3930,8 +3991,9 @@ in `org-agenda-text-search-extra-files'."
                      (goto-char beg)
                      (setq marker (org-agenda-new-marker (point))
                            category (org-get-category)
+                           org-category-pos (get-text-property (point) 'org-category-position)
                            tags (org-get-tags-at (point))
-                           txt (org-format-agenda-item
+                           txt (org-agenda-format-item
                                 ""
                                 (buffer-substring-no-properties
                                  beg1 (point-at-eol))
@@ -3941,6 +4003,7 @@ in `org-agenda-text-search-extra-files'."
                        'org-todo-regexp org-todo-regexp
                        'org-complex-heading-regexp org-complex-heading-regexp
                        'priority 1000 'org-category category
+                       'org-category-position org-category-pos
                        'type "search")
                      (push txt ee)
                      (goto-char (1- end))))))))))
@@ -4081,7 +4144,7 @@ The prefix arg TODO-ONLY limits the search to TODO entries."
                       (format "ORG-AGENDA-ERROR: No such org-file %s" file))
                  rtnall (append rtnall rtn))
          (with-current-buffer buffer
-           (unless (org-mode-p)
+           (unless (eq major-mode 'org-mode)
              (error "Agenda file %s is not in `org-mode'" file))
            (save-excursion
              (save-restriction
@@ -4177,7 +4240,7 @@ See `org-agenda-skip-if' for details."
 (defun org-agenda-skip-if (subtree conditions)
   "Checks current entity for CONDITIONS.
 If SUBTREE is non-nil, the entire subtree is checked.  Otherwise, only
-the entry, i.e. the text before the next heading is checked.
+the entry (i.e. the text before the next heading) is checked.
 
 CONDITIONS is a list of symbols, boolean OR is used to combine the results
 from different tests.  Valid conditions are:
@@ -4203,12 +4266,12 @@ keywords, which may include \"*\" to match any todo keyword.
 
 would skip all entries with \"TODO\" or \"WAITING\" keywords.
 
-Instead of a list a keyword class may be given
+Instead of a list, a keyword class may be given.  For example:
 
     (org-agenda-skip-entry-if 'nottodo 'done)
 
 would skip entries that haven't been marked with any of \"DONE\"
-keywords. Possible classes are: `todo', `done', `any'.
+keywords.  Possible classes are: `todo', `done', `any'.
 
 If any of these conditions is met, this function returns the end point of
 the entity, causing the search to continue from there.  This is a function
@@ -4241,8 +4304,8 @@ that can be put into `org-agenda-skip-function' for the duration of a command."
           (stringp (nth 1 m))
           (not (re-search-forward (nth 1 m) end t)))
       (and (or
-           (setq m (memq 'todo conditions))
-           (setq m (memq 'nottodo conditions)))
+           (setq m (memq 'nottodo conditions))
+           (setq m (memq 'todo conditions)))
           (org-agenda-skip-if-todo m end)))
      end)))
 
@@ -4305,8 +4368,8 @@ of what a project is and how to check if it stuck, customize the variable
                          "\\)\\>"))
         (tags (nth 2 org-stuck-projects))
         (tags-re (if (member "*" tags)
-                     (org-re (concat org-outline-regexp-bol
-                                     ".*:[[:alnum:]_@#%]+:[ \t]*$"))
+                     (concat org-outline-regexp-bol
+                             (org-re ".*:[[:alnum:]_@#%]+:[ \t]*$"))
                    (if tags
                        (concat org-outline-regexp-bol
                                ".*:\\("
@@ -4380,7 +4443,7 @@ of what a project is and how to check if it stuck, customize the variable
       (setq entries
            (mapcar
             (lambda (x)
-              (setq x (org-format-agenda-item "" x "Diary" nil 'time))
+              (setq x (org-agenda-format-item "" x "Diary" nil 'time))
               ;; Extend the text properties to the beginning of the line
               (org-add-props x (text-properties-at (1- (length x)) x)
                 'type "diary" 'date date 'face 'org-agenda-diary))
@@ -4525,7 +4588,7 @@ the documentation of `org-diary'."
        ;; If file does not exist, make sure an error message ends up in diary
        (list (format "ORG-AGENDA-ERROR: No such org-file %s" file))
       (with-current-buffer buffer
-       (unless (org-mode-p)
+       (unless (eq major-mode 'org-mode)
          (error "Agenda file %s is not in `org-mode'" file))
        (let ((case-fold-search nil))
          (save-excursion
@@ -4562,6 +4625,7 @@ the documentation of `org-diary'."
                  (setq results (append results rtn))))))))
        results))))
 
+(defvar org-heading-keyword-regexp-format) ; defined in org.el
 (defun org-agenda-get-todos ()
   "Return the TODO information for agenda display."
   (let* ((props (list 'face nil
@@ -4573,16 +4637,20 @@ the documentation of `org-diary'."
                      'help-echo
                      (format "mouse-2 or RET jump to org file %s"
                              (abbreviate-file-name buffer-file-name))))
-        (regexp (concat "^\\*+[ \t]+\\("
-                        (if org-select-this-todo-keyword
-                            (if (equal org-select-this-todo-keyword "*")
-                                org-todo-regexp
-                              (concat "\\<\\("
-                                      (mapconcat 'identity (org-split-string org-select-this-todo-keyword "|") "\\|")
-                                    "\\)\\>"))
-                          org-not-done-regexp)
-                        "[^\n\r]*\\)"))
-        marker priority category tags todo-state
+        (regexp (format org-heading-keyword-regexp-format
+                        (cond
+                         ((and org-select-this-todo-keyword
+                               (equal org-select-this-todo-keyword "*"))
+                          org-todo-regexp)
+                         (org-select-this-todo-keyword
+                          (concat "\\("
+                                  (mapconcat 'identity
+                                             (org-split-string
+                                              org-select-this-todo-keyword
+                                              "|")
+                                             "\\|") "\\)"))
+                         (t org-not-done-regexp))))
+        marker priority category org-category-pos tags todo-state
         ee txt beg end)
     (goto-char (point-min))
     (while (re-search-forward regexp nil t)
@@ -4595,21 +4663,24 @@ the documentation of `org-diary'."
            (goto-char (1+ beg))
            (or org-agenda-todo-list-sublevels (org-end-of-subtree 'invisible))
            (throw :skip nil)))
-       (goto-char (match-beginning 1))
+       (goto-char (match-beginning 2))
        (setq marker (org-agenda-new-marker (match-beginning 0))
              category (org-get-category)
-             txt (match-string 1)
+             org-category-pos (get-text-property (point) 'org-category-position)
+             txt (org-trim
+                  (buffer-substring (match-beginning 2) (match-end 0)))
              tags (org-get-tags-at (point))
-             txt (org-format-agenda-item "" txt category tags)
+             txt (org-agenda-format-item "" txt category tags)
              priority (1+ (org-get-priority txt))
              todo-state (org-get-todo-state))
        (org-add-props txt props
          'org-marker marker 'org-hd-marker marker
          'priority priority 'org-category category
+         'org-category-position org-category-pos
          'type "todo" 'todo-state todo-state)
        (push txt ee)
        (if org-agenda-todo-list-sublevels
-           (goto-char (match-end 1))
+           (goto-char (match-end 2))
          (org-end-of-subtree 'invisible))))
     (nreverse ee)))
 
@@ -4691,7 +4762,7 @@ This function is invoked if `org-agenda-todo-ignore-deadlines',
 
 (defun org-agenda-get-timestamps ()
   "Return the date stamp information for agenda display."
-  (let* ((props (list 'face nil
+  (let* ((props (list 'face 'org-agenda-calendar-event
                      'org-not-done-regexp org-not-done-regexp
                      'org-todo-regexp org-todo-regexp
                      'org-complex-heading-regexp org-complex-heading-regexp
@@ -4720,8 +4791,8 @@ This function is invoked if `org-agenda-todo-ignore-deadlines',
           "\\|\\(<[0-9]+-[0-9]+-[0-9]+[^>\n]+?\\+[0-9]+[dwmy]>\\)"
           "\\|\\(<%%\\(([^>\n]+)\\)>\\)"))
         marker hdmarker deadlinep scheduledp clockp closedp inactivep
-        donep tmp priority category ee txt timestr tags b0 b3 e3 head
-        todo-state end-of-match show-all)
+        donep tmp priority category org-category-pos ee txt timestr tags
+        b0 b3 e3 head todo-state end-of-match show-all)
     (goto-char (point-min))
     (while (setq end-of-match (re-search-forward regexp nil t))
       (setq b0 (match-beginning 0)
@@ -4735,7 +4806,8 @@ This function is invoked if `org-agenda-todo-ignore-deadlines',
        (org-agenda-skip)
        (if (and (match-end 1)
                 (not (= d1 (org-time-string-to-absolute
-                            (match-string 1) d1 nil show-all))))
+                            (match-string 1) d1 nil show-all
+                            (current-buffer) b0))))
            (throw :skip nil))
        (if (and e3
                 (not (org-diary-sexp-entry (buffer-substring b3 e3) "" date)))
@@ -4760,7 +4832,8 @@ This function is invoked if `org-agenda-todo-ignore-deadlines',
            ;; substring should only run to end of time stamp
            (setq timestr (substring timestr 0 (match-end 0))))
        (setq marker (org-agenda-new-marker b0)
-             category (org-get-category b0))
+             category (org-get-category b0)
+             org-category-pos (get-text-property b0 'org-category-position))
        (save-excursion
          (if (not (re-search-backward org-outline-regexp-bol nil t))
              (setq txt org-agenda-no-heading-message)
@@ -4769,7 +4842,7 @@ This function is invoked if `org-agenda-todo-ignore-deadlines',
                  tags (org-get-tags-at))
            (looking-at "\\*+[ \t]+\\([^\r\n]+\\)")
            (setq head (or (match-string 1) ""))
-           (setq txt (org-format-agenda-item
+           (setq txt (org-agenda-format-item
                       (if inactivep org-agenda-inactive-leader nil)
                       head category tags timestr
                       remove-re)))
@@ -4778,6 +4851,7 @@ This function is invoked if `org-agenda-todo-ignore-deadlines',
            'org-marker marker 'org-hd-marker hdmarker)
          (org-add-props txt nil 'priority priority
                         'org-category category 'date date
+                        'org-category-position org-category-pos
                         'todo-state todo-state
                         'type "timestamp")
          (push txt ee))
@@ -4789,13 +4863,14 @@ This function is invoked if `org-agenda-todo-ignore-deadlines',
 (defun org-agenda-get-sexps ()
   "Return the sexp information for agenda display."
   (require 'diary-lib)
-  (let* ((props (list 'mouse-face 'highlight
+  (let* ((props (list 'face 'org-agenda-calendar-sexp
+                     'mouse-face 'highlight
                      'help-echo
                      (format "mouse-2 or RET jump to org file %s"
                              (abbreviate-file-name buffer-file-name))))
         (regexp "^&?%%(")
-        marker category ee txt tags entry result beg b sexp sexp-entry
-        todo-state)
+        marker category org-category-pos ee txt tags entry
+        result beg b sexp sexp-entry todo-state)
     (goto-char (point-min))
     (while (re-search-forward regexp nil t)
       (catch :skip
@@ -4812,6 +4887,7 @@ This function is invoked if `org-agenda-todo-ignore-deadlines',
        (when result
          (setq marker (org-agenda-new-marker beg)
                category (org-get-category beg)
+               org-category-pos (get-text-property beg 'org-category-position)
                todo-state (org-get-todo-state))
 
          (dolist (r (if (stringp result)
@@ -4821,11 +4897,12 @@ This function is invoked if `org-agenda-todo-ignore-deadlines',
                (setq txt r)
              (setq txt "SEXP entry returned empty string"))
 
-           (setq txt (org-format-agenda-item
+           (setq txt (org-agenda-format-item
                       "" txt category tags 'time))
            (org-add-props txt props 'org-marker marker)
            (org-add-props txt nil
              'org-category category 'date date 'todo-state todo-state
+             'org-category-position org-category-pos
              'type "sexp")
            (push txt ee)))))
     (nreverse ee)))
@@ -4860,9 +4937,11 @@ This function is invoked if `org-agenda-todo-ignore-deadlines',
 ;; Define the` org-class' function
 (defun org-class (y1 m1 d1 y2 m2 d2 dayname &rest skip-weeks)
   "Entry applies if date is between dates on DAYNAME, but skips SKIP-WEEKS.
-DAYNAME is a number between 0 (Sunday) and 6 (Saturday).  SKIP-WEEKS
-is any number of ISO weeks in the block period for which the item should
-be skipped."
+DAYNAME is a number between 0 (Sunday) and 6 (Saturday).
+SKIP-WEEKS is any number of ISO weeks in the block period for which the
+item should be skipped.  If any of the SKIP-WEEKS arguments is the symbol
+`holidays', then any date that is known by the Emacs calendar to be a
+holiday will also be skipped."
   (let* ((date1 (calendar-absolute-from-gregorian (list m1 d1 y1)))
         (date2 (calendar-absolute-from-gregorian (list m2 d2 y2)))
         (d (calendar-absolute-from-gregorian date)))
@@ -4874,6 +4953,8 @@ be skipped."
         (progn
           (require 'cal-iso)
           (not (member (car (calendar-iso-from-absolute d)) skip-weeks))))
+     (not (and (memq 'holidays skip-weeks)
+              (calendar-check-holidays date)))
      entry)))
 
 (defun org-diary-class (m1 d1 y1 m2 d2 y2 dayname &rest skip-weeks)
@@ -4894,6 +4975,7 @@ please use `org-class' instead."
      (nth 2 date1) (car date1) (nth 1 date1)
      (nth 2 date2) (car date2) (nth 1 date2)
      dayname skip-weeks)))
+(make-obsolete 'org-diary-class 'org-class "")
 
 (defalias 'org-get-closed 'org-agenda-get-progress)
 (defun org-agenda-get-progress ()
@@ -4929,8 +5011,8 @@ please use `org-class' instead."
                            (list 0 0 0 (nth 1 date) (car date) (nth 2 date))))
                    1 11))))
         (org-agenda-search-headline-for-time nil)
-        marker hdmarker priority category tags closedp statep clockp state
-        ee txt extra timestr rest clocked)
+        marker hdmarker priority category org-category-pos tags closedp
+        statep clockp state ee txt extra timestr rest clocked)
     (goto-char (point-min))
     (while (re-search-forward regexp nil t)
       (catch :skip
@@ -4941,14 +5023,15 @@ please use `org-class' instead."
              clockp (not (or closedp statep))
              state (and statep (match-string 2))
              category (org-get-category (match-beginning 0))
-             timestr (buffer-substring (match-beginning 0) (point-at-eol))
-             )
+             org-category-pos (get-text-property (match-beginning 0) 'org-category-position)
+             timestr (buffer-substring (match-beginning 0) (point-at-eol)))
        (when (string-match "\\]" timestr)
          ;; substring should only run to end of time stamp
          (setq rest (substring timestr (match-end 0))
                timestr (substring timestr 0 (match-end 0)))
          (if (and (not closedp) (not statep)
-                  (string-match "\\([0-9]\\{1,2\\}:[0-9]\\{2\\}\\)\\].*?\\([0-9]\\{1,2\\}:[0-9]\\{2\\}\\)" rest))
+                  (string-match "\\([0-9]\\{1,2\\}:[0-9]\\{2\\}\\)\\].*?\\([0-9]\\{1,2\\}:[0-9]\\{2\\}\\)"
+                                rest))
              (progn (setq timestr (concat (substring timestr 0 -1)
                                           "-" (match-string 1 rest) "]"))
                     (setq clocked (match-string 2 rest)))
@@ -4975,7 +5058,7 @@ please use `org-class' instead."
                  (setq txt (concat (substring txt 0 (match-beginning 1))
                                    " - " extra " " (match-string 2 txt)))
                (setq txt (concat txt " - " extra))))
-           (setq txt (org-format-agenda-item
+           (setq txt (org-agenda-format-item
                       (cond
                        (closedp "Closed:    ")
                            (statep (concat "State:     (" state ")"))
@@ -4985,6 +5068,7 @@ please use `org-class' instead."
          (org-add-props txt props
            'org-marker marker 'org-hd-marker hdmarker 'face 'org-agenda-done
            'priority priority 'org-category category
+           'org-category-position org-category-pos
            'type "closed" 'date date
            'undone-face 'org-warning 'done-face 'org-agenda-done)
          (push txt ee))
@@ -5003,9 +5087,9 @@ See also the user option `org-agenda-clock-consistency-checks'."
                     "\\(-\\{1,3\\}\\(\\[.*?\\]\\)\\)?")) ; group 3 is second
         (tlstart 0.)
         (tlend 0.)
-        (maxtime (org-hh:mm-string-to-minutes 
+        (maxtime (org-hh:mm-string-to-minutes
                   (or (plist-get pl :max-duration) "24:00")))
-        (mintime (org-hh:mm-string-to-minutes 
+        (mintime (org-hh:mm-string-to-minutes
                   (or (plist-get pl :min-duration) 0)))
         (maxgap  (org-hh:mm-string-to-minutes
                   ;; default 30:00 means never complain
@@ -5122,9 +5206,9 @@ See also the user option `org-agenda-clock-consistency-checks'."
         (regexp org-deadline-time-regexp)
         (todayp (org-agenda-todayp date)) ; DATE bound by calendar
         (d1 (calendar-absolute-from-gregorian date))  ; DATE bound by calendar
-        d2 diff dfrac wdays pos pos1 category tags
-        suppress-prewarning
-        ee txt head face s todo-state show-all upcomingp donep timestr)
+        d2 diff dfrac wdays pos pos1 category org-category-pos
+        tags suppress-prewarning ee txt head face s todo-state
+        show-all upcomingp donep timestr)
     (goto-char (point-min))
     (while (re-search-forward regexp nil t)
       (setq suppress-prewarning nil)
@@ -5147,13 +5231,14 @@ See also the user option `org-agenda-clock-consistency-checks'."
                           (member todo-state
                                    org-agenda-repeating-timestamp-show-all))
              d2 (org-time-string-to-absolute
-                 (match-string 1) d1 'past show-all)
+                 (match-string 1) d1 'past show-all
+                 (current-buffer) pos)
              diff (- d2 d1)
              wdays (if suppress-prewarning
                        (let ((org-deadline-warning-days suppress-prewarning))
                          (org-get-wdays s))
                      (org-get-wdays s))
-             dfrac (/ (* 1.0 (- wdays diff)) (max wdays 1))
+             dfrac (- 1 (/ (* 1.0 diff) (max wdays 1)))
              upcomingp (and todayp (> diff 0)))
        ;; When to show a deadline in the calendar:
        ;; If the expiration is within wdays warning time.
@@ -5168,7 +5253,8 @@ See also the user option `org-agenda-clock-consistency-checks'."
                       (or org-agenda-skip-deadline-if-done
                           (not (= diff 0))))
                  (setq txt nil)
-               (setq category (org-get-category))
+               (setq category (org-get-category)
+                     org-category-pos (get-text-property (point) 'org-category-position))
                (if (not (re-search-backward "^\\*+[ \t]+" nil t))
                    (setq txt org-agenda-no-heading-message)
                  (goto-char (match-end 0))
@@ -5182,7 +5268,7 @@ See also the user option `org-agenda-clock-consistency-checks'."
                      (setq timestr
                            (concat (substring s (match-beginning 1)) " "))
                    (setq timestr 'time))
-                 (setq txt (org-format-agenda-item
+                 (setq txt (org-agenda-format-item
                             (if (= diff 0)
                                 (car org-agenda-deadline-leaders)
                               (if (functionp
@@ -5195,13 +5281,14 @@ See also the user option `org-agenda-clock-consistency-checks'."
                             head category tags
                             (if (not (= diff 0)) nil timestr)))))
              (when txt
-               (setq face (org-agenda-deadline-face dfrac wdays))
+               (setq face (org-agenda-deadline-face dfrac))
                (org-add-props txt props
                  'org-marker (org-agenda-new-marker pos)
                  'org-hd-marker (org-agenda-new-marker pos1)
                  'priority (+ (- diff)
                               (org-get-priority txt))
                  'org-category category
+                 'org-category-position org-category-pos
                  'todo-state todo-state
                  'type (if upcomingp "upcoming-deadline" "deadline")
                  'date (if upcomingp date d2)
@@ -5210,10 +5297,9 @@ See also the user option `org-agenda-clock-consistency-checks'."
                (push txt ee))))))
     (nreverse ee)))
 
-(defun org-agenda-deadline-face (fraction &optional wdays)
+(defun org-agenda-deadline-face (fraction)
   "Return the face to displaying a deadline item.
 FRACTION is what fraction of the head-warning time has passed."
-  (if (equal wdays 0) (setq fraction 1.))
   (let ((faces org-agenda-deadline-faces) f)
     (catch 'exit
       (while (setq f (pop faces))
@@ -5238,7 +5324,7 @@ FRACTION is what fraction of the head-warning time has passed."
                                        0 'org-hd-marker a))
                              (cons (marker-position mm) a)))
                  deadline-results))
-        d2 diff pos pos1 category tags donep
+        d2 diff pos pos1 category org-category-pos tags donep
         ee txt head pastschedp todo-state face timestr s habitp show-all)
     (goto-char (point-min))
     (while (re-search-forward regexp nil t)
@@ -5252,7 +5338,8 @@ FRACTION is what fraction of the head-warning time has passed."
                           (member todo-state
                                   org-agenda-repeating-timestamp-show-all))
              d2 (org-time-string-to-absolute
-                 (match-string 1) d1 'past show-all)
+                 (match-string 1) d1 'past show-all
+                 (current-buffer) pos)
              diff (- d2 d1))
        (setq pastschedp (and todayp (< diff 0)))
        ;; When to show a scheduled item in the calendar:
@@ -5271,7 +5358,8 @@ FRACTION is what fraction of the head-warning time has passed."
                (setq txt nil)
              (setq habitp (and (functionp 'org-is-habit-p)
                                (org-is-habit-p)))
-             (setq category (org-get-category))
+             (setq category (org-get-category)
+                   org-category-pos (get-text-property (point) 'org-category-position))
              (if (not (re-search-backward "^\\*+[ \t]+" nil t))
                  (setq txt org-agenda-no-heading-message)
                (goto-char (match-end 0))
@@ -5295,7 +5383,7 @@ FRACTION is what fraction of the head-warning time has passed."
                    (setq timestr
                          (concat (substring s (match-beginning 1)) " "))
                  (setq timestr 'time))
-               (setq txt (org-format-agenda-item
+               (setq txt (org-agenda-format-item
                           (if (= diff 0)
                               (car org-agenda-scheduled-leaders)
                             (format (nth 1 org-agenda-scheduled-leaders)
@@ -5322,6 +5410,7 @@ FRACTION is what fraction of the head-warning time has passed."
                              (org-habit-get-priority habitp)
                            (+ 94 (- 5 diff) (org-get-priority txt)))
                'org-category category
+               'org-category-position org-category-pos
                'org-habit-p habitp
                'todo-state todo-state)
              (push txt ee))))))
@@ -5339,8 +5428,8 @@ FRACTION is what fraction of the head-warning time has passed."
                              (abbreviate-file-name buffer-file-name))))
         (regexp org-tr-regexp)
         (d0 (calendar-absolute-from-gregorian date))
-        marker hdmarker ee txt d1 d2 s1 s2 category todo-state tags pos
-        head donep)
+        marker hdmarker ee txt d1 d2 s1 s2 category org-category-pos
+        todo-state tags pos head donep)
     (goto-char (point-min))
     (while (re-search-forward regexp nil t)
       (catch :skip
@@ -5350,8 +5439,8 @@ FRACTION is what fraction of the head-warning time has passed."
              (end-time (match-string 2)))
          (setq s1 (match-string 1)
                s2 (match-string 2)
-               d1 (time-to-days (org-time-string-to-time s1))
-               d2 (time-to-days (org-time-string-to-time s2)))
+               d1 (time-to-days (org-time-string-to-time s1 (current-buffer) pos))
+               d2 (time-to-days (org-time-string-to-time s2 (current-buffer) pos)))
          (if (and (> (- d0 d1) -1) (> (- d2 d0) -1))
              ;; Only allow days between the limits, because the normal
              ;; date stamps will catch the limits.
@@ -5361,7 +5450,8 @@ FRACTION is what fraction of the head-warning time has passed."
                (if (and donep org-agenda-skip-timestamp-if-done)
                    (throw :skip t))
                (setq marker (org-agenda-new-marker (point)))
-               (setq category (org-get-category))
+               (setq category (org-get-category)
+                     org-category-pos (get-text-property (point) 'org-category-position))
                (if (not (re-search-backward org-outline-regexp-bol nil t))
                    (setq txt org-agenda-no-heading-message)
                  (goto-char (match-beginning 0))
@@ -5376,13 +5466,15 @@ FRACTION is what fraction of the head-warning time has passed."
                              "--"
                              "<" (regexp-quote s2) ".*?>")
                           nil)))
-                   (setq txt (org-format-agenda-item
+                   (setq txt (org-agenda-format-item
                               (format
                                (nth (if (= d1 d2) 0 1)
                                     org-agenda-timerange-leaders)
                                (1+ (- d0 d1)) (1+ (- d2 d1)))
                               head category tags
-                              (cond ((= d1 d0)
+                              (cond ((and (= d1 d0) (= d2 d0))
+                                     (concat "<" start-time ">--<" end-time ">"))
+                                     ((= d1 d0)
                                      (concat "<" start-time ">"))
                                     ((= d2 d0)
                                      (concat "<" end-time ">"))
@@ -5392,7 +5484,8 @@ FRACTION is what fraction of the head-warning time has passed."
                  'org-marker marker 'org-hd-marker hdmarker
                  'type "block" 'date date
                  'todo-state todo-state
-                 'priority (org-get-priority txt) 'org-category category)
+                 'priority (org-get-priority txt) 'org-category category
+                 'org-category-position org-category-pos)
                (push txt ee))))
        (goto-char pos)))
     ;; Sort the entries by expiration date.
@@ -5422,7 +5515,7 @@ The flag is set if the currently compiled format contains a `%e'.")
          (return (cadr entry))
       (return (apply 'create-image (cdr entry)))))))
 
-(defun org-format-agenda-item (extra txt &optional category tags dotime
+(defun org-agenda-format-item (extra txt &optional category tags dotime
                                     remove-re habitp)
   "Format TXT to be inserted into the agenda buffer.
 In particular, it adds the prefix and corresponding text properties.  EXTRA
@@ -5464,7 +5557,7 @@ Any match of REMOVE-RE will be removed from TXT."
           (time-of-day (and dotime (org-get-time-of-day ts)))
           stamp plain s0 s1 s2 rtn srp l
           duration thecategory)
-      (and (org-mode-p) buffer-file-name
+      (and (eq major-mode 'org-mode) buffer-file-name
           (add-to-list 'org-agenda-contributing-files buffer-file-name))
       (when (and dotime time-of-day)
        ;; Extract starting and ending time and move them to prefix
@@ -5512,7 +5605,7 @@ Any match of REMOVE-RE will be removed from TXT."
                     (concat (make-string (max (- 50 (length txt)) 1) ?\ )
                             (match-string 2 txt))
                     t t txt))))
-      (when (org-mode-p)
+      (when (eq major-mode 'org-mode)
        (setq effort
              (condition-case nil
                  (org-get-effort
@@ -5642,14 +5735,14 @@ The modified list may contain inherited tags, and tags matched by
       (while (setq time (pop gridtimes))
        (unless (and remove (member time have))
          (setq time (replace-regexp-in-string " " "0" (format "%04s" time)))
-         (push (org-format-agenda-item
+         (push (org-agenda-format-item
                 nil string "" nil
                 (concat (substring time 0 -2) ":" (substring time -2)))
                new)
          (put-text-property
           2 (length (car new)) 'face 'org-time-grid (car new))))
       (when (and todayp org-agenda-show-current-time-in-grid)
-       (push (org-format-agenda-item
+       (push (org-agenda-format-item
               nil
               org-agenda-current-time-string
               "" nil
@@ -5667,7 +5760,8 @@ The modified list may contain inherited tags, and tags matched by
 The resulting form is returned and stored in the variable
 `org-prefix-format-compiled'."
   (setq org-prefix-has-time nil org-prefix-has-tag nil
-       org-prefix-category-length nil  org-prefix-has-effort nil)
+       org-prefix-category-length nil
+       org-prefix-has-effort nil)
   (let ((s (cond
            ((stringp org-agenda-prefix-format)
             org-agenda-prefix-format)
@@ -5779,7 +5873,7 @@ could bind the variable in the options section of a custom command.")
 (defun org-agenda-highlight-todo (x)
   (let ((org-done-keywords org-done-keywords-for-agenda)
        (case-fold-search nil)
-        re)
+       re)
     (if (eq x 'line)
        (save-excursion
          (beginning-of-line 1)
@@ -5795,18 +5889,28 @@ could bind the variable in the options section of a custom command.")
       (let ((pl (text-property-any 0 (length x) 'org-heading t x)))
        (setq re (get-text-property 0 'org-todo-regexp x))
        (when (and re
+                  ;; Test `pl' because if there's no heading content,
+                  ;; there's no point matching to highlight.  Note
+                  ;; that if we didn't test `pl' first, and there
+                  ;; happened to be no keyword from `org-todo-regexp'
+                  ;; on this heading line, then the `equal' comparison
+                  ;; afterwards would spuriously succeed in the case
+                  ;; where `pl' is nil -- causing an args-out-of-range
+                  ;; error when we try to add text properties to text
+                  ;; that isn't there.
+                  pl
                   (equal (string-match (concat "\\(\\.*\\)" re "\\( +\\)")
-                                       x (or pl 0)) pl))
+                                       x pl) pl))
          (add-text-properties
           (or (match-end 1) (match-end 0)) (match-end 0)
           (list 'face (org-get-todo-face (match-string 2 x)))
-        x)
+          x)
          (when (match-end 1)
            (setq x (concat (substring x 0 (match-end 1))
                            (format org-agenda-todo-keyword-format
                                    (match-string 2 x))
-                         (org-add-props " " (text-properties-at 0 x))
-                         (substring x (match-end 3)))))))
+                           (org-add-props " " (text-properties-at 0 x))
+                           (substring x (match-end 3)))))))
       x)))
 
 (defsubst org-cmp-priority (a b)
@@ -5818,7 +5922,7 @@ could bind the variable in the options section of a custom command.")
          (t nil))))
 
 (defsubst org-cmp-effort (a b)
-  "Compare the priorities of string A and B."
+  "Compare the effort values of string A and B."
   (let* ((def (if org-sort-agenda-noeffort-is-high 32767 -1))
         (ea (or (get-text-property 1 'effort-minutes a) def))
         (eb (or (get-text-property 1 'effort-minutes b) def)))
@@ -6081,29 +6185,46 @@ in the agenda."
 When this is the global TODO list, a prefix argument will be interpreted."
   (interactive)
   (let* ((org-agenda-keep-modes t)
-        (filter org-agenda-filter)
-        (preset (get 'org-agenda-filter :preset-filter))
-        (org-agenda-filter-while-redo (or filter preset))
+        (tag-filter org-agenda-tag-filter)
+        (tag-preset (get 'org-agenda-tag-filter :preset-filter))
+        (cat-filter org-agenda-category-filter)
+        (cat-preset (get 'org-agenda-category-filter :preset-filter))
+        (org-agenda-tag-filter-while-redo (or tag-filter tag-preset))
         (cols org-agenda-columns-active)
         (line (org-current-line))
         (window-line (- line (org-current-line (window-start))))
         (lprops (get 'org-agenda-redo-command 'org-lprops)))
-    (put 'org-agenda-filter :preset-filter nil)
+    (put 'org-agenda-tag-filter :preset-filter nil)
+    (put 'org-agenda-category-filter :preset-filter nil)
     (and cols (org-columns-quit))
     (message "Rebuilding agenda buffer...")
     (org-let lprops '(eval org-agenda-redo-command))
     (setq org-agenda-undo-list nil
          org-agenda-pending-undo-list nil)
     (message "Rebuilding agenda buffer...done")
-    (put 'org-agenda-filter :preset-filter preset)
-    (and (or filter preset) (org-agenda-filter-apply filter))
+    (put 'org-agenda-tag-filter :preset-filter tag-preset)
+    (put 'org-agenda-category-filter :preset-filter cat-preset)
+    (and (or tag-filter tag-preset) (org-agenda-filter-apply tag-filter 'tag))
+    (and (or cat-filter cat-preset) (org-agenda-filter-apply cat-filter 'category))
     (and cols (org-called-interactively-p 'any) (org-agenda-columns))
     (org-goto-line line)
     (recenter window-line)))
 
-
 (defvar org-global-tags-completion-table nil)
 (defvar org-agenda-filter-form nil)
+(defvar org-agenda-filtered-by-category nil)
+
+(defun org-agenda-filter-by-category (strip)
+  "Keep only those lines in the agenda buffer that have a specific category.
+The category is that of the current line."
+  (interactive "P")
+  (if org-agenda-filtered-by-category
+      (org-agenda-filter-show-all-cat)
+    (let ((cat (org-no-properties (get-text-property (point) 'org-category))))
+      (if cat (org-agenda-filter-apply
+              (list (concat (if strip "-" "+") cat)) 'category)
+       (error "No category at point")))))
+
 (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.
@@ -6127,21 +6248,21 @@ to switch to narrowing."
         (effort-op org-agenda-filter-effort-default-operator)
         (effort-prompt "")
         (inhibit-read-only t)
-        (current org-agenda-filter)
+        (current org-agenda-tag-filter)
         maybe-refresh a n tag)
     (unless char
       (message
        "%s by tag [%s ], [TAB], %s[/]:off, [+-]:narrow, [>=<?]:effort: "
        (if narrow "Narrow" "Filter") tag-chars
        (if org-agenda-auto-exclude-function "[RET], " ""))
-      (setq char (read-char)))
+      (setq char (read-char-exclusive)))
     (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)))
+      (setq char (read-char-exclusive)))
     (when (member char '(?< ?> ?= ??))
       ;; An effort operator
       (setq effort-op (char-to-string char))
@@ -6154,7 +6275,7 @@ to switch to narrowing."
                     (if (= i 9) "0" (int-to-string (1+ i)))
                     "]" (nth i efforts))))
        (message "Effort%s: %s " effort-op effort-prompt)
-       (setq char (read-char))
+       (setq char (read-char-exclusive))
        (when (or (< char ?0) (> char ?9))
          (error "Need 1-9,0 to select effort" ))))
     (when (equal char ?\t)
@@ -6166,20 +6287,26 @@ to switch to narrowing."
                   "Tag: " org-global-tags-completion-table))))
     (cond
      ((equal char ?\r)
-      (org-agenda-filter-by-tag-show-all)
+      (org-agenda-filter-show-all-tag)
       (when org-agenda-auto-exclude-function
-       (setq org-agenda-filter '())
+       (setq org-agenda-tag-filter '())
        (dolist (tag (org-agenda-get-represented-tags))
          (let ((modifier (funcall org-agenda-auto-exclude-function tag)))
            (if modifier
-               (push modifier org-agenda-filter))))
-       (if (not (null org-agenda-filter))
-           (org-agenda-filter-apply org-agenda-filter)))
+               (push modifier org-agenda-tag-filter))))
+       (if (not (null org-agenda-tag-filter))
+           (org-agenda-filter-apply org-agenda-tag-filter 'tag)))
       (setq maybe-refresh t))
      ((equal char ?/)
-      (org-agenda-filter-by-tag-show-all)
-      (when (get 'org-agenda-filter :preset-filter)
-       (org-agenda-filter-apply org-agenda-filter))
+      (org-agenda-filter-show-all-tag)
+      (when (get 'org-agenda-tag-filter :preset-filter)
+       (org-agenda-filter-apply org-agenda-tag-filter 'tag))
+      (setq maybe-refresh t))
+     ((equal char ?. )
+      (setq org-agenda-tag-filter
+           (mapcar (lambda(tag) (concat "+" tag))
+                   (org-get-at-bol 'tags)))
+      (org-agenda-filter-apply org-agenda-tag-filter 'tag)
       (setq maybe-refresh t))
      ((or (equal char ?\ )
          (setq a (rassoc char alist))
@@ -6191,12 +6318,12 @@ to switch to narrowing."
               (setq tag "?eff")
               a (cons tag nil))
          (and tag (setq a (cons tag nil))))
-      (org-agenda-filter-by-tag-show-all)
+      (org-agenda-filter-show-all-tag)
       (setq tag (car a))
-      (setq org-agenda-filter
+      (setq org-agenda-tag-filter
            (cons (concat (if strip "-" "+") tag)
                  (if narrow current nil)))
-      (org-agenda-filter-apply org-agenda-filter)
+      (org-agenda-filter-apply org-agenda-tag-filter 'tag)
       (setq maybe-refresh t))
      (t (error "Invalid tag selection character %c" char)))
     (when (and maybe-refresh
@@ -6215,15 +6342,17 @@ to switch to narrowing."
     tags))
 
 (defun org-agenda-filter-by-tag-refine (strip &optional char)
-  "Refine the current filter.  See `org-agenda-filter-by-tag."
+  "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 ()
-  "Create the form that tests a line for the agenda filter."
+  "Create the form that tests a line for agenda filter."
   (let (f f1)
-    (dolist (x (append (get 'org-agenda-filter :preset-filter)
-                      org-agenda-filter))
+    ;; first compute the tag-filter matcher
+    (dolist (x (delete-dups
+               (append (get 'org-agenda-tag-filter
+                            :preset-filter) org-agenda-tag-filter)))
       (if (member x '("-" "+"))
          (setq f1 (if (equal x "-") 'tags '(not tags)))
        (if (string-match "[<=>?]" x)
@@ -6232,6 +6361,14 @@ to switch to narrowing."
        (if (equal (string-to-char x) ?-)
            (setq f1 (list 'not f1))))
       (push f1 f))
+    ;; then compute the category-filter matcher
+    (dolist (x (delete-dups
+               (append (get 'org-agenda-category-filter
+                            :preset-filter) org-agenda-category-filter)))
+      (if (equal "-" (substring x 0 1))
+         (setq f1 (list 'not (list 'equal (substring x 1) 'cat)))
+       (setq f1 (list 'equal (substring x 1) 'cat)))
+      (push f1 f))
     (cons 'and (nreverse f))))
 
 (defun org-agenda-filter-effort-form (e)
@@ -6256,49 +6393,67 @@ If the line does not have an effort defined, return nil."
       (funcall op (or eff (if org-sort-agenda-noeffort-is-high 32767 0))
               value))))
 
-(defun org-agenda-filter-apply (filter)
+(defun org-agenda-filter-apply (filter type)
   "Set FILTER as the new agenda filter and apply it."
-  (let (tags)
-    (setq org-agenda-filter filter
-         org-agenda-filter-form (org-agenda-filter-make-matcher))
+  (let (tags cat)
+    (if (eq type 'tag)
+       (setq org-agenda-tag-filter filter)
+      (setq org-agenda-category-filter filter))
+    (setq org-agenda-filter-form (org-agenda-filter-make-matcher))
+    (if (and (eq type 'category)
+            (not (equal (substring (car filter) 0 1) "-")))
+       ;; Only set `org-agenda-filtered-by-category' to t
+       ;; when a unique category is used as the filter
+       (setq org-agenda-filtered-by-category t))
     (org-agenda-set-mode-name)
     (save-excursion
       (goto-char (point-min))
       (while (not (eobp))
        (if (org-get-at-bol 'org-marker)
            (progn
-             (setq tags (org-get-at-bol 'tags)) ; used in eval
+             (setq tags (org-get-at-bol 'tags) ; used in eval
+                   cat (get-text-property (point) 'org-category))
              (if (not (eval org-agenda-filter-form))
-                 (org-agenda-filter-by-tag-hide-line))
+                 (org-agenda-filter-hide-line type))
              (beginning-of-line 2))
          (beginning-of-line 2))))
     (if (get-char-property (point) 'invisible)
-       (org-agenda-previous-line))))
+       (ignore-errors (org-agenda-previous-line)))))
 
-(defun org-agenda-filter-by-tag-hide-line ()
+(defun org-agenda-filter-hide-line (type)
   (let (ov)
     (setq ov (make-overlay (max (point-min) (1- (point-at-bol)))
                               (point-at-eol)))
     (overlay-put ov 'invisible t)
-    (overlay-put ov 'type 'tags-filter)
-    (push ov org-agenda-filter-overlays)))
+    (overlay-put ov 'type type)
+    (if (eq type 'tag)
+       (push ov org-agenda-tag-filter-overlays)
+      (push ov org-agenda-cat-filter-overlays))))
 
 (defun org-agenda-fix-tags-filter-overlays-at (&optional pos)
   (setq pos (or pos (point)))
   (save-excursion
     (dolist (ov (overlays-at pos))
       (when (and (overlay-get ov 'invisible)
-                (eq (overlay-get ov 'type) 'tags-filter))
+                (eq (overlay-get ov 'type) 'tag))
        (goto-char pos)
        (if (< (overlay-start ov) (point-at-eol))
            (move-overlay ov (point-at-eol)
                              (overlay-end ov)))))))
 
-(defun org-agenda-filter-by-tag-show-all ()
-  (mapc 'delete-overlay org-agenda-filter-overlays)
-  (setq org-agenda-filter-overlays nil)
-  (setq org-agenda-filter nil)
-  (setq org-agenda-filter-form nil)
+(defun org-agenda-filter-show-all-tag nil
+  (mapc 'delete-overlay org-agenda-tag-filter-overlays)
+  (setq org-agenda-tag-filter-overlays nil
+       org-agenda-tag-filter nil
+       org-agenda-filter-form nil)
+  (org-agenda-set-mode-name))
+
+(defun org-agenda-filter-show-all-cat nil
+  (mapc 'delete-overlay org-agenda-cat-filter-overlays)
+  (setq org-agenda-cat-filter-overlays nil
+       org-agenda-filtered-by-category nil
+       org-agenda-category-filter nil
+       org-agenda-filter-form nil)
   (org-agenda-set-mode-name))
 
 (defun org-agenda-manipulate-query-add ()
@@ -6592,8 +6747,7 @@ so that the date SD will be in that range."
   (interactive)
   (setq org-agenda-follow-mode (not org-agenda-follow-mode))
   (org-agenda-set-mode-name)
-  (if (and org-agenda-follow-mode (org-get-at-bol 'org-marker))
-      (org-agenda-show))
+  (org-agenda-do-context-action)
   (message "Follow mode is %s"
           (if org-agenda-follow-mode "on" "off")))
 
@@ -6714,13 +6868,33 @@ When called with a prefix argument, include all archive files as well."
               ((eq org-agenda-show-log 'clockcheck) " ClkCk")
               (org-agenda-show-log " Log")
               (t ""))
-             (if (or org-agenda-filter (get 'org-agenda-filter
+             (if (or org-agenda-category-filter (get 'org-agenda-category-filter
+                                                     :preset-filter))
+                 '(:eval (org-propertize
+                          (concat " <"
+                                  (mapconcat
+                                   'identity
+                                   (append
+                                    (get 'org-agenda-category-filter :preset-filter)
+                                    org-agenda-category-filter)
+                                   "")
+                                  ">")
+                          'face 'org-agenda-filter-category
+                          'help-echo "Category used in filtering"))
+               "")
+             (if (or org-agenda-tag-filter (get 'org-agenda-tag-filter
                                             :preset-filter))
-                 (concat " {" (mapconcat
-                               'identity
-                               (append (get 'org-agenda-filter
-                                            :preset-filter)
-                                       org-agenda-filter) "") "}")
+                 '(:eval (org-propertize
+                          (concat " {"
+                                  (mapconcat
+                                   'identity
+                                   (append
+                                    (get 'org-agenda-tag-filter :preset-filter)
+                                    org-agenda-tag-filter)
+                                   "")
+                                  "}")
+                          'face 'org-agenda-filter-tags
+                          'help-echo "Tags used in filtering"))
                "")
              (if org-agenda-archives-mode
                  (if (eq org-agenda-archives-mode t)
@@ -6754,11 +6928,13 @@ When called with a prefix argument, include all archive files as well."
 (defun org-agenda-do-context-action ()
   "Show outline path and, maybe, follow mode window."
   (let ((m (org-get-at-bol 'org-marker)))
-    (if (and org-agenda-follow-mode m)
-       (org-agenda-show))
-    (if (and m org-agenda-show-outline-path)
-       (org-with-point-at m
-         (org-display-outline-path t)))))
+    (when (and (markerp m) (marker-buffer m))
+      (and org-agenda-follow-mode
+          (if org-agenda-follow-indirect
+              (org-agenda-tree-to-indirect-buffer)
+            (org-agenda-show)))
+      (and org-agenda-show-outline-path
+          (org-with-point-at m (org-display-outline-path t))))))
 
 (defun org-agenda-show-priority ()
   "Show the priority of the current item.
@@ -6788,7 +6964,7 @@ and by additional input from the age of a schedules or deadline entry."
     (widen)
     (push-mark)
     (goto-char pos)
-    (when (org-mode-p)
+    (when (eq major-mode 'org-mode)
       (org-show-context 'agenda)
       (save-excursion
        (and (outline-next-heading)
@@ -6817,7 +6993,7 @@ Point is in the buffer where the item originated.")
      (with-current-buffer buffer
        (save-excursion
         (goto-char pos)
-        (if (and (org-mode-p) (not (member type '("sexp"))))
+        (if (and (eq major-mode 'org-mode) (not (member type '("sexp"))))
             (setq dbeg (progn (org-back-to-heading t) (point))
                   dend (org-end-of-subtree t t))
           (setq dbeg (point-at-bol)
@@ -6869,7 +7045,7 @@ Point is in the buffer where the item originated.")
         (pos (marker-position marker)))
     (org-with-remote-undo buffer
       (with-current-buffer buffer
-       (if (org-mode-p)
+       (if (eq major-mode 'org-mode)
            (if (and confirm
                     (not (y-or-n-p "Archive this subtree or entry? ")))
                (error "Abort")
@@ -6970,11 +7146,11 @@ at the text of the entry itself."
                       (org-agenda-error)))
           (buffer (marker-buffer marker))
           (pos (marker-position marker)))
-      (switch-to-buffer buffer)
+      (org-pop-to-buffer-same-window buffer)
       (and delete-other-windows (delete-other-windows))
       (widen)
       (goto-char pos)
-      (when (org-mode-p)
+      (when (eq major-mode 'org-mode)
        (org-show-context 'agenda)
        (save-excursion
          (and (outline-next-heading)
@@ -7138,6 +7314,24 @@ With numerical prefix arg ARG, go up to this level and then take that tree.
 With a \\[universal-argument] prefix, make a separate frame for this tree (i.e. don't
 use the dedicated frame)."
   (interactive)
+  (if (and current-prefix-arg (listp current-prefix-arg))
+      (org-agenda-do-tree-to-indirect-buffer)
+    (let ((agenda-window (selected-window))
+          (indirect-window
+          (and org-last-indirect-buffer
+               (get-buffer-window org-last-indirect-buffer))))
+      (save-window-excursion (org-agenda-do-tree-to-indirect-buffer))
+      (unwind-protect
+          (progn
+            (unless (and indirect-window (window-live-p indirect-window))
+              (setq indirect-window (split-window agenda-window)))
+            (select-window indirect-window)
+            (switch-to-buffer org-last-indirect-buffer :norecord)
+            (fit-window-to-buffer indirect-window))
+        (select-window (get-buffer-window org-agenda-buffer-name))))))
+
+(defun org-agenda-do-tree-to-indirect-buffer ()
+  "Same as `org-agenda-tree-to-indirect-buffer' without saving window."
   (org-agenda-check-no-diary)
   (let* ((marker (or (org-get-at-bol 'org-marker)
                     (org-agenda-error)))
@@ -7227,7 +7421,7 @@ the same tree node, and the headline of the tree node in the Org-mode file."
                                            &optional fixface just-this)
   "Change all lines in the agenda buffer which match HDMARKER.
 The new content of the line will be NEWHEAD (as modified by
-`org-format-agenda-item').  HDMARKER is checked with
+`org-agenda-format-item').  HDMARKER is checked with
 `equal' against all `org-hd-marker' text properties in the file.
 If FIXFACE is non-nil, the face of each item is modified according to
 the new TODO state.
@@ -7260,7 +7454,7 @@ If FORCE-TAGS is non nil, the car of it returns the new tags."
                    (save-excursion
                      (save-restriction
                        (widen)
-                       (org-format-agenda-item (org-get-at-bol 'extra)
+                       (org-agenda-format-item (org-get-at-bol 'extra)
                                                newhead cat tags dotime)))))
                pl (text-property-any (point-at-bol) (point-at-eol) 'org-heading t)
                undone-face (org-get-at-bol 'undone-face)
@@ -7494,15 +7688,33 @@ the same tree node, and the headline of the tree node in the Org-mode file."
   (let* ((marker (or (org-get-at-bol 'org-marker)
                     (org-agenda-error)))
         (buffer (marker-buffer marker))
-        (pos (marker-position marker)))
+        (pos (marker-position marker))
+        cdate today)
     (org-with-remote-undo buffer
-     (with-current-buffer buffer
-       (widen)
-       (goto-char pos)
-       (if (not (org-at-timestamp-p))
-          (error "Cannot find time stamp"))
-       (org-timestamp-change arg (or what 'day)))
-     (org-agenda-show-new-time marker org-last-changed-timestamp))
+      (with-current-buffer buffer
+       (widen)
+       (goto-char pos)
+       (if (not (org-at-timestamp-p))
+           (error "Cannot find time stamp"))
+       (when (and org-agenda-move-date-from-past-immediately-to-today
+                  (equal arg 1)
+                  (or (not what) (eq what 'day))
+                  (not (save-match-data (org-at-date-range-p))))
+         (setq cdate (org-parse-time-string (match-string 0) 'nodefault)
+               cdate (calendar-absolute-from-gregorian
+                      (list (nth 4 cdate) (nth 3 cdate) (nth 5 cdate)))
+               today (org-today))
+         (if (> today cdate)
+             ;; immediately shift to today
+             (setq arg (- today cdate))))
+       (org-timestamp-change arg (or what 'day))
+       (when (and (org-at-date-range-p)
+                  (re-search-backward org-tr-regexp-both (point-at-bol)))
+         (let ((end org-last-changed-timestamp))
+           (org-timestamp-change arg (or what 'day))
+           (setq org-last-changed-timestamp
+                 (concat org-last-changed-timestamp "--" end)))))
+      (org-agenda-show-new-time marker org-last-changed-timestamp))
     (message "Time stamp changed to %s" org-last-changed-timestamp)))
 
 (defun org-agenda-date-earlier (arg &optional what)
@@ -7811,6 +8023,7 @@ top-level    as top-level entries at the end of the file."
 (defcustom org-agenda-insert-diary-extract-time nil
   "Non-nil means extract any time specification from the diary entry."
   :group 'org-agenda
+  :version "24.1"
   :type 'boolean)
 
 (defun org-agenda-add-entry-to-org-agenda-diary-file (type text &optional d1 d2)
@@ -7827,7 +8040,7 @@ the resulting entry will not be shown.  When TEXT is empty, switch to
      ((eq type 'anniversary)
       (or (re-search-forward "^*[ \t]+Anniversaries" nil t)
        (progn
-         (or (org-on-heading-p t)
+         (or (org-at-heading-p t)
              (progn
                (outline-next-heading)
                (insert "* Anniversaries\n\n")
@@ -7843,10 +8056,10 @@ the resulting entry will not be shown.  When TEXT is empty, switch to
            (org-agenda-time-leading-zero t)
            fmt time time2)
        (if org-agenda-insert-diary-extract-time
-           ;; Use org-format-agenda-item to parse text for a time-range and
+           ;; Use org-agenda-format-item to parse text for a time-range and
            ;; remove it.  FIXME: This is a hack, we should refactor
            ;; that function to make time extraction available separately
-           (setq fmt (org-format-agenda-item nil text nil nil t)
+           (setq fmt (org-agenda-format-item nil text nil nil t)
                  time (get-text-property 0 'time fmt)
                  time2 (if (> (length time) 0)
                            ;; split-string removes trailing ...... if
@@ -8081,7 +8294,7 @@ This is a command that has to be installed in `calendar-mode-map'."
 (defun org-agenda-bulk-mark (&optional arg)
   "Mark the entry at point for future bulk action."
   (interactive "p")
-  (dotimes (i (max arg 1))
+  (dotimes (i (or arg 1))
     (unless (org-get-at-bol 'org-agenda-diary-link)
       (let* ((m (org-get-at-bol 'org-hd-marker))
             ov)
@@ -8102,13 +8315,13 @@ This is a command that has to be installed in `calendar-mode-map'."
 (defun org-agenda-bulk-mark-regexp (regexp)
   "Mark entries match REGEXP."
   (interactive "sMark entries matching regexp: ")
-  (let (entries-marked)
+  (let ((entries-marked 0))
     (save-excursion
       (goto-char (point-min))
       (goto-char (next-single-property-change (point) 'txt))
       (while (re-search-forward regexp nil t)
        (when (string-match regexp (get-text-property (point) 'txt))
-         (setq entries-marked (+ entries-marked 1))
+         (setq entries-marked (1+ entries-marked))
          (call-interactively 'org-agenda-bulk-mark))))
     (if (not entries-marked)
        (message "No entry matching this regexp."))))
@@ -8299,7 +8512,8 @@ The prefix arg is passed through to the command if possible."
          (progn (message "Skipping removed entry at %s" e)
                 (setq cntskip (1+ cntskip)))
        (goto-char pos)
-       (eval cmd)
+       (let (org-loop-over-headlines-in-active-region)
+         (eval cmd))
        (setq org-agenda-bulk-marked-entries
              (delete e org-agenda-bulk-marked-entries))
        (setq cnt (1+ cnt))))
@@ -8331,7 +8545,7 @@ tag and (if present) the flagging note."
          (org-agenda-remove-flag hdmarker)
          (let ((win (get-buffer-window "*Flagging Note*")))
            (and win (delete-window win)))
-         (message "Entry unflaged"))
+         (message "Entry unflagged"))
       (setq note (org-entry-get hdmarker "THEFLAGGINGNOTE"))
       (unless note
        (error "No flagging note"))
@@ -8354,7 +8568,7 @@ tag and (if present) the flagging note."
       (org-entry-delete nil "THEFLAGGINGNOTE")
       (setq newhead (org-get-heading)))
     (org-agenda-change-all-lines newhead marker)
-    (message "Entry unflaged")))
+    (message "Entry unflagged")))
 
 (defun org-agenda-get-any-marker (&optional pos)
   (or (get-text-property (or pos (point-at-bol)) 'org-hd-marker)
@@ -8365,7 +8579,7 @@ tag and (if present) the flagging note."
 (defvar appt-time-msg-list)
 
 ;;;###autoload
-(defun org-agenda-to-appt (&optional refresh filter)
+(defun org-agenda-to-appt (&optional refresh filter &rest args)
   "Activate appointments found in `org-agenda-files'.
 With a \\[universal-argument] prefix, refresh the list of
 appointments.
@@ -8376,6 +8590,10 @@ expression, and filter out entries that don't match it.
 If FILTER is a string, use this string as a regular expression
 for filtering entries out.
 
+If FILTER is a function, filter out entries against which
+calling the function returns nil.  This function takes one
+argument: an entry from `org-agenda-get-day-entries'.
+
 FILTER can also be an alist with the car of each cell being
 either 'headline or 'category.  For example:
 
@@ -8383,12 +8601,18 @@ either 'headline or 'category.  For example:
     (category \"Work\"))
 
 will only add headlines containing IMPORTANT or headlines
-belonging to the \"Work\" category."
+belonging to the \"Work\" category.
+
+ARGS are symbols indicating what kind of entries to consider.
+By default `org-agenda-to-appt' will use :deadline, :scheduled
+and :timestamp entries.  See the docstring of `org-diary' for
+details and examples."
   (interactive "P")
   (if refresh (setq appt-time-msg-list nil))
   (if (eq filter t)
       (setq filter (read-from-minibuffer "Regexp filter: ")))
   (let* ((cnt 0) ; count added events
+        (scope (or args '(:deadline :scheduled :timestamp)))
         (org-agenda-new-buffers nil)
         (org-deadline-warning-days 0)
         ;; Do not use `org-today' here because appt only takes
@@ -8402,10 +8626,10 @@ belonging to the \"Work\" category."
     (org-prepare-agenda-buffers files)
     (while (setq file (pop files))
       (setq entries
-           (append entries
-                   (org-agenda-get-day-entries
-                    file today :timestamp :scheduled :deadline))))
-    (setq entries (delq nil entries))
+           (delq nil
+                 (append entries
+                         (apply 'org-agenda-get-day-entries
+                                file today scope)))))
     ;; Map thru entries and find if we should filter them out
     (mapc
      (lambda(x)
@@ -8414,11 +8638,14 @@ belonging to the \"Work\" category."
              (tod (get-text-property 1 'time-of-day x))
              (ok (or (null filter)
                      (and (stringp filter) (string-match filter evt))
+                     (and (functionp filter) (funcall filter x))
                      (and (listp filter)
-                          (or (string-match
-                               (cadr (assoc 'category filter)) cat)
-                              (string-match
-                               (cadr (assoc 'headline filter)) evt))))))
+                          (let ((cat-filter (cadr (assoc 'category filter)))
+                                (evt-filter (cadr (assoc 'headline filter))))
+                            (or (and (stringp cat-filter)
+                                     (string-match cat-filter cat))
+                                (and (stringp evt-filter)
+                                     (string-match evt-filter evt))))))))
         ;; FIXME: Shall we remove text-properties for the appt text?
         ;; (setq evt (set-text-properties 0 (length evt) nil evt))
         (when (and ok tod)
@@ -8441,8 +8668,14 @@ belonging to the \"Work\" category."
                date)))
     (eq date today)))
 
-(provide 'org-agenda)
-
+(defun org-agenda-todo-yesterday (&optional arg)
+  "Like `org-agenda-todo' but the time of change will be 23:59 of yesterday"
+  (interactive "P")
+  (let* ((hour (third (decode-time
+                       (org-current-time))))
+         (org-extend-today-until (1+ hour)))
+    (org-agenda-todo arg)))
 
+(provide 'org-agenda)
 
 ;;; org-agenda.el ends here