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