From: jan@ECHO.CANBERRA.EDU.AU (Jan Newmarch)
Newsgroups: comp.windows.x.motif
Subject: Motif FAQ (Part 3 of 5)
Keywords: FAQ question answer
Date: 17 Aug 92 14:51:52 GMT
Expires: 17 Oct 92 04:31:54 GMT
Reply-To: jan@ise.canberra.edu.au (Jan Newmarch)
Followup-To: comp.windows.x.motif
Distribution: inet
Organization: The Internet

Archive-name: motif-faq/part3
Last-modified: Tue July 14 1992
Version: 2.6



-------------------------------------------------------------------------------
Subject: 54) TOPIC: FORM WIDGET


-------------------------------------------------------------------------------
Subject: 55) Why don't labels in a Form resize when the label is changed?  I've
got some labels in a form. The labels don't resize whenever the label string
resource is changed. As a result, the operator has to resize the window to see
the new label contents. I am using Motif 1.1.

Answer: This problem may happen to any widget inside a Form widget. The problem
was that the Form will resize itself when it gets geometry requests from its
children. If its preferred size is not allowed, the Form will disallow all
geometry requests from its children. The workaround is that you should set any
ancestor of the Form to be resizable. For the shell which contains the Form you
should set the shell resource XmNallowShellResize to be True (by default, it is
set to FALSE).  There is currently an inconsistency on how resizing is being
done, and it may get fixed in Motif 1.2.

From db@sunbim.be (Danny Backx)

Basically what you have to do is set the XmNresizePolicy on the Form to
XmRESIZE_NONE.  The facts seem to be that XmRESIZE_NONE does NOT mean "do not
allow resizes".  You may also have to set XmNresizable on the form to True.

-------------------------------------------------------------------------------
Subject: 56) How can I center a widget in a form?

Answer: One of Motif's trickier questions.  The problems are that: Form gives no
support for centering, only for edge attachments, and the widget must stay in
the center if the form or the widget is resized.  Just looking at horizontal
centering (vertical is similar) some solutions are:

 a.  Use the table widget instead of Form.

 b.  A hack free solution is from Dan Heller:

     /* Written by Dan Heller.  Copyright 1991, O'Reilly && Associates.
      * This program is freely distributable without licensing fees and
      * is provided without guarantee or warranty expressed or implied.
      * This program is -not- in the public domain.  This program is
      * taken from the Motif Programming Manual, O'Reilly Volume 6.
      */

     /* corners.c -- demonstrate widget layout management for a
      * BulletinBoard widget.  There are four widgets each labeled
      * top-left, top-right, bottom-left and bottom-right.  Their
      * positions in the bulletin board correspond to their names.
      * Only when the widget is resized does the geometry management
      * kick in and position the children in their correct locations.
      */
     #include <Xm/BulletinB.h>
     #include <Xm/PushBG.h>

     char *corners[] = {
         "Top-Left", "Top-Right", "Bottom-Left", "Bottom-Right",
     };

     static void resize();

     main(argc, argv)
     int argc;
     char *argv[];
     {
         Widget toplevel, bboard;
         XtAppContext app;
         XtActionsRec rec;
         int i;

         /* Initialize toolkit and create toplevel shell */
         toplevel = XtVaAppInitialize(&app, "Demos", NULL, 0,
             &argc, argv, NULL, NULL);

         /* Create your standard BulletinBoard widget */
         bboard = XtVaCreateManagedWidget("bboard",
             xmBulletinBoardWidgetClass, toplevel, NULL);

         /* Set up a translation table that captures "Resize" events
          * (also called ConfigureNotify or Configure events).  If the
          * event is generated, call the function resize().
          */
         rec.string = "resize";
         rec.proc = resize;
         XtAppAddActions(app, &rec, 1);
         XtOverrideTranslations(bboard,
             XtParseTranslationTable("<Configure>: resize()"));

         /* Create children of the dialog -- a PushButton in each corner. */
         for (i = 0; i < XtNumber(corners); i++)
             XtVaCreateManagedWidget(corners[i],
                 xmPushButtonGadgetClass, bboard, NULL);

         XtRealizeWidget(toplevel);
         XtAppMainLoop(app);
     }

     /* resize(), the routine that is automatically called by Xt upon the
      * delivery of a Configure event.  This happens whenever the widget
      * gets resized.
      */
     static void
     resize(w, event, args, num_args)
     CompositeWidget w;   /* The widget (BulletinBoard) that got resized */
     XConfigureEvent *event;  /* The event struct associated with the event */
     String args[]; /* unused */
     int *num_args; /* unused */
     {
         WidgetList children;
         int width = event->width;
         int height = event->height;
         Dimension w_width, w_height;
         short margin_w, margin_h;

         /* get handle to BulletinBoard's children and marginal spacing */
         XtVaGetValues(w,
             XmNchildren, &children,
             XmNmarginWidth, &margin_w,
             XmNmarginHeight, &margin_h,
             NULL);

         /* place the top left widget */
         XtVaSetValues(children[0],
             XmNx, margin_w,

             XmNy, margin_h,
             NULL);

         /* top right */
         XtVaGetValues(children[1], XmNwidth, &w_width, NULL);

         /* To Center a widget in the middle of the BulletinBoard (or Form),
          * simply call:
          *   XtVaSetValues(widget,
               XmNx,    (width - w_width)/2,
               XmNy,    (height - w_height)/2,
               NULL);
          * and return.
          */
         XtVaSetValues(children[1],
             XmNx, width - margin_w - w_width,
             XmNy, margin_h,
             NULL);
         /* bottom left */
         XtVaGetValues(children[2], XmNheight, &w_height, NULL);
         XtVaSetValues(children[2],

             XmNx, margin_w,
             XmNy, height - margin_h - w_height,
             NULL);
         /* bottom right */
         XtVaGetValues(children[3],
             XmNheight, &w_height,
             XmNwidth, &w_width,
             NULL);
         XtVaSetValues(children[3],
             XmNx, width - margin_w - w_width,
             XmNy, height - margin_h - w_height,
             NULL);
     }

 c.  No uil solution has been suggested, because of the widget size problem

