Menus

  • the Athena widget set provides a set of four widgets that can be used to produce simple menus
  • these widgets can be used to produce pulldown and popup menus
  • a pulldown menu is attached to a menu button, when this button is selected, the menu appears under it
  • a popup menu appears when some combination of events is generated by the user, for example a cntl-button1 event in xterm produces the standard xterm menu
  • pulldown menus are easier to produce so we will start by describing how they work
  • the top widget in a menu is called a SimpleMenu widget, this widget is different from most of the other types of widgets that we have seen so far
  • it is a shell widget, the only other shell widget we have seen is the widget that is created by XtInitialize
  • a shell widget is a special type of widget that knows how to deal with window managers
  • when an application is repositioned or resized by the user, its shell widget receives the new size or position from the window manager, it is responsible for changing the positions and size of all the other widgets in the application
  • the SimpleMenu widget must also be a shell widget, since some types of popup’s must be able to deal with the window manager
  • the SimpleMenu widget is created by calling the XtCreatePopupShell procedure, this procedure has exactly the same calling sequence as XtCreateManagedWidget, the only difference is in the way it operates, unlike other types of widgets, a shell widget is not automatically displayed when it is managed and realized, a shell widget is displayed until it is explicity told to display itself
  • when the shell widget appears on the screen it can monopolize all the input to the application (this depends upon how the widget is defined and poped’up)
  • there are several resources of the SimpleMenu widget that are useful
  • the label resources is used to produce a label at the top of the menu, the menu label cannot be selected, it only services to identify the menu to the user
  • note that this resouce value must be specified when the widget is created, that is it must be passed to the XtCreatePopupShell procedure
  • the bottomMargin and topMargin resources are used to specify the amount of space at the top and bottom of the menu, this space is in pixels
  • if the menuOnScreen resource is true, the entire contents of the menu will always be on the screen regardless of the mouse position when the menu is poped’up
  • normally when a menu is poped’up the mouse cursor will be over top of its label of first entry (if the menu doesn’t have a label), this behavior can be changed by setting the popupOnEntry resource, this resource is set to the widget ID of the menu entry that should be under the mouse cursor when the menu is poped’up
  • there are two types of objects that can appear in a menu, these objects are SmeBSB and SmeLine
  • the SmeBSB object is used to contain the individual item in the menu, each menu item has a label and optionally bitmaps on the left and right side of the label
  • these objects are created by calling XtCreateManagedWidget with the widget ID of the SimpleMenu widget as their parent, the order of the items in the menu will be the same as the order of their creation
  • some of the important resources of the SmeBSB object are described below
  • the label resource is used to specify the text string that is displayed in the menu entry, if this resource isn’t specified the name of the object is used
  • the justify resource specifies how the label is positioned within the menu entry, the possible values for this resource are: XtJustifyLeft, XtJustifyCenter, and XtJustifyRight
  • the leftBitmap and rightBitmap resources specify the bitmaps that appear on the left and right side of the label in the menu entry
  • the leftMargin and rightMargin resources specifies the number of pixels between the label and the left and right margins of the object, if bitmaps are used these resources must be specified correctly or the bitmaps will overlap the label
  • the SmeBSB object has a callback list, all the procedures on this callback list are executed when the menu item is selected by the user
  • the SmeLine object is used as a spacer between items in a menu, this object is basically a horizontal line that is drawn from one side of the menu to the other
  • there are two resources of this widget that are particularly useful
  • the lineWidth resource is the width (in pixels) of the line that is drawn
  • the stipple resource is a bitmap that is used is drawing the line, it forms a pattern along the length of the line and makes the menu visually more interesting
  • the SmeLine object doesn’t respond to events and doesn’t have a callback resource
  • the MenuButton widget is a special type of Command widget that is used to construct pulldown menus
  • typically a pulldown menu consists of a row of MenuButton’s usually managed by a form widget, each of these widgets has a menu associated with it
  • when the MenuButton widget is selected (by pressing the left mouse button over top of the widget) the corresponding menu appears on the screen
  • with the mouse button still pressed the user can drag the mouse down the length of the menu, when the user releases the menu button, the menu underneath it is selected and the menu is removed from the screen
  • the MenuButton widget has all the resources of the command widget, plus the menuName resource
  • the menuName resource contains the name of the menu that will be poped’up when the button is selected, this menu will appear directly below the MenuButton widget
  • the value of the menuName resource is a text string the is the same as the menu’s name (the text string that is passed as the first parameter to XtCreatePopupShell), the button must search the widget tree for this widget when it is selected
  • it is a good idea to make the menu a child of the MenuButton widget, this simplifies the search process
  • the following example shows how a pulldown menus can be constructed
  • this program has three pulldown menus, and there are three MenuButton widgets for these menus inside of a form widget
  • the first menu contains three menu item, plus a quit item, a line is used to separate the first three items from the quit item
  • the second menu has a menu label
  • the third menu has three menu items and a quit item
  • when you run this program you will note that menus are longer than the main widget for the application, when they are displayed they overlap both the current application and whatever application is below it on the screen
  • since each menu is a separate shell the application shell is not resized when the menu is displayed
      /*******************************************************************
       *
       *                         menu1
       *
       *  This simple program shows how menus widgets are used,m
       *  this example program uses a menu bar for the menus.  This
       *  is the easiest way to set up an Athena menu.
       *
       *****************************************************************/
      #include <X11/StringDefs.h>
      #include <X11/Intrinsic.h>
      #include <X11/Xaw/Form.h>
      #include <X11/Xaw/SimpleMenu.h>
      #include <X11/Xaw/SmeBSB.h>
      #include <X11/Xaw/MenuButton.h>
      #include <X11/Xaw/SmeLine.h>
    
    
      void quit_callback(w, client, call)
      Widget w;
      XtPointer client;
      XtPointer call; {
    
           exit(0);
    
      }
    
    
      void print_string(w, client, call)
      Widget w;
      char *client;
      XtPointer call; {
    
           printf("%s\n",client);
    
      }
    
      main(argc,argv)
      int argc;
      char **argv; {
           Widget toplevel;
           Widget form;
           Widget quit;
           Widget button1;
           Widget button2;
           Widget button3;
           Widget menu1;
           Widget menu2;
           Widget menu3;
           Widget line1;
           Widget line2;
           Widget entry;
           int n;
           Arg wargs[10];
    
    
    
           toplevel = XtInitialize(argv[0],"menu",NULL,0,
    		 &argc;,argv);
    
           form = XtCreateManagedWidget("form", formWidgetClass,
    		 toplevel, NULL, 0);
    
           button1 = XtCreateManagedWidget("button1", menuButtonWidgetClass,
    		 form, NULL, 0);
    
           n = 0;
           XtSetArg(wargs[n], XtNmenuName, "menu1"); n++;
           XtSetValues(button1, wargs, n);
    
           button2 = XtCreateManagedWidget("button2", menuButtonWidgetClass,
    		 form, NULL, 0);
    
           n = 0;
           XtSetArg(wargs[n], XtNfromHoriz, button1); n++;
           XtSetArg(wargs[n], XtNmenuName, "menu2"); n++;
           XtSetValues(button2, wargs, n);
    
           button3 = XtCreateManagedWidget("button3", menuButtonWidgetClass,
    		 form, NULL, 0);
    
           n = 0;
           XtSetArg(wargs[n], XtNfromHoriz, button2); n++;
           XtSetArg(wargs[n], XtNmenuName, "menu3"); n++;
           XtSetValues(button3, wargs, n);
    
           /*
    	*  create the first pull down menu
    	*/
    
           menu1 = XtCreatePopupShell("menu1", simpleMenuWidgetClass,
    		 button1, NULL, 0);
    
           entry = XtCreateManagedWidget("one", smeBSBObjectClass,
    		 menu1, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "one");
    
           entry = XtCreateManagedWidget("two", smeBSBObjectClass,
    		 menu1, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "two");
    
           entry = XtCreateManagedWidget("three", smeBSBObjectClass,
    		 menu1, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "three");
    
           line1 = XtCreateManagedWidget("line1", smeLineObjectClass,
    		 menu1, NULL, 0);
    
           quit = XtCreateManagedWidget("quit", smeBSBObjectClass,
    		 menu1, NULL, 0);
    
           XtAddCallback(quit, XtNcallback, quit_callback, NULL);
    
           /*
    	*  create the second pull down menu
    	*/
    
           n = 0;
           XtSetArg(wargs[n], XtNlabel, "menu label"); n++;
    
           menu2 = XtCreatePopupShell("menu2", simpleMenuWidgetClass,
    		 button2, wargs, n);
    
           line2 = XtCreateManagedWidget("line2", smeLineObjectClass,
    		 menu2, NULL, 0);
    
           quit = XtCreateManagedWidget("quit", smeBSBObjectClass,
    		 menu2, NULL, 0);
    
           XtAddCallback(quit, XtNcallback, quit_callback, NULL);
    
           /*
    	*  create the third pull down menu
    	*/
    
           menu3 = XtCreatePopupShell("menu3", simpleMenuWidgetClass,
    		 button3, NULL, 0);
    
           entry = XtCreateManagedWidget("four", smeBSBObjectClass,
    		 menu3, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "four");
    
           entry = XtCreateManagedWidget("five", smeBSBObjectClass,
    		 menu3, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "five");
    
           entry = XtCreateManagedWidget("six", smeBSBObjectClass,
    		 menu3, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "six");
    
           quit = XtCreateManagedWidget("quit", smeBSBObjectClass,
    		 menu3, NULL, 0);
    
           XtAddCallback(quit, XtNcallback, quit_callback, NULL);
    
           XtRealizeWidget(toplevel);
    
           XtMainLoop();
    
      }
    

