Adding Menus to your Application

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.

Interacting with Menu Bars

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.


Creating Pop-Up Menus

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.

  1. Declare a handler that is derived from both IMenuHandler and ICommandHandler so that the same handler can be used for handling the pop-up menu and the commands that originate from it. Make an instance of this handler a child of the frame window. Make the pop-up menu itself a dynamic child of the handler, as follows:
  2. /***********************************************************/ 
    /*  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;
    };
    
  3. Create an instance of the pop-up handler and start handling events in the frame window constructor, as shown in the following code:
    /***********************************************************/
    /* 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);
     
    }
    
  4. Create the pop-up menu and start event handling in the PopUpHandler constructor. Show the pop-up menu in the makePopUpMenu function. Handle the events generated from the pop-up menu in the command function, as follows:
    /***********************************************************/
    /* 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;
    }
    

System Menu

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.


Window Menu Button

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.



Menus


Creating a Frame Window
Adding a Menu Bar


IMenu
IMenuBar
IPopUpMenu
ISubMenu
ISystemMenu
IWindow