changeset 71384:9853142939b3

Require noutline, also on XEmacs. (org-end-of-subtree): Return point. (org-dblock-start-re, org-dblock-end-re): New constants. (org-create-dblock, org-prepare-dblock, org-map-dblocks) (org-dblock-update, org-update-dblock, org-beginning-of-dblock) (org-update-all-dblocks, org-find-dblock): New functions. (org-collect-clock-time-entries): New function. (org-html-handle-time-stamps): Never export CLOCK timeranges. (org-fixup-indentation): Modified to deadl correctly with lines starting with TAB. Only one argument DIFF now. (org-demote, org-promote): Call `org-fixup-indentation' with just one argument, DIFF. (org-mode): Don't mark buffer as modified when aligning tables. (org-clock-sum): Don't makr buffer modified when adding time sum properties. (org-export-as-html): Added support for a link validation function. (org-archive-all-done): New function. (org-archive-subtree): New prefix argument. When set, archive all done subtrees in this buffer. (org-remove-clock-overlays) (org-remove-occur-highlights): Use `org-inhibit-highlight-removal'. (org-inhibit-highlight-removal): New variable, for dyn amic scoping. (org-put-clock-overlay): Don't swallow last headline character when displaying overlay. (org-store-link): Link to `image-mode' with just the file name.
author Carsten Dominik <dominik@science.uva.nl>
date Mon, 19 Jun 2006 06:52:55 +0000
parents 4a969fe4cb19
children 9ee77061d851
files lisp/textmodes/org.el
diffstat 1 files changed, 501 insertions(+), 195 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/textmodes/org.el	Sun Jun 18 17:12:16 2006 +0000
+++ b/lisp/textmodes/org.el	Mon Jun 19 06:52:55 2006 +0000
@@ -5,7 +5,7 @@
 ;; Author: Carsten Dominik <dominik at science dot uva dot nl>
 ;; Keywords: outlines, hypermedia, calendar, wp
 ;; Homepage: http://www.astro.uva.nl/~dominik/Tools/org/
-;; Version: 4.36b
+;; Version: 4.38
 ;;
 ;; This file is part of GNU Emacs.
 ;;
@@ -90,6 +90,14 @@
 ;;
 ;; Recent changes
 ;; --------------
+;; Version 4.38
+;;    - noutline.el is now required (important for XEmacs users only).
+;;    - Dynamic blocks.
+;;    - Archiving of all level 1 trees without open TODO items.
+;;    - Clock reports can be inserted into the file in a special section.
+;;    - FAQ removed from the manual, now only on the web.
+;;    - Bug fixes.
+;;
 ;; Version 4.37
 ;;    - Clock-feature for measuring time spent on specific items.
 ;;    - Improved emphasizing allows configuration and stacking.
@@ -170,13 +178,18 @@
 (eval-when-compile
   (require 'cl)
   (require 'calendar))
-(require 'outline)
+;; For XEmacs, noutline is not yet provided by outline.el, so arrange for
+;; the file noutline.el being loaded.
+(if (featurep 'xemacs) (condition-case nil (require 'noutline)))
+;; We require noutline, which might be provided in outline.el
+(require 'outline) (require 'noutline)
+;; Other stuff we need.
 (require 'time-date)
 (require 'easymenu)
 
 ;;; Customization variables
 
-(defvar org-version "4.36b"
+(defvar org-version "4.38"
   "The version number of the file org.el.")
 (defun org-version ()
   (interactive)
@@ -2202,7 +2215,7 @@
              `org-emphasis-alist') will be allowed as pre/post, aiding
              inside-out matching.
 Use customize to modify this, or restart emacs after changing it."
-  :group 'org-fixme
+  :group 'org-font-lock
   :set 'org-set-emph-re
   :type '(list
 	  (sexp    :tag "Allowed chars in pre      ")
@@ -2216,19 +2229,23 @@
   '(("*" bold "<b>" "</b>")
     ("/" italic "<i>" "</i>")
     ("_" underline "<u>" "</u>")
-    ("=" shadow "<code>" "</code>"))
+    ("=" shadow "<code>" "</code>")
+    ("+" (:strike-through t) "<del>" "</del>")
+)
 "Special syntax for emphasised text.
 Text starting and ending with a special character will be emphasized, for
 example *bold*, _underlined_ and /italic/.  This variable sets the marker
 characters, the face to bbe used by font-lock for highlighting in Org-mode
 emacs buffers, and the HTML tags to be used for this.
 Use customize to modify this, or restart emacs after changing it."
-  :group 'org-fixme
+  :group 'org-font-lock
   :set 'org-set-emph-re
   :type '(repeat
 	  (list
 	   (string :tag "Marker character")
-	   (face :tag "Font-lock-face")
+	   (choice
+	    (face :tag "Font-lock-face")
+	    (plist :tag "Face property list"))
 	   (string :tag "HTML start tag")
 	   (string :tag "HTML end tag"))))
 
@@ -2708,6 +2725,7 @@
 (defvar gnus-group-name) ; from gnus
 (defvar gnus-article-current) ; from gnus
 (defvar w3m-current-url) ; from w3m
+(defvar w3m-current-title) ; from w3m
 (defvar mh-progs) ; from MH-E
 (defvar mh-current-folder) ; from MH-E
 (defvar mh-show-folder-buffer) ; from MH-E
