[comp.windows.x] Sample widget menu program

charlesb@tdd.fai.com (Charles Brauer) (01/18/89)

Dear comp.windows.x:

Before Christmas, I asked Bob Scheifler and Ralph Swick for help on
an interesting problem I had using widgets.  I feel that the results
of Ralph's reply may be of interest to this news group, so I would
like to offer the enclosed program as an example of widget programming.
Comments and suggestions would be appreciated.

The problem is this:  about a year ago, Mr. Paul Vixie helped me put
together a "poor man's" marching menu system, using Athena Widgets.
At that time, we were running on R2 and Paul had to modify the source
to implement our menu system.  When R3 came along, the R2 patches would
no longer work, and, besides, I wanted to implement the menu's without
modifying the R3 source code.  Our approach is to display a set of menus 
that are composed of command widgets inside a box widget.  When one of the
command widgets is selected, a new sub-menu should appear to the right
(or down from) the first menu.  Suppose that you change your mind and go
back to the original menu, and select another item.  You would expect the
sub-menu to to disapear, and a new sub-menu to appear in its place.  The
problem is that you are inside a callback when you want to destroy the 
sub-menu. The enclosed program shows how to do it, and also, I have included
Ralph's reply to me when I complained to him that the old and new sub-menu's 
were both "flashing" on the screen in a temporary location before they were
moved to their final location.

I would publically like to thank Ralph Swick for his help, and I hope
he dosn't mind if I include his remarks.

Ralph's reply:

  The most obvious way to execute the callbacks after XtDispatchEvent
  returns is to write your own version of XtMainLoop() and check a queue
  of button callbacks on each cycle.  A quick hack, given below, is to
  take advantage of the WorkProc functions to use an already existing
  mechanism.  You'll have to decide yourself if the use of WorkProcs is
  acceptable in the finished application; if the user does 'mouse-ahead',
  you'll wind up processing the new events before executing the previous
  callback.  You may, however, be able to use this to your advantage.
      
Here is a "shar" file that contains the code to solve the above problem.
You should cut it off and feed it to "sh" for unpacking.

Enjoy.

Charles Brauer                 Mail Stop: B2-8
uunet.UU.NET!fai!charlesb      3055 Orchard Drive
Fujitsu America, Inc.          (408) 432-1300 Ext: 5226
                               San Jose, California 95134

-------------------------------- cut --------------------------------------

#! /bin/sh
##  This is a shell archive.  Remove anything before this line, then unpack
##  it by saving it into a file and typing "sh file".  To overwrite existing
##  files, type "sh file -c".  You can also feed this as standard input via
##  unshar, or by typing "sh <file".  If this archive is complete, you will
##  see the following message at the end:
#		"End of shell archive."
# Contents:  Readme Makefile Resources Menu.c tst.c
# Wrapped by charlesb@rome on Tue Jan 17 16:01:50 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f Readme -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Readme\"
else
echo shar: Extracting \"Readme\" \(164 characters\)
sed "s/^X//" >Readme <<'END_OF_Readme'
X
XTo make the test program, do:
X
X	 make
X
XThen do:
X
X	 xrdb Resources
X	 tst
X
XYou should see the Menu appear in the upper
Xleft corner of your screen.  
X
XCharles Brauer
END_OF_Readme
if test 164 -ne `wc -c <Readme`; then
    echo shar: \"Readme\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(271 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
