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