-------------------------------------------------------------------------------
Subject: 57) How do I line up two columns of widgets of different types?  I have
a column of say label widgets, and a column of text widgets and I want to have
them lined up horizontally. The problem is that they are of different heights.
Just putting them in a form or rowcolumn doesn't line them up properly because
the label and text widgets are of different height.

If you want the geometry to look like this

          -------------------------------------
         |          -------------------------- |
         |a label  |Some text                 ||
         |          -------------------------- |
                           ------------------- |
         |a longer label  |Some more text     ||
         |                 ------------------- |
         |                    ---------------- |
         |a very long label  |Even more text  ||
         |                    ---------------- |
          -------------------------------------

try

/* Written by Dan Heller.  Copyright 1991, O'Reilly && Associates.
 * This program is freely distributable without licensing fees and
 * is provided without guarantee or warranty expressed or implied.
 * This program is -not- in the public domain.  This program is
 * taken from the Motif Programming Manual, O'Reilly Volume 6.
 */

/* text_form.c -- demonstrate how attachments work in Form widgets.
 * by creating a text-entry form type application.
 */

#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/LabelG.h>
#include <Xm/Text.h>
#include <Xm/Form.h>

char *prompts[] = {
    "Name:", "Phone:", "Address:",
    "City:", "State:", "Zip:",
};

main(argc, argv)
int argc;
char *argv[];
{
    Widget toplevel, mainform, subform, label, text;
    XtAppContext app;
    char buf[32];
    int i;

    toplevel = XtVaAppInitialize(&app, "Demos", NULL, 0,
        &argc, argv, NULL, NULL);

    mainform = XtVaCreateWidget("mainform",
        xmFormWidgetClass, toplevel,
        NULL);

    for (i = 0; i < XtNumber(prompts); i++) {
        subform = XtVaCreateWidget("subform",
            xmFormWidgetClass,   mainform,
            /* first one should be attached for form */
            XmNtopAttachment,    i? XmATTACH_WIDGET : XmATTACH_FORM,
            /* others are attached to the previous subform */
            XmNtopWidget,        subform,
            XmNleftAttachment,   XmATTACH_FORM,
            XmNrightAttachment,  XmATTACH_FORM,
            NULL);
        label = XtVaCreateManagedWidget(prompts[i],
            xmLabelGadgetClass,  subform,
            XmNtopAttachment,    XmATTACH_FORM,
            XmNbottomAttachment, XmATTACH_FORM,
            XmNleftAttachment,   XmATTACH_FORM,
            XmNalignment,        XmALIGNMENT_BEGINNING,
            NULL);
        sprintf(buf, "text_%d", i);
        text = XtVaCreateManagedWidget(buf,
            xmTextWidgetClass,   subform,
            XmNtopAttachment,    XmATTACH_FORM,
            XmNbottomAttachment, XmATTACH_FORM,
            XmNrightAttachment,  XmATTACH_FORM,
            XmNleftAttachment,   XmATTACH_WIDGET,
            XmNleftWidget,       label,
            NULL);
        XtManageChild(subform);
    }
    /* Now that all the forms are added, manage the main form */
    XtManageChild(mainform);

    XtRealizeWidget(toplevel);
    XtAppMainLoop(app);
}