XCFLAGS		=  -g
XLFLAGS      =  -g
XLIBS		=  -lXaw -lXmu -lXt -lX
XSRC			= tst.c Menu.c /X/lib/Xaw/*.c /X/lib/Xt/*.c
X
Xall:		tst 
X
Xtst:		tst.o Menu.o
X			cc -o tst tst.o Menu.o $(LDFLAGS) $(LIBS)
X
Xsaber:		$(SRC)
X			#load $(CFLAGS) $(SRC) -lXmu -lX
X
Xclean:		; rm -f *.o core tst
END_OF_Makefile
if test 271 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Resources -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Resources\"
else
echo shar: Extracting \"Resources\" \(365 characters\)
sed "s/^X//" >Resources <<'END_OF_Resources'
Xtst*font:                 9x15
Xtst*geometry:             300x300+10+10
Xtst*Background:  Wheat
X
Xtst*title.Background:     Red
Xtst*title.Foreground:     White
X
Xtst*mainMenu*Background:  Blue
Xtst*mainMenu*Foreground:  White
X
Xtst*levelOneMenu*Background:  Green
Xtst*levelOneMenu*Foreground:  Black
X
Xtst*levelTwoMenu*Background:  Tan
Xtst*levelTwoMenu*Foreground:  Black
END_OF_Resources
if test 365 -ne `wc -c <Resources`; then
    echo shar: \"Resources\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Menu.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Menu.c\"
else
echo shar: Extracting \"Menu.c\" \(11668 characters\)
sed "s/^X//" >Menu.c <<'END_OF_Menu.c'
X#include <stdio.h>
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include <X11/Box.h>
X#include <X11/Command.h>
X#include <X11/Cardinals.h>
X#include <X11/AsciiText.h>
X#include <X11/Shell.h>
X#include <X11/Viewport.h>
X
X#define MAX_BUTTONS   50
X#define MAX_MENUS     20
X#define MENU_BORDER_H 10
X
Xtypedef    void    (*voidptr)();
X
Xtypedef struct _menuRec {
X    Widget    widget;                    /* this menu */
X    Widget    buttons[MAX_BUTTONS];      /* buttons in our menu */
X    voidptr    buttonCall[MAX_BUTTONS];
X    int    buttonN;                      /* how many used in above array */
X    int    active;                       /* an active button, -1==no active  */
X} menuRec;
X
Xextern menuRec menus[MAX_MENUS+1];
Xextern int     menuN;
X
Xextern  void   enterNewLevel();
Xextern  Widget InitMenus();
XDisplay *display;
X
Xtypedef struct _workRec {
X	voidptr     buttonCall;
X	Widget      widget;
X	Widget      closure;
X	caddr_t     call_data;
X} workRec;
X
XBoolean CallButtonCallback(client_data)
Xcaddr_t client_data;
X{
X	workRec *workP = (workRec*)client_data;
X	(*workP->buttonCall) (workP->widget, workP->closure, workP->call_data);
X	return True;
X}
X
X/*************************************************************************
X * Highlight or unhighlight the given button.  Used by buttonPressed.
X */
X
Xstatic void FlipColors(button)
X    Widget button;
X{
X    static Pixel foreground, background;
X    static Arg args[] = {
X        { XtNforeground, (XtArgVal) &foreground },
X        { XtNbackground, (XtArgVal) &background } };
X
X    args[0].value = (XtArgVal) & foreground;
X    args[1].value = (XtArgVal) & background;
X    XtGetValues(button, args, XtNumber(args));
X
X    args[0].value = (XtArgVal) background;
X    args[1].value = (XtArgVal) foreground;
X    XtSetValues(button, args, XtNumber(args));
X}
X
X/*************************************************************************
X * buttonPressed: This is a callback routine used by addExistingButton.
X */
X
Xstatic void buttonPressed(widget, closure, callData)
X    Widget widget;
X    Widget closure;
X    caddr_t callData;
X{
X    /*
X     * This routine is called on all menu button presses.  Since all the
X     * menu buttons are always sensitive, the user may select something
X     * on a menu as a way to abandon something else selected elsewhere.
X     * 
X     * We need to destroy any menus or applications "beyond" the menu where
X     * this button press occurred; they will have destroy callbacks if
X     * they care about this.
X     * 
X     * After we've pruned the widget tree back to the point of our menu, we
X     * need to highlight our button and call its callback.  remember that
X     * we are intercepting the button callback to be here in the first place.
X     */
X
X    void destroyCallback();
X    Widget daddy;
X    menuRec *daddy_menu;
X    int i, k, daddy_level;
X
X    /* daddy is the widget that this button is a member of. scan menus[] for him. */
X
X    daddy = XtParent(widget);
X    for (i = 0; i < menuN && menus[i].widget != daddy; i++)
X    {
X    }
X    daddy_level = i;
X    daddy_menu = &menus[daddy_level];
X
X    /* unhighlight the previously selected button. */
X    if (daddy_menu->active != -1)
X    {
X        FlipColors(daddy_menu->buttons[daddy_menu->active]);
X        daddy_menu->active = -1;
X    }
X
X    /* every widget further up the menus[] array than daddy needs to be nuked. */
X    for (; menuN - 1 > daddy_level; menuN--)
X    {
X        if (menuN < 0) exit(0);
X        XtRemoveCallback(menus[menuN - 1].widget, XtNdestroyCallback,
X                           destroyCallback, NULL);
X
X        XtDestroyWidget(menus[menuN - 1].widget);
X    }
X
X    FlipColors(widget);
X
X    /* we know our widget is one of the ones in the button list for this */
X    /* menu; scan for it, and then call the corresponding callback.      */
X    daddy_menu->active = -1;
X    for (i = daddy_menu->buttonN - 1; i >= 0; i--)
X    {
X        if (daddy_menu->buttons[i] == widget)
X        {
X            workRec *workP = XtNew(workRec);
X            daddy_menu->active = i;
X            workP->buttonCall = daddy_menu->buttonCall[i];
X            workP->widget = widget;
X            workP->closure = closure;
X            workP->call_data = callData;
X            (void) XtAddWorkProc(CallButtonCallback, workP);
X        }
X    }
X}
X
X/*************************************************************************
X * widthOfBox: called by _addTitleToBox.
X */
X
Xint widthOfBox(w)
X    Widget w;
X{
X    static Dimension width, borderWidth;
X    static int wBox, hSpace;
X    static Arg args[] = {
X    { XtNwidth,       (XtArgVal) &width },
X    { XtNhSpace,      (XtArgVal) &hSpace },
X    { XtNborderWidth, (XtArgVal) &borderWidth } };
X
X    XtGetValues(w, args, XtNumber(args));
X    wBox = width - (2 * hSpace) - (2 * borderWidth);
X    return wBox;
X}
X
X/*************************************************************************/
X
Xstatic void destroyCallback(widget, closure, callData)
X    Widget widget;
X    caddr_t closure;
X    caddr_t callData;
X{
X    int n, my_level;
X    menuRec *menu;
X
X    my_level = -1;
X    for (n = 0, menu = menus; n < menuN; n++, menu++)
X    {
X        if (menu->widget == widget)
X        {
X            my_level = n;
X            break;
X        }
X    }
X    if (my_level == -1)
X    {
X        printf("destroyCallback: can't find my level.\n");
X        return;
X    }
X
X    for (n = my_level; n < menuN - 2; n++)
X        menus[n] = menus[n + 1];
X    menuN--;
X}
X
X/*************************************************************************
X * _titleCallBack: used by _addTitleToBox.
X */
X
X
Xstatic void _titleCallback(widget, closure, callData)
X    Widget widget;        /* not used */
X    caddr_t closure;    /* widget to raise */
X    caddr_t callData;    /* not used */
X{
X    Widget widget_to_raise = (Widget) closure;
X    Display *d = XtDisplay(widget_to_raise);
X    Window w = XtWindow(widget_to_raise);
X
X    /*
X     * This is a hack.  We are raising the window of the widget passed as
X     * our closure; as seen below, this widget is the parent of the box
X     * to which the title is being added.  This is the shell widget,
X     * which it pretty much has to be.  This all needs to change, such
X     * that addTitleToBox is renamed to initializeMenus() or some such.
X     * It's name presently indicates that it would work for any box,
X     * which is FALSE FALSE FALSE. 
X     */
X
X    XRaiseWindow(d, w);
X}
X
X/*************************************************************************
X * _addTitleToBox: Used by InitMenus.
X */
X
Xstatic void _addTitleToBox(title, box)
X    char *title;
X    Widget box;
X{
X    Arg arg[10];
X    int n;
X    static XtCallbackRec callback[2];
X
X    n = 0;
X    XtSetArg(arg[n], XtNwidth, widthOfBox(box)); n++;
X    XtSetArg(arg[n], XtNlabel, title); n++;
X    XtSetArg(arg[n], XtNjustify, XtJustifyLeft); n++;
X    callback[0].callback = _titleCallback;
X    callback[0].closure = (caddr_t) XtParent(box);
X    XtSetArg(arg[n], XtNcallback, callback); n++;
X    XtCreateManagedWidget("title", commandWidgetClass, box, arg, n);
X}
X
X/*************************************************************************
X * _openMenu: Used by openMenu, and by openWideMenu.
X */
X
X#define opt_wide    0x0001    /* default: narrow */
X
Xstatic void _openMenu(parent, command, name, options)
X    Widget parent;
X    Widget command;
X    char *name;
X    int options;
X{
X    void enterNewLevel();
X    Arg arg[10];
X    int n;
X
X    n = 0;
X    if (options & opt_wide)
X    {
X        XtSetArg(arg[n], XtNwidth, widthOfBox(parent)); n++;
X    }
X    else
X    {
X        /* XtSetArg(arg[n], XtNalignVert, TRUE); n++; */
X    }
X    enterNewLevel(XtCreateWidget(name, boxWidgetClass, parent, arg, n));
X}
X
X
X/*************************************************************************
X *  addExistingButton: routine used by: addButton.
X */
X
Xvoid addExistingButton(button, func, widget)
X    Widget button;
X    void (*func) ();
X    Widget widget;
X{
X    Arg arg[10];
X    int n = 0;
X    menuRec *menu = &menus[menuN-1];
X
X    if (menuN-1 < 0) exit(0);
X
X    XtSetArg(arg[n], XtNgrayWhenInsensitive, False); n++;
X    XtSetArg(arg[n], XtNsensitive, (func) ? True : False); n++;
X    XtSetValues(button, arg, n);
X
X    if (func)
X        XtAddCallback(button, XtNcallback, buttonPressed, widget);
X
X    menu->buttons[menu->buttonN] = button;
X    menu->buttonCall[menu->buttonN] = func;
X    menu->buttonN++;
X}
X
X/*************************************************************************
X * enterNewLevel: Used by _openMenu. (applic_widg) this function should be
X *                called by any application code which creates a new widget
X *                (form, etc) that shares an outer box with some Menus.
X */
X
Xvoid enterNewLevel(w)
X    Widget w;
X{
X    menuRec *menu = &menus[menuN];
X
X    menu->widget = w;
X    menu->buttonN = 0;
X    menu->active = -1;
X    menuN++;
X    XtAddCallback(w, XtNdestroyCallback, destroyCallback, NULL);
X}
X
Xstatic XrmOptionDescRec options[] = {
X    {"-label", XtNlabel, XrmoptionSepArg, NULL}
X};
X
X/*************************************************************************
X * The following routines are called by application programs.  Routines
X * above this point a supporting routines only, and not known by the
X * application programmer.
X */
X
Xstatic Boolean scroll;
X
Xstatic XtResource resources[] = {
X    {"scroll", "Scroll", XtRBoolean, sizeof(Boolean),
X             (Cardinal)&scroll, XtRString, "False"},
X};
X
XWidget InitMenus(application_name, menu_title, p_argc, argv)
X    char *application_name;
X    char *menu_title;
X    int *p_argc;
X    char *argv[];
X{
X    Arg arg[10];
X    int n;
X    Widget outer, toplevel;
X
X    toplevel = XtInitialize("main", application_name, options, XtNumber(options),
X                  p_argc, argv);
X
X    XtGetApplicationResources( toplevel, (caddr_t)NULL,
X                               resources, XtNumber(resources),
X                               NULL, ZERO );
X
X    display = XtDisplay(toplevel);
X
X    n = 0;
X    XtSetArg(arg[n], XtNallowShellResize, TRUE); n++;
X    XtSetValues(toplevel, arg, n);
X
X    n = 0;
X    XtSetArg(arg[n], XtNx, 200); n++;
X    XtSetArg(arg[n], XtNy, 200); n++;
X    /* XtSetArg(arg[n], XtNalignVert, FALSE); n++; */
X    XtSetArg(arg[n], XtNshrinkToFit, FALSE); n++;
X    outer = XtCreateManagedWidget(NULL, boxWidgetClass, toplevel, arg, n);
X
X    XtRealizeWidget(toplevel);
X
X    _addTitleToBox(menu_title, outer);
X    return outer;
X}
X
X/***********************************************************************/
X
XopenMenu(parent, command, name)
X    Widget parent;
X    Widget command;
X    char *name;
X{
X    _openMenu(parent, command, name, 0);
X}
X
X/*************************************************************************/
X
Xvoid openWideMenu(parent, command, name)
X    Widget parent;
X    Widget command;
X    char *name;
X{
X    _openMenu(parent, command, name, opt_wide);
X}
X/*************************************************************************/
X
XWidget addButton(label, func, widget)
X    char *label;
X    void (*func) ();
X    Widget widget;
X{
X    Widget button;
X    menuRec *menu = &menus[menuN-1];
X    Arg arg[10];
X    int n;
X
X    if (menuN < 0) exit(0);
X    n = 0;
X    button = XtCreateManagedWidget(label, commandWidgetClass, menu->widget, arg, n);
X
X    addExistingButton(button, func, widget);
X    return button;
X}
X
X/*************************************************************************/
X
Xstatic void SetInsensitiveBorder(w, p)
X    Widget w;
X    Pixmap p;
X{
X    Arg arg[10];
X    int n;
X
X    n = 0;
X    XtSetArg(arg[n], XtNinsensitiveBorder, p); n++;
X    XtSetValues(w, arg, n);
X}
X
X/*************************************************************************/
X
Xvoid closeMenu()
X{
X    menuRec *menu = &menus[menuN-1];
X
X    if (menuN < 0) exit(0);
X    XtManageChild(menu->widget);
X}
END_OF_Menu.c
if test 11668 -ne `wc -c <Menu.c`; then
    echo shar: \"Menu.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f tst.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"tst.c\"