@@ -2823,8 +2841,10 @@
       (insert "    -*- mode: org -*-\n\n"))
 
   (unless org-inhibit-startup
-    (if org-startup-align-all-tables
-	(org-table-map-tables 'org-table-align))
+    (when org-startup-align-all-tables
+      (let ((bmp (buffer-modified-p)))
+	(org-table-map-tables 'org-table-align)
+	(set-buffer-modified-p bmp)))
     (if org-startup-with-deadline-check
 	(call-interactively 'org-check-deadlines)
       (cond
@@ -3722,9 +3742,7 @@
     (replace-match up-head nil t)
     ;; Fixup tag positioning
     (and org-auto-align-tags (org-set-tags nil t))
-    (if org-adapt-indentation
-	(org-fixup-indentation (if (> diff 1) "^  " "^ ") ""
-			       (if (> diff 1) "^ ? ?\\S-" "^ ?\\S-")))))
+    (if org-adapt-indentation (org-fixup-indentation (- diff)))))
 
 (defun org-demote ()
   "Demote the current heading lower down the tree.
@@ -3737,8 +3755,7 @@
     (replace-match down-head nil t)
     ;; Fixup tag positioning
     (and org-auto-align-tags (org-set-tags nil t))
-    (if org-adapt-indentation
-	(org-fixup-indentation "^ " (if (> diff 1) "   " "  ") "^\\S-"))))
+    (if org-adapt-indentation (org-fixup-indentation diff))))
 
 (defun org-map-tree (fun)
   "Call FUN for every heading underneath the current one."
@@ -3767,20 +3784,23 @@
 		  (not (eobp)))
 	(funcall fun)))))
 
-;; FIXME: this does not work well with Tabulators.  This has to be re-written entirely.
-(defun org-fixup-indentation (from to prohibit)
-  "Change the indentation in the current entry by re-replacing FROM with TO.
-However, if the regexp PROHIBIT matches at all, don't do anything.
-This is being used to change indentation along with the length of the
-heading marker.  But if there are any lines which are not indented, nothing
-is changed at all."
+(defun org-fixup-indentation (diff)
+  "Change the indentation in the current entry by DIFF
+However, if any line in the current entry has no indentation, or if it
+would end up with no indentation after the change, nothing at all is done."
   (save-excursion
     (let ((end (save-excursion (outline-next-heading)
-			       (point-marker))))
+			       (point-marker)))
+	  (prohibit (if (> diff 0)
+			"^\\S-" 
+		      (concat "^ \\{0," (int-to-string (- diff)) "\\}\\S-")))
+	  col)
       (unless (save-excursion (re-search-forward prohibit end t))
-	(while (re-search-forward from end t)
-	  (replace-match to)
-	  (beginning-of-line 2)))
+	(while (re-search-forward "^[ \t]+" end t)
+	  (goto-char (match-end 0))
+	  (setq col (current-column))
+	  (if (< diff 0) (replace-match ""))
+	  (indent-to (+ diff col))))
       (move-marker end nil))))
 
 ;;; Vertical tree motion, cutting and pasting of subtrees
@@ -3984,6 +4004,14 @@
 	      (throw 'exit nil)))
 	t))))
 
