A typical application uses a menu bar and several pull-down submenus. You can map pull-down submenus from the menu bar, another pull-down menu, or a pop-up menu.
The menu bar is a child of the frame window; the menu bar window handle is the key to communicating with the menu bar and its submenus.
This section shows you three ways to create a menu bar. Most applications use a menu resource script to define the menus they need, and there are two ways to load the predefined menu resource. Some applications opt to dynamically create the menu bar programmatically using IMenu member functions. Applications can also choose to mix and match these methods to obtain the results desired. An example of this would be to dynamically load a menu bar containing all possible menu options and then programmatically remove those items at run time which are not needed, depending on the state of the application.
Version 6 of the Hello World application creates a frame window with the following statements and then loads a menu bar from the resource file. In ahellow6.hpp, we do the following:
class AHelloWindow : public IFrameWindow { . . private: IMenuBar menuBar; . .
In ahellow6.cpp, we do the following:
AHelloWindow::AHelloWindow( unsigned long windowId ) : IFrameWindow( IFrameWindow::defaultStyle() | IFrameWindow::minimizedIcon | IFrameWindow::accelerator, windowId ) ,menuBar( windowId, this )
In the preceding example, the frame window is constructed without specifying the IFrameWindow::menuBar style on the constructor. During initialization of the frame, it will then construct an IMenuBar object and will load the menu bar from the resource file, using the windowId passed as the identifier for that resource. The content of the menu bar is defined in the ahellow6.rc resource file as in the following statements:
WND_MAIN MENUEX BEGIN POPUP "&Edit", MI_EDIT BEGIN POPUP "&Alignment", MI_ALIGNMENT BEGIN MENUITEM "&Left\tF7", MI_LEFT MENUITEM "&Center\tF8", MI_CENTER MENUITEM "&Right\tF9", MI_RIGHT END MENUITEM "&Text...", MI_TEXT MENUITEM "&Font...", MI_FONT END POPUP "&Settings", MI_SETTINGS BEGIN MENUITEM "&Read from profile", MI_READSETS MENUITEM "&Open ...", MI_OPENSETS MENUITEM "&Save to profile", MI_SAVESETS END END
This is a Windows resource file.
This same menu bar can also be created and loaded in the frame window constructor by specifying the IFrameWindow::menuBar style. If an IMenuBar object is needed, it can be created by wrappering the one loaded by the frame window constructor. The following statements demonstrate this:
AHelloWindow::AHelloWindow( unsigned long windowId ) : IFrameWindow( IFrameWindow::defaultStyle() | IFrameWindow::menuBar | IFrameWindow::minimizedIcon | IFrameWindow::accelerator, windowId ) ,menuBar( this, IMenuBar::wrapper )
The final way to create a menu bar is to create it dynamically at run time. This can be done using the IMenuBar constructor and specifying the IMenuBar::empty style and the frame window the menu bar is for. The menu can then be populated using IMenu member functions. The addText member function is used to add a menu item to the menu bar with the ID specified and the string passed. If a third parameter is passed, the menu item is added into the pull-down menu specified instead of the menu bar itself. The addSubmenu member functions is used to convert a menu item that represents a command event to a menu item that represents a pull-down menu. The following statements demonstrate building the same menu that is defined in the preceding resource file:
IMenuBar* menuBar = new IMenuBar( this ); (*menuBar) .addText( MI_EDIT, "&Edit" ) .addSubmenu( MI_EDIT ) .addText( MI_ALIGNMENT , "&Alignment" , MI_EDIT ) .addSubmenu( MI_ALIGNMENT ) .addText( MI_LEFT , "&Left\tF7", MI_ALIGNMENT ) .addText( MI_CENTER , "&Center\tF8", MI_ALIGNMENT ) .addText( MI_RIGHT , "&Right\tF9", MI_ALIGNMENT ) .addText( MI_TEXT , "&Text..." , MI_EDIT ) .addText( MI_FONT , "&Font..." , MI_EDIT ) .addText( MI_SETTINGS, "&Settings" ) .addSubmenu( MI_SETTINGS ) .addText( MI_READSETS , "&Read from profile", MI_SETTINGS ) .addText( MI_OPENSETS , "&Open ...", MI_SETTINGS ) .addText( MI_SAVESETS , "&Save to profile", MI_SETTINGS );
Refer to the IMenu reference information for a complete list of all of the IMenu member functions that you can use to dynamically create and manipulate menus.
The menu bar in its entirety, including the pull-down menus, represents a list of choices which allow an end user to interact with an application. When the user selects a menu item that represents a command choice, any command handlers attached to the frame window are notified of the command event, indicating which choice the user selected. See ICommandHandler for more information about its use.
This section shows you two ways to create a pop-up menu.
Version 6 of the Hello World application creates two pop-up menus: one for the "Hello, World" static text control and one for the Earth window static text control. The contents of the pop-up menus are defined in the ahellow6.rc resource file as follows:
· MENU WND_HELLOPOPUP BEGIN MENUITEM "~Left-align text", MI_LEFT MENUITEM "~Center text" , MI_CENTER MENUITEM "~Right-align text", MI_RIGHT END MENU WND_EARTHPOPUP BEGIN MENUITEM "~Twinkling stars", MI_TWINKLE MENUITEM SEPARATOR MENUITEM "~Brighten stars", MI_BRIGHT MENUITEM "~Dim stars", MI_DIM END ·
In the ahellow6.hpp file, an APopUpHandler class is defined to process requests for making the pop-up menus appear, as follows:
· class APopUpHandler : public IMenuHandler { protected: virtual bool makePopUpMenu(IMenuEvent& menuEvent); }; ·
The makePopUpMenu member function is called whenever the user requests a pop-up menu, usually by clicking mouse button 2, in a window for which menu requests are being handled. This function shows the appropriate pop-up menu. You can also dynamically create the pop-up menu in the makePopUpMenu function.
Hello World version 6 demonstrates how to create a pop-up menu as a data member of the AHelloWindow class and how to dynamically create a pop-up menu. Typically, you choose one of these approaches based on resource balancing. Static pop-up menus are only created and deleted once, but take up storage whether they are needed or not; dynamic pop-up menus are created and deleted on demand, which can slow processing if you request them frequently.
The following sample is from the ahellow6.cpp file:
/************************************************************************** * Class APopUpHandler :: makePopUpMenu - Creates a pop-up menu for * * windows attached to the handler when requested by the pointing device * **************************************************************************/ bool APopUpHandler::makePopUpMenu(IMenuEvent &menuEvent) { bool eventProcessed(true); //Assume event will be processed IPopUpMenu *popUpMenu; /*------------------------------------------------------------------------| | The window pointer stored in the menu event points to the owner of | | the menu to be popped up. | |------------------------------------------------------------------------*/ IWindow *popUpOwner = menuEvent.controlWindow(); IStaticText *hello; AEarthWindow *earth; /*------------------------------------------------------------------------| | Determine which menu to pop up based on the window ID of the window | | for which the menu event is being handled. | |------------------------------------------------------------------------*/ switch (popUpOwner->id()) { case WND_HELLO: /*------------------------------------------------------------------------| | Create a pop-up menu for the menu WND_HELLOPOPUP. | | The pointer, hello, is created by casting the popUpOwner to the type | | of the AHelloWindow::hello object. The pointer is used to call | | the AHelloWindow::alignment function to determine which pop-up menu | | item to disable. | |------------------------------------------------------------------------*/ popUpMenu = new IPopUpMenu(WND_HELLOPOPUP,popUpOwner); if (popUpMenu) { hello = (IStaticText *)popUpOwner; popUpMenu->enableItem(MI_LEFT, hello->alignment()!=IStaticText::centerLeft); popUpMenu->enableItem(MI_CENTER, hello->alignment()!=IStaticText::centerCenter); popUpMenu->enableItem(MI_RIGHT, hello->alignment()!=IStaticText::centerRight); } //If the pop-up wasn't found, else eventProcessed=false; // the event wasn't processed break; case WND_EARTH: /*------------------------------------------------------------------------| | If the earthWindow pop-up is requested, then create the pop-up menu | | dynamically, with the earthWindow as the owner. This approach is | | used to create pop-up menus on demand. The setAutoDeleteObject | | function is used to automatically delete the menu after the user | | has made a selection. | | The pointer, earth, is created by casting the popUpOwner to the type | | of the earthWindow object. The pointer is used to call the | | AEarthWindow isTwinkling and isBright functions. | | The Twinkling menu item contains a check mark when isTwinkling is true.| | Either the Bright or Dim menu item is disabled, depending on the | | result of isBright. | |------------------------------------------------------------------------*/ popUpMenu = new IPopUpMenu(WND_EARTHPOPUP,popUpOwner); if (popUpMenu) { popUpMenu->setAutoDeleteObject(); earth = (AEarthWindow *)popUpOwner; popUpMenu->checkItem(MI_TWINKLE,earth->isTwinkling()); if (earth->isBright()) { popUpMenu->disableItem(MI_BRIGHT); } else { popUpMenu->disableItem(MI_DIM); } } //If the pop-up wasn't created, else eventProcessed=false; // the event wasn't processed break; default: //If the owner wasn't hello or eart eventProcessed=false; // the event wasn't processed } /* end switch */ /*------------------------------------------------------------------------| | If the pop-up menus were setup successfully, use the | | IPopUpMenu::show function to show the menu at the position where the | | pointing device was when the menu event occurred. | |------------------------------------------------------------------------*/ if (eventProcessed) popUpMenu->show(menuEvent.mousePosition()); return(eventProcessed); } /* end APopUpHandler :: makePopUpMenu(...) */
The case statement for WND_HELLO uses the IWindow::windowWithId function to get a pointer to the static pop-up menu. The WND_EARTH case creates a pop-up menu dynamically. Because Hello World version 6 creates the menu using the new operator, it must also be deleted. The easiest way to delete a dynamic pop-up menu is to use the setAutoDeleteObject function, which causes the menu to be automatically deleted when the menu ends.
In either case, when the menu is found or created, the makePopUpMenu function displays the menu by using IPopUpMenu::show. The mouse pointer position, which is taken from the menu event, is passed as one argument to specify where the pop-up menu will appear.
The makePopUpMenu function is only called for windows that are attached to the pop-up menu handler. Hello World version 6 attaches a pop-up menu handler directly to the IStaticText objects, hello and earthWindow. It attaches to the objects instead of the frame because static text objects do not pass events up the owner chain. Therefore, use this approach if you develop portable applications. For the same reason, the command handler attaches to the static text objects so that the command events that result from using the pop-up menus will be sent to the command handler. In Hello World version 6, this is done using the following code from the ahellow6.cpp file:
commandHandler.handleEventsFor(&hello); commandHandler.handleEventsFor(&earthWindow); popUpHandler.handleEventsFor(&hello); popUpHandler.handleEventsFor(&earthWindow);
By reusing the existing command handler, commands such as MI_LEFT can have the same processing whether they are generated by a menu bar item, a push button, an accelerator key, or a pop-up menu.
The following example also shows you how to create a pop-up menu. To create a pop-up menu for a list box, follow the steps in this section.
/***********************************************************/ /* Define the frame window */ /***********************************************************/ class AppWindow : public IFrameWindow { public: AppWindow(unsigned long windowId); ~AppWindow(); private: ITitle title; IMenuBar menu; IMultiCellCanvas canvas; IStaticText sttxt; IListBox listbox; PopUpHandler * pLBPopUp; ACommandHandler * commandHandler; }; /***********************************************************/ /* Define the pop-up and command handler */ /***********************************************************/ class PopUpHandler : public IMenuHandler, public ICommandHandler { public: PopUpHandler(IListBox & lbUpdate, IStaticText & stMsg); ~PopUpHandler(); protected: virtual bool makePopUpMenu(IMenuEvent& menuEvent); virtual bool command(ICommandEvent& cmdevt); private: void setLBColor(unsigned long ulNewColor); void setLBText(unsigned long ulNewSize); IListBox * pLB; IStaticText * pST; IPopUpMenu * pPopUpMenu; unsigned long ulColor; unsigned long ulText; };
/***********************************************************/ /* Construct the frame window with children */ /***********************************************************/ AppWindow::AppWindow(unsigned long windowId) : IFrameWindow(windowId, defaultStyle()), title(this, "PopUp","Example"), canvas(ID_CANVAS, this, this), listbox(ID_LISTBX, &canvas, &canvas), sttxt(ID_TEXT, &canvas, &canvas), menu(ID_MENU, this), pLBPopUp(0) { listbox.setForegroundColor(IColor::kYellow); sttxt.setText("ListBox Set to Default Values"); sttxt.setLimit(30); //************************************************** // Customize the multicell canvas //************************************************** setClient(&canvas); canvas.addToCell(&listbox, 2, 2); canvas.setRowHeight(2, 20, true); canvas.setColumnWidth(2, 20, true); canvas.addToCell(&sttxt, 2, 6); canvas.setRowHeight(1, 10); canvas.setRowHeight(4, 25); canvas.setRowHeight(7, 10); canvas.setColumnWidth(1, 10); canvas.setColumnWidth(3, 10); // Attach the pop-up menu handler to the list box pLBPopUp = new PopUpHandler(listbox, sttxt); // Handle commands from the menu bar commandHandler = new ACommandHandler(this); commandHandler->handleEventsFor(this); }
/***********************************************************/ /* Pop-up handler constructor */ /***********************************************************/ PopUpHandler::PopUpHandler(IListBox & LBUpdate, IStaticText & stMsg) : pLB(0), pST(0), pPopUpMenu(0), ulColor(0), ulText(0) { unsigned long ulColor, ulText; // Need pointers to entry field and static text to update them // from the exit pLB= &LBUpdate; pST = &stMsg; // Create the pop-up menu pPopUpMenu = new IPopUpMenu(ID_POPUP2, pLB); // Default color and alignment values ulColor = ID_BLUE; ulText = ID_ADD_FIRST; // Check the default items pPopUpMenu->checkItem(ulColor); pPopUpMenu->checkItem(ulText); // Set default color and alignment setLBColor(ulColor); setLBText(ulText); // Handle menu events and pop-up menu requests ICommandHandler::handleEventsFor(pLB); IMenuHandler::handleEventsFor(pLB); } : : /***********************************************************/ /* Handle the menu functions to change the list box color */ /***********************************************************/ void PopUpHandler::setLBColor(unsigned long ulNewColor) { switch (ulNewColor) { case ID_RED: pLB->setBackgroundColor(IColor::kRed); pST->setText("Changed color to Red"); break; case ID_GREEN: pLB->setBackgroundColor(IColor::kGreen); pST->setText("Changed color to Green"); break; case ID_BLUE: pLB->setBackgroundColor(IColor::KBlue); pST->setText("Changed color to Blue"); break; default: break; } /* endswitch */ } /***********************************************************/ /* Handle the menu functions to add text to the list box */ /***********************************************************/ void PopUpHandler::setLBText(unsigned long ulNewText) { switch (ulNewText) { case ID_ADD_FIRST: pLB->addAscending("I need to learn C++"); pST->setText("Added text to list box in ascending order"); break; case ID_DELETE_TEXT: pLB->removeAll(); pST->setText("Deleted All Text from ListBox"); break; case ID_ADD_LAST: pLB->addDescending("I know C++"); pST->setText("Added text to list box in descending order"); break; default: break; } /* endswitch */ } /***********************************************************/ /* Show the pop-up menu created in the constructor */ /***********************************************************/ bool PopUpHandler::makePopUpMenu(IMenuEvent& menuEvt) { pPopUpMenu->show(menuEvt.mousePosition()); return true; } /***********************************************************/ /* Command handling for the pop-up menu */ /***********************************************************/ bool PopUpHandler::command(ICommandEvent& cmdevt) { switch (cmdevt.commandId()) { case ID_RED: case ID_GREEN: case ID_BLUE: setLBColor(cmdevt.commandId()); return true; case ID_ADD_FIRST: case ID_DELETE_TEXT: case ID_ADD_LAST: setLBText(cmdevt.commandId()); return true; } return false; }
The system menu in the upper-left corner of a standard frame window is different from the menus defined by the application. The system menu is controlled and defined by the system. However, using Open Class Library, you can enable and disable items on the system menu, add or delete items, or even decide not to include the system menu on your frame window. The ISystemMenu class gives you access to the system menu and allows you to manipulate its items. The IFrameWindow::Style class provides the flag systemMenu, which allows you to specify the presence of the system menu on your frame window.
The window menu displayed by the window menu button in the upper-left corner of a standard Motif frame window is different from the menus defined by the application. The window menu button is controlled and defined almost exclusively by the system; your only decision about it is whether to include it when creating a frame window.
Creating a Frame Window
Adding a Menu Bar
IMenu
IMenuBar
IPopUpMenu
ISubMenu
ISystemMenu
IWindow