Next: Other MainWindow children Up: The MenuBar Previous: A simple MenuBar

PullDown Menus

Let us now develop things a little further and add pulldown menus. A pulldown menu looks like this:

Fig. 5.2.2 menu_pull.c output

We will now develop a program menu_pull.c that will create two pulldown menus. The first Quit will contain a single item that lets us terminate our program.

The second menu Colour has 5 options that let's the label widget attached to the MainWindow have its background colour changed.

We create the top level widget hierarchy as usual, with the MainWindow widget being a child of the application and a MenuBar widget a child of the MainWindow.

Let's look at the full listing of menu_pull.c:


#include <Xm/Xm.h>
#include <Xm/MainW.h>
#include <Xm/Label.h>

Widget top_wid, label, main_w;
String colours[] = { "Black",  "Red", "Green", "Blue", "Grey"};

Display *display; /* xlib id of display */
Colormap cmap;


main(argc, argv)
int argc;
char *argv[];
{
    Widget  menubar, menu, quit_w, widget;
    XtAppContext app;

    XColor back, fore, spare;
    XmString  quit, colour, red, green, blue, black, 
              grey, label_str;
    void quit_call(), colour_call();
    
    int n = 0;
    Arg args[2];

    /* Initialize toolkit */
    top_wid = XtVaAppInitialize(&app, "Demos",
        NULL, 0, &argc, argv, NULL, NULL);
        
    /* MainWindow will contain a MenuBar and a Label */
    main_w = XtVaCreateManagedWidget("main_window",
        xmMainWindowWidgetClass,   top_wid,
        XmNwidth, 300,         
        XmNheight, 300,
        NULL);

    /* Create a simple MenuBar that contains three menus */
    quit = XmStringCreateSimple("Quit");
    colour = XmStringCreateSimple("Colour");
    
    menubar = XmVaCreateSimpleMenuBar(main_w, "menubar",
        XmVaCASCADEBUTTON, quit, 'Q',
        XmVaCASCADEBUTTON, colour, 'C',
        NULL);
        
        
    XmStringFree(colour); /* finished with this so free */
   
    /* First menu is the quit menu -- callback is quit_call() */
    
    quit_w = XmVaCreateSimplePulldownMenu(menubar, "quit_menu", 
        0, quit_call,
        XmVaPUSHBUTTON, quit, 'Q', NULL, NULL,
        NULL);
    XmStringFree(quit);

    /* Second menu is the color menu -- callback 
       is colour_call() */
    black = XmStringCreateSimple(colours[0]);
    red = XmStringCreateSimple(colours[1]);
    green = XmStringCreateSimple(colours[2]);
    blue = XmStringCreateSimple(colours[3]);
    grey = XmStringCreateSimple(colours[4]);
    
    menu = XmVaCreateSimplePulldownMenu(menubar, "edit_menu", 
        1, colour_call,
        XmVaRADIOBUTTON, black, 'k', NULL, NULL,
        XmVaRADIOBUTTON, red, 'R', NULL, NULL,
        XmVaRADIOBUTTON, green, 'G', NULL, NULL,
        XmVaRADIOBUTTON, blue, 'B', NULL, NULL,
        XmVaRADIOBUTTON, brey, 'e', NULL, NULL,
        /* RowColumn resources to enforce */
        XmNradioBehavior, True,     
        /* radio behavior in Menu */
        XmNradioAlwaysOne, True,    
        NULL);
    XmStringFree(black);
    XmStringFree(red);
    XmStringFree(green);
    XmStringFree(blue);

    /* Initialize menu so that "black" is selected. */
    if (widget = XtNameToWidget(menu, "button_0"))
        XtVaSetValues(widget, XmNset, True, NULL);

    XtManageChild(menubar);
    
    /* create a label text widget that will 
       be "work area" we colour */
    label_str = XmStringCreateSimple("Colour Me");
    
    label = XtVaCreateManagedWidget("main_window",
        xmLabelWidgetClass,   main_w,
        XmNlabelString, label_str,
        NULL);
        
     XmStringFree(label_str);
    
    /* set the label as the "work area" 
       of the main window */
    XtVaSetValues(main_w,
        XmNmenuBar,    menubar,
        XmNworkWindow, label,
        NULL);
       
     /* background pixel to black foreground to white */
    cmap = DefaultColormapOfScreen(XtScreen(label));
    display = XtDisplay(label);
    
    XAllocNamedColor(display, cmap, colours[0], &back, &spare);
    XAllocNamedColor(display, cmap, "white", &fore, &spare);
    
    XtSetArg(args[n],XmNbackground, back.pixel);      
    ++n;
    XtSetArg(args[n],XmNforeground, fore.pixel);      
    ++n;
    XtSetValues(label, args, n);
    
    XtRealizeWidget(top_wid);
    XtAppMainLoop(app);
}

