next up previous
Next: Dialog Widgets Up: X WIndow/Motif Programming Previous: Combining Widgets

The MainWindow Widget and Menus

   

When designing a GUI, care must be taken in the presentation of the application's primary window. This window is important as it is likely to be the major focus of the application -- the most visible and most used window in the application. In order to facilitate consistency amongst many different applications, Motif provides a general framework: the MainWindow Widget, for an application developer to work with. The Motif Style Guide  (Chapter 20) recommends that, whenever applicable, the MainWindow window should be used. It should be noted, however, that the general framework of the MainWindow is not always applicable to every application front end GUI. A text editor application front end is an example that might easily map into the MainWindow framework, a simple calculator application most certainly would not fit the prescribed framework.

A MainWindow widget can manage up to five specialised child widgets (Fig 9.1):

 

Fig. 9.1 The MainWindow Widget with its Specialised Child Widgets

The MainWindow basically provides an efficient method of managing widgets as recommended by the Motif Style Guide (Chapter 20). In particular, the provision of a menu bar and work area is a convenient mechanism with which to drive many applications. It should be noted that the work area can be any widget (or composite hierarchy of widgets). The scrollbars, command and message area are optional.

In this Chapter, we will mainly concentrate our study on the relationship between the MainWindow and certain types of menus. We will also see how to put widgets in the work area MainWindow. We will see further applications of the MainWindow widget in the remainder of the book. The MainWindow will be used as a container widget in many example programs throughout the book.

The MainWindow widget

  

As we have previously stated, the MainWindow is typically used as the top level container for an application's child widgets. The simplest functional MainWindow may consist of a menu bar along the top of the application and some work area below, this is illustrated in Fig. 9.2. We omit the scroll bars and command and message areas for the time being.

 

Fig. 9.2 menu_cascade.c output

Menu bar items are placed within the menu bar  . You can use menu bar items to perform selections directly. However, more usually a PullDown menu is attached to a menu bar item allowing greater selection opportunities.

The function of the work area is to perform the main application's functions. The work area can be any widget class. We will look at aspects of this in later sections when we have studied more widget classes.

Initially, we will concentrate on how to create menus in a MainWindow.

The MenuBar

  

The creation of a fully functional pulldown MenuBar typical of most MainWindow based applications is fairly complex. We will, therefore, break it down into two stages.

  1. We will we create a simple MenuBar that lets us select actions directly from the bar (MenuBar items).
  2. Having created simple MenuBar items we will attach pulldown menus to them.

A simple MenuBar

A MenuBar widget is really a RowColumn widget under another name. The way we create a MenuBar is to use one of the (several) XmCreatetex2html_wrap_inline8861 or XtVaCreatetex2html_wrap_inline8861 Menu options. For most of our applications, we will use the XmCreateMenuBar()  function.

Having created a MenuBar we create MenuBar items by attaching widgets to the MenuBar in exactly the same fashion as described for the RowColumn widget (Chapter 8). The widgets we usually attach are CascadeButton widgets   since we can hang pull down menus off these widgets.

Fig. 9.2 shows the output of the menu_cascade.c  program that simply creates a simple MenuBar widget and attaches two CascadeButton widgets - help and quit. Minimal callback functions are associated with each CascadeButton.

The full program listing of menu_cascade.c is:

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

/* Prototype callbacks */
void quit_call(void), help_call(void); 

main(int argc, char **argv)

{
    Widget top_wid, main_w, menu_bar, quit, help;
    XtAppContext app;
    Arg arg[1];
    
    /* create application, main and menubar widgets */
    
    top_wid = XtVaAppInitialize(&app, "menu_cascade",
        NULL, 0, &argc, argv, NULL, NULL);

    main_w = XtVaCreateManagedWidget("main_window",
        xmMainWindowWidgetClass,   top_wid,
        NULL);

    menu_bar = XmCreateMenuBar(main_w, "main_list", 
        NULL, 0);        
    XtManageChild(menu_bar);
    
    
     /* create quit widget + callback */
        
    quit = XtVaCreateManagedWidget( "Quit",
        xmCascadeButtonWidgetClass, menu_bar,
        XmNmnemonic, 'Q',
        NULL);
        
    
    XtAddCallback(quit, XmNactivateCallback, quit_call, NULL);

    /* create help widget + callback */
        
    help = XtVaCreateManagedWidget( "Help",
        xmCascadeButtonWidgetClass, menu_bar,
        XmNmnemonic, 'H',
        NULL);
        
    
    XtAddCallback(help, XmNactivateCallback, help_call, NULL);

    /* Tell the menubar which button is the help menu  */

    XtSetArg(arg[0],XmNmenuHelpWidget,help);
    XtSetValues(menu_bar,arg,1);
    
        
    XtRealizeWidget(top_wid);
    XtAppMainLoop(app);
}


