Просмотр исходного кода

Integrate John Wiegley's org-attach.el.

Carsten Dominik 16 лет назад
Родитель
Сommit
d043e31182
10 измененных файлов с 514 добавлено и 81 удалено
  1. 2 0
      Makefile
  2. 38 0
      ORGWEBPAGE/Changes.org
  3. 123 20
      doc/org.texi
  4. 4 5
      doc/orgcard.tex
  5. 24 0
      lisp/ChangeLog
  6. 1 0
      lisp/org-agenda.el
  7. 236 0
      lisp/org-attach.el
  8. 27 24
      lisp/org-id.el
  9. 0 1
      lisp/org-table.el
  10. 59 31
      lisp/org.el

+ 2 - 0
Makefile

@@ -62,6 +62,7 @@ INSTALL_INFO=install-info
 # The following variables need to be defined by the maintainer
 LISPF      = 	org.el			\
 		org-agenda.el		\
+	     	org-attach.el		\
 	     	org-archive.el		\
 		org-bbdb.el		\
 		org-bibtex.el		\
@@ -287,6 +288,7 @@ dummy:
 
 lisp/org.elc:           lisp/org-macs.elc lisp/org-compat.elc lisp/org-faces.elc
 lisp/org-agenda.elc:       lisp/org.elc
+lisp/org-attach.elc:       lisp/org.elc lisp/org-id.elc
 lisp/org-archive.elc:      lisp/org.elc
 lisp/org-bbdb.elc:         lisp/org.elc
 lisp/org-bibtex.elc:       lisp/org.elc

+ 38 - 0
ORGWEBPAGE/Changes.org

@@ -15,15 +15,53 @@
 :VISIBILITY: content
 :END:
 
+
+** Incompatible changes
+
+*** The default structure of IDs has changed
+
+    IDs created by Org have changed a bit:
+    - By default, there is no prefix on the ID.  There used to be
+      an "Org" prefix, but I now think this is not necesary.
+    - IDs use only lower-case letters, no upper-case letters
+      anymore.  The reason for this is that IDs are now also used
+      as directory names for org-attach, and some systems do not
+      distinguish upper and lower case in the file system.
+    - The ID string derived from the current time is now /reversed/
+      to become an ID.  This assures that the first two letters
+      of the ID change fast, so hat it makes sense to split them
+      off to create subdirectories to balance load.
+    - You can now set the `org-id-method' to `uuidgen' on systems
+      which support it. 
+
 ** Details
 
+*** New attachment system
+
+    You can now attach files to each node in the outline tree.
+    This works by creating special directories based on the ID of
+    an entry, and storing files in these directories.  Org can
+    keep track of changes to the attachments by automatically
+    committing changes to git.  See the manual for more
+    information.
+
+    Thanks to John Wiegley who contributed this fantastic new
+    concept and wrote org-attach.el to implement it.
+
 *** New remember template escape to add a property
     Thanks to James TD Smith for a patch to this effect.
+
 *** Clicking with mouse-2 on clock info in mode-line visits the clock.
     Thanks to James TD Smith for a patch to this effect.
+
 *** New file in contrib: lisp/org-checklist.el
     Thanks to James TD Smith for this contribution.
 
