]> code.delx.au - gnu-emacs/commitdiff
New files org-attach.el, org-list.el, org-plot.el.
authorCarsten Dominik <dominik@science.uva.nl>
Sun, 12 Oct 2008 06:18:14 +0000 (06:18 +0000)
committerCarsten Dominik <dominik@science.uva.nl>
Sun, 12 Oct 2008 06:18:14 +0000 (06:18 +0000)
etc/ChangeLog
lisp/org/org-attach.el [new file with mode: 0644]
lisp/org/org-list.el [new file with mode: 0644]
lisp/org/org-plot.el [new file with mode: 0644]

index af468126952e9a4cc9ec62da87a65115662eaed4..f849a96a66ebfa7c59dfdfbf20914cdf1dcd8398 100644 (file)
@@ -1,3 +1,12 @@
+2008-10-12  Carsten Dominik  <dominik@science.uva.nl>
+
+       * refcards/orgcard.tex: Add description for attachments, plus
+       minor changes.
+
+2008-07-24  Carsten Dominik  <dominik@science.uva.nl>
+
+       * refcards/orgcard.tex: Minor fixes.
+
 2008-09-08  Daiki Ueno  <ueno@unixuser.org>
 
        * TODO: Remove the entry describing auto-encryption.
        * PROBLEMS:
        * MACHINES: Remove VMS info.
 
+>>>>>>> 1.736
 2008-07-27  Dan Nicolaescu  <dann@ics.uci.edu>
 
        * PROBLEMS:
        * MACHINES: Remove mentions of Mac Carbon.
 
