Opening a file using ScriptingBridge - objective-c

I have an AppleScript that I am trying to convert to ScriptingBridge. Since my application is a C++/Obj-C application, ScriptingBridge is much easier to use and quite a bit faster (not to mention I hate dynamically building AppleScripts).
The AppleScript sends a message to Photoshop to open a file. The file parameter is sent as an alias, but ScriptingBridge imports the parameter as an id. I don't know what Obj-C object I should pass in?
I've tried passing an NSURL and an NSString (probably incorrectly :-P), but to no avail. Any suggestions on what I should be passing for the file alias?

The short answer is that you can't open documents in Photoshop with Scripting Bridge.
Apple's docs really spell it out like it is. All classes must have a container, which is a mutable array, that they need to be added to before they can be acted upon, as shown in the generated header...
#interface photoshopCS4Application : SBApplication
- (SBElementArray *) documents;
- (SBElementArray *) fonts;
- (SBElementArray *) notifiers;
... and that is the complete list of top-level containers available to us. The open command requires a photoshopCS4OpenOptions to be generated and populated. Because the API doesn't expose the array to store the newly created photoshopCS4OpenOptions object, we can't use a newly created photoshopCS4OpenOptions object. Therefore we can't make a target document and by extensions can't use the open command in Scripting Bridge. The same can be said of all the commands that require some kind of options object.
The only workaround that I have sorted out is to either open a document with native Applescript called from Cocoa or objc-appscript, and then parse the documents array looking for the one just opened. It's not ideal, but then neither is Scripting Bridge because it requires application developers write their scripting APIs in a very specific way that is not native to the OSA framework.

