-
Notifications
You must be signed in to change notification settings - Fork 24
/
org-mac-protocol.el
executable file
·386 lines (327 loc) · 11.8 KB
/
org-mac-protocol.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
;;; org-mac-protocol.el -- process events triggered by the
;;; org-mac-protocol suite of AppleScripts
;; Copyright (C) 2009, 2010 Christopher Suckling
;; Authors: Christopher Suckling <suckling at gmail dot com>
;; This file is Free Software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 3, or (at your option)
;; any later version.
;; It is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
;; License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
;; Version: 0.634
;; Keywords: outlines, hyperlinks, wp
;;; Commentary
;;
;; This file provides org-protocol handlers and various hacks to
;; produce a nicely centered, pop-up remember frame for links passed
;; to emacsclient by the org-mac-protocol suite of AppleScripts. It
;; also defines new org-mode hyperlink types for opening files in
;; various OS X applications.
;;; Installation and Usage
;;
;; Please see org-mac-protocol.org
;;; Credit
;;
;; Portions of this code are developed from a blog post by Jack Moffitt:
;; http://metajack.im/2008/12/30/gtd-capture-with-emacs-orgmode/
(require 'org-protocol)
(require 'org-bibtex)
;;; Define org-mode hyperlink types
;;
;; BibDesk
(org-add-link-type "bibdesk" 'org-mac-bibdesk-open)
(defun org-mac-bibdesk-open (uri)
"Visit bibtex entry in BibDesk"
(let* ((key (when (string-match "::\\(.+\\)\\'" uri)
(match-string 1 uri)))
(document (substring uri 0 (match-beginning 0))))
(do-applescript
(concat
"tell application \"BibDesk.app\"\n"
"activate\n"
"set theDoc to \"" document "\"\n"
"set theKey to \"" key "\"\n"
"open theDoc\n"
"tell document 1 of application \"BibDesk\"\n"
"set somePubs to (publications whose cite key contains theKey)\n"
"set the selection to somePubs\n"
"set theSelection to the selection\n"
"set thePub to get item 1 of theSelection\n"
"show thePub\n"
"end tell\n"
"end tell"))))
;; Skim
(org-add-link-type "skim" 'org-mac-skim-open)
(defun org-mac-skim-open (uri)
"Visit page of pdf in Skim"
(let* ((page (when (string-match "::\\(.+\\)\\'" uri)
(match-string 1 uri)))
(document (substring uri 0 (match-beginning 0))))
(do-applescript
(concat
"tell application \"Skim\"\n"
"activate\n"
"set theDoc to \"" document "\"\n"
"set thePage to " page "\n"
"open theDoc\n"
"go document 1 to page thePage of document 1\n"
"end tell"))))
;; Pages
(org-add-link-type "pages" 'org-mac-pages-open)
(defun org-mac-pages-open (uri)
"Visit page of document in Pages. Reliant on character offset
from start of document, so link may degrade if document is
edited"
(let* ((charoff (when (string-match "::\\(.+\\)\\'" uri)
(match-string 1 uri)))
(document (substring uri 0 (match-beginning 0))))
(do-applescript
(concat
"tell application \"Pages\"\n"
"activate\n"
"set theDoc to \"" document "\"\n"
"set theCharOff to " charoff "\n"
"open theDoc\n"
"select character theCharOff of document 1\n"
"end tell"))))
;; Numbers
(org-add-link-type "numbers" 'org-mac-numbers-open)
(defun org-mac-numbers-open (uri)
"Visit range of spreadsheet in Numbers. Although the correct
cells will be selected, it may be necessary to change sheets
manually"
(let* ((sheet (when (string-match "::\\(.+\\)::\\(.+\\)::\\(.+\\)\\'" uri)
(match-string 1 uri)))
(table (when (string-match "::\\(.+\\)::\\(.+\\)::\\(.+\\)\\'" uri)
(org-protocol-unhex-string (match-string 2 uri))))
(range (when (string-match "::\\(.+\\)::\\(.+\\)::\\(.+\\)\\'" uri)
(match-string 3 uri)))
(document (substring uri 0 (match-beginning 0))))
(do-applescript
(concat
"tell application \"Numbers\"\n"
"activate\n"
"set theDoc to \"" document "\"\n"
"set theSheet to \"" sheet "\"\n"
"set theTable to \"" table "\"\n"
"set theRange to \"" range "\"\n"
"open theDoc\n"
"tell document 1\n"
"tell sheet (theSheet as integer)\n"
"tell table theTable\n"
"set selection range to range theRange\n"
"end tell\n"
"end tell\n"
"end tell\n"
"end tell"))))
;; Keynote
(org-add-link-type "keynote" 'org-mac-keynote-open)
(defun org-mac-keynote-open (uri)
"Visit slide in Keynote"
(let* ((slide (when (string-match "::\\(.+\\)\\'" uri)
(match-string 1 uri)))
(document (substring uri 0 (match-beginning 0))))
(do-applescript
(concat
"tell application \"Keynote\"\n"
"activate\n"
"set theDoc to \"" document "\"\n"
"set theSlide to " slide "\n"
"open theDoc\n"
"show slide theSlide of slideshow 1\n"
"end tell"))))
;; iTunes
(org-add-link-type "iTunes" 'org-mac-iTunes-open)
(defun org-mac-iTunes-open (uri)
"Visit track in iTunes"
(let* ((track (when (string-match "[0-9a-zA-Z]+\\'" uri)
(match-string 0 uri))))
(do-applescript
(concat
"tell application \"iTunes\"\n"
"activate\n"
"set theTrack to \"" track "\"\n"
"set view of front browser window to user playlist \"Music\" of source \"Library\"\n"
"play (some track of playlist \"Music\" of source \"Library\" whose persistent ID is theTrack)\n"
"playpause\n"
"end tell"
))))
;; Address Book
;; Support based on code by Alexander Poslavsky
(org-add-link-type "address" 'org-mac-addressbook-open)
(defun org-mac-addressbook-open (uri)
"Applescript to open correct address-book entry"
(do-applescript
(concat
"set theID to \"" uri "\"\n"
"tell application \"System Events\"\n"
"activate\n"
"try\n"
"open location \"addressbook\:\/\/\" \& theID\n"
"on error\n"
"display dialog \"no matching contact found\"\n"
"end try\n"
"end tell"
)))
;;; Variables and advice for managing *remember* frames
;; updated for org-capture hooks
(defvar width-of-display (x-display-pixel-width)
"For some reason, (x-display-pixel-width) returns corrupted
values when called during (org-mac-protocol-remember); this
ensures that correct value is returned")
(setq width-of-display (x-display-pixel-width))
(defvar height-of-display (x-display-pixel-height)
"For some reason, (x-display-pixel-height) returns corrupted
values when called during (org-mac-protocol-remember); this
ensures that correct value is returned")
(setq height-of-display (x-display-pixel-height))
(defvar org-mac-protocol-app nil
"The application that AppleScript will activate
after the *remember* buffer is killed")
(defadvice remember-finalize (after delete-remember-frame activate)
"Advise remember-finalize to close the frame if it is the mac-remember frame"
(when (equal "*mac-remember*" (frame-parameter nil 'name))
(mapc
(lambda (x)
(when (not (equal (frame-parameter x 'name) "*mac-remember*"))
(make-frame-visible x)))
(frame-list))
(mapc
(lambda (x)
(when (equal (frame-parameter x 'name) "*mac-remember*")
(delete-frame x)))
(frame-list))
(do-applescript
(concat
"tell application \"" org-mac-protocol-app "\"\n"
"activate\n"
"end tell"))))
(defadvice remember-destroy (after delete-remember-frame activate)
"Advise remember-destroy to close the frame if it is the
mac-rememeber frame"
(when (equal "*mac-remember*" (frame-parameter nil 'name))
(mapc
(lambda (x)
(when (not (equal (frame-parameter x 'name) "*mac-remember*"))
(make-frame-visible x)))
(frame-list))
(mapc
(lambda (x)
(when (equal (frame-parameter x 'name) "*mac-remember*")
(delete-frame x)))
(frame-list))
(do-applescript
(concat
"tell application \"" org-mac-protocol-app "\"\n"
"activate\n"
"end tell"))))
(defadvice org-remember-apply-template (before delete-non-remember-windows activate)
"Advise org-remember to delete non-remember windows if it is in
the remember frame"
(when (equal "*mac-remember*" (frame-parameter nil 'name))
(delete-other-windows)))
(add-hook 'org-capture-mode-hook
'(lambda ()
(when (equal "*mac-remember*" (frame-parameter nil 'name))
(delete-other-windows))))
(add-hook 'org-capture-after-finalize-hook
'(lambda ()
(when (equal "*mac-remember*" (frame-parameter nil 'name))
(mapc
(lambda (x)
(when (not (equal (frame-parameter x 'name) "*mac-remember*"))
(make-frame-visible x)))
(frame-list))
(mapc
(lambda (x)
(when (equal (frame-parameter x 'name) "*mac-remember*")
(delete-frame x)))
(frame-list))
(do-applescript
(concat
"tell application \"" org-mac-protocol-app "\"\n"
"activate\n"
"end tell")))))
;;; Define org-protocol protocols
(add-to-list 'org-protocol-protocol-alist
'("org-mac-remember"
:protocol "mac-remember"
:function org-mac-protocol-remember
:kill-client t))
(add-to-list 'org-protocol-protocol-alist
'("org-mac-safari-tabs"
:protocol "safari-tabs"
:function org-mac-safari-tabs
:kill-client t))
(defun org-mac-protocol-remember (data)
"Process an org-protocol://mac-remember:// scheme URL.
Pop up a small, centred Emacs frame containing a remember
buffer. Destroys the frame after the remember buffer is filed and
return to originating application. This code is
essentially (org-protocol-remember) with an additional :shortdesc
link property"
(let* ((elements (org-protocol-split-data data nil ":"))
(info (car elements))
(app (org-protocol-unhex-string (cadr elements)))
(parts (org-protocol-split-data info t))
(template (or (and (= 1 (length (car parts))) (pop parts))
org-protocol-default-template-key))
(url (org-protocol-sanitize-uri (car parts)))
(type (if (string-match "^\\([a-z]+\\):" url)
(match-string 1 url)))
(title (or (cadr parts) ""))
(shorttitle (or (caddr parts) ""))
(region (or (cadddr parts) ""))
(orglink (org-make-link-string
url (if (string-match "[^[:space:]]" title) title url)))
(org-capture-link-is-already-stored t)
remember-annotation-functions)
(setq org-mac-protocol-app app)
(do-applescript
(concat
"tell application \"Emacs\"\n"
"activate\n"
"end tell"))
(make-frame '((name . "*mac-remember*") (width . 80) (height . 20)))
(mapc (lambda (x)
(if (not (equal (frame-parameter x 'name) "*mac-remember*"))
(make-frame-invisible x)
(let ((center-left
(/ (- width-of-display (frame-pixel-width)) 2))
(center-top
(/ (- (- height-of-display 22) (frame-pixel-height)) 2)))
(set-frame-parameter nil 'top center-top)
(set-frame-parameter nil 'left center-left))))
(frame-list))
(select-frame-by-name "*mac-remember*")
(setq org-stored-links
(cons (list url title) org-stored-links))
(kill-new orglink)
(org-store-link-props :type type
:link url
:description title
:shortdesc shorttitle
:initial region)
(message "protocol: %s" (plist-get org-store-link-plist :shortdesc))
(if (boundp 'org-capture-templates)
(org-capture nil template)
(org-remember nil (string-to-char template))))
nil)
(defun org-mac-safari-tabs (data)
"Process an org-protocol://safari-tabs:// scheme URL.
Inserts a formatted list of hyperlinks to the tabs open in the
front Safari window"
(let* ((elements (org-protocol-split-data data nil "::"))
;; discard the last element, as it is blank
(links (butlast elements)))
(mapc (lambda (x)
(org-protocol-store-link x))
links))
nil)
(provide 'org-mac-protocol)