From: jan@ECHO.CANBERRA.EDU.AU (Jan Newmarch)
Newsgroups: comp.windows.x.motif
Subject: Motif FAQ (Part 5 of 5)
Keywords: FAQ question answer
Date: 17 Aug 92 14:51:55 GMT
Expires: 17 Oct 92 04:32:23 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/part5
Last-modified: Tue July 14 1992
Version: 2.6



-------------------------------------------------------------------------------
Subject: 105) TOPIC: ICONS

Iconification/de-iconification is a co-operative process between a client and a
window manager.  The relevant standards are set by ICCCM.  Mwm is ICCCM
compliant.  The toplevel (non-override-redirect) windows of an application may
be in three states: WithdrawnState (neither the window nor icon visible),
NormalState (the window visible) or IconicState (the icon window or pixmap
visible).  This information is contained in the WM_STATE property but ordinary
clients are not supposed to look at that (its values have not yet been
standardised).  Movement between the three states is standardised by ICCCM.

-------------------------------------------------------------------------------
Subject: 106) How can I keep track of changes to iconic/normal window state?

Answer: You can look at the WM_STATE property, but this breaks ICCCM guidelines.
ICCCM compliant window managers will map windows in changing them to normal
state and unmap them in changing them to iconic state. Look for StructureNotify
events and check the event type:

        XtAddEventHandler (toplevel_widget,
                        StructureNotifyMask,
                        False,
                        StateWatcher,
                        (Opaque) NULL);
        ....
        void StateWatcher (w, unused, event)
        Widget w;
        caddr_t unused;
        XEvent *event;
        {
                if (event->type == MapNotify)
                        printf ("normal\n");
                else if (event->type == UnmapNotify)
                        printf ("iconified\n");
                else    printf ("other event\n");
        }


If you insist on looking at WM_STATE, here is some code (from Ken Sall) to do
it:

        /*
        ------------------------------------------------------------------
        Try a function such as CheckWinMgrState below which returns one of
        IconicState | NormalState | WithdrawnState | NULL :
        ------------------------------------------------------------------
        */
        #define WM_STATE_ELEMENTS 1

        unsigned long *CheckWinMgrState (dpy, window)
        Display *dpy;
        Window window;
        {
          unsigned long *property = NULL;
          unsigned long nitems;
          unsigned long leftover;
          Atom xa_WM_STATE, actual_type;
          int actual_format;
          int status;

            xa_WM_STATE = XInternAtom (dpy, "WM_STATE", False);

            status = XGetWindowProperty (dpy, window,
                          xa_WM_STATE, 0L, WM_STATE_ELEMENTS,
                          False, xa_WM_STATE, &actual_type, &actual_format,
                          &nitems, &leftover, (unsigned char **)&property);

            if ( ! ((status == Success) &&
                        (actual_type == xa_WM_STATE) &&
                        (nitems == WM_STATE_ELEMENTS)))
                {
                if (property)
                    {
                    XFree ((char *)property);
                    property = NULL;
                    }
                }
            return (property);
        } /* end CheckWinMgrState */


-------------------------------------------------------------------------------
Subject: 107) How can I check if my application has come up iconic?  I want to
delay initialisation code and other processing.

Answer: Use XtGetValues and check for the XmNinitialState value of the toplevel
shell just before XtMainLoop. -- IconicState is iconic, NormalState is not
iconic.




-------------------------------------------------------------------------------
Subject: 108) How can I start my application in iconic state?

Answer: From the command line

        application -iconic

Using the resource mechanism, set the resource XmNinitialState to IconicState of
the toplevel shell widget (the one returned from XtInitialise).

-------------------------------------------------------------------------------
Subject: 109) How can an application iconify itself?

Answer: In R4 and later, use the call XIconifyWindow.

For R3, send an event to the root window with a type of WM_CHANGE_STATE and data
IconicState.

        void
        IconifyMe (dpy, win)
        Display *dpy;
        Window win;     /* toplevel window to iconify */
        {
                Atom xa_WM_CHANGE_STATE;
                XClientMessageEvent ev;

                xa_WM_CHANGE_STATE = XInternAtom (dpy,
                                        "WM_CHANGE_STATE", False);

                ev.type = ClientMessage;
                ev.display = dpy;
                ev.message_type = xa_WM_CHANGE_STATE;
                ev.format = 32;
                ev.data.l[0] = IconicState;
                ev.window = win;

                XSendEvent (dpy,
                        RootWindow (dpy, DefaultScreen(dpy)),
                        True,
                        (SubstructureRedirectMask | SubstructureNotifyMask),
                        &ev);
                XFlush (dpy);
        }


-------------------------------------------------------------------------------
Subject: 110) How can an application de-iconify itself?

Answer: XMapWindow (XtDisplay (toplevel_widget), XtWindow (toplevel_widget)).

-------------------------------------------------------------------------------
Subject: 111) TOPIC: MISCELLANEOUS

-------------------------------------------------------------------------------
Subject: 112)  What is IMUG and how do I join it?

