瀏覽代碼

Improve adding up time estimates.

Appointment times can now be included into the estimated load for a day.
Carsten Dominik 17 年之前
父節點
當前提交
614ad45fac
共有 5 個文件被更改,包括 262 次插入146 次删除
  1. 7 0
      ChangeLog
  2. 19 7
      lisp/org-agenda.el
  3. 158 131
      lisp/org-colview.el
  4. 1 0
      lisp/org-exp.el
  5. 77 8
      lisp/org.el

+ 7 - 0
ChangeLog

@@ -1,7 +1,14 @@
+2008-04-16  Carsten Dominik  <dominik@science.uva.nl>
+
+	* lisp/org-colview.el (org-columns-compute): Only write property
+	value if it has changed.  This avoids raising the
+	buffer-change-flag unnecessarily.
+
 2008-04-15  Carsten Dominik  <dominik@science.uva.nl>
 2008-04-15  Carsten Dominik  <dominik@science.uva.nl>
 
 
 	* lisp/org-agenda.el (org-agenda-columns-show-summaries)
 	* lisp/org-agenda.el (org-agenda-columns-show-summaries)
 	(org-agenda-columns-compute-summary-properties): New options.
 	(org-agenda-columns-compute-summary-properties): New options.
+	(org-format-agenda-item): Compute the duration of the item.
 
 
 	* lisp/org-colview.el (org-agenda-colview-summarize)
 	* lisp/org-colview.el (org-agenda-colview-summarize)
 	(org-agenda-colview-compute): New functions.
 	(org-agenda-colview-compute): New functions.

+ 19 - 7
lisp/org-agenda.el

@@ -3527,10 +3527,11 @@ Any match of REMOVE-RE will be removed from TXT."
 	   time    ; time and tag are needed for the eval of the prefix format
 	   time    ; time and tag are needed for the eval of the prefix format
 	   (ts (if dotime (concat (if (stringp dotime) dotime "") txt)))
 	   (ts (if dotime (concat (if (stringp dotime) dotime "") txt)))
 	   (time-of-day (and dotime (org-get-time-of-day ts)))
 	   (time-of-day (and dotime (org-get-time-of-day ts)))