+*** New in-buffer seting #+STYLE
+    It can be used to locally set the variable
+    `org-expor-html-style-extra'.  See the [[http://orgmode.org/worg/org-tutorials/org-publish-html-tutorial.php][publishing tutorial]]
+    for an example on how tu use it.
+
 * Version 6.07
 :PROPERTIES:
 :VISIBILITY: content

+ 123 - 20
doc/org.texi

@@ -1,4 +1,4 @@
-925\input texinfo
+\input texinfo
 @c %**start of header
 @setfilename ../../info/org
 @settitle The Org Manual
@@ -87,7 +87,7 @@ license to the document, as described in section 6 of the license.
 * Tags::                        Tagging headlines and matching sets of tags
 * Properties and Columns::      Storing information about an entry
 * Dates and Times::             Making items useful for planning
-* Remember::                    Quickly adding nodes to the outline tree
+* Capture::                     Creating tasks and attaching files
 * Agenda Views::                Collecting information into views
 * Embedded LaTeX::              LaTeX fragments and formulas
 * Exporting::                   Sharing and publishing of notes
@@ -230,6 +230,11 @@ Deadlines and scheduling
 * Inserting deadline/schedule::  Planning items
 * Repeated tasks::              Items that show up again and again
 
+Capture
+
+* Remember::                    Capture new tasks/ideas with little interruption
+* Attachments::                 Add files to tasks.
+
 Remember
 
 * Setting up Remember::         Some code for .emacs to get things going
@@ -770,8 +775,8 @@ CONTENTS view up to headlines of level N will be shown.  Note that inside
 tables, @kbd{S-@key{TAB}} jumps to the previous field.
 
 @cindex show all, command
-@kindex C-c C-a
-@item C-c C-a
+@kindex C-c C-a C-a
+@item C-c C-a C-a
 Show all.
 @kindex C-c C-r
 @item C-c C-r
@@ -4192,7 +4197,7 @@ be used by Emacs Lisp programs to work with properties and to implement
 features based on them.  For more information see @ref{Using the
 property API}.
 
-@node Dates and Times, Remember, Properties and Columns, Top
+@node Dates and Times, Capture, Properties and Columns, Top
 @chapter Dates and Times
 @cindex dates
 @cindex times
@@ -4872,8 +4877,23 @@ option @code{org-agenda-columns-add-appointments-to-effort-sum}.  The
 appointments on a day that take place over a specified time interval will
 then also be added to the load estimate of the day.
 
-@node Remember, Agenda Views, Dates and Times, Top
-@chapter Remember
+
+@node Capture, Agenda Views, Dates and Times, Top
+@chapter Capture
+@cindex capture
+
+An important part of any organization system is the ability to quickly
+capture new ideas and tasks, and to associate reference material with them.
+Org uses the @file{remember} package to create tasks, and stores files
+related to a task (@i{attachments}) in a special directory.
+
+@menu
+* Remember::                    Capture new tasks/ideas with little interruption
+* Attachments::                 Add files to tasks.
+@end menu
+
+@node Remember, Attachments, Capture, Capture
+@section Remember
 @cindex @file{remember.el}
 
 The @i{Remember} package by John Wiegley lets you store quick notes with
@@ -4894,7 +4914,7 @@ interactively, on the fly.
 @end menu
 
 @node Setting up Remember, Remember templates, Remember, Remember
-@section Setting up Remember
+@subsection Setting up Remember
 
 The following customization will tell @i{remember} to use org files as
 target, and to create annotations compatible with Org links.
@@ -4922,7 +4942,7 @@ inserted by the selected remember template (see below) will default to
 the cursor date in the agenda, rather than to the current date.
 
 @node Remember templates, Storing notes, Setting up Remember, Remember
-@section Remember templates
+@subsection Remember templates
 @cindex templates, for remember
 
 In combination with Org, you can use templates to generate
@@ -5047,7 +5067,7 @@ If you change your mind about which template to use, call
 template that will be filled with the previous context information.
 
 @node Storing notes, Refiling notes, Remember templates, Remember
-@section Storing notes
+@subsection Storing notes
 
 When you are finished preparing a note with @i{remember}, you have to press
 @kbd{C-c C-c} to file the note away.  If you have started the clock in the
@@ -5096,15 +5116,14 @@ then leads to the following result.
       @tab at cursor position, level taken from context.
 @end multitable
 
-Before inserting the text into a tree, the function ensures that the
-text has a headline, i.e. a first line that starts with a @samp{*}.  If
-not, a headline is constructed from the current date and some additional
-data.  If you have indented the text of the note below the headline, the
-indentation will be adapted if inserting the note into the tree requires
-demotion from level 1.
+Before inserting the text into a tree, the function ensures that the text has
+a headline, i.e. a first line that starts with a @samp{*}.  If not, a
+headline is constructed from the current date.  If you have indented the text
+of the note below the headline, the indentation will be adapted if inserting
+the note into the tree requires demotion from level 1.
 
 @node Refiling notes,  , Storing notes, Remember
-@section Refiling notes
+@subsection Refiling notes
 @cindex refiling notes
 
 Remember is usually used to quickly capture notes and tasks into one or
@@ -5135,7 +5154,76 @@ Use the refile interface to jump to a heading.
 Jump to the location where @code{org-refile} last moved a tree to.
 @end table
 
-@node Agenda Views, Embedded LaTeX, Remember, Top
+@node Attachments,  , Remember, Capture
+@section Attachments
+@cindex attachments
+
+It is often useful to associate reference material with an outline node/task.
+Small chunks of plain text can simply be stored in the subtree of a project.
+Hyperlinks (@pxref{Hyperlinks}) can be used to establish associations with
+files that live elsewhere on your computer or in the cloud, like emails or
+source code files belonging to a project.  However, you may also have files
+that only belong to a given project and that you would like to store in a
+directory belonging to an outline node.
+
+Org allows to associate an arbitary number of files with each indivdual task.
+These files are moved to special directories named by the unique ID of each
+entry.  These directories are located in the @file{data} directory which
+lives in the same directory where your org-file lives@footnote{If you move
+entries or Org-files from one directory to the next, you may want to
+configure @code{org-attach-directory} to contain an absolute path.}.  If you
+initilize this directory with @code{git-init}, Org will automaically commit
+changes when it see them.  The attachment system has been contributed to Org
+by John Wiegley.
+
+The following commands deal with attachments.
+
+@table @kbd
+
+@kindex C-c C-a
+@item C-c C-a
+The dispatcher for commands related to the attachment system.  After these
+keys, a list of commands is displayed and you need to press an additional key
+to select a command:
+
+@table @kbd
+@kindex C-c C-a a 
+@item a 
+Select a file and move it into the task's attachment directory.
+
+@kindex C-c C-a c
+@item c
+Create a new attachment as an Emacs buffer.
+
+@kindex C-c C-a z
+@item z
+Synchronize the current task with its attachment directory, in case you added
+attachments yourself.
+
+@kindex C-c C-a o
+@item o
+Open current task's attachment, if there is only one.
+
+@kindex C-c C-a O
+@item O
+Also open the attachment, but using the Finder.
+
+@kindex C-c C-a f
+@item f
+Open current task's attachment directory in dired.
+
+@kindex C-c C-a F
+@item F
+Also open the directory, but using the Finder.
+
+@kindex C-c C-a D
+@item D
+Delete all of a task's attachments.  A safer way is to open the directory in
+dired and delete from there.
+@end table
+@end table
+
+@node Agenda Views, Embedded LaTeX, Capture, Top
 @chapter Agenda Views
 @cindex agenda views
 
@@ -5985,6 +6073,10 @@ key for this.
 @itemx S-@key{down}
 Decrease the priority of the current item.
 @c
+@kindex C-c C-a
+@item C-c C-a
+Dispatcher for all command related to attachments.
+@c
 @kindex C-c C-s
 @item C-c C-s
 Schedule this item
@@ -9456,6 +9548,16 @@ Get all property keys in the current buffer.
 Insert a property drawer at point.
 @end defun
 
+@defun org-entry-put-multivalued-property pom property &rest values
+Set PROPERTY at point-or-marker POM to VALUES.  VALUES should be a list of
+strings.  They will be concatenated, with spaces as separators.
+@end defun
+
+@defun org-entry-get-multivalued-property pom property
+Treat the value of the property PROPERTY as a whitespace-separated list of
+values and return the values as a list of strings.
+@end defun
+
 @defun org-entry-add-to-multivalued-property pom property value
 Treat the value of the property PROPERTY as a whitespace-separated list of
 values and make sure that VALUE is in this list.
@@ -9762,8 +9864,9 @@ system.
 development of Org was fully independent because I was not aware of the
 existence of these packages.  But with time I have accasionally looked
 at John's code and learned a lot from it.  John has also contributed a
-number of great ideas and patches directly to Org, including the file
-@code{org-mac-message.el}'
+number of great ideas and patches directly to Org, including the attachment
+system (@file{org-attach.el}) and integration with Apple Mail
+(@code{org-mac-message.el}).
 @item
 @i{Carsten Wimmer} suggested some changes and helped fix a bug in
 linking to Gnus.

+ 4 - 5
doc/orgcard.tex

@@ -290,10 +290,9 @@ are preserved on all copies.
 
 \key{rotate current subtree between states}{TAB}
 \key{rotate entire buffer between states}{S-TAB}
-\key{show the whole file}{C-c C-a}
+\key{restore property-dependent startup visibility}{C-u C-u TAB}
+\metax{show the whole file, including drawers}{C-u C-u C-u TAB}
 \key{reveal context around point}{C-c C-r}
-\key{show subtree in indirect buffer, ded.\ frame}{C-c C-k}
-%\key{show branches}{C-c C-k}
 
 \section{Motion}
 
@@ -648,13 +647,13 @@ after  ``{\tt :}'', and dictionary words elsewhere.
 \key{digit argument}{0-9}
 \key{change state of current TODO item}{t}
 \key{kill item and source}{C-k}
-\key{archive the subtree}{\$ / a / A}
+\key{archive the subtree (file/tag/sibling)}{\$ / a / A}
 \key{show tags of current headline}{T}
 \key{set tags for current headline/region}{:}
-\key{toggle ARCHIVE tag}{a}
 \key{set priority of current item}{p}
 \key{raise/lower priority of current item}{S-UP/DOWN$^3$}
 \key{display weighted priority of current item}{P}
+\key{run an attachment command}{C-c C-a}
 \key{schedule/set deadline for this item}{C-c C-s/d}
 \key{change timestamp to one day earlier/later}{S-LEFT/RIGHT$^3$}
 \key{change timestamp to today}{>}

+ 24 - 0
lisp/ChangeLog

@@ -1,5 +1,29 @@
+2008-09-30  Carsten Dominik  <dominik@science.uva.nl>
+
+	* org.el (org-entry-get-multivalued-property)
+	(org-entry-protect-space, org-entry-restore-space): New
+	functions.
+	(org-file-apps-defaults-macosx): Let postscript files be opened by
+	preview.
+	(org-time-stamp-inactive): Call `org-time-stamp'.
+	(org-time-stamp): New argument `inactive'.  Also edit inacive
+	stamps. Convert time stamp type.
+	(org-open-file): Interpret the `default' value for the `command'
+	in `org-file-apps'.
+
+	* org-id.el (org-id-int-to-b36-one-digit)
+	(org-id-b36-to-int-one-digit, org-id-int-to-b36)
+	(org-id-b36-to-int, org-id-time-to-b36): Modified from b62 to
+	b36.
+
 2008-09-29  Carsten Dominik  <dominik@science.uva.nl>
 
+	* org-id.el (org-id-reverse-string): New function.
+	(org-id-new): Use `org-id-reverse-string' to make sure the
+	beginning chars of the ID are mutating fast.  This allows to use a
+	directory structure to spread things better.
+	(org-id-prefix): Changed default to nil.
+
 	* org-list.el (org-move-item-down, org-move-item-up): Remember and
 	restore the column of the cursor position.
 

