views:

889

answers:

4

Cocoa used to work on CS3 with the trick of putting a Cocoa bundle inside the main Carbon plugin bundle, loading it from Carbon and issuing a NSApplicationLoad(). That's because Photoshop CS3 was Carbon-only and used to unload the plugin bundles.

Photoshop CS4 uses Cocoa and has its own NSAutorelease pool in place on the main thread.

On Photoshop CS4 very simple window-based xibs/nibs loaded by a NSWindowController work out of the box.

But just add a binding to a control on the window and you'll get funny crashes, optionally when you close the window, or the second time you use the plugin, or even when closing Photoshop itself.

Why everything seem to work well until I use some advanced Cocoa features? I'm stuck.

EDIT: I've really found myself the solution to the broader problem "How to use Cocoa in a Photoshop CS3/CS4 plugin?". See below.

A: 

I just started working on writing a Cocoa-based plugin for CS4. Really, there is almost no information out there on this topic, and I've been figuring it out as I go.

  • Start from this Apple example, and make sure you download the whole project, because there are a few little details missing from the text:

Carbon/Cocoa

  • Pick one of the Photoshop SDK examples (I used ColorMunger), and keep it simple to start, so just try to replace the "About" dialog box, using the Apple example as your template. Once you have that working with no memory issues, you should be on your way.

I've been a Java and Ruby programmer for 10 years, so my C/C++ foo is rusty, and I'm just learning Objective C as I go. Two "gotchas" I ran into, just in case....

  • do NOT create a controller object in your NIB/XIB file. Because, based on that Apple example, the controller opens up the NIB file in it's init method, and you get a really interesting recursive loop
  • The Apple example is embedding the Cocoa stuff in a Carbon based C app. The Adobe examples are all C++. Don't forget your extern "C" {} in your header file.
wndxlori
+1  A: 

You have to create a new Loadable Bundle target that contains your nibs and your Cocoa code. Add the bundle product to the Copy Bundle Resources phase of your plugin. Then the code for a filter plugin that loads a Cocoa window with some controls would be:

Boolean DoUI (void) {

    // Create the CF Cocoa bundle
    CFBundleRef pluginBundle;
    CFURLRef cocoaBundleURL;
    pluginBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.example.plugin"));
    cocoaBundleURL = CFBundleCopyResourceURL(pluginBundle, 
                                             CFSTR("Cocoa_bundle"), 
                                             CFSTR("bundle"), 
                                             NULL);
    CFBundleRef cocoaBundleRef;
    cocoaBundleRef = CFBundleCreate(kCFAllocatorDefault, cocoaBundleURL);
    CFRelease(cocoaBundleURL);

    // start Cocoa (for CS3)
    NSApplicationLoad(); 

    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

    // load the cocoa bundle by identifier
    NSBundle* cocoaBundle;
    cocoaBundle = [NSBundle bundleWithIdentifier:@"com.example.plugin.cocoa"];

    // load the window controller from the bundle
    Class testControllerClass;
    testControllerClass = [cocoaBundle classNamed:@"MyWindowController"];

    MyWindowController* winController = [[testControllerClass alloc] init];
    [NSApp runModalForWindow:[winController window]];
    [[winController window] performClose:nil];
    [winController release];

    // release the bundle
    CFRelease(cocoaBundleRef);

    [pool release];

    return 1;
}

This is based on the Craig Hockenberry bundle trick. I'm still testing it but it should work both on CS3 and CS4.

IlDan
@IlDan - have you been able to get cocoa bindings to work within your plugin? See my stack overflow question about cocoa in carbon crashing. I'm having trouble w/ that and any inside would be appreciated.
Jess Bowers