diff options
author | Carsten Dominik <carsten.dominik@gmail.com> | 2008-10-01 09:25:18 +0200 |
---|---|---|
committer | Carsten Dominik <carsten.dominik@gmail.com> | 2008-10-01 09:25:18 +0200 |
commit | d043e31182595983df3d191e80ca941ee171c400 (patch) | |
tree | 05656894d4362d7944539ad72e93554ba6b87893 | |
parent | 001e6df25c75e2566ad9c083341b4906e86cd8d8 (diff) | |
download | org-mode-d043e31182595983df3d191e80ca941ee171c400.tar.gz |
Integrate John Wiegley's org-attach.el.
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | ORGWEBPAGE/Changes.org | 38 | ||||
-rw-r--r-- | doc/org.texi | 143 | ||||
-rw-r--r-- | doc/orgcard.tex | 9 | ||||
-rwxr-xr-x | lisp/ChangeLog | 24 | ||||
-rw-r--r-- | lisp/org-agenda.el | 1 | ||||
-rw-r--r-- | lisp/org-attach.el | 236 | ||||
-rw-r--r-- | lisp/org-id.el | 51 | ||||
-rw-r--r-- | lisp/org-table.el | 1 | ||||
-rw-r--r-- | lisp/org.el | 90 |
10 files changed, 514 insertions, 81 deletions
@@ -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) |