If you resize horizontally it stretches the text widgets.  If you resize
vertically it leaves space under the bottom (if you don't resize, this is not
problem).

If you want the text widgets to be lined up on the left, as in

          ----------------------------------------
         |                    ------------------- |
         |          a label  |Some text          ||
         |                    ------------------- |
                              ------------------- |
         |   a longer label  |Some more text     ||
         |                    ------------------- |
         |                    ------------------- |
         |a very long label  |Even more text     ||
         |                    ------------------- |
          ----------------------------------------

try this

/* Written by Dan Heller.  Copyright 1991, O'Reilly && Associates.
 * This program is freely distributable without licensing fees and
 * is provided without guarantee or warranty expressed or implied.
 * This program is -not- in the public domain.  This program is
 * taken from the Motif Programming Manual, O'Reilly Volume 6.
 */

/* text_entry.c -- This demo shows how the RowColumn widget can be
 * configured to build a text entry form.  It displays a table of
 * right-justified Labels and Text widgets that extend to the right
 * edge of the Form.
 */
#include <Xm/LabelG.h>
#include <Xm/RowColumn.h>
#include <Xm/Text.h>

char *text_labels[] = {
    "Name:", "Phone:", "Address:", "City:", "State:", "Zip:",
};

main(argc, argv)
int argc;
char *argv[];
{
    Widget toplevel, rowcol;
    XtAppContext app;
    char buf[8];
    int i;

    toplevel = XtVaAppInitialize(&app, "Demos", NULL, 0,
        &argc, argv, NULL, NULL);

    rowcol = XtVaCreateWidget("rowcolumn",
        xmRowColumnWidgetClass, toplevel,
        XmNpacking,        XmPACK_COLUMN,
        XmNnumColumns,     XtNumber(text_labels),
        XmNorientation,    XmHORIZONTAL,
        XmNisAligned,      True,
        XmNentryAlignment, XmALIGNMENT_END,
        NULL);

    /* simply loop thru the strings creating a widget for each one */
    for (i = 0; i < XtNumber(text_labels); i++) {
        XtVaCreateManagedWidget(text_labels[i],
            xmLabelGadgetClass, rowcol,
            NULL);
        sprintf(buf, "text_%d", i);
        XtVaCreateManagedWidget(buf,
            xmTextWidgetClass, rowcol,
            NULL);
    }

    XtManageChild(rowcol);
    XtRealizeWidget(toplevel);
    XtAppMainLoop(app);
}

This makes all objects exactly the same size.  It does not resize in nice ways.

If you want the text widgets lined up on the left, and the labels to be the size
of the longest string, resizing nicely both horizontally and vertically, as in

         -------------------------------------
        |                    ---------------- |
        |          a label  |Some text       ||
        |                    ---------------- |
                             ---------------- |
        |   a longer label  |Some more text  ||
        |                    ---------------- |
        |                    ---------------- |
        |a very long label  |Even more text  ||
        |                    ---------------- |
         -------------------------------------



Answer: Do this: to get the widgets lined up horizontally, use a form but place
the widgets using XmATTACH_POSITION.  In the example, attach the top of the
first label to the form, the bottomPosition to 33 (33% of the height).  Attach
the topPosition of the second label to 33 and the bottomPosition to 66.  Attach
the topPosition of the third label to 66 and the bottom of the label to the
form.  Do the same with the text widgets.