If your program is such that opening a Photoshop document can be executed outside your AppleScript script/Scripting Bridge code, Cocoa provides a method to open files with a specific application:
[[NSWorkspace sharedWorkspace] openFile:#"/Users/bavarious/Desktop/test.psd" withApplication:#"Adobe Photoshop CS4"];
or, if you want to use the default application that handles that file type, you can drop the application name altogether:
[[NSWorkspace sharedWorkspace] openFile:#"/Users/bavarious/Desktop/test.psd"];

Consider Appscript. http://appscript.sourceforge.net/
Here's the code using that:
APApplication *adobePhotoshopCs4 = [APApplication applicationWithName: #"Adobe Photoshop CS4"];
id result = [[adobePhotoshopCs4 open_] send];
(Note, I'm not a Cocoa programmer - I mainly use Appscript with Python but Appscript comes with ASTranslate which translates Applescript into Python, Ruby or Obj-C and that's the output - but I've found there are subtle mistakes in the past sometimes with the translator)

Related

In Obj-C, how to programmatically set the default "open with" property of a file in Mac OS X

In creating some .mov files using Cocoa (Obj-C), I'd like to set them to be opened by default by a specific program, instead of the default. This should be a file level property, I do not wish to change the default program for all files with the same extension. This is to be done from Cocoa itself, as opposed to manually in "context menu">>"Get Info">>"Open With".
There's an undocumented function call that sets this:
// undocumented function call
extern OSStatus _LSSetStrongBindingForRef(const FSRef *inItemRef,
FSRef *inAppRefOrNil);
*If you use this in your application and submit it to the AppStore it will probably get rejected.
As an intermediate between doing it by hand and doing it from Cocoa, there is an Automator action called "Set Application for Files".
I don't think there is a supported way to do it programmatically, but some people have figured out what Finder is doing: Adding a resource of type 'usro' that contains a full path to the application. See for example this discussion. Note: the Resource Manager is deprecated as of 10.8.

Printing without an NSView

Currently I'm writing an app for OSX which will eventually need to be ported to iOS.
The data that needs to be printed is being drawn via CoreGraphics into a PDF context - that is working perfectly.
I've been reading the Apple dev documentation on printing in both iOS and OSX, and, ironically, it actually seems printing from iOS will be easier.
On iOS, UIPrintInteractionController's printingItem property can take an NSData object containing PDF data and print that. Looks like it should be fairly straight-forward.
OSX on the other hand, (looks like it) requires using the NSPrintOperation class - but it seems the only way to get data into an instance is via an NSView. (+printOperationWithView: or +printOperationWithView:printInfo:).
Seeing as the content is formatted and paginated already it seems rather pointless to have to re-draw the PDF data to something like an NSView.
Could there possibly be another way of achieving this that I've missed?
This code is by no means complete, but for anyone who comes across this later, this is basically how you can print directly from an NSData stream:
#define kMimeType #"application/pdf"
#define kPaperType #"A4"
- (void)printData:(NSData *)incomingPrintData {
CFArrayRef printerList; //will soon be an array of PMPrinter objects
PMServerCreatePrinterList(kPMServerLocal, &printerList);
PMPrinter myPrinter;
//iterate over printerList and determine which one you want, assign to myPrinter
PMPrintSession printSession;
PMPrintSettings printSettings;
PMCreateSession(&printSession);
PMCreatePrintSettings(&printSettings);
PMSessionDefaultPrintSettings(printSession, printSettings);
CFArrayRef paperList;
PMPrinterGetPaperList(myPrinter, &paperList);
PMPaper usingPaper;
//iterate over paperList and to set usingPaper to the paper desired
PMPageFormat pageFormat;
PMCreatePageFormatWithPMPaper(&pageFormat, usingPaper);
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((CFDataRef)incomingPrintData);
PMPrinterPrintWithProvider(myPrinter, printSettings, pageFormat, (CFStringRef)kMimeType, dataProvider);
}
(via Core Printing Reference)
Beware this code lacks memory management so you will need to use the PMRetain() and PMRelease() functions as well as the CoreFoundation memory-management functions as well.
If anyone can tell me how I can get data from the OSX print dialogue into data I can use in this method I'll accept their answer instead of this. That is, without using Carbon functions.

Get Path to selected file in Finder

How would I retrieve an array of paths to the selected files in Finder?
I have searched around but have only found links regarding AppleScript. I have also looked at NSWorkspace and NSFileManager but I didn't find anything.
Expanding on #Bavarious's (correct) answer, here's how I've gotten the selection from Finder using the Scripting Bridge:
#import "Finder.h" //my copy is here: https://github.com/davedelong/BetterInfo/blob/master/Finder.h
FinderApplication * finder = [SBApplication applicationWithBundleIdentifier:#"com.apple.finder"];
SBElementArray * selection = [[finder selection] get];
NSArray * items = [selection arrayByApplyingSelector:#selector(URL)];
for (NSString * item in items) {
NSURL * url = [NSURL URLWithString:item];
NSLog(#"selected item url: %#", url);
}
If it is possible to get the list of selected files in a given Finder window using AppleScript, you can probably use Scripting Bridge in a Cocoa application to interface with Finder. Quoting Apple’s documentation,
Scripting Bridge is a framework and a technology that makes it much easier for Cocoa developers to control and communicate with scriptable applications. Instead of incorporating AppleScript scripts in your application or dealing with the complexities of sending and handling Apple events, you can simply send Objective-C messages to an object that represents an application with a scripting interface. Your Cocoa application can do anything an AppleScript script can, but it does so in Objective-C code that is integrated with the rest of your project’s code.
There is no Cocoa class that represents Finder or, more specifically, Finder windows. Finder is an application, and a scriptable application at that, so Scripting Bridge is the way to go.

Objective C: Register file extension

I am creating an app which needs to be opened if a user double clicks on a file with a certain extension.. How do i register the file extension with my app? and then read the contents?.
E.G the file could have the extension words.ThisApp and it could be in XML Format.. how could I read that in objective c into an array?
I think you should read the Document-Based Applications Overview.
To register an extension to your application, bring up the Target info window (Project » Edit Current Target "My Target"... at the bottom) and open the "Properties" tab. Fill in the blanks for your document type there. For more info, read Storing document type informations in the Application's Property List, contained inside the above guide.
To read XML data, consider using a NSXMLParser (google it for examples) to drive the results into a NSMutableArray as you see fit; and to get the data into your application, consider using a NSDocument subclass, as suggested (again) in the document-based application overview.
As you might understand, this document is quite a vital read.

Add movie to iTunes using Scripting Bridge

I want to use Scripting Bridge to add a movie to iTunes. And preferably letting me choose between a 'music video' and a 'movie'. I know both Objective-C and AppleScript so I thought it wouldn't be that hard but I can't figure it out. I know how I would use NSAppleScript for it but I'm targeting 10.5 or later and read that Scripting Bridge obsoletes NSAppleScript. Is that right?
All I got is
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier: #"com.apple.iTunes"];
Which is, as you can see, not much at all.
Step 1. Generate iTunes.h header file:
sdef /Applications/iTunes.app | sdp -fh --basename "iTunes"
Step 2. The code to add a media file looks like the following:
NSString* sourceMediaFile = ...;
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
iTunesTrack * track = [iTunes add:[NSArray arrayWithObject:[NSURL fileURLWithPath:sourceMediaFile]] to:nil];
NSLog(#"Added %# to track: %#",sourceMediaFile,track);
You should use the "scripting definition processor" (sdp) program to generate a header file from iTunes' scripting definition (.sdef) file (which you can get using the sdef program):
sdef /Applications/iTunes.app | sdp -fh --basename "iTunes"
This'll give you a file called iTunes.h. Then you include that header into your project and read through it to see what the iTunes scripting bridge interface offers.
If it seems like you won't be able to do this with the scripting bridge (it's possible -- not everything that can be done via an app's AppleScript interface can also be done via the scripting bridge), just go ahead and write an AppleScript to do it instead, and then execute that in your program with NSAppleScript.
For the second parameter, it takes a playlist object (or nil as previously mentioned). Once you have fetched an instance of a iTunesPlaylist* object through some means (there are several depending on your needs), you can pass it as the second parameter.