Menu Bar, Menus, & MenuItems

Now we will examine menu bars, menus, and menu items. Any window can have only one menu bar. Each menu bar is composed of one or more menus and menu items. These menu are placed horizontally accross the menu bar. Each menu in turn opens up into one or more menus and menu items.

This time we will create a basic notepad type window that can be found in MenuSample.cpp. Lets look at the main function.

00017:   ///////////////////////////////////////////////////////////////////////////////
00018:   ///Main function
00019:   WPARAM main()
00020:   {
00021:     Window win( "Menu Bar Test" );          //Create the menu bar
00022:     me = new MultilineEdit( 0, 0, 0, 0 );   //Create the edit window
00023:     
00024:     MenuBar mbar( &win );                   //Create the menu bar
00025:     createMenu( mbar );                     //Add the menu items
00026:   
00027:     win.setMenuBar( &mbar );                //Set the menu bar
00028:     win.add( me );                          //Add the edit window
00029:   
00030:     win.show();                             //Show the window
00031:   
00032:     //Set the edit window to the size of the window
00033:     me->setSize( win.getWidth(), win.getHeight() );
00034:   
00035:     //Start processing
00036:     WPARAM param = win.beginMessageLoop();
00037:   
00038:     //Delete the edit window
00039:     delete me;
00040:   
00041:     return param;
00042:   }

The window reference passed to the menu bar constructor is for the window that will recieve the events. This is handy because it allows you to add the same menu bar to more than one window. As you can see by line 27 you must tell the window what menu bar to display. When the window is created it will create the menu bar passed to setMenuBar(). The menu bar in turn will create the sub-menus, and the sub-menus will create the menu items. This means that menus bars that are unattached to a window at its create time must be created with a call to its create() method. The same holds true for menus and menu items that are unattached to either a menu bar, a popup menu, or another menu.

Lets look at the creteMenu() funcion to see how menu bars get their elements.

00044:   ///////////////////////////////////////////////////////////////////////////////
00045:   ///Create the menu for the menu bar
00046:   void createMenu( MenuBar& mbar )
00047:   {
00048:     Menu *menu = new Menu( "&File" );       //Create sub-menu called File
00049:     
00050:     MenuItem* mi = new MenuItem( "&New" );  //Create a menu item called New
00051:     mi->addMenuCallBack( onNew );           //Set its callback
00052:     menu->append( mi );                     //Append to the sub-menu
00053:   
00054:     mi = new MenuItem( "&Save" );           //Create a menu item called Save
00055:     mi->addMenuCallBack( onSave );          //Set its callback
00056:     menu->append( mi );                     //Append to the sub-menu
00057:   
00058:     mi = new MenuItem( "E&xit" );           //Create a menu item called Exit
00059:     mi->addMenuCallBack( onExit );          //Set its callback
00060:     menu->append( mi );                     //Append it to the sub-menu
00061:   
00062:     mbar.append( menu );                    //Append the sub-menu to the menu bar
00063:   }

Menus and menu items take only one parameter in their constructor, a label that appears when the menu or menu item is shown. Currently only the append method can be used to add menus and menu items. You are probably wondering what those appersands are doing in the labels. Well if you press the ALT key, the focus jumps to the first item in the menubar. You will then see that the F of the File menu is underlined. If you then press F the File menu will open. Press X and the application will exit. You do not have to add the appersands but it is recomended to make your application accessible to the disabled, plus some times it is just convient to use the keyboard.

Next lets look at the menu item callbacks

00065:   ///////////////////////////////////////////////////////////////////////////////
00066:   ///Callback for "New" menu item
00067:   BOOL onNew(MenuItem* item, UINT message)
00068:   {
00069:     //Set the edit window's text to \0 which 
00070:     //in effect empties the window
00071:     me->setText( "" );
00072:   
00073:     return TRUE;
00074:   }
00075:   
00076:   ///////////////////////////////////////////////////////////////////////////////
00077:   ///Callback for "Save" menu item
00078:   BOOL onSave(MenuItem* item, UINT message)
00079:   {
00080:     static char text[256];                  //Text buffer
00081:     
00082:     //Display the save file dialog
00083:     char *fname = showSaveFileDlg( me->getParent() );
00084:     
00085:     //If the user pressed OK
00086:     if ( fname != NULL )
00087:     {
00088:       me->getText( text, 256 );             //Get the edit window's text
00089:       ofstream fout( fname );               //Open the file the user selected
00090:   
00091:       //Make sure it is open
00092:       if ( fout.is_open() )
00093:         fout << text;                       //Output the edit window's text
00094:   
00095:       fout.close();                         //Close the file
00096:       delete[] fname;                       //Delete the file name
00097:     }
00098:   
00099:     return TRUE;
00100:   }
00101:   
00102:   ///////////////////////////////////////////////////////////////////////////////
00103:   ///Callback for "Exit" menu item
00104:   BOOL onExit(MenuItem* item, UINT message)
00105:   {
00106:     //Quit the application.
00107:     Window::quitApp();
00108:   
00109:   
00110:     return TRUE;
00111:   }

The important thing to notice about the callbacks is that they have different prototypes than the callbacks we use for components. The menu item have only two parameters. The first is a pointer to the menu item that generated the event. The second is the message generated. You will notice that I did not bother checking the message. That is because currently menu items can only process the WM_COMMAND event, this event is generated when the user clicks on the menu item. Note that menus do not currently generate events.