else
echo shar: Extracting \"tst.c\" \(4831 characters\)
sed "s/^X//" >tst.c <<'END_OF_tst.c'
X/*******************************************************************************
X * tst - test the Menu routines.
X */
X
X#include <stdio.h>
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include <X11/Box.h>
X#include <X11/Command.h>
X#include <X11/Cardinals.h>
X#include <X11/AsciiText.h>
X#include <X11/Viewport.h>
X
X#define MAX_BUTTONS   50
X#define MAX_MENUS     20
X#define MENU_BORDER_H 10
X
Xtypedef    void    (*voidptr)();
X
Xtypedef struct _menuRec {
X    Widget    widget;                    /* this menu */
X    Widget    buttons[MAX_BUTTONS];      /* buttons in our menu */
X    voidptr    buttonCall[MAX_BUTTONS];
X    int    buttonN;                      /* how many used in above array */
X    int    active;                       /* an active button, -1==no active  */
X} menuRec;
X
XmenuRec menus[MAX_MENUS+1];
Xint     menuN;
X
Xextern  void   enterNewLevel();
Xextern  Widget InitMenus();
XDisplay *display;
X
X/******************************************************************************/
X
Xvoid Destroyed(widget, closure, callData)
XWidget widget;   
Xcaddr_t closure; 
Xcaddr_t callData;
X{
X    exit(0);
X}
X
X/******************************************************************************/
X
Xvoid quitCommand(widget, closure, callData)
XWidget widget;
XWidget closure;
Xcaddr_t callData;
X{
X    XtDestroyWidget( closure );
X}
X
X/******************************************************************************/
X
Xvoid levelTwoMenu(widget, parent, callData)
XWidget widget, parent;
Xcaddr_t callData;
X{
X    Widget addButton();
X    void closeMenu();
X
X    openMenu(parent, widget, "levelTwoMenu");
X
X    addButton("Button 1", NULL, NULL);
X    addButton("Quit", quitCommand, parent);
X
X    closeMenu();
X}
X
X/******************************************************************************/
X
Xvoid levelOneMenu(widget, parent, callData)
XWidget widget, parent;
Xcaddr_t callData;
X{
X    Widget addButton();
X    void closeMenu();
X
X    openMenu(parent, widget, "levelOneMenu");
X
X    addButton("Go To Level 2", levelTwoMenu, parent);
X    addButton("Quit", quitCommand, parent);
X
X    closeMenu();
X}
X
X/******************************************************************************/
X
Xvoid DoList(widget, parent, callData)
XWidget widget;
XWidget parent;
Xcaddr_t callData;
X{
X    PrintFile("/etc/printcap", parent);
X}
X
X/******************************************************************************/
X
XPrintFile(filename,parent)
Xcaddr_t filename;
XWidget parent;
X{
X    static  XtCallbackRec callback[2]; 
X    char file_name[100];
X    Arg arg[100];
X    int n;
X    Widget pWidget, scrollBar, formWidget;
X    FILE *fp, *fopen();
X
X    fp = fopen(filename,"r");
X    if ( fp == 0 )
X    {
X        fprintf(stderr, "/etc/printcap NOT EXIST");
X        exit(0);
X    }
X
X    n = 0;
X    formWidget = XtCreateWidget("scrollwindow",formWidgetClass, parent, arg, n);
X    enterNewLevel(formWidget);
X
X    n = 0;
X    XtSetArg(arg[n], XtNtextOptions, (scrollVertical|resizeWidth)); n++;
X    XtSetArg(arg[n], XtNheight, 400); n++;
X    XtSetArg(arg[n], XtNfile, file_name); n++;
X    XtSetArg(arg[n], XtNwidth, 500); n++;
X    XtSetArg(arg[n], XtNeditType, XttextRead); n++;
X
X    strcpy(file_name,filename);
X    scrollBar = XtCreateManagedWidget( NULL, asciiDiskWidgetClass,
X                                       formWidget, arg, n );
X
X    n = 0;
X    XtSetArg(arg[n], XtNlabel, "Printing Out ?"); n++;
X    callback[0].callback = quitCommand;
X    callback[0].closure = (caddr_t)parent;
X    XtSetArg(arg[n], XtNcallback, callback);  n++;
X    XtSetArg(arg[n], XtNfromVert, scrollBar); n++;
X    XtSetArg(arg[n], XtNvertDistance, 5);     n++;
X    pWidget = XtCreateManagedWidget(NULL,commandWidgetClass,formWidget,arg,n);
X
X    n = 0;
X    XtSetArg(arg[n], XtNlabel, "Quit ?"); n++;
X    callback[0].callback = quitCommand;
X    callback[0].closure = (caddr_t)parent;
X    XtSetArg(arg[n], XtNcallback, callback);  n++;
X    XtSetArg(arg[n], XtNfromVert, scrollBar); n++;
X    XtSetArg(arg[n], XtNfromHoriz, pWidget);  n++;
X    XtSetArg(arg[n], XtNvertDistance, 5);     n++;
X    XtCreateManagedWidget(NULL, commandWidgetClass, formWidget, arg, n);
X
X    XtManageChild( formWidget );
X}
X
X/******************************************************************************/
X
Xvoid MainMenu(outer)
XWidget outer;
X{
X    Widget addButton();
X    void closeMenu();
X
X    openMenu(outer, NULL, "mainMenu");
X
X    addButton("Go to Level 1", levelOneMenu, outer);
X    addButton("Go to Level 2", levelTwoMenu, outer);
X    addButton("List", DoList, outer);
X    addButton("Quit", quitCommand, outer);
X
X    closeMenu();
X}
X
X/******************************************************************************/
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X    Widget outer;
X
X    outer = InitMenus("tst", "Test Program", &argc, argv);
X
X    XtAddCallback(outer, XtNdestroyCallback, Destroyed, NULL);
X    MainMenu(outer);
X
X    XtMainLoop();
X}
END_OF_tst.c
if test 4831 -ne `wc -c <tst.c`; then
    echo shar: \"tst.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0

vixie@decwrl.dec.com (Paul A Vixie) (01/19/89)