void quit_call()

{   printf("Quitting program\n");
    exit(0);
}

void help_call()

{   printf("Sorry, I'm Not Much Help\n");
}

The first steps of the main function should now be familiar -- we initialise the application and create the top_wid top level widget.

A MainWindow widget , main_win, is then created as is a MenuBar  , menu_bar. The menu_bar widget is a child of main_win. Finally, we attach two CascadeButton   widgets, quit and help, as children of menu_bar. Note there is no work area created in this program.

A CascadeButton   widget is usually used to attach a pulldown menu to the MenuBar. This CascadeButton widget is similar to the PushButton widget and can have callback functions attached to its activateCallback  function -- illustrated in the menu_cascade.c program. However, it should be noted, that the main use of CascadeButton is to link a menu bar with a menu.

The program above simply attaches callback functions to the CascadeButtons. The callback functions do not do much except quit the program and print to standard output.

You can also associate a mnemonic  to a particular menu selection. This means that you can use ``hot keys'' on the keyboard as a short cut to selection. You need to press Meta key  the key in question.

In this program, we allow Meta-Q and Meta-H for the selection of Quit and Help. Note that Motif displays the appropriate Meta key by underlining the letter concerned on the menu bar (or menu item). The XmNmnemonic  resource is used to select the appropriate keyboard short cut.

Apart from prescribing the use of the Meta key for the selection of menu items, the Motif Style Guide  also insists that the Help MenuBar widget should always be placed on the right most side of the MenuBar (Fig. 9.2). This makes for easy location and selection of the help facility, should it exist.

The MenuBar resource, XmNmenuHelpWidget , is used to store the ID of the the appropriate widget (help in the above program).

PullDown Menus

  

Let us now develop things a little further by adding pulldown menus to our MenuBar. A pulldown menu looks like this:

 

Fig. 9.3 menu_pull.c output

In order to see how we create and use pulldown menu items we will develop a program, menu_pull.c  that will create two pulldown menus:

We also attach the Help Cascade button as in the previous menu_cascade.c program.

We should now be familiar with the first few steps of this program: 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.

The complete listing of menu_pull.c is as follows:

 

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

/* prototype functions */

void quit_call(Widget , int), 
       menu_call(Widget , int),
       help_call(void);


Widget label;
String food[] = { "Chicken",  "Beef", "Pork", "Lamb", "Cheese"};



main(int argc, char **argv)

{   Widget top_wid, main_w, help;
    Widget  menubar, menu, widget;
    XtAppContext app;
    XColor back, fore, spare;
    XmString  quit, menu_str,  help_str, chicken, beef, pork, 
              lamb, cheese, label_str;
    
    int n = 0;
    Arg args[2];

    /* Initialize toolkit */
    top_wid = XtVaAppInitialize(&app, "Demos",
        NULL, 0, &argc, argv, NULL, NULL);
        
    

    /* main window 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 = XmStringCreateLocalized("Quit");
    menu_str = XmStringCreateLocalized("Menu");
    help_str = XmStringCreateLocalized("Help");

    
    menubar = XmVaCreateSimpleMenuBar(main_w, "menubar",
        XmVaCASCADEBUTTON, quit, 'Q',
        XmVaCASCADEBUTTON, menu_str, 'M',
        XmVaCASCADEBUTTON, help_str, 'H',
        NULL);
        
        
    XmStringFree(menu_str); /* finished with this so free */
    XmStringFree(help_str);
    
    /* First menu is the quit menu -- callback is quit_call() */
    
    XmVaCreateSimplePulldownMenu(menubar, "quit_menu", 0, quit_call,
        XmVaPUSHBUTTON, quit, 'Q', NULL, NULL,
        NULL);
    XmStringFree(quit);

    /* Second menu is the food menu -- callback is menu_call() */
    chicken = XmStringCreateLocalized(food[0]);
    beef = XmStringCreateLocalized(food[1]);
    pork = XmStringCreateLocalized(food[2]);
    lamb = XmStringCreateLocalized(food[3]);
    cheese = XmStringCreateLocalized(food[4]);
    
    menu = XmVaCreateSimplePulldownMenu(menubar, "edit_menu", 1, 
        menu_call,
        XmVaRADIOBUTTON, chicken, 'C', NULL, NULL,
        XmVaRADIOBUTTON, beef, 'B', NULL, NULL,
        XmVaRADIOBUTTON, pork, 'P', NULL, NULL,
        XmVaRADIOBUTTON, lamb, 'L', NULL, NULL,
        XmVaRADIOBUTTON, cheese, 'h', NULL, NULL,
        /* RowColumn resources to enforce */
        XmNradioBehavior, True,    
        /* select radio behavior in Menu */ 
        XmNradioAlwaysOne, True,   
        NULL);
    XmStringFree(chicken);
    XmStringFree(beef);
    XmStringFree(pork);
    XmStringFree(lamb);
    XmStringFree(cheese);


    /* Initialize menu so that "chicken" is selected. */
    if (widget = XtNameToWidget(menu, "button_1"))
       { XtSetArg(args[n],XmNset, True);
         n++; 
         XtSetValues(widget, args, n);
       }

    n=0; /* reset n */

     /* get help widget ID to add callback */
        
    help = XtVaCreateManagedWidget( "Help",
        xmCascadeButtonWidgetClass, menubar,
        XmNmnemonic, 'H',
        NULL);
     
    XtAddCallback(help, XmNactivateCallback, help_call, NULL);

     /* Tell the menubar which button is the help menu  */

     XtSetArg(args[n],XmNmenuHelpWidget,help);
     n++;
     XtSetValues(menubar,args,n);
     n=0; /* reset n */
   


    
    XtManageChild(menubar);
    
   /* create a label text widget that will be "work area" 
      selections from "Menu"  menu change label
      default label is item 0 */

    label_str = XmStringCreateLocalized(food[0]);
    
    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);
        
         
    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(Widget w, int item_no)

   /* w = menu item that was selected 
      item_no = the index into the menu */