Answer: IMUG is the International Motif User Group founded by Quest Windows
Corporation and co-sponsored by FedUNIX.  IMUG is a non-profit organization
working to keep users informed on technical and standards issues, to strengthen
user groups on a local level, to increase communication among users
internationally, and to promote the use of an international conference as a
forum for sharing and learning more about Motif.  You can join it by

 1.  Pay the annual membership fee of $20 USD directly to IMUG.  Contact

     IMUG
     5200 Great America Parkway
     Santa Clara,  CA  95054
     (408) 496-1900
     imug@quest.com

 2.  Register at the International Motif User Conference, and automatically
     become an IMUG member.

 3.  Donate a pd widget, widget tool or widget builder to the IMUG Widget
     Depository and receive a free one year IMUG membership.



-------------------------------------------------------------------------------
Subject: 113)+ How do I set the title of a top level window?

Answer: Set XmNtitle (and optionally XmNtitleEncoding) for TopLevelShells.  For
XmDialogShells, set the XmNdialogTitle of its immediate child, assuming it's a
BulletinBoard subclass.


-------------------------------------------------------------------------------
Subject: 114)  Can I use editres with Motif?

Answer: It won't be built in to Motif, but you can do this in your application

    extern void _XEditResCheckMessages();
    ...
    XtAddEventHandler(shell_widget, (EventMask)0, True,
                        _XEditResCheckMessages, NULL);

once for each shell widget that you want to react to the "click to select
client" protocol.  Then link your client with the R5 libXmu.

David Brooks, OSF


-------------------------------------------------------------------------------
Subject: 115)  How can I put decorations on transient windows using olwm?

Answer: From Jean-Philippe Martin-Flatin <syj@ecmwf.co.uk>

/**********************************************************************
** WindowDecorations.c
**
** Manages window decorations under the OpenLook window manager (OLWM).
**
** Adapted from a C++ program posted to comp.windows.x.motif by:
**
**    +--------------------------------------------------------------+
**    | Ron Edmark                          User Interface Group     |
**    | Tel:        (408) 980-1500 x282     Integrated Systems, Inc. |
**    | Internet:   edmark@isi.com          3260 Jay St.             |
**    | Voice mail: (408) 980-1590 x282     Santa Clara, CA 95054    |
**    +--------------------------------------------------------------+
***********************************************************************/

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Protocols.h>
#include <Xm/Xm.h>
#include <Xm/AtomMgr.h>

/*
** Decorations for OpenLook:
** The caller can OR different mask options to change the frame decoration.
*/
#define OLWM_Header     (long)(1<<0)
#define OLWM_Resize     (long)(1<<1)
#define OLWM_Close      (long)(1<<2)

/*
** Prototypes
*/
static void InstallOLWMAtoms  (Widget w);
static void AddOLWMDialogFrame(Widget widget, long decorationMask);


/*
** Global variables
*/
static Atom AtomWinAttr;
static Atom AtomWTOther;
static Atom AtomDecor;
static Atom AtomResize;
static Atom AtomHeader;
static Atom AtomClose;
static int  not_installed_yet = TRUE;


static void InstallOLWMAtoms(Widget w)
{
        AtomWinAttr = XInternAtom(XtDisplay(w), "_OL_WIN_ATTR" ,    FALSE);
        AtomWTOther = XInternAtom(XtDisplay(w), "_OL_WT_OTHER",     FALSE);
        AtomDecor   = XInternAtom(XtDisplay(w), "_OL_DECOR_ADD",    FALSE);
        AtomResize  = XInternAtom(XtDisplay(w), "_OL_DECOR_RESIZE", FALSE);
        AtomHeader  = XInternAtom(XtDisplay(w), "_OL_DECOR_HEADER", FALSE);
        AtomClose   = XInternAtom(XtDisplay(w), "_OL_DECOR_CLOSE",  FALSE);

        not_installed_yet = FALSE;
}

static void AddOLWMDialogFrame(Widget widget, long decorationMask)
{
        Atom   winAttrs[2];
        Atom   winDecor[3];
        Widget shell = widget;
        Window win;
        int    numberOfDecorations = 0;

        /*
        ** Make sure atoms for OpenLook are installed only once
        */
        if (not_installed_yet) InstallOLWMAtoms(widget);

        while (!XtIsShell(shell)) shell = XtParent(shell);

        win = XtWindow(shell);

        /*
        ** Tell Open Look that our window is not one of the standard OLWM window        ** types. See OLIT Widget Set Programmer's Guide pp.70-73.
        */

        winAttrs[0] = AtomWTOther;

        XChangeProperty(XtDisplay(shell),
                        win,
                        AtomWinAttr,
                        XA_ATOM,
                        32,
                        PropModeReplace,
                        (unsigned char*)winAttrs,
                        1);

        /*
        ** Tell Open Look to add some decorations to our window
        */
        numberOfDecorations = 0;
        if (decorationMask & OLWM_Header)
                winDecor[numberOfDecorations++] = AtomHeader;
        if (decorationMask & OLWM_Resize)
                winDecor[numberOfDecorations++] = AtomResize;
        if (decorationMask & OLWM_Close)
        {
                winDecor[numberOfDecorations++] = AtomClose;

                /*
                ** If the close button is specified, the header must be
                ** specified. If the header bit is not set, set it.
                */
                if (!(decorationMask & OLWM_Header))
                        winDecor[numberOfDecorations++] = AtomHeader;
        }

        XChangeProperty(XtDisplay(shell),
                        win,
                        AtomDecor,
                        XA_ATOM,
                        32,
                        PropModeReplace,
                        (unsigned char*)winDecor,
                        numberOfDecorations);
}


