# 3 - Menus We are now going to add a menu to our tutorial application. 3.1 Creating a Menu ----------------------------------------------------------------- An `NSMenu` object represents a menu. You create a new menu simply with: NSMenu *menu; menu = AUTORELEASE ([NSMenu new]); This is an empty menu. To add items to it, you do as follows: [menu addItemWithTitle: @"Quit" action: @selector (terminate:) keyEquivalent: @"q"]; This adds to the `menu` menu an entry with title `Quit` (the title is the string which is displayed to the user). It's interesting to know that the method addItemWithTitle:action:keyEquivalent: actually returns a `NSMenuItem` object, which is the menu item object which was added to the menu. We don't need this return value in this example, so we discard it. Later on, we will need it. ------------------------------------------------------------------------ **Subsections** - 3.1.1 Action and Target ------------------------------------------------------------------------ ### 3.1.1 Action and Target A menu item behaves as most controls in the GNUstep gui library: it has an *action* and a *target*. The typical example of such a control is a button. Say, for example, that you have created a button called `myButton`. You want something to happen when the user clicks on the button - to do it, you need to specify an *action* and a *target* for the button. For example, if you want the method `terminate:` of `NSApp` (the application shared object) to be invoked when the user clicks the button, you do as follows: NSButton *myButton; id myObject; // [myButton setAction: @selector (terminate:)]; [myButton setTarget: NSApp]; When the user presses `myButton`, the gui library sends the `terminate:` message to the object `NSApp`, passing as argument `myButton`, executing the equivalent of the following code: [NSApp terminate: myButton]; This way, when the user clicks the button, the application quits. The button which was clicked is passed as an argument to the *action* so that the receiving object can determine (if needed) which button was actually pressed, so that the same method may be used for more than one button. In our case, `NSApp` simply discards the argument of `terminate:`. In brief, the *action* is the method to be invoked when the user clicks on the button; the *target* is the object on which to invoke the method. *action* should be a selector for a method returning `void` and taking a single argument (of type `id`, a generic object). The button which was clicked is passed as an argument to the invocation of *action* on the *target*. The case of a menu item is completely similar. When the user selects this menu item, the gui library performs the menu item *action* (`terminate:` in this case) on the menu item *target*. The argument passed to `terminate:` is the menu item which was selected. Usually, no target is specified for a menu item, as in our code [menu addItemWithTitle: @"Quit" action: @selector (terminate:) keyEquivalent: @"q"]; which only specifies that the action is `terminate:`. When no target is specified, the gui library tries to determine an appropriate target dynamically at run-time. For now the only important thing to know is that the gui library will try to send the action to certain objects (roughly, the objects inside a window which have the input focus) and, failing these objects, it will try to send the action to `NSApp`, and as a last resort to `NSApp`'s delegate. In our example, we want the action `terminate:` to be sent to `NSApp` (thus terminating the application when the user selects the `Quit` menu item), so this automatic mechanism works just fine for us (actually, you will soon discover that this automatic mechanism works fine extremely often). So, we don't need to set explicitly a target for our menu item. ------------------------------------------------------------------------ 3.2 Setting the Application Menu ------------------------------------------------------------------------------ Once you have created an `NSMenu` called, say, `myMenu`, to set it as the application menu you just invoke: [NSApp setMainMenu: myMenu]; ------------------------------------------------------------------------ 3.3 Our First Application ----------------------------------------------------------------------- We are ready now to create our first gui application: #include #include @interface MyDelegate : NSObject - (void) applicationWillFinishLaunching: (NSNotification *)not; @end @implementation MyDelegate : NSObject - (void) applicationWillFinishLaunching: (NSNotification *)not { NSMenu *menu; menu = AUTORELEASE ([NSMenu new]); [menu addItemWithTitle: @"Quit" action: @selector (terminate:) keyEquivalent: @"q"]; [NSApp setMainMenu: menu]; } @end int main (int argc, const char **argv) { [NSApplication sharedApplication]; [NSApp setDelegate: [MyDelegate new]]; return NSApplicationMain (argc, argv); } Put this code for example into a file called `MyApp.m`, and use the following `GNUmakefile` for it: include $(GNUSTEP_MAKEFILES)/common.make APP_NAME = MyFirstApp MyFirstApp_OBJC_FILES = MyApp.m # Uncomment the following if you have an icon #MyFirstApp_APPLICATION_ICON = MyApp.png #MyFirstApp_RESOURCE_FILES = MyApp.png include $(GNUSTEP_MAKEFILES)/application.make Compile it, then run it using `openapp ./MyFirstApplication.app` (see the GNUmakefile mini-tutorial for further information on writing GNUmakefiles). ------------------------------------------------------------------------ 3.4 Fun with NSMenuItem's target ------------------------------------------------------------------------------ The next step in our tutorial is to add to the menu an item which prints `Hello!` to the user when the user selects the item. So, we add a new menu item to our menu, invoking the `printHello:` action (which we'll implement in our custom object): [menu addItemWithTitle: @"Print Hello" action: @selector (printHello:) keyEquivalent: @"h"]; Since our custom object is the application's delegate, we don't need to set explicitly the target: the library can determine it at run-time. In other cases it could be necessary to set a different target, as in: NSMenuItem *menuItem; id myObject; // menuItem = [menu addItemWithTitle: @"Print Hello" action: @selector (printHello:) keyEquivalent: @"h"]; [menuItem setTarget: myObject]; But in this case, we don't need to set the target explicitly, and the code is simply: #include #include @interface MyDelegate : NSObject - (void) printHello: (id)sender; - (void) applicationWillFinishLaunching: (NSNotification *)not; @end @implementation MyDelegate : NSObject - (void) printHello: (id)sender { printf ("Hello!\n"); } - (void) applicationWillFinishLaunching: (NSNotification *)not { NSMenu *menu; menu = AUTORELEASE ([NSMenu new]); [menu addItemWithTitle: @"Print Hello" action: @selector (printHello:) keyEquivalent: @""]; [menu addItemWithTitle: @"Quit" action: @selector (terminate:) keyEquivalent: @"q"]; [NSApp setMainMenu: menu]; } @end int main (int argc, const char **argv) { [NSApplication sharedApplication]; [NSApp setDelegate: [MyDelegate new]]; return NSApplicationMain (argc, argv); } The `GNUmakefile` is the same. I hope you appreciate how easy and simple is coding in GNUstep; I encourage you to try it out and enjoy all the fun you of selecting the `Print Hello` menu item and see the program print out `Hello!`. ------------------------------------------------------------------------ 3.5 Creating sub-menus -------------------------------------------------------------------- Creating sub-menus is quite easy. For example, to add an `Info...` sub-menu to your main application menu, you first create a menu representing it: NSMenu *infoMenu; infoMenu = AUTORELEASE ([NSMenu new]); [infoMenu addItemWithTitle: @"Info Panel..." action: @selector (orderFrontStandardInfoPanel:) keyEquivalent: @""]; [infoMenu addItemWithTitle: @"Help..." action: @selector (orderFrontHelpPanel:) keyEquivalent: @"?"]; Then, you create an item in the main menu for your info menu, but instead of setting an action and a target for that item, you set the `infoMenu` as the sub-menu corresponding to that item: NSMenuItem *menuItem; menuItem = [menu addItemWithTitle: @"Info..." action: NULL keyEquivalent: @""]; [menu setSubmenu: infoMenu forItem: menuItem]; ------------------------------------------------------------------------ 3.6 Creating a standard info panel -------------------------------------------------------------------------------- You may have noticed that in our previous example with the `Info...` sub-menu, we have used `orderFrontStandardInfoPanel:` as the action for the `Info` menu entry. The `Info` menu entry is usually supposed to display an \`\`Info Panel'' (also called \`\`About Panel'' on Microsoft Windows), with the title of the program, the version, the author, the copyright info. In this case, we use `orderFrontStandardInfoPanel:`, which is implemented by `NSApplication` (it is a GNUstep extension), and which displays a standard info panel. The information on what to display in the panel is taken from the `Info-gnustep.plist` file in the application's main bundle. This file is created automatically for you at compile time by the GNUstep make system; but you can insert your own entries in this file as follows. If your application name is, for example, `MyFirstApp`, then you need to create a file called `MyFirstAppInfo.plist` in your source directory (you do not need to add anything to your `GNUmakefile`; the make system looks for this file automatically). Here is an example of such a file: { ApplicationName = "My First Application"; ApplicationDescription = "An Example of how to use NSMenu"; ApplicationRelease = "0.1"; Authors = ("Your Name Here ", "Another Name Here "); Copyright = "Copyright (c) 2008 Your Name Here "; CopyrightDescription = "This program is released under the GNU GPL"; } You should of course edit this example filling in with the information appropriate for your own app. The file is in a format called \`\`property list'' (that is why it has extension `plist`; see the *Basic Foundation Classes* GNUstep Mini Tutorial for more information on property lists). The entries should be self-explanatory; note that `Authors` should be equal to an array of names. If you want to omit some of the entries, you may safely do it. Here is the full listing of our latest app source code, containing an info submenu able to display the Info Panel: #include #include @interface MyDelegate : NSObject - (void) printHello: (id)sender; - (void) applicationWillFinishLaunching: (NSNotification *)not; @end @implementation MyDelegate : NSObject - (void) printHello: (id)sender { printf ("Hello!\n"); } - (void) applicationWillFinishLaunching: (NSNotification *)not { NSMenu *menu; NSMenu *infoMenu; NSMenuItem *menuItem; menu = AUTORELEASE ([NSMenu new]); infoMenu = AUTORELEASE ([NSMenu new]); [infoMenu addItemWithTitle: @"Info Panel..." action: @selector (orderFrontStandardInfoPanel:) keyEquivalent: @""]; [infoMenu addItemWithTitle: @"Help..." action: @selector (orderFrontHelpPanel:) keyEquivalent: @"?"]; menuItem = [menu addItemWithTitle: @"Info..." action: NULL keyEquivalent: @""]; [menu setSubmenu: infoMenu forItem: menuItem]; [menu addItemWithTitle: @"Print Hello" action: @selector (printHello:) keyEquivalent: @""]; [menu addItemWithTitle: @"Quit" action: @selector (terminate:) keyEquivalent: @"q"]; [NSApp setMainMenu: menu]; } @end int main (int argc, const char **argv) { [NSApplication sharedApplication]; [NSApp setDelegate: [MyDelegate new]]; return NSApplicationMain (argc, argv); } ------------------------------------------------------------------------