Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Branch: master
Fetching contributors…

Cannot retrieve contributors at this time

executable file 409 lines (348 sloc) 12.776 kB
;;; 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 'cl)
(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 ()
(unless (boundp 'org-refile-for-capture)
(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"))))))
(add-hook 'org-after-refile-insert-hook
'(lambda ()
(when (boundp 'org-refile-for-capture)
(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)
Jump to Line
Something went wrong with that request. Please try again.