Wow.  The evil that men do really does chase them forever.  I can see in
the code Charles Brauer just posted some of the unclever hacks and
self-derisive commentary I made as a contractor at Fujitsu America last
year.

Those of you who cannot compile this because of the lack of XtNshrinkToFit
et al, well, it looks like R3 didn't include all the diffs I made to the
Box widget.  I know I mailed (some of) them to xbugs, but given their
ugliness I am unsurprised that they didn't make the distribution.

Charles, perhaps you should post your Box widget source code?  It probably
won't win any awards for portability or cleanliness, but it would let every-
body run the demo.  It might also give someone an idea for a real menu
widget, which as far as I know, does not yet exist in the world.
--
Paul Vixie
Work:    vixie@decwrl.dec.com    decwrl!vixie    +1 415 853 6600
Play:    paul@vixie.sf.ca.us     vixie!paul      +1 415 864 7013

swick@ATHENA.MIT.EDU (Ralph R. Swick) (01/20/89)

I just added

#define XtNgrayWhenInsensitive		"grayWhenInsensitive"
#define XtNshrinkToFit			"shrinkToFit"

to the beginning of Menu.c and everything worked fine with the R3 widgets.

vixie@decwrl.dec.com (Paul A Vixie) (01/21/89)

[Swick]
# I just added
#
# #define XtNgrayWhenInsensitive		"grayWhenInsensitive"
# #define XtNshrinkToFit			"shrinkToFit"
#
# to the beginning of Menu.c and everything worked fine with the R3 widgets.

"Worked fine" != "worked the way my hacked up R2 widget worked".  Defining
these in Menu.c won't tell the Box widget to act differently.  I just looked
at the source to the R3 Xaw, and I see nothing that looks at these two fields.

XtNalignVert would also be a handy hack to have in the general R3 widget
set.  I'm not volunteering to hack them in again, but if Charles will post
his R2 Box source, I'm sure someone will do the feature merge.

--
Paul Vixie
Work:    vixie@decwrl.dec.com    decwrl!vixie    +1 415 853 6600
Play:    paul@vixie.sf.ca.us     vixie!paul      +1 415 864 7013

charlesb@tdd.fai.com (Charles Brauer) (01/28/89)

     When I returned from the X Conference, I noticed Paul Vixie's reply to my
posting on January 18th.  I apologize for not replying sooner.

     Paul Vixie suggested that I also post the Box widget source.  I have
re-posted the original "sample widget menu program", along with patches to the 
R3 source, and to illustrate some of the ideas that Paul Vixie and I had last
year, I have expanded the demonstration program.  Paul pointed out that the
idea of a "XtNshrinkToFit" needs some further explanation, so I would like to
first discuss the motivation behind the need for this feature (abomination?).

MOTIVATION:

     Suppose that you would like to have independent peer processes that manage
an area of the screen.  The current buzzword for this screen area is "desk top".
Our idea was to have Desk Tops that are all the same size, but staggered like
a deck of cards.  Back in X.V10R4 there was a "Menu" system like this.  You 
could mouse up and down on one of the cards to select an item, and if you went
outside the card, and over to another card, that card would then rise to the
top of the stack, so that you could then select items on this new card.

     How do you re-implement such a system using widgets?  Our approach was to
use command widgets inside a box widget for the various sets of menu items on 
each Desk Top. Desk Tops are also implemented as a Box widget with a Command 
widget as a "banner" at the top.  Desk Tops could then be selected by clicking 
on the header that is visible in the staggard deck.  The problem with using
widgets is that the Box widget that is used to contain the command widgets 
(menu items) wants to shrink-to-fit.  This is (almost) o.k. for the menu boxes,
but, it is not o.k. for the Desk Top.  For that, you want to suppress the 
shrink- to-fit feature of widgets.  

    Unlike the X.V10R4 menu system, we wanted to have sub-menus appear when a
menu item was selected.  And likewise, as sub-menu items were selected, another
sub-menu should appear.  The top level menu, and all sub-menus, would remain on
the Desk Top so that the user could see a trace of the menu tree traversal.
Since users have become accustom to seeing menu items aligned in the vertical
direction there needs to be a mechanism for doing this, but widgets have a 
mind of their own and do not want to align in only one direction.  So naturally
I had Paul Vixie change the source code.  As Paul mentioned in the last mail 
message, we submitted the changes on how to do this about a year ago, but, they
were not adopted.  I can see why... our changes were obviously offensive to
the original design of widgets.

PROBLEMS:

    There are other interesting problems that arose from this foray into Widget-
land.  After you select an item from the top level menu, and continue to select
items from the sub-menus, the question becomes: how do you delete all the sub-
menus from the Desk Top when you go back and select another item from the top
level menu?  Or, to put it another way, how do you delete child widgets from a
callback?  Ralph Swick's answer to this question was included in my last mail
message, and is implemented in the source code at the end of this message.  For
me, the true significance of Ralph's reply was that he could solve the problem
WITHOUT changing the widget source code... something that I am beginning to
resist more and more.

CONCLUSIONS:

     The enclosed file called Menu.c is a thin layer on top of widgets, and its
true intention was to isolate staff programmers from having to get involved 
with widget programming.  Hey... last year we were naive.  This proved to be 
successful in that we were able to build a lot of application code, WITHOUT our
programmers knowing anything about widgets.  I am throughly convinced that had
we not done this, we would not have succeeded in meeting a severe deadline.  
What we ended up with is not a good use of widgets, but on the other hand, I am
beginning to question: what is the proper use of widgets?  It seems to me that
widgets are an inflexible set of tools that are difficult to control.

     After having worked with widgets for about a year now, I am also starting
to ask more fundamental, sole searching, questions about their design.  It 
seems to me that Object Oriented programming in C is an oxymoron.  We are now
suffering through the C++/InterViews learning curve, but so far, I would
recommend that people also consider this approach before you get sucked in too 
deep into widgets.

ENCLOSED SAMPLE PROGRAM:

     As Paul Vixie asked, I have included patches that you need to apply to
your R3 source code.  I applied these patches first, and then all the X 
Consortium patches, and so far, there are no collisions.  The patches are not
as complete as Paul Vixie's original R2 patches, because widgets have changed 
radically since R2, and Paul's patches no longer come close to fitting into R3.
As you can tell by the above dialogue, I am loosing enthusiasm for trying to
keep our original ideas going.  Hopefully, however, the enclosed code, and our
experience will be of some help.

     First apply the enclosed patch file.  You can easily reverse the changes,
so I don't think this is too scary.  If you are really nervous, just uncomment
the lines:

    #define XtNgrayWhenInsensitive      "grayWhenInsensitive"
    #define XtNshrinkToFit              "shrinkToFit"