To get the label widgets lined up vertically, use the right attachment of
XmATTACH_OPPOSITE_WIDGET: starting from the one with the longest label, attach
widgets on the right to each other. In the example, attach the 2nd label to the
third, and the first to the second.  To get the text widgets lined up, just
attach them on the left to the labels.  To get the text in the labels aligned
correctly, use XmALIGNMENT_END for the XmNalignment resource.

        /* geometry for label 2
        */
        n = 0;
        XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++;
        XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
        XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
        XtSetArg (args[n], XmNtopAttachment, XmATTACH_POSITION); n++;
        XtSetArg (args[n], XmNtopPosition, 66); n++;
        XtSetValues (label[2], args, n);

        /* geometry for label 1
        */
        n = 0;
        XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++;
        XtSetArg (args[n], XmNbottomAttachment, XmATTACH_POSITION); n++;
        XtSetArg (args[n], XmNbottomPosition, 66); n++;
        XtSetArg (args[n], XmNtopAttachment, XmATTACH_POSITION); n++;
        XtSetArg (args[n], XmNtopPosition, 33); n++;
        XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
        XtSetArg (args[n], XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
        XtSetArg (args[n], XmNrightWidget, label[2]); n++;
        XtSetValues (label[1], args, n);

        /* geometry for label 0
        */
        n = 0;
        XtSetArg (args[n], XmNalignment, XmALIGNMENT_END); n++;
        XtSetArg (args[n], XmNbottomAttachment, XmATTACH_POSITION); n++;
        XtSetArg (args[n], XmNbottomPosition, 33); n++;
        XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
        XtSetArg (args[n], XmNleftAttachment, XmATTACH_FORM); n++;
        XtSetArg (args[n], XmNrightAttachment, XmATTACH_OPPOSITE_WIDGET); n++;
        XtSetArg (args[n], XmNrightWidget, label[1]); n++;
        XtSetValues (label[0], args, n);

        /* geometry for text 0
        */
        n = 0;
        XtSetArg (args[n], XmNtopAttachment, XmATTACH_FORM); n++;
        XtSetArg (args[n], XmNbottomAttachment, XmATTACH_POSITION); n++;
        XtSetArg (args[n], XmNbottomPosition, 33); n++;
        XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
        XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
        XtSetArg (args[n], XmNleftWidget, label[0]); n++;
        XtSetValues (text[0], args, n);

        /* geometry for text 1
        */
        XtSetArg (args[n], XmNtopAttachment, XmATTACH_POSITION); n++;
        XtSetArg (args[n], XmNtopPosition, 33); n++;
        XtSetArg (args[n], XmNbottomAttachment, XmATTACH_POSITION); n++;
        XtSetArg (args[n], XmNbottomPosition, 66); n++;
        XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
        XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
        XtSetArg (args[n], XmNleftWidget, label[1]); n++;
        XtSetValues (text[1], args, n);

        /* geometry for text 2
        */
        XtSetArg (args[n], XmNtopAttachment, XmATTACH_POSITION); n++;
        XtSetArg (args[n], XmNtopPosition, 66); n++;
        XtSetArg (args[n], XmNrightAttachment, XmATTACH_FORM); n++;
        XtSetArg (args[n], XmNleftAttachment, XmATTACH_WIDGET); n++;
        XtSetArg (args[n], XmNleftWidget, label[2]); n++;
        XtSetArg (args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
        XtSetValues (text[2], args, n);


-------------------------------------------------------------------------------
Subject: 58) TOPIC: PUSHBUTTON WIDGET

-------------------------------------------------------------------------------
Subject: 59) Why can't I use accelerators on buttons not in a menu?

Answer: It is apparently a difficult feature to implement, but OSF are
considering this for the future. It is problematic trying to use the Xt
accelerators since the Motif method interferes with this.  The workaround
suggested so far is to duplicate your non-menu button by a button in a menu
somewhere, which does have a menu-accelerator installed.  When the user invokes
what they think is the accelerator for the button they can see Motif actually
invokes the button on the menu that they can't see at the time.


-------------------------------------------------------------------------------
Subject: 60) TOPIC: LABEL WIDGET

-------------------------------------------------------------------------------
Subject: 61) How can I align the text in a label (button, etc) widget?

Answer: The alignment for the label widget is controlled by the resource
XmNalignment, and the default centers the text. Use this resource to change it
to left or right alignment.  However, when the label (or any descendant) is in a
row column, and XmNisAligned is True (the default), the row column aligns text
using its resource XmNentryAlignment. If you want simultaneous control over all
widgets use this, but otherwise turn XmNisAligned off and do it individually.



-------------------------------------------------------------------------------
Subject: 62) Why doesn't label alignment work in a RowColumn?

Answer: RowColumn has a  resource XmNisAligned (default True) and and
XmNentryAlignment (default XmALIGNMENT_BEGINNING).  These control alignment of
the labelString in Labels and descendants. Set XmNisAligned to False to turn
this off.