+>>>>>>> 1.727
 2008-07-24  Vinicius Jose Latorre  <viniciusjl@ig.com.br>
 
        * NEWS: New function `diff-show-trailing-blanks' in diff-mode.el.
diff --git a/lisp/org/org-attach.el b/lisp/org/org-attach.el
new file mode 100644 (file)
index 0000000..ae36323
--- /dev/null
@@ -0,0 +1,339 @@
+;;; org-attach.el --- Manage file attachments to org-mode tasks
+
+;; Copyright (C) 2008 Free Software Foundation, Inc.
+
+;; Author: John Wiegley <johnw@newartisans.com>
+;; Keywords: org data task
+;; Version: 6.09a
+
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; See the Org-mode manual for information on how to use it.
+;;
+;; Attachments are managed in a special directory called "data", which
+;; lives in the directory given by `org-directory'.  If this data
+;; directory is initialized as a Git repository, then org-attach will
+;; automatically commit changes when it sees them.
+;;
+;; Attachment directories are identified using a UUID generated for the
+;; task which has the attachments.  These are added as property to the
+;; task when necessary, and should not be deleted or changed by the
+;; user, ever.  UUIDs are generated by a mechanism defined in the variable
+;; `org-id-method'.
+
+;;; Code:
+
+(eval-when-compile
+  (require 'cl))
+(require 'org-id)
+(require 'org)
+
+(defgroup org-attach nil
+  "Options concerning entry attachments in Org-mode."
+  :tag "Org Attach"
+  :group 'org)
+
+(defcustom org-attach-directory "data/"
+  "The directory where attachments are stored.
+If this is a relative path, it will be interpreted relative to the directory
+where the Org file lives."
+  :group 'org-attach
+  :type 'direcory)
+
+(defcustom org-attach-auto-tag "ATTACH"
+  "Tag that will be triggered automatically when an entry has an attachment."
+  :group 'org-attach
+  :type '(choice
+         (const :tag "None" nil)
+         (string :tag "Tag")))
+
+(defcustom org-attach-file-list-property "Attachments"
+  "The property used to keep a list of attachment belonging to this entry.
+This is not really needed, so you may set this to nil if you don't want it."
+  :group 'org-attach
+  :type '(choice
+         (const :tag "None" nil)
+         (string :tag "Tag")))
+
+(defcustom org-attach-method 'cp
+  "The preferred method to attach a file.
+Allowed values are:
+
+mv    rename the file to move it into the attachment directory
+cp    copy the file
+ln    create a hard link.  Note that this is not supported
+      on all systems, and then the result is not defined."
+  :group 'org-attach
+  :type '(choice
+         (const :tag "Copy" cp)
+         (const :tag "Move/Rename" mv)
+         (const :tag "Link" ln)))
+
+(defcustom org-attach-expert nil
+  "Non-nil means do not show the splash buffer with the attach dispatcher."
+  :group 'org-attach
+  :type 'boolean)
+
+;;;###autoload
+(defun org-attach ()
+  "The dispatcher for attachment commands.
+Shows a list of commands and prompts for another key to execute a command."
+  (interactive)
+  (let (c marker)
+    (when (eq major-mode 'org-agenda-mode)
+      (setq marker (or (get-text-property (point) 'org-hd-marker)
+                      (get-text-property (point) 'org-marker)))
+      (unless marker
+       (error "No task in current line")))
+    (save-excursion
+      (when marker
+       (set-buffer (marker-buffer marker))
+       (goto-char marker))
+      (org-back-to-heading t)
+      (save-excursion
+       (save-window-excursion
+         (unless org-attach-expert
+           (with-output-to-temp-buffer "*Org Attach*"
+             (princ "Select an Attachment Command:
+
+a       Select a file and attach it to the task, using `org-attach-method'.
+c/m/l   Attach a file using copy/move/link method.
+n       Create a new attachment, as an Emacs buffer.
+z       Synchronize the current task with its attachment
+        directory, in case you added attachments yourself.
+
+o       Open current task's attachments.
+O       Like \"o\", but force opening in Emacs.
+f       Open current task's attachment directory.
+F       Like \"f\", but force using dired in Emacs.
+
+d       Delete one attachment, you will be prompted for a file name.
+D       Delete all of a task's attachments.  A safer way is
+        to open the directory in dired and delete from there.")))
+         (shrink-window-if-larger-than-buffer (get-buffer-window "*Org Attach*"))
+         (message "Select command: [acmlzoOfFdD]")
+         (setq c (read-char-exclusive))
+         (and (get-buffer "*Org Attach*") (kill-buffer "*Org Attach*"))))
+      (cond
+       ((memq c '(?a ?\C-a)) (call-interactively 'org-attach-attach))
+       ((memq c '(?c ?\C-c))
+       (let ((org-attach-method 'cp)) (call-interactively 'org-attach-attach)))
+       ((memq c '(?m ?\C-m))
+       (let ((org-attach-method 'mv)) (call-interactively 'org-attach-attach)))
+       ((memq c '(?l ?\C-l))
+       (let ((org-attach-method 'ln)) (call-interactively 'org-attach-attach)))
+       ((memq c '(?n ?\C-n)) (call-interactively 'org-attach-new))
+       ((memq c '(?z ?\C-z)) (call-interactively 'org-attach-sync))
+       ((memq c '(?o ?\C-o)) (call-interactively 'org-attach-open))
+       ((eq c ?O)            (call-interactively 'org-attach-open-in-emacs))
+       ((memq c '(?f ?\C-f)) (call-interactively 'org-attach-reveal))
+       ((memq c '(?F))       (call-interactively 'org-attach-reveal-in-emacs))
+       ((memq c '(?d ?\C-d)) (call-interactively
+                             'org-attach-delete-one))
+       ((eq c ?D)            (call-interactively 'org-attach-delete-all))
+       ((eq c ?q)            (message "Abort"))
+       (t (error "No such attachment command %c" c))))))
+
+(defun org-attach-dir (&optional create-if-not-exists-p)
+  "Return the directory associated with the current entry.
+If the directory does not exist and CREATE-IF-NOT-EXISTS-P is non-nil,
+the directory and the corresponding ID will be created."
+  (let ((uuid (org-id-get (point) create-if-not-exists-p)))
+    (when (or uuid create-if-not-exists-p)
+      (unless uuid
+       (let ((uuid-string (shell-command-to-string "uuidgen")))
+         (setf uuid-string
+               (substring uuid-string 0 (1- (length uuid-string))))
+         (org-entry-put (point) "ID" uuid-string)
+         (setf uuid uuid-string)))
+      (let ((attach-dir (expand-file-name
+                        (format "%s/%s"
+                                (substring uuid 0 2)
+                                (substring uuid 2))
+                        (expand-file-name org-attach-directory))))
+       (if (and create-if-not-exists-p
+                (not (file-directory-p attach-dir)))
+           (make-directory attach-dir t))
+       (and (file-exists-p attach-dir)
+            attach-dir)))))
+
+(defun org-attach-commit ()
+  "Commit changes to git if `org-attach-directory' is properly initialized.
+This checks for the existence of a \".git\" directory in that directory."
+  (let ((dir (expand-file-name org-attach-directory)))
+    (if (file-exists-p (expand-file-name ".git" dir))
+       (shell-command
+        (concat "(cd " dir "; "
+                " git add .; "
+                " git ls-files --deleted -z | xargs -0 git rm; "
+                " git commit -m 'Synchronized attachments')")))))
+  
+(defun org-attach-tag (&optional off)
+  "Turn the autotag on or (if OFF is set) off."
+  (when org-attach-auto-tag
+    (save-excursion
+      (org-back-to-heading t)
+      (org-toggle-tag org-attach-auto-tag (if off 'off 'on)))))
+
+(defun org-attach-untag ()
+  "Turn the autotag off."
+  (org-attach-tag 'off))
+
+(defun org-attach-attach (file &optional visit-dir method)
+  "Move/copy/link FILE into the attachment directory of the current task.
+If VISIT-DIR is non-nil, visit the directory with dired.
+METHOD may be `cp', `mv', or `ln', default taken from `org-attach-method'."
+  (interactive "fFile to keep as an attachment: \nP")
+  (setq method (or method org-attach-method))
+  (let ((basename (file-name-nondirectory file)))
+    (when org-attach-file-list-property
+      (org-entry-add-to-multivalued-property
+       (point) org-attach-file-list-property basename))
+    (let* ((attach-dir (org-attach-dir t))
+          (fname (expand-file-name basename attach-dir)))
+      (cond
+       ((eq method 'mv)        (rename-file file fname))
+       ((eq method 'cp)        (copy-file file fname))
+       ((eq method 'ln) (add-name-to-file file fname)))
+      (org-attach-commit)
+      (org-attach-tag)
+      (if visit-dir
+         (dired attach-dir)
+       (message "File \"%s\" is now a task attachment." basename)))))
+
+(defun org-attach-attach-cp ()
+  "Attach a file by copying it."
+  (interactive)
+  (let ((org-attach-method 'cp)) (call-interactively 'org-attach-attach)))
+(defun org-attach-attach-mv ()
+  "Attach a file by moving (renaming) it."
+  (interactive)
+  (let ((org-attach-method 'mv)) (call-interactively 'org-attach-attach)))
+(defun org-attach-attach-ln ()
+  "Attach a file by creating a hard link to it.
+Beware that this does not work on systems that do not support hard links.
+On some systems, this apparently does copy the file instead."
+  (interactive)
+  (let ((org-attach-method 'ln)) (call-interactively 'org-attach-attach)))
+
+(defun org-attach-new (file)
+  "Create a new attachment FILE for the current task.
+The attachment is created as an Emacs buffer."
+  (interactive "sCreate attachment named: ")
+  (when org-attach-file-list-property
+    (org-entry-add-to-multivalued-property
+     (point) org-attach-file-list-property file))
+  (let ((attach-dir (org-attach-dir t)))
+    (org-attach-tag)
+    (find-file (expand-file-name file attach-dir))
+    (message "New attachment %s" file)))
+
+(defun org-attach-delete-one (&optional file)
+  "Delete a single attachment."
+  (interactive)
+  (let* ((attach-dir (org-attach-dir t))
+        (files (org-attach-file-list attach-dir))
+        (file (or file
+                  (completing-read
+                   "Delete attachment: "
+                   (mapcar (lambda (f)
+                             (list (file-name-nondirectory f)))
+                           files)))))
+    (setq file (expand-file-name file attach-dir))
+    (unless (file-exists-p file)
+      (error "No such attachment: %s" file))
+    (delete-file file)))
+
+(defun org-attach-delete-all (&optional force)
+  "Delete all attachments from the current task.
+This actually deletes the entire attachment directory.
+A safer way is to open the directory in dired and delete from there."
+  (interactive "P")
+  (when org-attach-file-list-property
+    (org-entry-delete (point) org-attach-file-list-property))
+  (let ((attach-dir (org-attach-dir)))
+    (when 
+       (and attach-dir
+            (or force
+                (y-or-n-p "Are you sure you want to remove all attachments of this entry? ")))
+      (shell-command (format "rm -fr %s" attach-dir))
+      (message "Attachment directory removed")
+      (org-attach-commit)
+      (org-attach-untag))))
+
+(defun org-attach-sync ()
+  "Synchronize the current tasks with its attachments.
+This can be used after files have been added externally."
+  (interactive)
+  (org-attach-commit)
+  (when org-attach-file-list-property
+    (org-entry-delete (point) org-attach-file-list-property))
+  (let ((attach-dir (org-attach-dir)))
+    (when attach-dir
+      (let ((files (org-attach-file-list attach-dir)))
+       (and files (org-attach-tag))
+       (when org-attach-file-list-property
+         (dolist (file files)
+           (unless (string-match "^\\." file)
+             (org-entry-add-to-multivalued-property
+              (point) org-attach-file-list-property file))))))))
+
+(defun org-attach-file-list (dir)
+  "Return a list of files in the attachment directory.
+This ignores files starting with a \".\", and files ending in \"~\"."
+  (delq nil
+       (mapcar (lambda (x) (if (string-match "^\\." x) nil x))
+               (directory-files dir nil "[^~]\\'"))))
+
+(defun org-attach-reveal ()
+  "Show the attachment directory of the current task in dired."
+  (interactive)
+  (let ((attach-dir (org-attach-dir t)))
+    (org-open-file attach-dir)))
+
+(defun org-attach-reveal-in-emacs ()
+  "Show the attachment directory of the current task.
+This will attempt to use an external program to show the directory."
+  (interactive)
+  (let ((attach-dir (org-attach-dir t)))
+    (dired attach-dir)))
+
+(defun org-attach-open (&optional in-emacs)
+  "Open an attachment of the current task.
+If there are more than one attachment, you will be prompted for the file name.
+This command will open the file using the settings in `org-file-apps'
+and in the system-specific variants of this variable.
+If IN-EMACS is non-nil, force opening in Emacs."
+  (interactive "P")
+  (let* ((attach-dir (org-attach-dir t))
+        (files (org-attach-file-list attach-dir))
+        (file (if (= (length files) 1)
+                  (car files)
+                (completing-read "Open attachment: "
+                                 (mapcar 'list files) nil t))))
+    (org-open-file (expand-file-name file attach-dir) in-emacs)))
+
+(defun org-attach-open-in-emacs ()
+  "Open attachment, force opening in Emacs.
+See `org-attach-open'."
+  (interactive)
+  (org-attach-open 'in-emacs))
+
+(provide 'org-attach)
+
+;;; org-attach.el ends here
diff --git a/lisp/org/org-list.el b/lisp/org/org-list.el
new file mode 100644 (file)
index 0000000..d5e7556
--- /dev/null
@@ -0,0 +1,1042 @@
+;;; org-list.el --- Plain lists for Org-mode
+;;
+;; Copyright (C) 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+;;
+;; Author: Carsten Dominik <carsten at orgmode dot org>
+;;         Bastien Guerry <bzg AT altern DOT org>
+;; Keywords: outlines, hypermedia, calendar, wp
+;; Homepage: http://orgmode.org
+;; Version: 6.09a
+;;
+;; This file is part of GNU Emacs.
+;;
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;
+;;; Commentary:
+
+;; This file contains the code dealing with plain lists in Org-mode.
+
+;;; Code:
+
+(require 'org-macs)
+(require 'org-compat)
+
+(defvar org-blank-before-new-entry)
+(defvar org-M-RET-may-split-line)
+
+(declare-function org-invisible-p "org" ())
+(declare-function org-on-heading-p "org" (&optional invisible-ok))
+(declare-function outline-next-heading "org" ())
+(declare-function outline-back-to-heading "org" (&optional invisible-ok))
+(declare-function org-back-to-heading "org" (&optional invisible-ok))
+(declare-function org-back-over-empty-lines "org" ())
+(declare-function org-skip-whitespace "org" ())
+(declare-function org-trim "org" (s))
+(declare-function org-get-indentation "org" (&optional line))
+
+(defgroup org-plain-lists nil
+  "Options concerning plain lists in Org-mode."
+  :tag "Org Plain lists"
+  :group 'org-structure)
+
+(defcustom org-cycle-include-plain-lists nil
+  "Non-nil means, include plain lists into visibility cycling.
+This means that during cycling, plain list items will *temporarily* be
+interpreted as outline headlines with a level given by 1000+i where i is the
+indentation of the bullet.  In all other operations, plain list items are
+not seen as headlines.  For example, you cannot assign a TODO keyword to
+such an item."
+  :group 'org-plain-lists
+  :type 'boolean)
+
+(defcustom org-plain-list-ordered-item-terminator t
+  "The character that makes a line with leading number an ordered list item.
+Valid values are ?. and ?\).  To get both terminators, use t.  While
+?. may look nicer, it creates the danger that a line with leading
+number may be incorrectly interpreted as an item.  ?\) therefore is
+the safe choice."
+  :group 'org-plain-lists
+  :type '(choice (const :tag "dot like in \"2.\"" ?.)
+                (const :tag "paren like in \"2)\"" ?\))
+                (const :tab "both" t)))
+
+(defcustom org-empty-line-terminates-plain-lists nil
+  "Non-nil means, an empty line ends all plain list levels.
+When nil, empty lines are part of the preceeding item."
+  :group 'org-plain-lists
+  :type 'boolean)
+
+(defcustom org-auto-renumber-ordered-lists t
+  "Non-nil means, automatically renumber ordered plain lists.
+Renumbering happens when the sequence have been changed with
+\\[org-shiftmetaup] or \\[org-shiftmetadown].  After other editing commands,
+use \\[org-ctrl-c-ctrl-c] to trigger renumbering."
+  :group 'org-plain-lists
+  :type 'boolean)
+
+(defcustom org-provide-checkbox-statistics t
+  "Non-nil means, update checkbox statistics after insert and toggle.
+When this is set, checkbox statistics is updated each time you either insert
+a new checkbox with \\[org-insert-todo-heading] or toggle a checkbox
+with \\[org-ctrl-c-ctrl-c\\]."
+  :group 'org-plain-lists
+  :type 'boolean)
+
+(defcustom org-description-max-indent 20
+  "Maximum indentation for the second line of a description list.
+When the indentation would be larger than this, it will become
+5 characters instead."
+  :group 'org-plain-lists
+  :type 'integer)
+
+(defvar org-list-beginning-re
+  "^\\([ \t]*\\)\\([-+*]\\|[0-9]+[.)]\\) +\\(.*\\)$")
+
+(defcustom org-list-radio-list-templates
+  '((latex-mode "% BEGIN RECEIVE ORGLST %n
+% END RECEIVE ORGLST %n
+\\begin{comment}
+#+ORGLST: SEND %n org-list-to-latex
+| | |
+\\end{comment}\n")
+    (texinfo-mode "@c BEGIN RECEIVE ORGLST %n
+@c END RECEIVE ORGLST %n
+@ignore
+#+ORGLST: SEND %n org-list-to-texinfo
+| | |
+@end ignore\n")
+    (html-mode "<!-- BEGIN RECEIVE ORGLST %n -->
+<!-- END RECEIVE ORGLST %n -->
+<!--
+#+ORGLST: SEND %n org-list-to-html
+| | |
+-->\n"))
+  "Templates for radio lists in different major modes.
+All occurrences of %n in a template will be replaced with the name of the
+list, obtained by prompting the user."
+  :group 'org-plain-lists
+  :type '(repeat
+         (list (symbol :tag "Major mode")
+               (string :tag "Format"))))
+
+;;;; Plain list items, including checkboxes
+
+;;; Plain list items
+
+(defun org-at-item-p ()
+  "Is point in a line starting a hand-formatted item?"
+  (let ((llt org-plain-list-ordered-item-terminator))
+    (save-excursion
+      (goto-char (point-at-bol))
+      (looking-at
+       (cond
+       ((eq llt t)  "\\([ \t]*\\([-+]\\|\\([0-9]+[.)]\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
+       ((= llt ?.)  "\\([ \t]*\\([-+]\\|\\([0-9]+\\.\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
+       ((= llt ?\)) "\\([ \t]*\\([-+]\\|\\([0-9]+))\\)\\|[ \t]+\\*\\)\\( \\|$\\)")
+       (t (error "Invalid value of `org-plain-list-ordered-item-terminator'")))))))
+
+(defun org-in-item-p ()
+  "It the cursor inside a plain list item.
+Does not have to be the first line."
+  (save-excursion
+    (condition-case nil
+       (progn
+         (org-beginning-of-item)
+         (org-at-item-p)
+         t)
+      (error nil))))
+
+(defun org-insert-item (&optional checkbox)
+  "Insert a new item at the current level.
+Return t when things worked, nil when we are not in an item."
+  (when (save-excursion
+         (condition-case nil
+             (progn
+               (org-beginning-of-item)
+               (org-at-item-p)
+               (if (org-invisible-p) (error "Invisible item"))
+               t)
+           (error nil)))
+    (let* ((bul (match-string 0))
+          (descp (save-excursion (goto-char (match-beginning 0))
+                                 (beginning-of-line 1)
+                                 (save-match-data
+                                   (looking-at "[ \t]*.*? ::"))))
+          (eow (save-excursion (beginning-of-line 1) (looking-at "[ \t]*")
+                               (match-end 0)))
+          (blank (cdr (assq 'plain-list-item org-blank-before-new-entry)))
+          pos)
+      (if descp (setq checkbox nil))
+      (cond
+       ((and (org-at-item-p) (<= (point) eow))
+       ;; before the bullet
+       (beginning-of-line 1)
+       (open-line (if blank 2 1)))
+       ((<= (point) eow)
+       (beginning-of-line 1))
+       (t
+       (unless (org-get-alist-option org-M-RET-may-split-line 'item)
+         (end-of-line 1)
+         (delete-horizontal-space))
+       (newline (if blank 2 1))))
+      (insert bul
+             (if checkbox "[ ]" "")
+             (if descp (concat (if checkbox " " "")
+                               (read-string "Term: ") " :: ") ""))
+      (just-one-space)
+      (setq pos (point))
+      (end-of-line 1)
+      (unless (= (point) pos) (just-one-space) (backward-delete-char 1)))
+    (org-maybe-renumber-ordered-list)
+    (and checkbox (org-update-checkbox-count-maybe))
+    t))
+
+;;; Checkboxes
+
+(defun org-at-item-checkbox-p ()
+  "Is point at a line starting a plain-list item with a checklet?"
+  (and (org-at-item-p)
+       (save-excursion
+        (goto-char (match-end 0))
+        (skip-chars-forward " \t")
+        (looking-at "\\[[- X]\\]"))))
+
+(defun org-toggle-checkbox (&optional arg)
+  "Toggle the checkbox in the current line."
+  (interactive "P")
+  (catch 'exit
+    (let (beg end status (firstnew 'unknown))
+      (cond
+       ((org-region-active-p)
+       (setq beg (region-beginning) end (region-end)))
+       ((org-on-heading-p)
+       (setq beg (point) end (save-excursion (outline-next-heading) (point))))
+       ((org-at-item-checkbox-p)
+       (let ((pos (point)))
+         (replace-match
+          (cond (arg "[-]")
+                ((member (match-string 0) '("[ ]" "[-]")) "[X]")
+                (t "[ ]"))
+          t t)
+         (goto-char pos))
+       (throw 'exit t))
+       (t (error "Not at a checkbox or heading, and no active region")))
+      (save-excursion
+       (goto-char beg)
+       (while (< (point) end)
+         (when (org-at-item-checkbox-p)
+           (setq status (equal (match-string 0) "[X]"))
+           (when (eq firstnew 'unknown)
+             (setq firstnew (not status)))
+           (replace-match
+            (if (if arg (not status) firstnew) "[X]" "[ ]") t t))
+         (beginning-of-line 2)))))
+  (org-update-checkbox-count-maybe))
+
+(defun org-update-checkbox-count-maybe ()
+  "Update checkbox statistics unless turned off by user."
+  (when org-provide-checkbox-statistics
+    (org-update-checkbox-count)))
+
+(defun org-update-checkbox-count (&optional all)
+ "Update the checkbox statistics in the current section.
+This will find all statistic cookies like [57%] and [6/12] and update them
+with the current numbers.  With optional prefix argument ALL, do this for
+the whole buffer."
+ (interactive "P")
+ (save-excursion
+   (let* ((buffer-invisibility-spec (org-inhibit-invisibility)) ; Emacs 21
+         (beg (condition-case nil
+                  (progn (outline-back-to-heading) (point))
+                (error (point-min))))
+         (end (move-marker (make-marker)
+                           (progn (outline-next-heading) (point))))
+         (re "\\(\\(\\[[0-9]*%\\]\\)\\|\\(\\[[0-9]*/[0-9]*\\]\\)\\)")
+         (re-box "^[ \t]*\\([-+*]\\|[0-9]+[.)]\\) +\\(\\[[- X]\\]\\)")
+         (re-find (concat re "\\|" re-box))
+         beg-cookie end-cookie is-percent c-on c-off lim
+          eline curr-ind next-ind continue-from startsearch
+          (cstat 0)
+          )
+     (when all
+       (goto-char (point-min))
+       (outline-next-heading)
+       (setq beg (point) end (point-max)))
+     (goto-char end)
+     ;; find each statistic cookie
+     (while (re-search-backward re-find beg t)
+       (setq beg-cookie (match-beginning 1)
+             end-cookie (match-end 1)
+            cstat (+ cstat (if end-cookie 1 0))
+            startsearch (point-at-eol)
+            continue-from (point-at-bol)
+             is-percent (match-beginning 2)
+            lim (cond
+                 ((org-on-heading-p) (outline-next-heading) (point))
+                 ((org-at-item-p) (org-end-of-item) (point))
+                 (t nil))
+             c-on 0
+             c-off 0)
+       (when lim
+         ;; find first checkbox for this cookie and gather
+         ;; statistics from all that are at this indentation level
+         (goto-char startsearch)
+         (if (re-search-forward re-box lim t)
+             (progn
+               (org-beginning-of-item)
+               (setq curr-ind (org-get-indentation))
+               (setq next-ind curr-ind)
+               (while (and (bolp) (org-at-item-p) (= curr-ind next-ind))
+                 (save-excursion (end-of-line) (setq eline (point)))
+                 (if (re-search-forward re-box eline t)
+                    (if (member (match-string 2) '("[ ]" "[-]"))
+                        (setq c-off (1+ c-off))
+                       (setq c-on (1+ c-on))
+                       )
+                   )
+                 (org-end-of-item)
+                 (setq next-ind (org-get-indentation))
+                 )))
+        (goto-char continue-from)
+         ;; update cookie
+        (when end-cookie
+          (delete-region beg-cookie end-cookie)
+          (goto-char beg-cookie)
+          (insert
+           (if is-percent
+               (format "[%d%%]" (/ (* 100 c-on) (max 1 (+ c-on c-off))))
+             (format "[%d/%d]" c-on (+ c-on c-off)))))
+         ;; update items checkbox if it has one
+         (when (org-at-item-p)
+           (org-beginning-of-item)
+           (when (and (> (+ c-on c-off) 0)
+                     (re-search-forward re-box (point-at-eol) t))
+             (setq beg-cookie (match-beginning 2)
+                   end-cookie (match-end       2))
+             (delete-region beg-cookie end-cookie)
+             (goto-char beg-cookie)
+             (cond ((= c-off 0) (insert "[X]"))
+                   ((= c-on  0) (insert "[ ]"))
+                   (t           (insert "[-]")))
+             )))
+       (goto-char continue-from))
+     (when (interactive-p)
+       (message "Checkbox satistics updated %s (%d places)"
+               (if all "in entire file" "in current outline entry") cstat)))))
+
+(defun org-get-checkbox-statistics-face ()
+  "Select the face for checkbox statistics.
+The face will be `org-done' when all relevant boxes are checked.  Otherwise
+it will be `org-todo'."
+  (if (match-end 1)
+      (if (equal (match-string 1) "100%") 'org-done 'org-todo)
+    (if (and (> (match-end 2) (match-beginning 2))
+            (equal (match-string 2) (match-string 3)))
+       'org-done
+      'org-todo)))
+
+(defun org-beginning-of-item ()
+  "Go to the beginning of the current hand-formatted item.
+If the cursor is not in an item, throw an error."
+  (interactive)
+  (let ((pos (point))
+       (limit (save-excursion
+                (condition-case nil
+                    (progn
+                      (org-back-to-heading)
+                      (beginning-of-line 2) (point))
+                  (error (point-min)))))
+       (ind-empty (if org-empty-line-terminates-plain-lists 0 10000))
+       ind ind1)
+    (if (org-at-item-p)
+       (beginning-of-line 1)
+      (beginning-of-line 1)
+      (skip-chars-forward " \t")
+      (setq ind (current-column))
+      (if (catch 'exit
+           (while t
+             (beginning-of-line 0)
+             (if (or (bobp) (< (point) limit)) (throw 'exit nil))
+
+             (if (looking-at "[ \t]*$")
+                 (setq ind1 ind-empty)
+               (skip-chars-forward " \t")
+               (setq ind1 (current-column)))
+             (if (< ind1 ind)
+                 (progn (beginning-of-line 1) (throw 'exit (org-at-item-p))))))
+         nil
+       (goto-char pos)
+       (error "Not in an item")))))
+
+(defun org-end-of-item ()
+  "Go to the end of the current hand-formatted item.
+If the cursor is not in an item, throw an error."
+  (interactive)
+  (let* ((pos (point))
+        ind1
+        (ind-empty (if org-empty-line-terminates-plain-lists 0 10000))
+        (limit (save-excursion (outline-next-heading) (point)))
+        (ind (save-excursion
+               (org-beginning-of-item)
+               (skip-chars-forward " \t")
+               (current-column)))
+        (end (catch 'exit
+               (while t
+                 (beginning-of-line 2)
+                 (if (eobp) (throw 'exit (point)))
+                 (if (>= (point) limit) (throw 'exit (point-at-bol)))
+                 (if (looking-at "[ \t]*$")
+                     (setq ind1 ind-empty)
+                   (skip-chars-forward " \t")
+                   (setq ind1 (current-column)))
+                 (if (<= ind1 ind)
+                     (throw 'exit (point-at-bol)))))))
+    (if end
+       (goto-char end)
+      (goto-char pos)
+      (error "Not in an item"))))
+
+(defun org-next-item ()
+  "Move to the beginning of the next item in the current plain list.
+Error if not at a plain list, or if this is the last item in the list."
+  (interactive)
+  (let (ind ind1 (pos (point)))
+    (org-beginning-of-item)
+    (setq ind (org-get-indentation))
+    (org-end-of-item)
+    (setq ind1 (org-get-indentation))
+    (unless (and (org-at-item-p) (= ind ind1))
+      (goto-char pos)
+      (error "On last item"))))
+
+(defun org-previous-item ()
+  "Move to the beginning of the previous item in the current plain list.
+Error if not at a plain list, or if this is the first item in the list."
+  (interactive)
+  (let (beg ind ind1 (pos (point)))
+    (org-beginning-of-item)
+    (setq beg (point))
+    (setq ind (org-get-indentation))
+    (goto-char beg)
+    (catch 'exit
+      (while t
+       (beginning-of-line 0)
+       (if (looking-at "[ \t]*$")
+           nil
+         (if (<= (setq ind1 (org-get-indentation)) ind)
+             (throw 'exit t)))))
+    (condition-case nil
+       (if (or (not (org-at-item-p))
+               (< ind1 (1- ind)))
+           (error "")
+         (org-beginning-of-item))
+      (error (goto-char pos)
+            (error "On first item")))))
+
+(defun org-first-list-item-p ()
+  "Is this heading the item in a plain list?"
+  (unless (org-at-item-p)
+    (error "Not at a plain list item"))
+  (org-beginning-of-item)
+  (= (point) (save-excursion (org-beginning-of-item-list))))
+
+(defun org-move-item-down ()
+  "Move the plain list item at point down, i.e. swap with following item.
+Subitems (items with larger indentation) are considered part of the item,
+so this really moves item trees."
+  (interactive)
+  (let ((col (current-column))
+       (pos (point))
+       beg beg0 end end0 ind ind1  txt ne-end ne-beg)
+    (org-beginning-of-item)
+    (setq beg0 (point))
+    (save-excursion
+      (setq ne-beg (org-back-over-empty-lines))
+      (setq beg (point)))
+    (goto-char beg0)
+    (setq ind (org-get-indentation))
+    (org-end-of-item)
+    (setq end0 (point))
+    (setq ind1 (org-get-indentation))
+    (setq ne-end (org-back-over-empty-lines))
+    (setq end (point))
+    (goto-char beg0)
+    (when (and (org-first-list-item-p) (< ne-end ne-beg))
+      ;; include less whitespace
+      (save-excursion
+       (goto-char beg)
+       (forward-line (- ne-beg ne-end))
+       (setq beg (point))))
+    (goto-char end0)
+    (if (and (org-at-item-p) (= ind ind1))
+       (progn
+         (org-end-of-item)
+         (org-back-over-empty-lines)
+         (setq txt (buffer-substring beg end))
+         (save-excursion
+           (delete-region beg end))
+         (setq pos (point))
+         (insert txt)
+         (goto-char pos) (org-skip-whitespace)
+         (org-maybe-renumber-ordered-list)
+         (move-to-column col))
+      (goto-char pos)
+      (move-to-column col)
+      (error "Cannot move this item further down"))))
+
+(defun org-move-item-up (arg)
+  "Move the plain list item at point up, i.e. swap with previous item.
+Subitems (items with larger indentation) are considered part of the item,
+so this really moves item trees."
+  (interactive "p")
+  (let ((col (current-column)) (pos (point))
+       beg beg0 end ind ind1  txt
+       ne-beg ne-ins ins-end)
+    (org-beginning-of-item)
+    (setq beg0 (point))
+    (setq ind (org-get-indentation))
+    (save-excursion
+      (setq ne-beg (org-back-over-empty-lines))
+      (setq beg (point)))
+    (goto-char beg0)
+    (org-end-of-item)
+    (org-back-over-empty-lines)
+    (setq end (point))
+    (goto-char beg0)
+    (catch 'exit
+      (while t
+       (beginning-of-line 0)
+       (if (looking-at "[ \t]*$")
+           (if org-empty-line-terminates-plain-lists
+               (progn
+                 (goto-char pos)
+                 (error "Cannot move this item further up"))
+             nil)
+         (if (<= (setq ind1 (org-get-indentation)) ind)
+             (throw 'exit t)))))
+    (condition-case nil
+       (org-beginning-of-item)
+      (error (goto-char beg0)
+            (move-to-column col)
+            (error "Cannot move this item further up")))
+    (setq ind1 (org-get-indentation))
+    (if (and (org-at-item-p) (= ind ind1))
+       (progn
+         (setq ne-ins (org-back-over-empty-lines))
+         (setq txt (buffer-substring beg end))
+         (save-excursion
+           (delete-region beg end))
+         (setq pos (point))
+         (insert txt)
+         (setq ins-end (point))
+         (goto-char pos) (org-skip-whitespace)
+
+         (when (and (org-first-list-item-p) (> ne-ins ne-beg))
+           ;; Move whitespace back to beginning
+           (save-excursion
+             (goto-char ins-end)
+             (let ((kill-whole-line t))
+               (kill-line (- ne-ins ne-beg)) (point)))
+           (insert (make-string (- ne-ins ne-beg) ?\n)))
+
+         (org-maybe-renumber-ordered-list)
+         (move-to-column col))
+      (goto-char pos)
+      (move-to-column col)
+      (error "Cannot move this item further up"))))
+
+(defun org-maybe-renumber-ordered-list ()
+  "Renumber the ordered list at point if setup allows it.
+This tests the user option `org-auto-renumber-ordered-lists' before
+doing the renumbering."
+  (interactive)
+  (when (and org-auto-renumber-ordered-lists
+            (org-at-item-p))
+    (if (match-beginning 3)
+       (org-renumber-ordered-list 1)
+      (org-fix-bullet-type))))
+
+(defun org-maybe-renumber-ordered-list-safe ()
+  (condition-case nil
+      (save-excursion
+       (org-maybe-renumber-ordered-list))
+    (error nil)))
+
+(defun org-cycle-list-bullet (&optional which)
+  "Cycle through the different itemize/enumerate bullets.
+This cycle the entire list level through the sequence:
+
+   `-'  ->  `+'  ->  `*'  ->  `1.'  ->  `1)'
+
+If WHICH is a string, use that as the new bullet.  If WHICH is an integer,
+0 meand `-', 1 means `+' etc."
+  (interactive "P")
+  (org-preserve-lc
+   (org-beginning-of-item-list)
+   (org-at-item-p)
+   (beginning-of-line 1)
+   (let ((current (match-string 0))
+        (prevp (eq which 'previous))
+        new)
+     (setq new (cond
+               ((and (numberp which)
+                     (nth (1- which) '("-" "+" "*" "1." "1)"))))
+               ((string-match "-" current) (if prevp "1)" "+"))
+               ((string-match "\\+" current)
+                (if prevp "-" (if (looking-at "\\S-") "1." "*")))
+               ((string-match "\\*" current) (if prevp "+" "1."))
+               ((string-match "\\." current) (if prevp "*" "1)"))
+               ((string-match ")" current) (if prevp "1." "-"))
+               (t (error "This should not happen"))))
+     (and (looking-at "\\([ \t]*\\)\\S-+") (replace-match (concat "\\1" new)))
+     (org-fix-bullet-type)
+     (org-maybe-renumber-ordered-list))))
+
+(defun org-get-string-indentation (s)
+  "What indentation has S due to SPACE and TAB at the beginning of the string?"
+  (let ((n -1) (i 0) (w tab-width) c)
+    (catch 'exit
+      (while (< (setq n (1+ n)) (length s))
+       (setq c (aref s n))
+       (cond ((= c ?\ ) (setq i (1+ i)))
+             ((= c ?\t) (setq i (* (/ (+ w i) w) w)))
+             (t (throw 'exit t)))))
+    i))
+
+(defun org-renumber-ordered-list (arg)
+  "Renumber an ordered plain list.
+Cursor needs to be in the first line of an item, the line that starts
+with something like \"1.\" or \"2)\"."
+  (interactive "p")
+  (unless (and (org-at-item-p)
+              (match-beginning 3))
+    (error "This is not an ordered list"))
+  (let ((line (org-current-line))
+       (col (current-column))
+       (ind (org-get-string-indentation
+             (buffer-substring (point-at-bol) (match-beginning 3))))
+       ;; (term (substring (match-string 3) -1))
+       ind1 (n (1- arg))
+       fmt bobp)
+    ;; find where this list begins
+    (org-beginning-of-item-list)
+    (setq bobp (bobp))
+    (looking-at "[ \t]*[0-9]+\\([.)]\\)")
+    (setq fmt (concat "%d" (match-string 1)))
+    (beginning-of-line 0)
+    ;; walk forward and replace these numbers
+    (catch 'exit
+      (while t
+       (catch 'next
+         (if bobp (setq bobp nil) (beginning-of-line 2))
+         (if (eobp) (throw 'exit nil))
+         (if (looking-at "[ \t]*$") (throw 'next nil))
+         (skip-chars-forward " \t") (setq ind1 (current-column))
+         (if (> ind1 ind) (throw 'next t))
+         (if (< ind1 ind) (throw 'exit t))
+         (if (not (org-at-item-p)) (throw 'exit nil))
+         (delete-region (match-beginning 2) (match-end 2))
+         (goto-char (match-beginning 2))
+         (insert (format fmt (setq n (1+ n)))))))
+    (goto-line line)
+    (org-move-to-column col)))
+
+(defun org-fix-bullet-type ()
+  "Make sure all items in this list have the same bullet as the firsst item."
+  (interactive)
+  (unless (org-at-item-p) (error "This is not a list"))
+  (let ((line (org-current-line))
+       (col (current-column))
+       (ind (current-indentation))
+       ind1 bullet)
+    ;; find where this list begins
+    (org-beginning-of-item-list)
+    (beginning-of-line 1)
+    ;; find out what the bullet type is
+    (looking-at "[ \t]*\\(\\S-+\\)")
+    (setq bullet (match-string 1))
+    ;; walk forward and replace these numbers
+    (beginning-of-line 0)
+    (catch 'exit
+      (while t
+       (catch 'next
+         (beginning-of-line 2)
+         (if (eobp) (throw 'exit nil))
+         (if (looking-at "[ \t]*$") (throw 'next nil))
+         (skip-chars-forward " \t") (setq ind1 (current-column))
+         (if (> ind1 ind) (throw 'next t))
+         (if (< ind1 ind) (throw 'exit t))
+         (if (not (org-at-item-p)) (throw 'exit nil))
+         (skip-chars-forward " \t")
+         (looking-at "\\S-+")
+         (replace-match bullet))))
+    (goto-line line)
+    (org-move-to-column col)
+    (if (string-match "[0-9]" bullet)
+       (org-renumber-ordered-list 1))))
+
+(defun org-beginning-of-item-list ()
+  "Go to the beginning of the current item list.
+I.e. to the first item in this list."
+  (interactive)
+  (org-beginning-of-item)
+  (let ((pos (point-at-bol))
+        (ind (org-get-indentation))
+       ind1)
+    ;; find where this list begins
+    (catch 'exit
+      (while t
+       (catch 'next
+         (beginning-of-line 0)
+         (if (looking-at "[ \t]*$")
+             (throw (if (bobp) 'exit 'next) t))
+         (skip-chars-forward " \t") (setq ind1 (current-column))
+         (if (or (< ind1 ind)
+                 (and (= ind1 ind)
+                      (not (org-at-item-p)))
+                 (and (= (point-at-bol) (point-min))
+                      (setq pos (point-min))))
+             (throw 'exit t)
+           (when (org-at-item-p) (setq pos (point-at-bol)))))))
+    (goto-char pos)))
+
+
+(defun org-end-of-item-list ()
+  "Go to the end of the current item list.
+I.e. to the text after the last item."
+  (interactive)
+  (org-beginning-of-item)
+  (let ((pos (point-at-bol))
+        (ind (org-get-indentation))
+       ind1)
+    ;; find where this list begins
+    (catch 'exit
+      (while t
+       (catch 'next
+         (beginning-of-line 2)
+         (if (looking-at "[ \t]*$")
+             (throw (if (eobp) 'exit 'next) t))
+         (skip-chars-forward " \t") (setq ind1 (current-column))
+         (if (or (< ind1 ind)
+                 (and (= ind1 ind)
+                      (not (org-at-item-p)))
+                 (eobp))
+             (progn
+               (setq pos (point-at-bol))
+               (throw 'exit t))))))
+    (goto-char pos)))
+
+
+(defvar org-last-indent-begin-marker (make-marker))
+(defvar org-last-indent-end-marker (make-marker))
+
+(defun org-outdent-item (arg)
+  "Outdent a local list item."
+  (interactive "p")
+  (org-indent-item (- arg)))
+
+(defun org-indent-item (arg)
+  "Indent a local list item."
+  (interactive "p")
+  (unless (org-at-item-p)
+    (error "Not on an item"))
+  (save-excursion
+    (let (beg end ind ind1 tmp delta ind-down ind-up)
+      (if (memq last-command '(org-shiftmetaright org-shiftmetaleft))
+         (setq beg org-last-indent-begin-marker
+               end org-last-indent-end-marker)
+       (org-beginning-of-item)
+       (setq beg (move-marker org-last-indent-begin-marker (point)))
+       (org-end-of-item)
+       (setq end (move-marker org-last-indent-end-marker (point))))
+      (goto-char beg)
+      (setq tmp (org-item-indent-positions)
+           ind (car tmp)
+           ind-down (nth 2 tmp)
+           ind-up (nth 1 tmp)
+           delta (if (> arg 0)
+                     (if ind-down (- ind-down ind) 2)
+                   (if ind-up (- ind-up ind) -2)))
+      (if (< (+ delta ind) 0) (error "Cannot outdent beyond margin"))
+      (while (< (point) end)
+       (beginning-of-line 1)
+       (skip-chars-forward " \t") (setq ind1 (current-column))
+       (delete-region (point-at-bol) (point))
+       (or (eolp) (org-indent-to-column (+ ind1 delta)))
+       (beginning-of-line 2))))
+  (org-fix-bullet-type)
+  (org-maybe-renumber-ordered-list-safe)
+  (save-excursion
+    (beginning-of-line 0)
+    (condition-case nil (org-beginning-of-item) (error nil))
+    (org-maybe-renumber-ordered-list-safe)))
+
+(defun org-item-indent-positions ()
+  "Return indentation for plain list items.
+This returns a list with three values:  The current indentation, the
+parent indentation and the indentation a child should habe.
+Assumes cursor in item line."
+  (let* ((bolpos (point-at-bol))
+        (ind (org-get-indentation))
+        ind-down ind-up pos)
+    (save-excursion
+      (org-beginning-of-item-list)
+      (skip-chars-backward "\n\r \t")
+      (when (org-in-item-p)
+       (org-beginning-of-item)
+       (setq ind-up (org-get-indentation))))
+    (setq pos (point))
+    (save-excursion
+      (cond
+       ((and (condition-case nil (progn (org-previous-item) t)
+              (error nil))
+            (or (forward-char 1) t)
+            (re-search-forward "^\\([ \t]*\\([-+]\\|\\([0-9]+[.)]\\)\\)\\|[ \t]+\\*\\)\\( \\|$\\)" bolpos t))
+       (setq ind-down (org-get-indentation)))
+       ((and (goto-char pos)
+            (org-at-item-p))
+       (goto-char (match-end 0))
+       (skip-chars-forward " \t")
+       (setq ind-down (current-column)))))
+    (list ind ind-up ind-down)))
+
+
+;;; Send and receive lists
+
+(defun org-list-parse-list (&optional delete)
+  "Parse the list at point and maybe DELETE it.
+Return a list containing first level items as strings and
+sublevels as a list of strings."
+  (let* ((item-beginning (org-list-item-beginning))
+         (start (car item-beginning))
+         (end (org-list-end (cdr item-beginning)))
+         output itemsep ltype)
+    (while (re-search-forward org-list-beginning-re end t)
+      (goto-char (match-beginning 3))
+      (save-match-data
+        (cond ((string-match "[0-9]" (match-string 2))
+               (setq itemsep "[0-9]+\\(?:\\.\\|)\\)"
+                     ltype 'ordered))
+              ((string-match "^.*::" (match-string 0))
+               (setq itemsep "[-+]" ltype 'descriptive))
+              (t (setq itemsep "[-+]" ltype 'unordered))))
+      (let* ((indent1 (match-string 1))
+            (nextitem (save-excursion
+                        (save-match-data
+                          (or (and (re-search-forward
+                                    (concat "^" indent1 itemsep " *?") end t)
+                                   (match-beginning 0)) end))))
+            (item (buffer-substring
+                   (point)
+                   (or (and (re-search-forward
+                             org-list-beginning-re end t)
+                            (goto-char (match-beginning 0)))
+                       (goto-char end))))
+            (nextindent (match-string 1))
+            (item (org-trim item))
+            (item (if (string-match "^\\[.+\\]" item)
+                      (replace-match "\\\\texttt{\\&}"
+                                     t nil item) item)))
+       (push item output)
+       (when (> (length nextindent)
+                (length indent1))
+         (narrow-to-region (point) nextitem)
+         (push (org-list-parse-list) output)
+         (widen))))
+    (when delete (delete-region start end))
+    (setq output (nreverse output))
+    (push ltype output)))
+
+(defun org-list-item-beginning ()
+  "Find the beginning of the list item.
+Return a cons which car is the beginning position of the item and
+cdr is the indentation string."
+  (save-excursion
+    (if (not (or (looking-at org-list-beginning-re)
+                (re-search-backward
+                 org-list-beginning-re nil t)))
+       (progn (goto-char (point-min)) (point))
+      (cons (match-beginning 0) (match-string 1)))))
+
+(defun org-list-end (indent)
+  "Return the position of the end of the list.
+INDENT is the indentation of the list."
+  (save-excursion
+    (catch 'exit
+      (while (or (looking-at org-list-beginning-re)
+                (looking-at (concat "^" indent "[ \t]+\\|^$")))
+       (if (eq (point) (point-max))
+           (throw 'exit (point-max)))
+       (forward-line 1))) (point)))
+
+(defun org-list-insert-radio-list ()
+  "Insert a radio list template appropriate for this major mode."
+  (interactive)
+  (let* ((e (assq major-mode org-list-radio-list-templates))
+        (txt (nth 1 e))
+        name pos)
+    (unless e (error "No radio list setup defined for %s" major-mode))
+    (setq name (read-string "List name: "))
+    (while (string-match "%n" txt)
+      (setq txt (replace-match name t t txt)))
+    (or (bolp) (insert "\n"))
+    (setq pos (point))
+    (insert txt)
+    (goto-char pos)))
+
+(defun org-list-send-list (&optional maybe)
+  "Send a tranformed version of this list to the receiver position.
+With argument MAYBE, fail quietly if no transformation is defined for
+this list."
+  (interactive)
+  (catch 'exit
+    (unless (org-at-item-p) (error "Not at a list"))
+    (save-excursion
+      (goto-char (car (org-list-item-beginning)))
+      (beginning-of-line 0)
+      (unless (looking-at "#\\+ORGLST: *SEND +\\([a-zA-Z0-9_]+\\) +\\([^ \t\r\n]+\\)\\( +.*\\)?")
+       (if maybe
+           (throw 'exit nil)
+         (error "Don't know how to transform this list"))))
+    (let* ((name (match-string 1))
+           (item-beginning (org-list-item-beginning))
+          (transform (intern (match-string 2)))
+          (txt (buffer-substring-no-properties
+                 (car item-beginning)
+                (org-list-end (cdr item-beginning))))
+          (list (org-list-parse-list))
+           beg)
+      (unless (fboundp transform)
+       (error "No such transformation function %s" transform))
+      (setq txt (funcall transform list))
+      ;; Find the insertion place
+      (save-excursion
+       (goto-char (point-min))
+       (unless (re-search-forward
+                (concat "BEGIN RECEIVE ORGLST +" name "\\([ \t]\\|$\\)") nil t)
+         (error "Don't know where to insert translated list"))
+       (goto-char (match-beginning 0))
+       (beginning-of-line 2)
+       (setq beg (point))
+       (unless (re-search-forward (concat "END RECEIVE ORGLST +" name) nil t)
+         (error "Cannot find end of insertion region"))
+       (beginning-of-line 1)
+       (delete-region beg (point))
+       (goto-char beg)
+       (insert txt "\n"))
+      (message "List converted and installed at receiver location"))))
+
+(defun org-list-to-generic (list params)
+  "Convert a LIST parsed through `org-list-parse-list' to other formats.
+
+Valid parameters PARAMS are
+
+:ustart     String to start an unordered list
+:uend       String to end an unordered list
+
+:ostart     String to start an ordered list
+:oend       String to end an ordered list
+
+:dstart     String to start a descriptive list
+:dend       String to end a descriptive list
+:dtstart    String to start a descriptive term
+:dtend      String to end a descriptive term
+:ddstart    String to start a description
+:ddend      String to end a description
+
+:splice     When set to t, return only list body lines, don't wrap
+            them into :[u/o]start and :[u/o]end.  Default is nil.
+
+:istart     String to start a list item
+:iend       String to end a list item
+:isep       String to separate items
+:lsep       String to separate sublists"
+  (interactive)
+  (let* ((p params) sublist
+        (splicep (plist-get p :splice))
+        (ostart  (plist-get p :ostart))
+        (oend  (plist-get p :oend))
+        (ustart  (plist-get p :ustart))
+        (uend  (plist-get p :uend))
+        (dstart  (plist-get p :dstart))
+        (dend  (plist-get p :dend))
+        (dtstart  (plist-get p :dtstart))
+        (dtend  (plist-get p :dtend))
+        (ddstart  (plist-get p :ddstart))
+        (ddend  (plist-get p :ddend))
+        (istart  (plist-get p :istart))
+        (iend  (plist-get p :iend))
+        (isep  (plist-get p :isep))
+        (lsep  (plist-get p :lsep)))
+    (let ((wrapper
+          (cond ((eq (car list) 'ordered)
+                 (concat ostart "\n%s" oend "\n"))
+                ((eq (car list) 'unordered)
+                 (concat ustart "\n%s" uend "\n"))
+                ((eq (car list) 'descriptive)
+                 (concat dstart "\n%s" dend "\n"))))
+         rtn term defstart defend)
+      (while (setq sublist (pop list))
+       (cond ((symbolp sublist) nil)
+             ((stringp sublist)
+               (when (string-match "^\\(.*\\) ::" sublist)
+                 (setq term (org-trim (format (concat dtstart "%s" dtend)
+                                              (match-string 1 sublist))))
+                 (setq sublist (substring sublist (1+ (length term)))))
+               (setq rtn (concat rtn istart term ddstart
+                                 sublist ddend iend isep)))
+              (t (setq rtn (concat rtn   ;; previous list
+                                   lsep  ;; list separator
+                                   (org-list-to-generic sublist p)
+                                   lsep  ;; list separator
+                                   )))))
+      (format wrapper rtn))))
+
+(defun org-list-to-latex (list)
+  "Convert LIST into a LaTeX list."
+  (org-list-to-generic
+   list '(:splicep nil :ostart "\\begin{enumerate}" :oend "\\end{enumerate}"
+                       :ustart "\\begin{itemize}" :uend "\\end{itemize}"
+                       :dstart "\\begin{description}" :dend "\\end{description}"
+                       :dtstart "[" :dtend "]"
+                       :ddstart "" :ddend ""
+                       :istart "\\item " :iend ""
+                       :isep "\n" :lsep "\n")))
+
+(defun org-list-to-html (list)
+  "Convert LIST into a HTML list."
+  (org-list-to-generic
+   list '(:splicep nil :ostart "<ol>" :oend "</ol>"
+                       :ustart "<ul>" :uend "</ul>"
+                       :dstart "<dl>" :dend "</dl>"
+                       :dtstart "<dt>" :dtend "</dt>"
+                       :ddstart "<dd>" :ddend "</dd>"
+                       :istart "<li>" :iend "</li>"
+                       :isep "\n" :lsep "\n")))
+
+(defun org-list-to-texinfo (list)
+  "Convert LIST into a Texinfo list."
+  (org-list-to-generic
+   list '(:splicep nil :ostart "@itemize @minus" :oend "@end itemize"
+                       :ustart "@enumerate" :uend "@end enumerate"
+                       :dstart "@table" :dend "@end table"
+                       :dtstart "@item " :dtend "\n"
+                       :ddstart "" :ddend ""
+                       :istart "@item\n" :iend ""
+                       :isep "\n" :lsep "\n")))
+
+(provide 'org-list)
+
+;;; org-list.el ends here
diff --git a/lisp/org/org-plot.el b/lisp/org/org-plot.el
new file mode 100644 (file)
index 0000000..f8e268d
--- /dev/null
@@ -0,0 +1,314 @@
+;;; org-plot.el --- Support for plotting from Org-mode\r
+\r
+;; Copyright (C) 2008 Free Software Foundation, Inc.\r
+;;\r
+;; Author: Eric Schulte <schulte dot eric at gmail dot com>\r
+;; Keywords: tables, plotting\r
+;; Homepage: http://orgmode.org\r
+;; Version: 6.06b\r
+;;\r
+;; This file is part of GNU Emacs.\r
+;;\r
+;; GNU Emacs is free software: you can redistribute it and/or modify\r
+;; it under the terms of the GNU General Public License as published by\r
+;; the Free Software Foundation, either version 3 of the License, or\r
+;; (at your option) any later version.\r
+\r
+;; GNU Emacs is distributed in the hope that it will be useful,\r
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+;; GNU General Public License for more details.\r
+\r
+;; You should have received a copy of the GNU General Public License\r
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.\r
+\r
+;;; Commentary:\r
+\r
+;; Borrows ideas and a couple of lines of code from org-exp.el.\r
+\r
+;; Thanks to the org-mode mailing list for testing and implementation\r
+;; and feature suggestions\r
+\r
+;;; Code:\r
+(require 'org)\r
+(require 'org-exp)\r
+(require 'org-table)\r
+(eval-and-compile\r
+  (require 'cl))\r
+\r
+(declare-function gnuplot-delchar-or-maybe-eof "ext:gnuplot" (arg))\r
+(declare-function gnuplot-mode "ext:gnuplot" ())\r
+(declare-function gnuplot-send-buffer-to-gnuplot "ext:gnuplot" ())\r
+\r
+(defvar org-plot/gnuplot-default-options\r
+  '((:plot-type . 2d)\r
+    (:with . lines)\r
+    (:ind . 0))\r
+  "Default options to gnuplot used by `org-plot/gnuplot'")\r
+\r
+(defun org-plot/add-options-to-plist (p options)\r
+  "Parse an OPTIONS line and set values in the property list P.\r
+Returns the resulting property list."\r
+  (let (o)\r
+    (when options\r
+      (let ((op '(("type"   . :plot-type)\r
+                 ("script" . :script)\r
+                 ("line"   . :line)\r
+                 ("set"    . :set)\r
+                 ("title"  . :title)\r
+                 ("ind"    . :ind)\r
+                 ("deps"   . :deps)\r
+                 ("with"   . :with)\r
+                 ("file"   . :file)\r
+                 ("labels" . :labels)\r
+                 ("map"    . :map)))\r
+           (multiples '("set" "line"))\r
+           (regexp ":\\([\"][^\"]+?[\"]\\|[(][^)]+?[)]\\|[^ \t\n\r;,.]*\\)")\r
+           (start 0)\r
+           o)\r
+       (while (setq o (pop op))\r
+         (if (member (car o) multiples) ;; keys with multiple values\r
+             (while (string-match\r
+                     (concat (regexp-quote (car o)) regexp)\r
+                     options start)\r
+               (setq start (match-end 0))\r
+               (setq p (plist-put p (cdr o)\r
+                                  (cons (car (read-from-string\r
+                                              (match-string 1 options)))\r
+                                        (plist-get p (cdr o)))))\r
+               p)\r
+           (if (string-match (concat (regexp-quote (car o)) regexp)\r
+                             options)\r
+               (setq p (plist-put p (cdr o)\r
+                                  (car (read-from-string\r
+                                        (match-string 1 options)))))))))))\r
+  p)\r
+\r
+(defun org-plot/goto-nearest-table ()\r
+  "Move the point forward to the beginning of nearest table.\r
+Return value is the point at the beginning of the table."\r
+  (interactive) (move-beginning-of-line 1)\r
+  (while (not (or (org-at-table-p) (< 0 (forward-line 1)))))\r
+  (goto-char (org-table-begin)))\r
+\r
+(defun org-plot/collect-options (&optional params)\r
+  "Collect options from an org-plot '#+Plot:' line.\r
+Accepts an optional property list PARAMS, to which the options\r
+will be added.  Returns the resulting property list."\r
+  (interactive)\r
+  (let ((line (thing-at-point 'line)))\r
+    (if (string-match "#\\+PLOT: +\\(.*\\)$" line)\r
+       (org-plot/add-options-to-plist params (match-string 1 line))\r
+      params)))\r
+\r
+(defun org-plot-quote-tsv-field (s)\r
+  "Quote field S for export to gnuplot."\r
+  (if (string-match org-table-number-regexp s) s\r
+    (concat "\"" (mapconcat 'identity (split-string s "\"") "\"\"") "\"")))\r
+\r
+(defun org-plot/gnuplot-to-data (table data-file params)\r
+  "Export TABLE to DATA-FILE in a format readable by gnuplot.\r
+Pass PARAMS through to `orgtbl-to-generic' when exporting TABLE."\r
+  (with-temp-file\r
+      data-file (insert (orgtbl-to-generic\r
+                        table\r
+                        (org-combine-plists\r
+                         '(:sep "\t" :fmt org-plot-quote-tsv-field)\r
+                         params))))\r
+  nil)\r
+\r
+(defun org-plot/gnuplot-to-grid-data (table data-file params)\r
+  "Export the data in TABLE to DATA-FILE for gnuplot.\r
+This means, in a format appropriate for grid plotting by gnuplot.\r
+PARAMS specifies which columns of TABLE should be plotted as independant\r
+and dependant variables."\r
+  (interactive)\r
+  (let* ((ind (- (plist-get params :ind) 1))\r
+        (deps (if (plist-member params :deps)\r
+                  (mapcar (lambda (val) (- val 1)) (plist-get params :deps))\r
+                (let (collector)\r
+                  (dotimes (col (length (first table)))\r
+                    (setf collector (cons col collector)))\r
+                  collector)))\r
+        row-vals (counter 0))\r
+    (when (>= ind 0) ;; collect values of ind col\r
+      (setf row-vals (mapcar (lambda (row) (setf counter (+ 1 counter))\r
+                              (cons counter (nth ind row))) table)))\r
+    (when (or deps (>= ind 0)) ;; remove non-plotting columns\r
+      (setf deps (delq ind deps))\r
+      (setf table (mapcar (lambda (row)\r
+                           (dotimes (col (length row))\r
+                             (unless (memq col deps)\r
+                               (setf (nth col row) nil)))\r
+                           (delq nil row))\r
+                         table)))\r
+    ;; write table to gnuplot grid datafile format\r
+    (with-temp-file data-file\r
+      (let ((num-rows (length table)) (num-cols (length (first table)))\r
+           front-edge back-edge)\r
+       (flet ((gnuplot-row (col row value)\r
+                           (setf col (+ 1 col)) (setf row (+ 1 row))\r
+                           (format "%f  %f  %f\n%f  %f  %f\n"\r
+                                   col (- row 0.5) value ;; lower edge\r
+                                   col (+ row 0.5) value))) ;; upper edge\r
+         (dotimes (col num-cols)\r
+           (dotimes (row num-rows)\r
+             (setf back-edge\r
+                   (concat back-edge\r
+                           (gnuplot-row (- col 1) row (string-to-number\r
+                                                       (nth col (nth row table))))))\r
+             (setf front-edge\r
+                   (concat front-edge\r
+                           (gnuplot-row col row (string-to-number\r
+                                                 (nth col (nth row table)))))))\r
+           ;; only insert once per row\r
+           (insert back-edge) (insert "\n") ;; back edge\r
+           (insert front-edge) (insert "\n") ;; front edge\r
+           (setf back-edge "") (setf front-edge "")))))\r
+    row-vals))\r
+\r
+(defun org-plot/gnuplot-script (data-file num-cols params)\r
+  "Write a gnuplot script to DATA-FILE respecting the options set in PARAMS.\r
+NUM-COLS controls the number of columns plotted in a 2-d plot."\r
+  (let* ((type (plist-get params :plot-type))\r
+        (with (if (equal type 'grid)\r
+                  'pm3d\r
+                (plist-get params :with)))\r
+        (sets (plist-get params :set))\r
+        (lines (plist-get params :line))\r
+        (map (plist-get params :map))\r
+        (title (plist-get params :title))\r
+        (file (plist-get params :file))\r
+        (ind (plist-get params :ind))\r
+        (text-ind (plist-get params :textind))\r
+        (deps (if (plist-member params :deps) (plist-get params :deps)))\r
+        (col-labels (plist-get params :labels))\r
+        (x-labels (plist-get params :xlabels))\r
+        (y-labels (plist-get params :ylabels))\r
+        (plot-str "'%s' using %s%d%s with %s title '%s'")\r
+        (plot-cmd (case type\r
+                    ('2d "plot")\r
+                    ('3d "splot")\r
+                    ('grid "splot")))\r
+        (script "reset") plot-lines)\r
+    (flet ((add-to-script (line) (setf script (format "%s\n%s" script line))))\r
+      (when file ;; output file\r
+       (add-to-script (format "set term %s" (file-name-extension file)))\r
+       (add-to-script (format "set output '%s'" file)))\r
+      (case type ;; type\r
+       ('2d ())\r
+       ('3d (if map (add-to-script "set map")))\r
+       ('grid (if map\r
+                  (add-to-script "set pm3d map")\r
+                (add-to-script "set pm3d"))))\r
+      (when title (add-to-script (format "set title '%s'" title))) ;; title\r
+      (when lines (mapc (lambda (el) (add-to-script el)) lines)) ;; line\r
+      (when sets ;; set\r
+       (mapc (lambda (el) (add-to-script (format "set %s" el))) sets))\r
+      (when x-labels ;; x labels (xtics)\r
+       (add-to-script\r
+        (format "set xtics (%s)"\r
+                (mapconcat (lambda (pair)\r
+                             (format "\"%s\" %d" (cdr pair) (car pair)))\r
+                           x-labels ", "))))\r
+      (when y-labels ;; y labels (ytics)\r
+       (add-to-script\r
+        (format "set ytics (%s)"\r
+                (mapconcat (lambda (pair)\r
+                             (format "\"%s\" %d" (cdr pair) (car pair)))\r
+                           y-labels ", "))))\r
+      (case type ;; plot command\r
+       ('2d (dotimes (col num-cols)\r
+              (unless (and (equal type '2d)\r
+                           (or (and ind (equal (+ 1 col) ind))\r
+                               (and deps (not (member (+ 1 col) deps)))))\r
+                (setf plot-lines\r
+                      (cons\r
+                       (format plot-str data-file\r
+                               (or (and (not text-ind) ind\r
+                                        (> ind 0) (format "%d:" ind)) "")\r
+                               (+ 1 col)\r
+                               (if text-ind (format ":xticlabel(%d)" ind) "")\r
+                               with\r
+                               (or (nth col col-labels) (format "%d" (+ 1 col))))\r
+                       plot-lines)))))\r
+       ('3d\r
+        (setq plot-lines (list (format "'%s' matrix with %s title ''"\r
+                                       data-file with))))\r
+       ('grid\r
+        (setq plot-lines (list (format "'%s' with %s title ''"\r
+                                       data-file with)))))\r
+      (add-to-script\r
+       (concat plot-cmd " " (mapconcat 'identity (reverse plot-lines) ",\\\n    ")))\r
+      script)))\r
+\r
+;;-----------------------------------------------------------------------------\r
+;; facade functions\r
+;;;###autoload\r
+(defun org-plot/gnuplot (&optional params)\r
+  "Plot table using gnuplot. Gnuplot options can be specified with PARAMS.\r
+If not given options will be taken from the +PLOT\r
+line directly before or after the table."\r
+  (interactive)\r
+  (require 'gnuplot)\r
+  (save-window-excursion\r
+    (delete-other-windows)\r
+    (when (get-buffer "*gnuplot*") ;; reset *gnuplot* if it already running\r
+      (save-excursion\r
+       (set-buffer "*gnuplot*") (goto-char (point-max))\r
+       (gnuplot-delchar-or-maybe-eof nil)))\r
+    (org-plot/goto-nearest-table)\r
+    ;; set default options\r
+    (mapc\r
+     (lambda (pair)\r
+       (unless (plist-member params (car pair))\r
+        (setf params (plist-put params (car pair) (cdr pair)))))\r
+     org-plot/gnuplot-default-options)\r
+    ;; collect table and table information\r
+    (let* ((data-file (make-temp-file "org-plot"))\r
+          (table (org-table-to-lisp))\r
+          (num-cols (length (if (eq (first table) 'hline) (second table)\r
+                              (first table)))))\r
+      (while (equal 'hline (first table)) (setf table (cdr table)))\r
+      (when (equal (second table) 'hline)\r
+       (setf params (plist-put params :labels (first table))) ;; headers to labels\r
+       (setf table (delq 'hline (cdr table)))) ;; clean non-data from table\r
+      ;; collect options\r
+      (save-excursion (while (and (equal 0 (forward-line -1))\r
+                                 (looking-at "#\\+"))\r
+                       (setf params (org-plot/collect-options params))))\r
+      ;; dump table to datafile (very different for grid)\r
+      (case (plist-get params :plot-type)\r
+       ('2d   (org-plot/gnuplot-to-data table data-file params))\r
+       ('3d   (org-plot/gnuplot-to-data table data-file params))\r
+       ('grid (let ((y-labels (org-plot/gnuplot-to-grid-data\r
+                               table data-file params)))\r
+                (when y-labels (plist-put params :ylabels y-labels)))))\r
+      ;; check for text ind column\r
+      (let ((ind (- (plist-get params :ind) 1)))\r
+       (when (and (>= ind 0) (equal '2d (plist-get params :plot-type)))\r
+         (if (> (length\r
+                 (delq 0 (mapcar\r
+                          (lambda (el)\r
+                            (if (string-match org-table-number-regexp el)\r
+                                0 1))\r
+                          (mapcar (lambda (row) (nth ind row)) table)))) 0)\r
+             (plist-put params :textind t))))\r
+      ;; write script\r
+      (with-temp-buffer\r
+       (if (plist-get params :script) ;; user script\r
+           (progn (insert-file-contents (plist-get params :script))\r
+                  (goto-char (point-min))\r
+                  (while (re-search-forward "$datafile" nil t)\r
+                    (replace-match data-file nil nil)))\r
+         (insert\r
+          (org-plot/gnuplot-script data-file num-cols params)))\r
+       ;; graph table\r
+       (gnuplot-mode)\r
+       (gnuplot-send-buffer-to-gnuplot))\r
+      ;; cleanup\r
+      (bury-buffer (get-buffer "*gnuplot*"))(delete-file data-file))))\r
+\r
+(provide 'org-plot)\r
+\r
+;;; org-plot.el ends here\r