summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarsten Dominik <carsten.dominik@gmail.com>2008-10-01 09:25:18 +0200
committerCarsten Dominik <carsten.dominik@gmail.com>2008-10-01 09:25:18 +0200
commitd043e31182595983df3d191e80ca941ee171c400 (patch)
tree05656894d4362d7944539ad72e93554ba6b87893
parent001e6df25c75e2566ad9c083341b4906e86cd8d8 (diff)
downloadorg-mode-d043e31182595983df3d191e80ca941ee171c400.tar.gz
Integrate John Wiegley's org-attach.el.
-rw-r--r--Makefile2
-rw-r--r--ORGWEBPAGE/Changes.org38
-rw-r--r--doc/org.texi143
-rw-r--r--doc/orgcard.tex9
-rwxr-xr-xlisp/ChangeLog24
-rw-r--r--lisp/org-agenda.el1
-rw-r--r--lisp/org-attach.el236
-rw-r--r--lisp/org-id.el51
-rw-r--r--lisp/org-table.el1
-rw-r--r--lisp/org.el90
10 files changed, 514 insertions, 81 deletions
diff --git a/Makefile b/Makefile
index 778e55e..d113469 100644
--- a/Makefile
+++ b/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
diff --git a/ORGWEBPAGE/Changes.org b/ORGWEBPAGE/Changes.org
index 8140c52..af2aad6 100644
--- a/ORGWEBPAGE/Changes.org
+++ b/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
diff --git a/doc/org.texi b/doc/org.texi
index 0df2f10..812fee8 100644
--- a/doc/org.texi
+++ b/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.
diff --git a/doc/orgcard.tex b/doc/orgcard.tex
index 3fbb352..5d1ba41 100644
--- a/doc/orgcard.tex
+++ b/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}{>}
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 2687afd..6f341a6 100755
--- a/lisp/ChangeLog
+++ b/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.
diff --git a/lisp/org-agenda.el b/lisp/org-agenda.el
index 93ecfbf..d76bf84 100644
--- a/lisp/org-agenda.el
+++ b/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)
diff --git a/lisp/org-attach.el b/lisp/org-attach.el
new file mode 100644
index 0000000..ee85e88
--- /dev/null
+++ b/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
diff --git a/lisp/org-id.el b/lisp/org-id.el
index cbaa086..68224e2 100644
--- a/lisp/org-id.el
+++ b/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)
diff --git a/lisp/org-table.el b/lisp/org-table.el
index e5cfaec..a927d77 100644
--- a/lisp/org-table.el
+++ b/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)
diff --git a/lisp/org.el b/lisp/org.el
index dec71d7..1fc4fcf 100644
--- a/lisp/org.el
+++ b/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)