-------------------------------------------------------------------------------
Subject: 63)*  How can I set a multiline label?

Answer: In .Xdefaults

      *XmLabel*labelString:             Here\nis\nthe\nLabel

This method does not seem to work in some of the older Motif 1.0 versions.

In code,

      char buf[128];
      XmString msg;
      sprintf(buf, "Here\nis\nthe\nLabel");
      msg = XmStringCreateLtoR(buf, XmSTRING_DEFAULT_CHARSET);
      XtSetArg (args[n], XmNlabelString, msg);

Gives a four line label, using the escape sequence \n for a newline.  However,
XmStringCreateLtoR() is obsoleted from version 1.1 on, and may disappear.  This
is because it it is only in the AES as "trial-use" and has been proposed for
removal from the AES. Realistically, it will probably not be removed from any
backward compatible versions of Motif, but the potential is there.  If it does
disappear (or if you want to avoid using the non-AES compliant
XmSTRING_DEFAULT_CHARSET), try this from Jean-Philippe Martin-Flatin
<syj@ecmwf.co.uk>

#include <Xm/Xm.h>
#include <string.h>

/*-----------------------------------------------------
    Create a new XmString from a char*

    This function can deal with embedded 'newline' and
    is equivalent to the obsolete XmStringCreateLtoR,
    except it does not use non AES compliant charset
    XmSTRING_DEFAULT_CHARSET
------------------------------------------------------*/
XmString xec_NewString(char *s)
{
    XmString xms1;
    XmString xms2;
    XmString line;
    XmString separator;
    char     *p;
    char     *t = XtNewString(s);   /* Make a copy for strtok not to */

    separator = XmStringSeparatorCreate();
    p         = strtok(t,"0);
    xms1      = XmStringCreateSimple(p);

    while (p = strtok(NULL,"0))
    {
        line = XmStringCreateSimple(p);
        xms2 = XmStringConcat(xms1,separator);
        XmStringFree(xms1);
        xms1 = XmStringConcat(xms2,line);
        XmStringFree(xms2);
        XmStringFree(line);
    }

    XmStringFree(separator);
    XtFree(t);
    return xms1;
}


Do not use XmStringCreateSimple() - it does not process the newline character in
the way you want.

In UIL, you have to explicitly create a compound string with a separator.
Here's what W. Scott Meeks suggests:

value nl : compound_string('', seperate=true);

object my_label : XmLabel
{
    arguments
    {
        XmNlabelString = 'Here' & nl & 'is' & nl & 'the' & nl & 'Label';
    };
};


-------------------------------------------------------------------------------
Subject: 64)  How can I have a vertical label?

Answer: Make a multiline label with one character per line, as in the last
question. There is no way to make the text rotated by 90 degrees though.


-------------------------------------------------------------------------------
Subject: 65)+ How can I have a Pixmap in a Label?

Answer: From Bob Hays (bobhays@spss.com)

    Pixmap px_disarm, px_disarm_insens;

    Widget Label1;
    Pixel   foreground, background;
    Arg     args[4];
    Arg     arg[] = {
                { XmNforeground, &foreground },
                { XmNbackground, &background }
    };

    Label1 = XmCreateLabel ( Shell1, "Label1",
                                       (Arg *) NULL, (Cardinal) 0 );
    XtGetValues ( Label1, arg, XtNumber ( arg ) );
    px_disarm =
      XCreatePixmapFromBitmapData(display,
                                DefaultRootWindow(display),
                                mtn_bits, mtn_width, mtn_height,
                                foreground,
                                background,
                                DefaultDepth(display,DefaultScreen(display)));
    px_disarm_insens =
      XCreatePixmapFromBitmapData(display,
                                DefaultRootWindow(display),
                                mtn_ins_bits, mtn_ins_width, mtn_ins_height,
                                foreground,
                                background,
                                DefaultDepth(display,DefaultScreen(display)));

    n = 0;
    XtSetArg(args[n], XmNlabelType, XmPIXMAP);  n++;
    XtSetArg(args[n], XmNlabelPixmap, px_disarm);  n++;
    XtSetArg(args[n], XmNlabelInsensitivePixmap, px_disarm_insens ); n++;
    XtSetValues ( Label1, args, n );
    XtManageChild(Label1);

That will cause the foreground and background of your pixmap to be inherited
from the one that would be used by OSF/Motif when the label is displayed.  The
advantage is that this will utilize any resource values the user may have
requested without looking explicitly into the resource database.  And, you will
have a pixmap handy if the application insensitizes the label (without an
XmNlabelInsensitivePixmap your label will go empty if made insensitive).

[Bob's original code was for a PushButton. Just change all Label to PushButton
for them.]


-------------------------------------------------------------------------------
Subject: 66) TOPIC: DRAWING AREA WIDGET

