Search code examples
objective-capplescriptscripting-bridge

How to perform equivalent of AppleScript "copy" command from Scripting Bridge?


Is there a way to do a remote copy within a container from Scripting Bridge? In AppleScript, this looks like "copy [element] of [container] to [location in container]". For example, in the context of a tell to a TextEdit document, you can "copy first paragraph of its text to end of its text" to copy the first paragraph to the end of the document while preserving all formatting.

Is there any way to do with from Objective-C using Scripting Bridge?

I tried something like this:

SBObject* foo = [container objectAtIndex: 0];
[container addObject: foo];

And got a message "can't add an object that already exists." on the console; it appears that addObject and the various replace* methods only work when you're building a new SBObject instance and inserting it.


Solution

  • When the second parameter to AppleScript's built-in copy command is an application reference, AppleScript 'helpfully' aliases to an application duplicate or set command, depending on whether the first parameter is another reference or not. To avoid confusion, it's best to use duplicate or set explicitly when dealing with scriptable apps in AppleScript.

    As for SB, you would need to look for a duplicate... method in the header file. Be aware that SB's duplicate command is semi-crippled (SB has quite a few shortcomings) since it can only deal with a single object at a time. Most applications will allow you to manipulate multiple objects in a single command (caveat any bugs in their scripting support), e.g. AppleScript will let you say:

    tell application "iTunes"
        duplicate (every track whose artist is "Foo") to (playlist "Bar")
    end tell
    

    but SB won't; instead you have to extract a list of single references and iterate over those, processing them one at a time. Makes your code rather longwinded, and can be very inefficient if you've a large number of objects elsewhere.

    Another option is objc-appscript, which does all this stuff correctly and is much less prone to application compatibility issues in general ('quirk-for-quirk compatibility', as Matt Neuburg puts it). Plus you get ASTranslate, which converts AppleScript commands to the equivalent Python/Ruby/ObjC syntax - very handy when figuring out how to phrase a command correctly:

    #import "ITGlue/ITGlue.h"
    ITApplication *itunes = [ITApplication applicationWithName: @"iTunes"];
    ITReference *ref = [[itunes tracks] byTest: [[ITIts artist] equals: @"Foo"]];
    ITDuplicateCommand *cmd = [[ref duplicate] to: [[itunes playlists] byName: @"Bar"]];
    id result = [cmd send];
    

    Yet another option, if you're on 10.6, would be to use the AppleScriptObjC bridge, which allows you to combine AS and ObjC in the same program without any of that nasty NSAppleScript nonsense. That would allow you to use AppleScript for what it's best at (communicating with other apps) and ObjC for everything else. Official documentation is limited, but a web-search should throw up various third-party resources. MacScripter.net might be a good place to start - in addition to the ASOC forum, Craig Williams posted a series of tutorials a while back.

    (BTW, Craig also contributed a chapter about ASOC to the third edition of Apress's Learn AppleScript, which I co-wrote, and which includes lots of information on application scripting principles and practices, including clarification of set/duplicate/copy.)