Commit | Line | Data |
---|---|---|
86fbb8ca CD |
1 | ;;; org-capture.el --- Fast note taking in Org-mode |
2 | ||
ab422c4d | 3 | ;; Copyright (C) 2010-2013 Free Software Foundation, Inc. |
86fbb8ca CD |
4 | |
5 | ;; Author: Carsten Dominik <carsten at orgmode dot org> | |
6 | ;; Keywords: outlines, hypermedia, calendar, wp | |
7 | ;; Homepage: http://orgmode.org | |
86fbb8ca CD |
8 | ;; |
9 | ;; This file is part of GNU Emacs. | |
10 | ;; | |
11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
12 | ;; it under the terms of the GNU General Public License as published by | |
13 | ;; the Free Software Foundation, either version 3 of the License, or | |
14 | ;; (at your option) any later version. | |
15 | ||
16 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
20 | ||
21 | ;; You should have received a copy of the GNU General Public License | |
22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
23 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
24 | ;; | |
25 | ;;; Commentary: | |
26 | ||
27 | ;; This file contains an alternative implementation of the same functionality | |
28 | ;; that is also provided by org-remember.el. The implementation is more | |
29 | ;; streamlined, can produce more target types (e.g. plain list items or | |
30 | ;; table lines). Also, it does not use a temporary buffer for editing | |
31 | ;; the captured entry - instead it uses an indirect buffer that visits | |
32 | ;; the new entry already in the target buffer (this was an idea by Samuel | |
33 | ;; Wales). John Wiegley's excellent `remember.el' is not needed for this | |
34 | ;; implementation, even though we borrow heavily from its ideas. | |
35 | ||
36 | ;; This implementation heavily draws on ideas by James TD Smith and | |
37 | ;; Samuel Wales, and, of cause, uses John Wiegley's remember.el as inspiration. | |
38 | ||
39 | ;;; TODO | |
40 | ||
41 | ;; - find a clever way to not always insert an annotation maybe a | |
42 | ;; predicate function that can check for conditions for %a to be | |
43 | ;; used. This could be one of the properties. | |
44 | ||
45 | ;; - Should there be plist members that arrange for properties to be | |
46 | ;; asked for, like James proposed in his RFC? | |
47 | ||
48 | ;;; Code: | |
49 | ||
50 | (eval-when-compile | |
51 | (require 'cl)) | |
52 | (require 'org) | |
53 | (require 'org-mks) | |
54 | ||
55 | (declare-function org-datetree-find-date-create "org-datetree" | |
3ab2c837 | 56 | (date &optional keep-restriction)) |
86fbb8ca CD |
57 | (declare-function org-table-get-specials "org-table" ()) |
58 | (declare-function org-table-goto-line "org-table" (N)) | |
14e1337f | 59 | (declare-function org-pop-to-buffer-same-window "org-compat" |
e66ba1df | 60 | (&optional buffer-or-name norecord label)) |
8223b1d2 BG |
61 | (declare-function org-at-encrypted-entry-p "org-crypt" ()) |
62 | (declare-function org-encrypt-entry "org-crypt" ()) | |
63 | (declare-function org-decrypt-entry "org-crypt" ()) | |
3ab2c837 | 64 | |
86fbb8ca CD |
65 | (defvar org-remember-default-headline) |
66 | (defvar org-remember-templates) | |
67 | (defvar org-table-hlines) | |
3ab2c837 | 68 | (defvar dired-buffers) |
86fbb8ca CD |
69 | |
70 | (defvar org-capture-clock-was-started nil | |
71 | "Internal flag, noting if the clock was started.") | |
72 | ||
73 | (defvar org-capture-last-stored-marker (make-marker) | |
74 | "Marker pointing to the entry most recently stored with `org-capture'.") | |
75 | ||
76 | ;; The following variable is scoped dynamically by org-protocol | |
77 | ;; to indicate that the link properties have already been stored | |
78 | (defvar org-capture-link-is-already-stored nil) | |
79 | ||
80 | (defgroup org-capture nil | |
81 | "Options concerning capturing new entries." | |
82 | :tag "Org Capture" | |
83 | :group 'org) | |
84 | ||
85 | (defcustom org-capture-templates nil | |
86 | "Templates for the creation of new entries. | |
87 | ||
88 | Each entry is a list with the following items: | |
89 | ||
90 | keys The keys that will select the template, as a string, characters | |
91 | only, for example \"a\" for a template to be selected with a | |
92 | single key, or \"bt\" for selection with two keys. When using | |
93 | several keys, keys using the same prefix key must be together | |
94 | in the list and preceded by a 2-element entry explaining the | |
95 | prefix key, for example | |
96 | ||
97 | (\"b\" \"Templates for marking stuff to buy\") | |
98 | ||
99 | The \"C\" key is used by default for quick access to the | |
100 | customization of the template variable. But if you want to use | |
101 | that key for a template, you can. | |
102 | ||
103 | description A short string describing the template, will be shown during | |
104 | selection. | |
105 | ||
106 | type The type of entry. Valid types are: | |
8223b1d2 | 107 | entry an Org-mode node, with a headline. Will be |
86fbb8ca CD |
108 | filed as the child of the target entry or as |
109 | a top-level entry. | |
110 | item a plain list item, will be placed in the | |
111 | first plain list at the target | |
112 | location. | |
113 | checkitem a checkbox item. This differs from the | |
114 | plain list item only is so far as it uses a | |
115 | different default template. | |
116 | table-line a new line in the first table at target location. | |
117 | plain text to be inserted as it is. | |
118 | ||
119 | target Specification of where the captured item should be placed. | |
120 | In Org-mode files, targets usually define a node. Entries will | |
121 | become children of this node, other types will be added to the | |
122 | table or list in the body of this node. | |
123 | ||
3ab2c837 BG |
124 | Most target specifications contain a file name. If that file |
125 | name is the empty string, it defaults to `org-default-notes-file'. | |
126 | A file can also be given as a variable, function, or Emacs Lisp | |
127 | form. | |
128 | ||
86fbb8ca CD |
129 | Valid values are: |
130 | ||
131 | (file \"path/to/file\") | |
132 | Text will be placed at the beginning or end of that file | |
133 | ||
134 | (id \"id of existing org entry\") | |
135 | File as child of this entry, or in the body of the entry | |
136 | ||
137 | (file+headline \"path/to/file\" \"node headline\") | |
138 | Fast configuration if the target heading is unique in the file | |
139 | ||
140 | (file+olp \"path/to/file\" \"Level 1 heading\" \"Level 2\" ...) | |
141 | For non-unique headings, the full path is safer | |
142 | ||
143 | (file+regexp \"path/to/file\" \"regexp to find location\") | |
144 | File to the entry matching regexp | |
145 | ||
146 | (file+datetree \"path/to/file\") | |
acedf35c CD |
147 | Will create a heading in a date tree for today's date |
148 | ||
149 | (file+datetree+prompt \"path/to/file\") | |
3ab2c837 | 150 | Will create a heading in a date tree, prompts for date |
86fbb8ca CD |
151 | |
152 | (file+function \"path/to/file\" function-finding-location) | |
153 | A function to find the right location in the file | |
154 | ||
155 | (clock) | |
156 | File to the entry that is currently being clocked | |
157 | ||
158 | (function function-finding-location) | |
159 | Most general way, write your own function to find both | |
160 | file and location | |
161 | ||
162 | template The template for creating the capture item. If you leave this | |
163 | empty, an appropriate default template will be used. See below | |
164 | for more details. Instead of a string, this may also be one of | |
165 | ||
166 | (file \"/path/to/template-file\") | |
167 | (function function-returning-the-template) | |
168 | ||
169 | in order to get a template from a file, or dynamically | |
170 | from a function. | |
171 | ||
172 | The rest of the entry is a property list of additional options. Recognized | |
173 | properties are: | |
174 | ||
175 | :prepend Normally newly captured information will be appended at | |
176 | the target location (last child, last table line, | |
177 | last list item...). Setting this property will | |
178 | change that. | |
179 | ||
180 | :immediate-finish When set, do not offer to edit the information, just | |
181 | file it away immediately. This makes sense if the | |
182 | template only needs information that can be added | |
183 | automatically. | |
184 | ||
185 | :empty-lines Set this to the number of lines the should be inserted | |
186 | before and after the new item. Default 0, only common | |
187 | other value is 1. | |
188 | ||
8223b1d2 BG |
189 | :empty-lines-before Set this to the number of lines the should be inserted |
190 | before the new item. Overrides :empty-lines for the | |
191 | number lines inserted before. | |
192 | ||
193 | :empty-lines-after Set this to the number of lines the should be inserted | |
194 | after the new item. Overrides :empty-lines for the | |
195 | number of lines inserted after. | |
196 | ||
86fbb8ca CD |
197 | :clock-in Start the clock in this item. |
198 | ||
3ab2c837 BG |
199 | :clock-keep Keep the clock running when filing the captured entry. |
200 | ||
86fbb8ca | 201 | :clock-resume Start the interrupted clock when finishing the capture. |
3ab2c837 BG |
202 | Note that :clock-keep has precedence over :clock-resume. |
203 | When setting both to `t', the current clock will run and | |
204 | the previous one will not be resumed. | |
86fbb8ca CD |
205 | |
206 | :unnarrowed Do not narrow the target buffer, simply show the | |
207 | full buffer. Default is to narrow it so that you | |
208 | only see the new stuff. | |
209 | ||
210 | :table-line-pos Specification of the location in the table where the | |
3ab2c837 BG |
211 | new line should be inserted. It should be a string like |
212 | \"II-3\", meaning that the new line should become the | |
213 | third line before the second horizontal separator line. | |
86fbb8ca | 214 | |
afe98dfa CD |
215 | :kill-buffer If the target file was not yet visited by a buffer when |
216 | capture was invoked, kill the buffer again after capture | |
217 | is finalized. | |
218 | ||
3ab2c837 BG |
219 | The template defines the text to be inserted. Often this is an |
220 | org-mode entry (so the first line should start with a star) that | |
221 | will be filed as a child of the target headline. It can also be | |
222 | freely formatted text. Furthermore, the following %-escapes will | |
223 | be replaced with content and expanded in this order: | |
224 | ||
8223b1d2 BG |
225 | %[pathname] Insert the contents of the file given by `pathname'. |
226 | %(sexp) Evaluate elisp `(sexp)' and replace with the result. | |
227 | %<...> The result of format-time-string on the ... format specification. | |
228 | %t Time stamp, date only. | |
229 | %T Time stamp with date and time. | |
230 | %u, %U Like the above, but inactive time stamps. | |
231 | %i Initial content, copied from the active region. If %i is | |
86fbb8ca | 232 | indented, the entire inserted text will be indented as well. |
8223b1d2 BG |
233 | %a Annotation, normally the link created with `org-store-link'. |
234 | %A Like %a, but prompt for the description part. | |
235 | %l Like %a, but only insert the literal link. | |
236 | %c Current kill ring head. | |
237 | %x Content of the X clipboard. | |
238 | %k Title of currently clocked task. | |
239 | %K Link to currently clocked task. | |
240 | %n User name (taken from `user-full-name'). | |
241 | %f File visited by current buffer when org-capture was called. | |
242 | %F Full path of the file or directory visited by current buffer. | |
243 | %:keyword Specific information for certain link types, see below. | |
244 | %^g Prompt for tags, with completion on tags in target file. | |
245 | %^G Prompt for tags, with completion on all tags in all agenda files. | |
246 | %^t Like %t, but prompt for date. Similarly %^T, %^u, %^U. | |
247 | You may define a prompt like: %^{Please specify birthday}t | |
248 | %^C Interactive selection of which kill or clip to use. | |
249 | %^L Like %^C, but insert as link. | |
250 | %^{prop}p Prompt the user for a value for property `prop'. | |
251 | %^{prompt} Prompt the user for a string and replace this sequence with it. | |
3ab2c837 BG |
252 | A default value and a completion table ca be specified like this: |
253 | %^{prompt|default|completion2|completion3|...}. | |
86fbb8ca | 254 | %? After completing the template, position cursor here. |
8223b1d2 BG |
255 | %\\n Insert the text entered at the nth %^{prompt}, where `n' is |
256 | a number, starting from 1. | |
86fbb8ca | 257 | |
8223b1d2 BG |
258 | Apart from these general escapes, you can access information specific to |
259 | the link type that is created. For example, calling `org-capture' in emails | |
260 | or in Gnus will record the author and the subject of the message, which you | |
afe98dfa | 261 | can access with \"%:from\" and \"%:subject\", respectively. Here is a |
86fbb8ca CD |
262 | complete list of what is recorded for each link type. |
263 | ||
afe98dfa CD |
264 | Link type | Available information |
265 | ------------------------+------------------------------------------------------ | |
266 | bbdb | %:type %:name %:company | |
8223b1d2 BG |
267 | vm, wl, mh, mew, rmail, | %:type %:subject %:message-id |
268 | gnus | %:from %:fromname %:fromaddress | |
afe98dfa CD |
269 | | %:to %:toname %:toaddress |
270 | | %:fromto (either \"to NAME\" or \"from NAME\") | |
8223b1d2 | 271 | | %:date %:date-timestamp (as active timestamp) |
afe98dfa CD |
272 | | %:date-timestamp-inactive (as inactive timestamp) |
273 | gnus | %:group, for messages also all email fields | |
274 | w3, w3m | %:type %:url | |
275 | info | %:type %:file %:node | |
276 | calendar | %:type %:date" | |
86fbb8ca | 277 | :group 'org-capture |
372d7b21 | 278 | :version "24.1" |
86fbb8ca CD |
279 | :type |
280 | '(repeat | |
281 | (choice :value ("" "" entry (file "~/org/notes.org") "") | |
8223b1d2 BG |
282 | (list :tag "Multikey description" |
283 | (string :tag "Keys ") | |
284 | (string :tag "Description")) | |
285 | (list :tag "Template entry" | |
286 | (string :tag "Keys ") | |
287 | (string :tag "Description ") | |
288 | (choice :tag "Capture Type " :value entry | |
289 | (const :tag "Org entry" entry) | |
290 | (const :tag "Plain list item" item) | |
291 | (const :tag "Checkbox item" checkitem) | |
292 | (const :tag "Plain text" plain) | |
293 | (const :tag "Table line" table-line)) | |
294 | (choice :tag "Target location" | |
295 | (list :tag "File" | |
296 | (const :format "" file) | |
297 | (file :tag " File")) | |
298 | (list :tag "ID" | |
299 | (const :format "" id) | |
300 | (string :tag " ID")) | |
301 | (list :tag "File & Headline" | |
302 | (const :format "" file+headline) | |
303 | (file :tag " File ") | |
304 | (string :tag " Headline")) | |
305 | (list :tag "File & Outline path" | |
306 | (const :format "" file+olp) | |
307 | (file :tag " File ") | |
308 | (repeat :tag "Outline path" :inline t | |
309 | (string :tag "Headline"))) | |
310 | (list :tag "File & Regexp" | |
311 | (const :format "" file+regexp) | |
312 | (file :tag " File ") | |
313 | (regexp :tag " Regexp")) | |
314 | (list :tag "File & Date tree" | |
315 | (const :format "" file+datetree) | |
316 | (file :tag " File")) | |
317 | (list :tag "File & Date tree, prompt for date" | |
318 | (const :format "" file+datetree+prompt) | |
319 | (file :tag " File")) | |
320 | (list :tag "File & function" | |
321 | (const :format "" file+function) | |
322 | (file :tag " File ") | |
323 | (sexp :tag " Function")) | |
324 | (list :tag "Current clocking task" | |
325 | (const :format "" clock)) | |
326 | (list :tag "Function" | |
327 | (const :format "" function) | |
328 | (sexp :tag " Function"))) | |
329 | (choice :tag "Template" | |
330 | (string) | |
331 | (list :tag "File" | |
332 | (const :format "" file) | |
333 | (file :tag "Template file")) | |
334 | (list :tag "Function" | |
335 | (const :format "" function) | |
336 | (function :tag "Template function"))) | |
337 | (plist :inline t | |
338 | ;; Give the most common options as checkboxes | |
339 | :options (((const :format "%v " :prepend) (const t)) | |
340 | ((const :format "%v " :immediate-finish) (const t)) | |
341 | ((const :format "%v " :empty-lines) (const 1)) | |
342 | ((const :format "%v " :clock-in) (const t)) | |
343 | ((const :format "%v " :clock-keep) (const t)) | |
344 | ((const :format "%v " :clock-resume) (const t)) | |
345 | ((const :format "%v " :unnarrowed) (const t)) | |
346 | ((const :format "%v " :kill-buffer) (const t)))))))) | |
86fbb8ca CD |
347 | |
348 | (defcustom org-capture-before-finalize-hook nil | |
3ab2c837 | 349 | "Hook that is run right before a capture process is finalized. |
e66ba1df BG |
350 | The capture buffer is still current when this hook runs and it is |
351 | widened to the entire buffer." | |
86fbb8ca | 352 | :group 'org-capture |
372d7b21 | 353 | :version "24.1" |
86fbb8ca CD |
354 | :type 'hook) |
355 | ||
acedf35c CD |
356 | (defcustom org-capture-after-finalize-hook nil |
357 | "Hook that is run right after a capture process is finalized. | |
8223b1d2 BG |
358 | Suitable for window cleanup." |
359 | :group 'org-capture | |
360 | :version "24.1" | |
361 | :type 'hook) | |
362 | ||
363 | (defcustom org-capture-prepare-finalize-hook nil | |
364 | "Hook that is run before the finalization starts. | |
365 | The capture buffer is current and still narrowed." | |
acedf35c | 366 | :group 'org-capture |
372d7b21 | 367 | :version "24.1" |
acedf35c CD |
368 | :type 'hook) |
369 | ||
8223b1d2 BG |
370 | (defcustom org-capture-bookmark t |
371 | "When non-nil, add a bookmark pointing at the last stored | |
372 | position when capturing." | |
373 | :group 'org-capture | |
374 | :version "24.3" | |
375 | :type 'boolean) | |
376 | ||
86fbb8ca CD |
377 | ;;; The property list for keeping information about the capture process |
378 | ||
379 | (defvar org-capture-plist nil | |
380 | "Plist for the current capture process, global, to avoid having to pass it.") | |
3ab2c837 | 381 | |
86fbb8ca CD |
382 | (defvar org-capture-current-plist nil |
383 | "Local variable holding the plist in a capture buffer. | |
3ab2c837 BG |
384 | This is used to store the plist for use when finishing a capture process |
385 | because another such process might have changed the global variable by then. | |
386 | ||
387 | Each time a new capture buffer has been set up, the global `org-capture-plist' | |
388 | is copied to this variable, which is local in the indirect buffer.") | |
389 | ||
390 | (defvar org-capture-clock-keep nil | |
391 | "Local variable to store the value of the :clock-keep parameter. | |
392 | This is needed in case org-capture-finalize is called interactively.") | |
86fbb8ca CD |
393 | |
394 | (defun org-capture-put (&rest stuff) | |
3ab2c837 | 395 | "Add properties to the capture property list `org-capture-plist'." |
86fbb8ca CD |
396 | (while stuff |
397 | (setq org-capture-plist (plist-put org-capture-plist | |
398 | (pop stuff) (pop stuff))))) | |
399 | (defun org-capture-get (prop &optional local) | |
3ab2c837 BG |
400 | "Get properties from the capture property list `org-capture-plist'. |
401 | When LOCAL is set, use the local variable `org-capture-current-plist', | |
402 | this is necessary after initialization of the capture process, | |
403 | to avoid conflicts with other active capture processes." | |
86fbb8ca CD |
404 | (plist-get (if local org-capture-current-plist org-capture-plist) prop)) |
405 | ||
3ab2c837 | 406 | (defun org-capture-member (prop &optional local) |
27e428e7 | 407 | "Is PROP a property in `org-capture-plist'. |
3ab2c837 BG |
408 | When LOCAL is set, use the local variable `org-capture-current-plist', |
409 | this is necessary after initialization of the capture process, | |
410 | to avoid conflicts with other active capture processes." | |
411 | (plist-get (if local org-capture-current-plist org-capture-plist) prop)) | |
86fbb8ca CD |
412 | |
413 | ;;; The minor mode | |
414 | ||
415 | (defvar org-capture-mode-map (make-sparse-keymap) | |
416 | "Keymap for `org-capture-mode', a minor mode. | |
417 | Use this map to set additional keybindings for when Org-mode is used | |
3ab2c837 | 418 | for a capture buffer.") |
86fbb8ca CD |
419 | |
420 | (defvar org-capture-mode-hook nil | |
421 | "Hook for the minor `org-capture-mode'.") | |
422 | ||
423 | (define-minor-mode org-capture-mode | |
8223b1d2 BG |
424 | "Minor mode for special key bindings in a capture buffer. |
425 | ||
426 | Turning on this mode runs the normal hook `org-capture-mode-hook'." | |
86fbb8ca CD |
427 | nil " Rem" org-capture-mode-map |
428 | (org-set-local | |
429 | 'header-line-format | |
8223b1d2 | 430 | "Capture buffer. Finish `C-c C-c', refile `C-c C-w', abort `C-c C-k'.")) |
86fbb8ca CD |
431 | (define-key org-capture-mode-map "\C-c\C-c" 'org-capture-finalize) |
432 | (define-key org-capture-mode-map "\C-c\C-k" 'org-capture-kill) | |
433 | (define-key org-capture-mode-map "\C-c\C-w" 'org-capture-refile) | |
434 | ||
435 | ;;; The main commands | |
436 | ||
8223b1d2 BG |
437 | (defvar org-capture-initial nil) |
438 | (defvar org-capture-entry nil) | |
bdebdb64 BG |
439 | |
440 | ;;;###autoload | |
8223b1d2 BG |
441 | (defun org-capture-string (string &optional keys) |
442 | (interactive "sInitial text: \n") | |
443 | (let ((org-capture-initial string) | |
444 | (org-capture-entry (org-capture-select-template keys))) | |
445 | (org-capture))) | |
446 | ||
447 | (defcustom org-capture-templates-contexts nil | |
448 | "Alist of capture templates and valid contexts. | |
449 | ||
450 | For example, if you have a capture template \"c\" and you want | |
451 | this template to be accessible only from `message-mode' buffers, | |
452 | use this: | |
453 | ||
a89c8ef0 | 454 | '((\"c\" ((in-mode . \"message-mode\")))) |
8223b1d2 BG |
455 | |
456 | Here are the available contexts definitions: | |
457 | ||
458 | in-file: command displayed only in matching files | |
459 | in-mode: command displayed only in matching modes | |
460 | not-in-file: command not displayed in matching files | |
461 | not-in-mode: command not displayed in matching modes | |
462 | [function]: a custom function taking no argument | |
463 | ||
464 | If you define several checks, the agenda command will be | |
465 | accessible if there is at least one valid check. | |
466 | ||
467 | You can also bind a key to another agenda custom command | |
468 | depending on contextual rules. | |
469 | ||
a89c8ef0 | 470 | '((\"c\" \"d\" ((in-mode . \"message-mode\")))) |
8223b1d2 | 471 | |
a89c8ef0 | 472 | Here it means: in `message-mode buffers', use \"c\" as the |
8223b1d2 | 473 | key for the capture template otherwise associated with \"d\". |
a89c8ef0 | 474 | \(The template originally associated with \"d\" is not displayed |
8223b1d2 BG |
475 | to avoid duplicates.)" |
476 | :version "24.3" | |
477 | :group 'org-capture | |
478 | :type '(repeat (list :tag "Rule" | |
479 | (string :tag " Capture key") | |
480 | (string :tag "Replace by template") | |
481 | (repeat :tag "Available when" | |
482 | (choice | |
483 | (cons :tag "Condition" | |
484 | (choice | |
485 | (const :tag "In file" in-file) | |
486 | (const :tag "Not in file" not-in-file) | |
487 | (const :tag "In mode" in-mode) | |
488 | (const :tag "Not in mode" not-in-mode)) | |
489 | (regexp)) | |
490 | (function :tag "Custom function")))))) | |
491 | ||
492 | (defcustom org-capture-use-agenda-date nil | |
493 | "Non-nil means use the date at point when capturing from agendas. | |
494 | When nil, you can still capturing using the date at point with \\[org-agenda-capture]]." | |
495 | :group 'org-capture | |
496 | :version "24.3" | |
497 | :type 'boolean) | |
498 | ||
86fbb8ca CD |
499 | ;;;###autoload |
500 | (defun org-capture (&optional goto keys) | |
501 | "Capture something. | |
502 | \\<org-capture-mode-map> | |
503 | This will let you select a template from `org-capture-templates', and then | |
504 | file the newly captured information. The text is immediately inserted | |
505 | at the target location, and an indirect buffer is shown where you can | |
506 | edit it. Pressing \\[org-capture-finalize] brings you back to the previous state | |
507 | of Emacs, so that you can continue your work. | |
508 | ||
509 | When called interactively with a \\[universal-argument] prefix argument GOTO, don't capture | |
510 | anything, just go to the file/headline where the selected template | |
511 | stores its notes. With a double prefix argument \ | |
512 | \\[universal-argument] \\[universal-argument], go to the last note | |
513 | stored. | |
514 | ||
515 | When called with a `C-0' (zero) prefix, insert a template at point. | |
516 | ||
8223b1d2 BG |
517 | Lisp programs can set KEYS to a string associated with a template |
518 | in `org-capture-templates'. In this case, interactive selection | |
519 | will be bypassed. | |
520 | ||
521 | If `org-capture-use-agenda-date' is non-nil, capturing from the | |
522 | agenda will use the date at point as the default date." | |
86fbb8ca | 523 | (interactive "P") |
8223b1d2 BG |
524 | (when (and org-capture-use-agenda-date |
525 | (eq major-mode 'org-agenda-mode)) | |
526 | (setq org-overriding-default-time | |
527 | (org-get-cursor-date))) | |
86fbb8ca CD |
528 | (cond |
529 | ((equal goto '(4)) (org-capture-goto-target)) | |
530 | ((equal goto '(16)) (org-capture-goto-last-stored)) | |
531 | (t | |
532 | ;; FIXME: Are these needed? | |
533 | (let* ((orig-buf (current-buffer)) | |
534 | (annotation (if (and (boundp 'org-capture-link-is-already-stored) | |
535 | org-capture-link-is-already-stored) | |
536 | (plist-get org-store-link-plist :annotation) | |
3ab2c837 | 537 | (ignore-errors (org-store-link nil)))) |
8223b1d2 BG |
538 | (entry (or org-capture-entry (org-capture-select-template keys))) |
539 | initial) | |
540 | (setq initial (or org-capture-initial | |
541 | (and (org-region-active-p) | |
542 | (buffer-substring (point) (mark))))) | |
afe98dfa CD |
543 | (when (stringp initial) |
544 | (remove-text-properties 0 (length initial) '(read-only t) initial)) | |
545 | (when (stringp annotation) | |
546 | (remove-text-properties 0 (length annotation) | |
547 | '(read-only t) annotation)) | |
86fbb8ca CD |
548 | (cond |
549 | ((equal entry "C") | |
550 | (customize-variable 'org-capture-templates)) | |
551 | ((equal entry "q") | |
552 | (error "Abort")) | |
553 | (t | |
554 | (org-capture-set-plist entry) | |
555 | (org-capture-get-template) | |
3ab2c837 BG |
556 | (org-capture-put :original-buffer orig-buf |
557 | :original-file (or (buffer-file-name orig-buf) | |
558 | (and (featurep 'dired) | |
559 | (car (rassq orig-buf | |
560 | dired-buffers)))) | |
561 | :original-file-nondirectory | |
562 | (and (buffer-file-name orig-buf) | |
563 | (file-name-nondirectory | |
564 | (buffer-file-name orig-buf))) | |
565 | :annotation annotation | |
86fbb8ca CD |
566 | :initial initial) |
567 | (org-capture-put :default-time | |
568 | (or org-overriding-default-time | |
569 | (org-current-time))) | |
570 | (org-capture-set-target-location) | |
571 | (condition-case error | |
572 | (org-capture-put :template (org-capture-fill-template)) | |
573 | ((error quit) | |
574 | (if (get-buffer "*Capture*") (kill-buffer "*Capture*")) | |
575 | (error "Capture abort: %s" error))) | |
576 | ||
3ab2c837 | 577 | (setq org-capture-clock-keep (org-capture-get :clock-keep)) |
86fbb8ca CD |
578 | (if (equal goto 0) |
579 | ;;insert at point | |
580 | (org-capture-insert-template-here) | |
581 | (condition-case error | |
582 | (org-capture-place-template) | |
583 | ((error quit) | |
584 | (if (and (buffer-base-buffer (current-buffer)) | |
585 | (string-match "\\`CAPTURE-" (buffer-name))) | |
586 | (kill-buffer (current-buffer))) | |
587 | (set-window-configuration (org-capture-get :return-to-wconf)) | |
588 | (error "Capture template `%s': %s" | |
589 | (org-capture-get :key) | |
590 | (nth 1 error)))) | |
8223b1d2 | 591 | (if (and (derived-mode-p 'org-mode) |
3ab2c837 BG |
592 | (org-capture-get :clock-in)) |
593 | (condition-case nil | |
594 | (progn | |
595 | (if (org-clock-is-active) | |
596 | (org-capture-put :interrupted-clock | |
597 | (copy-marker org-clock-marker))) | |
598 | (org-clock-in) | |
599 | (org-set-local 'org-capture-clock-was-started t)) | |
600 | (error | |
601 | "Could not start the clock in this capture buffer"))) | |
86fbb8ca | 602 | (if (org-capture-get :immediate-finish) |
3ab2c837 | 603 | (org-capture-finalize nil))))))))) |
86fbb8ca CD |
604 | |
605 | (defun org-capture-get-template () | |
606 | "Get the template from a file or a function if necessary." | |
607 | (let ((txt (org-capture-get :template)) file) | |
608 | (cond | |
609 | ((and (listp txt) (eq (car txt) 'file)) | |
610 | (if (file-exists-p | |
611 | (setq file (expand-file-name (nth 1 txt) org-directory))) | |
612 | (setq txt (org-file-contents file)) | |
613 | (setq txt (format "* Template file %s not found" (nth 1 txt))))) | |
614 | ((and (listp txt) (eq (car txt) 'function)) | |
615 | (if (fboundp (nth 1 txt)) | |
616 | (setq txt (funcall (nth 1 txt))) | |
617 | (setq txt (format "* Template function %s not found" (nth 1 txt))))) | |
618 | ((not txt) (setq txt "")) | |
619 | ((stringp txt)) | |
620 | (t (setq txt "* Invalid capture template"))) | |
621 | (org-capture-put :template txt))) | |
622 | ||
acedf35c CD |
623 | (defun org-capture-finalize (&optional stay-with-capture) |
624 | "Finalize the capture process. | |
625 | With prefix argument STAY-WITH-CAPTURE, jump to the location of the | |
626 | captured item after finalizing." | |
627 | (interactive "P") | |
86fbb8ca CD |
628 | (unless (and org-capture-mode |
629 | (buffer-base-buffer (current-buffer))) | |
630 | (error "This does not seem to be a capture buffer for Org-mode")) | |
631 | ||
8223b1d2 BG |
632 | (run-hooks 'org-capture-prepare-finalize-hook) |
633 | ||
86fbb8ca CD |
634 | ;; Did we start the clock in this capture buffer? |
635 | (when (and org-capture-clock-was-started | |
636 | org-clock-marker (marker-buffer org-clock-marker) | |
637 | (equal (marker-buffer org-clock-marker) (buffer-base-buffer)) | |
638 | (> org-clock-marker (point-min)) | |
639 | (< org-clock-marker (point-max))) | |
640 | ;; Looks like the clock we started is still running. Clock out. | |
3ab2c837 BG |
641 | (when (not org-capture-clock-keep) (let (org-log-note-clock-out) (org-clock-out))) |
642 | (when (and (not org-capture-clock-keep) | |
643 | (org-capture-get :clock-resume 'local) | |
86fbb8ca CD |
644 | (markerp (org-capture-get :interrupted-clock 'local)) |
645 | (buffer-live-p (marker-buffer | |
646 | (org-capture-get :interrupted-clock 'local)))) | |
afe98dfa CD |
647 | (let ((clock-in-task (org-capture-get :interrupted-clock 'local))) |
648 | (org-with-point-at clock-in-task | |
649 | (org-clock-in))) | |
86fbb8ca CD |
650 | (message "Interrupted clock has been resumed"))) |
651 | ||
652 | (let ((beg (point-min)) | |
653 | (end (point-max)) | |
654 | (abort-note nil)) | |
3ab2c837 BG |
655 | ;; Store the size of the capture buffer |
656 | (org-capture-put :captured-entry-size (- (point-max) (point-min))) | |
86fbb8ca | 657 | (widen) |
3ab2c837 BG |
658 | ;; Store the insertion point in the target buffer |
659 | (org-capture-put :insertion-point (point)) | |
86fbb8ca CD |
660 | |
661 | (if org-note-abort | |
662 | (let ((m1 (org-capture-get :begin-marker 'local)) | |
663 | (m2 (org-capture-get :end-marker 'local))) | |
664 | (if (and m1 m2 (= m1 beg) (= m2 end)) | |
665 | (progn | |
3ab2c837 BG |
666 | (setq m2 (if (cdr (assoc 'heading org-blank-before-new-entry)) |
667 | m2 (1+ m2)) | |
668 | m2 (if (< (point-max) m2) (point-max) m2)) | |
86fbb8ca CD |
669 | (setq abort-note 'clean) |
670 | (kill-region m1 m2)) | |
671 | (setq abort-note 'dirty))) | |
672 | ||
673 | ;; Make sure that the empty lines after are correct | |
674 | (when (and (> (point-max) end) ; indeed, the buffer was still narrowed | |
675 | (member (org-capture-get :type 'local) | |
676 | '(entry item checkitem plain))) | |
677 | (save-excursion | |
678 | (goto-char end) | |
679 | (or (bolp) (newline)) | |
680 | (org-capture-empty-lines-after | |
8223b1d2 BG |
681 | (or (org-capture-get :empty-lines-after 'local) |
682 | (org-capture-get :empty-lines 'local) 0)))) | |
86fbb8ca | 683 | ;; Postprocessing: Update Statistics cookies, do the sorting |
8223b1d2 | 684 | (when (derived-mode-p 'org-mode) |
86fbb8ca CD |
685 | (save-excursion |
686 | (when (ignore-errors (org-back-to-heading)) | |
687 | (org-update-parent-todo-statistics) | |
688 | (org-update-checkbox-count))) | |
689 | ;; FIXME Here we should do the sorting | |
690 | ;; If we have added a table line, maybe recompute? | |
691 | (when (and (eq (org-capture-get :type 'local) 'table-line) | |
692 | (org-at-table-p)) | |
693 | (if (org-table-get-stored-formulas) | |
694 | (org-table-recalculate 'all) ;; FIXME: Should we iterate??? | |
3ab2c837 | 695 | (org-table-align)))) |
86fbb8ca CD |
696 | ;; Store this place as the last one where we stored something |
697 | ;; Do the marking in the base buffer, so that it makes sense after | |
698 | ;; the indirect buffer has been killed. | |
8223b1d2 BG |
699 | (when org-capture-bookmark |
700 | (org-capture-bookmark-last-stored-position)) | |
86fbb8ca CD |
701 | |
702 | ;; Run the hook | |
3ab2c837 | 703 | (run-hooks 'org-capture-before-finalize-hook)) |
86fbb8ca | 704 | |
8223b1d2 BG |
705 | (when (org-capture-get :decrypted) |
706 | (save-excursion | |
707 | (goto-char (org-capture-get :decrypted)) | |
708 | (org-encrypt-entry))) | |
709 | ||
86fbb8ca CD |
710 | ;; Kill the indirect buffer |
711 | (save-buffer) | |
afe98dfa CD |
712 | (let ((return-wconf (org-capture-get :return-to-wconf 'local)) |
713 | (new-buffer (org-capture-get :new-buffer 'local)) | |
714 | (kill-buffer (org-capture-get :kill-buffer 'local)) | |
715 | (base-buffer (buffer-base-buffer (current-buffer)))) | |
716 | ||
3ab2c837 | 717 | ;; Kill the indirect buffer |
86fbb8ca | 718 | (kill-buffer (current-buffer)) |
afe98dfa | 719 | |
3ab2c837 BG |
720 | ;; Narrow back the target buffer to its previous state |
721 | (with-current-buffer (org-capture-get :buffer) | |
722 | (let ((reg (org-capture-get :initial-target-region)) | |
723 | (pos (org-capture-get :initial-target-position)) | |
724 | (ipt (org-capture-get :insertion-point)) | |
725 | (size (org-capture-get :captured-entry-size))) | |
726 | (when reg | |
727 | (cond ((< ipt (car reg)) | |
728 | ;; insertion point is before the narrowed region | |
729 | (narrow-to-region (+ size (car reg)) (+ size (cdr reg)))) | |
730 | ((> ipt (cdr reg)) | |
731 | ;; insertion point is after the narrowed region | |
732 | (narrow-to-region (car reg) (cdr reg))) | |
733 | (t | |
734 | ;; insertion point is within the narrowed region | |
735 | (narrow-to-region (car reg) (+ size (cdr reg))))) | |
736 | ;; now place back the point at its original position | |
737 | (if (< ipt (car reg)) | |
738 | (goto-char (+ size pos)) | |
739 | (goto-char (if (< ipt pos) (+ size pos) pos)))))) | |
740 | ||
afe98dfa CD |
741 | ;; Kill the target buffer if that is desired |
742 | (when (and base-buffer new-buffer kill-buffer) | |
743 | (with-current-buffer base-buffer (save-buffer)) | |
744 | (kill-buffer base-buffer)) | |
745 | ||
86fbb8ca CD |
746 | ;; Restore the window configuration before capture |
747 | (set-window-configuration return-wconf)) | |
acedf35c CD |
748 | |
749 | (run-hooks 'org-capture-after-finalize-hook) | |
750 | ;; Special cases | |
751 | (cond | |
752 | (abort-note | |
86fbb8ca CD |
753 | (cond |
754 | ((equal abort-note 'clean) | |
afe98dfa | 755 | (message "Capture process aborted and target buffer cleaned up")) |
86fbb8ca | 756 | ((equal abort-note 'dirty) |
acedf35c CD |
757 | (error "Capture process aborted, but target buffer could not be cleaned up correctly")))) |
758 | (stay-with-capture | |
759 | (org-capture-goto-last-stored))) | |
760 | ;; Return if we did store something | |
761 | (not abort-note))) | |
86fbb8ca CD |
762 | |
763 | (defun org-capture-refile () | |
764 | "Finalize the current capture and then refile the entry. | |
765 | Refiling is done from the base buffer, because the indirect buffer is then | |
3ab2c837 | 766 | already gone. Any prefix argument will be passed to the refile command." |
86fbb8ca CD |
767 | (interactive) |
768 | (unless (eq (org-capture-get :type 'local) 'entry) | |
769 | (error | |
770 | "Refiling from a capture buffer makes only sense for `entry'-type templates")) | |
771 | (let ((pos (point)) | |
772 | (base (buffer-base-buffer (current-buffer))) | |
773 | (org-refile-for-capture t)) | |
774 | (org-capture-finalize) | |
775 | (save-window-excursion | |
776 | (with-current-buffer (or base (current-buffer)) | |
777 | (save-excursion | |
778 | (save-restriction | |
779 | (widen) | |
780 | (goto-char pos) | |
781 | (call-interactively 'org-refile))))))) | |
782 | ||
783 | (defun org-capture-kill () | |
784 | "Abort the current capture process." | |
785 | (interactive) | |
8223b1d2 BG |
786 | ;; FIXME: This does not do the right thing, we need to remove the |
787 | ;; new stuff by hand it is easy: undo, then kill the buffer | |
3ab2c837 BG |
788 | (let ((org-note-abort t) |
789 | (org-capture-before-finalize-hook nil)) | |
86fbb8ca CD |
790 | (org-capture-finalize))) |
791 | ||
792 | (defun org-capture-goto-last-stored () | |
3ab2c837 | 793 | "Go to the location where the last capture note was stored." |
86fbb8ca CD |
794 | (interactive) |
795 | (org-goto-marker-or-bmk org-capture-last-stored-marker | |
796 | "org-capture-last-stored") | |
797 | (message "This is the last note stored by a capture process")) | |
798 | ||
799 | ;;; Supporting functions for handling the process | |
800 | ||
3ab2c837 BG |
801 | (defun org-capture-put-target-region-and-position () |
802 | "Store the initial region with `org-capture-put'." | |
803 | (org-capture-put | |
804 | :initial-target-region | |
805 | ;; Check if the buffer is currently narrowed | |
806 | (when (/= (buffer-size) (- (point-max) (point-min))) | |
807 | (cons (point-min) (point-max)))) | |
808 | ;; store the current point | |
809 | (org-capture-put :initial-target-position (point))) | |
810 | ||
8223b1d2 | 811 | (defvar org-time-was-given) ; dynamically scoped parameter |
86fbb8ca | 812 | (defun org-capture-set-target-location (&optional target) |
8223b1d2 BG |
813 | "Find TARGET buffer and position. |
814 | Store them in the capture property list." | |
815 | (let ((target-entry-p t) decrypted-hl-pos) | |
86fbb8ca CD |
816 | (setq target (or target (org-capture-get :target))) |
817 | (save-excursion | |
818 | (cond | |
819 | ((eq (car target) 'file) | |
820 | (set-buffer (org-capture-target-buffer (nth 1 target))) | |
3ab2c837 BG |
821 | (org-capture-put-target-region-and-position) |
822 | (widen) | |
86fbb8ca CD |
823 | (setq target-entry-p nil)) |
824 | ||
825 | ((eq (car target) 'id) | |
826 | (let ((loc (org-id-find (nth 1 target)))) | |
827 | (if (not loc) | |
828 | (error "Cannot find target ID \"%s\"" (nth 1 target)) | |
829 | (set-buffer (org-capture-target-buffer (car loc))) | |
3ab2c837 BG |
830 | (widen) |
831 | (org-capture-put-target-region-and-position) | |
86fbb8ca CD |
832 | (goto-char (cdr loc))))) |
833 | ||
834 | ((eq (car target) 'file+headline) | |
835 | (set-buffer (org-capture-target-buffer (nth 1 target))) | |
3ab2c837 BG |
836 | (org-capture-put-target-region-and-position) |
837 | (widen) | |
86fbb8ca CD |
838 | (let ((hd (nth 2 target))) |
839 | (goto-char (point-min)) | |
8223b1d2 | 840 | (unless (derived-mode-p 'org-mode) |
3ab2c837 BG |
841 | (error |
842 | "Target buffer \"%s\" for file+headline should be in Org mode" | |
843 | (current-buffer))) | |
86fbb8ca CD |
844 | (if (re-search-forward |
845 | (format org-complex-heading-regexp-format (regexp-quote hd)) | |
846 | nil t) | |
847 | (goto-char (point-at-bol)) | |
848 | (goto-char (point-max)) | |
849 | (or (bolp) (insert "\n")) | |
850 | (insert "* " hd "\n") | |
851 | (beginning-of-line 0)))) | |
852 | ||
853 | ((eq (car target) 'file+olp) | |
3ab2c837 BG |
854 | (let ((m (org-find-olp |
855 | (cons (org-capture-expand-file (nth 1 target)) | |
856 | (cddr target))))) | |
86fbb8ca | 857 | (set-buffer (marker-buffer m)) |
3ab2c837 BG |
858 | (org-capture-put-target-region-and-position) |
859 | (widen) | |
86fbb8ca CD |
860 | (goto-char m))) |
861 | ||
862 | ((eq (car target) 'file+regexp) | |
863 | (set-buffer (org-capture-target-buffer (nth 1 target))) | |
3ab2c837 BG |
864 | (org-capture-put-target-region-and-position) |
865 | (widen) | |
86fbb8ca CD |
866 | (goto-char (point-min)) |
867 | (if (re-search-forward (nth 2 target) nil t) | |
868 | (progn | |
869 | (goto-char (if (org-capture-get :prepend) | |
870 | (match-beginning 0) (match-end 0))) | |
871 | (org-capture-put :exact-position (point)) | |
8223b1d2 | 872 | (setq target-entry-p (and (derived-mode-p 'org-mode) (org-at-heading-p)))) |
86fbb8ca CD |
873 | (error "No match for target regexp in file %s" (nth 1 target)))) |
874 | ||
acedf35c | 875 | ((memq (car target) '(file+datetree file+datetree+prompt)) |
86fbb8ca CD |
876 | (require 'org-datetree) |
877 | (set-buffer (org-capture-target-buffer (nth 1 target))) | |
3ab2c837 BG |
878 | (org-capture-put-target-region-and-position) |
879 | (widen) | |
86fbb8ca CD |
880 | ;; Make a date tree entry, with the current date (or yesterday, |
881 | ;; if we are extending dates for a couple of hours) | |
882 | (org-datetree-find-date-create | |
883 | (calendar-gregorian-from-absolute | |
acedf35c | 884 | (cond |
acedf35c CD |
885 | (org-overriding-default-time |
886 | ;; use the overriding default time | |
887 | (time-to-days org-overriding-default-time)) | |
86fbb8ca | 888 | |
acedf35c CD |
889 | ((eq (car target) 'file+datetree+prompt) |
890 | ;; prompt for date | |
3ab2c837 BG |
891 | (let ((prompt-time (org-read-date |
892 | nil t nil "Date for tree entry:" | |
893 | (current-time)))) | |
8223b1d2 BG |
894 | (org-capture-put |
895 | :default-time | |
896 | (cond ((and (not org-time-was-given) | |
897 | (not (= (time-to-days prompt-time) (org-today)))) | |
898 | ;; Use 00:00 when no time is given for another date than today? | |
899 | (apply 'encode-time (append '(0 0 0) (cdddr (decode-time prompt-time))))) | |
900 | ((string-match "\\([^ ]+\\)--?[^ ]+[ ]+\\(.*\\)" org-read-date-final-answer) | |
901 | ;; Replace any time range by its start | |
902 | (apply 'encode-time | |
903 | (org-read-date-analyze | |
904 | (replace-match "\\1 \\2" nil nil org-read-date-final-answer) | |
905 | prompt-time (decode-time prompt-time)))) | |
906 | (t prompt-time))) | |
3ab2c837 | 907 | (time-to-days prompt-time))) |
acedf35c | 908 | (t |
8223b1d2 | 909 | ;; current date, possibly corrected for late night workers |
acedf35c | 910 | (org-today)))))) |
3ab2c837 | 911 | |
86fbb8ca CD |
912 | ((eq (car target) 'file+function) |
913 | (set-buffer (org-capture-target-buffer (nth 1 target))) | |
3ab2c837 BG |
914 | (org-capture-put-target-region-and-position) |
915 | (widen) | |
86fbb8ca CD |
916 | (funcall (nth 2 target)) |
917 | (org-capture-put :exact-position (point)) | |
8223b1d2 | 918 | (setq target-entry-p (and (derived-mode-p 'org-mode) (org-at-heading-p)))) |
86fbb8ca CD |
919 | |
920 | ((eq (car target) 'function) | |
921 | (funcall (nth 1 target)) | |
922 | (org-capture-put :exact-position (point)) | |
8223b1d2 | 923 | (setq target-entry-p (and (derived-mode-p 'org-mode) (org-at-heading-p)))) |
86fbb8ca CD |
924 | |
925 | ((eq (car target) 'clock) | |
926 | (if (and (markerp org-clock-hd-marker) | |
927 | (marker-buffer org-clock-hd-marker)) | |
928 | (progn (set-buffer (marker-buffer org-clock-hd-marker)) | |
3ab2c837 BG |
929 | (org-capture-put-target-region-and-position) |
930 | (widen) | |
86fbb8ca CD |
931 | (goto-char org-clock-hd-marker)) |
932 | (error "No running clock that could be used as capture target"))) | |
933 | ||
934 | (t (error "Invalid capture target specification"))) | |
935 | ||
8223b1d2 BG |
936 | (when (and (featurep 'org-crypt) (org-at-encrypted-entry-p)) |
937 | (org-decrypt-entry) | |
938 | (setq decrypted-hl-pos | |
939 | (save-excursion (and (org-back-to-heading t) (point))))) | |
940 | ||
86fbb8ca | 941 | (org-capture-put :buffer (current-buffer) :pos (point) |
8223b1d2 BG |
942 | :target-entry-p target-entry-p |
943 | :decrypted decrypted-hl-pos)))) | |
86fbb8ca | 944 | |
3ab2c837 BG |
945 | (defun org-capture-expand-file (file) |
946 | "Expand functions and symbols for FILE. | |
947 | When FILE is a function, call it. When it is a form, evaluate | |
948 | it. When it is a variable, retrieve the value. Return whatever we get." | |
949 | (cond | |
950 | ((org-string-nw-p file) file) | |
951 | ((functionp file) (funcall file)) | |
952 | ((and (symbolp file) (boundp file)) (symbol-value file)) | |
953 | ((and file (consp file)) (eval file)) | |
954 | (t file))) | |
955 | ||
86fbb8ca CD |
956 | (defun org-capture-target-buffer (file) |
957 | "Get a buffer for FILE." | |
3ab2c837 | 958 | (setq file (org-capture-expand-file file)) |
afe98dfa CD |
959 | (setq file (or (org-string-nw-p file) |
960 | org-default-notes-file | |
961 | (error "No notes file specified, and no default available"))) | |
86fbb8ca | 962 | (or (org-find-base-buffer-visiting file) |
afe98dfa CD |
963 | (progn (org-capture-put :new-buffer t) |
964 | (find-file-noselect (expand-file-name file org-directory))))) | |
86fbb8ca CD |
965 | |
966 | (defun org-capture-steal-local-variables (buffer) | |
967 | "Install Org-mode local variables." | |
968 | (mapc (lambda (v) | |
969 | (ignore-errors (org-set-local (car v) (cdr v)))) | |
970 | (buffer-local-variables buffer))) | |
971 | ||
972 | (defun org-capture-place-template () | |
973 | "Insert the template at the target location, and display the buffer." | |
974 | (org-capture-put :return-to-wconf (current-window-configuration)) | |
975 | (delete-other-windows) | |
976 | (org-switch-to-buffer-other-window | |
977 | (org-capture-get-indirect-buffer (org-capture-get :buffer) "CAPTURE")) | |
afe98dfa | 978 | (widen) |
86fbb8ca CD |
979 | (show-all) |
980 | (goto-char (org-capture-get :pos)) | |
981 | (org-set-local 'org-capture-target-marker | |
c7cf0ebc | 982 | (point-marker)) |
e66ba1df | 983 | (org-set-local 'outline-level 'org-outline-level) |
86fbb8ca CD |
984 | (let* ((template (org-capture-get :template)) |
985 | (type (org-capture-get :type))) | |
986 | (case type | |
987 | ((nil entry) (org-capture-place-entry)) | |
988 | (table-line (org-capture-place-table-line)) | |
989 | (plain (org-capture-place-plain-text)) | |
afe98dfa CD |
990 | (item (org-capture-place-item)) |
991 | (checkitem (org-capture-place-item)))) | |
86fbb8ca CD |
992 | (org-capture-mode 1) |
993 | (org-set-local 'org-capture-current-plist org-capture-plist)) | |
994 | ||
995 | (defun org-capture-place-entry () | |
996 | "Place the template as a new Org entry." | |
997 | (let* ((txt (org-capture-get :template)) | |
998 | (reversed (org-capture-get :prepend)) | |
999 | (target-entry-p (org-capture-get :target-entry-p)) | |
1000 | level beg end file) | |
1001 | ||
1002 | (cond | |
1003 | ((org-capture-get :exact-position) | |
1004 | (goto-char (org-capture-get :exact-position))) | |
1005 | ((not target-entry-p) | |
1006 | ;; Insert as top-level entry, either at beginning or at end of file | |
1007 | (setq level 1) | |
1008 | (if reversed | |
1009 | (progn (goto-char (point-min)) | |
afe98dfa CD |
1010 | (or (org-at-heading-p) |
1011 | (outline-next-heading))) | |
86fbb8ca CD |
1012 | (goto-char (point-max)) |
1013 | (or (bolp) (insert "\n")))) | |
1014 | (t | |
1015 | ;; Insert as a child of the current entry | |
1016 | (and (looking-at "\\*+") | |
1017 | (setq level (- (match-end 0) (match-beginning 0)))) | |
1018 | (setq level (org-get-valid-level (or level 1) 1)) | |
1019 | (if reversed | |
1020 | (progn | |
1021 | (outline-next-heading) | |
1022 | (or (bolp) (insert "\n"))) | |
8223b1d2 | 1023 | (org-end-of-subtree t nil) |
86fbb8ca CD |
1024 | (or (bolp) (insert "\n"))))) |
1025 | (org-capture-empty-lines-before) | |
1026 | (setq beg (point)) | |
3ab2c837 | 1027 | (org-capture-verify-tree txt) |
86fbb8ca CD |
1028 | (org-paste-subtree level txt 'for-yank) |
1029 | (org-capture-empty-lines-after 1) | |
1030 | (org-capture-position-for-last-stored beg) | |
1031 | (outline-next-heading) | |
1032 | (setq end (point)) | |
1033 | (org-capture-mark-kill-region beg (1- end)) | |
1034 | (org-capture-narrow beg (1- end)) | |
8223b1d2 BG |
1035 | (if (or (re-search-backward "%\\?" beg t) |
1036 | (re-search-forward "%\\?" end t)) | |
1037 | (replace-match "")))) | |
86fbb8ca CD |
1038 | |
1039 | (defun org-capture-place-item () | |
1040 | "Place the template as a new plain list item." | |
1041 | (let* ((txt (org-capture-get :template)) | |
1042 | (target-entry-p (org-capture-get :target-entry-p)) | |
1043 | (ind 0) | |
1044 | beg end) | |
153ae947 BG |
1045 | (if (org-capture-get :exact-position) |
1046 | (goto-char (org-capture-get :exact-position)) | |
1047 | (cond | |
1048 | ((not target-entry-p) | |
1049 | ;; Insert as top-level entry, either at beginning or at end of file | |
1050 | (setq beg (point-min) end (point-max))) | |
1051 | (t | |
1052 | (setq beg (1+ (point-at-eol)) | |
1053 | end (save-excursion (outline-next-heading) (point))))) | |
1054 | (if (org-capture-get :prepend) | |
86fbb8ca | 1055 | (progn |
153ae947 BG |
1056 | (goto-char beg) |
1057 | (if (org-list-search-forward (org-item-beginning-re) end t) | |
1058 | (progn | |
1059 | (goto-char (match-beginning 0)) | |
1060 | (setq ind (org-get-indentation))) | |
1061 | (goto-char end) | |
1062 | (setq ind 0))) | |
1063 | (goto-char end) | |
1064 | (if (org-list-search-backward (org-item-beginning-re) beg t) | |
1065 | (progn | |
1066 | (setq ind (org-get-indentation)) | |
1067 | (org-end-of-item)) | |
1068 | (setq ind 0)))) | |
86fbb8ca CD |
1069 | ;; Remove common indentation |
1070 | (setq txt (org-remove-indentation txt)) | |
1071 | ;; Make sure this is indeed an item | |
1072 | (unless (string-match (concat "\\`" (org-item-re)) txt) | |
1073 | (setq txt (concat "- " | |
1074 | (mapconcat 'identity (split-string txt "\n") | |
1075 | "\n ")))) | |
1076 | ;; Set the correct indentation, depending on context | |
1077 | (setq ind (make-string ind ?\ )) | |
1078 | (setq txt (concat ind | |
1079 | (mapconcat 'identity (split-string txt "\n") | |
1080 | (concat "\n" ind)) | |
1081 | "\n")) | |
1082 | ;; Insert, with surrounding empty lines | |
1083 | (org-capture-empty-lines-before) | |
1084 | (setq beg (point)) | |
1085 | (insert txt) | |
1086 | (or (bolp) (insert "\n")) | |
1087 | (org-capture-empty-lines-after 1) | |
1088 | (org-capture-position-for-last-stored beg) | |
1089 | (forward-char 1) | |
1090 | (setq end (point)) | |
1091 | (org-capture-mark-kill-region beg (1- end)) | |
1092 | (org-capture-narrow beg (1- end)) | |
8223b1d2 BG |
1093 | (if (or (re-search-backward "%\\?" beg t) |
1094 | (re-search-forward "%\\?" end t)) | |
1095 | (replace-match "")))) | |
86fbb8ca CD |
1096 | |
1097 | (defun org-capture-place-table-line () | |
1098 | "Place the template as a table line." | |
1099 | (require 'org-table) | |
1100 | (let* ((txt (org-capture-get :template)) | |
1101 | (target-entry-p (org-capture-get :target-entry-p)) | |
1102 | (table-line-pos (org-capture-get :table-line-pos)) | |
1103 | ind beg end) | |
1104 | (cond | |
1105 | ((org-capture-get :exact-position) | |
1106 | (goto-char (org-capture-get :exact-position))) | |
1107 | ((not target-entry-p) | |
1108 | ;; Table is not necessarily under a heading | |
1109 | (setq beg (point-min) end (point-max))) | |
1110 | (t | |
1111 | ;; WE are at a heading, limit search to the body | |
1112 | (setq beg (1+ (point-at-eol)) | |
1113 | end (save-excursion (outline-next-heading) (point))))) | |
1114 | (if (re-search-forward org-table-dataline-regexp end t) | |
8223b1d2 | 1115 | (let ((b (org-table-begin)) (e (org-table-end)) (case-fold-search t)) |
86fbb8ca | 1116 | (goto-char e) |
8223b1d2 | 1117 | (if (looking-at "[ \t]*#\\+tblfm:") |
86fbb8ca CD |
1118 | (forward-line 1)) |
1119 | (narrow-to-region b (point))) | |
1120 | (goto-char end) | |
1121 | (insert "\n| |\n|----|\n| |\n") | |
1122 | (narrow-to-region (1+ end) (point))) | |
1123 | ;; We are narrowed to the table, or to an empty line if there was no table | |
1124 | ||
1125 | ;; Check if the template is good | |
1126 | (if (not (string-match org-table-dataline-regexp txt)) | |
1127 | (setq txt "| %?Bad template |\n")) | |
1128 | (cond | |
1129 | ((and table-line-pos | |
1130 | (string-match "\\(I+\\)\\([-+][0-9]\\)" table-line-pos)) | |
1131 | ;; we have a complex line specification | |
1132 | (goto-char (point-min)) | |
1133 | (let ((nh (- (match-end 1) (match-beginning 1))) | |
1134 | (delta (string-to-number (match-string 2 table-line-pos))) | |
1135 | ll) | |
1136 | ;; The user wants a special position in the table | |
1137 | (org-table-get-specials) | |
1138 | (setq ll (ignore-errors (aref org-table-hlines nh))) | |
1139 | (unless ll (error "Invalid table line specification \"%s\"" | |
1140 | table-line-pos)) | |
1141 | (setq ll (+ ll delta (if (< delta 0) 0 -1))) | |
1142 | (org-goto-line ll) | |
1143 | (org-table-insert-row 'below) | |
1144 | (beginning-of-line 1) | |
1145 | (delete-region (point) (1+ (point-at-eol))) | |
1146 | (setq beg (point)) | |
1147 | (insert txt) | |
1148 | (setq end (point)))) | |
1149 | ((org-capture-get :prepend) | |
1150 | (goto-char (point-min)) | |
1151 | (re-search-forward org-table-hline-regexp nil t) | |
1152 | (beginning-of-line 1) | |
1153 | (re-search-forward org-table-dataline-regexp nil t) | |
1154 | (beginning-of-line 1) | |
1155 | (setq beg (point)) | |
1156 | (org-table-insert-row) | |
1157 | (beginning-of-line 1) | |
1158 | (delete-region (point) (1+ (point-at-eol))) | |
1159 | (insert txt) | |
1160 | (setq end (point))) | |
1161 | (t | |
1162 | (goto-char (point-max)) | |
1163 | (re-search-backward org-table-dataline-regexp nil t) | |
1164 | (beginning-of-line 1) | |
1165 | (org-table-insert-row 'below) | |
1166 | (beginning-of-line 1) | |
1167 | (delete-region (point) (1+ (point-at-eol))) | |
1168 | (setq beg (point)) | |
1169 | (insert txt) | |
1170 | (setq end (point)))) | |
1171 | (goto-char beg) | |
1172 | (org-capture-position-for-last-stored 'table-line) | |
8223b1d2 BG |
1173 | (if (or (re-search-backward "%\\?" beg t) |
1174 | (re-search-forward "%\\?" end t)) | |
1175 | (replace-match "")) | |
86fbb8ca CD |
1176 | (org-table-align))) |
1177 | ||
1178 | (defun org-capture-place-plain-text () | |
3ab2c837 BG |
1179 | "Place the template plainly. |
1180 | If the target locator points at an Org node, place the template into | |
1181 | the text of the entry, before the first child. If not, place the | |
1182 | template at the beginning or end of the file. | |
1183 | Of course, if exact position has been required, just put it there." | |
86fbb8ca CD |
1184 | (let* ((txt (org-capture-get :template)) |
1185 | beg end) | |
3ab2c837 BG |
1186 | (cond |
1187 | ((org-capture-get :exact-position) | |
1188 | (goto-char (org-capture-get :exact-position))) | |
1189 | ((and (org-capture-get :target-entry-p) | |
1190 | (bolp) | |
1191 | (looking-at org-outline-regexp)) | |
1192 | ;; we should place the text into this entry | |
1193 | (if (org-capture-get :prepend) | |
1194 | ;; Skip meta data and drawers | |
1195 | (org-end-of-meta-data-and-drawers) | |
1196 | ;; go to ent of the entry text, before the next headline | |
1197 | (outline-next-heading))) | |
1198 | (t | |
1199 | ;; beginning or end of file | |
1200 | (goto-char (if (org-capture-get :prepend) (point-min) (point-max))))) | |
86fbb8ca CD |
1201 | (or (bolp) (newline)) |
1202 | (org-capture-empty-lines-before) | |
1203 | (setq beg (point)) | |
1204 | (insert txt) | |
1205 | (org-capture-empty-lines-after 1) | |
1206 | (org-capture-position-for-last-stored beg) | |
1207 | (setq end (point)) | |
1208 | (org-capture-mark-kill-region beg (1- end)) | |
1209 | (org-capture-narrow beg (1- end)) | |
8223b1d2 BG |
1210 | (if (or (re-search-backward "%\\?" beg t) |
1211 | (re-search-forward "%\\?" end t)) | |
1212 | (replace-match "")))) | |
86fbb8ca CD |
1213 | |
1214 | (defun org-capture-mark-kill-region (beg end) | |
1215 | "Mark the region that will have to be killed when aborting capture." | |
1216 | (let ((m1 (move-marker (make-marker) beg)) | |
1217 | (m2 (move-marker (make-marker) end))) | |
1218 | (org-capture-put :begin-marker m1) | |
1219 | (org-capture-put :end-marker m2))) | |
1220 | ||
1221 | (defun org-capture-position-for-last-stored (where) | |
1222 | "Memorize the position that should later become the position of last capture." | |
1223 | (cond | |
1224 | ((integerp where) | |
1225 | (org-capture-put :position-for-last-stored | |
1226 | (move-marker (make-marker) where | |
1227 | (or (buffer-base-buffer (current-buffer)) | |
1228 | (current-buffer))))) | |
1229 | ((eq where 'table-line) | |
1230 | (org-capture-put :position-for-last-stored | |
1231 | (list 'table-line | |
1232 | (org-table-current-dline)))) | |
1233 | (t (error "This should not happen")))) | |
1234 | ||
1235 | (defun org-capture-bookmark-last-stored-position () | |
1236 | "Bookmark the last-captured position." | |
1237 | (let* ((where (org-capture-get :position-for-last-stored 'local)) | |
1238 | (pos (cond | |
1239 | ((markerp where) | |
1240 | (prog1 (marker-position where) | |
1241 | (move-marker where nil))) | |
1242 | ((and (listp where) (eq (car where) 'table-line)) | |
1243 | (if (org-at-table-p) | |
1244 | (save-excursion | |
1245 | (org-table-goto-line (nth 1 where)) | |
1246 | (point-at-bol)) | |
1247 | (point)))))) | |
1248 | (with-current-buffer (buffer-base-buffer (current-buffer)) | |
1249 | (save-excursion | |
1250 | (save-restriction | |
1251 | (widen) | |
1252 | (goto-char pos) | |
bdebdb64 BG |
1253 | (with-demoted-errors |
1254 | (bookmark-set "org-capture-last-stored")) | |
86fbb8ca CD |
1255 | (move-marker org-capture-last-stored-marker (point))))))) |
1256 | ||
1257 | (defun org-capture-narrow (beg end) | |
1258 | "Narrow, unless configuration says not to narrow." | |
1259 | (unless (org-capture-get :unnarrowed) | |
1260 | (narrow-to-region beg end) | |
1261 | (goto-char beg))) | |
1262 | ||
1263 | (defun org-capture-empty-lines-before (&optional n) | |
1264 | "Arrange for the correct number of empty lines before the insertion point. | |
1265 | Point will be after the empty lines, so insertion can directly be done." | |
8223b1d2 BG |
1266 | (setq n (or n (org-capture-get :empty-lines-before) |
1267 | (org-capture-get :empty-lines) 0)) | |
86fbb8ca CD |
1268 | (let ((pos (point))) |
1269 | (org-back-over-empty-lines) | |
1270 | (delete-region (point) pos) | |
afe98dfa | 1271 | (if (> n 0) (newline n)))) |
86fbb8ca CD |
1272 | |
1273 | (defun org-capture-empty-lines-after (&optional n) | |
1274 | "Arrange for the correct number of empty lines after the inserted string. | |
1275 | Point will remain at the first line after the inserted text." | |
8223b1d2 BG |
1276 | (setq n (or n (org-capture-get :empty-lines-after) |
1277 | (org-capture-get :empty-lines) 0)) | |
86fbb8ca CD |
1278 | (org-back-over-empty-lines) |
1279 | (while (looking-at "[ \t]*\n") (replace-match "")) | |
1280 | (let ((pos (point))) | |
afe98dfa | 1281 | (if (> n 0) (newline n)) |
86fbb8ca CD |
1282 | (goto-char pos))) |
1283 | ||
1284 | (defvar org-clock-marker) ; Defined in org.el | |
bdebdb64 | 1285 | |
86fbb8ca CD |
1286 | (defun org-capture-insert-template-here () |
1287 | (let* ((template (org-capture-get :template)) | |
1288 | (type (org-capture-get :type)) | |
1289 | beg end pp) | |
1290 | (or (bolp) (newline)) | |
1291 | (setq beg (point)) | |
1292 | (cond | |
8223b1d2 | 1293 | ((and (eq type 'entry) (derived-mode-p 'org-mode)) |
3ab2c837 | 1294 | (org-capture-verify-tree (org-capture-get :template)) |
86fbb8ca CD |
1295 | (org-paste-subtree nil template t)) |
1296 | ((and (memq type '(item checkitem)) | |
8223b1d2 | 1297 | (derived-mode-p 'org-mode) |
86fbb8ca CD |
1298 | (save-excursion (skip-chars-backward " \t\n") |
1299 | (setq pp (point)) | |
1300 | (org-in-item-p))) | |
1301 | (goto-char pp) | |
1302 | (org-insert-item) | |
1303 | (skip-chars-backward " ") | |
1304 | (skip-chars-backward "-+*0123456789).") | |
1305 | (delete-region (point) (point-at-eol)) | |
1306 | (setq beg (point)) | |
1307 | (org-remove-indentation template) | |
1308 | (insert template) | |
1309 | (org-capture-empty-lines-after) | |
1310 | (goto-char beg) | |
afe98dfa | 1311 | (org-list-repair) |
86fbb8ca CD |
1312 | (org-end-of-item) |
1313 | (setq end (point))) | |
1314 | (t (insert template))) | |
1315 | (setq end (point)) | |
1316 | (goto-char beg) | |
1317 | (if (re-search-forward "%\\?" end t) | |
1318 | (replace-match "")))) | |
1319 | ||
1320 | (defun org-capture-set-plist (entry) | |
1321 | "Initialize the property list from the template definition." | |
1322 | (setq org-capture-plist (copy-sequence (nthcdr 5 entry))) | |
1323 | (org-capture-put :key (car entry) :description (nth 1 entry) | |
1324 | :target (nth 3 entry)) | |
1325 | (let ((txt (nth 4 entry)) (type (or (nth 2 entry) 'entry))) | |
1326 | (when (or (not txt) (and (stringp txt) (not (string-match "\\S-" txt)))) | |
1327 | ;; The template may be empty or omitted for special types. | |
1328 | ;; Here we insert the default templates for such cases. | |
1329 | (cond | |
1330 | ((eq type 'item) (setq txt "- %?")) | |
1331 | ((eq type 'checkitem) (setq txt "- [ ] %?")) | |
1332 | ((eq type 'table-line) (setq txt "| %? |")) | |
1333 | ((member type '(nil entry)) (setq txt "* %?\n %a")))) | |
1334 | (org-capture-put :template txt :type type))) | |
1335 | ||
1336 | (defun org-capture-goto-target (&optional template-key) | |
1337 | "Go to the target location of a capture template. | |
1338 | The user is queried for the template." | |
1339 | (interactive) | |
1340 | (let* (org-select-template-temp-major-mode | |
1341 | (entry (org-capture-select-template template-key))) | |
1342 | (unless entry | |
1343 | (error "No capture template selected")) | |
1344 | (org-capture-set-plist entry) | |
1345 | (org-capture-set-target-location) | |
e66ba1df | 1346 | (org-pop-to-buffer-same-window (org-capture-get :buffer)) |
86fbb8ca CD |
1347 | (goto-char (org-capture-get :pos)))) |
1348 | ||
1349 | (defun org-capture-get-indirect-buffer (&optional buffer prefix) | |
1350 | "Make an indirect buffer for a capture process. | |
1351 | Use PREFIX as a prefix for the name of the indirect buffer." | |
1352 | (setq buffer (or buffer (current-buffer))) | |
1353 | (let ((n 1) (base (buffer-name buffer)) bname) | |
1354 | (setq bname (concat prefix "-" base)) | |
1355 | (while (buffer-live-p (get-buffer bname)) | |
1356 | (setq bname (concat prefix "-" (number-to-string (incf n)) "-" base))) | |
1357 | (condition-case nil | |
1358 | (make-indirect-buffer buffer bname 'clone) | |
e66ba1df BG |
1359 | (error |
1360 | (let ((buf (make-indirect-buffer buffer bname))) | |
1361 | (with-current-buffer buf (org-mode)) | |
1362 | buf))))) | |
86fbb8ca | 1363 | |
3ab2c837 | 1364 | (defun org-capture-verify-tree (tree) |
8223b1d2 | 1365 | "Throw error if TREE is not a valid tree." |
3ab2c837 BG |
1366 | (unless (org-kill-is-subtree-p tree) |
1367 | (error "Template is not a valid Org entry or tree"))) | |
1368 | ||
86fbb8ca CD |
1369 | ;;; The template code |
1370 | ||
1371 | (defun org-capture-select-template (&optional keys) | |
1372 | "Select a capture template. | |
1373 | Lisp programs can force the template by setting KEYS to a string." | |
3ab2c837 | 1374 | (let ((org-capture-templates |
8223b1d2 BG |
1375 | (or (org-contextualize-keys |
1376 | org-capture-templates org-capture-templates-contexts) | |
3ab2c837 BG |
1377 | '(("t" "Task" entry (file+headline "" "Tasks") |
1378 | "* TODO %?\n %u\n %a"))))) | |
1379 | (if keys | |
1380 | (or (assoc keys org-capture-templates) | |
1381 | (error "No capture template referred to by \"%s\" keys" keys)) | |
1382 | (org-mks org-capture-templates | |
1383 | "Select a capture template\n=========================" | |
1384 | "Template key: " | |
1385 | '(("C" "Customize org-capture-templates") | |
1386 | ("q" "Abort")))))) | |
86fbb8ca CD |
1387 | |
1388 | (defun org-capture-fill-template (&optional template initial annotation) | |
1389 | "Fill a template and return the filled template as a string. | |
1390 | The template may still contain \"%?\" for cursor positioning." | |
1391 | (setq template (or template (org-capture-get :template))) | |
1392 | (when (stringp initial) | |
8223b1d2 | 1393 | (setq initial (org-no-properties initial))) |
86fbb8ca CD |
1394 | (let* ((buffer (org-capture-get :buffer)) |
1395 | (file (buffer-file-name (or (buffer-base-buffer buffer) buffer))) | |
1396 | (ct (org-capture-get :default-time)) | |
1397 | (dct (decode-time ct)) | |
1398 | (ct1 | |
1399 | (if (< (nth 2 dct) org-extend-today-until) | |
1400 | (encode-time 0 59 23 (1- (nth 3 dct)) (nth 4 dct) (nth 5 dct)) | |
1401 | ct)) | |
1402 | (plist-p (if org-store-link-plist t nil)) | |
1403 | (v-c (and (> (length kill-ring) 0) (current-kill 0))) | |
1404 | (v-x (or (org-get-x-clipboard 'PRIMARY) | |
1405 | (org-get-x-clipboard 'CLIPBOARD) | |
1406 | (org-get-x-clipboard 'SECONDARY))) | |
1407 | (v-t (format-time-string (car org-time-stamp-formats) ct)) | |
1408 | (v-T (format-time-string (cdr org-time-stamp-formats) ct)) | |
1409 | (v-u (concat "[" (substring v-t 1 -1) "]")) | |
1410 | (v-U (concat "[" (substring v-T 1 -1) "]")) | |
1411 | ;; `initial' and `annotation' might habe been passed. | |
1412 | ;; But if the property list has them, we prefer those values | |
1413 | (v-i (or (plist-get org-store-link-plist :initial) | |
1414 | initial | |
1415 | (org-capture-get :initial) | |
1416 | "")) | |
1417 | (v-a (or (plist-get org-store-link-plist :annotation) | |
1418 | annotation | |
1419 | (org-capture-get :annotation) | |
1420 | "")) | |
1421 | ;; Is the link empty? Then we do not want it... | |
1422 | (v-a (if (equal v-a "[[]]") "" v-a)) | |
1423 | (clipboards (remove nil (list v-i | |
1424 | (org-get-x-clipboard 'PRIMARY) | |
1425 | (org-get-x-clipboard 'CLIPBOARD) | |
1426 | (org-get-x-clipboard 'SECONDARY) | |
1427 | v-c))) | |
8223b1d2 BG |
1428 | (l-re "\\[\\[\\(.*?\\)\\]\\(\\[.*?\\]\\)?\\]") |
1429 | (v-A (if (and v-a (string-match l-re v-a)) | |
1430 | (replace-match "[[\\1][%^{Link description}]]" nil nil v-a) | |
1431 | v-a)) | |
1432 | (v-l (if (and v-a (string-match l-re v-a)) | |
1433 | (replace-match "\\1" nil nil v-a) | |
86fbb8ca CD |
1434 | v-a)) |
1435 | (v-n user-full-name) | |
1436 | (v-k (if (marker-buffer org-clock-marker) | |
8223b1d2 | 1437 | (org-no-properties org-clock-heading))) |
86fbb8ca CD |
1438 | (v-K (if (marker-buffer org-clock-marker) |
1439 | (org-make-link-string | |
1440 | (buffer-file-name (marker-buffer org-clock-marker)) | |
1441 | org-clock-heading))) | |
3ab2c837 BG |
1442 | (v-f (or (org-capture-get :original-file-nondirectory) "")) |
1443 | (v-F (or (org-capture-get :original-file) "")) | |
86fbb8ca CD |
1444 | v-I |
1445 | (org-startup-folded nil) | |
1446 | (org-inhibit-startup t) | |
1447 | org-time-was-given org-end-time-was-given x | |
8223b1d2 | 1448 | prompt completions char time pos default histvar strings) |
86fbb8ca CD |
1449 | |
1450 | (setq org-store-link-plist | |
1451 | (plist-put org-store-link-plist :annotation v-a) | |
1452 | org-store-link-plist | |
1453 | (plist-put org-store-link-plist :initial v-i)) | |
afe98dfa | 1454 | (setq initial v-i) |
86fbb8ca CD |
1455 | |
1456 | (unless template (setq template "") (message "No template") (ding) | |
1457 | (sit-for 1)) | |
1458 | (save-window-excursion | |
1459 | (delete-other-windows) | |
e66ba1df | 1460 | (org-pop-to-buffer-same-window (get-buffer-create "*Capture*")) |
86fbb8ca CD |
1461 | (erase-buffer) |
1462 | (insert template) | |
1463 | (goto-char (point-min)) | |
1464 | (org-capture-steal-local-variables buffer) | |
1465 | (setq buffer-file-name nil) | |
1466 | ||
1467 | ;; %[] Insert contents of a file. | |
1468 | (goto-char (point-min)) | |
1469 | (while (re-search-forward "%\\[\\(.+\\)\\]" nil t) | |
1470 | (unless (org-capture-escaped-%) | |
1471 | (let ((start (match-beginning 0)) | |
1472 | (end (match-end 0)) | |
1473 | (filename (expand-file-name (match-string 1)))) | |
1474 | (goto-char start) | |
1475 | (delete-region start end) | |
1476 | (condition-case error | |
1477 | (insert-file-contents filename) | |
1478 | (error (insert (format "%%![Couldn't insert %s: %s]" | |
1479 | filename error))))))) | |
1480 | ;; %() embedded elisp | |
8223b1d2 | 1481 | (org-capture-expand-embedded-elisp) |
86fbb8ca | 1482 | |
3ab2c837 BG |
1483 | ;; The current time |
1484 | (goto-char (point-min)) | |
1485 | (while (re-search-forward "%<\\([^>\n]+\\)>" nil t) | |
1486 | (replace-match (format-time-string (match-string 1)) t t)) | |
1487 | ||
86fbb8ca | 1488 | ;; Simple %-escapes |
afe98dfa | 1489 | (goto-char (point-min)) |
8223b1d2 | 1490 | (while (re-search-forward "%\\([tTuUaliAcxkKInfF]\\)" nil t) |
86fbb8ca CD |
1491 | (unless (org-capture-escaped-%) |
1492 | (when (and initial (equal (match-string 0) "%i")) | |
1493 | (save-match-data | |
1494 | (let* ((lead (buffer-substring | |
1495 | (point-at-bol) (match-beginning 0)))) | |
1496 | (setq v-i (mapconcat 'identity | |
1497 | (org-split-string initial "\n") | |
1498 | (concat "\n" lead)))))) | |
1499 | (replace-match | |
8223b1d2 BG |
1500 | (or (org-add-props (eval (intern (concat "v-" (match-string 1)))) |
1501 | '(org-protected t)) "") | |
86fbb8ca CD |
1502 | t t))) |
1503 | ||
1504 | ;; From the property list | |
1505 | (when plist-p | |
1506 | (goto-char (point-min)) | |
1507 | (while (re-search-forward "%\\(:[-a-zA-Z]+\\)" nil t) | |
1508 | (unless (org-capture-escaped-%) | |
1509 | (and (setq x (or (plist-get org-store-link-plist | |
1510 | (intern (match-string 1))) "")) | |
1511 | (replace-match x t t))))) | |
1512 | ||
1513 | ;; Turn on org-mode in temp buffer, set local variables | |
1514 | ;; This is to support completion in interactive prompts | |
1515 | (let ((org-inhibit-startup t)) (org-mode)) | |
1516 | ;; Interactive template entries | |
1517 | (goto-char (point-min)) | |
8223b1d2 BG |
1518 | (while (and (re-search-forward "%^\\({\\([^}]*\\)}\\)?\\([gGtTuUCLp]\\)?" nil t) |
1519 | (not (get-text-property (1- (point)) 'org-protected))) | |
86fbb8ca | 1520 | (unless (org-capture-escaped-%) |
3ab2c837 BG |
1521 | (setq char (if (match-end 3) (match-string-no-properties 3)) |
1522 | prompt (if (match-end 2) (match-string-no-properties 2))) | |
86fbb8ca CD |
1523 | (goto-char (match-beginning 0)) |
1524 | (replace-match "") | |
1525 | (setq completions nil default nil) | |
1526 | (when prompt | |
1527 | (setq completions (org-split-string prompt "|") | |
1528 | prompt (pop completions) | |
1529 | default (car completions) | |
1530 | histvar (intern (concat | |
1531 | "org-capture-template-prompt-history::" | |
1532 | (or prompt ""))) | |
1533 | completions (mapcar 'list completions))) | |
afe98dfa | 1534 | (unless (boundp histvar) (set histvar nil)) |
86fbb8ca CD |
1535 | (cond |
1536 | ((member char '("G" "g")) | |
1537 | (let* ((org-last-tags-completion-table | |
1538 | (org-global-tags-completion-table | |
1539 | (if (equal char "G") | |
1540 | (org-agenda-files) | |
1541 | (and file (list file))))) | |
1542 | (org-add-colon-after-tag-completion t) | |
1543 | (ins (org-icompleting-read | |
1544 | (if prompt (concat prompt ": ") "Tags: ") | |
1545 | 'org-tags-completion-function nil nil nil | |
1546 | 'org-tags-history))) | |
1547 | (setq ins (mapconcat 'identity | |
1548 | (org-split-string | |
afe98dfa | 1549 | ins (org-re "[^[:alnum:]_@#%]+")) |
8223b1d2 | 1550 | ":")) |
86fbb8ca CD |
1551 | (when (string-match "\\S-" ins) |
1552 | (or (equal (char-before) ?:) (insert ":")) | |
1553 | (insert ins) | |
afe98dfa | 1554 | (or (equal (char-after) ?:) (insert ":")) |
e66ba1df | 1555 | (and (org-at-heading-p) (org-set-tags nil 'align))))) |
86fbb8ca CD |
1556 | ((equal char "C") |
1557 | (cond ((= (length clipboards) 1) (insert (car clipboards))) | |
1558 | ((> (length clipboards) 1) | |
1559 | (insert (read-string "Clipboard/kill value: " | |
1560 | (car clipboards) '(clipboards . 1) | |
1561 | (car clipboards)))))) | |
1562 | ((equal char "L") | |
1563 | (cond ((= (length clipboards) 1) | |
1564 | (org-insert-link 0 (car clipboards))) | |
1565 | ((> (length clipboards) 1) | |
1566 | (org-insert-link 0 (read-string "Clipboard/kill value: " | |
1567 | (car clipboards) | |
1568 | '(clipboards . 1) | |
1569 | (car clipboards)))))) | |
1570 | ((equal char "p") | |
8223b1d2 | 1571 | (org-set-property (org-no-properties prompt) nil)) |
86fbb8ca CD |
1572 | (char |
1573 | ;; These are the date/time related ones | |
1574 | (setq org-time-was-given (equal (upcase char) char)) | |
1575 | (setq time (org-read-date (equal (upcase char) char) t nil | |
1576 | prompt)) | |
1577 | (if (equal (upcase char) char) (setq org-time-was-given t)) | |
1578 | (org-insert-time-stamp time org-time-was-given | |
1579 | (member char '("u" "U")) | |
1580 | nil nil (list org-end-time-was-given))) | |
1581 | (t | |
1582 | (let (org-completion-use-ido) | |
8223b1d2 BG |
1583 | (push (org-completing-read-no-i |
1584 | (concat (if prompt prompt "Enter string") | |
1585 | (if default (concat " [" default "]")) | |
1586 | ": ") | |
1587 | completions nil nil nil histvar default) | |
1588 | strings) | |
1589 | (insert (car strings))))))) | |
1590 | ;; Replace %n escapes with nth %^{...} string | |
1591 | (setq strings (nreverse strings)) | |
1592 | (goto-char (point-min)) | |
1593 | (while (re-search-forward "%\\\\\\([1-9][0-9]*\\)" nil t) | |
1594 | (unless (org-capture-escaped-%) | |
1595 | (replace-match | |
1596 | (nth (1- (string-to-number (match-string 1))) strings) | |
1597 | nil t))) | |
86fbb8ca CD |
1598 | ;; Make sure there are no empty lines before the text, and that |
1599 | ;; it ends with a newline character | |
1600 | (goto-char (point-min)) | |
1601 | (while (looking-at "[ \t]*\n") (replace-match "")) | |
1602 | (if (re-search-forward "[ \t\n]*\\'" nil t) (replace-match "\n")) | |
27e428e7 | 1603 | ;; Return the expanded template and kill the temporary buffer |
86fbb8ca CD |
1604 | (untabify (point-min) (point-max)) |
1605 | (set-buffer-modified-p nil) | |
1606 | (prog1 (buffer-string) (kill-buffer (current-buffer)))))) | |
1607 | ||
1608 | (defun org-capture-escaped-% () | |
1609 | "Check if % was escaped - if yes, unescape it now." | |
1610 | (if (equal (char-before (match-beginning 0)) ?\\) | |
1611 | (progn | |
1612 | (delete-region (1- (match-beginning 0)) (match-beginning 0)) | |
1613 | t) | |
1614 | nil)) | |
1615 | ||
8223b1d2 BG |
1616 | (defun org-capture-expand-embedded-elisp () |
1617 | "Evaluate embedded elisp %(sexp) and replace with the result." | |
1618 | (goto-char (point-min)) | |
1619 | (while (re-search-forward "%(" nil t) | |
1620 | (unless (org-capture-escaped-%) | |
1621 | (goto-char (match-beginning 0)) | |
1622 | (let ((template-start (point))) | |
1623 | (forward-char 1) | |
1624 | (let ((result (org-eval (read (current-buffer))))) | |
1625 | (delete-region template-start (point)) | |
1626 | (insert result)))))) | |
1627 | ||
1628 | (defun org-capture-inside-embedded-elisp-p () | |
1629 | "Return non-nil if point is inside of embedded elisp %(sexp)." | |
1630 | (let (beg end) | |
1631 | (with-syntax-table emacs-lisp-mode-syntax-table | |
1632 | (save-excursion | |
1633 | ;; `looking-at' and `search-backward' below do not match the "%(" if | |
1634 | ;; point is in its middle | |
1635 | (when (equal (char-before) ?%) | |
1636 | (backward-char)) | |
1637 | (save-match-data | |
1638 | (when (or (looking-at "%(") (search-backward "%(" nil t)) | |
1639 | (setq beg (point)) | |
1640 | (setq end (progn (forward-char) (forward-sexp) (1- (point))))))) | |
1641 | (when (and beg end) | |
1642 | (and (<= (point) end) (>= (point) beg)))))) | |
1643 | ||
86fbb8ca CD |
1644 | ;;;###autoload |
1645 | (defun org-capture-import-remember-templates () | |
1646 | "Set org-capture-templates to be similar to `org-remember-templates'." | |
1647 | (interactive) | |
1648 | (when (and (yes-or-no-p | |
1649 | "Import old remember templates into org-capture-templates? ") | |
1650 | (yes-or-no-p | |
1651 | "Note that this will remove any templates currently defined in `org-capture-templates'. Do you still want to go ahead? ")) | |
1652 | (require 'org-remember) | |
1653 | (setq org-capture-templates | |
1654 | (mapcar | |
1655 | (lambda (entry) | |
1656 | (let ((desc (car entry)) | |
1657 | (key (char-to-string (nth 1 entry))) | |
1658 | (template (nth 2 entry)) | |
1659 | (file (or (nth 3 entry) org-default-notes-file)) | |
1660 | (position (or (nth 4 entry) org-remember-default-headline)) | |
1661 | (type 'entry) | |
1662 | (prepend org-reverse-note-order) | |
1663 | immediate target) | |
1664 | (cond | |
1665 | ((member position '(top bottom)) | |
1666 | (setq target (list 'file file) | |
1667 | prepend (eq position 'top))) | |
1668 | ((eq position 'date-tree) | |
1669 | (setq target (list 'file+datetree file) | |
1670 | prepend nil)) | |
1671 | (t (setq target (list 'file+headline file position)))) | |
1672 | ||
1673 | (when (string-match "%!" template) | |
1674 | (setq template (replace-match "" t t template) | |
1675 | immediate t)) | |
1676 | ||
1677 | (append (list key desc type target template) | |
1678 | (if prepend '(:prepend t)) | |
1679 | (if immediate '(:immediate-finish t))))) | |
1680 | ||
1681 | org-remember-templates)))) | |
1682 | ||
1683 | (provide 'org-capture) | |
1684 | ||
86fbb8ca | 1685 | ;;; org-capture.el ends here |