Popup Menus and Translations

  • in order to understand how popup menus work you need to understand translations
  • each widget has a translation table, this table specifies how certain events are treated by the widget
  • each entry in the table consists of a sequence of events and a list of procedures that are executed when those events occur
  • the documentation for a widget contains a list of the procedures, which are usually called actions, that are defined by the widget, and the standard translatations that are provided by the widget
  • for example, some fo the action procedures for the Command widget are:
        highlight(condition) - highlights the widget
    
        unhighlist - turns off the widget highlighting
    
        notify - call all the procedures on the callback list
    
  • the standard translations for this widget are:
      ;EnterWindow>:    highlight()
      ;LeaveWindow>:    reset()
      ;Btn1Down>:       set()
      ;Btn2Down>:       notify() unset()
    
  • the programmer can augment or replace the standard translations that are provided by a widget
  • this allows that programmer to change the widget’s behavior, or add a new behavior that is application dependent
  • a entry in a translation table starts with a sequence of one or more events, each event consists of a list of modifiers, followed by an event, followed by event detail
  • some of the more useful events, and their synonyms are
        Event             Synonym
    
        KeyPress          Key, KeyDown
        KeyRelease        KeyUp
        ButtonPress       BtnDown
        ButtonRelease     BtnUp
        MotionNotify      Motion, PtrMoved, MouseMoved
        EnterNotify       Enter, EnterWindow
        LeaveNotify       Leave, LeaveWindow
    
  • some of the more common modifiers are:
        None
        Any
        Cntl
        Shift
        Lock
        Meta
        Button1
        Button2
        Button3
    
  • the ~ can be used before a modifier to indicate “not”, so ~Cntl specifies that the control key isn’t pressed
  • in an event specification if a modifier key isn’t specified it is assumed to be a “don’t care”, the event will occur whether the key is pressed or not
  • so Shift <ButtonPress> will occur when the shift key is pressed and a mouse button is pressed, this event will occur if the cntl key is pressed or released, X only examines the state of the shift key, it doesn’t look at any of the other modifiers
  • if we didn’t want the event to occur if the cntl key was pressed then we could specify it in the following way:
           Shift ~Cntl <ButtonPress>
    
  • the ! at the beginning of an event states that only the modifier keys in the event specification can be pressed, all the other modifier keys must be released, so the event
           ! Shift <ButtonPress>
    
    will only occur when the shift key is pressed, and all the other modifier keys are released
  • the detail follows the event name, in the case of KeyPress and KeyRelease events this is the key on the keyboard (not the corresponding character) that has been pressed
  • so <KeyPress> 4 and <KeyPress> $ are in fact the same event, since they only differ by the state of the Shift modifier, to make these distinct events you would need to use ~ Shift <KeyPress> 4 Shift <KeyPress> 4
  • in the case of ButtonPress and ButtonRelease events the detail is the number of the button that was pressed or released
  • there are a number of abbreviations for combinations of modifiers, events, and detail, most of the them make sense when you see them
  • the event part of a translation is separated from the actions by a :, the actions consist of a list of procedure calls, with each of the calls separated by one or more spaces, each of these procedures must have been previously defined by the widget, the programmer can’t specify an arbitrary procedure in the application
  • a translation table consists of one or more translation table entries, the order of these entries is important, when an event is generated Xt performs a linear search, starting at the first entry, for an entry that matches the event, the first entry that matches the event is the one that is used
  • the most specific events must be placed at the front of the table, otherwise they won’t be recognized by Xt
  • for example:
         ;ButtonPress> : action1()
         ;ButtonPress> 1 : action2()
           Cntl <ButtonPress> 1 : action3()
    
  • in this case action1() will always be executed, even if the user presses both cntl and the left mouse button, the first line matches this key sequence, so it will be used
  • the correct order for this translation table is:
         ;ButtonPress> : action1()
         ;ButtonPress> 1 : action2()
           Cntl <ButtonPress> 1 : action3()
    
  • the translation table is a resource, this resource has a complicated structure, so a number of procedure are provided to make specifying translation easier
  • programmer specify translation tables in the form of text strings, there is one line in the text string for each entry in the translation table, then the XtParseTranslationTable procedure is used to convert the text string into translation table
  • the XtParseTranslationTable procedure has the following declaration
        XtTranslations XtParseTranslationTable(table)
        char *table;
    
  • so returning to the translations for the command widget, we could specify its translation table in the following way:
        char table[] = " <EnterWindow> : highlight()\n\
      ;LeaveWindow> : reset()\n\
      ;Btn1Down> : set()\n\
      ;Btn2Down> : notify() unset()";
    
  • there are two procedures that can be used to add new translations to an existing translation table, the declarations of these procedures are:
        XtAugmentTranslations(w, translations)
        Widget         w;
        XtTranslations translations;
    
        XtOverrideTranslations(w, translations)
        Widget         w;
        XtTranslations translations;
    
  • both of these procedures add translations to an existing translations table, they differ in how they treat translations that have the same left hand side
  • in the case of XtAugmentTranslations if a new translation has a left side that matches an existing translation then the new translation is ignored
  • with XtOverrideTranslations if the new translation has a left side that matches an existing translation then the new translation replaces the existing translation
  • all the translations in a translation table can be removed by calling the following procedure
        XtUninstallTranslations(w)
        Widget w;
    
  • to completely replace all the translations in a widget you can either uninstall all its translations can either of the above procedure, or directly set the widget’s translations resouce to the output from XtParseTranslationTable
  • now we can examine how popup menus work
  • the SimpleMenu widget defines two special actions that are used for popup menus
  • the XawPositionSimpleMenu action takes one parameter, the name of a menu and positions it on the screen, it takes the current mouse position and by default places the upper left corner of the menu at that position
  • this default action can be changed by specifying values for the menuOnScreen and popupOnEntry resources for the menu widget
  • XtMenuPopup action takes the name of a menu as a parameter and displays that menu on the screen, the menu remains on the screen until the user generates ButtonRelease event, when this event occurs the menu disappears from the screen, if the mouse was overtop of a menu entry when this event was generated, that menu item is selected, and the procedures on its callback list are called
  • the example program shows how these actions can be used to popup menus, when the mouse buttons are pressed
      /*******************************************************************
       *
       *                         menu1
       *
       *  This simple program shows how menus widgets are used,m
       *  this example program uses a menu bar for the menus.  This
       *  is the easiest way to set up an Athena menu.
       *
       *****************************************************************/
      #include <X11/StringDefs.h>
      #include <X11/Intrinsic.h>
      #include <X11/Xaw/Box.h>
      #include <X11/Xaw/SimpleMenu.h>
      #include <X11/Xaw/SmeBSB.h>
      #include <X11/Xaw/MenuButton.h>
      #include <X11/Xaw/SmeLine.h>
      #include <X11/X.h>
    
    
      Widget menu1;
      Widget menu2;
      Widget menu3;
    
      char translations[] =
      "! <Btn1Down> : XawPositionSimpleMenu(menu1) XtMenuPopup(menu1)\n\
      ;Btn2Down> : XawPositionSimpleMenu(menu2) XtMenuPopup(menu2)"
    
    
      void quit_callback(w, client, call)
      Widget w;
      XtPointer client;
      XtPointer call; {
    
           exit(0);
    
      }
    
    
      void print_string(w, client, call)
      Widget w;
      char *client;
      XtPointer call; {
    
           printf("%s\n",client);
    
      }
    
    
      main(argc,argv)
      int argc;
      char **argv; {
           Widget toplevel;
           Widget box;
           Widget quit;
           Widget line1;
           Widget line2;
           Widget entry;
           int n;
           Arg wargs[10];
    
           toplevel = XtInitialize(argv[0],"menu",NULL,0,
    		 &argc;,argv);
    
           box = XtCreateManagedWidget("box", boxWidgetClass,
    		 toplevel, NULL, 0);
    
           XawSimpleMenuAddGlobalActions(XtWidgetToApplicationContext(box));
    
           n = 0;
           XtSetArg(wargs[n], XtNwidth, 200); n++;
           XtSetArg(wargs[n], XtNheight, 200); n++;
           XtSetValues(box, wargs, n);
           XtOverrideTranslations(box, XtParseTranslationTable(
    		 translations));
    
           /*
    	*  create the first pull down menu
    	*/
    
           menu1 = XtCreatePopupShell("menu1", simpleMenuWidgetClass,
    		 box, NULL, 0);
    
           entry = XtCreateManagedWidget("one", smeBSBObjectClass,
    		 menu1, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "one");
    
           entry = XtCreateManagedWidget("two", smeBSBObjectClass,
    		 menu1, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "two");
    
           entry = XtCreateManagedWidget("three", smeBSBObjectClass,
    		 menu1, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "three");
    
           line1 = XtCreateManagedWidget("line1", smeLineObjectClass,
    		 menu1, NULL, 0);
    
           quit = XtCreateManagedWidget("quit", smeBSBObjectClass,
    		 menu1, NULL, 0);
    
           XtAddCallback(quit, XtNcallback, quit_callback, NULL);
    
           /*
    	*  create the second pull down menu
    	*/
    
    
           n = 0;
           XtSetArg(wargs[n], XtNlabel, "menu label"); n++;
    
           menu2 = XtCreatePopupShell("menu2", simpleMenuWidgetClass,
    		 box, wargs, n);
    
           line2 = XtCreateManagedWidget("line2", smeLineObjectClass,
    		 menu2, NULL, 0);
    
           quit = XtCreateManagedWidget("quit", smeBSBObjectClass,
    		 menu2, NULL, 0);
    
           XtAddCallback(quit, XtNcallback, quit_callback, NULL);
    
           /*
    	*  create the third pull down menu
    	*/
    
           menu3 = XtCreatePopupShell("menu3", simpleMenuWidgetClass,
    		 box, NULL, 0);
    
           entry = XtCreateManagedWidget("four", smeBSBObjectClass,
    		 menu3, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "four");
    
           entry = XtCreateManagedWidget("five", smeBSBObjectClass,
    		 menu3, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "five");
    
           entry = XtCreateManagedWidget("six", smeBSBObjectClass,
    		 menu3, NULL, 0);
    
           XtAddCallback(entry, XtNcallback, print_string, "six");
    
           quit = XtCreateManagedWidget("quit", smeBSBObjectClass,
    		 menu3, NULL, 0);
    
           XtAddCallback(quit, XtNcallback, quit_callback, NULL);
    
           XtRealizeWidget(toplevel);
    
           XtMainLoop();
    
      }