+(defun org-narrow-to-subtree ()
+  "Narrow buffer to the current subtree."
+  (interactive)
+  (save-excursion
+    (narrow-to-region
+     (progn (org-back-to-heading) (point))
+     (progn (org-end-of-subtree t) (point)))))
+
 ;;; Plain list items
 
 (defun org-at-item-p ()
@@ -4292,103 +4320,259 @@
 
 ;;; Archiving
 
-(defun org-archive-subtree ()
+(defun org-archive-subtree (&optional find-done)
   "Move the current subtree to the archive.
 The archive can be a certain top-level heading in the current file, or in
 a different file.  The tree will be moved to that location, the subtree
-heading be marked DONE, and the current time will be added."
-  (interactive)
-  ;; Save all relevant TODO keyword-relatex variables
-  (let ((tr-org-todo-line-regexp org-todo-line-regexp) ; keep despite compiler
-	(tr-org-todo-keywords org-todo-keywords)
-	(tr-org-todo-interpretation org-todo-interpretation)
-	(tr-org-done-string org-done-string)
-	(tr-org-todo-regexp org-todo-regexp)
-	(tr-org-todo-line-regexp org-todo-line-regexp)
-	(this-buffer (current-buffer))
-	file heading buffer level newfile-p)
-    (if (string-match "\\(.*\\)::\\(.*\\)" org-archive-location)
+heading be marked DONE, and the current time will be added.
+
+When called with prefix argument FIND-DONE, find whole trees without any
+open TODO items and archive them (after getting confirmation from the user).
+If the cursor is not at a headline when this comand is called, try all level
+1 trees.  If the cursor is on a headline, only try the direct children of
+this heading. "
+  (interactive "P")
+  (if find-done
+      (org-archive-all-done)
+    ;; Save all relevant TODO keyword-relatex variables
+    
+    (let ((tr-org-todo-line-regexp org-todo-line-regexp) ; keep despite compiler
+	  (tr-org-todo-keywords org-todo-keywords)
+	  (tr-org-todo-interpretation org-todo-interpretation)
+	  (tr-org-done-string org-done-string)
+	  (tr-org-todo-regexp org-todo-regexp)
+	  (tr-org-todo-line-regexp org-todo-line-regexp)
+	  (this-buffer (current-buffer))
+	  file heading buffer level newfile-p)
+      (if (string-match "\\(.*\\)::\\(.*\\)" org-archive-location)
+	  (progn
+	    (setq file (format (match-string 1 org-archive-location)
+			       (file-name-nondirectory buffer-file-name))
+		  heading (match-string 2 org-archive-location)))
+	(error "Invalid `org-archive-location'"))
+      (if (> (length file) 0)
+	  (setq newfile-p (not (file-exists-p file))
+		buffer (find-file-noselect file))
+	(setq buffer (current-buffer)))
+      (unless buffer
+	(error "Cannot access file \"%s\"" file))
+      (if (and (> (length heading) 0)
+	       (string-match "^\\*+" heading))
+	  (setq level (match-end 0))
+	(setq heading nil level 0))
+      (save-excursion
+	;; We first only copy, in case something goes wrong
+	;; we need to protect this-command, to avoid kill-region sets it,
+	;; which would lead to duplication of subtrees
+	(let (this-command) (org-copy-subtree))
+	(set-buffer buffer)
+	;; Enforce org-mode for the archive buffer
+	(if (not (eq major-mode 'org-mode))
+	    ;; Force the mode for future visits.
+	    (let ((org-insert-mode-line-in-empty-file t))
+	      (call-interactively 'org-mode)))
+	(when newfile-p
+	  (goto-char (point-max))
+	  (insert (format "\nArchived entries from file %s\n\n"
+			  (buffer-file-name this-buffer))))
+	;; Force the TODO keywords of the original buffer
+	(let ((org-todo-line-regexp tr-org-todo-line-regexp)
+	      (org-todo-keywords tr-org-todo-keywords)
+	      (org-todo-interpretation tr-org-todo-interpretation)
+	      (org-done-string tr-org-done-string)
+	      (org-todo-regexp tr-org-todo-regexp)
+	      (org-todo-line-regexp tr-org-todo-line-regexp))
+	  (goto-char (point-min))
+	  (if heading
+	      (progn
+		(if (re-search-forward
+		     (concat "\\(^\\|\r\\)"
+			     (regexp-quote heading) "[ \t]*\\($\\|\r\\)")
+		     nil t)
+		    (goto-char (match-end 0))
+		  ;; Heading not found, just insert it at the end
+		  (goto-char (point-max))
+		  (or (bolp) (insert "\n"))
+		  (insert "\n" heading "\n")
+		  (end-of-line 0))
+		;; Make the subtree visible
+		(show-subtree)
+		(org-end-of-subtree t)
+		(skip-chars-backward " \t\r\n]")
+		(and (looking-at "[ \t\r\n]*")
+		     (replace-match "\n\n")))
+	    ;; No specific heading, just go to end of file.
+	    (goto-char (point-max)) (insert "\n"))
+	  ;; Paste
+	  (org-paste-subtree (1+ level))
+	  ;; Mark the entry as done, i.e. set to last work in org-todo-keywords
+	  (if org-archive-mark-done
+	      (org-todo (length org-todo-keywords)))
+	  ;; Move cursor to right after the TODO keyword
+	  (when org-archive-stamp-time
+	    (beginning-of-line 1)
+	    (looking-at org-todo-line-regexp)
+	    (goto-char (or (match-end 2) (match-beginning 3)))
+	    (insert "(" (format-time-string (cdr org-time-stamp-formats)
+					    (org-current-time))
+		    ")"))
+	  ;; Save the buffer, if it is not the same buffer.
+	  (if (not (eq this-buffer buffer)) (save-buffer))))
+      ;; Here we are back in the original buffer.  Everything seems to have
+      ;; worked.  So now cut the tree and finish up.
+      (let (this-command) (org-cut-subtree))
+      (if (and (not (eobp)) (looking-at "[ \t]*$")) (kill-line))
+      (message "Subtree archived %s"
+	       (if (eq this-buffer buffer)
+		   (concat "under heading: " heading)
+		 (concat "in file: " (abbreviate-file-name file)))))))
+
+(defun org-archive-all-done ()
+  "Archive sublevels of the current tree without open TODO items.
+If the cursor is not on a headline, try all level 1 trees.  If
+it is on a headline, try all direct children."
+  (let ((re (concat "^\\*+ +" org-not-done-regexp)) re1
+	(begm (make-marker))
+	(endm (make-marker))
+	beg end (cntarch 0))
+    (if (org-on-heading-p)
 	(progn
-	  (setq file (format (match-string 1 org-archive-location)
-			     (file-name-nondirectory buffer-file-name))
-		heading (match-string 2 org-archive-location)))
-      (error "Invalid `org-archive-location'"))
-    (if (> (length file) 0)
-	(setq newfile-p (not (file-exists-p file))
-	      buffer (find-file-noselect file))
-      (setq buffer (current-buffer)))
-    (unless buffer
-      (error "Cannot access file \"%s\"" file))
-    (if (and (> (length heading) 0)
-	     (string-match "^\\*+" heading))
-	(setq level (match-end 0))
-      (setq heading nil level 0))
+	  (setq re1 (concat "^" (regexp-quote
+				 (make-string 
+				  (1+ (- (match-end 0) (match-beginning 0)))
+				  ?*))
+			    " "))
+	  (move-marker begm (point))
+	  (move-marker endm (org-end-of-subtree)))
+      (setq re1 "^* ")
+      (move-marker begm (point-min))
+      (move-marker endm (point-max)))
     (save-excursion
-      ;; We first only copy, in case something goes wrong
-      ;; we need to protect this-command, to avoid kill-region sets it,
-      ;; which would lead to duplication of subtrees
-      (let (this-command) (org-copy-subtree))
-      (set-buffer buffer)
-      ;; Enforce org-mode for the archive buffer
-      (if (not (eq major-mode 'org-mode))
-	  ;; Force the mode for future visits.
-	  (let ((org-insert-mode-line-in-empty-file t))
-	    (call-interactively 'org-mode)))
-      (when newfile-p
-	(goto-char (point-max))
-	(insert (format "\nArchived entries from file %s\n\n"
-			(buffer-file-name this-buffer))))
-      ;; Force the TODO keywords of the original buffer
-      (let ((org-todo-line-regexp tr-org-todo-line-regexp)
-	    (org-todo-keywords tr-org-todo-keywords)
-	    (org-todo-interpretation tr-org-todo-interpretation)
-	    (org-done-string tr-org-done-string)
-	    (org-todo-regexp tr-org-todo-regexp)
-	    (org-todo-line-regexp tr-org-todo-line-regexp))
-	(goto-char (point-min))
-	(if heading
-	    (progn
-	      (if (re-search-forward
-		   (concat "\\(^\\|\r\\)"
-			   (regexp-quote heading) "[ \t]*\\($\\|\r\\)")
-		   nil t)
-		  (goto-char (match-end 0))
-		;; Heading not found, just insert it at the end
-		(goto-char (point-max))
-		(or (bolp) (insert "\n"))
-		(insert "\n" heading "\n")
-		(end-of-line 0))
-	      ;; Make the subtree visible
-	      (show-subtree)
-	      (org-end-of-subtree t)
-	      (skip-chars-backward " \t\r\n]")
-	      (and (looking-at "[ \t\r\n]*")
-		   (replace-match "\n\n")))
-	  ;; No specific heading, just go to end of file.
-	  (goto-char (point-max)) (insert "\n"))
-	;; Paste
-	(org-paste-subtree (1+ level))
-	;; Mark the entry as done, i.e. set to last work in org-todo-keywords
-	(if org-archive-mark-done
-	    (org-todo (length org-todo-keywords)))
-	;; Move cursor to right after the TODO keyword
-	(when org-archive-stamp-time
-	  (beginning-of-line 1)
-	  (looking-at org-todo-line-regexp)
-	  (goto-char (or (match-end 2) (match-beginning 3)))
-	  (insert "(" (format-time-string (cdr org-time-stamp-formats)
-					  (org-current-time))
-		  ")"))
-	;; Save the buffer, if it is not the same buffer.
-	(if (not (eq this-buffer buffer)) (save-buffer))))
-    ;; Here we are back in the original buffer.  Everything seems to have
-    ;; worked.  So now cut the tree and finish up.
-    (let (this-command) (org-cut-subtree))
-    (if (and (not (eobp)) (looking-at "[ \t]*$")) (kill-line))
-    (message "Subtree archived %s"
-	     (if (eq this-buffer buffer)
-		 (concat "under heading: " heading)
-	       (concat "in file: " (abbreviate-file-name file))))))
+      (goto-char begm)
+      (while (re-search-forward re1 endm t)
+	      beg (match-beginning 0)
+	      end (save-excursion (org-end-of-subtree t) (point)))
+	(goto-char beg)
+	(if (re-search-forward re end t)
+	    (goto-char end)
+	  (goto-char beg)
+	  (if (y-or-n-p "Archive this subtree (no open TODO items)? ")
+	      (progn
+		(org-archive-subtree)
+		(setq cntarch (1+ cntarch)))
+	    (goto-char end))))
+    (message "%d trees archived" cntarch)))
+
+;;; Dynamic blocks
+
+(defun org-find-dblock (name)
+  "Find the first dynamic block with name NAME in the buffer.
+If not found, stay at current position and return nil."
+  (let (pos)
+    (save-excursion
+      (goto-char (point-min))
+      (setq pos (and (re-search-forward (concat "^#\\+BEGIN:[ \t]+" name "\\>")
+					nil t)
+		     (match-beginning 0))))
+    (if pos (goto-char pos))
+    pos))
+
+(defconst org-dblock-start-re
+  "^#\\+BEGIN:[ \t]+\\(\\S-+\\)[ \t]+\\(.*\\)"
+  "Matches the startline of a dynamic block, with parameters.")
+
+(defconst org-dblock-end-re "^#\\+END\\([: \t\r\n]\\|$\\)"
+  "Matches the end of a dyhamic block.")
+
+(defun org-create-dblock (plist)
+  "Create a dynamic block section, with parameters taken from PLIST.
+PLIST must containe a :name entry which is used as name of the block."
+  (unless (bolp) (newline))
+  (let ((name (plist-get plist :name)))
+    (insert "#+BEGIN: " name)
+    (while plist
+      (if (eq (car plist) :name)
+	  (setq plist (cddr plist))
+	(insert " " (prin1-to-string (pop plist)))))
+    (insert "\n\n#+END:\n")
+    (beginning-of-line -2)))
+
+(defun org-prepare-dblock ()
+  "Prepare dynamic block for refresh.
+This empties the block, puts the cursor at the insert position and returns
+the property list including an extra property :name with the block name."
+  (unless (looking-at org-dblock-start-re)
+    (error "Not at a dynamic block"))
+  (let* ((beg (match-beginning 0))
+	 (begdel (1+ (match-end 0)))
+	 (name (match-string 1))
+	 (params (append (list :name name)
+			 (read (concat "(" (match-string 2) ")")))))
+    (unless (re-search-forward org-dblock-end-re nil t)
+      (error "Dynamic block not terminated"))
+    (delete-region begdel (match-beginning 0))
+    (goto-char begdel)
+    (open-line 1)
+    params))
+
+(defun org-map-dblocks (&optional command)
+  "Apply COMMAND to all dynamic blocks in the current buffer.
+If COMMAND is not given, use `org-update-dblock'."
+  (let ((cmd (or command 'org-update-dblock))
+	pos)
+    (save-excursion
+      (goto-char (point-min))
+      (while (re-search-forward org-dblock-start-re nil t)
+	(goto-char (setq pos (match-beginning 0)))
+	(condition-case nil
+	    (funcall cmd)
+	  (error (message "Error during update of dynamic block")))
+	(goto-char pos)
+	(unless (re-search-forward org-dblock-end-re nil t)
+	  (error "Dynamic block not terminated"))))))
+
+(defun org-dblock-update (&optional arg)
+  "User command for updating dynamic blocks.
+Update the dynamic block at point.  With prefix ARG, update all dynamic
+blocks in the buffer."
+  (interactive "P")
+  (if arg
+      (org-update-all-dblocks)
+    (or (looking-at org-dblock-start-re)
+	(org-beginning-of-dblock))
+    (org-update-dblock)))
+
+(defun org-update-dblock ()
+  "Update the dynamic block at point
+This means to empty the block, parse for parameters and then call
+the correct writing function."
+  (let* ((pos (point))
+	 (params (org-prepare-dblock))
+	 (name (plist-get params :name))
+	 (cmd (intern (concat "org-dblock-write:" name))))
+    (funcall cmd params)
+    (goto-char pos)))
+
+(defun org-beginning-of-dblock ()
+  "Find the beginning of the dynamic block at point.
+Error if there is no scuh block at point."
+  (let ((pos (point))
+	beg end)
+    (end-of-line 1)
+    (if (and (re-search-backward org-dblock-start-re nil t)
+	     (setq beg (match-beginning 0))
+	     (re-search-forward org-dblock-end-re nil t)
+	     (> (match-end 0) pos))
+	(goto-char beg)
+      (goto-char pos)
+      (error "Not in a dynamic block"))))
+
+(defun org-update-all-dblocks ()
+  "Update all dynamic blocks in the buffer.
+This function can be used in a hook."
+  (when (eq major-mode 'org-mode)
+    (org-map-dblocks 'org-update-dblock)))
+
 
 ;;; Completion
 
@@ -4783,16 +4967,18 @@
     (org-overlay-put ov 'face 'secondary-selection)
     (push ov org-occur-highlights)))
 
+(defvar org-inhibit-highlight-removal nil)
 (defun org-remove-occur-highlights (&optional beg end noremove)
   "Remove the occur highlights from the buffer.
 BEG and END are ignored.  If NOREMOVE is nil, remove this function
 from the `before-change-functions' in the current buffer."
   (interactive)
-  (mapc 'org-delete-overlay org-occur-highlights)
-  (setq org-occur-highlights nil)
-  (unless noremove
-    (remove-hook 'before-change-functions
-		 'org-remove-occur-highlights 'local)))
+  (unless org-inhibit-highlight-removal
+    (mapc 'org-delete-overlay org-occur-highlights)
+    (setq org-occur-highlights nil)
+    (unless noremove
+      (remove-hook 'before-change-functions
+		   'org-remove-occur-highlights 'local))))
 
 ;;; Priorities
 
@@ -5449,8 +5635,8 @@
   "Sum the times for each subtree.
 Puts the resulting times in minutes as a text property on each headline."
   (interactive)
-  (remove-text-properties (point-min) (point-max) '(:org-clock-minutes t))
-  (let* ((re (concat "^\\(\\*+\\)[ \t]\\|^[ \t]*"
+  (let* ((bmp (buffer-modified-p))
+	 (re (concat "^\\(\\*+\\)[ \t]\\|^[ \t]*"
 		     org-clock-string
 		     ".*=>[ \t]*\\([0-9]+\\):\\([0-9]+\\)[ \t]*$"))
 	 (lmax 30)
@@ -5458,6 +5644,7 @@
 	 (t1 0)
 	 (level 0)
 	 (lastlevel 0) time)
+    (remove-text-properties (point-min) (point-max) '(:org-clock-minutes t))
     (save-excursion
       (goto-char (point-max))
       (while (re-search-backward re nil t)
@@ -5475,7 +5662,8 @@
 		  (aset ltimes l 0))
 	    (goto-char (match-beginning 0))
 	    (put-text-property (point) (point-at-eol) :org-clock-minutes time))))
-      (setq org-clock-file-total-minutes (aref ltimes 0)))))
+      (setq org-clock-file-total-minutes (aref ltimes 0)))
+    (set-buffer-modified-p bmp)))
 
 (defun org-clock-display (&optional total-only)
   "Show subtree times in the entire buffer.
@@ -5510,11 +5698,11 @@
 	 (off 0)
 	 ov tx)
     (move-to-column c)
-    (if (eolp) (setq off 1))
     (unless (eolp) (skip-chars-backward "^ \t"))
     (skip-chars-backward " \t")
-    (setq ov (org-make-overlay (- (point) off) (point-at-eol))
-	  tx (concat (make-string (+ off (max 0 (- c (current-column)))) ?.)
+    (setq ov (org-make-overlay (1- (point)) (point-at-eol))
+	  tx (concat (buffer-substring (1- (point)) (point))
+		     (make-string (+ off (max 0 (- c (current-column)))) ?.)
 		     (org-add-props (format "%s %2d:%02d%s"
 					    (make-string l ?*) h m
 					    (make-string (- 10 l) ?\ ))
@@ -5528,11 +5716,12 @@
 BEG and END are ignored.  If NOREMOVE is nil, remove this function
 from the `before-change-functions' in the current buffer."
   (interactive)
-  (mapc 'org-delete-overlay org-clock-overlays)
-  (setq org-clock-overlays nil)
-  (unless noremove
-    (remove-hook 'before-change-functions
-		 'org-remove-clock-overlays 'local)))
+  (unless org-inhibit-highlight-removal
+    (mapc 'org-delete-overlay org-clock-overlays)
+    (setq org-clock-overlays nil)
+    (unless noremove
+      (remove-hook 'before-change-functions
+		   'org-remove-clock-overlays 'local))))
 
 (defun org-clock-out-if-current ()
   "Clock out if the current entry contains the running clock.
@@ -5557,6 +5746,113 @@
     (when (y-or-n-p "Save changed buffer?")
       (save-buffer))))
 
+(defun org-clock-report ()
+  "Create a table containing a report about clocked time.
+If the buffer contains lines
+#+BEGIN: clocktable :maxlevel 3 :emphasize nil
+
+#+END: clocktable
+then the table will be inserted between these lines, replacing whatever
+is was there before.  If these lines are not in the buffer, the table
+is inserted at point, surrounded by the special lines.
+The BEGIN line can contain parameters.  Allowed are:
+:maxlevel   The maximum level to be included in the table.  Default is 3.
+:emphasize  t/nil, if levell 1 and level 2 should be bold/italic in the table."
+  (interactive)
+  (org-remove-clock-overlays)
+  (unless (org-find-dblock "clocktable")
+    (org-create-dblock  (list :name "clocktable"
+			      :maxlevel 2 :emphasize nil)))
+  (org-update-dblock))
+
+(defun org-dblock-write:clocktable (params)
+  "Write the standard clocktable."
+  (let ((hlchars '((1 . "*") (2 . ?/)))
+	(emph nil)
+	(pos (point)) ipos
+	(ins (make-marker))
+	time h m p level hlc hdl maxlevel)
+    (setq maxlevel (or (plist-get params :maxlevel) 3)
+	  emph (plist-get params :emphasize))
+    (move-marker ins (point))
+    (setq ipos (point))
+    (insert-before-markers "Clock summary at [" 
+			   (substring
+			    (format-time-string (cdr org-time-stamp-formats))
+			    1 -1)
+			   "]\n|L|Headline|Time|\n")
+    (org-clock-sum)
+    (setq h (/ org-clock-file-total-minutes 60)
+	  m (- org-clock-file-total-minutes (* 60 h)))
+    (insert-before-markers "|-\n|0|" "*Total file time*| "
+			   (format "*%d:%02d*" h m)
+			   "|\n")
+    (goto-char (point-min))
+    (while (setq p (next-single-property-change (point) :org-clock-minutes))
+      (goto-char p)
+      (when (setq time (get-text-property p :org-clock-minutes))
+	(beginning-of-line 1)
+	(when (and (looking-at "\\(\\*+\\)[ \t]+\\(.*?\\)\\([ \t]+:[0-9a-zA-Z_@:]+:\\)?[ \t]*$")
+		   (setq level (- (match-end 1) (match-beginning 1)))
+		   (<= level maxlevel))
+	  (setq hlc (if emph (or (cdr (assoc level hlchars)) "") "")
+		hdl (match-string 2)
+		h (/ time 60)
+		m (- time (* 60 h)))
+	  (save-excursion
+	    (goto-char ins)
+	    (if (= level 1) (insert-before-markers "|-\n"))
+	    (insert-before-markers
+	     "| " (int-to-string level) "|" hlc hdl hlc " |" 
+	     (make-string (1- level) ?|)
+	     hlc
+	     (format "%d:%02d" h m)
+	     hlc
+	     " |\n")))))
+    (goto-char ins)
+    (backward-delete-char 1)
+    (goto-char ipos)
+    (skip-chars-forward "^|")
+    (org-table-align)))
+
+(defun org-collect-clock-time-entries ()
+  "Return an internal list with clocking information.
+This list has one entry for each CLOCK interval.
+FIXME: describe the elements."
+  (interactive)
+  (let ((re (concat "^[ \t]*" org-clock-string
+		    " *\\[\\(.*?\\)\\]--\\[\\(.*?\\)\\]"))
+	rtn beg end next cont level title total closedp leafp
+	clockpos titlepos h m donep)
+    (save-excursion
+      (org-clock-sum)
+      (goto-char (point-min))
+      (while (re-search-forward re nil t)
+	(setq clockpos (match-beginning 0)
+	      beg (match-string 1) end (match-string 2)
+	      cont (match-end 0))
+	(setq beg (apply 'encode-time (org-parse-time-string beg))
+	      end (apply 'encode-time (org-parse-time-string end)))
+	(org-back-to-heading t)
+	(setq donep (org-entry-is-done-p))
+	(setq titlepos (point)
+	      total (or (get-text-property (1+ (point)) :org-clock-minutes) 0)
+	      h (/ total 60) m (- total (* 60 h))
+	      total (cons h m))
+	(looking-at "\\(\\*+\\) +\\(.*\\)")
+	(setq level (- (match-end 1) (match-beginning 1))
+	      title (org-match-string-no-properties 2))
+	(save-excursion (outline-next-heading) (setq next (point)))
+	(setq closedp (re-search-forward org-closed-time-regexp next t))
+	(goto-char next)
+	(setq leafp (and (looking-at "^\\*+ ")
+			 (<= (- (match-end 0) (point)) level)))
+	(push (list beg end clockpos closedp donep
+		    total title titlepos level leafp)
+	      rtn)
+	(goto-char cont)))
+    (nreverse rtn)))
+
 ;;; Agenda, and Diary Integration
 
 ;;; Define the mode
@@ -9186,8 +9482,8 @@
       (setq cpltxt (url-view-url t)
 	    link (org-make-link cpltxt)))
      ((eq major-mode 'w3m-mode)
-      (setq cpltxt w3m-current-url
-	    link (org-make-link cpltxt)))
+      (setq cpltxt (or w3m-current-title w3m-current-url)
+	    link (org-make-link w3m-current-url)))
 
      ((setq search (run-hook-with-args-until-success
 		    'org-create-file-search-functions))
@@ -9195,6 +9491,11 @@
 			 "::" search))
       (setq cpltxt (or description link)))
 
+     ((eq major-mode 'image-mode)
+      (setq cpltxt (concat "file:"
+			   (abbreviate-file-name buffer-file-name))
+	    link (org-make-link cpltxt)))      
+
      ((eq major-mode 'org-mode)
       ;; Just link to current headline
       (setq cpltxt (concat "file:"
@@ -9414,7 +9715,9 @@
 completed in the minibuffer (i.e. normally ~/path/to/file).
 
 With two \\[universal-argument] prefixes, enforce an absolute path even if the file
-is in the current directory or below."
+is in the current directory or below.
+With three \\[universal-argument] prefixes, negate the meaning of
+`org-keep-stored-link-after-insertion'."
   (interactive "P")
   (let (link desc entry remove file (pos (point)))
     (cond
@@ -9430,7 +9733,7 @@
       (setq link (read-string "Link: "
 			      (org-link-unescape
 			       (org-match-string-no-properties 1)))))
-     (complete-file
+     ((equal complete-file '(4))
       ;; Completing read for file names.
       (setq file (read-file-name "File: "))
       (let ((pwd (file-name-as-directory (expand-file-name ".")))
@@ -9455,7 +9758,8 @@
 		  org-insert-link-history
 		  (or (car (car org-stored-links)))))
       (setq entry (assoc link org-stored-links))
-      (if (not org-keep-stored-link-after-insertion)
+      (if (funcall (if (equal complete-file '(64)) 'not 'identity)
+		   (not org-keep-stored-link-after-insertion))
 	  (setq org-stored-links (delq (assoc link org-stored-links)
 				       org-stored-links)))
       (setq link (if entry (nth 1 entry) link)
@@ -12199,7 +12503,8 @@
 \[X] publish... (project will be prompted for)
 \[A] publish all projects")
 	(cmds
-	 '((?v . org-export-visible)
+	 '((?t . org-insert-export-options-template)
+	   (?v . org-export-visible)
 	   (?a . org-export-as-ascii)
 	   (?h . org-export-as-html)
 	   (?b . org-export-as-html-and-open)
@@ -12566,7 +12871,7 @@
 	  (match-string 1) "[[" (match-string 2) ":" (match-string 3) "]]")
 	 t t))
       ;; Find multiline emphasis and put them into single line
-      (when (assq :emph-multiline parameters)
+      (when (memq :emph-multiline parameters)
 	(goto-char (point-min))
 	(while (re-search-forward org-emph-re nil t)
 	  (subst-char-in-region (match-beginning 0) (match-end 0) ?\n ?\  t)
@@ -12858,13 +13163,18 @@
   (interactive
    (list (progn
 	   (message "Export visible: [a]SCII  [h]tml  [b]rowse HTML  [x]OXO  [ ]keep buffer")
-	   (char-to-string (read-char-exclusive)))
+	   (read-char-exclusive))
 	 current-prefix-arg))
-  (if (not (member type '("a" "\C-a" "b" "\C-b" "h" "x" " ")))
+  (if (not (member type '(?a ?\C-a ?b ?\C-b ?h ?x ?\ )))
       (error "Invalid export key"))
-  ;; FIXME: do this more explicit?
-  (let* ((binding (key-binding (concat "\C-c\C-x" type)))
-	 (keepp (equal type " "))
+  (let* ((binding (cdr (assoc type
+			      '((?a . org-export-as-ascii)
+				(?\C-a . org-export-as-ascii)
+				(?b . org-export-as-html-and-open)
+				(?\C-b . org-export-as-html-and-open)
+				(?h . org-export-as-html)
+				(?x . org-export-as-xoxo)))))
+	 (keepp (equal type ?\ ))
 	 (file buffer-file-name)
 	 (buffer (get-buffer-create "*Org Export Visible*"))
 	 s e)
@@ -13049,6 +13359,8 @@
 					(org-infile-export-plist)))
 
 	 (style (plist-get opt-plist :style))
+	 (link-validate (plist-get opt-plist :link-validation-function))
+	 valid
 	 (odd org-odd-levels-only)
 	 (region-p (org-region-active-p))
          (region
@@ -13068,6 +13380,7 @@
 			   (file-name-sans-extension
 			    (file-name-nondirectory buffer-file-name))
 			   ".html"))
+	 (current-dir (file-name-directory buffer-file-name))
          (buffer (find-file-noselect filename))
          (levels-open (make-vector org-level-max nil))
 	 (date (format-time-string "%Y/%m/%d" (current-time)))
@@ -13314,6 +13627,10 @@
 		  (if (string-match "::\\(.*\\)" filename)
 		      (setq search (match-string 1 filename)
 			    filename (replace-match "" t nil filename)))
+		  (setq valid
+			(if (functionp link-validate)
+			    (funcall link-validate filename current-dir)
+			  t))		    
 		  (setq file-is-image-p
 			(string-match (org-image-file-name-regexp) filename))
 		  (setq thefile (if abs-p (expand-file-name filename) filename))
@@ -13339,7 +13656,8 @@
 				       (and org-export-html-inline-images
 					    (not descp))))
 			      (concat "<img src=\"" thefile "\"/>")
-			    (concat "<a href=\"" thefile "\">" desc "</a>")))))
+			    (concat "<a href=\"" thefile "\">" desc "</a>")))
+		(if (not valid) (setq rpl desc))))
 	     ((member type '("bbdb" "vm" "wl" "mhe" "rmail" "gnus" "shell" "info" "elisp"))
 	      (setq rpl (concat "<i>&lt;" type ":"
 				(save-match-data (org-link-unescape path))
@@ -13650,27 +13968,31 @@
 
 (defun org-html-handle-time-stamps (s)
   "Format time stamps in string S, or remove them."
-  (let (r b)
-    (while (string-match org-maybe-keyword-time-regexp s)
-      (or b (setq b (substring s 0 (match-beginning 0))))
-      (if (not org-export-with-timestamps)
-	  (setq r (concat r (substring s 0 (match-beginning 0)))
-		s (substring s (match-end 0)))
-	(setq r (concat
-		 r (substring s 0 (match-beginning 0))
-		 (if (match-end 1)
-		     (format "@<span class=\"timestamp-kwd\">%s @</span>"
-			     (match-string 1 s)))
-		 (format " @<span class=\"timestamp\">%s@</span>"
-			 (substring (match-string 3 s) 1 -1)))
-	      s (substring s (match-end 0)))))
-    ;; Line break of line started and ended with time stamp stuff
-    (if (not r)
-	s
-      (setq r (concat r s))
-      (unless (string-match "\\S-" (concat b s))
-	(setq r (concat r "@<br/>")))
-      r)))
+  (catch 'exit
+    (let (r b)
+      (while (string-match org-maybe-keyword-time-regexp s)
+	;; FIXME: is it good to never export CLOCK, or do we need control?
+	(if (and (match-end 1) (equal (match-string 1 s) org-clock-string))
+	    (throw 'exit ""))
+	(or b (setq b (substring s 0 (match-beginning 0))))
+	(if (not org-export-with-timestamps)
+	    (setq r (concat r (substring s 0 (match-beginning 0)))
+		  s (substring s (match-end 0)))
+	  (setq r (concat
+		   r (substring s 0 (match-beginning 0))
+		   (if (match-end 1)
+		       (format "@<span class=\"timestamp-kwd\">%s @</span>"
+			       (match-string 1 s)))
+		   (format " @<span class=\"timestamp\">%s@</span>"
+			   (substring (match-string 3 s) 1 -1)))
+		s (substring s (match-end 0)))))
+      ;; Line break if line started and ended with time stamp stuff
+      (if (not r)
+	  s
+	(setq r (concat r s))
+	(unless (string-match "\\S-" (concat b s))
+	  (setq r (concat r "@<br/>")))
+	r))))
 
 (defun org-html-protect (s)
   ;; convert & to &amp;, < to &lt; and > to &gt;
@@ -14212,6 +14534,7 @@
 ;; All the other keys
 
 (define-key org-mode-map "\C-c\C-a" 'show-all)  ; in case allout messed up.
+(define-key org-mode-map "\C-xns" 'org-narrow-to-subtree)
 (define-key org-mode-map "\C-c$"    'org-archive-subtree)
 (define-key org-mode-map "\C-c\C-j" 'org-goto)
 (define-key org-mode-map "\C-c\C-t" 'org-todo)
@@ -14255,24 +14578,7 @@
 (define-key org-mode-map "\C-c~"          'org-table-create-with-table.el)
 (define-key org-mode-map "\C-c\C-q"       'org-table-wrap-region)
 (define-key org-mode-map "\C-c\C-e"       'org-export)
-;(define-key org-mode-map "\C-c\C-xa"      'org-export-as-ascii)
-;(define-key org-mode-map "\C-c\C-x\C-a"   'org-export-as-ascii)
-;(define-key org-mode-map "\C-c\C-xv"      'org-export-visible)
-;(define-key org-mode-map "\C-c\C-x\C-v"   'org-export-visible)
-;; OPML support is only an option for the future
-;(define-key org-mode-map "\C-c\C-xo"      'org-export-as-opml)
-;(define-key org-mode-map "\C-c\C-x\C-o"   'org-export-as-opml)
-;(define-key org-mode-map "\C-c\C-xi"      'org-export-icalendar-this-file)
-;(define-key org-mode-map "\C-c\C-x\C-i"   'org-export-icalendar-all-agenda-files)
-;(define-key org-mode-map "\C-c\C-xc"      'org-export-icalendar-combine-agenda-files)
-;(define-key org-mode-map "\C-c\C-x\C-c"   'org-export-icalendar-combine-agenda-files)
-;(define-key org-mode-map "\C-c\C-xt"      'org-insert-export-options-template)
 (define-key org-mode-map "\C-c:"          'org-toggle-fixed-width-section)
-;(define-key org-mode-map "\C-c\C-xh"      'org-export-as-html)
-;(define-key org-mode-map "\C-c\C-xx"      'org-export-as-xoxo)
-;(define-key org-mode-map "\C-c\C-x\C-x"   'org-export-as-xoxo)
-;(define-key org-mode-map "\C-c\C-xb"      'org-export-as-html-and-open)
-;(define-key org-mode-map "\C-c\C-x\C-b"   'org-export-as-html-and-open)
 
 (define-key org-mode-map "\C-c\C-x\C-k"   'org-cut-special)
 (define-key org-mode-map "\C-c\C-x\C-w"   'org-cut-special)
@@ -14283,15 +14589,9 @@
 (define-key org-mode-map "\C-c\C-x\C-o" 'org-clock-out)
 (define-key org-mode-map "\C-c\C-x\C-x" 'org-clock-cancel)
 (define-key org-mode-map "\C-c\C-x\C-d" 'org-clock-display)
-
-;(define-key org-mode-map "\C-c\C-ef"    'org-publish-current-file)
-;(define-key org-mode-map "\C-c\C-ep"    'org-publish-current-project)
-;(define-key org-mode-map "\C-c\C-ec"    'org-publish)
-;(define-key org-mode-map "\C-c\C-ea"    'org-publish-all)
-;(define-key org-mode-map "\C-c\C-e\C-f" 'org-publish-current-file)
-;(define-key org-mode-map "\C-c\C-e\C-p" 'org-publish-current-project)
-;(define-key org-mode-map "\C-c\C-e\C-c" 'org-publish)
-;(define-key org-mode-map "\C-c\C-e\C-a" 'org-publish-all)
+(define-key org-mode-map "\C-c\C-x\C-r" 'org-clock-report)
+
+(define-key org-mode-map "\C-c\C-x\C-u" 'org-dblock-update)
 
 (when (featurep 'xemacs)
   (define-key org-mode-map 'button3   'popup-mode-menu))
@@ -14785,6 +15085,7 @@
      ["Clock out" org-clock-out t]
      ["Clock cancel" org-clock-cancel t]
      ["Display times" org-clock-display t]
+     ["Create clock table" org-clock-report t]
      "--"
      ["Record DONE time"
       (progn (setq org-log-done (not org-log-done))
@@ -15284,7 +15585,8 @@
 	  (forward-char -1)
 	  (if (memq (preceding-char) '(?\n ?\^M))
 	      ;; leave blank line before heading
-	      (forward-char -1))))))
+	      (forward-char -1)))))
+  (point))
 
 (defun org-show-subtree ()
   "Show everything after this heading at deeper levels."
@@ -15334,8 +15636,12 @@
 			   (org-invisible-p)))
        (org-show-hierarchy-above)))
 
+
+;;; Experimental code
+
+
 ;;; Finish up
-
+	
 (provide 'org)
 
 (run-hooks 'org-load-hook)