-	   stamp plain s0 s1 s2 rtn srp)
+	   stamp plain s0 s1 s2 rtn srp
+	   duration)
       (and (org-mode-p) buffer-file-name
       (and (org-mode-p) buffer-file-name
 	   (add-to-list 'org-agenda-contributing-files buffer-file-name))
 	   (add-to-list 'org-agenda-contributing-files buffer-file-name))
-      (when (and dotime time-of-day org-prefix-has-time)
+      (when (and dotime time-of-day)
 	;; Extract starting and ending time and move them to prefix
 	;; Extract starting and ending time and move them to prefix
 	(when (or (setq stamp (string-match org-stamp-time-of-day-regexp ts))
 	(when (or (setq stamp (string-match org-stamp-time-of-day-regexp ts))
 		  (setq plain (string-match org-plain-time-of-day-regexp ts)))
 		  (setq plain (string-match org-plain-time-of-day-regexp ts)))
@@ -3542,7 +3543,8 @@ Any match of REMOVE-RE will be removed from TXT."
 	  ;; If the times are in TXT (not in DOTIMES), and the prefix will list
 	  ;; If the times are in TXT (not in DOTIMES), and the prefix will list
 	  ;; them, we might want to remove them there to avoid duplication.
 	  ;; them, we might want to remove them there to avoid duplication.
 	  ;; The user can turn this off with a variable.
 	  ;; The user can turn this off with a variable.
-	  (if (and org-agenda-remove-times-when-in-prefix (or stamp plain)
+	  (if (and org-prefix-has-time
+		   org-agenda-remove-times-when-in-prefix (or stamp plain)
 		   (string-match (concat (regexp-quote s0) " *") txt)
 		   (string-match (concat (regexp-quote s0) " *") txt)
 		   (not (equal ?\] (string-to-char (substring txt (match-end 0)))))
 		   (not (equal ?\] (string-to-char (substring txt (match-end 0)))))
 		   (if (eq org-agenda-remove-times-when-in-prefix 'beg)
 		   (if (eq org-agenda-remove-times-when-in-prefix 'beg)
@@ -3551,7 +3553,18 @@ Any match of REMOVE-RE will be removed from TXT."
 	      (setq txt (replace-match "" nil nil txt))))
 	      (setq txt (replace-match "" nil nil txt))))
 	;; Normalize the time(s) to 24 hour
 	;; Normalize the time(s) to 24 hour
 	(if s1 (setq s1 (org-get-time-of-day s1 'string t)))
 	(if s1 (setq s1 (org-get-time-of-day s1 'string t)))
-	(if s2 (setq s2 (org-get-time-of-day s2 'string t))))
+	(if s2 (setq s2 (org-get-time-of-day s2 'string t)))
+	;; Compute the duration
+	(when s1
+	  (setq t1 (+ (* 60 (string-to-number (substring s1 0 2)))
+		      (string-to-number (substring s1 3)))
+		t2 (cond
+		    (s2 (+ (* 60 (string-to-number (substring s2 0 2)))
+			   (string-to-number (substring s2 3))))
+		    (org-agenda-default-appointment-duration
+		     (+ t1 org-agenda-default-appointment-duration))
+		    (t nil)))
+	  (setq duration (if t2 (- t2 t1)))))      
 
 
       (when (and s1 (not s2) org-agenda-default-appointment-duration
       (when (and s1 (not s2) org-agenda-default-appointment-duration
 		 (string-match "\\([0-9]+\\):\\([0-9]+\\)" s1))
 		 (string-match "\\([0-9]+\\):\\([0-9]+\\)" s1))
@@ -3597,6 +3610,7 @@ Any match of REMOVE-RE will be removed from TXT."
 	'org-lowest-priority org-lowest-priority
 	'org-lowest-priority org-lowest-priority
 	'prefix-length (- (length rtn) (length txt))
 	'prefix-length (- (length rtn) (length txt))
 	'time-of-day time-of-day
 	'time-of-day time-of-day
+	'duration duration
 	'txt txt
 	'txt txt
 	'time time
 	'time time
 	'extra extra
 	'extra extra
@@ -3882,9 +3896,7 @@ If ERROR is non-nil, throw an error, otherwise just return nil."
   "Exit agenda by removing the window or the buffer."
   "Exit agenda by removing the window or the buffer."
   (interactive)
   (interactive)
   (if org-agenda-columns-active
   (if org-agenda-columns-active
-      (progn
-	(setq org-agenda-columns-active nil)
-	(org-columns-quit))
+      (org-columns-quit)
     (let ((buf (current-buffer)))
     (let ((buf (current-buffer)))
       (if (not (one-window-p)) (delete-window))
       (if (not (one-window-p)) (delete-window))
       (kill-buffer buf)
       (kill-buffer buf)

+ 158 - 131
lisp/org-colview.el

@@ -171,7 +171,8 @@ This is the compiled version of the format.")
 	    string (format f (or modval val)))
 	    string (format f (or modval val)))
       ;; Create the overlay
       ;; Create the overlay
       (org-unmodified
       (org-unmodified
-       (setq ov (org-columns-new-overlay beg (setq beg (1+ beg)) string face))
+       (setq ov (org-columns-new-overlay
+		 beg (setq beg (1+ beg)) string face))
        (org-overlay-put ov 'keymap org-columns-map)
        (org-overlay-put ov 'keymap org-columns-map)
        (org-overlay-put ov 'org-columns-key property)
        (org-overlay-put ov 'org-columns-key property)
        (org-overlay-put ov 'org-columns-value (cdr ass))
        (org-overlay-put ov 'org-columns-value (cdr ass))
@@ -290,6 +291,7 @@ This is the compiled version of the format.")
    (let ((inhibit-read-only t))
    (let ((inhibit-read-only t))
      (remove-text-properties (point-min) (point-max) '(read-only t))))
      (remove-text-properties (point-min) (point-max) '(read-only t))))
   (when (eq major-mode 'org-agenda-mode)
   (when (eq major-mode 'org-agenda-mode)
+    (setq org-agenda-columns-active nil)
     (message
     (message
      "Modification not yet reflected in Agenda buffer, use `r' to refresh")))
      "Modification not yet reflected in Agenda buffer, use `r' to refresh")))
 
 
@@ -370,7 +372,8 @@ Where possible, use the standard interface for changing this line."
 	(setq eval '(org-entry-put pom key nval)))))
 	(setq eval '(org-entry-put pom key nval)))))
     (when eval
     (when eval
       (let ((inhibit-read-only t))
       (let ((inhibit-read-only t))
-	(remove-text-properties (max (point-min) (1- bol)) eol '(read-only t))
+	(org-unmodified
+	 (remove-text-properties (max (point-min) (1- bol)) eol '(read-only t)))
 	(unwind-protect
 	(unwind-protect
 	    (progn
 	    (progn
 	      (setq org-columns-overlays
 	      (setq org-columns-overlays
@@ -482,9 +485,12 @@ Where possible, use the standard interface for changing this line."
 	    (org-columns-eval '(org-entry-put pom key nval)))
 	    (org-columns-eval '(org-entry-put pom key nval)))
 	(org-columns-display-here)))
 	(org-columns-display-here)))
     (move-to-column col)
     (move-to-column col)
-    (if (and (org-mode-p)
-	     (nth 3 (assoc key org-columns-current-fmt-compiled)))
-	(org-columns-update key))))
+    (cond
+     ((equal major-mode 'org-agenda-mode)
+      (org-agenda-redo))
+     ((and (org-mode-p)
+	   (nth 3 (assoc key org-columns-current-fmt-compiled)))
+      (org-columns-update key)))))
 
 
 (defun org-verify-version (task)
 (defun org-verify-version (task)
   (cond
   (cond
@@ -667,131 +673,6 @@ display, or in the #+COLUMNS line of the current buffer."
 
 
 (defvar org-overriding-columns-format nil
 (defvar org-overriding-columns-format nil
   "When set, overrides any other definition.")
   "When set, overrides any other definition.")
-(defvar org-agenda-view-columns-initially nil
-  "When set, switch to columns view immediately after creating the agenda.")
-
-(defvar org-agenda-columns-show-summaries) ; defined in org-agenda.el
-(defun org-agenda-columns ()
-  "Turn on column view in the agenda."
-  (interactive)
-  (org-verify-version 'columns)
-  (org-columns-remove-overlays)
-  (move-marker org-columns-begin-marker (point))
-  (let (fmt cache maxwidths m)
-    (cond
-     ((and (local-variable-p 'org-overriding-columns-format)
-	   org-overriding-columns-format)
-      (setq fmt org-overriding-columns-format))
-     ((setq m (get-text-property (point-at-bol) 'org-hd-marker))
-      (setq fmt (or (org-entry-get m "COLUMNS" t)
-		    (with-current-buffer (marker-buffer m)
-		      org-columns-default-format))))
-     ((and (boundp 'org-columns-current-fmt)
-	   (local-variable-p 'org-columns-current-fmt)
-	   org-columns-current-fmt)
-      (setq fmt org-columns-current-fmt))
-     ((setq m (next-single-property-change (point-min) 'org-hd-marker))
-      (setq m (get-text-property m 'org-hd-marker))
-      (setq fmt (or (org-entry-get m "COLUMNS" t)
-		    (with-current-buffer (marker-buffer m)
-		      org-columns-default-format)))))
-    (setq fmt (or fmt org-columns-default-format))
-    (org-set-local 'org-columns-current-fmt fmt)
-    (org-columns-compile-format fmt)
-    (org-agenda-colview-compute org-columns-current-fmt-compiled)
-    (save-excursion
-      ;; Get and cache the properties
-      (goto-char (point-min))
-      (while (not (eobp))
-	(when (setq m (or (get-text-property (point) 'org-hd-marker)
-			  (get-text-property (point) 'org-marker)))
-	  (push (cons (org-current-line) (org-entry-properties m)) cache))
-	(beginning-of-line 2))
-      (when cache
-	(setq maxwidths (org-columns-get-autowidth-alist fmt cache))
-	(org-set-local 'org-columns-current-maxwidths maxwidths)
-	(org-columns-display-here-title)
-	(mapc (lambda (x)
-		(goto-line (car x))
-		(org-columns-display-here (cdr x)))
-	      cache)
-	(when org-agenda-columns-show-summaries
-	  (org-agenda-colview-summarize cache))))))
-
-(defun org-agenda-colview-summarize (cache)
-  "Summarize the summarizable columns in column view in the agenda.
-This will add overlays to the date lines, to show the summary for each day."
-  (let* ((fmt (mapcar (lambda (x)
-			(list (car x) (if (equal (car x) "CLOCKSUM")
-					  'add_times (nth 4 x))))
-		      org-columns-current-fmt-compiled))
-	 line c c1 stype props lsum entries prop v)
-    (when (delq nil (mapcar 'cadr fmt))
-      ;; OK, at least one summation column, it makes sense to try this
-      (goto-char (point-max))
-      (while (not (bobp))
-	(if (not (or (get-text-property (point) 'org-date-line)
-		     (eq (get-text-property (point) 'face)
-			 'org-agenda-structure)))
-	    (beginning-of-line 0)
-	  ;; OK, this is a date line
-	  (setq line (org-current-line))
-	  (setq entries nil c cache cache nil)
-	  (while (setq c1 (pop c))
-	    (if (> (car c1) line)
-		(push c1 entries)
-	      (push c1 cache)))
-	  ;; now ENTRIES are the ones we want to use, CACHE is the rest
-	  ;; Compute the summaries for the properties we want,
-	  ;; set nil properties for the rest.
-	  (when (setq entries (mapcar 'cdr entries))
-	    (setq props
-		  (mapcar
-		   (lambda (f)
-		     (setq prop (car f) stype (nth 1 f))
-		     (cond
-		      ((equal prop "ITEM")
-		       (cons prop (buffer-substring (point-at-bol)
-						    (point-at-eol))))
-		      ((not stype) (cons prop ""))
-		      (t
-		       ;; do the summary
-		       (setq lsum 0)
-		       (mapc (lambda (x)
-			       (setq v (cdr (assoc prop x)))
-			       (if v (setq lsum (+ lsum
-						   (org-column-string-to-number
-						    v stype)))))
-			     entries)
-		       (cons prop (org-columns-number-to-string lsum stype)))))
-		   fmt))
-	    (org-columns-display-here props)
-	    (org-set-local 'org-agenda-columns-active t))
-	  (beginning-of-line 0))))))
-
-(defvar org-agenda-columns-compute-summary-properties); defined in org-agenda.el
-(defun org-agenda-colview-compute (fmt)
-  "Compute the relevant columns in the contributing source buffers."
-  (when org-agenda-columns-compute-summary-properties
-    (let ((files org-agenda-contributing-files)
-	  (org-columns-begin-marker (make-marker))
-	  (org-columns-top-level-marker (make-marker))
-	  f fm a b)
-      (while (setq f (pop files))
-	(setq b (find-buffer-visiting f))
-	(with-current-buffer (or (buffer-base-buffer b) b)
-	  (save-excursion
-	    (save-restriction
-	      (goto-char (point-min))
-	      (org-columns-get-format-and-top-level)
-	      (while (setq fm (pop fmt))
-		(if (equal (car fm) "CLOCKSUM")
-		    (org-clock-sum)
-		  (when (and (nth 4 fm)
-			     (setq a (assoc (car fm)
-					    org-columns-current-fmt-compiled))
-			     (equal (nth 4 a) (nth 4 fm)))
-		    (org-columns-compute (car fm))))))))))))
 
 
 (defun org-columns-get-autowidth-alist (s cache)
 (defun org-columns-get-autowidth-alist (s cache)
   "Derive the maximum column widths from the format and the cache."
   "Derive the maximum column widths from the format and the cache."
@@ -875,7 +756,7 @@ This will add overlays to the date lines, to show the summary for each day."
 	    (org-unmodified
 	    (org-unmodified
 	     (add-text-properties sumpos (1+ sumpos)
 	     (add-text-properties sumpos (1+ sumpos)
 				  (list 'org-summaries sum-alist))))
 				  (list 'org-summaries sum-alist))))
-	  (when val
+	  (when (and val (not (equal val (if flag str val))))
 	    (org-entry-put nil property (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)
 	  (when (or flag valflag)
@@ -1144,6 +1025,152 @@ and tailing newline characters."
     (org-create-dblock defaults)
     (org-create-dblock defaults)
     (org-update-dblock)))
     (org-update-dblock)))
 
 
+;;; Column view in the agenda
+
+(defvar org-agenda-view-columns-initially nil
+  "When set, switch to columns view immediately after creating the agenda.")
+
+(defvar org-agenda-columns-show-summaries) ; defined in org-agenda.el
+
+(defun org-agenda-columns ()
+  "Turn on column view in the agenda."
+  (interactive)
+  (org-verify-version 'columns)
+  (org-columns-remove-overlays)
+  (move-marker org-columns-begin-marker (point))
+  (let (fmt cache maxwidths m p a)
+    (cond
+     ((and (local-variable-p 'org-overriding-columns-format)
+	   org-overriding-columns-format)
+      (setq fmt org-overriding-columns-format))
+     ((setq m (get-text-property (point-at-bol) 'org-hd-marker))
+      (setq fmt (or (org-entry-get m "COLUMNS" t)
+		    (with-current-buffer (marker-buffer m)
+		      org-columns-default-format))))
+     ((and (boundp 'org-columns-current-fmt)
+	   (local-variable-p 'org-columns-current-fmt)
+	   org-columns-current-fmt)
+      (setq fmt org-columns-current-fmt))
+     ((setq m (next-single-property-change (point-min) 'org-hd-marker))
+      (setq m (get-text-property m 'org-hd-marker))
+      (setq fmt (or (org-entry-get m "COLUMNS" t)
+		    (with-current-buffer (marker-buffer m)
+		      org-columns-default-format)))))
+    (setq fmt (or fmt org-columns-default-format))
+    (org-set-local 'org-columns-current-fmt fmt)
+    (org-columns-compile-format fmt)
+    (org-agenda-colview-compute org-columns-current-fmt-compiled)
+    (save-excursion
+      ;; Get and cache the properties
+      (goto-char (point-min))
+      (while (not (eobp))
+	(when (setq m (or (get-text-property (point) 'org-hd-marker)
+			  (get-text-property (point) 'org-marker)))
+	  (setq p (org-entry-properties m))
+
+	  (when (or (not (setq a (assoc org-time-estimates-property p)))
+			 (not (string-match "\\S-" (or (cdr a) ""))))
+	    ;; OK, no property gives us a value
+	    (when (and org-time-estimate-include-appointments
+		       (setq d (get-text-property (point) 'duration)))
+	      (setq d (org-minutes-to-hours d))
+	      (put-text-property 0 (length d) 'face 'org-warning d)
+	      (push (cons org-time-estimates-property d) p)))
+	  (push (cons (org-current-line) p) cache))
+	(beginning-of-line 2))
+      (when cache
+	(setq maxwidths (org-columns-get-autowidth-alist fmt cache))
+	(org-set-local 'org-columns-current-maxwidths maxwidths)
+	(org-columns-display-here-title)
+	(mapc (lambda (x)
+		(goto-line (car x))
+		(org-columns-display-here (cdr x)))
+	      cache)
+	(when org-agenda-columns-show-summaries
+	  (org-agenda-colview-summarize cache))))))
+
+(defun org-agenda-colview-summarize (cache)
+  "Summarize the summarizable columns in column view in the agenda.
+This will add overlays to the date lines, to show the summary for each day."
+  (let* ((fmt (mapcar (lambda (x)
+			(list (car x) (if (equal (car x) "CLOCKSUM")
+					  'add_times (nth 4 x))))
+		      org-columns-current-fmt-compiled))
+	 line c c1 stype props lsum entries prop v)
+    (when (delq nil (mapcar 'cadr fmt))
+      ;; OK, at least one summation column, it makes sense to try this
+      (goto-char (point-max))
+      (while (not (bobp))
+	(if (not (or (get-text-property (point) 'org-date-line)
+		     (eq (get-text-property (point) 'face)
+			 'org-agenda-structure)))
+	    (beginning-of-line 0)
+	  ;; OK, this is a date line
+	  (setq line (org-current-line))
+	  (setq entries nil c cache cache nil)
+	  (while (setq c1 (pop c))
+	    (if (> (car c1) line)
+		(push c1 entries)
+	      (push c1 cache)))
+	  ;; now ENTRIES are the ones we want to use, CACHE is the rest
+	  ;; Compute the summaries for the properties we want,
+	  ;; set nil properties for the rest.
+	  (when (setq entries (mapcar 'cdr entries))
+	    (setq props
+		  (mapcar
+		   (lambda (f)
+		     (setq prop (car f) stype (nth 1 f))
+		     (cond
+		      ((equal prop "ITEM")
+		       (cons prop (buffer-substring (point-at-bol)
+						    (point-at-eol))))
+		      ((not stype) (cons prop ""))
+		      (t
+		       ;; do the summary
+		       (setq lsum 0)
+		       (mapc (lambda (x)
+			       (setq v (cdr (assoc prop x)))
+			       (if v (setq lsum (+ lsum
+						   (org-column-string-to-number
+						    v stype)))))
+			     entries)
+		       (setq lsum (org-columns-number-to-string lsum stype))
+		       (put-text-property
+			0 (length lsum) 'face 'bold lsum)
+		       (cons prop lsum))))
+		   fmt))
+	    (org-columns-display-here props)
+	    (org-set-local 'org-agenda-columns-active t))
+	  (beginning-of-line 0))))))
+
+(defvar org-agenda-columns-compute-summary-properties); defined in org-agenda.el
+(defun org-agenda-colview-compute (fmt)
+  "Compute the relevant columns in the contributing source buffers."
+  (when org-agenda-columns-compute-summary-properties
+    (let ((files org-agenda-contributing-files)
+	  (org-columns-begin-marker (make-marker))
+	  (org-columns-top-level-marker (make-marker))
+	  f fm a b)
+      (while (setq f (pop files))
+	(setq b (find-buffer-visiting f))
+	(with-current-buffer (or (buffer-base-buffer b) b)
+	  (save-excursion
+	    (save-restriction
+	      (widen)
+	      (org-unmodified
+	       (remove-text-properties (point-min) (point-max)
+				       '(org-summaries t)))
+	      (goto-char (point-min))
+	      (org-columns-get-format-and-top-level)
+	      (while (setq fm (pop fmt))
+		(if (equal (car fm) "CLOCKSUM")
+		    (org-clock-sum)
+		  (when (and (nth 4 fm)
+			     (setq a (assoc (car fm)
+					    org-columns-current-fmt-compiled))
+			     (equal (nth 4 a) (nth 4 fm)))
+		    (org-columns-compute (car fm))))))))))))
+
 (provide 'org-colview)
 (provide 'org-colview)
 
 
 ;;; org-colview.el ends here
 ;;; org-colview.el ends here

+ 1 - 0
lisp/org-exp.el

@@ -2474,6 +2474,7 @@ lang=\"%s\" xml:lang=\"%s\">
 				  "\">\\nbsp@</a>")
 				  "\">\\nbsp@</a>")
 			  t t line)))
 			  t t line)))
 	     ((and org-export-with-toc (equal (string-to-char line) ?*))
 	     ((and org-export-with-toc (equal (string-to-char line) ?*))
+	      ;; FIXME: NOT DEPENDENT on TOC?????????????????????
 	      (setq line (replace-match
 	      (setq line (replace-match
 			  (concat "@<span class=\"target\">" (match-string 1 line) "@</span> ")
 			  (concat "@<span class=\"target\">" (match-string 1 line) "@</span> ")
 ;			  (concat "@<i>" (match-string 1 line) "@</i> ")
 ;			  (concat "@<i>" (match-string 1 line) "@</i> ")

+ 77 - 8
lisp/org.el

@@ -6614,6 +6614,17 @@ Org-mode syntax."
   (interactive)
   (interactive)
   (org-run-like-in-org-mode 'org-open-at-point))
   (org-run-like-in-org-mode 'org-open-at-point))
 
 
+;;;###autoload
+(defun org-open-link-from-string (s &optional arg)
+  "Open a link in the string S, as if it was in Org-mode."
+  (interactive "sLink: \nP")
+  (with-temp-buffer
+    (let ((org-inhibit-startup t))
+      (org-mode)
+      (insert s)
+      (goto-char (point-min))
+      (org-open-at-point arg))))
+
 (defun org-open-at-point (&optional in-emacs)
 (defun org-open-at-point (&optional in-emacs)
   "Open link at or after point.
   "Open link at or after point.
 If there is no link at point, this function will search forward up to
 If there is no link at point, this function will search forward up to
@@ -6763,6 +6774,9 @@ optional argument IN-EMACS is non-nil, Emacs will visit the file."
   (move-marker org-open-link-marker nil)
   (move-marker org-open-link-marker nil)
   (run-hook-with-args 'org-follow-link-hook))
   (run-hook-with-args 'org-follow-link-hook))
 
 
+;;;; Time estimates
+
+
 ;;; File search
 ;;; File search
 
 
 (defvar org-create-file-search-functions nil
 (defvar org-create-file-search-functions nil
@@ -13554,13 +13568,68 @@ Still experimental, may disappear in the future."
 ;; arch-tag: e77da1a7-acc7-4336-b19e-efa25af3f9fd
 ;; arch-tag: e77da1a7-acc7-4336-b19e-efa25af3f9fd
 ;;; org.el ends here
 ;;; org.el ends here
 
 
-(defun org-open-link-from-string (s &optional arg)
-  "Open a link in the string S, as if it was in Org-mode."
+
+(defcustom org-time-estimates-property "Effort"
+  "The property that is being used to keep track of time estimates.
+
+- If an entry is queried for this property, the default is taken from the
+  time estimateion cookie in the headline.
+- In the agenda, the duration of appointments is treated as a time estimate
+  if the option `org-time-estimate-include-appointments' is set."
+  :group 'org-time-estimates
+  :type '(string :tag "Property"))
+
+(defcustom org-time-estimate-include-appointments t
+  "Non-nil means, the duration of an appointment will add to the time estimate."
+  :group 'org-time-estimates
+  :type 'boolean)
+
+(defcustom org-time-estimates '("5m" "10m" "15m" "30m" "45m" "1h" "1:30h" "2h" "3h" "4h" "5h" "6h" "7h" "8h")
+  "Discrete time estimates."
+  :group 'org-time-estimates
+  :type '(repeat (string)))
+
+(defun org-time-estimate-up ()
+  "Increate the time estimate."
   (interactive)
   (interactive)
-  (with-temp-buffer
-    (let ((org-inhibit-startup t))
-      (org-mode)
-      (insert s)
-      (goto-char (point-min))
-      (org-open-at-point arg))))
+  (org-time-estimate-change 'up))
 
 
+(defun org-time-estimate-down ()
+  "Increate the time estimate."
+  (interactive)
+  (org-time-estimate-change 'down))
+
+(defun org-time-estimate-change (how)
+  ""
+  (save-excursion
+    (if (not (or (org-at-regexp-p org-time-estimate-regexp)
+		 (progn
+		   (goto-char (point-at-bol))
+		   (re-search-forward org-time-estimate-regexp
+				      (point-at-eol) t))))
+	(error "Don't know which time estimate to change here"))
+    (let* ((match (match-string 0))
+	   (rest (member match org-time-estimates))
+	   new)
+      (unless rest
+	(error "Not a standard value: %s" match))
+      (if (eq how 'up)
+	  (setq new (cadr rest))
+	(setq new (car (nthcdr (- (length org-time-estimates) (length rest) 1)
+			       org-time-estimates))))
+      (replace-match new t t))))
+
+(defconst org-time-estimate-regexp
+  "\\<[0-9]+m\\|\\([0-9]+:\\)?\\([0-9]+h\\)\\>"
+  "Regular expression matching time estimates.")
+
+(defun org-get-time-estimate (&optional string)
+  (setq string (or string (buffer-substring (point-at-bol) (point-at-eol))))
+  (if (string-match org-time-estimate-regexp string)
+      (cond ((match-end 1)
+	     (+ (* 60 (string-to-number (match-string 1 string)))
+		(string-to-number (match-string 2 string))))
+	    ((match-end 2)
+	     (* 60 (string-to-number (match-string 2 string))))
+	    (t (string-to-number (match-string 0 string))))
+    nil))