#include <Foundation/Foundation.h> #include <AppKit/AppKit.h> #include <Renaissance/Renaissance.h> /* Dummy function pointer to get it working on Windows. */ int (*linkRenaissanceIn)(int, const char **) = GSMarkupApplicationMain; int main (int argc, const char **argv, char** env) { CREATE_AUTORELEASE_POOL (pool); [NSApplication sharedApplication]; [NSApp setDelegate: [MyApplicationDelegate new]]; /* Load the menu before calling NSApplicationMain(), because on * Apple Mac OS X NSApplicationMain() creates automatically a menu * if none is there, and when we try to replace it later, it doesn't * really get replaced ... (?) * * After extensive experiments, loading the menu at this stage is the best * way of having it work on both platforms. */ #ifdef GNUSTEP [NSBundle loadGSMarkupNamed: @"MainMenu-GNUstep" owner: [NSApp delegate]]; #else [NSBundle loadGSMarkupNamed: @"MainMenu-OSX" owner: [NSApp delegate]]; #endif RELEASE (pool); return NSApplicationMain (argc, argv); }Please note that in the example we have two separate gsmarkup files for the menu on the different platforms. While it can be clumsy to do so, it's certainly the way which works best at the moment - unless you know what you are doing, it's recommended that you do it this way (and that you check/use default template examples of main menu gsmarkup files). Finally, we set an instance of an hypothetic MyApplicationDelegate as the application delegate. That is only an example, but implementing an application delegate custom class can be useful for loading the main window gsmarkup if you need so, as explained in the next section.
In practice, you can implement your own application delegate class, and have it implement the
- (void)applicationDidFinishLaunching: (NSNotification *)aNotification;method. This method will be called when the application has finished launching; you can load the main window gsmarkup from there. For example:
@interface MyApplicationDelegate : NSObject { /* ... */ } - (void)applicationDidFinishLaunching: (NSNotification *)aNotification; @end @implementation MyApplicationDelegate - (void)applicationDidFinishLaunching: (NSNotification *)aNotification { [NSBundle loadGSMarkupNamed: @"MainWindow" owner: self]; } @endOf course, in your main function, you need to set an instance of MyApplicationDelegate as the application delegate (as demonstrated in the previous section). Please note that this is just a very simple example: depending on how you are organizing the code in your application, you might be loading the MainWindow from a different object, or with a different owner - as a classical variant, in -applicationDidFinishLaunching: you could be creating a controller object, and that object might be loading the gsmarkup file during initialization.
Renaissance includes full examples of applications demonstrating how to do all this - the first example you should look at is probably the CurrencyConverter example -
/Examples/Applications/CurrencyConverter
/* ... code here ... */ [NSBundle loadGSMarkupNamed: @"HighScores" owner: self]; /* ... more code here ... */that would load the HighScores.gsmarkup file from the main bundle, create the window(s) from the file, using self as the owner (assuming this method call is done inside a method implementation). The owner used when loading is quite important, because instance variables of the owner can be set to point to objects in the window (by using outlets in the gsmarkup file), and vice versa objects in the window can have some of their instance variables (/attributes) set to point to the file owner, so it is particularly natural to use as owner the object which will be interacting more closely and directly with the window while the program is running.
So, if you want to use NSWindowController with Renaissance, instead of using NSWindowController, you just need to use GSMarkupWindowController; the API is precisely the same. You can subclass a GSMarkupWindowController in the same way as you subclass a NSWindowController.
It's worth making an example of a gsmarkup file which can be loaded by a GSMarkupWindowController:
<?xml version="1.0"?> <!DOCTYPE gsmarkup> <gsmarkup> <objects> <window id="window"> <!-- ... your code here ... --> </window> </objects> <connectors> <outlet source="#NSOwner" target="#window" key="window" /> </connectors> </gsmarkup>Please note the outlet which sets the window outlet of the NSOwner to the window objects in your file (in practice, it calls [#NSOwner setWindow: #window]) - it's essential that you have this outlet in your file, or it won't work. It's the same outlet that is required in a nib/gorm file which is meant to be loaded by a NSWindowController.
Please refer to the NSWindowController documentation for more information on using window controllers.
So, if you want to use Renaissance with NSDocument, instead of using NSDocument, you just need to use GSMarkupDocument; the API is precisely the same. You can subclass a GSMarkupDocument in the same way as you subclass a NSDocument.
In a typical document-based application, you add entries to the application Info.plist describing the type of files/documents that your application can manage (TODO: make examples); you then load your main menu at startup (as explained in section 2.6.2). Actions in the File menu will typically be about creating, opening, saving, printing document. You should probably start with a standard document menu copied from a template.
Finally, you implement a subclass of GSMarkupDocument able to read/write those data; you override windowNibName to return the name of the gsmarkup file (without the .gsmarkup extension) to use to create the window which the user can use to edit the data. Make sure the gsmarkup file sets the window outlet of the NSOwner to point to the window (as described/exemplified in the example code in the previous section).
Please refer to the documentation on NSDocument for more information; the Renaissance distribution provides complete examples of document-based application built using Renaissace which can be a useful starting point - for example
Examples/Applications/Ink Examples/Applications/SimpleEditor