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