1. 2011-01-29: Opening a Finder's window from Impromptu (alas how to use the applescript bridge..)
Imagine you've got a bunch of audio samples you want to load up while livecoding with Impromptu but you can't remember exactly their names - it'd be handy to be able to open up the corresponding Finder window directly from scheme, without too much clicking around. Do-able or not?
I spent some time trying to figure this out, and the answer is yes! Quite a nice learning experience... although it came with a surprise at the end.
Originally I thought, let's do it via impromptu's ObjC bridge. I don't know much about ObjC but after a bit of googling it seemed evident that the quickest way to accomplish this is by writing ObjC code that, in turns, runs a simple applescript command that opens a window:
[NSString](http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/) \*s \= \[[NSString](http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/) stringWithFormat:
@"tell application "Terminal" to do script "cd %@"", folderPath];
[NSAppleScript](http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSAppleScript_Class/) \*as \= \[\[[NSAppleScript](http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSAppleScript_Class/) alloc\] initWithSource: s\];
[as executeAndReturnError:nil];
So I translated the bottom part of the code above into impromptu/scheme:
(define run_applescript
(lambda (script)
(objc:call (objc:call (objc:call "NSAppleScript" "alloc")
"initWithSource:"
script)
"executeAndReturnError:" )))
That is a generic script-running function, that is, you can pass any script and it'll run it, eg:
(define script0 "
tell application "Finder" to open folder "Macintosh HD:Users"
tell application "Finder" to activate")
(define script1 "
tell application "Terminal" to do script "cd ~; open ."")
(define script2 "
tell application "System Events"n
tell dock preferencesn
set properties to {autohide:false}n
end telln
end tell")
;; finally, let's choose randomly one of the scripts above and run it
(run_applescript (random '(script0 script1 script2)))
Now, back to the original problem: in order to open a Finder's window at a specified location we need to pass the location variable to our function run_applescript; also, we want to be able to pass unix path expressions (eg '/Users/mike/music/'), so we've got to transform that path syntax into the semicolon-delimited macintosh syntax ("Macintosh HD:Users:mike:music") needed by the applescript function we're using. That's easily done with string-replace, so here we go:
(define open_finder_at
(lambda (location)
(let* ((llocation (string-replace location "/" ":"))
(script (string-append "tell application "Finder" to activate open folder "Macintosh HD" llocation """)))
(objc:call (objc:call (objc:call "NSAppleScript" "alloc")
"initWithSource:"
script)
"executeAndReturnError:" ))))
(open_finder_at "/Users/me/")
That's pretty much it really... now we can easily open OsX Finder's windows from within Impromptu.
But as I said above, there's a surprise: after getting this far I thought I'd search impromptu's mailing list for more examples of obj:call .... and guess what, there's already a system function that runs applescripts, it's called sys:run-applescript !
Too bad, it's been a rewarding learning experience anyways...