Add arch tagline
[bpt/emacs.git] / lisp / org / org-colview.el
CommitLineData
20908596
CD
1;;; org-colview.el --- Column View in Org-mode
2
3;; Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
4
5;; Author: Carsten Dominik <carsten at orgmode dot org>
6;; Keywords: outlines, hypermedia, calendar, wp
7;; Homepage: http://orgmode.org
8;; Version: 6.02b
9;;
10;; This file is part of GNU Emacs.
11;;
12;; GNU Emacs is free software; you can redistribute it and/or modify
13;; it under the terms of the GNU General Public License as published by
14;; the Free Software Foundation; either version 3, or (at your option)
15;; any later version.
16
17;; GNU Emacs is distributed in the hope that it will be useful,
18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
23;; along with GNU Emacs; see the file COPYING. If not, write to the
24;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25;; Boston, MA 02110-1301, USA.
26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
27;;
28;;; Commentary:
29
30;; This file contains the face definitons for Org.
31
32;;; Code:
33
34(eval-when-compile (require 'cl))
35(require 'org)
36
37;;; Column View
38
39(defvar org-columns-overlays nil
40 "Holds the list of current column overlays.")
41
42(defvar org-columns-current-fmt nil
43 "Local variable, holds the currently active column format.")
44(make-variable-buffer-local 'org-columns-current-fmt)
45(defvar org-columns-current-fmt-compiled nil
46 "Local variable, holds the currently active column format.
47This is the compiled version of the format.")
48(make-variable-buffer-local 'org-columns-current-fmt-compiled)
49(defvar org-columns-current-widths nil
50 "Loval variable, holds the currently widths of fields.")
51(make-variable-buffer-local 'org-columns-current-widths)
52(defvar org-columns-current-maxwidths nil
53 "Loval variable, holds the currently active maximum column widths.")
54(make-variable-buffer-local 'org-columns-current-maxwidths)
55(defvar org-columns-begin-marker (make-marker)
56 "Points to the position where last a column creation command was called.")
57(defvar org-columns-top-level-marker (make-marker)
58 "Points to the position where current columns region starts.")
59
60(defvar org-columns-map (make-sparse-keymap)
61 "The keymap valid in column display.")
62
63(defun org-columns-content ()
64 "Switch to contents view while in columns view."
65 (interactive)
66 (org-overview)
67 (org-content))
68
69(org-defkey org-columns-map "c" 'org-columns-content)
70(org-defkey org-columns-map "o" 'org-overview)
71(org-defkey org-columns-map "e" 'org-columns-edit-value)
72(org-defkey org-columns-map "\C-c\C-t" 'org-columns-todo)
73(org-defkey org-columns-map "\C-c\C-c" 'org-columns-set-tags-or-toggle)
74(org-defkey org-columns-map "\C-c\C-o" 'org-columns-open-link)
75(org-defkey org-columns-map "v" 'org-columns-show-value)
76(org-defkey org-columns-map "q" 'org-columns-quit)
77(org-defkey org-columns-map "r" 'org-columns-redo)
78(org-defkey org-columns-map "g" 'org-columns-redo)
79(org-defkey org-columns-map [left] 'backward-char)
80(org-defkey org-columns-map "\M-b" 'backward-char)
81(org-defkey org-columns-map "a" 'org-columns-edit-allowed)
82(org-defkey org-columns-map "s" 'org-columns-edit-attributes)
83(org-defkey org-columns-map "\M-f" (lambda () (interactive) (goto-char (1+ (point)))))
84(org-defkey org-columns-map [right] (lambda () (interactive) (goto-char (1+ (point)))))
85(org-defkey org-columns-map [(shift right)] 'org-columns-next-allowed-value)
86(org-defkey org-columns-map "n" 'org-columns-next-allowed-value)
87(org-defkey org-columns-map [(shift left)] 'org-columns-previous-allowed-value)
88(org-defkey org-columns-map "p" 'org-columns-previous-allowed-value)
89(org-defkey org-columns-map "<" 'org-columns-narrow)
90(org-defkey org-columns-map ">" 'org-columns-widen)
91(org-defkey org-columns-map [(meta right)] 'org-columns-move-right)
92(org-defkey org-columns-map [(meta left)] 'org-columns-move-left)
93(org-defkey org-columns-map [(shift meta right)] 'org-columns-new)
94(org-defkey org-columns-map [(shift meta left)] 'org-columns-delete)
95
96(easy-menu-define org-columns-menu org-columns-map "Org Column Menu"
97 '("Column"
98 ["Edit property" org-columns-edit-value t]
99 ["Next allowed value" org-columns-next-allowed-value t]
100 ["Previous allowed value" org-columns-previous-allowed-value t]
101 ["Show full value" org-columns-show-value t]
102 ["Edit allowed values" org-columns-edit-allowed t]
103 "--"
104 ["Edit column attributes" org-columns-edit-attributes t]
105 ["Increase column width" org-columns-widen t]
106 ["Decrease column width" org-columns-narrow t]
107 "--"
108 ["Move column right" org-columns-move-right t]
109 ["Move column left" org-columns-move-left t]
110 ["Add column" org-columns-new t]
111 ["Delete column" org-columns-delete t]
112 "--"
113 ["CONTENTS" org-columns-content t]
114 ["OVERVIEW" org-overview t]
115 ["Refresh columns display" org-columns-redo t]
116 "--"
117 ["Open link" org-columns-open-link t]
118 "--"
119 ["Quit" org-columns-quit t]))
120
121(defun org-columns-new-overlay (beg end &optional string face)
122 "Create a new column overlay and add it to the list."
123 (let ((ov (org-make-overlay beg end)))
124 (org-overlay-put ov 'face (or face 'secondary-selection))
125 (org-overlay-display ov string face)
126 (push ov org-columns-overlays)
127 ov))
128
129(defun org-columns-display-here (&optional props)
130 "Overlay the current line with column display."
131 (interactive)
132 (let* ((fmt org-columns-current-fmt-compiled)
133 (beg (point-at-bol))
134 (level-face (save-excursion
135 (beginning-of-line 1)
136 (and (looking-at "\\(\\**\\)\\(\\* \\)")
137 (org-get-level-face 2))))
138 (ref-face (or level-face
139 (and (eq major-mode 'org-agenda-mode)
140 (get-text-property (point-at-bol) 'face))
141 'default))
142 (color (list :foreground
143 (face-attribute ref-face :foreground)
144 :weight 'normal :strike-through nil
145 :underline nil))
146 (face (list color 'org-column level-face))
147 pom property ass width f string ov column val modval)
148 ;; Check if the entry is in another buffer.
149 (unless props
150 (if (eq major-mode 'org-agenda-mode)
151 (setq pom (or (get-text-property (point) 'org-hd-marker)
152 (get-text-property (point) 'org-marker))
153 props (if pom (org-entry-properties pom) nil))
154 (setq props (org-entry-properties nil))))
155 ;; Walk the format
156 (while (setq column (pop fmt))
157 (setq property (car column)
158 ass (if (equal property "ITEM")
159 (cons "ITEM"
160 (save-match-data
161 (org-no-properties
162 (org-remove-tabs
163 (buffer-substring-no-properties
164 (point-at-bol) (point-at-eol))))))
165 (assoc property props))
166 width (or (cdr (assoc property org-columns-current-maxwidths))
167 (nth 2 column)
168 (length property))
169 f (format "%%-%d.%ds | " width width)
170 val (or (cdr ass) "")
171 modval (if (equal property "ITEM")
172 (org-columns-cleanup-item val org-columns-current-fmt-compiled))
173 string (format f (or modval val)))
174 ;; Create the overlay
175 (org-unmodified
176 (setq ov (org-columns-new-overlay
177 beg (setq beg (1+ beg)) string face))
178 (org-overlay-put ov 'keymap org-columns-map)
179 (org-overlay-put ov 'org-columns-key property)
180 (org-overlay-put ov 'org-columns-value (cdr ass))
181 (org-overlay-put ov 'org-columns-value-modified modval)
182 (org-overlay-put ov 'org-columns-pom pom)
183 (org-overlay-put ov 'org-columns-format f))
184 (if (or (not (char-after beg))
185 (equal (char-after beg) ?\n))
186 (let ((inhibit-read-only t))
187 (save-excursion
188 (goto-char beg)
189 (org-unmodified (insert " ")))))) ;; FIXME: add props and remove later?
190 ;; Make the rest of the line disappear.
191 (org-unmodified
192 (setq ov (org-columns-new-overlay beg (point-at-eol)))
193 (org-overlay-put ov 'invisible t)
194 (org-overlay-put ov 'keymap org-columns-map)
195 (org-overlay-put ov 'intangible t)
196 (push ov org-columns-overlays)
197 (setq ov (org-make-overlay (1- (point-at-eol)) (1+ (point-at-eol))))
198 (org-overlay-put ov 'keymap org-columns-map)
199 (push ov org-columns-overlays)
200 (let ((inhibit-read-only t))
201 (put-text-property (max (point-min) (1- (point-at-bol)))
202 (min (point-max) (1+ (point-at-eol)))
203 'read-only "Type `e' to edit property")))))
204
205(defvar org-columns-full-header-line-format nil
206 "Fthe full header line format, will be shifted by horizontal scrolling." )
207(defvar org-previous-header-line-format nil
208 "The header line format before column view was turned on.")
209(defvar org-columns-inhibit-recalculation nil
210 "Inhibit recomputing of columns on column view startup.")
211(defvar org-columns-flyspell-was-active nil
212 "Remember the state of `flyspell-mode' before column view.
213Flyspell-mode can cause problems in columns view, so it is turned off
214for the duration of the command.")
215
216(defvar header-line-format)
217(defvar org-columns-previous-hscroll 0)
218(defun org-columns-display-here-title ()
219 "Overlay the newline before the current line with the table title."
220 (interactive)
221 (let ((fmt org-columns-current-fmt-compiled)
222 string (title "")
223 property width f column str widths)
224 (while (setq column (pop fmt))
225 (setq property (car column)
226 str (or (nth 1 column) property)
227 width (or (cdr (assoc property org-columns-current-maxwidths))
228 (nth 2 column)
229 (length str))
230 widths (push width widths)
231 f (format "%%-%d.%ds | " width width)
232 string (format f str)
233 title (concat title string)))
234 (setq title (concat
235 (org-add-props " " nil 'display '(space :align-to 0))
236 ;;(org-add-props title nil 'face '(:weight bold :underline t :inherit default))))
237 (org-add-props title nil 'face 'org-column-title)))
238 (org-set-local 'org-previous-header-line-format header-line-format)
239 (org-set-local 'org-columns-current-widths (nreverse widths))
240 (setq org-columns-full-header-line-format title)
241 (setq org-columns-previous-hscroll -1)
242; (org-columns-hscoll-title)
243 (org-add-hook 'post-command-hook 'org-columns-hscoll-title nil 'local)))
244
245(defun org-columns-hscoll-title ()
246 "Set the header-line-format so that it scrolls along with the table."
247 (sit-for .0001) ; need to force a redisplay to update window-hscroll
248 (when (not (= (window-hscroll) org-columns-previous-hscroll))
249 (setq header-line-format
250 (concat (substring org-columns-full-header-line-format 0 1)
251 (substring org-columns-full-header-line-format
252 (1+ (window-hscroll))))
253 org-columns-previous-hscroll (window-hscroll))
254 (force-mode-line-update)))
255
256(defun org-columns-remove-overlays ()
257 "Remove all currently active column overlays."
258 (interactive)
259 (when (marker-buffer org-columns-begin-marker)
260 (with-current-buffer (marker-buffer org-columns-begin-marker)
261 (when (local-variable-p 'org-previous-header-line-format)
262 (setq header-line-format org-previous-header-line-format)
263 (kill-local-variable 'org-previous-header-line-format)
264 (remove-hook 'post-command-hook 'org-columns-hscoll-title 'local))
265 (move-marker org-columns-begin-marker nil)
266 (move-marker org-columns-top-level-marker nil)
267 (org-unmodified
268 (mapc 'org-delete-overlay org-columns-overlays)
269 (setq org-columns-overlays nil)
270 (let ((inhibit-read-only t))
271 (remove-text-properties (point-min) (point-max) '(read-only t))))
272 (when org-columns-flyspell-was-active
273 (flyspell-mode 1)))))
274
275(defun org-columns-cleanup-item (item fmt)
276 "Remove from ITEM what is a column in the format FMT."
277 (if (not org-complex-heading-regexp)
278 item
279 (when (string-match org-complex-heading-regexp item)
280 (concat
281 (org-add-props (concat (match-string 1 item) " ") nil
282 'org-whitespace (* 2 (1- (org-reduced-level (- (match-end 1) (match-beginning 1))))))
283 (and (match-end 2) (not (assoc "TODO" fmt)) (concat " " (match-string 2 item)))
284 (and (match-end 3) (not (assoc "PRIORITY" fmt)) (concat " " (match-string 3 item)))
285 " " (match-string 4 item)
286 (and (match-end 5) (not (assoc "TAGS" fmt)) (concat " " (match-string 5 item)))))))
287
288(defun org-columns-show-value ()
289 "Show the full value of the property."
290 (interactive)
291 (let ((value (get-char-property (point) 'org-columns-value)))
292 (message "Value is: %s" (or value ""))))
293
294(defvar org-agenda-columns-active) ;; defined in org-agenda.el
295(defun org-columns-quit ()
296 "Remove the column overlays and in this way exit column editing."
297 (interactive)
298 (org-unmodified
299 (org-columns-remove-overlays)
300 (let ((inhibit-read-only t))
301 (remove-text-properties (point-min) (point-max) '(read-only t))))
302 (when (eq major-mode 'org-agenda-mode)
303 (setq org-agenda-columns-active nil)
304 (message
305 "Modification not yet reflected in Agenda buffer, use `r' to refresh")))
306
307(defun org-columns-check-computed ()
308 "Check if this column value is computed.
309If yes, throw an error indicating that changing it does not make sense."
310 (let ((val (get-char-property (point) 'org-columns-value)))
311 (when (and (stringp val)
312 (get-char-property 0 'org-computed val))
313 (error "This value is computed from the entry's children"))))
314
315(defun org-columns-todo (&optional arg)
316 "Change the TODO state during column view."
317 (interactive "P")
318 (org-columns-edit-value "TODO"))
319
320(defun org-columns-set-tags-or-toggle (&optional arg)
321 "Toggle checkbox at point, or set tags for current headline."
322 (interactive "P")
323 (if (string-match "\\`\\[[ xX-]\\]\\'"
324 (get-char-property (point) 'org-columns-value))
325 (org-columns-next-allowed-value)
326 (org-columns-edit-value "TAGS")))
327
328(defun org-columns-edit-value (&optional key)
329 "Edit the value of the property at point in column view.
330Where possible, use the standard interface for changing this line."
331 (interactive)
332 (org-columns-check-computed)
333 (let* ((external-key key)
334 (col (current-column))
335 (key (or key (get-char-property (point) 'org-columns-key)))
336 (value (get-char-property (point) 'org-columns-value))
337 (bol (point-at-bol)) (eol (point-at-eol))
338 (pom (or (get-text-property bol 'org-hd-marker)
339 (point))) ; keep despite of compiler waring
340 (line-overlays
341 (delq nil (mapcar (lambda (x)
342 (and (eq (overlay-buffer x) (current-buffer))
343 (>= (overlay-start x) bol)
344 (<= (overlay-start x) eol)
345 x))
346 org-columns-overlays)))
347 nval eval allowed)
348 (cond
349 ((equal key "CLOCKSUM")
350 (error "This special column cannot be edited"))
351 ((equal key "ITEM")
352 (setq eval '(org-with-point-at pom
353 (org-edit-headline))))
354 ((equal key "TODO")
355 (setq eval '(org-with-point-at pom
356 (let ((current-prefix-arg
357 (if external-key current-prefix-arg '(4))))
358 (call-interactively 'org-todo)))))
359 ((equal key "PRIORITY")
360 (setq eval '(org-with-point-at pom
361 (call-interactively 'org-priority))))
362 ((equal key "TAGS")
363 (setq eval '(org-with-point-at pom
364 (let ((org-fast-tag-selection-single-key
365 (if (eq org-fast-tag-selection-single-key 'expert)
366 t org-fast-tag-selection-single-key)))
367 (call-interactively 'org-set-tags)))))
368 ((equal key "DEADLINE")
369 (setq eval '(org-with-point-at pom
370 (call-interactively 'org-deadline))))
371 ((equal key "SCHEDULED")
372 (setq eval '(org-with-point-at pom
373 (call-interactively 'org-schedule))))
374 (t
375 (setq allowed (org-property-get-allowed-values pom key 'table))
376 (if allowed
377 (setq nval (completing-read "Value: " allowed nil t))
378 (setq nval (read-string "Edit: " value)))
379 (setq nval (org-trim nval))
380 (when (not (equal nval value))
381 (setq eval '(org-entry-put pom key nval)))))
382 (when eval
383
384 (cond
385 ((equal major-mode 'org-agenda-mode)
386 (org-columns-eval '(org-entry-put pom key nval))
387 ;; The following let preserves the current format, and makes sure
388 ;; that in only a single file things need to be upated.
389 (let* ((org-agenda-overriding-columns-format org-columns-current-fmt)
390 (buffer (marker-buffer pom))
391 (org-agenda-contributing-files
392 (list (with-current-buffer buffer
393 (buffer-file-name (buffer-base-buffer))))))
394 (org-agenda-columns)))
395 (t
396 (let ((inhibit-read-only t))
397 (org-unmodified
398 (remove-text-properties
399 (max (point-min) (1- bol)) eol '(read-only t)))
400 (unwind-protect
401 (progn
402 (setq org-columns-overlays
403 (org-delete-all line-overlays org-columns-overlays))
404 (mapc 'org-delete-overlay line-overlays)
405 (org-columns-eval eval))
406 (org-columns-display-here)))
407 (org-move-to-column col)
408 (if (and (org-mode-p)
409 (nth 3 (assoc key org-columns-current-fmt-compiled)))
410 (org-columns-update key)))))))
411
412(defun org-edit-headline () ; FIXME: this is not columns specific. Make interactive????? Use from agenda????
413 "Edit the current headline, the part without TODO keyword, TAGS."
414 (org-back-to-heading)
415 (when (looking-at org-todo-line-regexp)
416 (let ((pre (buffer-substring (match-beginning 0) (match-beginning 3)))
417 (txt (match-string 3))
418 (post "")
419 txt2)
420 (if (string-match (org-re "[ \t]+:[[:alnum:]:_@]+:[ \t]*$") txt)
421 (setq post (match-string 0 txt)
422 txt (substring txt 0 (match-beginning 0))))
423 (setq txt2 (read-string "Edit: " txt))
424 (when (not (equal txt txt2))
425 (beginning-of-line 1)
426 (insert pre txt2 post)
427 (delete-region (point) (point-at-eol))
428 (org-set-tags nil t)))))
429
430(defun org-columns-edit-allowed ()
431 "Edit the list of allowed values for the current property."
432 (interactive)
433 (let* ((pom (or (get-text-property (point-at-bol) 'org-marker)
434 (get-text-property (point-at-bol) 'org-hd-marker)
435 (point)))
436 (key (get-char-property (point) 'org-columns-key))
437 (key1 (concat key "_ALL"))
438 (allowed (org-entry-get pom key1 t))
439 nval)
440 ;; FIXME: Cover editing TODO, TAGS etc in-buffer settings.????
441 ;; FIXME: Write back to #+PROPERTY setting if that is needed.
442 (setq nval (read-string "Allowed: " allowed))
443 (org-entry-put
444 (cond ((marker-position org-entry-property-inherited-from)
445 org-entry-property-inherited-from)
446 ((marker-position org-columns-top-level-marker)
447 org-columns-top-level-marker)
448 (t pom))
449 key1 nval)))
450
451(defun org-columns-eval (form)
452 (let (hidep)
453 (save-excursion
454 (beginning-of-line 1)
455 ;; `next-line' is needed here, because it skips invisible line.
456 (condition-case nil (org-no-warnings (next-line 1)) (error nil))
457 (setq hidep (org-on-heading-p 1)))
458 (eval form)
459 (and hidep (hide-entry))))
460
461(defun org-columns-previous-allowed-value ()
462 "Switch to the previous allowed value for this column."
463 (interactive)
464 (org-columns-next-allowed-value t))
465
466(defun org-columns-next-allowed-value (&optional previous)
467 "Switch to the next allowed value for this column."
468 (interactive)
469 (org-columns-check-computed)
470 (let* ((col (current-column))
471 (key (get-char-property (point) 'org-columns-key))
472 (value (get-char-property (point) 'org-columns-value))
473 (bol (point-at-bol)) (eol (point-at-eol))
474 (pom (or (get-text-property bol 'org-hd-marker)
475 (point))) ; keep despite of compiler waring
476 (line-overlays
477 (delq nil (mapcar (lambda (x)
478 (and (eq (overlay-buffer x) (current-buffer))
479 (>= (overlay-start x) bol)
480 (<= (overlay-start x) eol)
481 x))
482 org-columns-overlays)))
483 (allowed (or (org-property-get-allowed-values pom key)
484 (and (memq
485 (nth 4 (assoc key org-columns-current-fmt-compiled))
486 '(checkbox checkbox-n-of-m checkbox-percent))
487 '("[ ]" "[X]"))))
488 nval)
489 (when (equal key "ITEM")
490 (error "Cannot edit item headline from here"))
491 (unless (or allowed (member key '("SCHEDULED" "DEADLINE")))
492 (error "Allowed values for this property have not been defined"))
493 (if (member key '("SCHEDULED" "DEADLINE"))
494 (setq nval (if previous 'earlier 'later))
495 (if previous (setq allowed (reverse allowed)))
496 (if (member value allowed)
497 (setq nval (car (cdr (member value allowed)))))
498 (setq nval (or nval (car allowed)))
499 (if (equal nval value)
500 (error "Only one allowed value for this property")))
501 (cond
502 ((equal major-mode 'org-agenda-mode)
503 (org-columns-eval '(org-entry-put pom key nval))
504 ;; The following let preserves the current format, and makes sure
505 ;; that in only a single file things need to be upated.
506 (let* ((org-agenda-overriding-columns-format org-columns-current-fmt)
507 (buffer (marker-buffer pom))
508 (org-agenda-contributing-files
509 (list (with-current-buffer buffer
510 (buffer-file-name (buffer-base-buffer))))))
511 (org-agenda-columns)))
512 (t
513 (let ((inhibit-read-only t))
514 (remove-text-properties (1- bol) eol '(read-only t))
515 (unwind-protect
516 (progn
517 (setq org-columns-overlays
518 (org-delete-all line-overlays org-columns-overlays))
519 (mapc 'org-delete-overlay line-overlays)
520 (org-columns-eval '(org-entry-put pom key nval)))
521 (org-columns-display-here)))
522 (org-move-to-column col)
523 (and (nth 3 (assoc key org-columns-current-fmt-compiled))
524 (org-columns-update key))))))
525
526(defun org-verify-version (task)
527 (cond
528 ((eq task 'columns)
529 (if (or (featurep 'xemacs)
530 (< emacs-major-version 22))
531 (error "Emacs 22 is required for the columns feature")))))
532
533(defun org-columns-open-link (&optional arg)
534 (interactive "P")
535 (let ((value (get-char-property (point) 'org-columns-value)))
536 (org-open-link-from-string value arg)))
537
538(defun org-columns-get-format-and-top-level ()
539 (let (fmt)
540 (when (condition-case nil (org-back-to-heading) (error nil))
541 (move-marker org-entry-property-inherited-from nil)
542 (setq fmt (org-entry-get nil "COLUMNS" t)))
543 (setq fmt (or fmt org-columns-default-format))
544 (org-set-local 'org-columns-current-fmt fmt)
545 (org-columns-compile-format fmt)
546 (if (marker-position org-entry-property-inherited-from)
547 (move-marker org-columns-top-level-marker
548 org-entry-property-inherited-from)
549 (move-marker org-columns-top-level-marker (point)))
550 fmt))
551
552(defun org-columns ()
553 "Turn on column view on an org-mode file."
554 (interactive)
555 (org-verify-version 'columns)
556 (org-columns-remove-overlays)
557 (move-marker org-columns-begin-marker (point))
558 (let (beg end fmt cache maxwidths)
559 (setq fmt (org-columns-get-format-and-top-level))
560 (save-excursion
561 (goto-char org-columns-top-level-marker)
562 (setq beg (point))
563 (unless org-columns-inhibit-recalculation
564 (org-columns-compute-all))
565 (setq end (or (condition-case nil (org-end-of-subtree t t) (error nil))
566 (point-max)))
567 ;; Get and cache the properties
568 (goto-char beg)
569 (when (assoc "CLOCKSUM" org-columns-current-fmt-compiled)
570 (save-excursion
571 (save-restriction
572 (narrow-to-region beg end)
573 (org-clock-sum))))
574 (while (re-search-forward (concat "^" outline-regexp) end t)
575 (push (cons (org-current-line) (org-entry-properties)) cache))
576 (when cache
577 (setq maxwidths (org-columns-get-autowidth-alist fmt cache))
578 (org-set-local 'org-columns-current-maxwidths maxwidths)
579 (org-columns-display-here-title)
580 (when (org-set-local 'org-columns-flyspell-was-active
581 (org-bound-and-true-p flyspell-mode))
582 (flyspell-mode 0))
583 (mapc (lambda (x)
584 (goto-line (car x))
585 (org-columns-display-here (cdr x)))
586 cache)))))
587
588(defun org-columns-new (&optional prop title width op fmt &rest rest)
589 "Insert a new column, to the left of the current column."
590 (interactive)
591 (let ((editp (and prop (assoc prop org-columns-current-fmt-compiled)))
592 cell)
593 (setq prop (completing-read
594 "Property: " (mapcar 'list (org-buffer-property-keys t nil t))
595 nil nil prop))
596 (setq title (read-string (concat "Column title [" prop "]: ") (or title prop)))
597 (setq width (read-string "Column width: " (if width (number-to-string width))))
598 (if (string-match "\\S-" width)
599 (setq width (string-to-number width))
600 (setq width nil))
601 (setq fmt (completing-read "Summary [none]: "
602 '(("none") ("add_numbers") ("currency") ("add_times") ("checkbox") ("checkbox-n-of-m") ("checkbox-percent"))
603 nil t))
604 (if (string-match "\\S-" fmt)
605 (setq fmt (intern fmt))
606 (setq fmt nil))
607 (if (eq fmt 'none) (setq fmt nil))
608 (if editp
609 (progn
610 (setcar editp prop)
611 (setcdr editp (list title width nil fmt)))
612 (setq cell (nthcdr (1- (current-column))
613 org-columns-current-fmt-compiled))
614 (setcdr cell (cons (list prop title width nil fmt)
615 (cdr cell))))
616 (org-columns-store-format)
617 (org-columns-redo)))
618
619(defun org-columns-delete ()
620 "Delete the column at point from columns view."
621 (interactive)
622 (let* ((n (current-column))
623 (title (nth 1 (nth n org-columns-current-fmt-compiled))))
624 (when (y-or-n-p
625 (format "Are you sure you want to remove column \"%s\"? " title))
626 (setq org-columns-current-fmt-compiled
627 (delq (nth n org-columns-current-fmt-compiled)
628 org-columns-current-fmt-compiled))
629 (org-columns-store-format)
630 (org-columns-redo)
631 (if (>= (current-column) (length org-columns-current-fmt-compiled))
632 (backward-char 1)))))
633
634(defun org-columns-edit-attributes ()
635 "Edit the attributes of the current column."
636 (interactive)
637 (let* ((n (current-column))
638 (info (nth n org-columns-current-fmt-compiled)))
639 (apply 'org-columns-new info)))
640
641(defun org-columns-widen (arg)
642 "Make the column wider by ARG characters."
643 (interactive "p")
644 (let* ((n (current-column))
645 (entry (nth n org-columns-current-fmt-compiled))
646 (width (or (nth 2 entry)
647 (cdr (assoc (car entry) org-columns-current-maxwidths)))))
648 (setq width (max 1 (+ width arg)))
649 (setcar (nthcdr 2 entry) width)
650 (org-columns-store-format)
651 (org-columns-redo)))
652
653(defun org-columns-narrow (arg)
654 "Make the column nrrower by ARG characters."
655 (interactive "p")
656 (org-columns-widen (- arg)))
657
658(defun org-columns-move-right ()
659 "Swap this column with the one to the right."
660 (interactive)
661 (let* ((n (current-column))
662 (cell (nthcdr n org-columns-current-fmt-compiled))
663 e)
664 (when (>= n (1- (length org-columns-current-fmt-compiled)))
665 (error "Cannot shift this column further to the right"))
666 (setq e (car cell))
667 (setcar cell (car (cdr cell)))
668 (setcdr cell (cons e (cdr (cdr cell))))
669 (org-columns-store-format)
670 (org-columns-redo)
671 (forward-char 1)))
672
673(defun org-columns-move-left ()
674 "Swap this column with the one to the left."
675 (interactive)
676 (let* ((n (current-column)))
677 (when (= n 0)
678 (error "Cannot shift this column further to the left"))
679 (backward-char 1)
680 (org-columns-move-right)
681 (backward-char 1)))
682
683(defun org-columns-store-format ()
684 "Store the text version of the current columns format in appropriate place.
685This is either in the COLUMNS property of the node starting the current column
686display, or in the #+COLUMNS line of the current buffer."
687 (let (fmt (cnt 0))
688 (setq fmt (org-columns-uncompile-format org-columns-current-fmt-compiled))
689 (org-set-local 'org-columns-current-fmt fmt)
690 (if (marker-position org-columns-top-level-marker)
691 (save-excursion
692 (goto-char org-columns-top-level-marker)
693 (if (and (org-at-heading-p)
694 (org-entry-get nil "COLUMNS"))
695 (org-entry-put nil "COLUMNS" fmt)
696 (goto-char (point-min))
697 ;; Overwrite all #+COLUMNS lines....
698 (while (re-search-forward "^#\\+COLUMNS:.*" nil t)
699 (setq cnt (1+ cnt))
700 (replace-match (concat "#+COLUMNS: " fmt) t t))
701 (unless (> cnt 0)
702 (goto-char (point-min))
703 (or (org-on-heading-p t) (outline-next-heading))
704 (let ((inhibit-read-only t))
705 (insert-before-markers "#+COLUMNS: " fmt "\n")))
706 (org-set-local 'org-columns-default-format fmt))))))
707
708(defvar org-agenda-overriding-columns-format nil
709 "When set, overrides any other format definition for the agenda.
710Don't set this, this is meant for dynamic scoping.")
711
712(defun org-columns-get-autowidth-alist (s cache)
713 "Derive the maximum column widths from the format and the cache."
714 (let ((start 0) rtn)
715 (while (string-match (org-re "%\\([[:alpha:]][[:alnum:]_-]*\\)") s start)
716 (push (cons (match-string 1 s) 1) rtn)
717 (setq start (match-end 0)))
718 (mapc (lambda (x)
719 (setcdr x (apply 'max
720 (mapcar
721 (lambda (y)
722 (length (or (cdr (assoc (car x) (cdr y))) " ")))
723 cache))))
724 rtn)
725 rtn))
726
727(defun org-columns-compute-all ()
728 "Compute all columns that have operators defined."
729 (org-unmodified
730 (remove-text-properties (point-min) (point-max) '(org-summaries t)))
731 (let ((columns org-columns-current-fmt-compiled) col)
732 (while (setq col (pop columns))
733 (when (nth 3 col)
734 (save-excursion
735 (org-columns-compute (car col)))))))
736
737(defun org-columns-update (property)
738 "Recompute PROPERTY, and update the columns display for it."
739 (org-columns-compute property)
740 (let (fmt val pos)
741 (save-excursion
742 (mapc (lambda (ov)
743 (when (equal (org-overlay-get ov 'org-columns-key) property)
744 (setq pos (org-overlay-start ov))
745 (goto-char pos)
746 (when (setq val (cdr (assoc property
747 (get-text-property
748 (point-at-bol) 'org-summaries))))
749 (setq fmt (org-overlay-get ov 'org-columns-format))
750 (org-overlay-put ov 'org-columns-value val)
751 (org-overlay-put ov 'display (format fmt val)))))
752 org-columns-overlays))))
753
754(defun org-columns-compute (property)
755 "Sum the values of property PROPERTY hierarchically, for the entire buffer."
756 (interactive)
757 (let* ((re (concat "^" outline-regexp))
758 (lmax 30) ; Does anyone use deeper levels???
759 (lsum (make-vector lmax 0))
760 (lflag (make-vector lmax nil))
761 (level 0)
762 (ass (assoc property org-columns-current-fmt-compiled))
763 (format (nth 4 ass))
764 (printf (nth 5 ass))
765 (beg org-columns-top-level-marker)
766 last-level val valflag flag end sumpos sum-alist sum str str1 useval)
767 (save-excursion
768 ;; Find the region to compute
769 (goto-char beg)
770 (setq end (condition-case nil (org-end-of-subtree t) (error (point-max))))
771 (goto-char end)
772 ;; Walk the tree from the back and do the computations
773 (while (re-search-backward re beg t)
774 (setq sumpos (match-beginning 0)
775 last-level level
776 level (org-outline-level)
777 val (org-entry-get nil property)
778 valflag (and val (string-match "\\S-" val)))
779 (cond
780 ((< level last-level)
781 ;; put the sum of lower levels here as a property
782 (setq sum (aref lsum last-level) ; current sum
783 flag (aref lflag last-level) ; any valid entries from children?
784 str (org-columns-number-to-string sum format printf)
785 str1 (org-add-props (copy-sequence str) nil 'org-computed t 'face 'bold)
786 useval (if flag str1 (if valflag val ""))
787 sum-alist (get-text-property sumpos 'org-summaries))
788 (if (assoc property sum-alist)
789 (setcdr (assoc property sum-alist) useval)
790 (push (cons property useval) sum-alist)
791 (org-unmodified
792 (add-text-properties sumpos (1+ sumpos)
793 (list 'org-summaries sum-alist))))
794 (when (and val (not (equal val (if flag str val))))
795 (org-entry-put nil property (if flag str val)))
796 ;; add current to current level accumulator
797 (when (or flag valflag)
798 (aset lsum level (+ (aref lsum level)
799 (if flag sum (org-column-string-to-number
800 (if flag str val) format))))
801 (aset lflag level t))
802 ;; clear accumulators for deeper levels
803 (loop for l from (1+ level) to (1- lmax) do
804 (aset lsum l 0)
805 (aset lflag l nil)))
806 ((>= level last-level)
807 ;; add what we have here to the accumulator for this level
808 (aset lsum level (+ (aref lsum level)
809 (org-column-string-to-number (or val "0") format)))
810 (and valflag (aset lflag level t)))
811 (t (error "This should not happen")))))))
812
813(defun org-columns-redo ()
814 "Construct the column display again."
815 (interactive)
816 (message "Recomputing columns...")
817 (save-excursion
818 (if (marker-position org-columns-begin-marker)
819 (goto-char org-columns-begin-marker))
820 (org-columns-remove-overlays)
821 (if (org-mode-p)
822 (call-interactively 'org-columns)
823 (call-interactively 'org-agenda-columns)))
824 (message "Recomputing columns...done"))
825
826(defun org-columns-not-in-agenda ()
827 (if (eq major-mode 'org-agenda-mode)
828 (error "This command is only allowed in Org-mode buffers")))
829
830
831(defun org-string-to-number (s)
832 "Convert string to number, and interpret hh:mm:ss."
833 (if (not (string-match ":" s))
834 (string-to-number s)
835 (let ((l (nreverse (org-split-string s ":"))) (sum 0.0))
836 (while l
837 (setq sum (+ (string-to-number (pop l)) (/ sum 60))))
838 sum)))
839
840(defun org-columns-number-to-string (n fmt &optional printf)
841 "Convert a computed column number to a string value, according to FMT."
842 (cond
843 ((eq fmt 'add_times)
844 (let* ((h (floor n)) (m (floor (+ 0.5 (* 60 (- n h))))))
845 (format "%d:%02d" h m)))
846 ((eq fmt 'checkbox)
847 (cond ((= n (floor n)) "[X]")
848 ((> n 1.) "[-]")
849 (t "[ ]")))
850 ((memq fmt '(checkbox-n-of-m checkbox-percent))
851 (let* ((n1 (floor n)) (n2 (floor (+ .5 (* 1000000 (- n n1))))))
852 (org-nofm-to-completion n1 (+ n2 n1) (eq fmt 'checkbox-percent))))
853 (printf (format printf n))
854 ((eq fmt 'currency)
855 (format "%.2f" n))
856 (t (number-to-string n))))
857
858(defun org-nofm-to-completion (n m &optional percent)
859 (if (not percent)
860 (format "[%d/%d]" n m)
861 (format "[%d%%]"(floor (+ 0.5 (* 100. (/ (* 1.0 n) m)))))))
862
863(defun org-column-string-to-number (s fmt)
864 "Convert a column value to a number that can be used for column computing."
865 (cond
866 ((string-match ":" s)
867 (let ((l (nreverse (org-split-string s ":"))) (sum 0.0))
868 (while l
869 (setq sum (+ (string-to-number (pop l)) (/ sum 60))))
870 sum))
871 ((memq fmt '(checkbox checkbox-n-of-m checkbox-percent))
872 (if (equal s "[X]") 1. 0.000001))
873 (t (string-to-number s))))
874
875(defun org-columns-uncompile-format (cfmt)
876 "Turn the compiled columns format back into a string representation."
877 (let ((rtn "") e s prop title op width fmt printf)
878 (while (setq e (pop cfmt))
879 (setq prop (car e)
880 title (nth 1 e)
881 width (nth 2 e)
882 op (nth 3 e)
883 fmt (nth 4 e)
884 printf (nth 5 e))
885 (cond
886 ((eq fmt 'add_times) (setq op ":"))
887 ((eq fmt 'checkbox) (setq op "X"))
888 ((eq fmt 'checkbox-n-of-m) (setq op "X/"))
889 ((eq fmt 'checkbox-percent) (setq op "X%"))
890 ((eq fmt 'add_numbers) (setq op "+"))
891 ((eq fmt 'currency) (setq op "$")))
892 (if (and op printf) (setq op (concat op ";" printf)))
893 (if (equal title prop) (setq title nil))
894 (setq s (concat "%" (if width (number-to-string width))
895 prop
896 (if title (concat "(" title ")"))
897 (if op (concat "{" op "}"))))
898 (setq rtn (concat rtn " " s)))
899 (org-trim rtn)))
900
901(defun org-columns-compile-format (fmt)
902 "Turn a column format string into an alist of specifications.
903The alist has one entry for each column in the format. The elements of
904that list are:
905property the property
906title the title field for the columns
907width the column width in characters, can be nil for automatic
908operator the operator if any
909format the output format for computed results, derived from operator
910printf a printf format for computed values"
911 (let ((start 0) width prop title op f printf)
912 (setq org-columns-current-fmt-compiled nil)
913 (while (string-match
914 (org-re "%\\([0-9]+\\)?\\([[:alnum:]_-]+\\)\\(?:(\\([^)]+\\))\\)?\\(?:{\\([^}]+\\)}\\)?\\s-*")
915 fmt start)
916 (setq start (match-end 0)
917 width (match-string 1 fmt)
918 prop (match-string 2 fmt)
919 title (or (match-string 3 fmt) prop)
920 op (match-string 4 fmt)
921 f nil
922 printf nil)
923 (if width (setq width (string-to-number width)))
924 (when (and op (string-match ";" op))
925 (setq printf (substring op (match-end 0))
926 op (substring op 0 (match-beginning 0))))
927 (cond
928 ((equal op "+") (setq f 'add_numbers))
929 ((equal op "$") (setq f 'currency))
930 ((equal op ":") (setq f 'add_times))
931 ((equal op "X") (setq f 'checkbox))
932 ((equal op "X/") (setq f 'checkbox-n-of-m))
933 ((equal op "X%") (setq f 'checkbox-percent))
934 )
935 (push (list prop title width op f printf) org-columns-current-fmt-compiled))
936 (setq org-columns-current-fmt-compiled
937 (nreverse org-columns-current-fmt-compiled))))
938
939
940;;; Dynamic block for Column view
941
942(defun org-columns-capture-view (&optional maxlevel skip-empty-rows)
943 "Get the column view of the current buffer or subtree.
944The first optional argument MAXLEVEL sets the level limit. A
945second optional argument SKIP-EMPTY-ROWS tells whether to skip
946empty rows, an empty row being one where all the column view
947specifiers except ITEM are empty. This function returns a list
948containing the title row and all other rows. Each row is a list
949of fields."
950 (save-excursion
951 (let* ((title (mapcar 'cadr org-columns-current-fmt-compiled))
952 (n (length title)) row tbl)
953 (goto-char (point-min))
954 (while (and (re-search-forward "^\\(\\*+\\) " nil t)
955 (or (null maxlevel)
956 (>= maxlevel
957 (if org-odd-levels-only
958 (/ (1+ (length (match-string 1))) 2)
959 (length (match-string 1))))))
960 (when (get-char-property (match-beginning 0) 'org-columns-key)
961 (setq row nil)
962 (loop for i from 0 to (1- n) do
963 (push (or (get-char-property (+ (match-beginning 0) i) 'org-columns-value-modified)
964 (get-char-property (+ (match-beginning 0) i) 'org-columns-value)
965 "")
966 row))
967 (setq row (nreverse row))
968 (unless (and skip-empty-rows
969 (eq 1 (length (delete "" (delete-dups row)))))
970 (push row tbl))))
971 (append (list title 'hline) (nreverse tbl)))))
972
973(defun org-dblock-write:columnview (params)
974 "Write the column view table.
975PARAMS is a property list of parameters:
976
977:width enforce same column widths with <N> specifiers.
978:id the :ID: property of the entry where the columns view
979 should be built, as a string. When `local', call locally.
980 When `global' call column view with the cursor at the beginning
981 of the buffer (usually this means that the whole buffer switches
982 to column view).
983:hlines When t, insert a hline before each item. When a number, insert
984 a hline before each level <= that number.
985:vlines When t, make each column a colgroup to enforce vertical lines.
986:maxlevel When set to a number, don't capture headlines below this level.
987:skip-empty-rows
988 When t, skip rows where all specifiers other than ITEM are empty."
989 (let ((pos (move-marker (make-marker) (point)))
990 (hlines (plist-get params :hlines))
991 (vlines (plist-get params :vlines))
992 (maxlevel (plist-get params :maxlevel))
993 (skip-empty-rows (plist-get params :skip-empty-rows))
994 tbl id idpos nfields tmp)
995 (save-excursion
996 (save-restriction
997 (when (setq id (plist-get params :id))
998 (cond ((not id) nil)
999 ((eq id 'global) (goto-char (point-min)))
1000 ((eq id 'local) nil)
1001 ((setq idpos (org-find-entry-with-id id))
1002 (goto-char idpos))
1003 (t (error "Cannot find entry with :ID: %s" id))))
1004 (org-columns)
1005 (setq tbl (org-columns-capture-view maxlevel skip-empty-rows))
1006 (setq nfields (length (car tbl)))
1007 (org-columns-quit)))
1008 (goto-char pos)
1009 (move-marker pos nil)
1010 (when tbl
1011 (when (plist-get params :hlines)
1012 (setq tmp nil)
1013 (while tbl
1014 (if (eq (car tbl) 'hline)
1015 (push (pop tbl) tmp)
1016 (if (string-match "\\` *\\(\\*+\\)" (caar tbl))
1017 (if (and (not (eq (car tmp) 'hline))
1018 (or (eq hlines t)
1019 (and (numberp hlines) (<= (- (match-end 1) (match-beginning 1)) hlines))))
1020 (push 'hline tmp)))
1021 (push (pop tbl) tmp)))
1022 (setq tbl (nreverse tmp)))
1023 (when vlines
1024 (setq tbl (mapcar (lambda (x)
1025 (if (eq 'hline x) x (cons "" x)))
1026 tbl))
1027 (setq tbl (append tbl (list (cons "/" (make-list nfields "<>"))))))
1028 (setq pos (point))
1029 (insert (org-listtable-to-string tbl))
1030 (when (plist-get params :width)
1031 (insert "\n|" (mapconcat (lambda (x) (format "<%d>" (max 3 x)))
1032 org-columns-current-widths "|")))
1033 (goto-char pos)
1034 (org-table-align))))
1035
1036(defun org-listtable-to-string (tbl)
1037 "Convert a listtable TBL to a string that contains the Org-mode table.
1038The table still need to be alligned. The resulting string has no leading
1039and tailing newline characters."
1040 (mapconcat
1041 (lambda (x)
1042 (cond
1043 ((listp x)
1044 (concat "|" (mapconcat 'identity x "|") "|"))
1045 ((eq x 'hline) "|-|")
1046 (t (error "Garbage in listtable: %s" x))))
1047 tbl "\n"))
1048
1049(defun org-insert-columns-dblock ()
1050 "Create a dynamic block capturing a column view table."
1051 (interactive)
1052 (let ((defaults '(:name "columnview" :hlines 1))
1053 (id (completing-read
1054 "Capture columns (local, global, entry with :ID: property) [local]: "
1055 (append '(("global") ("local"))
1056 (mapcar 'list (org-property-values "ID"))))))
1057 (if (equal id "") (setq id 'local))
1058 (if (equal id "global") (setq id 'global))
1059 (setq defaults (append defaults (list :id id)))
1060 (org-create-dblock defaults)
1061 (org-update-dblock)))
1062
1063;;; Column view in the agenda
1064
1065(defvar org-agenda-view-columns-initially nil
1066 "When set, switch to columns view immediately after creating the agenda.")
1067
1068(defvar org-agenda-columns-show-summaries) ; defined in org-agenda.el
1069(defvar org-agenda-columns-compute-summary-properties); defined in org-agenda.el
1070(defvar org-agenda-columns-add-appointments-to-effort-sum); as well
1071
1072(defun org-agenda-columns ()
1073 "Turn on or update column view in the agenda."
1074 (interactive)
1075 (org-verify-version 'columns)
1076 (org-columns-remove-overlays)
1077 (move-marker org-columns-begin-marker (point))
1078 (let (fmt cache maxwidths m p a d)
1079 (cond
1080 ((and (boundp 'org-agenda-overriding-columns-format)
1081 org-agenda-overriding-columns-format)
1082 (setq fmt org-agenda-overriding-columns-format)
1083 (org-set-local 'org-agenda-overriding-columns-format fmt))
1084 ((setq m (get-text-property (point-at-bol) 'org-hd-marker))
1085 (setq fmt (or (org-entry-get m "COLUMNS" t)
1086 (with-current-buffer (marker-buffer m)
1087 org-columns-default-format))))
1088 ((and (boundp 'org-columns-current-fmt)
1089 (local-variable-p 'org-columns-current-fmt)
1090 org-columns-current-fmt)
1091 (setq fmt org-columns-current-fmt))
1092 ((setq m (next-single-property-change (point-min) 'org-hd-marker))
1093 (setq m (get-text-property m 'org-hd-marker))
1094 (setq fmt (or (org-entry-get m "COLUMNS" t)
1095 (with-current-buffer (marker-buffer m)
1096 org-columns-default-format)))))
1097 (setq fmt (or fmt org-columns-default-format))
1098 (org-set-local 'org-columns-current-fmt fmt)
1099 (org-columns-compile-format fmt)
1100 (when org-agenda-columns-compute-summary-properties
1101 (org-agenda-colview-compute org-columns-current-fmt-compiled))
1102 (save-excursion
1103 ;; Get and cache the properties
1104 (goto-char (point-min))
1105 (while (not (eobp))
1106 (when (setq m (or (get-text-property (point) 'org-hd-marker)
1107 (get-text-property (point) 'org-marker)))
1108 (setq p (org-entry-properties m))
1109
1110 (when (or (not (setq a (assoc org-effort-property p)))
1111 (not (string-match "\\S-" (or (cdr a) ""))))
1112 ;; OK, the property is not defined. Use appointment duration?
1113 (when (and org-agenda-columns-add-appointments-to-effort-sum
1114 (setq d (get-text-property (point) 'duration)))
1115 (setq d (org-minutes-to-hh:mm-string d))
1116 (put-text-property 0 (length d) 'face 'org-warning d)
1117 (push (cons org-effort-property d) p)))
1118 (push (cons (org-current-line) p) cache))
1119 (beginning-of-line 2))
1120 (when cache
1121 (setq maxwidths (org-columns-get-autowidth-alist fmt cache))
1122 (org-set-local 'org-columns-current-maxwidths maxwidths)
1123 (org-columns-display-here-title)
1124 (when (org-set-local 'org-columns-flyspell-was-active
1125 (org-bound-and-true-p flyspell-mode))
1126 (flyspell-mode 0))
1127 (mapc (lambda (x)
1128 (goto-line (car x))
1129 (org-columns-display-here (cdr x)))
1130 cache)
1131 (when org-agenda-columns-show-summaries
1132 (org-agenda-colview-summarize cache))))))
1133
1134(defun org-agenda-colview-summarize (cache)
1135 "Summarize the summarizable columns in column view in the agenda.
1136This will add overlays to the date lines, to show the summary for each day."
1137 (let* ((fmt (mapcar (lambda (x)
1138 (list (car x) (if (equal (car x) "CLOCKSUM")
1139 'add_times (nth 4 x))))
1140 org-columns-current-fmt-compiled))
1141 line c c1 stype props lsum entries prop v)
1142 (catch 'exit
1143 (when (delq nil (mapcar 'cadr fmt))
1144 ;; OK, at least one summation column, it makes sense to try this
1145 (goto-char (point-max))
1146 (while t
1147 (when (or (get-text-property (point) 'org-date-line)
1148 (eq (get-text-property (point) 'face)
1149 'org-agenda-structure))
1150 ;; OK, this is a date line that should be used
1151 (setq line (org-current-line))
1152 (setq entries nil c cache cache nil)
1153 (while (setq c1 (pop c))
1154 (if (> (car c1) line)
1155 (push c1 entries)
1156 (push c1 cache)))
1157 ;; now ENTRIES are the ones we want to use, CACHE is the rest
1158 ;; Compute the summaries for the properties we want,
1159 ;; set nil properties for the rest.
1160 (when (setq entries (mapcar 'cdr entries))
1161 (setq props
1162 (mapcar
1163 (lambda (f)
1164 (setq prop (car f) stype (nth 1 f))
1165 (cond
1166 ((equal prop "ITEM")
1167 (cons prop (buffer-substring (point-at-bol)
1168 (point-at-eol))))
1169 ((not stype) (cons prop ""))
1170 (t
1171 ;; do the summary
1172 (setq lsum 0)
1173 (mapc (lambda (x)
1174 (setq v (cdr (assoc prop x)))
1175 (if v (setq lsum (+ lsum
1176 (org-column-string-to-number
1177 v stype)))))
1178 entries)
1179 (setq lsum (org-columns-number-to-string lsum stype))
1180 (put-text-property
1181 0 (length lsum) 'face 'bold lsum)
1182 (cons prop lsum))))
1183 fmt))
1184 (org-columns-display-here props)
1185 (org-set-local 'org-agenda-columns-active t)))
1186 (if (bobp) (throw 'exit t))
1187 (beginning-of-line 0))))))
1188
1189(defun org-agenda-colview-compute (fmt)
1190 "Compute the relevant columns in the contributing source buffers."
1191 (let ((files org-agenda-contributing-files)
1192 (org-columns-begin-marker (make-marker))
1193 (org-columns-top-level-marker (make-marker))
1194 f fm a b)
1195 (while (setq f (pop files))
1196 (setq b (find-buffer-visiting f))
1197 (with-current-buffer (or (buffer-base-buffer b) b)
1198 (save-excursion
1199 (save-restriction
1200 (widen)
1201 (org-unmodified
1202 (remove-text-properties (point-min) (point-max)
1203 '(org-summaries t)))
1204 (goto-char (point-min))
1205 (org-columns-get-format-and-top-level)
1206 (while (setq fm (pop fmt))
1207 (if (equal (car fm) "CLOCKSUM")
1208 (org-clock-sum)
1209 (when (and (nth 4 fm)
1210 (setq a (assoc (car fm)
1211 org-columns-current-fmt-compiled))
1212 (equal (nth 4 a) (nth 4 fm)))
1213 (org-columns-compute (car fm)))))))))))
1214
1215(provide 'org-colview)
1216
1217;;; org-colview.el ends here
1218
88ac7b50 1219;; arch-tag: 61f5128d-747c-4983-9479-e3871fa3d73c