/*
** Example of use of AddOLWMDialogFrame, with a bit of extra stuff
*/
void register_dialog_to_WM(Widget shell, XtCallbackProc Cbk_func)
{
        Atom atom;

        /*
        ** Alias the "Close" item in system menu attached to dialog shell
        ** to the activate callback of "Exit" in the menubar
        */
        if (Cbk_func)
        {
            atom = XmInternAtom(XtDisplay(shell),"WM_DELETE_WINDOW",TRUE);
            XmAddWMProtocolCallback(shell,atom, Cbk_func,NULL);
        }

        /*
        ** If Motif is the window manager, skip OpenLook specific stuff
        */
        if (XmIsMotifWMRunning(shell)) return;

        /*
        ** Register dialog shell to OpenLook.
        **
        ** WARNING: on some systems, adding the "Close" button allows the title
        ** to be properly centered in the title bar. On others, activating
        ** "Close" crashes OpenLook. The reason is not clear yet, but it seems
        ** the first case occurs with OpenWindows 2 while the second occurs with
        ** Openwindows 3. Thus, comment out one of the two following lines as
        ** suitable for your site, and send e-mail to syj@ecmwf.co.uk if you
        ** find out what is going on !
        */
        AddOLWMDialogFrame(shell,(OLWM_Header | OLWM_Resize));
/*      AddOLWMDialogFrame(shell,(OLWM_Header | OLWM_Resize | OLWM_Close)); */
}


-------------------------------------------------------------------------------
Subject: 116) Why does an augment translation appear to act as replace for some
widgets?  When I use either augment or override translations in .Xdefaults it
seems to act as replace in both Motif 1.0 and 1.1

Answer: By default, the translation table is NULL.  If there is nothing
specified (either in resource file, or in args), the widget's Initialize finds:
Oh, there is NULL in translations, lets use our default ones.  If, however, the
translations have become non-NULL, the default translations are NOT used at all.
Thus, using #augment, #override or a new table has identical effect: defines the
new translations. The only way you can augment/override Motif's default
translations is AFTER Initialize, using XtSetValues.  Note, however, that Motif
managers do play with translation tables as well ... so that results are not
always easy to predict.

From OSF: A number of people have complained about not being able to
augment/override translations from the .Xdefaults.  This is due to the
complexity of the menu system/keyboard traversal and the necessary translations
changes required to support the Motif Style Guide in menus.  It cannot be fixed
in a simple way. Fixing it requires re-design of the menus/buttons and it is
planned to be fixed in 1.2.





-------------------------------------------------------------------------------
Subject: 117) How do you "grey" out a widget so that it cannot be activated?

Answer: Use XtSetSensitive(widget, False). Do not set the XmNsensitive resource
directly yourself (by XtSetValues) since the widget may need to talk to parents
first.


-------------------------------------------------------------------------------
Subject: 118) Why doesn't the Help callback work on some widgets?