{
    

    if (item_no == 0) /* the "quit" item */
        exit(0);

   }


/* Called from any of the food "Menu" items.  
   Change the XmNlabelString of the label widget. 
   Note: we have to use dynamic setting with setargs().
 */

void  menu_call(Widget w, int item_no)
                          

{
    int n =0;
    Arg args[1];
    
    XmString   label_str;     
    
    label_str = XmStringCreateLocalized(food[item_no]);
           
    XtSetArg(args[n],XmNlabelString, label_str);      
    ++n;
    XtSetValues(label, args, n);
  
}

 

void help_call()

{   printf("Sorry, I'm Not Much Help\n");
}

In this program we create the MenuBar widget with the convenience function XmVaCreateSimpleMenuBar() :

The creation of a pulldown menu is now relatively straightforward:

Let us now look at how we create  the Quit menu:

 
quit_w = XmVaCreateSimplePulldownMenu(menubar, "quit_menu", 
             0, quit_call,
             XmVaPUSHBUTTON, quit, 'Q', NULL, NULL,
             NULL);

We have used the Motif convenience function XmVaCreateSimplePulldownMenu()  to return a PulldownMenu widget. This function has several arguments:

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 Menu callback, menu_call(), the index corresponds to a food item setting of Chicken, Beef, tex2html_wrap_inline8861 etc..

Chapter 20 discusses aspects of the Motif Style guidelines for menus which also incorporate some general menu design issues.

Tear-off menus

  

Tear-off menus allow the user to remove (or tear) a menu off the MenuBar and keep it displayed in a small dialog window (Figs. 9.4 and 9.5) on the screen until the user closes it from the window menu. The Motif Style Guide  (Chapter 20) prescribes this for menus that are frequently used in order to ease menu selection In order to make a menu a tear-off variety the XmNtearOffModel  resource for a PullDownMenu widget needs to set to XmTEAR_OFF_ENABLED. If a menu is XmTEAR_OFF_ENABLED then its appearance is modified to include a small perforated line at the top of the menu (Fig. 9.4).

 

Fig. 9.4 A Tear-off Menu on the MenuBar  

Fig. 9.5 A Tear-off Menu Dialog

Other MainWindow children

 

So far in this Chapter we have concentrated on the MenuBar and work area parts of the MainWindow. In this section we will develop a simple program, test_for_echo.c , which creates and uses the command and message areas of the MainWindow. We create a minimal MenuBar that simply allows you to quit the program. The work area created is a Label widget that does not perform any useful task in this example. The command and message areas created are both TextField   widgets (see Chapter 11 for a complete description of the TextField widget). The command area receives (String) input and echoes it to the message area (Fig 9.6). The message area is not usually allowed to receive any user input. In this example we set the XmNeditable resource for the message area to be False