+ 1 - 0
lisp/org-agenda.el

@@ -1132,6 +1132,7 @@ The following commands are available:
 (org-defkey org-agenda-mode-map "T" 'org-agenda-show-tags)
 (org-defkey org-agenda-mode-map "n" 'next-line)
 (org-defkey org-agenda-mode-map "p" 'previous-line)
+(org-defkey org-agenda-mode-map "\C-c\C-a" 'org-attach)
 (org-defkey org-agenda-mode-map "\C-c\C-n" 'org-agenda-next-date-line)
 (org-defkey org-agenda-mode-map "\C-c\C-p" 'org-agenda-previous-date-line)
 (org-defkey org-agenda-mode-map "," 'org-agenda-priority)

+ 236 - 0
lisp/org-attach.el

@@ -0,0 +1,236 @@
+;;; 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.08-pre01
+
+;; 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'.
+
+;; Ideas:  Store region or kill as an attachment.
+;;         Support drag-and-drop 
+
+(eval-when-compile
+  (require 'cl))
+(require 'org-id)
+(require 'org)
+
+(defgroup org-attach nil
+  "Options concerning entry attachments in Org-mode."
+  :tag "Org Remember"
+  :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-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 move it into the task's attachment  directory.
+c    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 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: [azoOfFD^a]")
+	  (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)) (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))
+       ((eq c ?D)            (call-interactively 'org-attach-delete))
+       (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 available."
+  (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-attach (file &optional visit-dir)
+  "Move FILE into the attachment directory of the current task.
+If VISIT-DIR is non-nil, visit the direcory with dired."
+  (interactive "fFile to keep as an attachment: \nP")
+  (let ((basename (file-name-nondirectory file)))
+    (org-entry-add-to-multivalued-property (point) "Attachments"
+					   basename)
+    (let ((attach-dir (org-attach-dir t)))
+      (rename-file file (expand-file-name basename attach-dir))
+      (org-attach-commit)
+      (if visit-dir
+	  (dired attach-dir)
+	(message "File \"%s\" is now a task attachment." basename)))))
+
+(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: ")
+  (org-entry-add-to-multivalued-property (point) "Attachments"
+					 file)
+  (let ((attach-dir (org-attach-dir t)))
+    (find-file (expand-file-name file attach-dir))
+    (message "New attachment %s" file)))
+
+(defun org-attach-delete ()
+  "Delete all attachments from the current task.
+A safer way is to open the directory in dired and delete from there."
+  (interactive)
+  (org-entry-delete (point) "Attachments")
+  (let ((attach-dir (org-attach-dir)))
+    (if attach-dir
+	(shell-command (format "rm -fr %s" attach-dir))))
+  (org-attach-commit))
+
+(defun org-attach-sync ()
+  "Synchonize the current tasks with its attachments.
+This can be used after files have been added externally."
+  (interactive)
+  (org-attach-commit)
+  (org-entry-delete (point) "Attachments")
+  (let ((attach-dir (org-attach-dir)))
+    (when attach-dir
+      (let ((files (directory-files attach-dir)))
+	(dolist (file files)
+	  (unless (string-match "^\\." file)
+	    (org-entry-add-to-multivalued-property
+	     (point) "Attachments" file)))))))
+
+(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-entry-get-multivalued-property (point) "Attachments"))
+	 (file (if (= (length files) 1)
+		   (car files)
+		 (completing-read "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'."
+  (org-attach-open 'in-emacs))
+
+
+(defun org-attach-open-single-attachment (&optional in-emacs)
+  (interactive)
+  (let* ((attach-dir (org-attach-dir t))
+	 (file (read-file-name "Attachment: " attach-dir nil t)))
+    (org-open-file file in-emacs)))
+  
+
+(provide 'org-attach)
+
+;;; org-attach.el ends here

+ 27 - 24
lisp/org-id.el

@@ -96,7 +96,7 @@ uuidgen    Call the external command uuidgen."
 	  (const :tag "Org's internal method" org)
 	  (const :tag "external: uuidgen" uuidgen)))
 
-(defcustom org-id-prefix "Org"
+(defcustom org-id-prefix nil
   "The prefix for IDs.
 
 This may be a string, or it can be nil to indicate that no prefix is required.
@@ -252,7 +252,7 @@ So a typical ID could look like \"Org:4nd91V40HI\"."
      ((eq org-id-method 'uuidgen)
       (setq unique (substring (shell-command-to-string "uuidgen") 1 -1)))
      ((eq org-id-method 'org)
-      (let* ((etime (org-id-time-to-b62))
+      (let* ((etime (org-id-reverse-string (org-id-time-to-b36)))
 	     (postfix (if org-id-include-domain
 			  (progn
 			    (require 'message)
@@ -261,51 +261,53 @@ So a typical ID could look like \"Org:4nd91V40HI\"."
      (t (error "Invalid `org-id-method'")))
     (concat prefix unique)))
 
-(defun org-id-int-to-b62-one-digit (i)
+(defun org-id-reverse-string (s)
+  (mapconcat 'char-to-string (nreverse (string-to-list s)) ""))
+
+(defun org-id-int-to-b36-one-digit (i)
   "Turn an integer between 0 and 61 into a single character 0..9, A..Z, a..z."
   (cond
    ((< i 10) (+ ?0 i))
-   ((< i 36) (+ ?A i -10))
-   ((< i 62) (+ ?a i -36))
-   (t (error "Larger that 61"))))
+   ((< i 36) (+ ?a i -10))
+   (t (error "Larger that 35"))))
 
-(defun org-id-b62-to-int-one-digit (i)
+(defun org-id-b36-to-int-one-digit (i)
   "Turn a character 0..9, A..Z, a..z into a number 0..61.
 The input I may be a character, or a single-letter string."
   (and (stringp i) (setq i (string-to-char i)))
   (cond
    ((and (>= i ?0) (<= i ?9)) (- i ?0))
-   ((and (>= i ?A) (<= i ?Z)) (+ (- i ?A) 10))
-   ((and (>= i ?a) (<= i ?z)) (+ (- i ?a) 36))
-   (t (error "Invalid b62 letter"))))
+   ((and (>= i ?a) (<= i ?z)) (+ (- i ?a) 10))
+   (t (error "Invalid b36 letter"))))
 
-(defun org-id-int-to-b62 (i &optional length)
-  "Convert an integer to a base-62 number represented as a string."
+(defun org-id-int-to-b36 (i &optional length)
+  "Convert an integer to a base-36 number represented as a string."
   (let ((s ""))
     (while (> i 0)
       (setq s (concat (char-to-string
-		       (org-id-int-to-b62-one-digit (mod i 62))) s)
-	    i (/ i 62)))
+		       (org-id-int-to-b36-one-digit (mod i 36))) s)
+	    i (/ i 36)))
     (setq length (max 1 (or length 1)))
     (if (< (length s) length)
 	(setq s (concat (make-string (- length (length s)) ?0) s)))
     s))
 
-(defun org-id-b62-to-int (s)
-  "Convert a base-62 string into the corresponding integer."
+(defun org-id-b36-to-int (s)
+  "Convert a base-36 string into the corresponding integer."
   (let ((r 0))
-    (mapc (lambda (i) (setq r (+ (* r 62) (org-id-b62-to-int-one-digit i))))
+    (mapc (lambda (i) (setq r (+ (* r 36) (org-id-b36-to-int-one-digit i))))
 	  s)
     r))
 
-(defun org-id-time-to-b62 (&optional time)
+(defun org-id-time-to-b36 (&optional time)
   "Encode TIME as a 10-digit string.
 This string holds the time to micro-second accuracy, and can be decoded
 using `org-id-decode'."
+  (debug)
   (setq time (or time (current-time)))
-  (concat (org-id-int-to-b62 (nth 0 time) 3)
-	  (org-id-int-to-b62 (nth 1 time) 3)
-	  (org-id-int-to-b62 (or (nth 2 time) 0) 4)))
+  (concat (org-id-int-to-b36 (nth 0 time) 4)
+	  (org-id-int-to-b36 (nth 1 time) 4)
+	  (org-id-int-to-b36 (or (nth 2 time) 0) 4)))
 
 (defun org-id-decode (id)
   "Split ID into the prefix and the time value that was used to create it.
@@ -316,9 +318,10 @@ and time is the usual three-integer representation of time."
     (if (= 2 (length parts))
 	(setq prefix (car parts) time (nth 1 parts))
       (setq prefix nil time (nth 0 parts)))
-    (setq time (list (org-id-b62-to-int (substring time 0 3))
-		     (org-id-b62-to-int (substring time 3 6))
-		     (org-id-b62-to-int (substring time 6 10))))
+    (setq time (org-id-reverse-string time))
+    (setq time (list (org-id-b36-to-int (substring time 0 4))
+		     (org-id-b36-to-int (substring time 4 8))
+		     (org-id-b36-to-int (substring time 8 12))))
     (cons prefix time)))
 
 ;; Storing ID locations (files)

+ 0 - 1
lisp/org-table.el

@@ -3315,7 +3315,6 @@ to execute outside of tables."
 	  '("\C-c{"              org-table-toggle-formula-debugger)
 	  '("\C-m"               org-table-next-row)
 	  '([(shift return)]     org-table-copy-down)
-	  '("\C-c\C-q"           org-table-wrap-region)
 	  '("\C-c?"              org-table-field-info)
 	  '("\C-c "              org-table-blank-field)
 	  '("\C-c+"              org-table-sum)

+ 59 - 31
lisp/org.el

@@ -1115,9 +1115,7 @@ See `org-file-apps'.")
 (defconst org-file-apps-defaults-macosx
   '((remote . emacs)
     (t . "open %s")
-    ("ps"     . "gv %s")
     ("ps.gz"  . "gv %s")
-    ("eps"    . "gv %s")
     ("eps.gz" . "gv %s")
     ("dvi"    . "xdvi %s")
     ("fig"    . "xfig %s"))
@@ -3880,9 +3878,11 @@ If KWD is a number, get the corresponding match group."
   1. OVERVIEW: Show only top-level headlines.
   2. CONTENTS: Show all headlines of all levels, but no body text.
   3. SHOW ALL: Show everything.
-  When called with two C-c C-u prefixes, switch to the startup visibility,
+  When called with two C-u C-u prefixes, switch to the startup visibility,
   determined by the variable `org-startup-folded', and by any VISIBILITY
   properties in the buffer.
+  When called with three C-u C-u C-u prefixed, show the entire buffer,
+  including drawers.
 
 - When point is at the beginning of a headline, rotate the subtree started
   by this line through 3 different states (local cycling)
@@ -3926,7 +3926,11 @@ If KWD is a number, get the corresponding match group."
 
      ((equal arg '(16))
       (org-set-startup-visibility)
-      (message "Startup visibility, plus VISIBILITY properties."))
+      (message "Startup visibility, plus VISIBILITY properties"))
+
+     ((equal arg '(64))
+      (show-all)
+      (message "Entire buffer visible, including drawers"))
 
      ((org-at-table-p 'any)
       ;; Enter the table or move to the next field in the table
@@ -6980,6 +6984,8 @@ If the file does not exist, an error is thrown."
 		    (and dirp (cdr (assoc 'directory apps)))
 		    (cdr (assoc ext apps))
 		    (cdr (assoc t apps)))))
+    (when (eq cmd 'default)
+      (setq cmd (cdr (assoc t apps))))
     (when (eq cmd 'mailcap)
       (require 'mailcap)
       (mailcap-parse-mailcaps)
@@ -9775,6 +9781,7 @@ If yes, return this value.  If not, return the current value of the variable."
   "Add VALUE to the words in the PROPERTY in entry at point-or-marker POM."
   (let* ((old (org-entry-get pom property))
 	 (values (and old (org-split-string old "[ \t]"))))
+    (setq value (org-entry-protect-space value))
     (unless (member value values)
       (setq values (cons value values))
       (org-entry-put pom property
@@ -9784,6 +9791,7 @@ If yes, return this value.  If not, return the current value of the variable."
   "Remove VALUE from words in the PROPERTY in entry at point-or-marker POM."
   (let* ((old (org-entry-get pom property))
 	 (values (and old (org-split-string old "[ \t]"))))
+    (setq value (org-entry-protect-space value))
     (when (member value values)
       (setq values (delete value values))
       (org-entry-put pom property
@@ -9793,8 +9801,40 @@ If yes, return this value.  If not, return the current value of the variable."
   "Is VALUE one of the words in the PROPERTY in entry at point-or-marker POM?"
   (let* ((old (org-entry-get pom property))
 	 (values (and old (org-split-string old "[ \t]"))))
+    (setq value (org-entry-protect-space value))
     (member value values)))
 
+(defun org-entry-get-multivalued-property (pom property)
+  "Return a list of values in a multivalued property."
+  (let* ((value (org-entry-get pom property))
+	 (values (and value (org-split-string value "[ \t]"))))
+    (mapcar 'org-entry-restore-space values)))
+
+(defun org-entry-put-multivalued-property (pom property &rest values)
+  "Set multivalued PROPERTY at point-or-marker POM to VALUES.
+VALUES should be a list of strings.  Spaces will be protected."
+  (org-entry-put pom property
+		 (mapconcat 'org-entry-protect-space values " "))
+  (let* ((value (org-entry-get pom property))
+	 (values (and old (org-split-string value "[ \t]"))))
+    (mapcar 'org-entry-restore-space values)))
+
+(defun org-entry-protect-space (s)
+  "Protect spaces and newline in string S."
+  (while (string-match " " s)
+    (setq s (replace-match "%20" t t s)))
+  (while (string-match "\n" s)
+    (setq s (replace-match "%0A" t t s)))
+  s)
+
+(defun org-entry-restore-space (s)
+  "Restore spaces and newline in string S."
+  (while (string-match "%20" s)
+    (setq s (replace-match " " t t s)))
+  (while (string-match "%0A" s)
+    (setq s (replace-match "\n" t t s)))
+  s)
+
 (defvar org-entry-property-inherited-from (make-marker))
 
 (defun org-entry-get-with-inheritance (property)
@@ -10127,7 +10167,7 @@ Return the position where this entry starts, or nil if there is no such entry."
 (defvar org-end-time-was-given) ; dynamically scoped parameter
 (defvar org-ts-what) ; dynamically scoped parameter
 
-(defun org-time-stamp (arg)
+(defun org-time-stamp (arg &optional inactive)
   "Prompt for a date/time and insert a time stamp.
 If the user specifies a time like HH:MM, or if this command is called
 with a prefix argument, the time stamp will contain date and time.
@@ -10151,28 +10191,30 @@ at the cursor, it will be modified."
 	 (default-input (and ts (org-get-compact-tod ts)))
 	 org-time-was-given org-end-time-was-given time)
     (cond
-     ((and (org-at-timestamp-p)
-	   (eq last-command 'org-time-stamp)
-	   (eq this-command 'org-time-stamp))
+     ((and (org-at-timestamp-p t)
+	   (memq last-command '(org-time-stamp org-time-stamp-inactive))
+	   (memq this-command '(org-time-stamp org-time-stamp-inactive)))
       (insert "--")
       (setq time (let ((this-command this-command))
-		  (org-read-date arg 'totime nil nil default-time default-input)))
-      (org-insert-time-stamp time (or org-time-was-given arg)))
-     ((org-at-timestamp-p)
+		  (org-read-date arg 'totime nil nil
+				 default-time default-input)))
+      (org-insert-time-stamp time (or org-time-was-given arg) inactive))
+     ((org-at-timestamp-p t)
       (setq time (let ((this-command this-command))
 		   (org-read-date arg 'totime nil nil default-time default-input)))
-      (when (org-at-timestamp-p) ; just to get the match data
+      (when (org-at-timestamp-p t) ; just to get the match data
+;	(setq inactive (eq (char-after (match-beginning 0)) ?\[))
 	(replace-match "")
 	(setq org-last-changed-timestamp
 	      (org-insert-time-stamp
 	       time (or org-time-was-given arg)
-	       nil nil nil (list org-end-time-was-given))))
+	       inactive nil nil (list org-end-time-was-given))))
       (message "Timestamp updated"))
      (t
       (setq time (let ((this-command this-command))
 		   (org-read-date arg 'totime nil nil default-time default-input)))
-      (org-insert-time-stamp time (or org-time-was-given arg)
-			     nil nil nil (list org-end-time-was-given))))))
+      (org-insert-time-stamp time (or org-time-was-given arg) inactive
+			     nil nil (list org-end-time-was-given))))))
 
 ;; FIXME: can we use this for something else, like computing time differences?
 (defun org-get-compact-tod (s)
@@ -10198,10 +10240,7 @@ brackets.  It is inactive in the sense that it does not trigger agenda entries,
 does not link to the calendar and cannot be changed with the S-cursor keys.
 So these are more for recording a certain time/date."
   (interactive "P")
-  (let (org-time-was-given org-end-time-was-given time)
-    (setq time (org-read-date arg 'totime))
-    (org-insert-time-stamp time (or org-time-was-given arg) 'inactive
-			   nil nil (list org-end-time-was-given))))
+  (org-time-stamp arg 'inactive))
 
 (defvar org-date-ovl (org-make-overlay 1 1))
 (org-overlay-put org-date-ovl 'face 'org-warning)
@@ -10587,17 +10626,6 @@ Also, store the cursor date in variable org-ans2."
     (org-move-overlay org-date-ovl (1- (point)) (1+ (point)) (current-buffer))
     (select-window sw)))
 
-;    ;; Update the prompt to show new default date
-;    (save-excursion
-;      (goto-char (point-min))
-;      (when (and org-ans2
-;		 (re-search-forward "\\[[-0-9]+\\]" nil t)
-;		 (get-text-property (match-end 0) 'field))
-;	(let ((inhibit-read-only t))
-;	  (replace-match (concat "[" org-ans2 "]") t t)
-;	  (add-text-properties (point-min) (1+ (match-end 0))
-;			       (text-properties-at (1+ (point-min)))))))))
-
 (defun org-calendar-select ()
   "Return to `org-read-date' with the date currently selected.
 This is used by `org-read-date' in a temporary keymap for the calendar buffer."
@@ -12020,7 +12048,7 @@ The images can be removed again with \\[org-ctrl-c-ctrl-c]."
 (org-defkey org-mode-map "\C-c|"    'org-table-create-or-convert-from-region)
 (org-defkey org-mode-map [(control ?#)] 'org-table-rotate-recalc-marks)
 (org-defkey org-mode-map "\C-c~"    'org-table-create-with-table.el)
-(org-defkey org-mode-map "\C-c\C-q" 'org-table-wrap-region)
+(org-defkey org-mode-map "\C-c\C-a" 'org-attach)
 (org-defkey org-mode-map "\C-c}"    'org-table-toggle-coordinate-overlays)
 (org-defkey org-mode-map "\C-c{"    'org-table-toggle-formula-debugger)
 (org-defkey org-mode-map "\C-c\C-e" 'org-export)