Next: Message Dialog boxes Up: Working with dialogs Previous: Working with dialogs

Menu Bars

A simple menu bar is quite easy to create in Motif, although the coding and concepts are a little unusual and confusing. The following code demonstrates the general idea by creating a menu bar in a form widget. In this code, a menu bar is created so that as each menu item is selected, a message is sent to stdout.

Generally, if you were building an application containing a menu bar, the bottom part of the form widget would be filled with the other widgets of the user interface. Here this is represented by a simple label widget. Get this code running and see what it does before you try to decode what it is doing:


        #include <X11/Intrinsic.h>
        #include <Xm/Xm.h>
        #include <Xm/Label.h>
        #include <Xm/Form.h>
        #include <Xm/PushB.h>
        #include <Xm/RowColumn.h>
        #include <Xm/CascadeB.h>

        void menuCB(Widget w,
                   char *client_data, /*normally an integer used to decide
                                        which menu was selected. */
                   XmAnyCallbackStruct *call_data)
        {
           printf("%s\n",client_data);
        }

        Widget make_menu_option(char *option_name,char *client_data,Widget menu)
        {
          int ac;
          Arg al[10];
          Widget b;

          ac = 0;
          XtSetArg(al[ac], XmNlabelString,
                XmStringCreateLtoR(option_name,
                XmSTRING_DEFAULT_CHARSET)); ac++;
          b=XtCreateManagedWidget(option_name,xmPushButtonWidgetClass,
                menu,al,ac);
          XtAddCallback (b, XmNactivateCallback, menuCB, client_data);
          XtSetSensitive(b, True);
          return(b);
        }

        Widget make_menu(char *menu_name, Widget menu_bar)
        {
          int ac;
          Arg al[10];
          Widget menu, cascade;

          ac = 0;
          menu = XmCreatePulldownMenu (menu_bar, menu_name, al, ac);

          ac = 0;
          XtSetArg (al[ac], XmNsubMenuId, menu);  ac++;
          XtSetArg(al[ac], XmNlabelString,
                XmStringCreateLtoR(menu_name, XmSTRING_DEFAULT_CHARSET)); ac++;
          cascade = XmCreateCascadeButton (menu_bar, menu_name, al, ac);
          XtManageChild (cascade); 

          return(menu);
        }

        void create_menus(Widget menu_bar)
        {
          int ac;
          Arg al[10];
          Widget menu, cascade;

          menu=make_menu("Menu1",menu_bar);
        /* Note: if you want to be able to change sensitivity of a menu option
           during execution, or if you want to change the menu's label at some
           point, you need to accept the parameter returned by make_menu_option
           into a unique variable for each button.*/
          make_menu_option("Menu1a","Hi from Menu1a",menu);
          make_menu_option("Menu1b","Hi from Menu1b",menu);

          menu=make_menu("Menu2",menu_bar);
          make_menu_option("Menu2a","Hi from Menu2a",menu);
          make_menu_option("Menu2b","Hi from Menu2b",menu);
        }

        main(int argc, char *argv[])
        {
          Widget toplevel, form, label, menu_bar;
          Arg al[10];
          int ac;

          toplevel=XtInitialize(argv[0],"",NULL,0,&argc,argv);

          /* resize the window */
          ac=0;
          XtSetArg(al[ac],XmNheight,200); ac++;
          XtSetArg(al[ac],XmNwidth,200); ac++;
          XtSetValues(toplevel,al,ac);

          /* create a form widget */
          ac=0;
          form=XtCreateManagedWidget("form",xmFormWidgetClass,
                toplevel,al,ac);

          /* create a label widget */
          ac=0;
          XtSetArg(al[ac],XmNlabelString,
                XmStringCreate("I'm a label",
                XmSTRING_DEFAULT_CHARSET)); ac++;
          label=XtCreateManagedWidget("label",xmLabelWidgetClass,
                form,al,ac);

          /* create the menu bar */
          ac=0;
          menu_bar=XmCreateMenuBar(form,"menu_bar",al,ac);
          XtManageChild(menu_bar);

          /* attach the menu bar to the form */
          ac=0;
          XtSetArg(al[ac], XmNtopAttachment,    XmATTACH_FORM); ac++;
          XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
          XtSetArg(al[ac], XmNleftAttachment,   XmATTACH_FORM); ac++;
          XtSetValues(menu_bar,al,ac);

          /* attach the label to the form */
          ac=0;
          XtSetArg(al[ac], XmNtopAttachment,    XmATTACH_WIDGET); ac++;
          XtSetArg(al[ac], XmNtopWidget, menu_bar); ac++;
          XtSetArg(al[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
          XtSetArg(al[ac], XmNleftAttachment,   XmATTACH_FORM); ac++;
          XtSetArg(al[ac], XmNbottomAttachment,   XmATTACH_FORM); ac++;
          XtSetValues(label,al,ac);

          create_menus(menu_bar);

          XtRealizeWidget(toplevel);
          XtMainLoop();
        }

You can see in this code that a menu bar capability is provided by Motif. The menu bar is appropriately attached to the form. On the menu bar are placed "cascade button" widgets. Cascade buttons are another form of container widget specifically designed for the creation of menus. [It is also possible to place simple pushbuttons on the menu bar.] Each cascade button contains several standard pushbuttons that act as the menu choices. These pushbuttons use callbacks whenever they are selected. Also notice that in this example the "sensitivity" of all buttons is set to true, but they can also be set to false to prevent selection of that option by the user.

You will notice that several "convenience functions" are used here. For example, XmCreateMenuBar creates a menu bar for you. It is called a convenience function because this is a much easier way to create a menu bar than setting it up yourself. [Many widgets have associated convenience functions.] The convenience function does not manage the widget however, so this must be done separately with the call to XtManageChild. We will see more of this in the discussion of dialog boxes.

You will also notice the "Note" comment right before the menu options are created. In many simple applications, like this one, there is no need to turn the sensitivity of menu items on and off, or to change menu labels. But if there is such a need, then you should take the result of the call to create_menu_optionand place it in a valiable declared as a Widget. This variable can then be used later to change the characteristics of the button in the menu. For example, you might have a button named "Open" on a menu that you wish to make insensitive while a file is open (the user uses the menu option to open the file, but needs to be prevented from opening more than one file at once). You might say:


        open_button=make_menu_option("Open File",OPEN_OPTION,menu);

and then later say:


        XtSetSensitive(open_button,False);

A menu can contain separators and labels along with pushbuttons. These extra widgets cannot be selected. They are created and added to the menu in the same way that a pushbutton menu item is by the make_menu_optioncode.



Next: Message Dialog boxes Up: Working with dialogs Previous: Working with dialogs


morbe@enstb.enst-bretagne.fr