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