TextField, Label or Command widgets are typically employed as the command and message areas.

 

Fig. 9.6 The test_for_echo.c program output The test_for_echo.c program achieves this by:

The complete program listing for test_for_echo.c is as follows:

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

#include <stdio.h>  
#include <string.h> /* For String Handling */

#define MAX_STR_LEN 30 /* Max Char length of Text Field */

/* Callback function prototypes */

void   cmd_cbk(), quit_cbk();

Widget   msg_wid;

char *cmd_label  = "Command Area: ";
char *msg_label  =  "Message Area: ";

int cmd_label_length;
int msg_label_length;


main(int argc,  char **argv)

{
    Widget        top_wid, main_win, menubar, menu,   
                  label, cmd_wid;
    XtAppContext  app;
    XmString      label_str, quit;

    cmd_label_length = strlen(cmd_label);
    msg_label_length = strlen(msg_label);

    /* initialize toolkit and create top_widlevel shell */
    top_wid = XtVaAppInitialize(&app, "Main Window",
        NULL, 0, &argc, argv, NULL, NULL);


    /* Create MainWindow */    

   main_win = XtVaCreateWidget("main_w",
        xmMainWindowWidgetClass, top_wid,
        XmNcommandWindowLocation, XmCOMMAND_BELOW_WORKSPACE,
        NULL);

    /* Create a simple MenuBar that contains one menu */
    quit = XmStringCreateLocalized("Quit");
    menubar = XmVaCreateSimpleMenuBar(main_win, "menubar",
        XmVaCASCADEBUTTON, quit, 'Q',
        NULL);


    menu = XmVaCreateSimplePulldownMenu(menubar, "file_menu", 0, 
        quit_cbk,
        XmVaPUSHBUTTON, quit, 'Q', NULL, NULL,
        NULL);

    XmStringFree(quit);

    /*  Manage Menubar */

    XtManageChild(menubar);

    /* create a label text widget that wil be work area  */
    label_str = XmStringCreateLocalized("Work Area");
    
    label = XtVaCreateManagedWidget("main_window",
        xmLabelWidgetClass,   main_win,
        XmNlabelString, label_str,
        XmNwidth, 1000,         
        XmNheight, 800,
        NULL);
        
     XmStringFree(label_str);


    /* Create the command area  */

   
    cmd_wid = XtVaCreateWidget(  "Command",
        xmTextFieldWidgetClass,  main_win,
        XmNmaxLength, MAX_STR_LEN,
       NULL);


    XmTextSetString(cmd_wid,cmd_label);
    XmTextSetInsertionPosition(cmd_wid, cmd_label_length);

    XtAddCallback(cmd_wid, XmNactivateCallback, cmd_cbk);

    XtManageChild(cmd_wid);  


 /* Create the message area  */
   
    msg_wid= XtVaCreateWidget(  "Message:",
        xmTextFieldWidgetClass,   main_win,
        XmNeditable, False,
        XmNmaxLength, MAX_STR_LEN,
        NULL);

    XmTextSetString(msg_wid,msg_label);
  
    XtManageChild(msg_wid);

/* set the label as the work, command and message areas
   of the main window */

    XtVaSetValues(main_win,
        XmNmenuBar,    menubar,
        XmNworkWindow, label,
        XmNcommandWindow, cmd_wid,
        XmNmessageWindow,msg_wid,
        NULL);

    XtManageChild(main_win);
    XtRealizeWidget(top_wid);
    XtAppMainLoop(app);
}

/* execute the command and redirect message area */

void cmd_cbk(Widget cmd_widget, XtPointer *client_data, 
            XmAnyCallbackStruct  *cbs)
{
    char cmd[MAX_STR_LEN],msg[MAX_STR_LEN];
                         									                  
    XmTextGetSubstring(cmd_widget,cmd_label_length, 
            MAX_STR_LEN - cmd_label_length, MAX_STR_LEN ,cmd);        
        
    /* Append input message to Message area */
    
    XmTextReplace(msg_wid,msg_label_length, MAX_STR_LEN,cmd);

    /* Reset Command Area label  and insertion point*/  

    XmTextSetString(cmd_widget, cmd_label);
    XmTextSetInsertionPosition(cmd_widget, cmd_label_length);    
}

void  quit_cbk(Widget w, int item_no)

{    if (item_no == 0) /* the "quit" item */
        exit(0);
}

Exercises

NEEDS SOME


next up previous
Next: Dialog Widgets Up: X WIndow/Motif Programming Previous: Combining Widgets

dave@cs.cf.ac.uk