-------------------------------------------------------------------------------
Subject: 67) How can I send an expose event to a Drawing Area widget?  (or any
other, come to that). I want to send an expose event so that it will redraw
itself.

Answer: Use the Xlib call

        XClearArea(XtDisplay(w), XtWindow(w), 0, 0, 0, 0, True)

This clears the widget's window and generates an expose event in doing so.  The
widgets expose action will then redraw it.  This uses a round trip request.  An
alternative, without the round trip is

from orca!mesa!rthomson@uunet.uu.net  (Rich Thomson):

    Widget da;
    XmDrawingAreaCallbackStruct da_struct;

    da_struct.reason = XmCR_EXPOSE;
    da_struct.event = (XEvent *) NULL;
    da_struct.window = XtWindow(da);

    XtCallCallbacks(da, XmNexposeCallback, (XtPointer) da_struct);


-------------------------------------------------------------------------------
Subject: 68) How can I know when a DrawingArea has been resized?  It generates
an expose event whn it is enlarged, but not when it is shrunk.

Answer: Use the resize callback.

-------------------------------------------------------------------------------
Subject: 69) TOPIC: MENUS

-------------------------------------------------------------------------------
Subject: 70) What can I put inside a menu bar?

Answer: You can only put cascade buttons in menu bars. No pushbuttons, toggle
buttons or gadgets are allowed. When you create a pulldown menu with parent a
menu bar, its real parent is a shell widget.

-------------------------------------------------------------------------------
Subject: 71) Can I have a cascade button without a submenu in a pulldown menu?

Answer: Yes you can. A cascade button has an activate callback which is called
when you click on it and it doesn't have a submenu. It can have a mnemonic, but
keyboard traversal using the arrow keys in the menu will skip over it.

-------------------------------------------------------------------------------
Subject: 72) Should I have a cascade button without a submenu in a pulldown
menu?

Answer: No. This is forbidden by the style guide. Technically you can do it (see
previous question) but if you do it will not be Motif style compliant. This is
unlikely to change - if a "button" is important enough to be in a pulldown menu
bar with no pulldown, it should be a button elsewhere.  (Mind you, you won't be
able to put accelerators on it elsewhere though.)

-------------------------------------------------------------------------------
Subject: 73) TOPIC: INPUT FOCUS

-------------------------------------------------------------------------------
Subject: 74) How can I direct the keyboard input to a particular widget?

Answer: In Motif 1.1 call XmProcessTraversal(target, XmTRAVERSE_CURRENT).  The
widget (and all of its ancestors) does need to be realized BEFORE you call this.
Otherwise it has no effect.  XmProcessTraversal is reported to have many bugs,
so it may not work right.  A common occurrence is that it doesn't move to the
widget, but if you call XmProcessTraversal *twice* in a row, it will.  If you
can't get it to work, try this from Kee Hinckley:

    // This insane sequence is as follows:
    //      On manage set up a focus callback
    //      On focus callback set up a timer (and get rid of focus callback!)
    //      On timer set the focus (which only works if the parent
    //      has the focus,
    //      which is why we went through all of this garbage)
    // There may be a better way, but I haven't time to try it now.
    //
    static void focusTO(void *data, XtIntervalId *) {
        XmProcessTraversal((Widget) data, XmTRAVERSE_CURRENT);
    }

    static void focusCB(Widget w, XtPointer data, XtPointer) {
        XtRemoveCallback(w, XmNfocusCallback, focusCB, data);
        XtAppAddTimeOut(XtWidgetToApplicationContext(w), 0, focusTO, data);
    }

    void OmXSetFocus(Widget parent, Widget w) {
        XtAddCallback(parent, XmNfocusCallback, focusCB, w);
    }


In Motif 1.0 call the undocumented _XmGrabTheFocus(target).

Do not use the X or Xt calls such as XtSetKeyboardFocus since this bypasses the
Motif traversal layer and can cause it to get confused.  This can lead to odd
keyboard behaviour elsewhere in your application.