in the beginning of Menu.c.  The Desk Tops will not appear as they should, but
at least you can get the idea.  The Makefile builds two crude test programs
called "deskTop1" and deskTop2"  First do "xrdb Resources" to set the geometry
and color (if you don't have color... thats o.k. too, it still works).  Execute
both deskTop programs, putting each in the background.  Select the "banner" of
the lower deskTop to raise the deskTop.  Items selected in the top level menu
only activate sub-menus two levels deep, but you can see by the simplicity of
the deskTop sample programs, that you can easily make things complex in a hurry.

Charles Brauer                 Mail Stop: B2-8
uunet!fai!charlesb             3055 Orchard Drive
Fujitsu America, Inc.          (408) 432-1300 Ext: 5226
                               San Jose, California 95134

------------------------- cut -------------------------------------------------
#! /bin/sh
##  This is a shell archive.  Remove anything before this line, then unpack
##  it by saving it into a file and typing "sh file".  To overwrite existing
##  files, type "sh file -c".  You can also feed this as standard input via
##  unshar, or by typing "sh <file".  If this archive is complete, you will
##  see the following message at the end:
#		"End of shell archive."
# Contents:  Makefile Readme fujitsu.diffs Resources deskTop1.c
#   deskTop2.c Menu.c
# Wrapped by charlesb@rome on Fri Jan 27 17:06:17 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(407 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
XCFLAGS		=  -O
XLFLAGS      =  -O
XLIBS		=  -lXaw -lXmu -lXt -lX
XSRC			= deskTop1.c Menu.c /X/lib/Xaw/*.c /X/lib/Xt/*.c
X
Xall:		deskTop1 deskTop2 
X
XdeskTop1:	deskTop1.o Menu.o
X			cc -o deskTop1 deskTop1.o Menu.o $(LDFLAGS) $(LIBS)
X
XdeskTop2:	deskTop2.o Menu.o
X			cc -o deskTop2 deskTop2.o Menu.o $(LDFLAGS) $(LIBS)
X
Xsaber:		$(SRC)
X			#load $(CFLAGS) $(SRC) -lXmu -lX
X
Xclean:		; rm -f *.o core deskTop1 deskTop2
END_OF_Makefile
if test 407 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Readme -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Readme\"
else
echo shar: Extracting \"Readme\" \(284 characters\)
sed "s/^X//" >Readme <<'END_OF_Readme'
X
XTo make the test program, do:
X
X	 make
X
XThen do:
X
X	 xrdb Resources
X	 deskTop1 &
X	 deskTop2 &
X
XYou should see the two Desk Tops appear in the upper
Xleft corner of your screen.  To raise the lower Desk
XTop, click in its "banner" area.
X
XCharles Brauer
XFujitsu America
Xuunet.fai.charlesb
END_OF_Readme
if test 284 -ne `wc -c <Readme`; then
    echo shar: \"Readme\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f fujitsu.diffs -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"fujitsu.diffs\"
else
echo shar: Extracting \"fujitsu.diffs\" \(6368 characters\)
sed "s/^X//" >fujitsu.diffs <<'END_OF_fujitsu.diffs'
X*** lib/Xaw/Box.c	Fri Sep 23 06:33:49 1988
X--- lib/Xaw/Box.c.new	Thu Nov 10 12:00:58 1988
X***************
X*** 44,54 ****
X--- 44,61 ----
X   *
X   ****************************************************************/
X  
X+ static Boolean defF = FALSE;
X+ static Boolean defT = TRUE;
X+ 
X  static XtResource resources[] = {
X      {XtNhSpace, XtCHSpace, XtRDimension, sizeof(Dimension),
X  	 XtOffset(BoxWidget, box.h_space), XtRImmediate, (caddr_t)4},
X      {XtNvSpace, XtCVSpace, XtRDimension, sizeof(Dimension),
X  	 XtOffset(BoxWidget, box.v_space), XtRImmediate, (caddr_t)4},
X+     {XtNalignVert, XtCAlignVert, XtRBoolean, sizeof(Boolean),
X+          XtOffset(BoxWidget, box.align_vert), XtRBoolean, (caddr_t)&defF},
X+     {XtNshrinkToFit, XtCShrinkToFit, XtRBoolean, sizeof(Boolean),
X+          XtOffset(BoxWidget, box.shrink_to_fit), XtRBoolean, (caddr_t)&defT},
X  };
X  
X  /****************************************************************
X***************
X*** 163,169 ****
X  	    if (widget->core.mapped_when_managed) num_mapped_children++;
X  	    /* Compute widget width */
X  	    bw = widget->core.width + 2*widget->core.border_width + h_space;
X! 	    if (lw + bw > width) {
X  		if (lw > h_space) {
X  		    /* At least one widget on this line, and
X  		     * can't fit any more.  Start new line.
X--- 170,176 ----
X  	    if (widget->core.mapped_when_managed) num_mapped_children++;
X  	    /* Compute widget width */
X  	    bw = widget->core.width + 2*widget->core.border_width + h_space;
X! 	    if (lw + bw > width || bbw->box.align_vert) {
X  		if (lw > h_space) {
X  		    /* At least one widget on this line, and
X  		     * can't fit any more.  Start new line.
X***************
X*** 224,229 ****
X--- 231,254 ----
X  
X      *reply_width = Max(w, 1);
X      *reply_height = Max(h, 1);
X+ 
X+     if (! bbw->box.shrink_to_fit) {
X+ 	if (bbw->box.unshrinking_height==0 && bbw->core.height>=100)
X+             bbw->box.unshrinking_height = bbw->core.height;
X+ 	if (bbw->box.unshrinking_width==0 && bbw->core.width>=100)
X+             bbw->box.unshrinking_width = bbw->core.width;
X+ 
X+ 	if (bbw->box.unshrinking_width==0) {
X+ 	    AssignMax(*reply_width, bbw->core.width);
X+ 	} else {
X+ 	    AssignMax(*reply_width, bbw->box.unshrinking_width);
X+ 	}
X+ 	if (bbw->box.unshrinking_height==0) {
X+ 	    AssignMax(*reply_height, bbw->core.height);
X+ 	} else {
X+ 	    AssignMax(*reply_height, bbw->box.unshrinking_height);
X+ 	}
X+     }
X  }
X  
X  /*
X***************
X*** 516,521 ****
X--- 541,548 ----
X      newbbw->box.last_query_width = newbbw->box.last_query_height = 0;
X      newbbw->box.preferred_width = Max(newbbw->box.h_space, 1);
X      newbbw->box.preferred_height = Max(newbbw->box.v_space, 1);
X+     newbbw->box.unshrinking_width = 0;
X+     newbbw->box.unshrinking_height = 0;
X  
X      if (newbbw->core.width == 0)
X          newbbw->core.width = newbbw->box.preferred_width;
X*** lib/Xaw/Box.h	Sun Oct 23 10:22:40 1988
X--- lib/Xaw/Box.h.new	Thu Nov 10 11:19:42 1988
X***************
X*** 51,56 ****
X--- 51,57 ----
X   width		     Width		Dimension	0
X   x		     Position		Position	0
X   y		     Position		Position	0
X+  alignVert	     AlignVert		Boolean		False
X  
X  */
X  
X***************
X*** 58,63 ****
X--- 59,69 ----
X  /* New fields */
X  #define XtNhSpace		"hSpace"
X  #define XtNvSpace		"vSpace"
X+ #define XtNalignVert		"alignVert"
X+ #define XtNshrinkToFit		"shrinkToFit"
X+ 
X+ #define XtCAlignVert		"AlignVert"
X+ #define	XtCShrinkToFit		"ShrinkToFit"
X  
X  /* Class record constants */
X  
X*** lib/Xaw/BoxP.h	Tue Sep  6 13:40:56 1988
X--- lib/Xaw/BoxP.h.new	Fri Sep 30 08:56:17 1988
X***************
X*** 62,67 ****
X--- 62,69 ----
X      Dimension	preferred_width, preferred_height;
X      Dimension	last_query_width, last_query_height;
X      XtGeometryMask last_query_mode;
X+     Boolean	align_vert, shrink_to_fit;
X+     Dimension   unshrinking_height, unshrinking_width;
X  } BoxPart;
X  
X  
X*** lib/Xaw/Command.c	Thu Nov  3 17:14:18 1988
X--- lib/Xaw/Command.c.new	Thu Nov 10 15:51:17 1988
X***************
X*** 61,66 ****
X--- 61,67 ----
X       <Btn1Up>:		notify() unset()	";
X  
X  #define offset(field) XtOffset(CommandWidget, field)
X+ static Boolean defT = TRUE;
X  static XtResource resources[] = { 
X  
X     {XtNcallback, XtCCallback, XtRCallback, sizeof(caddr_t), 
X***************
X*** 67,72 ****
X--- 68,75 ----
X        offset(command.callbacks), XtRCallback, (caddr_t)NULL},
X     {XtNhighlightThickness, XtCThickness, XtRDimension, sizeof(Dimension),
X        offset(command.highlight_thickness), XtRImmediate, (caddr_t)2},
X+    {XtNgrayWhenInsensitive, XtCGrayWhenInsensitive, XtRBoolean, sizeof(Boolean),
X+ 	 offset(command.gray_when_insensitive), XtRBoolean, (caddr_t)&defT},
X  };
X  #undef offset
X  
X*** lib/Xaw/Command.h	Sun Oct 23 10:28:51 1988
X--- lib/Xaw/Command.h.new	Thu Nov 10 11:34:54 1988
X***************
X*** 61,66 ****
X--- 61,67 ----
X   mappedWhenManaged   MappedWhenManaged	Boolean		True
X   resize		     Resize		Boolean		True
X   sensitive	     Sensitive		Boolean		True
X+  grayWhenInsensitive GrayWhenInsensitive Boolean        True
X   width		     Width		Dimension	text width
X   x		     Position		Position	0
X   y		     Position		Position	0
X***************
X*** 70,75 ****
X--- 71,78 ----
X  #define XtNcallback		"callback"
X  #define XtNhighlightThickness   "highlightThickness"
X  #define XtNtranslations		"translations"
X+ #define XtNgrayWhenInsensitive  "grayWhenInsensitive"
X+ #define XtCGrayWhenInsensitive  "GrayWhenInsensitive"
X  
X  extern WidgetClass     commandWidgetClass;
X  
X*** lib/Xaw/CommandP.h	Tue Sep 27 08:20:04 1988
X--- lib/Xaw/CommandP.h.new	Thu Nov 10 11:37:27 1988
X***************
X*** 85,90 ****
X--- 85,91 ----
X      GC          inverse_GC;
X      Boolean     set;
X      Boolean     highlighted;
X+     Boolean     gray_when_insensitive;
X  } CommandPart;
X  
X  
X*** lib/Xaw/CommandI.h	Tue Oct 18 09:36:47 1988
X--- lib/Xaw/CommandI.h.new	Thu Nov 10 11:41:55 1988
X***************
X*** 58,63 ****
X--- 58,64 ----
X  #define ComWgrayGC                       cbw->label.gray_GC
X  #define ComWgrayPixmap                   cbw->label.gray_pixmap
X  #define ComWsensitive                    cbw->core.sensitive 
X+ #define ComWgrayWhenInsensitive          cbw->command.gray_when_insensitive
X  #define ComWcallbackList                 cbw->command.callback_list
X  #define ComWcallback                     cbw->command.callback
X  #define ComWclosure                      cbw->command.closure
END_OF_fujitsu.diffs
if test 6368 -ne `wc -c <fujitsu.diffs`; then
    echo shar: \"fujitsu.diffs\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Resources -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Resources\"
else
echo shar: Extracting \"Resources\" \(919 characters\)
sed "s/^X//" >Resources <<'END_OF_Resources'
XdeskTop1*font:                     9x15
XdeskTop1*geometry:                 300x300+35+10
XdeskTop1*Background:               Wheat
X
XdeskTop1*title.Background:         Red
XdeskTop1*title.Foreground:         White
X
XdeskTop1*mainMenu*Background:      Blue
XdeskTop1*mainMenu*Foreground:      White
X
XdeskTop1*levelOneMenu*Background:  Green
XdeskTop1*levelOneMenu*Foreground:  Black
X
XdeskTop1*levelTwoMenu*Background:  Tan
XdeskTop1*levelTwoMenu*Foreground:  Black
X
XdeskTop2*font:                     9x15
XdeskTop2*geometry:                 300x300+10+35
XdeskTop2*Background:               Wheat
X
XdeskTop2*title.Background:         Orange
XdeskTop2*title.Foreground:         White
X
XdeskTop2*mainMenu*Background:      Blue
XdeskTop2*mainMenu*Foreground:      White
X
XdeskTop2*levelOneMenu*Background:  Green
XdeskTop2*levelOneMenu*Foreground:  Black
X
XdeskTop2*levelTwoMenu*Background:  Tan
XdeskTop2*levelTwoMenu*Foreground:  Black
X
END_OF_Resources
if test 919 -ne `wc -c <Resources`; then
    echo shar: \"Resources\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f deskTop1.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"deskTop1.c\"
else
echo shar: Extracting \"deskTop1.c\" \(2751 characters\)
sed "s/^X//" >deskTop1.c <<'END_OF_deskTop1.c'
X/*******************************************************************************
X * deskTop1 - test the Menu routines.
X */
X
X#include <stdio.h>
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include <X11/Box.h>
X#include <X11/Command.h>
X#include <X11/Cardinals.h>
X#include <X11/AsciiText.h>
X#include <X11/Viewport.h>
X
X#define MAX_BUTTONS   50
X#define MAX_MENUS     20
X#define MENU_BORDER_H 10
X
Xtypedef void (*voidptr)();
X
Xtypedef struct _menuRec {
X    Widget    widget;                    /* this menu */
X    Widget    buttons[MAX_BUTTONS];      /* buttons in our menu */
X    voidptr   buttonCall[MAX_BUTTONS];
X    int       buttonN;                   /* how many used in above array */
X    int       active;                    /* an active button, -1==no active  */
X} menuRec;
X
XmenuRec menus[MAX_MENUS+1];
Xint menuN;
X
Xextern  void   enterNewLevel();
Xextern  Widget InitMenus();
XDisplay *display;
X
X/******************************************************************************/
X
Xvoid Destroyed(widget, closure, callData)
XWidget widget;   
Xcaddr_t closure; 
Xcaddr_t callData;
X{
X    exit(0);
X}
X
X/******************************************************************************/
X
Xvoid quitCommand(widget, closure, callData)
XWidget widget;
XWidget closure;
Xcaddr_t callData;
X{
X    XtDestroyWidget( closure );
X}
X
X/******************************************************************************/
X
Xvoid levelTwoMenu(widget, parent, callData)
XWidget widget, parent;
Xcaddr_t callData;
X{
X    Widget addButton();
X    void closeMenu();
X
X    openMenu(parent, widget, "levelTwoMenu");
X
X    addButton("Button 1", NULL, NULL);
X    addButton("Quit", quitCommand, parent);
X
X    closeMenu();
X}
X
X/******************************************************************************/
X
Xvoid levelOneMenu(widget, parent, callData)
XWidget widget, parent;
Xcaddr_t callData;
X{
X    Widget addButton();
X    void closeMenu();
X
X    openMenu(parent, widget, "levelOneMenu");
X
X    addButton("Go To Level 2", levelTwoMenu, parent);
X    addButton("Quit", quitCommand, parent);
X
X    closeMenu();
X}
X
X/******************************************************************************/
X
Xvoid MainMenu(outer)
XWidget outer;
X{
X    Widget addButton();
X    void closeMenu();
X
X    openMenu(outer, NULL, "mainMenu");
X
X    addButton("Go to Level 1", levelOneMenu, outer);
X    addButton("Go to Level 2", levelTwoMenu, outer);
X    addButton("Quit", quitCommand, outer);
X
X    closeMenu();
X}
X
X/******************************************************************************/
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X    Widget outer;
X
X    outer = InitMenus("deskTop1", "Desk Top One", &argc, argv);
X
X    XtAddCallback(outer, XtNdestroyCallback, Destroyed, NULL);
X    MainMenu(outer);
X
X    XtMainLoop();
X}
END_OF_deskTop1.c
if test 2751 -ne `wc -c <deskTop1.c`; then
    echo shar: \"deskTop1.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f deskTop2.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"deskTop2.c\"
else
echo shar: Extracting \"deskTop2.c\" \(2751 characters\)
sed "s/^X//" >deskTop2.c <<'END_OF_deskTop2.c'
X/*******************************************************************************
X * deskTop2 - test the Menu routines.
X */
X
X#include <stdio.h>
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include <X11/Box.h>
X#include <X11/Command.h>
X#include <X11/Cardinals.h>
X#include <X11/AsciiText.h>
X#include <X11/Viewport.h>
X
X#define MAX_BUTTONS   50
X#define MAX_MENUS     20
X#define MENU_BORDER_H 10
X
Xtypedef void (*voidptr)();
X
Xtypedef struct _menuRec {
X    Widget    widget;                    /* this menu */
X    Widget    buttons[MAX_BUTTONS];      /* buttons in our menu */
X    voidptr   buttonCall[MAX_BUTTONS];
X    int       buttonN;                   /* how many used in above array */
X    int       active;                    /* an active button, -1==no active  */
X} menuRec;
X
XmenuRec menus[MAX_MENUS+1];
Xint menuN;
X
Xextern  void   enterNewLevel();
Xextern  Widget InitMenus();
XDisplay *display;
X
X/******************************************************************************/
X
Xvoid Destroyed(widget, closure, callData)
XWidget widget;   
Xcaddr_t closure; 
Xcaddr_t callData;
X{
X    exit(0);
X}
X
X/******************************************************************************/
X
Xvoid quitCommand(widget, closure, callData)
XWidget widget;
XWidget closure;
Xcaddr_t callData;
X{
X    XtDestroyWidget( closure );
X}
X
X/******************************************************************************/
X
Xvoid levelTwoMenu(widget, parent, callData)
XWidget widget, parent;
Xcaddr_t callData;
X{
X    Widget addButton();
X    void closeMenu();
X
X    openMenu(parent, widget, "levelTwoMenu");
X
X    addButton("Button 1", NULL, NULL);
X    addButton("Quit", quitCommand, parent);
X
X    closeMenu();
X}
X
X/******************************************************************************/
X
Xvoid levelOneMenu(widget, parent, callData)
XWidget widget, parent;
Xcaddr_t callData;
X{
X    Widget addButton();
X    void closeMenu();
X
X    openMenu(parent, widget, "levelOneMenu");
X
X    addButton("Go To Level 2", levelTwoMenu, parent);
X    addButton("Quit", quitCommand, parent);
X
X    closeMenu();
X}
X
X/******************************************************************************/
X
Xvoid MainMenu(outer)
XWidget outer;
X{
X    Widget addButton();
X    void closeMenu();
X
X    openMenu(outer, NULL, "mainMenu");
X
X    addButton("Go to Level 1", levelOneMenu, outer);
X    addButton("Go to Level 2", levelTwoMenu, outer);
X    addButton("Quit", quitCommand, outer);
X
X    closeMenu();
X}
X
X/******************************************************************************/
X
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X{
X    Widget outer;
X
X    outer = InitMenus("deskTop2", "Desk Top Two", &argc, argv);
X
X    XtAddCallback(outer, XtNdestroyCallback, Destroyed, NULL);
X    MainMenu(outer);
X
X    XtMainLoop();
X}
END_OF_deskTop2.c
if test 2751 -ne `wc -c <deskTop2.c`; then
    echo shar: \"deskTop2.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Menu.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Menu.c\"
else
echo shar: Extracting \"Menu.c\" \(11782 characters\)
sed "s/^X//" >Menu.c <<'END_OF_Menu.c'
X#include <stdio.h>
X#include <X11/Intrinsic.h>
X#include <X11/StringDefs.h>
X#include <X11/Box.h>
X#include <X11/Command.h>
X#include <X11/Cardinals.h>
X#include <X11/AsciiText.h>
X#include <X11/Shell.h>
X#include <X11/Viewport.h>
X
X#define MAX_BUTTONS   50
X#define MAX_MENUS     20
X#define MENU_BORDER_H 10
X/*
X#define XtNgrayWhenInsensitive      "grayWhenInsensitive"
X#define XtNshrinkToFit              "shrinkToFit"
X*/
X
Xtypedef    void    (*voidptr)();
X
Xtypedef struct _menuRec {
X    Widget    widget;                    /* this menu */
X    Widget    buttons[MAX_BUTTONS];      /* buttons in our menu */
X    voidptr    buttonCall[MAX_BUTTONS];
X    int    buttonN;                      /* how many used in above array */
X    int    active;                       /* an active button, -1==no active  */
X} menuRec;
X
Xextern menuRec menus[MAX_MENUS+1];
Xextern int     menuN;
X
Xextern  void   enterNewLevel();
Xextern  Widget InitMenus();
XDisplay *display;
X
Xtypedef struct _workRec {
X	voidptr     buttonCall;
X	Widget      widget;
X	Widget      closure;
X	caddr_t     call_data;
X} workRec;
X
XBoolean CallButtonCallback(client_data)
Xcaddr_t client_data;
X{
X	workRec *workP = (workRec*)client_data;
X	(*workP->buttonCall) (workP->widget, workP->closure, workP->call_data);
X	return True;
X}
X
X/*************************************************************************
X * Highlight or unhighlight the given button.  Used by buttonPressed.
X */
X
Xstatic void FlipColors(button)
X    Widget button;
X{
X    static Pixel foreground, background;
X    static Arg args[] = {
X        { XtNforeground, (XtArgVal) &foreground },
X        { XtNbackground, (XtArgVal) &background } };
X
X    args[0].value = (XtArgVal) & foreground;
X    args[1].value = (XtArgVal) & background;
X    XtGetValues(button, args, XtNumber(args));
X
X    args[0].value = (XtArgVal) background;
X    args[1].value = (XtArgVal) foreground;
X    XtSetValues(button, args, XtNumber(args));
X}
X
X/*************************************************************************
X * buttonPressed: This is a callback routine used by addExistingButton.
X */
X
Xstatic void buttonPressed(widget, closure, callData)
X    Widget widget;
X    Widget closure;
X    caddr_t callData;
X{
X    /*
X     * This routine is called on all menu button presses.  Since all the
X     * menu buttons are always sensitive, the user may select something
X     * on a menu as a way to abandon something else selected elsewhere.
X     * 
X     * We need to destroy any menus or applications "beyond" the menu where
X     * this button press occurred; they will have destroy callbacks if
X     * they care about this.
X     * 
X     * After we've pruned the widget tree back to the point of our menu, we
X     * need to highlight our button and call its callback.  remember that
X     * we are intercepting the button callback to be here in the first place.
X     */
X
X    void destroyCallback();
X    Widget daddy;
X    menuRec *daddy_menu;
X    int i, k, daddy_level;
X
X    /* daddy is the widget that this button is a member of. scan menus[] for him. */
X
X    daddy = XtParent(widget);
X    for (i = 0; i < menuN && menus[i].widget != daddy; i++)
X    {
X    }
X    daddy_level = i;
X    daddy_menu = &menus[daddy_level];
X
X    /* unhighlight the previously selected button. */
X    if (daddy_menu->active != -1)
X    {
X        FlipColors(daddy_menu->buttons[daddy_menu->active]);
X        daddy_menu->active = -1;
X    }
X
X    /* every widget further up the menus[] array than daddy needs to be nuked. */
X    for (; menuN - 1 > daddy_level; menuN--)
X    {
X        if (menuN < 0) exit(0);
X        XtRemoveCallback(menus[menuN - 1].widget, XtNdestroyCallback,
X                           destroyCallback, NULL);
X
X        XtDestroyWidget(menus[menuN - 1].widget);
X    }
X
X    FlipColors(widget);
X
X    /* we know our widget is one of the ones in the button list for this */
X    /* menu; scan for it, and then call the corresponding callback.      */
X    daddy_menu->active = -1;
X    for (i = daddy_menu->buttonN - 1; i >= 0; i--)
X    {
X        if (daddy_menu->buttons[i] == widget)
X        {
X            workRec *workP = XtNew(workRec);
X            daddy_menu->active = i;
X            workP->buttonCall = daddy_menu->buttonCall[i];
X            workP->widget = widget;
X            workP->closure = closure;
X            workP->call_data = callData;
X            (void) XtAddWorkProc(CallButtonCallback, workP);
X        }
X    }
X}
X
X/*************************************************************************
X * widthOfBox: called by _addTitleToBox.
X */
X
Xint widthOfBox(w)
X    Widget w;
X{
X    static Dimension width, borderWidth;
X    static int wBox, hSpace;
X    static Arg args[] = {
X    { XtNwidth,       (XtArgVal) &width },
X    { XtNhSpace,      (XtArgVal) &hSpace },
X    { XtNborderWidth, (XtArgVal) &borderWidth } };
X
X    XtGetValues(w, args, XtNumber(args));
X    wBox = width - (2 * hSpace) - (2 * borderWidth);
X    return wBox;
X}
X
X/*************************************************************************/
X
Xstatic void destroyCallback(widget, closure, callData)
X    Widget widget;
X    caddr_t closure;
X    caddr_t callData;
X{
X    int n, my_level;
X    menuRec *menu;
X
X    my_level = -1;
X    for (n = 0, menu = menus; n < menuN; n++, menu++)
X    {
X        if (menu->widget == widget)
X        {
X            my_level = n;
X            break;
X        }
X    }
X    if (my_level == -1)
X    {
X        printf("destroyCallback: can't find my level.\n");
X        return;
X    }
X
X    for (n = my_level; n < menuN - 2; n++)
X        menus[n] = menus[n + 1];
X    menuN--;
X}
X
X/*************************************************************************
X * _titleCallBack: used by _addTitleToBox.
X */
X
X
Xstatic void _titleCallback(widget, closure, callData)
X    Widget widget;        /* not used */
X    caddr_t closure;    /* widget to raise */
X    caddr_t callData;    /* not used */
X{
X    Widget widget_to_raise = (Widget) closure;
X    Display *d = XtDisplay(widget_to_raise);
X    Window w = XtWindow(widget_to_raise);
X
X    /*
X     * This is a hack.  We are raising the window of the widget passed as
X     * our closure; as seen below, this widget is the parent of the box
X     * to which the title is being added.  This is the shell widget,
X     * which it pretty much has to be.  This all needs to change, such
X     * that addTitleToBox is renamed to initializeMenus() or some such.
X     * It's name presently indicates that it would work for any box,
X     * which is FALSE FALSE FALSE. 
X     */
X
X    XRaiseWindow(d, w);
X}
X
X/*************************************************************************
X * _addTitleToBox: Used by InitMenus.
X */
X
Xstatic void _addTitleToBox(title, box)
X    char *title;
X    Widget box;
X{
X    Arg arg[10];
X    int n;
X    static XtCallbackRec callback[2];
X
X    n = 0;
X    XtSetArg(arg[n], XtNwidth, widthOfBox(box)); n++;
X    XtSetArg(arg[n], XtNlabel, title); n++;
X    XtSetArg(arg[n], XtNjustify, XtJustifyLeft); n++;
X    callback[0].callback = _titleCallback;
X    callback[0].closure = (caddr_t) XtParent(box);
X    XtSetArg(arg[n], XtNcallback, callback); n++;
X    XtCreateManagedWidget("title", commandWidgetClass, box, arg, n);
X}
X
X/*************************************************************************
X * _openMenu: Used by openMenu, and by openWideMenu.
X */
X
X#define opt_wide    0x0001    /* default: narrow */
X
Xstatic void _openMenu(parent, command, name, options)
X    Widget parent;
X    Widget command;
X    char *name;
X    int options;
X{
X    void enterNewLevel();
X    Arg arg[10];
X    int n;
X
X    n = 0;
X    if (options & opt_wide)
X    {
X        XtSetArg(arg[n], XtNwidth, widthOfBox(parent)); n++;
X    }
X    else
X    {
X        /* XtSetArg(arg[n], XtNalignVert, TRUE); n++; */
X    }
X    enterNewLevel(XtCreateWidget(name, boxWidgetClass, parent, arg, n));
X}
X
X
X/*************************************************************************
X *  addExistingButton: routine used by: addButton.
X */
X
Xvoid addExistingButton(button, func, widget)
X    Widget button;
X    void (*func) ();
X    Widget widget;
X{
X    Arg arg[10];
X    int n = 0;
X    menuRec *menu = &menus[menuN-1];
X
X    if (menuN-1 < 0) exit(0);
X
X    XtSetArg(arg[n], XtNgrayWhenInsensitive, False); n++;
X    XtSetArg(arg[n], XtNsensitive, (func) ? True : False); n++;
X    XtSetValues(button, arg, n);
X
X    if (func)
X        XtAddCallback(button, XtNcallback, buttonPressed, widget);
X
X    menu->buttons[menu->buttonN] = button;
X    menu->buttonCall[menu->buttonN] = func;
X    menu->buttonN++;
X}
X
X/*************************************************************************
X * enterNewLevel: Used by _openMenu. (applic_widg) this function should be
X *                called by any application code which creates a new widget
X *                (form, etc) that shares an outer box with some Menus.
X */
X
Xvoid enterNewLevel(w)
X    Widget w;
X{
X    menuRec *menu = &menus[menuN];
X
X    menu->widget = w;
X    menu->buttonN = 0;
X    menu->active = -1;
X    menuN++;
X    XtAddCallback(w, XtNdestroyCallback, destroyCallback, NULL);
X}
X
Xstatic XrmOptionDescRec options[] = {
X    {"-label", XtNlabel, XrmoptionSepArg, NULL}
X};
X
X/*************************************************************************
X * The following routines are called by application programs.  Routines
X * above this point a supporting routines only, and not known by the
X * application programmer.
X */
X
Xstatic Boolean scroll;
X
Xstatic XtResource resources[] = {
X    {"scroll", "Scroll", XtRBoolean, sizeof(Boolean),
X             (Cardinal)&scroll, XtRString, "False"},
X};
X
XWidget InitMenus(application_name, menu_title, p_argc, argv)
X    char *application_name;
X    char *menu_title;
X    int *p_argc;
X    char *argv[];
X{
X    Arg arg[10];
X    int n;
X    Widget outer, toplevel;
X
X    toplevel = XtInitialize("main", application_name, options, XtNumber(options),
X                  p_argc, argv);
X
X    XtGetApplicationResources( toplevel, (caddr_t)NULL,
X                               resources, XtNumber(resources),
X                               NULL, ZERO );
X
X    display = XtDisplay(toplevel);
X
X    n = 0;
X    XtSetArg(arg[n], XtNallowShellResize, TRUE); n++;
X    XtSetValues(toplevel, arg, n);
X
X    n = 0;
X    XtSetArg(arg[n], XtNx, 200); n++;
X    XtSetArg(arg[n], XtNy, 200); n++;
X    /* XtSetArg(arg[n], XtNalignVert, FALSE); n++; */
X    XtSetArg(arg[n], XtNshrinkToFit, FALSE); n++;
X    outer = XtCreateManagedWidget(NULL, boxWidgetClass, toplevel, arg, n);
X
X    XtRealizeWidget(toplevel);
X
X    _addTitleToBox(menu_title, outer);
X    return outer;
X}
X
X/***********************************************************************/
X
XopenMenu(parent, command, name)
X    Widget parent;
X    Widget command;
X    char *name;
X{
X    _openMenu(parent, command, name, 0);
X}
X
X/*************************************************************************/
X
Xvoid openWideMenu(parent, command, name)
X    Widget parent;
X    Widget command;
X    char *name;
X{
X    _openMenu(parent, command, name, opt_wide);
X}
X/*************************************************************************/
X
XWidget addButton(label, func, widget)
X    char *label;
X    void (*func) ();
X    Widget widget;
X{
X    Widget button;
X    menuRec *menu = &menus[menuN-1];
X    Arg arg[10];
X    int n;
X
X    if (menuN < 0) exit(0);
X    n = 0;
X    button = XtCreateManagedWidget(label, commandWidgetClass, menu->widget, arg, n);
X
X    addExistingButton(button, func, widget);
X    return button;
X}
X
X/*************************************************************************/
X
Xstatic void SetInsensitiveBorder(w, p)
X    Widget w;
X    Pixmap p;
X{
X    Arg arg[10];
X    int n;
X
X    n = 0;
X    XtSetArg(arg[n], XtNinsensitiveBorder, p); n++;
X    XtSetValues(w, arg, n);
X}
X
X/*************************************************************************/
X
Xvoid closeMenu()
X{
X    menuRec *menu = &menus[menuN-1];
X
X    if (menuN < 0) exit(0);
X    XtManageChild(menu->widget);
X}
END_OF_Menu.c
if test 11782 -ne `wc -c <Menu.c`; then
    echo shar: \"Menu.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0