Implement Summaries for Column View in agenda.

This commit is contained in:
Carsten Dominik 2008-04-15 11:02:10 +02:00
parent 157a29d052
commit b0ba028394
5 changed files with 214 additions and 23 deletions

View File

@ -1,5 +1,14 @@
2008-04-15 Carsten Dominik <dominik@science.uva.nl>
* lisp/org-agenda.el (org-agenda-columns-show-summaries)
(org-agenda-columns-compute-summary-properties): New options.
* lisp/org-colview.el (org-agenda-colview-summarize)
(org-agenda-colview-compute): New functions.
(org-agenda-columns): Call `org-agenda-colview-summarize'.
* doc/org.texi (Agenda column view): New section.
* lisp/org.el (org-tbl-menu): Protect the use of variables that
are only available when org-table.el gets loaded.
(org-read-agenda-file-list): Error if `org-agenda-files' is a

View File

@ -238,6 +238,7 @@ Agenda Views
* Presentation and sorting:: How agenda items are prepared for display
* Agenda commands:: Remote editing of Org trees
* Custom agenda views:: Defining special searches and views
* Agenda column view:: Using column view for collected entries
The built-in agenda views
@ -4896,6 +4897,7 @@ window configuration is restored when the agenda exits:
* Presentation and sorting:: How agenda items are prepared for display
* Agenda commands:: Remote editing of Org trees
* Custom agenda views:: Defining special searches and views
* Agenda column view:: Using column view for collected entries
@end menu
@node Agenda files, Agenda dispatcher, Agenda Views, Agenda Views
@ -5780,7 +5782,7 @@ visit org files will not be removed.
@end table
@node Custom agenda views, , Agenda commands, Agenda Views
@node Custom agenda views, Agenda column view, Agenda commands, Agenda Views
@section Custom agenda views
@cindex custom agenda views
@cindex agenda views, custom
@ -6203,6 +6205,60 @@ foreach $line (split(/\n/,$agenda)) @{
@end group
@end example
@node Agenda column view, , Custom agenda views, Agenda Views
@section Using column view in the agenda
@cindex column view, in agenda
@cindex agenda, column view
Column view (@pxref{Column view}) is normally used to view and edit
properties embedded in the hierarchical structure of an Org file. It can be
quite useful to use column view also from the agenda, where entries are
collected by certain criteria.
@table @kbd
@kindex C-c C-x C-c
@item C-c C-x C-c
Turn on column view in the agenda.
@end table
To understand how to use this properly, it is important to realize that the
entries in the agenda are no longer in their proper outline environment.
This causes the following issues:
@enumerate
@item
Org needs to make a decision which @code{COLUMNS} format to use. Since the
entries in the agenda are collected from different files, and different files
may have different @code{COLUMNS} formats, this is a non-trivial problem.
Org first checks if the variable @code{org-overriding-columns-format} is
currently set, and if yes takes the format from there. Otherwise it takes
the format associated with the first item in the agenda, or, if that item
does not have a specific format (defined in a property, or in it's file), it
uses @code{org-columns-default-format}.
@item
If any of the columns has a summary type defined (@pxref{Column attributes}),
turning on column view in the agenda will visit all relevant agenda files and
make sure that the computations of this property are up to date. This is
also true for the special @code{CLOCKSUM} property. Org will then sum the
values displayed in the agenda. In the daily/weekly agenda, the sums will
cover a single day, in all other views they cover the entire block. It is
vital to realize that the agenda may show the same entry @emph{twice} (for
example as scheduled and as a deadline), and it may show two entries from the
same hierarchy (for example a @emph{parent} and it's @emph{child}). In these
cases, the summation in the agenda will lead to incorrect results because
some values will count double.
@item
When the column view in the agenda shows the @code{CLOCKSUM}, that is always
the entire clocked time for this item. So even in the daily/weekly agenda,
the clocksum listed in column view may originate from times outside the
current view. This has the advantage that you can compare these values with
a column listing the planned total effort for a task - one of the major
applications for column view in the agenda. If you want information about
clocked time in the displayed period use clock table mode (press @kbd{R} in
the agenda).
@end enumerate
@node Embedded LaTeX, Exporting, Agenda Views, Top
@chapter Embedded LaTeX
@cindex @TeX{} interpretation

View File

@ -927,6 +927,28 @@ a names face, or a list like `(:background \"Red\")'."
(sexp :tag "face")))))
(defgroup org-agenda-column-view nil
"Options concerning column view in the agenda."
:tag "Org Agenda Column View"
:group 'org-agenda)
(defcustom org-agenda-columns-show-summaries t
"Non-nil means, show summaries for columns displayed in the agenda view."
:group 'org-agenda-column-view
:type 'boolean)
(defcustom org-agenda-columns-compute-summary-properties t
"Non-nil means, recompute all summary properties before column view.
When column view in the agenda is listing properties that have a summary
operator, it can go to all relevant buffers and recompute the summaries
there. This can mean overhead for the agenda column view, but is necessary
to have thing up to date.
As a special case, a CLOCKSUM property also makes sure that the clock
computations are current."
:group 'org-agenda-column-view
:type 'boolean)
(eval-when-compile
(require 'cl))
(require 'org)
@ -1858,6 +1880,7 @@ higher priority settings."
(defvar org-agenda-multi nil) ; dynammically scoped
(defvar org-agenda-buffer-name "*Org Agenda*")
(defvar org-pre-agenda-window-conf nil)
(defvar org-agenda-columns-active nil)
(defvar org-agenda-name nil)
(defun org-prepare-agenda (&optional name)
(setq org-todo-keywords-for-agenda nil)
@ -1870,6 +1893,8 @@ higher priority settings."
(insert "\n" (make-string (window-width) ?=) "\n"))
(narrow-to-region (point) (point-max)))
(org-agenda-reset-markers)
(setq org-agenda-contributing-files nil)
(setq org-agenda-columns-active nil)
(org-prepare-agenda-buffers (org-agenda-files))
(setq org-todo-keywords-for-agenda
(org-uniquify org-todo-keywords-for-agenda))
@ -1891,7 +1916,7 @@ higher priority settings."
(delete-other-windows)
(org-switch-to-buffer-other-window abuf))))
(setq buffer-read-only nil)
(erase-buffer)
(let ((inhibit-read-only t)) (erase-buffer))
(org-agenda-mode)
(and name (not org-agenda-name)
(org-set-local 'org-agenda-name name)))
@ -3503,6 +3528,8 @@ Any match of REMOVE-RE will be removed from TXT."
(ts (if dotime (concat (if (stringp dotime) dotime "") txt)))
(time-of-day (and dotime (org-get-time-of-day ts)))
stamp plain s0 s1 s2 rtn srp)
(and (org-mode-p) buffer-file-name
(add-to-list 'org-agenda-contributing-files buffer-file-name))
(when (and dotime time-of-day org-prefix-has-time)
;; Extract starting and ending time and move them to prefix
(when (or (setq stamp (string-match org-stamp-time-of-day-regexp ts))
@ -3854,17 +3881,21 @@ If ERROR is non-nil, throw an error, otherwise just return nil."
(defun org-agenda-quit ()
"Exit agenda by removing the window or the buffer."
(interactive)
(let ((buf (current-buffer)))
(if (not (one-window-p)) (delete-window))
(kill-buffer buf)
(org-agenda-reset-markers)
(org-columns-remove-overlays))
;; Maybe restore the pre-agenda window configuration.
(and org-agenda-restore-windows-after-quit
(not (eq org-agenda-window-setup 'other-frame))
org-pre-agenda-window-conf
(set-window-configuration org-pre-agenda-window-conf)))
(if org-agenda-columns-active
(progn
(setq org-agenda-columns-active nil)
(org-columns-quit))
(let ((buf (current-buffer)))
(if (not (one-window-p)) (delete-window))
(kill-buffer buf)
(org-agenda-reset-markers)
(org-columns-remove-overlays))
;; Maybe restore the pre-agenda window configuration.
(and org-agenda-restore-windows-after-quit
(not (eq org-agenda-window-setup 'other-frame))
org-pre-agenda-window-conf
(set-window-configuration org-pre-agenda-window-conf))))
(defun org-agenda-exit ()
"Exit agenda by removing the window or the buffer.
Also kill all Org-mode buffers which have been loaded by `org-agenda'.
@ -3893,14 +3924,17 @@ So this is just a shortcut for `\\[org-agenda]', available in the agenda."
When this is the global TODO list, a prefix argument will be interpreted."
(interactive)
(let* ((org-agenda-keep-modes t)
(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)))
(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")
(and cols (interactive-p) (org-agenda-columns))
(goto-line line)
(recenter window-line)))
@ -4965,7 +4999,6 @@ This is a command that has to be installed in `calendar-mode-map'."
(if (fboundp 'fit-window-to-buffer)
(fit-window-to-buffer (get-buffer-window "*Dates*")))))
;;; Appointment reminders
(defvar appt-time-msg-list)

View File

@ -41,13 +41,17 @@
(defvar org-columns-current-fmt nil
"Local variable, holds the currently active column format.")
(make-variable-buffer-local 'org-columns-current-fmt)
(defvar org-columns-current-fmt-compiled nil
"Local variable, holds the currently active column format.
This is the compiled version of the format.")
(make-variable-buffer-local 'org-columns-current-fmt-compiled)
(defvar org-columns-current-widths nil
"Loval variable, holds the currently widths of fields.")
(make-variable-buffer-local 'org-columns-current-widths)
(defvar org-columns-current-maxwidths nil
"Loval variable, holds the currently active maximum column widths.")
(make-variable-buffer-local 'org-columns-current-maxwidths)
(defvar org-columns-begin-marker (make-marker)
"Points to the position where last a column creation command was called.")
(defvar org-columns-top-level-marker (make-marker)
@ -132,8 +136,13 @@ This is the compiled version of the format.")
(and (looking-at "\\(\\**\\)\\(\\* \\)")
(org-get-level-face 2))))
(color (list :foreground
(face-attribute (or level-face 'default) :foreground)))
props pom property ass width f string ov column val modval)
(face-attribute
(or level-face
(and (eq major-mode 'org-agenda-mode)
(get-text-property (point-at-bol) 'face))
'default) :foreground)))
(face (list color 'org-column))
pom property ass width f string ov column val modval)
;; Check if the entry is in another buffer.
(unless props
(if (eq major-mode 'org-agenda-mode)
@ -162,9 +171,7 @@ This is the compiled version of the format.")
string (format f (or modval val)))
;; Create the overlay
(org-unmodified
(setq ov (org-columns-new-overlay
beg (setq beg (1+ beg)) string
(list color 'org-column)))
(setq ov (org-columns-new-overlay beg (setq beg (1+ beg)) string face))
(org-overlay-put ov 'keymap org-columns-map)
(org-overlay-put ov 'org-columns-key property)
(org-overlay-put ov 'org-columns-value (cdr ass))
@ -397,17 +404,22 @@ Where possible, use the standard interface for changing this line."
(defun org-columns-edit-allowed ()
"Edit the list of allowed values for the current property."
(interactive)
(let* ((key (get-char-property (point) 'org-columns-key))
(let* ((pom (or (get-text-property (point-at-bol) 'org-marker)
(get-text-property (point-at-bol) 'org-hd-marker)
(point)))
(key (get-char-property (point) 'org-columns-key))
(key1 (concat key "_ALL"))
(allowed (org-entry-get (point) key1 t))
(allowed (org-entry-get pom key1 t))
nval)
;; FIXME: Cover editing TODO, TAGS etc in-buffer settings.????
;; FIXME: Write back to #+PROPERTY setting if that is needed.
(setq nval (read-string "Allowed: " allowed))
(org-entry-put
(cond ((marker-position org-entry-property-inherited-from)
org-entry-property-inherited-from)
((marker-position org-columns-top-level-marker)
org-columns-top-level-marker))
org-columns-top-level-marker)
(t pom))
key1 nval)))
(defun org-columns-eval (form)
@ -658,6 +670,7 @@ display, or in the #+COLUMNS line of the current buffer."
(defvar org-agenda-view-columns-initially nil
"When set, switch to columns view immediately after creating the agenda.")
(defvar org-agenda-columns-show-summaries) ; defined in org-agenda.el
(defun org-agenda-columns ()
"Turn on column view in the agenda."
(interactive)
@ -685,6 +698,7 @@ display, or in the #+COLUMNS line of the current buffer."
(setq fmt (or fmt org-columns-default-format))
(org-set-local 'org-columns-current-fmt fmt)
(org-columns-compile-format fmt)
(org-agenda-colview-compute org-columns-current-fmt-compiled)
(save-excursion
;; Get and cache the properties
(goto-char (point-min))
@ -700,7 +714,84 @@ display, or in the #+COLUMNS line of the current buffer."
(mapc (lambda (x)
(goto-line (car x))
(org-columns-display-here (cdr x)))
cache)))))
cache)
(when org-agenda-columns-show-summaries
(org-agenda-colview-summarize cache))))))
(defun org-agenda-colview-summarize (cache)
"Summarize the summarizable columns in column view in the agenda.
This will add overlays to the date lines, to show the summary for each day."
(let* ((fmt (mapcar (lambda (x)
(list (car x) (if (equal (car x) "CLOCKSUM")
'add_times (nth 4 x))))
org-columns-current-fmt-compiled))
line c c1 stype props lsum entries prop v)
(when (delq nil (mapcar 'cadr fmt))
;; OK, at least one summation column, it makes sense to try this
(goto-char (point-max))
(while (not (bobp))
(if (not (or (get-text-property (point) 'org-date-line)
(eq (get-text-property (point) 'face)
'org-agenda-structure)))
(beginning-of-line 0)
;; OK, this is a date line
(setq line (org-current-line))
(setq entries nil c cache cache nil)
(while (setq c1 (pop c))
(if (> (car c1) line)
(push c1 entries)
(push c1 cache)))
;; now ENTRIES are the ones we want to use, CACHE is the rest
;; Compute the summaries for the properties we want,
;; set nil properties for the rest.
(when (setq entries (mapcar 'cdr entries))
(setq props
(mapcar
(lambda (f)
(setq prop (car f) stype (nth 1 f))
(cond
((equal prop "ITEM")
(cons prop (buffer-substring (point-at-bol)
(point-at-eol))))
((not stype) (cons prop ""))
(t
;; do the summary
(setq lsum 0)
(mapc (lambda (x)
(setq v (cdr (assoc prop x)))
(if v (setq lsum (+ lsum
(org-column-string-to-number
v stype)))))
entries)
(cons prop (org-columns-number-to-string lsum stype)))))
fmt))
(org-columns-display-here props)
(org-set-local 'org-agenda-columns-active t))
(beginning-of-line 0))))))
(defvar org-agenda-columns-compute-summary-properties); defined in org-agenda.el
(defun org-agenda-colview-compute (fmt)
"Compute the relevant columns in the contributing source buffers."
(when org-agenda-columns-compute-summary-properties
(let ((files org-agenda-contributing-files)
(org-columns-begin-marker (make-marker))
(org-columns-top-level-marker (make-marker))
f fm a b)
(while (setq f (pop files))
(setq b (find-buffer-visiting f))
(with-current-buffer (or (buffer-base-buffer b) b)
(save-excursion
(save-restriction
(goto-char (point-min))
(org-columns-get-format-and-top-level)
(while (setq fm (pop fmt))
(if (equal (car fm) "CLOCKSUM")
(org-clock-sum)
(when (and (nth 4 fm)
(setq a (assoc (car fm)
org-columns-current-fmt-compiled))
(equal (nth 4 a) (nth 4 fm)))
(org-columns-compute (car fm))))))))))))
(defun org-columns-get-autowidth-alist (s cache)
"Derive the maximum column widths from the format and the cache."
@ -1056,3 +1147,4 @@ and tailing newline characters."
(provide 'org-colview)
;;; org-colview.el ends here

View File

@ -1324,6 +1324,7 @@ taken from the (otherwise obsolete) variable `org-todo-interpretation'."
(make-variable-buffer-local 'org-todo-keywords-1)
(defvar org-todo-keywords-for-agenda nil)
(defvar org-done-keywords-for-agenda nil)
(defvar org-agenda-contributing-files nil)
(defvar org-not-done-keywords nil)
(make-variable-buffer-local 'org-not-done-keywords)
(defvar org-done-keywords nil)