Merge changes from emacs-23 branch
[bpt/emacs.git] / lisp / org / org-colview.el
index 3daef2a..c4f18c7 100644 (file)
@@ -1,11 +1,12 @@
 ;;; org-colview.el --- Column View in Org-mode
 
-;; Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;; Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010
+;;   Free Software Foundation, Inc.
 
 ;; Author: Carsten Dominik <carsten at orgmode dot org>
 ;; Keywords: outlines, hypermedia, calendar, wp
 ;; Homepage: http://orgmode.org
-;; Version: 6.13
+;; Version: 7.4
 ;;
 ;; This file is part of GNU Emacs.
 ;;
@@ -25,7 +26,7 @@
 ;;
 ;;; Commentary:
 
-;; This file contains the face definitons for Org.
+;; This file contains the column view for Org.
 
 ;;; Code:
 
 (require 'org)
 
 (declare-function org-agenda-redo "org-agenda" ())
+(declare-function org-agenda-do-context-action "org-agenda" ())
+
+(when (featurep 'xemacs)
+  (error "Do not load this file into XEmacs, use 'org-colview-xemacs.el'."))
 
 ;;; Column View
 
@@ -80,8 +85,28 @@ This is the compiled version of the format.")
 (org-defkey org-columns-map "\M-b" 'backward-char)
 (org-defkey org-columns-map "a" 'org-columns-edit-allowed)
 (org-defkey org-columns-map "s" 'org-columns-edit-attributes)
-(org-defkey org-columns-map "\M-f" (lambda () (interactive) (goto-char (1+ (point)))))
-(org-defkey org-columns-map [right] (lambda () (interactive) (goto-char (1+ (point)))))
+(org-defkey org-columns-map "\M-f"
+           (lambda () (interactive) (goto-char (1+ (point)))))
+(org-defkey org-columns-map [right]
+           (lambda () (interactive) (goto-char (1+ (point)))))
+(org-defkey org-columns-map [down]
+           (lambda () (interactive)
+             (let ((col (current-column)))
+               (beginning-of-line 2)
+               (while (and (org-invisible-p2) (not (eobp)))
+                 (beginning-of-line 2))
+               (move-to-column col)
+               (if (eq major-mode 'org-agenda-mode)
+                   (org-agenda-do-context-action)))))
+(org-defkey org-columns-map [up]
+           (lambda () (interactive)
+             (let ((col (current-column)))
+               (beginning-of-line 0)
+               (while (and (org-invisible-p2) (not (bobp)))
+                 (beginning-of-line 0))
+               (move-to-column col)
+               (if (eq major-mode 'org-agenda-mode)
+                   (org-agenda-do-context-action)))))
 (org-defkey org-columns-map [(shift right)] 'org-columns-next-allowed-value)
 (org-defkey org-columns-map "n" 'org-columns-next-allowed-value)
 (org-defkey org-columns-map [(shift left)] 'org-columns-previous-allowed-value)
@@ -94,8 +119,8 @@ This is the compiled version of the format.")
 (org-defkey org-columns-map [(shift meta left)] 'org-columns-delete)
 (dotimes (i 10)
   (org-defkey org-columns-map (number-to-string i)
-              `(lambda () (interactive)
-                 (org-columns-next-allowed-value nil ,i))))
+             `(lambda () (interactive)
+                (org-columns-next-allowed-value nil ,i))))
 
 (easy-menu-define org-columns-menu org-columns-map "Org Column Menu"
   '("Column"
@@ -124,8 +149,8 @@ This is the compiled version of the format.")
 
 (defun org-columns-new-overlay (beg end &optional string face)
   "Create a new column overlay and add it to the list."
-  (let ((ov (org-make-overlay beg end)))
-    (org-overlay-put ov 'face (or face 'secondary-selection))
+  (let ((ov (make-overlay beg end)))
+    (overlay-put ov 'face (or face 'secondary-selection))
     (org-overlay-display ov string face)
     (push ov org-columns-overlays)
     ov))
@@ -148,12 +173,12 @@ This is the compiled version of the format.")
         (face1 (list color 'org-agenda-column-dateline ref-face))
         (pl (or (get-text-property (point-at-bol) 'prefix-length) 0))
         (cphr (get-text-property (point-at-bol) 'org-complex-heading-regexp))
-        pom property ass width f string ov column val modval s1 s2 title)
+        pom property ass width f string ov column val modval s2 title calc)
     ;; Check if the entry is in another buffer.
     (unless props
       (if (eq major-mode 'org-agenda-mode)
-         (setq pom (or (get-text-property (point) 'org-hd-marker)
-                       (get-text-property (point) 'org-marker))
+         (setq pom (or (org-get-at-bol 'org-hd-marker)
+                       (org-get-at-bol 'org-marker))
                props (if pom (org-entry-properties pom) nil))
        (setq props (org-entry-properties nil))))
     ;; Walk the format
@@ -172,55 +197,66 @@ This is the compiled version of the format.")
                      (nth 2 column)
                      (length property))
            f (format "%%-%d.%ds | " width width)
+           calc (nth 7 column)
            val (or (cdr ass) "")
-           modval (or (and org-columns-modify-value-for-display-function
-                           (functionp
-                            org-columns-modify-value-for-display-function)
-                           (funcall
-                            org-columns-modify-value-for-display-function
-                            title val))
-                      (if (equal property "ITEM")
-                          (if (org-mode-p)
-                              (org-columns-cleanup-item
-                               val org-columns-current-fmt-compiled)
-                            (org-agenda-columns-cleanup-item
-                             val pl cphr org-columns-current-fmt-compiled)))))
+           modval (cond ((and org-columns-modify-value-for-display-function
+                              (functionp
+                               org-columns-modify-value-for-display-function))
+                         (funcall org-columns-modify-value-for-display-function
+                                  title val))
+                        ((equal property "ITEM")
+                         (if (org-mode-p)
+                             (org-columns-cleanup-item
+                              val org-columns-current-fmt-compiled)
+                           (org-agenda-columns-cleanup-item
+                            val pl cphr org-columns-current-fmt-compiled)))
+                        ((and calc (functionp calc)
+                              (not (string= val ""))
+                              (not (get-text-property 0 'org-computed val)))
+                         (org-columns-number-to-string
+                          (funcall calc (org-columns-string-to-number
+                                         val (nth 4 column)))
+                          (nth 4 column)))))
       (setq s2 (org-columns-add-ellipses (or modval val) width))
       (setq string (format f s2))
       ;; Create the overlay
       (org-unmodified
        (setq ov (org-columns-new-overlay
                 beg (setq beg (1+ beg)) string (if dateline face1 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))
-       (org-overlay-put ov 'org-columns-value-modified modval)
-       (org-overlay-put ov 'org-columns-pom pom)
-       (org-overlay-put ov 'org-columns-format f))
+       (overlay-put ov 'keymap org-columns-map)
+       (overlay-put ov 'org-columns-key property)
+       (overlay-put ov 'org-columns-value (cdr ass))
+       (overlay-put ov 'org-columns-value-modified modval)
+       (overlay-put ov 'org-columns-pom pom)
+       (overlay-put ov 'org-columns-format f)
+       (overlay-put ov 'line-prefix "")
+       (overlay-put ov 'wrap-prefix ""))
       (if (or (not (char-after beg))
              (equal (char-after beg) ?\n))
          (let ((inhibit-read-only t))
            (save-excursion
              (goto-char beg)
              (org-unmodified (insert " ")))))) ;; FIXME: add props and remove later?
-    ;; Make the rest of the line disappear.
-    (org-unmodified
-     (setq ov (org-columns-new-overlay beg (point-at-eol)))
-     (org-overlay-put ov 'invisible t)
-     (org-overlay-put ov 'keymap org-columns-map)
-     (org-overlay-put ov 'intangible t)
-     (push ov org-columns-overlays)
-     (setq ov (org-make-overlay (1- (point-at-eol)) (1+ (point-at-eol))))
-     (org-overlay-put ov 'keymap org-columns-map)
-     (push ov org-columns-overlays)
-     (let ((inhibit-read-only t))
-       (put-text-property (max (point-min) (1- (point-at-bol)))
+      ;; Make the rest of the line disappear.
+      (org-unmodified
+       (setq ov (org-columns-new-overlay beg (point-at-eol)))
+       (overlay-put ov 'invisible t)
+       (overlay-put ov 'keymap org-columns-map)
+       (overlay-put ov 'intangible t)
+       (overlay-put ov 'line-prefix "")
+       (overlay-put ov 'wrap-prefix "")
+       (push ov org-columns-overlays)
+       (setq ov (make-overlay (1- (point-at-eol)) (1+ (point-at-eol))))
+       (overlay-put ov 'keymap org-columns-map)
+       (push ov org-columns-overlays)
+       (let ((inhibit-read-only t))
+        (put-text-property (max (point-min) (1- (point-at-bol)))
                          (min (point-max) (1+ (point-at-eol)))
                          'read-only "Type `e' to edit property")))))
 
 (defun org-columns-add-ellipses (string width)
   "Truncate STRING with WIDTH characters, with ellipses."
-  (cond 
+  (cond
    ((<= (length string) width) string)
    ((<= width (length org-columns-ellipses))
     (substring org-columns-ellipses 0 width))
@@ -228,7 +264,7 @@ This is the compiled version of the format.")
              org-columns-ellipses))))
 
 (defvar org-columns-full-header-line-format nil
-  "Fthe full header line format, will be shifted by horizontal scrolling." )
+  "The full header line format, will be shifted by horizontal scrolling." )
 (defvar org-previous-header-line-format nil
   "The header line format before column view was turned on.")
 (defvar org-columns-inhibit-recalculation nil
@@ -240,6 +276,7 @@ for the duration of the command.")
 
 (defvar header-line-format)
 (defvar org-columns-previous-hscroll 0)
+
 (defun org-columns-display-here-title ()
   "Overlay the newline before the current line with the table title."
   (interactive)
@@ -268,7 +305,7 @@ for the duration of the command.")
     (org-add-hook 'post-command-hook 'org-columns-hscoll-title nil 'local)))
 
 (defun org-columns-hscoll-title ()
-  "Set the header-line-format so that it scrolls along with the table."
+  "Set the `header-line-format' so that it scrolls along with the table."
   (sit-for .0001) ; need to force a redisplay to update window-hscroll
   (when (not (= (window-hscroll) org-columns-previous-hscroll))
     (setq header-line-format
@@ -278,6 +315,9 @@ for the duration of the command.")
          org-columns-previous-hscroll (window-hscroll))
     (force-mode-line-update)))
 
+(defvar org-colview-initial-truncate-line-value nil
+  "Remember the value of `truncate-lines' across colview.")
+
 (defun org-columns-remove-overlays ()
   "Remove all currently active column overlays."
   (interactive)
@@ -290,12 +330,14 @@ for the duration of the command.")
       (move-marker org-columns-begin-marker nil)
       (move-marker org-columns-top-level-marker nil)
       (org-unmodified
-       (mapc 'org-delete-overlay org-columns-overlays)
+       (mapc 'delete-overlay org-columns-overlays)
        (setq org-columns-overlays nil)
        (let ((inhibit-read-only t))
         (remove-text-properties (point-min) (point-max) '(read-only t))))
       (when org-columns-flyspell-was-active
-       (flyspell-mode 1)))))
+       (flyspell-mode 1))
+      (when (local-variable-p 'org-colview-initial-truncate-line-value)
+       (setq truncate-lines org-colview-initial-truncate-line-value)))))
 
 (defun org-columns-cleanup-item (item fmt)
   "Remove from ITEM what is a column in the format FMT."
@@ -325,8 +367,9 @@ for the duration of the command.")
   s)
 
 (defvar org-agenda-columns-remove-prefix-from-item)
+
 (defun org-agenda-columns-cleanup-item (item pl cphr fmt)
-  "Cleanup the tiem property for agenda column view.
+  "Cleanup the time property for agenda column view.
 See also the variable `org-agenda-columns-remove-prefix-from-item'."
   (let* ((org-complex-heading-regexp cphr)
         (prefix (substring item 0 pl))
@@ -344,6 +387,7 @@ See also the variable `org-agenda-columns-remove-prefix-from-item'."
     (message "Value is: %s" (or value ""))))
 
 (defvar org-agenda-columns-active) ;; defined in org-agenda.el
+
 (defun org-columns-quit ()
   "Remove the column overlays and in this way exit column editing."
   (interactive)
@@ -382,8 +426,7 @@ If yes, throw an error indicating that changing it does not make sense."
 Where possible, use the standard interface for changing this line."
   (interactive)
   (org-columns-check-computed)
-  (let* ((external-key key)
-        (col (current-column))
+  (let* ((col (current-column))
         (key (or key (get-char-property (point) 'org-columns-key)))
         (value (get-char-property (point) 'org-columns-value))
         (bol (point-at-bol)) (eol (point-at-eol))
@@ -396,6 +439,7 @@ Where possible, use the standard interface for changing this line."
                                   (<= (overlay-start x) eol)
                                   x))
                            org-columns-overlays)))
+        (org-columns-time (time-to-number-of-days (current-time)))
         nval eval allowed)
     (cond
      ((equal key "CLOCKSUM")
@@ -404,10 +448,9 @@ Where possible, use the standard interface for changing this line."
       (setq eval '(org-with-point-at pom
                    (org-edit-headline))))
      ((equal key "TODO")
-      (setq eval '(org-with-point-at pom
-                   (let ((current-prefix-arg
-                          (if external-key current-prefix-arg '(4))))
-                     (call-interactively 'org-todo)))))
+      (setq eval '(org-with-point-at
+                  pom
+                  (call-interactively 'org-todo))))
      ((equal key "PRIORITY")
       (setq eval '(org-with-point-at pom
                    (call-interactively 'org-priority))))
@@ -423,10 +466,16 @@ Where possible, use the standard interface for changing this line."
      ((equal key "SCHEDULED")
       (setq eval '(org-with-point-at pom
                    (call-interactively 'org-schedule))))
+     ((equal key "BEAMER_env")
+      (setq eval '(org-with-point-at pom
+                   (call-interactively 'org-beamer-select-environment))))
      (t
       (setq allowed (org-property-get-allowed-values pom key 'table))
       (if allowed
-         (setq nval (org-ido-completing-read "Value: " allowed nil t))
+         (setq nval (org-icompleting-read
+                     "Value: " allowed nil
+                     (not (get-text-property 0 'org-unrestricted
+                                             (caar allowed)))))
        (setq nval (read-string "Edit: " value)))
       (setq nval (org-trim nval))
       (when (not (equal nval value))
@@ -453,7 +502,7 @@ Where possible, use the standard interface for changing this line."
              (progn
                (setq org-columns-overlays
                      (org-delete-all line-overlays org-columns-overlays))
-               (mapc 'org-delete-overlay line-overlays)
+               (mapc 'delete-overlay line-overlays)
                (org-columns-eval eval))
            (org-columns-display-here)))
        (org-move-to-column col)
@@ -470,7 +519,7 @@ Where possible, use the standard interface for changing this line."
          (txt (match-string 3))
          (post "")
          txt2)
-      (if (string-match (org-re "[ \t]+:[[:alnum:]:_@]+:[ \t]*$") txt)
+      (if (string-match (org-re "[ \t]+:[[:alnum:]:_@#%]+:[ \t]*$") txt)
          (setq post (match-string 0 txt)
                txt (substring txt 0 (match-beginning 0))))
       (setq txt2 (read-string "Edit: " txt))
@@ -483,8 +532,8 @@ 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* ((pom (or (get-text-property (point-at-bol) 'org-marker)
-                 (get-text-property (point-at-bol) 'org-hd-marker)
+  (let* ((pom (or (org-get-at-bol 'org-marker)
+                 (org-get-at-bol 'org-hd-marker)
                  (point)))
         (key (get-char-property (point) 'org-columns-key))
         (key1 (concat key "_ALL"))
@@ -582,7 +631,7 @@ an integer, select that value."
            (progn
              (setq org-columns-overlays
                    (org-delete-all line-overlays org-columns-overlays))
-             (mapc 'org-delete-overlay line-overlays)
+             (mapc 'delete-overlay line-overlays)
              (org-columns-eval '(org-entry-put pom key nval)))
          (org-columns-display-here)))
       (org-move-to-column col)
@@ -594,7 +643,7 @@ an integer, select that value."
 This respects the format of the time stamp in S, active or non-active,
 and also including time or not.  S must be just a time stamp, no text
 around it."
-  (when (string-match (concat "^" org-ts-regexp3 "$") s)
+  (when (and s (string-match (concat "^" org-ts-regexp3 "$") s))
     (let* ((time (org-parse-time-string s 'nodefaults))
           (active (equal (string-to-char s) ?<))
           (fmt (funcall (if (nth 1 time) 'cdr 'car) org-time-stamp-formats))
@@ -641,7 +690,8 @@ around it."
   (org-verify-version 'columns)
   (org-columns-remove-overlays)
   (move-marker org-columns-begin-marker (point))
-  (let (beg end fmt cache maxwidths)
+  (let ((org-columns-time (time-to-number-of-days (current-time)))
+       beg end fmt cache maxwidths)
     (setq fmt (org-columns-get-format-and-top-level))
     (save-excursion
       (goto-char org-columns-top-level-marker)
@@ -658,7 +708,10 @@ around it."
            (narrow-to-region beg end)
            (org-clock-sum))))
       (while (re-search-forward (concat "^" outline-regexp) end t)
-       (push (cons (org-current-line) (org-entry-properties)) cache))
+       (if (and org-columns-skip-archived-trees
+                (looking-at (concat ".*:" org-archive-tag ":")))
+           (org-end-of-subtree t)
+         (push (cons (org-current-line) (org-entry-properties)) cache)))
       (when cache
        (setq maxwidths (org-columns-get-autowidth-alist fmt cache))
        (org-set-local 'org-columns-current-maxwidths maxwidths)
@@ -666,17 +719,60 @@ around it."
        (when (org-set-local 'org-columns-flyspell-was-active
                             (org-bound-and-true-p flyspell-mode))
          (flyspell-mode 0))
+       (unless (local-variable-p 'org-colview-initial-truncate-line-value)
+         (org-set-local 'org-colview-initial-truncate-line-value
+                        truncate-lines))
+       (setq truncate-lines t)
        (mapc (lambda (x)
-               (goto-line (car x))
+               (org-goto-line (car x))
                (org-columns-display-here (cdr x)))
              cache)))))
 
-(defun org-columns-new (&optional prop title width op fmt &rest rest)
+(eval-when-compile (defvar org-columns-time))
+
+(defvar org-columns-compile-map
+  '(("none" none +)
+    (":" add_times +)
+    ("+" add_numbers +)
+    ("$" currency +)
+    ("X" checkbox +)
+    ("X/" checkbox-n-of-m +)
+    ("X%" checkbox-percent +)
+    ("max" max_numbers max)
+    ("min" min_numbers min)
+    ("mean" mean_numbers
+     (lambda (&rest x) (/ (apply '+ x) (float (length x)))))
+    (":max" max_times max)
+    (":min" min_times min)
+    (":mean" mean_times
+     (lambda (&rest x) (/ (apply '+ x) (float (length x)))))
+    ("@min" min_age min (lambda (x) (- org-columns-time x)))
+    ("@max" max_age max (lambda (x) (- org-columns-time x)))
+    ("@mean" mean_age
+     (lambda (&rest x) (/ (apply '+ x) (float (length x))))
+     (lambda (x) (- org-columns-time x)))
+    ("est+" estimate org-estimate-combine))
+  "Operator <-> format,function,calc  map.
+Used to compile/uncompile columns format and completing read in
+interactive function `org-columns-new'.
+
+operator    string used in #+COLUMNS definition describing the
+           summary type
+format      symbol describing summary type selected interactively in
+           `org-columns-new' and internally in
+           `org-columns-number-to-string' and
+           `org-columns-string-to-number'
+function    called with a list of values as argument to calculate
+           the summary value
+calc        function called on every element before summarizing.  This is
+           optional and should only be specified if needed")
+
+(defun org-columns-new (&optional prop title width op fmt fun &rest rest)
   "Insert a new column, to the left of the current column."
   (interactive)
   (let ((editp (and prop (assoc prop org-columns-current-fmt-compiled)))
        cell)
-    (setq prop (org-ido-completing-read
+    (setq prop (org-icompleting-read
                "Property: " (mapcar 'list (org-buffer-property-keys t nil t))
                nil nil prop))
     (setq title (read-string (concat "Column title [" prop "]: ") (or title prop)))
@@ -684,20 +780,22 @@ around it."
     (if (string-match "\\S-" width)
        (setq width (string-to-number width))
       (setq width nil))
-    (setq fmt (org-ido-completing-read "Summary [none]: "
-                              '(("none") ("add_numbers") ("currency") ("add_times") ("checkbox") ("checkbox-n-of-m") ("checkbox-percent"))
-                              nil t))
-    (if (string-match "\\S-" fmt)
-       (setq fmt (intern fmt))
-      (setq fmt nil))
+    (setq fmt (org-icompleting-read
+              "Summary [none]: "
+              (mapcar (lambda (x) (list (symbol-name (cadr x))))
+                      org-columns-compile-map)
+              nil t))
+    (setq fmt (intern fmt)
+         fun (cdr (assoc fmt (mapcar 'cdr org-columns-compile-map))))
     (if (eq fmt 'none) (setq fmt nil))
     (if editp
        (progn
          (setcar editp prop)
-         (setcdr editp (list title width nil fmt)))
+         (setcdr editp (list title width nil fmt nil fun)))
       (setq cell (nthcdr (1- (current-column))
                         org-columns-current-fmt-compiled))
-      (setcdr cell (cons (list prop title width nil fmt)
+      (setcdr cell (cons (list prop title width nil fmt nil
+                              (car fun) (cadr fun))
                         (cdr cell))))
     (org-columns-store-format)
     (org-columns-redo)))
@@ -737,7 +835,7 @@ around it."
     (org-columns-redo)))
 
 (defun org-columns-narrow (arg)
-  "Make the column nrrower by ARG characters."
+  "Make the column narrower by ARG characters."
   (interactive "p")
   (org-columns-widen (- arg)))
 
@@ -814,7 +912,9 @@ Don't set this, this is meant for dynamic scoping.")
   "Compute all columns that have operators defined."
   (org-unmodified
    (remove-text-properties (point-min) (point-max) '(org-summaries t)))
-  (let ((columns org-columns-current-fmt-compiled) col)
+  (let ((columns org-columns-current-fmt-compiled)
+       (org-columns-time (time-to-number-of-days (current-time)))
+       col)
     (while (setq col (pop columns))
       (when (nth 3 col)
        (save-excursion
@@ -826,15 +926,15 @@ Don't set this, this is meant for dynamic scoping.")
   (let (fmt val pos)
     (save-excursion
       (mapc (lambda (ov)
-             (when (equal (org-overlay-get ov 'org-columns-key) property)
-               (setq pos (org-overlay-start ov))
+             (when (equal (overlay-get ov 'org-columns-key) property)
+               (setq pos (overlay-start ov))
                (goto-char pos)
                (when (setq val (cdr (assoc property
                                            (get-text-property
                                             (point-at-bol) 'org-summaries))))
-                 (setq fmt (org-overlay-get ov 'org-columns-format))
-                 (org-overlay-put ov 'org-columns-value val)
-                 (org-overlay-put ov 'display (format fmt val)))))
+                 (setq fmt (overlay-get ov 'org-columns-format))
+                 (overlay-put ov 'org-columns-value val)
+                 (overlay-put ov 'display (format fmt val)))))
            org-columns-overlays))))
 
 (defun org-columns-compute (property)
@@ -842,12 +942,14 @@ Don't set this, this is meant for dynamic scoping.")
   (interactive)
   (let* ((re (concat "^" outline-regexp))
         (lmax 30) ; Does anyone use deeper levels???
-        (lsum (make-vector lmax 0))
+        (lvals (make-vector lmax nil))
         (lflag (make-vector lmax nil))
         (level 0)
         (ass (assoc property org-columns-current-fmt-compiled))
         (format (nth 4 ass))
         (printf (nth 5 ass))
+        (fun (nth 6 ass))
+        (calc (or (nth 7 ass) 'identity))
         (beg org-columns-top-level-marker)
         last-level val valflag flag end sumpos sum-alist sum str str1 useval)
     (save-excursion
@@ -865,7 +967,8 @@ Don't set this, this is meant for dynamic scoping.")
        (cond
         ((< level last-level)
          ;; put the sum of lower levels here as a property
-         (setq sum (aref lsum last-level)   ; current sum
+         (setq sum (when (aref lvals last-level)
+                     (apply fun (aref lvals last-level)))
                flag (aref lflag last-level) ; any valid entries from children?
                str (org-columns-number-to-string sum format printf)
                str1 (org-add-props (copy-sequence str) nil 'org-computed t 'face 'bold)
@@ -879,21 +982,24 @@ Don't set this, this is meant for dynamic scoping.")
                                  (list 'org-summaries sum-alist))))
          (when (and val (not (equal val (if flag str val))))
            (org-entry-put nil property (if flag str val)))
-         ;; add current to current  level accumulator
+         ;; add current to current level accumulator
          (when (or flag valflag)
-           (aset lsum level (+ (aref lsum level)
-                               (if flag sum (org-column-string-to-number
-                                             (if flag str val) format))))
+           (push (if flag
+                     sum
+                   (funcall calc (org-columns-string-to-number
+                                  (if flag str val) format)))
+                 (aref lvals level))
            (aset lflag level t))
          ;; clear accumulators for deeper levels
          (loop for l from (1+ level) to (1- lmax) do
-               (aset lsum l 0)
+               (aset lvals l nil)
                (aset lflag l nil)))
         ((>= level last-level)
          ;; add what we have here to the accumulator for this level
-         (aset lsum level (+ (aref lsum level)
-                             (org-column-string-to-number (or val "0") format)))
-         (and valflag (aset lflag level t)))
+         (when valflag
+           (push (funcall calc (org-columns-string-to-number val format))
+                 (aref lvals level))
+           (aset lflag level t)))
         (t (error "This should not happen")))))))
 
 (defun org-columns-redo ()
@@ -910,7 +1016,7 @@ Don't set this, this is meant for dynamic scoping.")
          (call-interactively 'org-columns)
        (org-agenda-redo)
        (call-interactively 'org-agenda-columns)))
-    (goto-line line)
+    (org-goto-line line)
     (move-to-column col))
   (message "Recomputing columns...done"))
 
@@ -918,7 +1024,6 @@ Don't set this, this is meant for dynamic scoping.")
   (if (eq major-mode 'org-agenda-mode)
       (error "This command is only allowed in Org-mode buffers")))
 
-
 (defun org-string-to-number (s)
   "Convert string to number, and interpret hh:mm:ss."
   (if (not (string-match ":" s))
@@ -931,7 +1036,9 @@ Don't set this, this is meant for dynamic scoping.")
 (defun org-columns-number-to-string (n fmt &optional printf)
   "Convert a computed column number to a string value, according to FMT."
   (cond
-   ((eq fmt 'add_times)
+   ((memq fmt '(estimate)) (org-estimate-print n printf))
+   ((not (numberp n)) "")
+   ((memq fmt '(add_times max_times min_times mean_times))
     (let* ((h (floor n)) (m (floor (+ 0.5 (* 60 (- n h))))))
       (format org-time-clocksum-format h m)))
    ((eq fmt 'checkbox)
@@ -944,6 +1051,8 @@ Don't set this, this is meant for dynamic scoping.")
    (printf (format printf n))
    ((eq fmt 'currency)
     (format "%.2f" n))
+   ((memq fmt '(min_age max_age mean_age))
+    (org-format-time-period n))
    (t (number-to-string n))))
 
 (defun org-nofm-to-completion (n m &optional percent)
@@ -951,35 +1060,46 @@ Don't set this, this is meant for dynamic scoping.")
       (format "[%d/%d]" n m)
     (format "[%d%%]"(floor (+ 0.5 (* 100. (/ (* 1.0 n) m)))))))
 
-(defun org-column-string-to-number (s fmt)
+
+(defun org-columns-string-to-number (s fmt)
   "Convert a column value to a number that can be used for column computing."
-  (cond
-   ((string-match ":" s)
-    (let ((l (nreverse (org-split-string s ":"))) (sum 0.0))
-      (while l
-       (setq sum (+ (string-to-number (pop l)) (/ sum 60))))
-      sum))
-   ((memq fmt '(checkbox checkbox-n-of-m checkbox-percent))
-    (if (equal s "[X]") 1. 0.000001))
-   (t (string-to-number s))))
+  (if s
+      (cond
+       ((memq fmt '(min_age max_age mean_age))
+        (cond ((string= s "") org-columns-time)
+              ((string-match
+                "\\([0-9]+\\)d \\([0-9]+\\)h \\([0-9]+\\)m \\([0-9]+\\)s"
+                s)
+               (+ (* 60 (+ (* 60 (+ (* 24 (string-to-number (match-string 1 s)))
+                                    (string-to-number (match-string 2 s))))
+                           (string-to-number (match-string 3 s))))
+                  (string-to-number (match-string 4 s))))
+              (t (time-to-number-of-days (apply 'encode-time
+                                                (org-parse-time-string s t))))))
+       ((string-match ":" s)
+        (let ((l (nreverse (org-split-string s ":"))) (sum 0.0))
+          (while l
+            (setq sum (+ (string-to-number (pop l)) (/ sum 60))))
+          sum))
+       ((memq fmt '(checkbox checkbox-n-of-m checkbox-percent))
+        (if (equal s "[X]") 1. 0.000001))
+       ((memq fmt '(estimate)) (org-string-to-estimate s))
+       (t (string-to-number s)))))
 
 (defun org-columns-uncompile-format (cfmt)
   "Turn the compiled columns format back into a string representation."
-  (let ((rtn "") e s prop title op width fmt printf)
+  (let ((rtn "") e s prop title op op-match width fmt printf fun calc)
     (while (setq e (pop cfmt))
       (setq prop (car e)
            title (nth 1 e)
            width (nth 2 e)
            op (nth 3 e)
            fmt (nth 4 e)
-           printf (nth 5 e))
-      (cond
-       ((eq fmt 'add_times) (setq op ":"))
-       ((eq fmt 'checkbox) (setq op "X"))
-       ((eq fmt 'checkbox-n-of-m) (setq op "X/"))
-       ((eq fmt 'checkbox-percent) (setq op "X%"))
-       ((eq fmt 'add_numbers) (setq op "+"))
-       ((eq fmt 'currency) (setq op "$")))
+           printf (nth 5 e)
+           fun (nth 6 e)
+           calc (nth 7 e))
+      (when (setq op-match (rassoc (list fmt fun calc) org-columns-compile-map))
+       (setq op (car op-match)))
       (if (and op printf) (setq op (concat op ";" printf)))
       (if (equal title prop) (setq title nil))
       (setq s (concat "%" (if width (number-to-string width))
@@ -998,8 +1118,10 @@ title        the title field for the columns
 width        the column width in characters, can be nil for automatic
 operator     the operator if any
 format       the output format for computed results, derived from operator
-printf       a printf format for computed values"
-  (let ((start 0) width prop title op f printf)
+printf       a printf format for computed values
+fun          the lisp function to compute summary values, derived from operator
+calc         function to get values from base elements"
+  (let ((start 0) width prop title op op-match f printf fun calc)
     (setq org-columns-current-fmt-compiled nil)
     (while (string-match
            (org-re "%\\([0-9]+\\)?\\([[:alnum:]_-]+\\)\\(?:(\\([^)]+\\))\\)?\\(?:{\\([^}]+\\)}\\)?\\s-*")
@@ -1010,20 +1132,19 @@ printf       a printf format for computed values"
            title (or (match-string 3 fmt) prop)
            op (match-string 4 fmt)
            f nil
-           printf nil)
+           printf nil
+           fun '+
+           calc nil)
       (if width (setq width (string-to-number width)))
       (when (and op (string-match ";" op))
        (setq printf (substring op (match-end 0))
              op (substring op 0 (match-beginning 0))))
-      (cond
-       ((equal op "+")  (setq f 'add_numbers))
-       ((equal op "$")  (setq f 'currency))
-       ((equal op ":")  (setq f 'add_times))
-       ((equal op "X")  (setq f 'checkbox))
-       ((equal op "X/") (setq f 'checkbox-n-of-m))
-       ((equal op "X%") (setq f 'checkbox-percent))
-       )
-      (push (list prop title width op f printf) org-columns-current-fmt-compiled))
+      (when (setq op-match (assoc op org-columns-compile-map))
+       (setq f (cadr op-match)
+             fun (caddr op-match)
+             calc (cadddr op-match)))
+      (push (list prop title width op f printf fun calc)
+           org-columns-current-fmt-compiled))
     (setq org-columns-current-fmt-compiled
          (nreverse org-columns-current-fmt-compiled))))
 
@@ -1040,25 +1161,36 @@ containing the title row and all other rows.  Each row is a list
 of fields."
   (save-excursion
     (let* ((title (mapcar 'cadr org-columns-current-fmt-compiled))
+          (re-comment (concat "\\*+[ \t]+" org-comment-string "\\>"))
+          (re-archive (concat ".*:" org-archive-tag ":"))
           (n (length title)) row tbl)
       (goto-char (point-min))
       (while (re-search-forward "^\\(\\*+\\) " nil t)
-       (when (and (or (null maxlevel)
-                       (>= maxlevel
-                           (if org-odd-levels-only
-                               (/ (1+ (length (match-string 1))) 2)
-                             (length (match-string 1)))))
-                   (get-char-property (match-beginning 0) 'org-columns-key))
-         (setq row nil)
-         (loop for i from 0 to (1- n) do
-               (push (or (get-char-property (+ (match-beginning 0) i) 'org-columns-value-modified)
-                         (get-char-property (+ (match-beginning 0) i) 'org-columns-value)
-                         "")
-                     row))
-         (setq row (nreverse row))
-         (unless (and skip-empty-rows
-                      (eq 1 (length (delete "" (delete-dups row)))))
-           (push row tbl))))
+       (catch 'next
+         (when (and (or (null maxlevel)
+                        (>= maxlevel
+                            (if org-odd-levels-only
+                                (/ (1+ (length (match-string 1))) 2)
+                              (length (match-string 1)))))
+                    (get-char-property (match-beginning 0) 'org-columns-key))
+           (when (save-excursion
+                   (goto-char (point-at-bol))
+                   (or (looking-at re-comment)
+                       (looking-at re-archive)))
+             (org-end-of-subtree t)
+             (throw 'next t))
+           (setq row nil)
+           (loop for i from 0 to (1- n) do
+                 (push
+                  (org-quote-vert
+                   (or (get-char-property (+ (match-beginning 0) i) 'org-columns-value-modified)
+                       (get-char-property (+ (match-beginning 0) i) 'org-columns-value)
+                       ""))
+                  row))
+           (setq row (nreverse row))
+           (unless (and skip-empty-rows
+                        (eq 1 (length (delete "" (delete-dups (copy-sequence row))))))
+             (push row tbl)))))
       (append (list title 'hline) (nreverse tbl)))))
 
 (defun org-dblock-write:columnview (params)
@@ -1067,36 +1199,56 @@ PARAMS is a property list of parameters:
 
 :width    enforce same column widths with <N> specifiers.
 :id       the :ID: property of the entry where the columns view
-          should be built, as a string.  When `local', call locally.
-          When `global' call column view with the cursor at the beginning
-          of the buffer (usually this means that the whole buffer switches
-          to column view).
+         should be built.  When the symbol `local', call locally.
+         When `global' call column view with the cursor at the beginning
+         of the buffer (usually this means that the whole buffer switches
+         to column view).  When \"file:path/to/file.org\", invoke column
+         view at the start of that file.  Otherwise, the ID is located
+         using `org-id-find'.
 :hlines   When t, insert a hline before each item.  When a number, insert
-          a hline before each level <= that number.
+         a hline before each level <= that number.
 :vlines   When t, make each column a colgroup to enforce vertical lines.
 :maxlevel When set to a number, don't capture headlines below this level.
 :skip-empty-rows
-          When t, skip rows where all specifiers other than ITEM are empty."
+         When t, skip rows where all specifiers other than ITEM are empty."
   (let ((pos (move-marker (make-marker) (point)))
        (hlines (plist-get params :hlines))
        (vlines (plist-get params :vlines))
        (maxlevel (plist-get params :maxlevel))
        (content-lines (org-split-string (plist-get params :content) "\n"))
        (skip-empty-rows (plist-get params :skip-empty-rows))
-       tbl id idpos nfields tmp recalc line)
-    (save-excursion
-      (save-restriction
-       (when (setq id (plist-get params :id))
-         (cond ((not id) nil)
-               ((eq id 'global) (goto-char (point-min)))
-               ((eq id 'local)  nil)
-               ((setq idpos (org-find-entry-with-id id))
-                (goto-char idpos))
-               (t (error "Cannot find entry with :ID: %s" id))))
-       (org-columns)
-       (setq tbl (org-columns-capture-view maxlevel skip-empty-rows))
-       (setq nfields (length (car tbl)))
-       (org-columns-quit)))
+       tbl id idpos nfields tmp recalc line
+       id-as-string view-file view-pos)
+    (when (setq id (plist-get params :id))
+      (setq id-as-string (cond ((numberp id) (number-to-string id))
+                              ((symbolp id) (symbol-name id))
+                              ((stringp id) id)
+                              (t "")))
+      (cond ((not id) nil)
+           ((eq id 'global) (setq view-pos (point-min)))
+           ((eq id 'local))
+           ((string-match "^file:\\(.*\\)" id-as-string)
+            (setq view-file (match-string 1 id-as-string)
+                  view-pos 1)
+            (unless (file-exists-p view-file)
+              (error "No such file: \"%s\"" id-as-string)))
+           ((setq idpos (org-find-entry-with-id id))
+            (setq view-pos idpos))
+           ((setq idpos (org-id-find id))
+            (setq view-file (car idpos))
+            (setq view-pos (cdr idpos)))
+           (t (error "Cannot find entry with :ID: %s" id))))
+    (with-current-buffer (if view-file
+                            (get-file-buffer view-file)
+                          (current-buffer))
+      (save-excursion
+       (save-restriction
+         (widen)
+         (goto-char (or view-pos (point)))
+         (org-columns)
+         (setq tbl (org-columns-capture-view maxlevel skip-empty-rows))
+         (setq nfields (length (car tbl)))
+         (org-columns-quit))))
     (goto-char pos)
     (move-marker pos nil)
     (when tbl
@@ -1108,7 +1260,9 @@ PARAMS is a property list of parameters:
            (if (string-match "\\` *\\(\\*+\\)" (caar tbl))
                (if (and (not (eq (car tmp) 'hline))
                         (or (eq hlines t)
-                            (and (numberp hlines) (<= (- (match-end 1) (match-beginning 1)) hlines))))
+                            (and (numberp hlines)
+                                 (<= (- (match-end 1) (match-beginning 1))
+                                     hlines))))
                    (push 'hline tmp)))
            (push (pop tbl) tmp)))
        (setq tbl (nreverse tmp)))
@@ -1128,7 +1282,7 @@ PARAMS is a property list of parameters:
       (while (setq line (pop content-lines))
        (when (string-match "^#" line)
          (insert "\n" line)
-         (when (string-match "^#\\+TBLFM" line)
+         (when (string-match "^[ \t]*#\\+TBLFM" line)
            (setq recalc t))))
       (if recalc
          (progn (goto-char pos) (org-table-recalculate 'all))
@@ -1137,7 +1291,7 @@ PARAMS is a property list of parameters:
 
 (defun org-listtable-to-string (tbl)
   "Convert a listtable TBL to a string that contains the Org-mode table.
-The table still need to be alligned.  The resulting string has no leading
+The table still need to be aligned.  The resulting string has no leading
 and tailing newline characters."
   (mapconcat
    (lambda (x)
@@ -1152,7 +1306,7 @@ and tailing newline characters."
   "Create a dynamic block capturing a column view table."
   (interactive)
   (let ((defaults '(:name "columnview" :hlines 1))
-       (id (org-ido-completing-read
+       (id (org-icompleting-read
             "Capture columns (local, global, entry with :ID: property) [local]: "
             (append '(("global") ("local"))
                     (mapcar 'list (org-property-values "ID"))))))
@@ -1177,13 +1331,14 @@ and tailing newline characters."
   (org-verify-version 'columns)
   (org-columns-remove-overlays)
   (move-marker org-columns-begin-marker (point))
-  (let (fmt cache maxwidths m p a d)
+  (let ((org-columns-time (time-to-number-of-days (current-time)))
+        cache maxwidths m p a d fmt)
     (cond
      ((and (boundp 'org-agenda-overriding-columns-format)
           org-agenda-overriding-columns-format)
       (setq fmt org-agenda-overriding-columns-format)
       (org-set-local 'org-agenda-overriding-columns-format fmt))
-     ((setq m (get-text-property (point-at-bol) 'org-hd-marker))
+     ((setq m (org-get-at-bol 'org-hd-marker))
       (setq fmt (or (org-entry-get m "COLUMNS" t)
                    (with-current-buffer (marker-buffer m)
                      org-columns-default-format))))
@@ -1205,8 +1360,8 @@ and tailing newline characters."
       ;; Get and cache the properties
       (goto-char (point-min))
       (while (not (eobp))
-       (when (setq m (or (get-text-property (point) 'org-hd-marker)
-                         (get-text-property (point) 'org-marker)))
+       (when (setq m (or (org-get-at-bol 'org-hd-marker)
+                         (org-get-at-bol 'org-marker)))
          (setq p (org-entry-properties m))
 
          (when (or (not (setq a (assoc org-effort-property p)))
@@ -1227,7 +1382,7 @@ and tailing newline characters."
                             (org-bound-and-true-p flyspell-mode))
          (flyspell-mode 0))
        (mapc (lambda (x)
-               (goto-line (car x))
+               (org-goto-line (car x))
                (org-columns-display-here (cdr x)))
              cache)
        (when org-agenda-columns-show-summaries
@@ -1237,10 +1392,12 @@ and tailing newline characters."
   "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))))
+                       (if (equal (car x) "CLOCKSUM")
+                           (list "CLOCKSUM" (nth 1 x) (nth 2 x) ":" 'add_times
+                                 nil '+ nil)
+                         x))
                      org-columns-current-fmt-compiled))
-        line c c1 stype props lsum entries prop v)
+        line c c1 stype calc sumfunc props lsum entries prop v title)
     (catch 'exit
       (when (delq nil (mapcar 'cadr fmt))
        ;; OK, at least one summation column, it makes sense to try this
@@ -1263,24 +1420,41 @@ This will add overlays to the date lines, to show the summary for each day."
              (setq props
                    (mapcar
                     (lambda (f)
-                      (setq prop (car f) stype (nth 1 f))
+                      (setq prop (car f)
+                            title (nth 1 f)
+                            stype (nth 4 f)
+                            sumfunc (nth 6 f)
+                            calc (or (nth 7 f) 'identity))
                       (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)
-                        (setq lsum (org-columns-number-to-string lsum stype))
-                        (put-text-property
-                         0 (length lsum) 'face 'bold lsum)
+                       (t ;; do the summary
+                        (setq lsum nil)
+                        (dolist (x entries)
+                          (setq v (cdr (assoc prop x)))
+                          (if v
+                              (push
+                               (funcall
+                                (if (not (get-text-property 0 'org-computed v))
+                                    calc
+                                  'identity)
+                                (org-columns-string-to-number
+                                 v stype))
+                               lsum)))
+                        (setq lsum (remove nil lsum))
+                        (setq lsum
+                              (cond ((> (length lsum) 1)
+                                     (org-columns-number-to-string
+                                      (apply sumfunc lsum) stype))
+                                    ((eq (length lsum) 1)
+                                     (org-columns-number-to-string
+                                      (car lsum) stype))
+                                    (t "")))
+                        (put-text-property 0 (length lsum) 'face 'bold lsum)
+                        (unless (eq calc 'identity)
+                          (put-text-property 0 (length lsum) 'org-computed t lsum))
                         (cons prop lsum))))
                     fmt))
              (org-columns-display-here props 'dateline)
@@ -1314,6 +1488,53 @@ This will add overlays to the date lines, to show the summary for each day."
                           (equal (nth 4 a) (nth 4 fm)))
                  (org-columns-compute (car fm)))))))))))
 
+(defun org-format-time-period (interval)
+  "Convert time in fractional days to days/hours/minutes/seconds."
+  (if (numberp interval)
+    (let* ((days (floor interval))
+          (frac-hours (* 24 (- interval days)))
+          (hours (floor frac-hours))
+          (minutes (floor (* 60 (- frac-hours hours))))
+          (seconds (floor (* 60 (- (* 60 (- frac-hours hours)) minutes)))))
+      (format "%dd %02dh %02dm %02ds" days hours minutes seconds))
+    ""))
+
+(defun org-estimate-mean-and-var (v)
+  "Return the mean and variance of an estimate."
+  (let* ((low (float (car v)))
+         (high (float (cadr v)))
+         (mean (/ (+ low high) 2.0))
+         (var (/ (+ (expt (- mean low) 2.0) (expt (- high mean) 2.0)) 2.0)))
+    (list  mean var)))
+
+(defun org-estimate-combine (&rest el)
+  "Combine a list of estimates, using mean and variance.
+The mean and variance of the result will be the sum of the means
+and variances (respectively) of the individual estimates."
+  (let ((mean 0)
+        (var 0))
+    (mapc (lambda (e)
+              (let ((stats (org-estimate-mean-and-var e)))
+                (setq mean (+ mean (car stats)))
+                (setq var (+ var (cadr stats)))))
+            el)
+    (let ((stdev (sqrt var)))
+      (list (- mean stdev) (+ mean stdev)))))
+
+(defun org-estimate-print (e &optional fmt)
+  "Prepare a string representation of an estimate.
+This formats these numbers as two numbers with a \"-\" between them."
+  (if (null fmt) (set 'fmt "%.0f"))
+  (format "%s" (mapconcat (lambda (n) (format fmt n))  e "-")))
+
+(defun org-string-to-estimate (s)
+  "Convert a string to an estimate.
+The string should be two numbers joined with a \"-\"."
+  (if (string-match "\\(.*\\)-\\(.*\\)" s)
+      (list (string-to-number (match-string 1 s))
+           (string-to-number(match-string 2 s)))
+    (list (string-to-number s) (string-to-number s))))
+
 (provide 'org-colview)
 
 ;; arch-tag: 61f5128d-747c-4983-9479-e3871fa3d73c