-------------------------------------------------------------------------------
Subject: 75) How can I have a modal dialog which has to be answered before the
application can continue?

Answer: The answer depends on whether you are using the Motif window manager mwm
or not.  Test for this by XmIsMotifWMRunning.

The window manager mwm knows how to control event passing to dialog widgets
declared as modal. If the dialog is set to application modal, then no
interaction with the rest of the application can occur until the dialog is
destroyed or unmanaged.

Use the appropriate code in the following program.  There is followup discussion
after the program.


/* Written by Dan Heller.  Copyright 1991, O'Reilly && Associates.
 * This program is freely distributable without licensing fees and
 * is provided without guarantee or warranty expressed or implied.
 * This program is -not- in the public domain.  This program is
 * taken from the Motif Programming Manual, O'Reilly Volume 6.
 */

/*
 * ask_user.c -- create a pushbutton that posts a dialog box
 * that asks the user a question that requires an immediate
 * response.  The function that asks the question actually
 * posts the dialog that displays the question, waits for and
 * returns the result.
 */
#include <X11/Intrinsic.h>
#include <Xm/DialogS.h>
#include <Xm/SelectioB.h>
#include <Xm/RowColumn.h>
#include <Xm/MessageB.h>
#include <Xm/PushBG.h>
#include <Xm/PushB.h>

XtAppContext app;

#define YES 1
#define NO  2

/* main() --create a pushbutton whose callback pops up a dialog box */
main(argc, argv)
char *argv[];
{
    Widget parent, button, toplevel;
    XmString label;
    void pushed();

    toplevel = XtAppInitialize(&app, "Demos",
        NULL, 0, &argc, argv, NULL, NULL);

    label = XmStringCreateSimple("/bin/rm *");
    button = XtVaCreateManagedWidget("button",
        xmPushButtonWidgetClass, toplevel,
        XmNlabelString,          label,
        NULL);
    XtAddCallback(button, XmNactivateCallback,
        pushed, "Remove Everything?");
    XmStringFree(label);

    XtRealizeWidget(toplevel);
    XtAppMainLoop(app);
}

/* pushed() --the callback routine for the main app's pushbutton. */
void
pushed(w, question)
Widget w;
char *question;
{
    if (AskUser(w, question) == YES)
        puts("Yes");
    else
        puts("No");
}

/*
 * AskUser() -- a generalized routine that asks the user a question
 * and returns the response.
 */
AskUser(parent, question)
char *question;
{
    static Widget dialog;
    XmString text, yes, no;
    static int answer = 0;
    extern void response();

    if (!dialog) {
        dialog = XmCreateQuestionDialog(parent, "dialog", NULL, 0);
        yes = XmStringCreateSimple("Yes");
        no = XmStringCreateSimple("No");
        XtVaSetValues(dialog,
            XmNdialogStyle,        XmDIALOG_APPLICATION_MODAL,
            XmNokLabelString,      yes,
            XmNcancelLabelString,  no,
            NULL);
        XtSetSensitive(
            XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON), False);
        XtAddCallback(dialog, XmNokCallback, response, &answer);
        XtAddCallback(dialog, XmNcancelCallback, response, &answer);
    }
    text = XmStringCreateSimple(question);
    XtVaSetValues(dialog,
        XmNmessageString,      text,
        NULL);
    XmStringFree(text);
    XtManageChild(dialog);
    XtPopup(XtParent(dialog), XtGrabNone);

    /* while the user hasn't provided an answer, simulate XtMainLoop.
     * The answer changes as soon as the user selects one of the
     * buttons and the callback routine changes its value.  Don't
     * break loop until XtPending() also returns False to assure
     * widget destruction.
     */
    while (answer == 0 || XtAppPending(app))
        XtAppProcessEvent(app, XtIMAll);
    return answer;
}

/* response() --The user made some sort of response to the
 * question posed in AskUser().  Set the answer (client_data)
 * accordingly and destroy the dialog.
 */
void
response(w, answer, reason)
Widget w;
int *answer;
XmAnyCallbackStruct *reason;
{
    switch (reason->reason) {
        case XmCR_OK:
            *answer = YES;
            break;
        case XmCR_CANCEL:
            *answer = NO;
            break;
        default:
            return;
    }
}



If you aren't running a window manager that acknowledges this hint, then you may
have to grab the pointer (and keyboard) yourself to make sure the user doesn't
interact with any other widget.  Change the grab flag in XtPopup to
XtGrabExclusive, and XtRemoveGrab(XtParent(w)) to the response() function.


