Browse Source

Integrate John Wiegley's org-attach.el.

Carsten Dominik 16 years ago
parent
commit
d043e31182
10 changed files with 514 additions and 81 deletions
  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
 # The following variables need to be defined by the maintainer
 LISPF      = 	org.el			\
 LISPF      = 	org.el			\
 		org-agenda.el		\
 		org-agenda.el		\
+	     	org-attach.el		\
 	     	org-archive.el		\
 	     	org-archive.el		\
 		org-bbdb.el		\
 		org-bbdb.el		\
 		org-bibtex.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.elc:           lisp/org-macs.elc lisp/org-compat.elc lisp/org-faces.elc
 lisp/org-agenda.elc:       lisp/org.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-archive.elc:      lisp/org.elc
 lisp/org-bbdb.elc:         lisp/org.elc
 lisp/org-bbdb.elc:         lisp/org.elc
 lisp/org-bibtex.elc:       lisp/org.elc
 lisp/org-bibtex.elc:       lisp/org.elc

+ 38 - 0
ORGWEBPAGE/Changes.org

@@ -15,15 +15,53 @@
 :VISIBILITY: content
 :VISIBILITY: content
 :END:
 :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
 ** 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
 *** New remember template escape to add a property
     Thanks to James TD Smith for a patch to this effect.
     Thanks to James TD Smith for a patch to this effect.
+
 *** Clicking with mouse-2 on clock info in mode-line visits the clock.
 *** Clicking with mouse-2 on clock info in mode-line visits the clock.
     Thanks to James TD Smith for a patch to this effect.
     Thanks to James TD Smith for a patch to this effect.
+
 *** New file in contrib: lisp/org-checklist.el
 *** New file in contrib: lisp/org-checklist.el
     Thanks to James TD Smith for this contribution.
     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
 * Version 6.07
 :PROPERTIES:
 :PROPERTIES:
 :VISIBILITY: content
 :VISIBILITY: content

+ 123 - 20
doc/org.texi

@@ -1,4 +1,4 @@
-925\input texinfo
+\input texinfo
 @c %**start of header
 @c %**start of header
 @setfilename ../../info/org
 @setfilename ../../info/org
 @settitle The Org Manual
 @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
 * Tags::                        Tagging headlines and matching sets of tags
 * Properties and Columns::      Storing information about an entry
 * Properties and Columns::      Storing information about an entry
 * Dates and Times::             Making items useful for planning
 * 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
 * Agenda Views::                Collecting information into views
 * Embedded LaTeX::              LaTeX fragments and formulas
 * Embedded LaTeX::              LaTeX fragments and formulas
 * Exporting::                   Sharing and publishing of notes
 * Exporting::                   Sharing and publishing of notes
@@ -230,6 +230,11 @@ Deadlines and scheduling
 * Inserting deadline/schedule::  Planning items
 * Inserting deadline/schedule::  Planning items
 * Repeated tasks::              Items that show up again and again
 * Repeated tasks::              Items that show up again and again
 
 
+Capture
+
+* Remember::                    Capture new tasks/ideas with little interruption
+* Attachments::                 Add files to tasks.
+
 Remember
 Remember
 
 
 * Setting up Remember::         Some code for .emacs to get things going
 * 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.
 tables, @kbd{S-@key{TAB}} jumps to the previous field.
 
 
 @cindex show all, command
 @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.
 Show all.
 @kindex C-c C-r
 @kindex C-c C-r
 @item 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
 features based on them.  For more information see @ref{Using the
 property API}.
 property API}.
 
 
-@node Dates and Times, Remember, Properties and Columns, Top
+@node Dates and Times, Capture, Properties and Columns, Top
 @chapter Dates and Times
 @chapter Dates and Times
 @cindex dates
 @cindex dates
 @cindex times
 @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
 appointments on a day that take place over a specified time interval will
 then also be added to the load estimate of the day.
 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}
 @cindex @file{remember.el}
 
 
 The @i{Remember} package by John Wiegley lets you store quick notes with
 The @i{Remember} package by John Wiegley lets you store quick notes with
