SimpleMenu.c revision 5b16253f
1/*
2Copyright 1989, 1994, 1998  The Open Group
3
4Permission to use, copy, modify, distribute, and sell this software and its
5documentation for any purpose is hereby granted without fee, provided that
6the above copyright notice appear in all copies and that both that
7copyright notice and this permission notice appear in supporting
8documentation.
9
10The above copyright notice and this permission notice shall be included in
11all copies or substantial portions of the Software.
12
13THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
16OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
17AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
20Except as contained in this notice, the name of The Open Group shall not be
21used in advertising or otherwise to promote the sale, use or other dealings
22in this Software without prior written authorization from The Open Group.
23 */
24
25/*
26 * SimpleMenu.c - Source code file for SimpleMenu widget.
27 *
28 * Date:    April 3, 1989
29 *
30 * By:      Chris D. Peterson
31 *          MIT X Consortium
32 *          kit@expo.lcs.mit.edu
33 */
34
35#ifdef HAVE_CONFIG_H
36#include <config.h>
37#endif
38#include <stdio.h>
39#include <X11/IntrinsicP.h>
40#include <X11/StringDefs.h>
41#include <X11/Xmu/Initer.h>
42#include <X11/Xaw/Cardinals.h>
43#include <X11/Xaw/SimpleMenP.h>
44#include <X11/Xaw/SmeBSBP.h>
45#include <X11/Xaw/XawInit.h>
46#include "Private.h"
47
48#define streq(a, b)	(strcmp((a), (b)) == 0)
49
50#define ForAllChildren(smw, childP)				\
51for ((childP) = (SmeObject *)(smw)->composite.children;		\
52     (childP) < (SmeObject *)((smw)->composite.children		\
53			      + (smw)->composite.num_children);	\
54     (childP)++)
55
56#ifndef OLDXAW
57#define	SMW_UNMAPPING	0x01
58#define SMW_POPLEFT	0x02
59#endif
60
61/*
62 * Class Methods
63 */
64static void XawSimpleMenuChangeManaged(Widget);
65static void XawSimpleMenuClassInitialize(void);
66static void XawSimpleMenuClassPartInitialize(WidgetClass);
67static XtGeometryResult XawSimpleMenuGeometryManager(Widget, XtWidgetGeometry*,
68						     XtWidgetGeometry*);
69static void XawSimpleMenuInitialize(Widget, Widget, ArgList, Cardinal*);
70static void XawSimpleMenuRealize(Widget, XtValueMask*, XSetWindowAttributes*);
71static void XawSimpleMenuRedisplay(Widget, XEvent*, Region);
72static void XawSimpleMenuResize(Widget);
73static Boolean XawSimpleMenuSetValues(Widget, Widget, Widget,
74				      ArgList, Cardinal*);
75static Boolean XawSimpleMenuSetValuesHook(Widget, ArgList, Cardinal*);
76#ifndef OLDXAW
77static void PopupSubMenu(SimpleMenuWidget);
78static void PopdownSubMenu(SimpleMenuWidget);
79static void PopupCB(Widget, XtPointer, XtPointer);
80#endif
81
82/*
83 * Prototypes
84 */
85static void AddPositionAction(XtAppContext, XPointer);
86static void CalculateNewSize(Widget, Dimension*, Dimension*);
87static void ChangeCursorOnGrab(Widget, XtPointer, XtPointer);
88static void CreateLabel(Widget);
89static SmeObject DoGetEventEntry(Widget, int, int);
90static Widget FindMenu(Widget, String);
91static SmeObject GetEventEntry(Widget, XEvent*);
92static void Layout(Widget, Dimension*, Dimension*);
93static void MakeResizeRequest(Widget);
94static void MakeSetValuesRequest(Widget, unsigned int, unsigned int);
95static void MoveMenu(Widget, int, int);
96static void PositionMenu(Widget, XPoint*);
97
98/*
99 * Actions
100 */
101static void Highlight(Widget, XEvent*, String*, Cardinal*);
102static void Notify(Widget, XEvent*, String*, Cardinal*);
103#ifndef OLDXAW
104static void Popdown(Widget, XEvent*, String*, Cardinal*);
105#endif
106static void PositionMenuAction(Widget, XEvent*, String*, Cardinal*);
107static void Unhighlight(Widget, XEvent*, String*, Cardinal*);
108
109/*
110 * Initialization
111 */
112#define offset(field)	XtOffsetOf(SimpleMenuRec, simple_menu.field)
113
114static XtResource resources[] = {
115  /* label */
116  {
117    XtNlabel,
118    XtCLabel,
119    XtRString,
120    sizeof(String),
121    offset(label_string),
122    XtRString,
123    NULL
124  },
125  {
126    XtNlabelClass,
127    XtCLabelClass,
128    XtRPointer,
129    sizeof(WidgetClass),
130    offset(label_class),
131    XtRImmediate,
132    NULL
133  },
134
135  /* layout */
136  {
137    XtNrowHeight,
138    XtCRowHeight,
139    XtRDimension,
140    sizeof(Dimension),
141    offset(row_height),
142    XtRImmediate,
143    (XtPointer)0
144  },
145  {
146    XtNtopMargin,
147    XtCVerticalMargins,
148    XtRDimension,
149    sizeof(Dimension),
150    offset(top_margin),
151    XtRImmediate,
152    (XtPointer)0
153  },
154  {
155    XtNbottomMargin,
156    XtCVerticalMargins,
157    XtRDimension,
158    sizeof(Dimension),
159    offset(bottom_margin),
160    XtRImmediate,
161    (XtPointer)0
162  },
163#ifndef OLDXAW
164  {
165    XtNleftMargin,
166    XtCHorizontalMargins,
167    XtRDimension,
168    sizeof(Dimension),
169    offset(left_margin),
170    XtRImmediate,
171    (XtPointer)0
172  },
173  {
174    XtNrightMargin,
175    XtCHorizontalMargins,
176    XtRDimension,
177    sizeof(Dimension),
178    offset(right_margin),
179    XtRImmediate,
180    (XtPointer)0
181  },
182#endif
183
184  /* misc */
185  {
186    XtNallowShellResize,
187    XtCAllowShellResize,
188    XtRBoolean,
189    sizeof(Boolean),
190    XtOffsetOf(SimpleMenuRec, shell.allow_shell_resize),
191    XtRImmediate,
192    (XtPointer)True
193  },
194  {
195    XtNcursor,
196    XtCCursor,
197    XtRCursor,
198    sizeof(Cursor),
199    offset(cursor),
200    XtRImmediate,
201    (XtPointer)None
202  },
203  {
204    XtNmenuOnScreen,
205    XtCMenuOnScreen,
206    XtRBoolean,
207    sizeof(Boolean),
208    offset(menu_on_screen),
209    XtRImmediate,
210    (XtPointer)True
211  },
212  {
213    XtNpopupOnEntry,
214    XtCPopupOnEntry,
215    XtRWidget,
216    sizeof(Widget),
217    offset(popup_entry),
218    XtRWidget,
219    NULL
220  },
221  {
222    XtNbackingStore,
223    XtCBackingStore,
224    XtRBackingStore,
225    sizeof(int),
226    offset(backing_store),
227    XtRImmediate,
228    (XtPointer)(Always + WhenMapped + NotUseful)
229  },
230#ifndef OLDXAW
231  {
232    XawNdisplayList,
233    XawCDisplayList,
234    XawRDisplayList,
235    sizeof(XawDisplayList*),
236    offset(display_list),
237    XtRImmediate,
238    NULL
239  },
240#endif
241};
242#undef offset
243
244static char defaultTranslations[] =
245"<Enter>:"	"highlight()\n"
246"<Leave>:"	"unhighlight()\n"
247"<BtnMotion>:"	"highlight()\n"
248#ifndef OLDXAW
249"<BtnUp>:"	"popdown() notify() unhighlight()\n"
250#else
251"<BtnUp>:"	"MenuPopdown() notify() unhighlight()\n"
252#endif
253;
254
255static XtActionsRec actionsList[] =
256{
257  {"notify",            Notify},
258  {"highlight",         Highlight},
259  {"unhighlight",       Unhighlight},
260#ifndef OLDXAW
261  {"popdown",		Popdown},
262  {"set-values",	XawSetValuesAction},
263  {"get-values",	XawGetValuesAction},
264  {"declare",		XawDeclareAction},
265  {"call-proc",		XawCallProcAction},
266#endif
267};
268
269static CompositeClassExtensionRec extension_rec = {
270  NULL,					/* next_extension */
271  NULLQUARK,				/* record_type */
272  XtCompositeExtensionVersion,		/* version */
273  sizeof(CompositeClassExtensionRec),	/* record_size */
274  True,					/* accepts_objects */
275};
276
277#define Superclass	(&overrideShellClassRec)
278SimpleMenuClassRec simpleMenuClassRec = {
279  /* core */
280  {
281    (WidgetClass)Superclass,		/* superclass */
282    "SimpleMenu",			/* class_name */
283    sizeof(SimpleMenuRec),		/* size */
284    XawSimpleMenuClassInitialize,	/* class_initialize */
285    XawSimpleMenuClassPartInitialize,	/* class_part_initialize */
286    False,				/* class_inited */
287    XawSimpleMenuInitialize,		/* initialize */
288    NULL,				/* initialize_hook */
289    XawSimpleMenuRealize,		/* realize */
290    actionsList,			/* actions */
291    XtNumber(actionsList),		/* num_actions */
292    resources,				/* resources */
293    XtNumber(resources),		/* num_resources */
294    NULLQUARK,				/* xrm_class */
295    True,				/* compress_motion */
296    True,				/* compress_exposure */
297    True,				/* compress_enterleave */
298    False,				/* visible_interest */
299    NULL,				/* destroy */
300    XawSimpleMenuResize,		/* resize */
301    XawSimpleMenuRedisplay,		/* expose */
302    XawSimpleMenuSetValues,		/* set_values */
303    XawSimpleMenuSetValuesHook,		/* set_values_hook */
304    XtInheritSetValuesAlmost,		/* set_values_almost */
305    NULL,				/* get_values_hook */
306    NULL,				/* accept_focus */
307    XtVersion,				/* intrinsics version */
308    NULL,				/* callback offsets */
309    defaultTranslations,		/* tm_table */
310    NULL,				/* query_geometry */
311    NULL,				/* display_accelerator */
312    NULL,				/* extension */
313  },
314  /* composite */
315  {
316    XawSimpleMenuGeometryManager,	/* geometry_manager */
317    XawSimpleMenuChangeManaged,		/* change_managed */
318    XtInheritInsertChild,		/* insert_child */
319    XtInheritDeleteChild,		/* delete_child */
320    NULL,				/* extension */
321  },
322  /* shell */
323  {
324    NULL,				/* extension */
325  },
326  /* override */
327  {
328    NULL,				/* extension */
329  },
330  /* simple_menu */
331  {
332    NULL,				/* extension */
333  },
334};
335
336WidgetClass simpleMenuWidgetClass = (WidgetClass)&simpleMenuClassRec;
337
338/*
339 * Implementation
340 */
341/*
342 * Function:
343 *	XawSimpleMenuClassInitialize
344 *
345 * Description:
346 *	Class Initialize routine, called only once.
347 */
348static void
349XawSimpleMenuClassInitialize(void)
350{
351    XawInitializeWidgetSet();
352    XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
353		   NULL, 0);
354    XtSetTypeConverter(XtRBackingStore, XtRString, XmuCvtBackingStoreToString,
355		       NULL, 0, XtCacheNone, NULL);
356    XmuAddInitializer(AddPositionAction, NULL);
357}
358
359/*
360 * Function:
361 *	XawSimpleMenuClassPartInitialize
362 *      Arguments: wc - the widget class of the subclass.
363 *
364 * Description:
365 *	  Class Part Initialize routine, called for every subclass.  Makes
366 *	sure that the subclasses pick up the extension record.
367 */
368static void
369XawSimpleMenuClassPartInitialize(WidgetClass wc)
370{
371    SimpleMenuWidgetClass smwc = (SimpleMenuWidgetClass)wc;
372
373    /*
374     * Make sure that our subclass gets the extension rec too
375     */
376    extension_rec.next_extension = smwc->composite_class.extension;
377    smwc->composite_class.extension = (XtPointer) &extension_rec;
378}
379
380/*
381 *  Function:
382 *	XawSimpleMenuInitialize
383 *
384 * Parameters:
385 *	request - widget requested by the argument list
386 *	cnew	- new widget with both resource and non resource values
387 *
388 * Description:
389 *	Initializes the simple menu widget.
390 */
391/*ARGSUSED*/
392static void
393XawSimpleMenuInitialize(Widget request _X_UNUSED, Widget cnew,
394			ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
395{
396    SimpleMenuWidget smw = (SimpleMenuWidget)cnew;
397    Dimension width, height;
398
399    XmuCallInitializers(XtWidgetToApplicationContext(cnew));
400
401    if (smw->simple_menu.label_class == NULL)
402	smw->simple_menu.label_class = smeBSBObjectClass;
403
404    smw->simple_menu.label = NULL;
405    smw->simple_menu.entry_set = NULL;
406    smw->simple_menu.recursive_set_values = False;
407#ifndef OLDXAW
408    smw->simple_menu.sub_menu = NULL;
409    smw->simple_menu.state = 0;
410
411    XtAddCallback(cnew, XtNpopupCallback, PopupCB, NULL);
412#endif
413
414    if (smw->simple_menu.label_string != NULL)
415	CreateLabel(cnew);
416
417    width = height = 0;
418    CalculateNewSize(cnew, &width, &height);
419
420    smw->simple_menu.menu_width = True;
421
422    if (XtWidth(smw) == 0) {
423	smw->simple_menu.menu_width = False;
424	XtWidth(smw) = width;
425    }
426
427    smw->simple_menu.menu_height = True;
428
429    if (XtHeight(smw) == 0) {
430	smw->simple_menu.menu_height = False;
431	XtHeight(smw) = height;
432    }
433
434    /*
435     * Add a popup_callback routine for changing the cursor
436     */
437    XtAddCallback(cnew, XtNpopupCallback, ChangeCursorOnGrab, NULL);
438}
439
440/*
441 * Function:
442 *	XawSimpleMenuRedisplay
443 *
444 * Parameters:
445 *	w      - simple menu widget
446 *	event  - X event that caused this redisplay
447 *	region - region the needs to be repainted
448 *
449 * Description:
450 *	Redisplays the contents of the widget.
451 */
452/*ARGSUSED*/
453static void
454XawSimpleMenuRedisplay(Widget w, XEvent *event _X_UNUSED, Region region)
455{
456    SimpleMenuWidget smw = (SimpleMenuWidget)w;
457    SmeObject *entry;
458    SmeObjectClass cclass;
459
460    if (region == NULL)
461	XClearWindow(XtDisplay(w), XtWindow(w));
462
463#ifndef OLDXAW
464    if (smw->simple_menu.display_list)
465      XawRunDisplayList(w, smw->simple_menu.display_list, event, region);
466#endif
467
468    /*
469     * Check and Paint each of the entries - including the label
470     */
471    ForAllChildren(smw, entry) {
472	if (!XtIsManaged((Widget)*entry))
473	    continue;
474
475	if (region != NULL)
476	    switch(XRectInRegion(region, XtX(*entry),XtY(*entry),
477				 XtWidth(*entry), XtHeight(*entry))) {
478		case RectangleIn:
479		case RectanglePart:
480		    break;
481		default:
482		    continue;
483	    }
484
485	cclass = (SmeObjectClass)(*entry)->object.widget_class;
486
487	if (cclass->rect_class.expose != NULL)
488	    (cclass->rect_class.expose)((Widget)*entry, NULL, NULL);
489    }
490}
491
492/*
493 * Function:
494 *	XawSimpleMenuRealize
495 *
496 * Parameters:
497 *	w     - simple menu widget
498 *	mask  - value mask for the window to create
499 *	attrs - attributes for the window to create
500 *
501 * Description:
502 *	Realizes the widget.
503 */
504static void
505XawSimpleMenuRealize(Widget w, XtValueMask *mask, XSetWindowAttributes *attrs)
506{
507    SimpleMenuWidget smw = (SimpleMenuWidget)w;
508#ifndef OLDXAW
509    XawPixmap *pixmap;
510#endif
511
512    attrs->cursor = smw->simple_menu.cursor;
513    *mask |= CWCursor;
514    if (smw->simple_menu.backing_store == Always ||
515	smw->simple_menu.backing_store == NotUseful ||
516	smw->simple_menu.backing_store == WhenMapped) {
517	*mask |= CWBackingStore;
518	attrs->backing_store = smw->simple_menu.backing_store;
519    }
520    else
521	*mask &= (XtValueMask)(~CWBackingStore);
522
523    (*Superclass->core_class.realize)(w, mask, attrs);
524
525#ifndef OLDXAW
526    if (w->core.background_pixmap > XtUnspecifiedPixmap) {
527	pixmap = XawPixmapFromXPixmap(w->core.background_pixmap, XtScreen(w),
528				      w->core.colormap, (int)w->core.depth);
529	if (pixmap && pixmap->mask)
530	    XawReshapeWidget(w, pixmap);
531    }
532#endif
533}
534
535/*
536 * Function:
537 *	XawSimpleMenuResize
538 *
539 * Parameters:
540 *	w - simple menu widget
541 *
542 * Description:
543 *	Handle the menu being resized.
544 */
545static void
546XawSimpleMenuResize(Widget w)
547{
548    if (!XtIsRealized(w))
549	return;
550
551    Layout(w, NULL, NULL);
552
553    XawSimpleMenuRedisplay(w, NULL, NULL);
554}
555
556/*
557 * Function:
558 *	XawSimpleMenuSetValues
559 *
560 * Parameters:
561 *	current - current state of the widget
562 *	request - what was requested
563 *	cnew    - what the widget will become
564 *
565 * Description:
566 *	Relayout the menu when one of the resources is changed.
567 */
568/*ARGSUSED*/
569static Boolean
570XawSimpleMenuSetValues(Widget current, Widget request _X_UNUSED, Widget cnew,
571		       ArgList args _X_UNUSED, Cardinal *num_args _X_UNUSED)
572{
573    SimpleMenuWidget smw_old = (SimpleMenuWidget)current;
574    SimpleMenuWidget smw_new = (SimpleMenuWidget)cnew;
575    Boolean ret_val = False, layout = False;
576
577    if (!XtIsRealized(current))
578	return (False);
579
580    if (!smw_new->simple_menu.recursive_set_values) {
581	if (XtWidth(smw_new) != XtWidth(smw_old)) {
582	    smw_new->simple_menu.menu_width = XtWidth(smw_new) != 0;
583	    layout = True;
584	}
585	if (XtHeight(smw_new) != XtHeight(smw_old)) {
586	    smw_new->simple_menu.menu_height = XtHeight(smw_new) != 0;
587	    layout = True;
588	}
589    }
590
591    if (smw_old->simple_menu.cursor != smw_new->simple_menu.cursor)
592	XDefineCursor(XtDisplay(cnew), XtWindow(cnew),
593		      smw_new->simple_menu.cursor);
594
595    if (smw_old->simple_menu.label_string !=smw_new->simple_menu.label_string) {
596	if (smw_new->simple_menu.label_string == NULL)	    /* Destroy */
597	    XtDestroyWidget((Widget)smw_old->simple_menu.label);
598	else if (smw_old->simple_menu.label_string == NULL) /* Create */
599	    CreateLabel(cnew);
600	else {						    /* Change */
601	    Arg arglist[1];
602
603	    XtSetArg(arglist[0], XtNlabel, smw_new->simple_menu.label_string);
604	    XtSetValues((Widget)smw_new->simple_menu.label, arglist, ONE);
605	}
606    }
607
608    if (smw_old->simple_menu.label_class != smw_new->simple_menu.label_class)
609	XtAppWarning(XtWidgetToApplicationContext(cnew),
610		     "No Dynamic class change of the SimpleMenu Label.");
611
612    if (smw_old->simple_menu.top_margin != smw_new->simple_menu.top_margin
613	|| smw_old->simple_menu.bottom_margin
614	!= smw_new->simple_menu.bottom_margin) {
615	layout = True;
616	ret_val = True;
617    }
618
619#ifndef OLDXAW
620    if (smw_old->core.background_pixmap != smw_new->core.background_pixmap) {
621	XawPixmap *opix, *npix;
622
623	opix = XawPixmapFromXPixmap(smw_old->core.background_pixmap,
624				    XtScreen(smw_old), smw_old->core.colormap,
625				    (int)smw_old->core.depth);
626	npix = XawPixmapFromXPixmap(smw_new->core.background_pixmap,
627				    XtScreen(smw_new), smw_new->core.colormap,
628				    (int)smw_new->core.depth);
629	if ((npix && npix->mask) || (opix && opix->mask))
630	    XawReshapeWidget(cnew, npix);
631    }
632#endif
633
634    if (layout)
635	Layout(cnew, NULL, NULL);
636
637    return (ret_val);
638}
639
640/*
641 * Function:
642 *	XawSimpleMenuSetValuesHook
643 *
644 * Parameters:
645 *	w	 - menu widget
646 *	arglist	 - argument list passed to XtSetValues
647 *	num_args - number of args
648 *
649 * Description:
650 *	To handle a special case, this is passed the actual arguments.
651 */
652static Boolean
653XawSimpleMenuSetValuesHook(Widget w, ArgList arglist, Cardinal *num_args)
654{
655    Cardinal i;
656    Dimension width, height;
657
658    width = XtWidth(w);
659    height = XtHeight(w);
660
661    for (i = 0 ; i < *num_args ; i++) {
662	if (streq(arglist[i].name, XtNwidth))
663	    width = (Dimension)arglist[i].value;
664	if (streq(arglist[i].name, XtNheight))
665	    height = (Dimension) arglist[i].value;
666    }
667
668    if (width != XtWidth(w) || height != XtHeight(w))
669	MakeSetValuesRequest(w, width, height);
670
671    return (False);
672}
673
674/*
675 * Geometry Management routines
676 */
677/*
678 * Function:
679 *	XawSimpleMenuGeometryManager
680 *
681 * Parameters:
682 *	w	- Menu Entry making the request
683 *	request - requested new geometry
684 *                 reply - the allowed geometry.
685 *
686 * Description:
687 *	This is the SimpleMenu Widget's Geometry Manager.
688 *
689 * Returns:
690 *	XtGeometry{Yes, No, Almost}
691 */
692static XtGeometryResult
693XawSimpleMenuGeometryManager(Widget w, XtWidgetGeometry *request,
694			     XtWidgetGeometry *reply)
695{
696    SimpleMenuWidget smw = (SimpleMenuWidget)XtParent(w);
697    SmeObject entry = (SmeObject)w;
698    XtGeometryMask mode = request->request_mode;
699    XtGeometryResult answer;
700    Dimension old_height, old_width;
701
702    if (!(mode & CWWidth) && !(mode & CWHeight))
703	return (XtGeometryNo);
704
705    reply->width = request->width;
706    reply->height = request->height;
707
708    old_width = XtWidth(entry);
709    old_height = XtHeight(entry);
710
711    Layout(w, &reply->width, &reply->height);
712
713    /*
714     * Since we are an override shell and have no parent there is no one to
715     * ask to see if this geom change is okay, so I am just going to assume
716     * we can do whatever we want.  If you subclass be very careful with this
717     * assumption, it could bite you.
718     *
719     * Chris D. Peterson - Sept. 1989.
720     */
721    if ((!(mode & CWWidth) || reply->width == request->width)
722	&& (!(mode & CWHeight) || reply->height == request->height)) {
723	if (mode & XtCWQueryOnly) {	/* Actually perform the layout */
724	    XtWidth(entry) = old_width;
725	    XtHeight(entry) = old_height;
726	}
727	else
728	    Layout((Widget)smw, NULL, NULL);
729	answer = XtGeometryDone;
730    }
731    else {
732	XtWidth(entry) = old_width;
733	XtHeight(entry) = old_height;
734
735	if ((reply->width == request->width && !(mode & CWHeight))
736	    || (reply->height == request->height && !(mode & CWWidth))
737	    || (reply->width == request->width
738	    && reply->height == request->height))
739	    answer = XtGeometryNo;
740	else {
741	    answer = XtGeometryAlmost;
742	    reply->request_mode = 0;
743	    if (reply->width != request->width)
744		reply->request_mode |= CWWidth;
745	    if (reply->height != request->height)
746		reply->request_mode |= CWHeight;
747	}
748    }
749
750    return (answer);
751}
752
753/*
754 * Function:
755 *	XawSimpleMenuChangeManaged
756 *
757 * Parameters:
758 *	w - simple menu widget
759 *
760 * Description:
761 *	Called whenever a new child is managed.
762 */
763static void
764XawSimpleMenuChangeManaged(Widget w)
765{
766    Layout(w, NULL, NULL);
767}
768
769/*
770 * Global Action Routines
771 *
772 * These actions routines will be added to the application's
773 * global action list
774 */
775/*
776 * Function:
777 *	PositionMenuAction
778 *
779 * Parameters:
780 *	w	   - a widget (no the simple menu widget)
781 *	event	   - the event that caused this action
782 *	params	   - parameters passed to the routine.
783 *                                      we expect the name of the menu here.
784 *	num_params - ""
785 *
786 * Description:
787 *	Positions the simple menu widget.
788 */
789/*ARGSUSED*/
790static void
791PositionMenuAction(Widget w, XEvent *event,
792		   String *params, Cardinal *num_params)
793{
794    Widget menu;
795    XPoint loc;
796
797    if (*num_params != 1) {
798	XtAppWarning(XtWidgetToApplicationContext(w),
799		     "SimpleMenuWidget: position menu action expects "
800		     "only one parameter which is the name of the menu.");
801	return;
802    }
803
804    if ((menu = FindMenu(w, params[0])) == NULL) {
805	char error_buf[BUFSIZ];
806
807	snprintf(error_buf, sizeof(error_buf),
808		 "SimpleMenuWidget: could not find menu named %s.",
809		 params[0]);
810	XtAppWarning(XtWidgetToApplicationContext(w), error_buf);
811	return;
812    }
813
814    switch (event->type) {
815	case ButtonPress:
816	case ButtonRelease:
817	    loc.x = (short)event->xbutton.x_root;
818	    loc.y = (short)event->xbutton.y_root;
819	    PositionMenu(menu, &loc);
820	    break;
821	case EnterNotify:
822	case LeaveNotify:
823	    loc.x = (short)event->xcrossing.x_root;
824	    loc.y = (short)event->xcrossing.y_root;
825	    PositionMenu(menu, &loc);
826	    break;
827	case MotionNotify:
828	    loc.x = (short)event->xmotion.x_root;
829	    loc.y = (short)event->xmotion.y_root;
830	    PositionMenu(menu, &loc);
831	    break;
832	default:
833	    PositionMenu(menu, NULL);
834	    break;
835    }
836}
837
838/*
839 * Widget Action Routines
840 */
841/*
842 * Function:
843 *	Unhighlight
844 *
845 * Parameters:
846 *	w	   - simple menu widget
847 *	event	   - event that caused this action
848 *	params	   - not used
849 *	num_params - ""
850 *
851 * Description:
852 *	Unhighlights current entry.
853 */
854/*ARGSUSED*/
855static void
856Unhighlight(Widget w, XEvent *event _X_UNUSED, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
857{
858    SimpleMenuWidget smw = (SimpleMenuWidget)w;
859    SmeObject entry = smw->simple_menu.entry_set;
860
861    if (entry == NULL)
862	return;
863
864#ifndef OLDXAW
865    if (!smw->simple_menu.sub_menu)
866#endif
867    {
868	SmeObjectClass cclass;
869
870	smw->simple_menu.entry_set = NULL;
871	cclass = (SmeObjectClass)entry->object.widget_class;
872	(cclass->sme_class.unhighlight)((Widget)entry);
873    }
874}
875
876/*
877 * Function:
878 *	Highlight
879 *
880 * Parameters:
881 *	w	   - simple menu widget
882 *	event	   - event that caused this action
883 *	params	   - not used
884 *	num_params - ""
885 *
886 * Description:
887 *	Highlights current entry.
888 */
889/*ARGSUSED*/
890static void
891Highlight(Widget w, XEvent *event, String *params, Cardinal *num_params)
892{
893    SimpleMenuWidget smw = (SimpleMenuWidget)w;
894    SmeObject entry;
895
896    if (!XtIsSensitive(w))
897	return;
898
899    entry = GetEventEntry(w, event);
900
901    if (entry == smw->simple_menu.entry_set)
902	return;
903
904#ifndef OLDXAW
905    if (!smw->simple_menu.sub_menu)
906#endif
907	Unhighlight(w, event, params, num_params);
908
909    if (entry == NULL)
910	return;
911
912    if (!XtIsSensitive((Widget)entry))
913	return;
914
915#ifndef OLDXAW
916    if (smw->simple_menu.sub_menu)
917	PopdownSubMenu(smw);
918#endif
919
920    Unhighlight(w, event, params, num_params);
921
922#ifndef OLDXAW
923    if (!(smw->simple_menu.state & SMW_UNMAPPING))
924#endif
925    {
926	SmeObjectClass cclass;
927
928	smw->simple_menu.entry_set = entry;
929	cclass = (SmeObjectClass)entry->object.widget_class;
930
931	(cclass->sme_class.highlight)((Widget)entry);
932
933#ifndef OLDXAW
934	if (XtIsSubclass((Widget)entry, smeBSBObjectClass))
935	    PopupSubMenu(smw);
936#endif
937    }
938}
939
940/*
941 * Function:
942 *	Notify
943 *
944 * Parameters:
945 *	w	   - simple menu widget
946 *	event	   - event that caused this action
947 *	params	   - not used
948 *	num_params - ""
949 *
950 * Description:
951 *	Notify user of current entry.
952 */
953/*ARGSUSED*/
954static void
955Notify(Widget w, XEvent *event, String *params _X_UNUSED, Cardinal *num_params _X_UNUSED)
956{
957    SmeObject entry;
958    SmeObjectClass cclass;
959
960    /* may be a propagated event from a sub menu, need to check it */
961    if (XtWindow(w) != event->xany.window)
962	return;
963    entry = GetEventEntry(w, event);
964    if (entry == NULL || !XtIsSensitive((Widget)entry))
965	return;
966
967    cclass = (SmeObjectClass) entry->object.widget_class;
968    (cclass->sme_class.notify)((Widget)entry);
969}
970
971/*
972 * Public Functions
973 */
974/*
975 * Function:
976 *	XawSimpleMenuAddGlobalActions
977 *
978 * Arguments:
979 *	app_con - appcontext
980 *
981 * Description:
982 *	Adds the global actions to the simple menu widget.
983 */
984void
985XawSimpleMenuAddGlobalActions(XtAppContext app_con)
986{
987    XtInitializeWidgetClass(simpleMenuWidgetClass);
988    XmuCallInitializers(app_con);
989}
990
991/*
992 * Function:
993 *	XawSimpleMenuGetActiveEntry
994 *
995 * Parameters:
996 *	w - smw widget
997 *
998 * Description:
999 *	Gets the currently active (set) entry.
1000 *
1001 * Returns:
1002 *	The currently set entry or NULL if none is set
1003 */
1004Widget
1005XawSimpleMenuGetActiveEntry(Widget w)
1006{
1007    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1008
1009    return ((Widget)smw->simple_menu.entry_set);
1010}
1011
1012/*
1013 * Function:
1014 *	XawSimpleMenuClearActiveEntry
1015 *
1016 * Parameters:
1017 *	w - smw widget
1018 *
1019 * Description:
1020 *	Unsets the currently active (set) entry.
1021 */
1022void
1023XawSimpleMenuClearActiveEntry(Widget w)
1024{
1025    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1026
1027    smw->simple_menu.entry_set = NULL;
1028}
1029
1030/*
1031 * Private Functions
1032 */
1033/*
1034 * Function:
1035 *	CreateLabel
1036 *
1037 * Parameters:
1038 *	w - smw widget
1039 *
1040 * Description:
1041 * Creates the label object and makes sure it is the first child in
1042 * in the list.
1043 */
1044static void
1045CreateLabel(Widget w)
1046{
1047    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1048    Widget *child, *next_child;
1049    int i;
1050    Arg args[2];
1051
1052    if (smw->simple_menu.label_string == NULL ||
1053	smw->simple_menu.label != NULL) {
1054	XtAppWarning(XtWidgetToApplicationContext(w),
1055		     "Xaw Simple Menu Widget: label string is NULL or "
1056		     "label already exists, no label is being created.");
1057	return;
1058    }
1059
1060    XtSetArg(args[0], XtNlabel, smw->simple_menu.label_string);
1061    XtSetArg(args[1], XtNjustify, XtJustifyCenter);
1062    smw->simple_menu.label = (SmeObject)
1063	XtCreateManagedWidget("menuLabel",
1064			      smw->simple_menu.label_class, w, args, TWO);
1065
1066    next_child = NULL;
1067    for (child = smw->composite.children + smw->composite.num_children,
1068	 i = (int)smw->composite.num_children; i > 0; i--, child--) {
1069	if (next_child != NULL)
1070	    *next_child = *child;
1071	next_child = child;
1072    }
1073    *child = (Widget)smw->simple_menu.label;
1074}
1075
1076/*
1077 * Function:
1078 *	Layout
1079 *
1080 * Arguments:
1081 *	w	   - See below
1082 *	width_ret  - returned width
1083 *	height_ret - returned height
1084 *
1085 * Note:
1086 * if width == NULL || height == NULL then it assumes the you do not care
1087 * about the return values, and just want a relayout.
1088 *
1089 * if this is not the case then it will set width_ret and height_ret
1090 * to be width and height that the child would get if it were laid out
1091 * at this time.
1092 *
1093 *	"w" can be the simple menu widget or any of its object children.
1094 */
1095static void
1096Layout(Widget w, Dimension *width_ret, Dimension *height_ret)
1097{
1098    SmeObject current_entry;
1099    SimpleMenuWidget smw;
1100    Dimension width, height;
1101    Boolean allow_change_size;
1102    Widget kid;
1103    Cardinal i, count, n;
1104    int width_kid, height_kid, tmp_w, tmp_h;
1105    short vadd, hadd, x_ins, y_ins;
1106    Dimension *widths;
1107
1108    height = 0;
1109
1110    if (XtIsSubclass(w, simpleMenuWidgetClass)) {
1111	smw = (SimpleMenuWidget)w;
1112	current_entry = NULL;
1113    }
1114    else {
1115	smw = (SimpleMenuWidget)XtParent(w);
1116	current_entry = (SmeObject)w;
1117    }
1118
1119    allow_change_size = (!XtIsRealized((Widget)smw)
1120			 || smw->shell.allow_shell_resize);
1121
1122    for (i = smw->simple_menu.label ? 1 : 0;
1123	 i < smw->composite.num_children;
1124	 i++) {
1125	XtWidgetGeometry preferred;
1126
1127	kid = smw->composite.children[i];
1128	if (!XtIsManaged(kid))
1129	    continue;
1130	if (smw->simple_menu.row_height != 0)
1131	    XtHeight(kid) = smw->simple_menu.row_height;
1132	XtQueryGeometry(kid, NULL, &preferred);
1133	if (preferred.request_mode & CWWidth)
1134	    XtWidth(kid) = preferred.width;
1135    }
1136
1137    if (smw->simple_menu.label
1138	&& XtIsManaged((Widget)smw->simple_menu.label)) {
1139	XtWidgetGeometry preferred;
1140
1141	kid = (Widget)smw->simple_menu.label;
1142	XtQueryGeometry(kid, NULL, &preferred);
1143	if (preferred.request_mode & CWWidth)
1144	    XtWidth(kid) = preferred.width;
1145	if (preferred.request_mode & CWHeight)
1146	    XtHeight(kid) = preferred.height;
1147    }
1148
1149    /* reset */
1150    if (!smw->simple_menu.menu_width)
1151	XtWidth(smw) = 0;
1152    if (!smw->simple_menu.menu_height)
1153	XtHeight(smw) = 0;
1154    if (!XtWidth(smw) || !XtHeight(smw))
1155	MakeResizeRequest((Widget)smw);
1156
1157    widths = (Dimension *)XtMalloc(sizeof(Dimension));
1158#ifndef OLDXAW
1159    hadd = (short)smw->simple_menu.left_margin;
1160#else
1161    hadd = 0;
1162#endif
1163    vadd = (short)smw->simple_menu.top_margin;
1164    if (smw->simple_menu.label)
1165	vadd = (short)(vadd + XtHeight(smw->simple_menu.label));
1166
1167    count = 1;
1168    width = (Dimension)(tmp_w = tmp_h = (int)(n = 0));
1169    height = (Dimension)vadd;
1170
1171    for (i = smw->simple_menu.label ? 1 : 0;
1172	 i < smw->composite.num_children;
1173	 i++) {
1174	kid = smw->composite.children[i];
1175	if (!XtIsManaged(kid))
1176	    continue;
1177	width_kid = XtWidth(kid);
1178	height_kid = XtHeight(kid);
1179
1180	if (n && (height + height_kid + smw->simple_menu.bottom_margin
1181		  > XtHeight(smw))) {
1182	    ++count;
1183	    widths = (Dimension *)XtRealloc((char *)widths,
1184					    (Cardinal)(sizeof(Dimension) * count));
1185	    widths[count - 1] = (Dimension)width_kid;
1186	    width = (Dimension)(width + tmp_w);
1187	    tmp_w = width_kid;
1188	    height = (Dimension)(height_kid + vadd);
1189	}
1190	else
1191	    height = (Dimension)(height + height_kid);
1192	if (height > tmp_h)
1193	    tmp_h = height;
1194	if (width_kid > tmp_w)
1195	    widths[count - 1] = (Dimension)(tmp_w = width_kid);
1196	++n;
1197    }
1198
1199    height = (tmp_h + smw->simple_menu.bottom_margin);
1200    width = (Dimension)(width + tmp_w);
1201
1202    if (smw->simple_menu.label && width < XtWidth(smw->simple_menu.label)) {
1203	float inc;
1204
1205	inc = (float)(XtWidth(smw->simple_menu.label) - width) / (float)count;
1206	width = XtWidth(smw->simple_menu.label);
1207	for (n = 0; n < count; n++)
1208	    widths[n] = (Dimension)(widths[n] + inc);
1209    }
1210
1211#ifndef OLDXAW
1212    width = (Dimension)(width + (hadd + smw->simple_menu.right_margin));
1213#endif
1214
1215    x_ins = (short)(n = count = 0);
1216    tmp_w = widths[0];
1217    tmp_h = vadd;
1218
1219    for (i = smw->simple_menu.label ? 1 : 0;
1220	 i < smw->composite.num_children;
1221	 i++) {
1222	kid = smw->composite.children[i];
1223	if (!XtIsManaged(kid))
1224	    continue;
1225
1226	height_kid = XtHeight(kid);
1227
1228	if (n && (tmp_h + height_kid + smw->simple_menu.bottom_margin
1229		  > XtHeight(smw))) {
1230	    x_ins = (short)tmp_w;
1231	    y_ins = vadd;
1232	    ++count;
1233	    tmp_w += widths[count];
1234	    tmp_h = height_kid + vadd;
1235	}
1236	else {
1237	    y_ins = (short)tmp_h;
1238	    tmp_h += height_kid;
1239	}
1240	++n;
1241
1242	XtX(kid) = (Position)(x_ins + hadd);
1243	XtY(kid) = y_ins;
1244	XtWidth(kid) = widths[count];
1245    }
1246
1247    XtFree((char *)widths);
1248
1249    if (allow_change_size)
1250	MakeSetValuesRequest((Widget) smw, width, height);
1251
1252    if (smw->simple_menu.label) {
1253	XtX(smw->simple_menu.label) = 0;
1254	XtY(smw->simple_menu.label) = (Position)smw->simple_menu.top_margin;
1255	XtWidth(smw->simple_menu.label) = (Dimension)(XtWidth(smw)
1256#ifndef OLDXAW
1257	    - (smw->simple_menu.left_margin + smw->simple_menu.right_margin)
1258#endif
1259	    );
1260    }
1261    if (current_entry) {
1262	if (width_ret)
1263	    *width_ret = XtWidth(current_entry);
1264	if (height_ret)
1265	    *height_ret = XtHeight(current_entry);
1266    }
1267}
1268
1269/*
1270 * Function:
1271 *	AddPositionAction
1272 *
1273 * Parameters:
1274 *	app_con - application context
1275 *	data	- (not used)
1276 *
1277 * Description:
1278 *	  Adds the XawPositionSimpleMenu action to the global
1279 *                   action list for this appcon.
1280 */
1281/*ARGSUSED*/
1282static void
1283AddPositionAction(XtAppContext app_con, XPointer data _X_UNUSED)
1284{
1285    static XtActionsRec pos_action[] = {
1286	{"XawPositionSimpleMenu",	PositionMenuAction},
1287    };
1288
1289    XtAppAddActions(app_con, pos_action, XtNumber(pos_action));
1290}
1291
1292/*
1293 * Function:
1294 *	FindMenu
1295 *
1296 * Parameters:
1297 *	widget - reference widget
1298 *	name   - menu widget's name
1299 *
1300 * Description:
1301 *	Find the menu give a name and reference widget
1302 *
1303 * Returns:
1304 *	The menu widget or NULL.
1305 */
1306static Widget
1307FindMenu(Widget widget, String name)
1308{
1309    Widget w, menu;
1310
1311    for (w = widget; w != NULL; w = XtParent(w))
1312	if ((menu = XtNameToWidget(w, name)) != NULL)
1313	    return (menu);
1314
1315    return (NULL);
1316}
1317
1318/*
1319 * Function:
1320 *	PositionMenu
1321 *
1322 * Parameters:
1323 *	w	 - simple menu widget
1324 *	location - pointer the the position or NULL
1325 *
1326 * Description:
1327 *	Places the menu
1328 */
1329static void
1330PositionMenu(Widget w, XPoint *location)
1331{
1332    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1333    SmeObject entry;
1334    XPoint t_point;
1335
1336    if (location == NULL) {
1337	Window temp1, temp2;
1338	int root_x, root_y, tempX, tempY;
1339	unsigned int tempM;
1340
1341	location = &t_point;
1342	if (XQueryPointer(XtDisplay(w), XtWindow(w), &temp1, &temp2,
1343			  &root_x, &root_y, &tempX, &tempY, &tempM) == False) {
1344	    XtAppWarning(XtWidgetToApplicationContext(w),
1345			 "Xaw Simple Menu Widget: "
1346			 "Could not find location of mouse pointer");
1347	    return;
1348	}
1349	location->x = (short) root_x;
1350	location->y = (short) root_y;
1351    }
1352
1353    /*
1354     * The width will not be correct unless it is realized
1355     */
1356    XtRealizeWidget(w);
1357
1358    location->x = (short)(location->x - (XtWidth(w) >> 1));
1359
1360    if (smw->simple_menu.popup_entry == NULL)
1361	entry = smw->simple_menu.label;
1362    else
1363	entry = smw->simple_menu.popup_entry;
1364
1365    if (entry != NULL)
1366      location->y = (short)(location->y - (XtY(entry) + (XtHeight(entry) >> 1)));
1367
1368    MoveMenu(w, location->x, location->y);
1369}
1370
1371/*
1372 * Function:
1373 *	MoveMenu
1374 *
1375 * Parameters:
1376 *	w - simple menu widget
1377 *	x - current location of the widget
1378 *	y - ""
1379 *
1380 * Description:
1381 *	  Actually moves the menu, may force it to
1382 *	to be fully visible if menu_on_screen is True.
1383 */
1384static void
1385MoveMenu(Widget w, int x, int y)
1386{
1387    Arg arglist[2];
1388    Cardinal num_args = 0;
1389    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1390
1391    if (smw->simple_menu.menu_on_screen) {
1392	int width = XtWidth(w) + (XtBorderWidth(w) << 1);
1393	int height = XtHeight(w) + (XtBorderWidth(w) << 1);
1394
1395	if (x >= 0) {
1396	    int scr_width = WidthOfScreen(XtScreen(w));
1397
1398	    if (x + width > scr_width)
1399		x = scr_width - width;
1400	}
1401	if (x < 0)
1402	    x = 0;
1403
1404	if (y >= 0) {
1405	    int scr_height = HeightOfScreen(XtScreen(w));
1406
1407	    if (y + height > scr_height)
1408		y = scr_height - height;
1409	}
1410	if (y < 0)
1411	    y = 0;
1412    }
1413
1414    XtSetArg(arglist[num_args], XtNx, x); num_args++;
1415    XtSetArg(arglist[num_args], XtNy, y); num_args++;
1416    XtSetValues(w, arglist, num_args);
1417}
1418
1419/*
1420 * Function:
1421 *	ChangeCursorOnGrab
1422 *
1423 * Parameters:
1424 *	w     - menu widget
1425 *	temp1 - not used
1426 *	temp2 - ""
1427 *
1428 * Description:
1429 *	  Changes the cursor on the active grab to the one
1430 *                   specified in out resource list.
1431 */
1432/*ARGSUSED*/
1433static void
1434ChangeCursorOnGrab(Widget w, XtPointer temp1 _X_UNUSED, XtPointer temp2 _X_UNUSED)
1435{
1436    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1437
1438    /*
1439     * The event mask here is what is currently in the MIT implementation.
1440     * There really needs to be a way to get the value of the mask out
1441     * of the toolkit (CDP 5/26/89).
1442     */
1443    XChangeActivePointerGrab(XtDisplay(w), ButtonPressMask | ButtonReleaseMask,
1444			     smw->simple_menu.cursor,
1445			     XtLastTimestampProcessed(XtDisplay(w)));
1446}
1447
1448/*
1449 * Function:
1450 *	MakeSetValuesRequest
1451 *
1452 * Parameters:
1453 *	w      - simple menu widget
1454 *	width  - size requested
1455 *	height - ""
1456 */
1457static void
1458MakeSetValuesRequest(Widget w, unsigned int width, unsigned int height)
1459{
1460    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1461    Arg arglist[2];
1462    Cardinal num_args = 0;
1463
1464    if (!smw->simple_menu.recursive_set_values) {
1465	if (XtWidth(smw) != width || XtHeight(smw) != height) {
1466	    smw->simple_menu.recursive_set_values = True;
1467	    XtSetArg(arglist[num_args], XtNwidth, width);   num_args++;
1468	    XtSetArg(arglist[num_args], XtNheight, height); num_args++;
1469	    XtSetValues(w, arglist, num_args);
1470	}
1471	else if (XtIsRealized((Widget)smw))
1472	    XawSimpleMenuRedisplay((Widget)smw, NULL, NULL);
1473    }
1474    smw->simple_menu.recursive_set_values = False;
1475}
1476
1477static SmeObject
1478DoGetEventEntry(Widget w, int x_loc, int y_loc)
1479{
1480    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1481    SmeObject *entry;
1482
1483    ForAllChildren(smw, entry) {
1484	if (!XtIsManaged((Widget)*entry))
1485	    continue;
1486
1487	if (x_loc > XtX(*entry)
1488	    && x_loc <= XtX(*entry) + XtWidth(*entry)
1489	    && y_loc > XtY(*entry)
1490	    &&  y_loc <= XtY(*entry) + XtHeight(*entry)) {
1491	    if (*entry == smw->simple_menu.label)
1492		return (NULL);	/* cannot select the label */
1493	    else
1494		return (*entry);
1495	}
1496    }
1497
1498    return (NULL);
1499}
1500
1501/*
1502 * Function:
1503 *	GetEventEntry
1504 *
1505 * Parameters:
1506 *	w     - simple menu widget
1507 *	event - X event
1508 *
1509 * Description:
1510 *	Gets an entry given an event that has X and Y coords.
1511 *
1512 * Returns:
1513 *	The entry that this point is in
1514 */
1515static SmeObject
1516GetEventEntry(Widget w, XEvent *event)
1517{
1518    int x_loc, y_loc, x_root;
1519    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1520    SmeObject entry;
1521    int warp, move;
1522
1523    switch (event->type) {
1524	case MotionNotify:
1525	    x_loc = event->xmotion.x;
1526	    y_loc = event->xmotion.y;
1527	    x_root = event->xmotion.x_root;
1528	    break;
1529	case EnterNotify:
1530	case LeaveNotify:
1531	    x_loc = event->xcrossing.x;
1532	    y_loc = event->xcrossing.y;
1533	    x_root = event->xcrossing.x_root;
1534	    break;
1535	case ButtonPress:
1536	case ButtonRelease:
1537	    x_loc = event->xbutton.x;
1538	    y_loc = event->xbutton.y;
1539	    x_root = event->xbutton.x_root;
1540	    break;
1541	default:
1542	    XtAppError(XtWidgetToApplicationContext(w),
1543		       "Unknown event type in GetEventEntry().");
1544	    return (NULL);
1545    }
1546
1547    if (x_loc < 0 || x_loc >= XtWidth(smw) ||
1548	y_loc < 0 || y_loc >= XtHeight(smw))
1549	return (NULL);
1550
1551    /* Move the menu if it's outside the screen, does not check
1552     * smw->simple_menu.menu_on_screen because menus is bigger than screen
1553     */
1554    if (x_root == WidthOfScreen(XtScreen(w)) - 1 &&
1555	XtX(w) + XtWidth(w) + (XtBorderWidth(w)) > x_root) {
1556	warp = -8;
1557	if (smw->simple_menu.entry_set) {
1558	    entry = DoGetEventEntry(w,
1559				    XtX(smw->simple_menu.entry_set)
1560				    + XtWidth(smw->simple_menu.entry_set) + 1,
1561				    y_loc);
1562	    Unhighlight(w, event, NULL, NULL);
1563	    if (entry) {
1564		warp = -(int)XtWidth(entry) >> 1;
1565		move = x_loc - XtWidth(entry) - XtX(entry) + XtBorderWidth(w);
1566	    }
1567	    else {
1568		warp = 0;
1569		move = WidthOfScreen(XtScreen(w)) -
1570		       (XtX(w) + XtWidth(w) + (XtBorderWidth(w) << 1));
1571	    }
1572	}
1573	else {
1574	    warp = 0;
1575	    move = WidthOfScreen(XtScreen(w)) -
1576		   (XtX(w) + XtWidth(w) + (XtBorderWidth(w) << 1));
1577	}
1578    }
1579    else if (x_root == 0 && XtX(w) < 0) {
1580	warp = 8;
1581	if (smw->simple_menu.entry_set) {
1582	    entry = DoGetEventEntry(w, XtX(smw->simple_menu.entry_set) - 1,
1583				    y_loc);
1584	    Unhighlight(w, event, NULL, NULL);
1585	    if (entry) {
1586		warp = XtWidth(entry) >> 1;
1587		move = x_loc - XtX(entry);
1588	    }
1589	    else
1590		move = x_loc + XtBorderWidth(w);
1591	}
1592	else
1593	    move = x_loc + XtBorderWidth(w);
1594    }
1595    else
1596	move = warp = 0;
1597
1598    if (move)
1599	XtMoveWidget(w, (Position)(XtX(w) + move), XtY(w));
1600    if (warp)
1601	XWarpPointer(XtDisplay(w), None, None, 0, 0, 0, 0, warp, 0);
1602
1603    return (DoGetEventEntry(w, x_loc, y_loc));
1604}
1605
1606static void
1607CalculateNewSize(Widget w, Dimension *width_return, Dimension *height_return)
1608{
1609    SimpleMenuWidget xaw = (SimpleMenuWidget)w;
1610    Widget kid;
1611    Cardinal i;
1612    int width_kid, height_kid;
1613    int width, height, tmp_w, tmp_h, max_dim;
1614    short vadd, hadd;
1615    int n, columns, test_h, num_children = 0;
1616    Boolean try_layout = False;
1617
1618#ifndef OLDXAW
1619    hadd = (short)(xaw->simple_menu.left_margin + xaw->simple_menu.right_margin);
1620#else
1621    hadd = 0;
1622#endif
1623    vadd = (short)(xaw->simple_menu.top_margin + xaw->simple_menu.bottom_margin);
1624    if (xaw->simple_menu.label)
1625	vadd = (short)(vadd + XtHeight(xaw->simple_menu.label));
1626
1627    if (*height_return)
1628	max_dim = *height_return;
1629    else if (!XtHeight(w)) {
1630	max_dim = HeightOfScreen(XtScreen(w));
1631	try_layout = True;
1632    }
1633    else
1634	max_dim = XtHeight(w);
1635    max_dim -= vadd;
1636
1637    width = height = tmp_w = tmp_h = n = test_h = 0;
1638    columns = 1;
1639    for (i = xaw->simple_menu.label ? 1 : 0;
1640	 i < xaw->composite.num_children;
1641	 i++) {
1642	kid = xaw->composite.children[i];
1643	if (!XtIsManaged(kid))
1644	    continue;
1645	++num_children;
1646	width_kid = XtWidth(kid);
1647	height_kid = XtHeight(kid);
1648
1649	if (try_layout) {
1650	    if (!test_h)
1651		test_h = height_kid;
1652	    else if (test_h != height_kid)
1653		try_layout = False;
1654	}
1655
1656	if (n && (height + height_kid > max_dim)) {
1657	    ++columns;
1658	    width += tmp_w;
1659	    tmp_w = width_kid;
1660	    height = height_kid;
1661	}
1662	else
1663	    height += height_kid;
1664	if (height > tmp_h)
1665	    tmp_h = height;
1666	if (width_kid > tmp_w)
1667	    tmp_w = width_kid;
1668	++n;
1669    }
1670
1671    height = tmp_h + vadd;
1672    width += tmp_w + hadd;
1673
1674    if (xaw->simple_menu.label)
1675	width = XawMax(width, XtWidth(xaw->simple_menu.label) + hadd);
1676
1677    *width_return = (Dimension)width;
1678    *height_return = (Dimension)height;
1679
1680    if (try_layout && columns > 1 && num_children > 2) {
1681	int space;
1682
1683	height = test_h * (xaw->simple_menu.label ?
1684			   num_children - 1 :
1685			   num_children);
1686
1687	max_dim -= max_dim % test_h;
1688	space = max_dim - (height % max_dim);
1689	if (space >= test_h * columns) {
1690	    height = max_dim - space / columns;
1691	    if (height % test_h)
1692		height += test_h - (height % test_h);
1693	    *height_return = (Dimension)(height + vadd);
1694	    CalculateNewSize(w, width_return, height_return);
1695	}
1696    }
1697}
1698
1699static void
1700MakeResizeRequest(Widget w)
1701{
1702    int tries;
1703    Dimension width, height;
1704
1705    width = XtWidth(w);
1706    height = XtHeight(w);
1707
1708    for (tries = 0; tries < 100; tries++) {
1709	CalculateNewSize(w, &width, &height);
1710	if (width == XtWidth(w) && height == XtHeight(w))
1711	    break;
1712	if (XtMakeResizeRequest(w, width, height, &width, &height) ==
1713	    XtGeometryNo)
1714	break;
1715    }
1716}
1717
1718#ifndef OLDXAW
1719static void
1720Popdown(Widget w, XEvent *event, String *params, Cardinal *num_params)
1721{
1722    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1723
1724    while (XtParent(w) &&
1725	   XtIsSubclass(XtParent(w), simpleMenuWidgetClass)) {
1726	if (((SimpleMenuWidget)XtParent(w))->simple_menu.sub_menu == (Widget)w) {
1727	    w = XtParent(w);
1728	    smw = (SimpleMenuWidget)w;
1729	    smw->simple_menu.entry_set = NULL;
1730	}
1731	else
1732	    break;
1733    }
1734
1735    smw->simple_menu.state |= SMW_UNMAPPING;
1736    if (smw->simple_menu.sub_menu)
1737	PopdownSubMenu(smw);
1738    XtCallActionProc(w, "XtMenuPopdown", event, params, *num_params);
1739}
1740
1741static void
1742PopupSubMenu(SimpleMenuWidget smw)
1743{
1744    Arg args[2];
1745    Cardinal num_args;
1746    Widget menu;
1747    SmeBSBObject entry = (SmeBSBObject)smw->simple_menu.entry_set;
1748    Position menu_x, menu_y;
1749    Bool popleft;
1750
1751    if (entry->sme_bsb.menu_name == NULL)
1752	return;
1753
1754    if ((menu = FindMenu((Widget)smw, entry->sme_bsb.menu_name)) == NULL)
1755	return;
1756
1757    smw->simple_menu.sub_menu = menu;
1758
1759    if (!XtIsRealized(menu))
1760	XtRealizeWidget(menu);
1761
1762    popleft = (smw->simple_menu.state & SMW_POPLEFT) != 0;
1763
1764    if (popleft)
1765	XtTranslateCoords((Widget)smw,
1766			  (Position)(-(int)XtWidth(menu)),
1767			  (Position)(XtY(entry) - XtBorderWidth(menu)),
1768			  &menu_x, &menu_y);
1769    else
1770	XtTranslateCoords((Widget)smw,
1771			  (Position)XtWidth(smw),
1772			  (Position)(XtY(entry) - XtBorderWidth(menu)),
1773			  &menu_x, &menu_y);
1774
1775    if (!popleft && menu_x >= 0) {
1776	int scr_width = WidthOfScreen(XtScreen(menu));
1777
1778	if (menu_x + XtWidth(menu) > scr_width) {
1779	    menu_x = (Position)(menu_x - (XtWidth(menu) + XtWidth(smw)));
1780	    popleft = True;
1781	}
1782    }
1783    else if (popleft && menu_x < 0) {
1784	menu_x = 0;
1785	popleft = False;
1786    }
1787    if (menu_y >= 0) {
1788	int scr_height = HeightOfScreen(XtScreen(menu));
1789
1790	if (menu_y + XtHeight(menu) > scr_height)
1791	    menu_y = (Position)(scr_height - XtHeight(menu) - XtBorderWidth(menu));
1792    }
1793    if (menu_y < 0)
1794	menu_y = 0;
1795
1796    num_args = 0;
1797    XtSetArg(args[num_args], XtNx, menu_x);	num_args++;
1798    XtSetArg(args[num_args], XtNy, menu_y);	num_args++;
1799    XtSetValues(menu, args, num_args);
1800
1801    if (popleft)
1802	((SimpleMenuWidget)menu)->simple_menu.state |= SMW_POPLEFT;
1803    else
1804	((SimpleMenuWidget)menu)->simple_menu.state &= (unsigned char)(~SMW_POPLEFT);
1805
1806    XtPopup(menu, XtGrabNone);
1807}
1808
1809static void
1810PopdownSubMenu(SimpleMenuWidget smw)
1811{
1812    SimpleMenuWidget menu = (SimpleMenuWidget)smw->simple_menu.sub_menu;
1813
1814    if (!menu)
1815	return;
1816
1817    menu->simple_menu.state |= SMW_UNMAPPING;
1818    PopdownSubMenu(menu);
1819
1820    XtPopdown((Widget)menu);
1821
1822    smw->simple_menu.sub_menu = NULL;
1823}
1824
1825/*ARGSUSED*/
1826static void
1827PopupCB(Widget w, XtPointer client_data _X_UNUSED, XtPointer call_data _X_UNUSED)
1828{
1829    SimpleMenuWidget smw = (SimpleMenuWidget)w;
1830
1831    smw->simple_menu.state &= (unsigned char)(~(SMW_UNMAPPING | SMW_POPLEFT));
1832}
1833#endif /* OLDXAW */
1834