-------------------------------------------------------------------------------
Subject: 76) TOPIC: MEMORY AND SPEED

-------------------------------------------------------------------------------
Subject: 77)* Why does my application grow in size?

Answer: Motif 1.0 has many memory leaks, particularly in XmString manipulation.
Switch to Motif 1.1.

Answer: The Intrinsics have a memory leak in accelerator table management, and
Motif uses this heavily.  Avoid this by mapping/unmapping widgets rather than
creating/destroying them, or get  X11R4 fix-15/16/17.

Answer: The server may grow in size due to its own memory leaks.  Switch to a
later server.

Answer: You are responsible for garbage collection in `C'.  Some common cases
where a piece of memory becomes garbage are

 a.  Memory is allocated by Motif for XmStrings by the functions XmStringConcat,
     XmStringCopy, XmStringCreate, XmStringCreateLtoR, XmStringCreateSimple,
     XmStringDirectionCreate, XmStringNConcat, XmStringNCopy,
     XmStringSegmentCreate, and XmStringSeparatorCreate.  The values returned by
     these functions should be freed using XmStringFree when they are no longer
     needed.

 b.  Memory is allocated by Motif for ordinary character strings (of type
     String) by Motif in XmStringGetLtoR, XmStringGetNextComponent, and
     XmStringGetNextSegment. After using the string, XtFree() it. [Note that
     XmStrings and Strings are two different data types.  XmStrings are
     XmStringFree'd, Strings are XtFree'd.]

 c.  If you have set the label (an XmString) in a label, pushbutton, etc widget,
     free it after calling XtSetValues() or the widget creation routine by
     XmStringFree().

 d.  If you have set text in a text widget, the text widget makes its own copy.
     Unless you have a use for it, there is no need to keep your own copy.

 e.  If you have set the strings in a list widget the list widget makes its own
     copy.  Unless you have a use for it, there is no need to keep your own
     copy.

 f.  When you get the value of a single compound string from a Widget e.g.
     XmNlabelString, XmNmessageString, ... Motif gives you a copy of its
     internal value.  You should XmStringFree this when you have finished with
     it.

 g.  On the other hand, when you get a value of a Table e.g. XmStringTable for a
     List, you get a *pointer* to the internal Table, and should not free it.

 h.  When you get the value of the text in a widget by XmTextGetString or from
     the resource XmNvalue, you get a copy of the text.  You should XtFree this
     when you have finished with it.

Answer: From Josef Nelissen: at least in Motif 1.1.4, X11R4 on a HP 720, the
XmText/XmTextFieldSetString() functions have a memory leak.  The old
value/contents of the Widget isn't freed correctly.  To work around this bug,
one should use a XmText Widget (in single-line-mode) instead of a XmTextField
Widget (the solution fails with XmTextField Widgets !) and replace any

       XmTextSetString(text_widget, str);

by

       XmTextReplace(text_widget, (XmTextPosition) 0,
                     XmTextGetLastPosition(text_widget), str);


-------------------------------------------------------------------------------
Subject: 78) Why does my application take a long time to start up?

Answer: You are probably creating too many widgets at startup time.  Delay
creating them until needed.  If you have a large number of resources in text
files (such as in app-defaults), time may be spent reading and parsing it.

-------------------------------------------------------------------------------
Subject: 79) My application is running too slowly. How can I speed it up?

Answer: Use the R4 rather than R3 server.  It is much faster.

Answer: The standard memory allocator is not well tuned to Motif, and can
degrade performance.  Use a better allocator.  e.g. with SCO Unix, link with
libmalloc.a; use the allocator from GNU emacs; use the allocator from Perl.

Answer: Avoid lots of widget creation and destruction.  It fragments memory and
slows everything down.  Popup/popdown, manage/unmanage instead.

Answer: Set mappedWhenManaged to FALSE, and then call XtMapWidget()
XtUnmapWidget() rather than managing.

Answer: Get more memory - your application, the server and the Operating System
may be spending a lot of time being swapped.

Answer: If you are doing much XmString work yourself, such as heavy use of
XmStringCompare, speed may deteriorate due to the large amount of internal
conversions and malloc'ing.  Try using XmStringByteCompare if appropriate or
ordinary Ascii strings if you can.



-------------------------------------------------------------------------------
END OF PART THREE