Path: utzoo!attcan!uunet!unisoft!fai!hadar!charlesb From: charlesb@tdd.fai.com (Charles Brauer) Newsgroups: comp.windows.x Subject: Sample widget menu program (re-post) Message-ID: <8@hadar.tdd.fai.com> Date: 28 Jan 89 01:25:02 GMT Reply-To: charlesb@hadar (Charles Brauer) Organization: Fujitsu America, San Jose Lines: 1124 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 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 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 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 : 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 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 deskTop1.c <<'END_OF_deskTop1.c' X/******************************************************************************* X * deskTop1 - test the Menu routines. X */ X X#include X#include X#include X#include X#include X#include X#include X#include 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 deskTop2.c <<'END_OF_deskTop2.c' X/******************************************************************************* X * deskTop2 - test the Menu routines. X */ X X#include X#include X#include X#include X#include X#include X#include X#include 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 Menu.c <<'END_OF_Menu.c' X#include X#include X#include X#include X#include X#include X#include X#include X#include 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