/* Any item the user selects from the File menu calls 
  this function. It will "Quit" (item_no == 0).
 */
void
quit_call(w, item_no)
Widget w;     /* menu item that was selected */
int item_no;  /* the index into the menu */
{  if (item_no == 0) /* the "quit" item */
        exit(0);
}

/* called from any of the "Colour" menu items.  
   Change the color of the label widget. 
   Note: we have to use dynamic setting with setargs()..
 */
void
colour_call(w, item_no)
Widget w;     /* menu item that was selected */
int item_no;  /* the index into the menu */
{   int n =0;
    Arg args[1];
    
    XColor xcolour, spare; /* xlib color struct */
        
    if (XAllocNamedColor(display, cmap, colours[item_no], 
                         &xcolour, &spare) == 0)
       return;
           
    XtSetArg(args[n],XmNbackground, xcolour.pixel);      
    ++n;
    XtSetValues(label, args, n);
}
In this program we create the MenuBar widget with the convenience function XmVaCreateSimpleMenuBar().

The Argument XmVACASCADEBUTTON sets the label to the specified XmString. The third argument specifies the Meta key.

Note: we convert a String to an XmString with the XmStringCreateSimple() function.

It is good practice to free an XmString as soon as you have finished using it with the XmStringFree() command.

To create a pull down menu we need to create a PullDownMenu widget. To attach this to a MenuBar item simply make the PullDownMenu Widget a child of the MenuBar's CascadeButton.

Let us look at how we create the Quit menu:


quit_w = XmVaCreateSimplePulldownMenu(menubar, "quit_menu", 
        0, quit_call,
        XmVaPUSHBUTTON, quit, 'Q', NULL, NULL,
        NULL);
This Motif convenience function returns a PulldownMenu widget.

The first argument is the parent widget (MenuBar in this example). The second is the name given to the widget.

The third argument is the integer id of the CascadeButton the menu is attached to. So in this program an integer id of 0 attaches to the Quit button and an id of 1 would attached to the Colour button.

The fourth argument specifies the callback function associated with a menu choice. NOTE: we do not specify a callback with an XtAddCallback() call here.

We then specify a NULL terminated list giving the PulldownMenu items.

We use XmVaPUSHBUTTON to specify the XmString and Meta key for the entry. XmVaPUSHBUTTON takes four arguments, the last two are only needed for advanced Motif use and are not considered here. They are just set to NULL.

The Colour menu is created in a similar way except that we have 5 menu items.

We have one minor problem in assigning Meta keys for the colours. We cannot have the same Meta key for two menu selections. So Meta-B is chosen for Blue and Meta-G for Green. Black and Grey must be assigned different keys (Meta-k and Meta-e).

The last thing we need to look at is how we find out which selection has been made in our program.

Each PulldownMenu has an associated callback function. The callback function of a pulldown has two parameters which we must define.

So in the Quit callback, quit_call(), we only have one possible selection (item_no must equal zero).

In the Colour callback, colour_call(), the index corresponds to a colour setting of black, red, etc..

Do not worry too much about how the colour is dealt with in X/Motif. We will study this later - it needs some knowledge of Xlib.

Just accept that the XColour structure, xcolour has the colour we require stored in xcolour.pixel.

We need to set the XmNbackground resource to this value and we use the (dynamic) XSetArg()/XSetValues() method to do this.



Next: Other MainWindow children Up: The MenuBar Previous: A simple MenuBar


Dave.Marshall@cm.cf.ac.uk
Tue May 24 16:52:56 BST 1994