@@ -4894,7 +4914,7 @@ interactively, on the fly.
 @end menu
 @end menu
 
 
 @node Setting up Remember, Remember templates, Remember, Remember
 @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
 The following customization will tell @i{remember} to use org files as
 target, and to create annotations compatible with Org links.
 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.
 the cursor date in the agenda, rather than to the current date.
 
 
 @node Remember templates, Storing notes, Setting up Remember, Remember
 @node Remember templates, Storing notes, Setting up Remember, Remember
-@section Remember templates
+@subsection Remember templates
 @cindex templates, for remember
 @cindex templates, for remember
 
 
 In combination with Org, you can use templates to generate
 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.
 template that will be filled with the previous context information.
 
 
 @node Storing notes, Refiling notes, Remember templates, Remember
 @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
 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
 @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.
       @tab at cursor position, level taken from context.
 @end multitable
 @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
 @node Refiling notes,  , Storing notes, Remember
-@section Refiling notes
+@subsection Refiling notes
 @cindex refiling notes
 @cindex refiling notes
 
 
 Remember is usually used to quickly capture notes and tasks into one or
 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.
 Jump to the location where @code{org-refile} last moved a tree to.
 @end table
 @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
 @chapter Agenda Views
 @cindex agenda views
 @cindex agenda views
 
 
@@ -5985,6 +6073,10 @@ key for this.
 @itemx S-@key{down}
 @itemx S-@key{down}
 Decrease the priority of the current item.
 Decrease the priority of the current item.
 @c
 @c
+@kindex C-c C-a
+@item C-c C-a
+Dispatcher for all command related to attachments.
+@c
 @kindex C-c C-s
 @kindex C-c C-s
 @item C-c C-s
 @item C-c C-s
 Schedule this item
 Schedule this item
@@ -9456,6 +9548,16 @@ Get all property keys in the current buffer.
 Insert a property drawer at point.
 Insert a property drawer at point.
 @end defun
 @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
 @defun org-entry-add-to-multivalued-property pom property value
 Treat the value of the property PROPERTY as a whitespace-separated list of
 Treat the value of the property PROPERTY as a whitespace-separated list of
 values and make sure that VALUE is in this list.
 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
 development of Org was fully independent because I was not aware of the
 existence of these packages.  But with time I have accasionally looked
 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
 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
 @item
 @i{Carsten Wimmer} suggested some changes and helped fix a bug in
 @i{Carsten Wimmer} suggested some changes and helped fix a bug in
 linking to Gnus.
 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 current subtree between states}{TAB}
 \key{rotate entire buffer between states}{S-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{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}
 \section{Motion}
 
 
@@ -648,13 +647,13 @@ after  ``{\tt :}'', and dictionary words elsewhere.
 \key{digit argument}{0-9}
 \key{digit argument}{0-9}
 \key{change state of current TODO item}{t}
 \key{change state of current TODO item}{t}
 \key{kill item and source}{C-k}
 \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{show tags of current headline}{T}
 \key{set tags for current headline/region}{:}
 \key{set tags for current headline/region}{:}
-\key{toggle ARCHIVE tag}{a}
 \key{set priority of current item}{p}
 \key{set priority of current item}{p}
 \key{raise/lower priority of current item}{S-UP/DOWN$^3$}
 \key{raise/lower priority of current item}{S-UP/DOWN$^3$}
 \key{display weighted priority of current item}{P}
 \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{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 one day earlier/later}{S-LEFT/RIGHT$^3$}
 \key{change timestamp to today}{>}
 \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>
 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
 	* org-list.el (org-move-item-down, org-move-item-up): Remember and
 	restore the column of the cursor position.
 	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 "T" 'org-agenda-show-tags)
 (org-defkey org-agenda-mode-map "n" 'next-line)
 (org-defkey org-agenda-mode-map "n" 'next-line)
 (org-defkey org-agenda-mode-map "p" 'previous-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-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 "\C-c\C-p" 'org-agenda-previous-date-line)
 (org-defkey org-agenda-mode-map "," 'org-agenda-priority)
 (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 "Org's internal method" org)
 	  (const :tag "external: uuidgen" uuidgen)))
 	  (const :tag "external: uuidgen" uuidgen)))
 
 