Answer: If you press the help key the help callback of the widget with the
keyboard focus is called (not the one containing the mouse).  You can't get the
help callback of a non-keyboard-selectable widget called. To get `context
sensitive' help on these, you have to find the mouse, associate its position
with a widget and then do the help.



-------------------------------------------------------------------------------
Subject: 119) Where can I get a Table widget?

Answer: Send email to Kee Hinckley (nazgul@alfalfa.com) asking for a copy of his
table widget.  The Widget Creation Library also has one.  See under Motif
prototyping tools for the contact.

Expert Database Systems, Inc., 305 Madison Ave, Suite 1166, New York, NY 10165
has a very comprehensive table widget that uses both motif scrollbars or a
"virtual" scrollbar showing a miniature version of the entire spreadsheet.
Allows for different width columns, changing colors in each cell.  Only one X-
Window is used so as to reduce the amount of system resources used.  Contact Ken
Jones


-------------------------------------------------------------------------------
Subject: 120)*  Has anyone done a bar graph widget?

Answer: You can fake one by using for each bar a scroll bar or even a label
which changes in size, put inside a container of some kind.

Try the StripChart widget in the Athena widget set. Set the XtNupdate resource
to 0 to keep it from automatically updating.

The comp.windows.x FAQ mentions a bar graph widget.

Expert Database Systems, Inc.  sells a bar graph widget as well as a multi-line
graph with automatic scaling, a 3-D surface graph, and a high/Low graph with two
lines for moving averages.  Contact Ken Jones Expert Database Systems, Inc., 305
Madison Ave, Suite 1166, New York, NY 10165, Main Number: (212) 370-6700, Phone
Mail:  (212) 714-8345

The Xtra XWidget library contains a set of widgets that are subclassed from and
compatible with either OSF/Motif or OLIT widgets.  The library includes widgets
that implement the following:

   Spreadsheet
   Bar Graph
   Stacked Bar Graph
   Line Graph
   Pie Chart
   XY Plot
   Hypertext
   Hypertext based Help System
   Entry Form with type checking

Contact Graphical Software Technology at 310-328-9338  (info@gst.com) for
information.

The XRT/graph widget, available for Motif, XView and OLIT, displays X-Y plots,
bar and pie charts, and supports user-feedback, fast updates and PostScript
output. Contact KL Group Inc. at 416-594-1026 (xrt_info%klg@uunet.ca)

The product Xmath, made by Integrated Systems Inc. is a product which has
interactive 2d and 3d graphics for bar,strip,line,symbol, surface,contour,etc...
that costs $2500.00 for commercial use and a mere $250.00 for university use
that also has complete numerics capabilities, an easy to use debugger, a
complete high level language, a spreadsheet, a motif gui access capability, and
much more all created on top of motif.

You can either email to xmath-info@isi.com or call (408)980-1500.

Digital Equipment Corporation (DEC) provides the following product NetEd: "The
network editor widget is a Motif toolkit conforming widget that applications can
use to express complex interrelationships graphically in the form of networks or
graphs. The network editor supports interactive or application-controlled
creation and editing of directed graphs or networks."




-------------------------------------------------------------------------------
Subject: 121)  Does anyone know of a source code of a graph widget where you can
add vertices and edges and get an automated updating?

Answer: No Motif one has been reported.  The XUG FAQ in comp.windows.x includes
information on graph display widgets.  There is also an implementation in the
Asente/Swick book.

  From Martin Janzen: "You could have a look at DataViews, from V.I.
   Corporation.  This package is used mainly to display a variety of graph
   drawings (eg. bar, line, pie, high/low, and other charts), and to update
   the graphs as information is received from "data sources" such as files,
   processes (through pipes), or devices.

   However, it also provides "node" and "edge" objects which can be used
   when working with network graphs.  The DV-Tools function library
   provides routines which traverse a graph, count visits to each node or
   edge, mark nodes or edges of interest, and so on.  A node or edge object
   can have an associated "geometry object" (such as a symbol or a line),
   which represents that node or edge.

   Drawbacks: There's no automatic positioning algorithm; when you add a
   node or edge, you have to create and position its geometry object
   yourself.  Also, this isn't a set of add-on widgets; you can either have
   DataViews create an X window (ie. a separate shell), or you can create
   your own XmDrawingArea and use DataViews to update its window when
   expose events are received.  Finally, the package is quite expensive,
   and there is a run-time charge.

   The vendor's address is:
     V.I. Corporation,
     47 Pleasant Street,
     Northampton, MA  01060,
            Email: vi@vicorp.com, Phone: (413) 586-4144, Fax:   (413) 584-2649

   or

     V.I. Corporation (Europe) Ltd.,
     First Base, Beacontree Plaza,
     Gillette Way,                      Email: viesales@eurovi.uucp
     Reading, Berkshire  RG2 0BP"
   Phone: +44 734 756010,      Fax:   +44 734 756104

From Craig Timmerman: Just wanted others to know that there is a third
competitor in what may be come a big market for generic APIs.  The product is
called Open Interface and Neuron Data is the vendor.  Neuron has added some
extra, more complex widgets to their set.  The two most notable are a table and
network widget.  [...] I believe that the network widget got its name from its
ability to display expert system networks that Neuron's AI tools needed.  It
would be more aptly named the graph widget.  It can display and manipulate
graphs of various types (trees, directed graphs, etc).  Contact is

        Neuron Data
        156 University Avenue
        Palo Alto,  CA  94301
        (415) 321-4488


prism!gt3273b@gatech.edu  (RENALDS,ANDREW THEODORE) posted a set of public
domain routines for graph drawing.  Contact him for a later set.

-------------------------------------------------------------------------------
Subject: 122)*  Is there a help system available, such as in Windows 3?  Or any
Motif based hypertext system.

Answer:

Bristol Technology have a hypertext system with the look-and-feel of either
Motif or OpenLook. It should be available from january 31, 1992. Contact

        Bristol Technology Inc.
        898 Ethan Allen Highway
        Ridgefield, CT  06877
        203-438-6969 (phone)
        203-438-5013 (fax)
        uunet.uu.net!bristol!keith


There was a posting of a motif hypertext-widget to comp.sources.x (Author:
B.Raoult ( mab@ecmwf.co.uk ) ).  It had the facility to read in helptext from a
file.

From Francois Felix Ingrand (felix@idefix.laas.fr): I have translated the Info
AW (originally written by Jordan Hubbard) to Motif. It is a Widget to browse
Info files (format used by GNU for their various documentations). I use it as
the help system of various tool I wrote.  It is available on laas.laas.fr
(140.93.0.15) in /pub/prs/xinfo-motif.tar.Z

Form Scott Raney (raney@metacard.com) MetaCard is a commercial package that can
be used to implement hypertext help.  The text fields support multiple
typefaces, sizes, styles, colors, subscript/superscript, and hypertext links.
It has a Motif interface, and a template for calling it from an Xt/Motif
application is included.  You can FTP a save-disabled distribution from
ftp.metacard.com or from world.std.com.  For more info, email to
info@metacard.com.




-------------------------------------------------------------------------------
Subject: 123) Can I specify a widget in a resource file?

Answer: This answer, which uses the Xmu library, is due to David Elliott.  If
the converter is added, then the name of a widget (a string) can be used in
resource files, and will be converted to the appropriate widget.

This code, which was basically stolen from the Athena Form widget, adds a String
to Widget converter.  I wrote it as a general routine that I call at the
beginning of all of my programs, and made it so I could add other converters as
needed (like String to Unit Type ;-).

        #include <X11/Intrinsic.h>
        #include <X11/StringDefs.h>
        #include <Xm/Xm.h>
        #include <X11/Xmu/Converters.h>
        #include <X11/IntrinsicP.h>
        #include <X11/CoreP.h>

        void
        setupConverters()
        {
                static XtConvertArgRec parentCvtArgs[] = {
                        {XtBaseOffset, (caddr_t)XtOffset(Widget, core.parent),
                                sizeof(Widget)}
                };

                XtAddConverter(XmRString, XmRWindow, XmuCvtStringToWidget,
                        parentCvtArgs, XtNumber(parentCvtArgs));
        }



-------------------------------------------------------------------------------
Subject: 124) Why are only some of my translations are being installed?  I have
a translation table like the following, but only the first ones are getting
installed and the rest are ignored.

 *Text.translations:    #override \
     Ctrl<Key>a:    beginning-of-line() \n\
     Ctrl<Key>e:    end-of-line() \n\
     Ctrl<Key>f:    forward-character() \n\


Answer: Most likely, you have a space at the end of one of the lines (the first
in this case).

     Ctrl<Key>a:    beginning-of-line() \n\
                                           ^ space here

The second backslash in each line is there to protect the real newline character
and so you must not follow it with anything other than the newline itself.
Otherwise it acts as the end of the resource definition and the remaining lines
are not added.


-------------------------------------------------------------------------------
Subject: 125)* Where can I get the PanHandler code?

Answer: It is available by email from Chuck Ocheret:  chuck@IMSI.COM.

-------------------------------------------------------------------------------
Subject: 126) What are these passive grab warnings?  When I destroy certain
widgets I get a stream of messages

    Warning: Attempt to remove non-existant passive grab


Answer: They are meaningless, and you want to ignore them.  Do this (from Kee
Hinckley) by installing an XtWarning handler that explicitly looks for them and
discards them:

        static void xtWarnCB(String message) {
           if (asi_strstr(message, "non-existant passive grab", TRUE)) return;
           ...

They come from Xt, and (W. Scott Meeks): "it's something that the designers of
Xt decided the toolkit should do. Unfortunately, Motif winds up putting passive
grabs all over the place for the menu system.  On the one hand, we want to
remove all these grabs when menus get destroyed so that they don't leak memory;
on the other hand, it's almost impossible to keep track of all the grabs, so we
have a conservative strategy of ungrabbing any place where a grab could have
been made and we don't explicitly know that there is no grab.  The unfortunate
side effect is the little passive grab warning messages.  We're trying to clean
these up where possible, but there are some new places where the warning is
generated.  Until we get this completely cleaned up (1.2 maybe), your best bet
is probably to use a warning handler."

-------------------------------------------------------------------------------
Subject: 127) How do I have more buttons than three in a box?  I want to have
something like a MessageBox (or other widget) with more than three buttons, but
with the same nice appearance.

Answer: A SelectionBox is created with four buttons, but the fourth (the Apply
button) is unmanaged. To manage it get its widget ID via
XmSelectionBoxGetChild(parent, XmDIALOG_APPLY_BUTTON) and then XtManage it.
Unmanage all of the other bits in the SelectionBox that you don't want.  If you
want more than four buttons, try two SelectionBoxes (or similar) together in a
container, where all of the unwanted parts of the widgets are unmanaged.

Alternatively, build your own dialog:

/* 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.
 */

/* action_area.c -- demonstrate how CreateActionArea() can be used
 * in a real application.  Create what would otherwise be identified
 * as a PromptDialog, only this is of our own creation.  As such,
 * we provide a TextField widget for input.  When the user presses
 * Return, the Ok button is activated.
 */
#include <Xm/DialogS.h>
#include <Xm/PushBG.h>
#include <Xm/PushB.h>
#include <Xm/LabelG.h>
#include <Xm/PanedW.h>
#include <Xm/Form.h>
#include <Xm/RowColumn.h>
#include <Xm/TextF.h>

typedef struct {
    char *label;
    void (*callback)();
    caddr_t data;
} ActionAreaItem;

static void
    do_dialog(), close_dialog(), activate_cb(),
    ok_pushed(), cancel_pushed(), help();

main(argc, argv)
int argc;
char *argv[];
{
    Widget toplevel, button;
    XtAppContext app;

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

    button = XtVaCreateManagedWidget("Push Me",
        xmPushButtonWidgetClass, toplevel, NULL);
    XtAddCallback(button, XmNactivateCallback, do_dialog, NULL);

    XtRealizeWidget(toplevel);
    XtAppMainLoop(app);
}

/* callback routine for "Push Me" button.  Actually, this represents
 * a function that could be invoked by any arbitrary callback.  Here,
 * we demonstrate how one can build a standard customized dialog box.
 * The control area is created here and the action area is created in
 * a separate, generic routine: CreateActionArea().
 */
static void
do_dialog(w, file)
Widget w; /* will act as dialog's parent */
char *file;
{
    Widget dialog, pane, rc, label, text_w, action_a;
    XmString string;
    extern Widget CreateActionArea();
    Arg args[10];
    static ActionAreaItem action_items[] = {
        { "Ok",     ok_pushed,     NULL          },
        { "Cancel", cancel_pushed, NULL          },
        { "Close",  close_dialog,  NULL          },
        { "Help",   help,          "Help Button" },
    };

    /* The DialogShell is the Shell for this dialog.  Set it up so
     * that the "Close" button in the window manager's system menu
     * destroys the shell (it only unmaps it by default).
     */
    dialog = XtVaCreatePopupShell("dialog",
        xmDialogShellWidgetClass, XtParent(w),
        XmNtitle,  "Dialog Shell",     /* give arbitrary title in wm */
        XmNdeleteResponse, XmDESTROY,  /* system menu "Close" action */
        NULL);

    /* now that the dialog is created, set the Close button's
     * client data, so close_dialog() will know what to destroy.
     */
    action_items[2].data = (caddr_t)dialog;

    /* Create the paned window as a child of the dialog.  This will
     * contain the control area (a Form widget) and the action area
     * (created by CreateActionArea() using the action_items above).
     */
    pane = XtVaCreateWidget("pane", xmPanedWindowWidgetClass, dialog,
        XmNsashWidth,  1,
        XmNsashHeight, 1,
        NULL);

    /* create the control area (Form) which contains a
     * Label gadget and a List widget.
     */
    rc = XtVaCreateWidget("control_area", xmRowColumnWidgetClass, pane, NULL);
    string = XmStringCreateSimple("Type Something:");
    XtVaCreateManagedWidget("label", xmLabelGadgetClass, rc,
        XmNlabelString,    string,
        XmNleftAttachment, XmATTACH_FORM,
        XmNtopAttachment,  XmATTACH_FORM,
        NULL);
    XmStringFree(string);

    text_w = XtVaCreateManagedWidget("text-field",
        xmTextFieldWidgetClass, rc, NULL);

    /* RowColumn is full -- now manage */
    XtManageChild(rc);

    /* Set the client data "Ok" and "Cancel" button's callbacks. */
    action_items[0].data = (caddr_t)text_w;
    action_items[1].data = (caddr_t)text_w;

    /* Create the action area -- we don't need the widget it returns. */
    action_a = CreateActionArea(pane, action_items, XtNumber(action_items));

    /* callback for Return in TextField.  Use action_a as client data */
    XtAddCallback(text_w, XmNactivateCallback, activate_cb, action_a);

    XtManageChild(pane);
    XtPopup(dialog, XtGrabNone);
}

/*--------------*/
/* The next four functions are the callback routines for the buttons
 * in the action area for the dialog created above.  Again, they are
 * simple examples, yet they demonstrate the fundamental design approach.
 */
static void
close_dialog(w, shell)
Widget w, shell;
{
    XtDestroyWidget(shell);
}

/* The "ok" button was pushed or the user pressed Return */
static void
ok_pushed(w, text_w, cbs)
Widget w, text_w;         /* the text widget is the client data */
XmAnyCallbackStruct *cbs;
{
    char *text = XmTextFieldGetString(text_w);

    printf("String = %s0, text);
    XtFree(text);
}

static void
cancel_pushed(w, text_w, cbs)
Widget w, text_w;         /* the text field is the client data */
XmAnyCallbackStruct *cbs;
{
    /* cancel the whole operation; reset to NULL. */
    XmTextFieldSetString(text_w, "");
}

static void
help(w, string)
Widget w;
String string;
{
    puts(string);
}
/*--------------*/

/* When Return is pressed in TextField widget, respond by getting
 * the designated "default button" in the action area and activate
 * it as if the user had selected it.
 */
static void
activate_cb(text_w, client_data, cbs)
Widget text_w;              /* user pressed Return in this widget */
XtPointer client_data;        /* action_area passed as client data */
XmAnyCallbackStruct *cbs;   /* borrow the "event" field from this */
{
    Widget dflt, action_area = (Widget)client_data;

    XtVaGetValues(action_area, XmNdefaultButton, &dflt, NULL);
    if (dflt) /* sanity check -- this better work */
        /* make the default button think it got pushed.  This causes
         * "ok_pushed" to be called, but XtCallActionProc() causes
         * the button appear to be activated as if the user selected it.
         */
        XtCallActionProc(dflt, "ArmAndActivate", cbs->event, NULL, 0);
}

#define TIGHTNESS 20

Widget
CreateActionArea(parent, actions, num_actions)
Widget parent;
ActionAreaItem *actions;
int num_actions;
{
    Widget action_area, widget;
    int i;

    action_area = XtVaCreateWidget("action_area", xmFormWidgetClass, parent,
        XmNfractionBase, TIGHTNESS*num_actions - 1,
        XmNleftOffset,   10,
        XmNrightOffset,  10,
        NULL);

    for (i = 0; i < num_actions; i++) {
        widget = XtVaCreateManagedWidget(actions[i].label,
            xmPushButtonWidgetClass, action_area,
            XmNleftAttachment,       i? XmATTACH_POSITION : XmATTACH_FORM,
            XmNleftPosition,         TIGHTNESS*i,
            XmNtopAttachment,        XmATTACH_FORM,
            XmNbottomAttachment,     XmATTACH_FORM,
            XmNrightAttachment,
                    i != num_actions-1? XmATTACH_POSITION : XmATTACH_FORM,
            XmNrightPosition,        TIGHTNESS*i + (TIGHTNESS-1),
            XmNshowAsDefault,        i == 0,
            XmNdefaultButtonShadowThickness, 1,
            NULL);
        if (actions[i].callback)
            XtAddCallback(widget, XmNactivateCallback,
                actions[i].callback, actions[i].data);
        if (i == 0) {
            /* Set the action_area's default button to the first widget
             * created (or, make the index a parameter to the function
             * or have it be part of the data structure). Also, set the
             * pane window constraint for max and min heights so this
             * particular pane in the PanedWindow is not resizable.
             */
            Dimension height, h;
            XtVaGetValues(action_area, XmNmarginHeight, &h, NULL);
            XtVaGetValues(widget, XmNheight, &height, NULL);
            height += 2 * h;
            XtVaSetValues(action_area,
                XmNdefaultButton, widget,
                XmNpaneMaximum,   height,
                XmNpaneMinimum,   height,
                NULL);
        }
    }

    XtManageChild(action_area);

    return action_area;
}



-------------------------------------------------------------------------------
Subject: 128)*  How do I create a "busy working cursor"?

Answer: - in Baudouin's code (following), the idea is to keep in an array an
up-to-date list of all shells used in the application, and set for all of them
the cursor to a watch or to the default cursor, with the 2 functions provided.

- in Dan Heller's code (later), the idea is to turn on the watch cursor for the
top-level shell only, popup a working window to possibly abort the callback, and
manage some expose events during the callback.

- in the FAQ for comp.windows.x (#113), the idea is to bring a large window on
top of the application, hide all windows below it, and turn on the watch cursor
on this large window. Unmapping the large window resets the default cursor,
mapping it turns on the watch cursor.

From Baudouin Raoult (mab@ecmwf.co.uk)

void my_SetWatchCursor(w)
Widget w;
{
        static Cursor watch = NULL;

        if(!watch)
                watch = XCreateFontCursor(XtDisplay(w),XC_watch);

        XDefineCursor(XtDisplay(w),XtWindow(w),watch);
        XmUpdateDisplay(w);
}

void my_ResetCursor(w)
Widget w;
{
        XUndefineCursor(XtDisplay(w),XtWindow(w));
        XmUpdateDisplay(w);
}


Answer: A solution with lots of bells and whistles is

/* Written by Dan Heller.  Copyright 1991, O'Reilly && Associates.
 * This program is freely distributable without licensing fees and
 * is provided without guarantee or warrantee expressed or implied.
 * This program is -not- in the public domain.
 */

/* busy.c -- demonstrate how to use a WorkingDialog and to process
 * only "important" events.  e.g., those that may interrupt the
 * task or to repaint widgets for exposure.  Set up a simple shell
 * and a widget that, when pressed, immediately goes into its own
 * loop.  First, "lock" the shell so that a timeout cursor is set on
 * the shell and pop up a WorkingDialog.  Then enter loop ... sleep
 * for one second ten times, checking between each interval to see
 * if the user clicked the Stop button or if any widgets need to be
 * refreshed.  Ignore all other events.
 *
 * main() and get_busy() are stubs that would be replaced by a real
 * application; all other functions can be used "as is."
 */
#include <Xm/MessageB.h>
#include <Xm/PushB.h>
#include <X11/cursorfont.h>

Widget shell;
void TimeoutCursors();
Boolean CheckForInterrupt();

main(argc, argv)
int argc;
char *argv[];
{
    XtAppContext app;
    Widget button;
    XmString label;
    void get_busy();

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

    label = XmStringCreateSimple(
        "Boy, is *this* going to take a long time.");
    button = XtVaCreateManagedWidget("button",
        xmPushButtonWidgetClass, shell,
        XmNlabelString,          label,
        NULL);
    XmStringFree(label);
    XtAddCallback(button, XmNactivateCallback, get_busy, argv[1]);

    XtRealizeWidget(shell);
    XtAppMainLoop(app);
}

void
get_busy(widget)
Widget widget;
{
    int n;

    TimeoutCursors(True, True);
    for (n = 0; n < 10; n++) {
        sleep(1);
        if (CheckForInterrupt()) {
            puts("Interrupt!");
            break;
        }
    }
    if (n == 10)
        puts("done.");
    TimeoutCursors(False, NULL);
}

/* The interesting part of the program -- extract and use at will */
static Boolean stopped;  /* True when user wants to stop processing */
static Widget dialog;    /* WorkingDialog displayed when timed out */

/* timeout_cursors() turns on the "watch" cursor over the application
 * to provide feedback for the user that he's going to be waiting
 * a while before he can interact with the appliation again.
 */
void
TimeoutCursors(on, interruptable)
int on, interruptable;
{
    static int locked;
    static Cursor cursor;
    extern Widget shell;
    XSetWindowAttributes attrs;
    Display *dpy = XtDisplay(shell);
    XEvent event;
    Arg args[1];
    XmString str;
    extern void stop();

    /* "locked" keeps track if we've already called the function.
     * This allows recursion and is necessary for most situations.
     */
    on? locked++ : locked--;
    if (locked > 1 || locked == 1 && on == 0)
        return; /* already locked and we're not unlocking */

    stopped = False; /* doesn't matter at this point; initialize */
    if (!cursor) /* make sure the timeout cursor is initialized */
        cursor = XCreateFontCursor(dpy, XC_watch);

    /* if "on" is true, then turn on watch cursor, otherwise, return
     * the shell's cursor to normal.
     */
    attrs.cursor = on? cursor : None;

    /* change the main application shell's cursor to be the timeout
     * cursor (or to reset it to normal).  If other shells exist in
     * this application, they will have to be listed here in order
     * for them to have timeout cursors too.
     */
    XChangeWindowAttributes(dpy, XtWindow(shell), CWCursor, &attrs);

    XFlush(dpy);

    if (on) {
        /* we're timing out, put up a WorkingDialog.  If the process
         * is interruptable, allow a "Stop" button.  Otherwise, remove
         * all actions so the user can't stop the processing.
         */
        str = XmStringCreateSimple("Busy.  Please Wait.");
        XtSetArg(args[0], XmNmessageString, str);
        dialog = XmCreateWorkingDialog(shell, "Busy", args, 1);
        XmStringFree(str);
        XtUnmanageChild(
            XmMessageBoxGetChild(dialog, XmDIALOG_OK_BUTTON));
        if (interruptable) {
            str = XmStringCreateSimple("Stop");
            XtVaSetValues(dialog, XmNcancelLabelString, str, NULL);
            XmStringFree(str);
            XtAddCallback(dialog, XmNcancelCallback, stop, NULL);
        } else
            XtUnmanageChild(
                XmMessageBoxGetChild(dialog, XmDIALOG_CANCEL_BUTTON));
        XtUnmanageChild(
            XmMessageBoxGetChild(dialog, XmDIALOG_HELP_BUTTON));
        XtManageChild(dialog);
    } else {
        /* get rid of all button and keyboard events that occured
         * during the time out.  The user shouldn't have done anything
         * during this time, so flush for button and keypress events.
         * KeyRelease events are not discarded because accelerators
         * require the corresponding release event before normal input
         * can continue.
         */
        while (XCheckMaskEvent(dpy,
                ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
                | PointerMotionMask | KeyPressMask, &event)) {
            /* do nothing */;
        }
        XtDestroyWidget(dialog);
    }
}

/* User Pressed the "Stop" button in dialog. */
void
stop(dialog)
Widget dialog;
{
    stopped = True;
}

Boolean
CheckForInterrupt()
{
    extern Widget shell;
    Display *dpy = XtDisplay(shell);
    Window win = XtWindow(dialog);
    XEvent event;

    /* Make sure all our requests get to the server */
    XFlush(dpy);

    /* Let motif process all pending exposure events for us. */
    XmUpdateDisplay(shell);

    /* Check the event loop for events in the dialog ("Stop"?) */
    while (XCheckMaskEvent(dpy,
            ButtonPressMask | ButtonReleaseMask | ButtonMotionMask |
            PointerMotionMask | KeyPressMask | KeyReleaseMask,
            &event)) {
        /* got an "interesting" event. */
        if (event.xany.window == win)
            XtDispatchEvent(&event); /* it's in our dialog.. */
        else /* uninteresting event--throw it away and sound bell */
            XBell(dpy, 50);
    }
    return stopped;
}


-------------------------------------------------------------------------------
Subject: 129) TOPIC: ACKNOWLEDGEMENTS

This list was compiled using questions and answers posed to comp.windows.x.motif
and motif-talk. Some extracts were also taken from FAQs of comp.windows.x.  To
all who contributed one way or the other, thanks! I haven't often given
individual references, but  you may recognise contributions. If I have mangled
them too much, let me know.



That's all folks!


+----------------------+---+
  Jan Newmarch, Information Science and Engineering,
  University of Canberra, PO Box 1, Belconnen, Act 2616
  Australia. Tel: (Aust) 6-2522422. Fax: (Aust) 6-2522999

  ACSnet: jan@ise.canberra.edu.au
  ARPA:   jan%ise.canberra.edu.au@uunet.uu.net
  UUCP:   {uunet,ukc}!munnari!ise.canberra.edu.au!jan
  JANET:  jan%au.edu.canberra.ise@EAN-RELAY

+--------------------------+


Local Variables:
mode: outline
outline-regexp: "Subject: +[0-9]+)"
eval: (hide-body)
End: