Next: MenuBar Up: Working with dialogs Previous: Prompt Dialog

Selection Box Dialogs

Motif provides a dialog box that can be used to let the user select an item from a list of possible items. This is called a selection box. The use of a selection box is nearly identical to the use of a prompt dialog, except that some extra code is needed to set up the list of possible items for the user to choose from. The following code demonstrates the process:


        #include <X11/Intrinsic.h>
        #include <Xm/Xm.h>
        #include <Xm/PushB.h>
        #include <Xm/SelectioB.h>

        #define OK      1
        #define CANCEL  2

        Widget toplevel, dialog;
        Boolean first_time=True;
        char *animals[]={"cat","dog","cow","goat","horse","mouse","pig",
                        "sheep","rat","donkey","elephant","squirrel"};

        void dialogCB(Widget w,
                   int client_data,
                   XmAnyCallbackStruct *call_data)
        {
          char *s;
          XmSelectionBoxCallbackStruct *selection;

          switch (client_data)
          {
                case OK:
                        selection=(XmSelectionBoxCallbackStruct *) call_data;
                        XmStringGetLtoR(selection->value, 
                                XmSTRING_DEFAULT_CHARSET, &s);
                        printf("string='%s'\n",s);
                        XtFree(selection);
                        break;
                case CANCEL:
                        printf("CANCEL selected\n");
                        break;
          }
          XtUnmanageChild(w);
        }

        void buttonCB(Widget w,
                   caddr_t client_data,
                   XmAnyCallbackStruct *call_data)
        {
          Arg al[10];
          int ac;
          XmString list[100];
          int list_cnt;

          /* In the version of Motif I use, there is a bug that causes the
             selection box's list to be unchangeable once it has been loaded.
             This means that you can load it the first time, but after that
             the list cannot be changed to anything else. The only way I have
             found to get around the problem is to destroy the widget each
             time, recreate it, and then reload it with the new list. You might
             try it without recreating the widget each time and see if it works 
             for you. If it does, then you should create the widget once 
             in the main program instead of every time, and you can also 
             remove this destruction code.*/
          if (first_time)
            first_time=False;
          else
            XtDestroyWidget(dialog);

          /* create the selection box widget */
          ac = 0;
          /* the following line is commented out to make a point. Read
             more about it in the text description.                    */
          /* XtSetArg(al[ac],XmNautoUnmanage,False); ac++;             */
          XtSetArg(al[ac], XmNselectionLabelString, XmStringCreateLtoR
                ("Pick an animal. ", XmSTRING_DEFAULT_CHARSET));  ac++;
          dialog = XmCreateSelectionDialog(toplevel, "dialog", al, ac);
          XtAddCallback (dialog, XmNokCallback, dialogCB, OK);
          XtAddCallback (dialog, XmNcancelCallback, dialogCB, CANCEL);
          XtUnmanageChild(XmSelectionBoxGetChild(dialog,
                XmDIALOG_HELP_BUTTON));

          /* create an appopriate list to give to the sb widget. */
          for (list_cnt=0; list_cnt<XtNumber(animals); list_cnt++) 
            list[list_cnt]= XmStringCreate(animals[list_cnt],
                XmSTRING_DEFAULT_CHARSET);

          /* set the list into the sb widget's list resources. */
          ac=0;
          XtSetArg(al[ac], XmNlistItems, list); ac++;
          XtSetArg(al[ac],XmNlistItemCount, list_cnt); ac++;
          XtSetArg(al[ac],XmNmustMatch,True); ac++;
          XtSetValues(dialog, al, ac);
          XtManageChild(dialog);
        }

        main(int argc, char *argv[])
        {
          Widget button;
          Arg al[10];
          int ac;

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

          ac=0;
          XtSetArg(al[ac],XmNlabelString, XmStringCreate("Push Me",
                XmSTRING_DEFAULT_CHARSET)); ac++;
          button=XtCreateManagedWidget("label",xmPushButtonWidgetClass,
                toplevel,al,ac);
          XtAddCallback (button, XmNactivateCallback, buttonCB, NULL);

          XtRealizeWidget(toplevel);
          XtMainLoop();
        }

This code has a slightly different style than previous code, in that it uses several global variables. The use of globals, especially for the dialog widget, insures that the variable exists even though it is created inside the function (if it were declared as a local to the function, then there are problems that arise from the loss of local variables once a function returns). The "animals" array here represents some array or list of strings that you would want loaded into the selection box. [The compiler I am using requires an initialized array to be global.] Toplevel was made global to avoid having to pass it around. In the code there is also a strange thing occuring in buttonCB: the widget is destroyed and recreated each time it is used. This is done to avoid a bug in the copy of Motif I am currently using. The comment describes the bug - your copy of motif may be fine, in which case you can simply create the widget once in main as was done with the prompt dialog in the previous example.

The lower part of buttonCB is involved with creating the list for the selection box to display, and then passing that list into the appropriate resources of the selection box. The list of strings is simply translated into an array of XmStrings. [You might want to determine the size of your list and then XtMalloc an appropriately sized array, but I'm just using a fixed size array here.] Once the XmString array is formed, it is passed to the correct resource using an XtSetValues call.

The mustMatch resource is set here just to make you aware of it's existence. The selection box contains a field that the user can use to enter a word not in the list. But what if the items in the list are the only valid entries? Then you don't want the user typing in garbage. The mustMatch resource forces the user to choose something that matches an entry in the item list. Try running the code as is, and type some garbage into the string area of the selection box. The selection box will go away, and the callback will not get triggered. Now change the code and set mustMatch to false. Now when you type in garbage, the garbage will be returned.

When mustMatch is true, the fact that the dialog box goes away on it's own can be rather disconcerting. If the user enters garbage, the widget goes away without any notification to the program (through the callback), nor a message to the user. What you would like instead, when mustMatch is true, is to have the selection box just sit there waiting for a valid response, and not accepting anything other than a valid response.

To create this effect, you can make use of the autoUnmanage resource that is part of a bulletinBoard widget (the selection box is made up of a bunch of separate widgets attached to a bulletinBoard - this makes sense if you think about it). It turns out that this autoUnmanage resource (when true) causes a bulletin board to automatically unmanage (ie - dissappear) whenever the OK, Cancel or Help button is pressed. Here we don't want that to happen. To change the behavior, uncomment the line that sets the autoUnmanage resource and rerun the program (making sure that mustMatch is true). Now when you enter garbage, the dialog box will stick on the screen waiting for appropriate input. [An interesting sidelight - try moving the autoUnmanage line down to where mustMatch is set and rerun it. It will have no effect. AutoUnmanage only works if it is set False at the time of widget CREATION.]

Now you know enough to begin creating applications. The next three tutorials will demonstrate three applications, and introduce a few more widgets in the process.



Next: MenuBar Up: Working with dialogs Previous: Prompt Dialog


morbe@enstb.enst-bretagne.fr