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