-(defcustom org-id-prefix "Org"
+(defcustom org-id-prefix nil
   "The prefix for IDs.
   "The prefix for IDs.
 
 
 This may be a string, or it can be nil to indicate that no prefix is required.
 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)
      ((eq org-id-method 'uuidgen)
       (setq unique (substring (shell-command-to-string "uuidgen") 1 -1)))
       (setq unique (substring (shell-command-to-string "uuidgen") 1 -1)))
      ((eq org-id-method 'org)
      ((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
 	     (postfix (if org-id-include-domain
 			  (progn
 			  (progn
 			    (require 'message)
 			    (require 'message)
@@ -261,51 +261,53 @@ So a typical ID could look like \"Org:4nd91V40HI\"."
      (t (error "Invalid `org-id-method'")))
      (t (error "Invalid `org-id-method'")))
     (concat prefix unique)))
     (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."
   "Turn an integer between 0 and 61 into a single character 0..9, A..Z, a..z."
   (cond
   (cond
    ((< i 10) (+ ?0 i))
    ((< 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.
   "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."
 The input I may be a character, or a single-letter string."
   (and (stringp i) (setq i (string-to-char i)))
   (and (stringp i) (setq i (string-to-char i)))
   (cond
   (cond
    ((and (>= i ?0) (<= i ?9)) (- i ?0))
    ((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 ""))
   (let ((s ""))
     (while (> i 0)
     (while (> i 0)
       (setq s (concat (char-to-string
       (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)))
     (setq length (max 1 (or length 1)))
     (if (< (length s) length)
     (if (< (length s) length)
 	(setq s (concat (make-string (- length (length s)) ?0) s)))
 	(setq s (concat (make-string (- length (length s)) ?0) s)))
     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))
   (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)
 	  s)
     r))
     r))
 
 
-(defun org-id-time-to-b62 (&optional time)
+(defun org-id-time-to-b36 (&optional time)
   "Encode TIME as a 10-digit string.
   "Encode TIME as a 10-digit string.
 This string holds the time to micro-second accuracy, and can be decoded
 This string holds the time to micro-second accuracy, and can be decoded
 using `org-id-decode'."
 using `org-id-decode'."
+  (debug)
   (setq time (or time (current-time)))
   (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)
 (defun org-id-decode (id)
   "Split ID into the prefix and the time value that was used to create it.
   "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))
     (if (= 2 (length parts))
 	(setq prefix (car parts) time (nth 1 parts))
 	(setq prefix (car parts) time (nth 1 parts))
       (setq prefix nil time (nth 0 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)))
     (cons prefix time)))
 
 
 ;; Storing ID locations (files)
 ;; 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-c{"              org-table-toggle-formula-debugger)
 	  '("\C-m"               org-table-next-row)
 	  '("\C-m"               org-table-next-row)
 	  '([(shift return)]     org-table-copy-down)
 	  '([(shift return)]     org-table-copy-down)
-	  '("\C-c\C-q"           org-table-wrap-region)
 	  '("\C-c?"              org-table-field-info)
 	  '("\C-c?"              org-table-field-info)
 	  '("\C-c "              org-table-blank-field)
 	  '("\C-c "              org-table-blank-field)
 	  '("\C-c+"              org-table-sum)
 	  '("\C-c+"              org-table-sum)

+ 59 - 31
lisp/org.el

@@ -1115,9 +1115,7 @@ See `org-file-apps'.")
 (defconst org-file-apps-defaults-macosx
 (defconst org-file-apps-defaults-macosx
   '((remote . emacs)
   '((remote . emacs)
     (t . "open %s")
     (t . "open %s")
-    ("ps"     . "gv %s")
     ("ps.gz"  . "gv %s")
     ("ps.gz"  . "gv %s")
-    ("eps"    . "gv %s")
     ("eps.gz" . "gv %s")
     ("eps.gz" . "gv %s")
     ("dvi"    . "xdvi %s")
     ("dvi"    . "xdvi %s")
     ("fig"    . "xfig %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.
   1. OVERVIEW: Show only top-level headlines.
   2. CONTENTS: Show all headlines of all levels, but no body text.
   2. CONTENTS: Show all headlines of all levels, but no body text.
   3. SHOW ALL: Show everything.
   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
   determined by the variable `org-startup-folded', and by any VISIBILITY
   properties in the buffer.
   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
 - When point is at the beginning of a headline, rotate the subtree started
   by this line through 3 different states (local cycling)
   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))
      ((equal arg '(16))
       (org-set-startup-visibility)
       (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)
      ((org-at-table-p 'any)
       ;; Enter the table or move to the next field in the table
       ;; 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)))
 		    (and dirp (cdr (assoc 'directory apps)))
 		    (cdr (assoc ext apps))
 		    (cdr (assoc ext apps))
 		    (cdr (assoc t apps)))))
 		    (cdr (assoc t apps)))))
+    (when (eq cmd 'default)
+      (setq cmd (cdr (assoc t apps))))
     (when (eq cmd 'mailcap)
     (when (eq cmd 'mailcap)
       (require 'mailcap)
       (require 'mailcap)
       (mailcap-parse-mailcaps)
       (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."
   "Add VALUE to the words in the PROPERTY in entry at point-or-marker POM."
   (let* ((old (org-entry-get pom property))
   (let* ((old (org-entry-get pom property))
 	 (values (and old (org-split-string old "[ \t]"))))
 	 (values (and old (org-split-string old "[ \t]"))))
+    (setq value (org-entry-protect-space value))
     (unless (member value values)
     (unless (member value values)
       (setq values (cons value values))
       (setq values (cons value values))
       (org-entry-put pom property
       (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."
   "Remove VALUE from words in the PROPERTY in entry at point-or-marker POM."
   (let* ((old (org-entry-get pom property))
   (let* ((old (org-entry-get pom property))
 	 (values (and old (org-split-string old "[ \t]"))))
 	 (values (and old (org-split-string old "[ \t]"))))
+    (setq value (org-entry-protect-space value))
     (when (member value values)
     (when (member value values)
       (setq values (delete value values))
       (setq values (delete value values))
       (org-entry-put pom property
       (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?"
   "Is VALUE one of the words in the PROPERTY in entry at point-or-marker POM?"
   (let* ((old (org-entry-get pom property))
   (let* ((old (org-entry-get pom property))
 	 (values (and old (org-split-string old "[ \t]"))))
 	 (values (and old (org-split-string old "[ \t]"))))
+    (setq value (org-entry-protect-space value))
     (member value values)))
     (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))
 (defvar org-entry-property-inherited-from (make-marker))
 
 
 (defun org-entry-get-with-inheritance (property)
 (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-end-time-was-given) ; dynamically scoped parameter
 (defvar org-ts-what) ; 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.
   "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
 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.
 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)))
 	 (default-input (and ts (org-get-compact-tod ts)))
 	 org-time-was-given org-end-time-was-given time)
 	 org-time-was-given org-end-time-was-given time)
     (cond
     (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 "--")
       (insert "--")
       (setq time (let ((this-command this-command))
       (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))
       (setq time (let ((this-command this-command))
 		   (org-read-date arg 'totime nil nil default-time default-input)))
 		   (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 "")
 	(replace-match "")
 	(setq org-last-changed-timestamp
 	(setq org-last-changed-timestamp
 	      (org-insert-time-stamp
 	      (org-insert-time-stamp
 	       time (or org-time-was-given arg)
 	       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"))
       (message "Timestamp updated"))
      (t
      (t
       (setq time (let ((this-command this-command))
       (setq time (let ((this-command this-command))
 		   (org-read-date arg 'totime nil nil default-time default-input)))
 		   (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?
 ;; FIXME: can we use this for something else, like computing time differences?
 (defun org-get-compact-tod (s)
 (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.
 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."
 So these are more for recording a certain time/date."
   (interactive "P")
   (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))
 (defvar org-date-ovl (org-make-overlay 1 1))
 (org-overlay-put org-date-ovl 'face 'org-warning)
 (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))
     (org-move-overlay org-date-ovl (1- (point)) (1+ (point)) (current-buffer))
     (select-window sw)))
     (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 ()
 (defun org-calendar-select ()
   "Return to `org-read-date' with the date currently selected.
   "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."
 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 "\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 [(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~"    '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-coordinate-overlays)
 (org-defkey org-mode-map "\C-c{"    'org-table-toggle-formula-debugger)
 (org-defkey org-mode-map "\C-c{"    'org-table-toggle-formula-debugger)
 (org-defkey org-mode-map "\C-c\C-e" 'org-export)
 (org-defkey org-mode-map "\C-c\C-e" 'org-export)