/* $Xorg: SimpleMenu.c,v 1.4 2001/02/09 02:03:45 xorgcvs Exp $ */

/*
Copyright 1989, 1994, 1998  The Open Group

Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that
the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation.

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of The Open Group shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from The Open Group.
 */

/* $XFree86: xc/lib/Xaw/SimpleMenu.c,v 3.21 2001/03/23 23:59:15 paulo Exp $ */

/*
 * SimpleMenu.c - Source code file for SimpleMenu widget.
 *
 * Date:    April 3, 1989
 *
 * By:      Chris D. Peterson
 *          MIT X Consortium 
 *          kit@expo.lcs.mit.edu
 */

#include <stdio.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Xmu/Initer.h>
#include <X11/Xmu/SysUtil.h>
#include <X11/Xaw/Cardinals.h>
#include <X11/Xaw/SimpleMenP.h>
#include <X11/Xaw/SmeBSBP.h>
#include <X11/Xaw/XawInit.h>
#include "Private.h"

#define streq(a, b)	(strcmp((a), (b)) == 0)

#define ForAllChildren(smw, childP)				\
for ((childP) = (SmeObject *)(smw)->composite.children;		\
     (childP) < (SmeObject *)((smw)->composite.children		\
			      + (smw)->composite.num_children);	\
     (childP)++)

#ifndef OLDXAW
#define	SMW_UNMAPPING	0x01
#define SMW_POPLEFT	0x02
#endif

/*
 * Class Methods
 */
static void XawSimpleMenuChangeManaged(Widget);
static void XawSimpleMenuClassInitialize(void);
static void XawSimpleMenuClassPartInitialize(WidgetClass);
static XtGeometryResult XawSimpleMenuGeometryManager(Widget, XtWidgetGeometry*,
						     XtWidgetGeometry*);
static void XawSimpleMenuInitialize(Widget, Widget, ArgList, Cardinal*);
static void XawSimpleMenuRealize(Widget, XtValueMask*, XSetWindowAttributes*);
static void XawSimpleMenuRedisplay(Widget, XEvent*, Region);
static void XawSimpleMenuResize(Widget);
static Boolean XawSimpleMenuSetValues(Widget, Widget, Widget,
				      ArgList, Cardinal*);
static Boolean XawSimpleMenuSetValuesHook(Widget, ArgList, Cardinal*);
#ifndef OLDXAW
static void PopupSubMenu(SimpleMenuWidget);
static void PopdownSubMenu(SimpleMenuWidget);
static void PopupCB(Widget, XtPointer, XtPointer);
#endif

/*
 * Prototypes
 */
static void AddPositionAction(XtAppContext, XPointer);
static void CalculateNewSize(Widget, Dimension*, Dimension*);
static void ChangeCursorOnGrab(Widget, XtPointer, XtPointer);
static void CreateLabel(Widget);
static SmeObject DoGetEventEntry(Widget, int, int);
static Widget FindMenu(Widget, String);
static SmeObject GetEventEntry(Widget, XEvent*);
static void Layout(Widget, Dimension*, Dimension*);
static void MakeResizeRequest(Widget);
static void MakeSetValuesRequest(Widget, unsigned int, unsigned int);
static void MoveMenu(Widget, int, int);
static void PositionMenu(Widget, XPoint*);

/*
 * Actions
 */
static void Highlight(Widget, XEvent*, String*, Cardinal*);
static void Notify(Widget, XEvent*, String*, Cardinal*);
#ifndef OLDXAW
static void Popdown(Widget, XEvent*, String*, Cardinal*);
#endif
static void PositionMenuAction(Widget, XEvent*, String*, Cardinal*);
static void Unhighlight(Widget, XEvent*, String*, Cardinal*);

/*
 * Initialization
 */
#define offset(field)	XtOffsetOf(SimpleMenuRec, simple_menu.field)

static XtResource resources[] = {
  /* label */
  {
    XtNlabel,
    XtCLabel,
    XtRString,
    sizeof(String),
    offset(label_string),
    XtRString,
    NULL
  },
  {
    XtNlabelClass,
    XtCLabelClass,
    XtRPointer,
    sizeof(WidgetClass),
    offset(label_class),
    XtRImmediate,
    NULL
  },

  /* layout */
  {
    XtNrowHeight,
    XtCRowHeight,
    XtRDimension,
    sizeof(Dimension),
    offset(row_height),
    XtRImmediate,
    (XtPointer)0
  },
  {
    XtNtopMargin,
    XtCVerticalMargins,
    XtRDimension,
    sizeof(Dimension),
    offset(top_margin),
    XtRImmediate,
    (XtPointer)0
  },
  {
    XtNbottomMargin,
    XtCVerticalMargins,
    XtRDimension,
    sizeof(Dimension),
    offset(bottom_margin),
    XtRImmediate,
    (XtPointer)0
  },
#ifndef OLDXAW
  {
    XtNleftMargin,
    XtCHorizontalMargins,
    XtRDimension,
    sizeof(Dimension),
    offset(left_margin),
    XtRImmediate,
    (XtPointer)0
  },
  {
    XtNrightMargin,
    XtCHorizontalMargins,
    XtRDimension,
    sizeof(Dimension),
    offset(right_margin),
    XtRImmediate,
    (XtPointer)0
  },
#endif

  /* misc */
  {
    XtNallowShellResize,
    XtCAllowShellResize,
    XtRBoolean,
    sizeof(Boolean),
    XtOffsetOf(SimpleMenuRec, shell.allow_shell_resize),
    XtRImmediate,
    (XtPointer)True
  },
  {
    XtNcursor,
    XtCCursor,
    XtRCursor,
    sizeof(Cursor),
    offset(cursor),
    XtRImmediate,
    (XtPointer)None
  },
  {
    XtNmenuOnScreen,
    XtCMenuOnScreen,
    XtRBoolean,
    sizeof(Boolean),
    offset(menu_on_screen),
    XtRImmediate,
    (XtPointer)True
  },
  {
    XtNpopupOnEntry,
    XtCPopupOnEntry,
    XtRWidget,
    sizeof(Widget),
    offset(popup_entry),
    XtRWidget,
    NULL
  },
  {
    XtNbackingStore,
    XtCBackingStore,
    XtRBackingStore,
    sizeof(int),
    offset(backing_store), 
    XtRImmediate,
    (XtPointer)(Always + WhenMapped + NotUseful)
  },
#ifndef OLDXAW
  {
    XawNdisplayList,
    XawCDisplayList,
    XawRDisplayList,
    sizeof(XawDisplayList*),
    offset(display_list), 
    XtRImmediate,
    NULL
  },
#endif
};  
#undef offset

static char defaultTranslations[] =
"<Enter>:"	"highlight()\n"
"<Leave>:"	"unhighlight()\n"
"<BtnMotion>:"	"highlight()\n"
#ifndef OLDXAW
"<BtnUp>:"	"popdown() notify() unhighlight()\n"
#else
"<BtnUp>:"	"MenuPopdown() notify() unhighlight()\n"
#endif
;

static XtActionsRec actionsList[] =
{
  {"notify",            Notify},
  {"highlight",         Highlight},
  {"unhighlight",       Unhighlight},
#ifndef OLDXAW
  {"popdown",		Popdown},
  {"set-values",	XawSetValuesAction},
  {"get-values",	XawGetValuesAction},
  {"declare",		XawDeclareAction},
  {"call-proc",		XawCallProcAction},
#endif
};
 
static CompositeClassExtensionRec extension_rec = {
  NULL,					/* next_extension */
  NULLQUARK,				/* record_type */
  XtCompositeExtensionVersion,		/* version */
  sizeof(CompositeClassExtensionRec),	/* record_size */
  True,					/* accepts_objects */
};

#define Superclass	(&overrideShellClassRec)
SimpleMenuClassRec simpleMenuClassRec = {
  /* core */
  {
    (WidgetClass)Superclass,		/* superclass */
    "SimpleMenu",			/* class_name */
    sizeof(SimpleMenuRec),		/* size */
    XawSimpleMenuClassInitialize,	/* class_initialize */
    XawSimpleMenuClassPartInitialize,	/* class_part_initialize */
    False,				/* class_inited */
    XawSimpleMenuInitialize,		/* initialize */
    NULL,				/* initialize_hook */
    XawSimpleMenuRealize,		/* realize */
    actionsList,			/* actions */
    XtNumber(actionsList),		/* num_actions */
    resources,				/* resources */
    XtNumber(resources),		/* num_resources */
    NULLQUARK,				/* xrm_class */
    True,				/* compress_motion */
    True,				/* compress_exposure */
    True,				/* compress_enterleave */
    False,				/* visible_interest */
    NULL,				/* destroy */
    XawSimpleMenuResize,		/* resize */
    XawSimpleMenuRedisplay,		/* expose */
    XawSimpleMenuSetValues,		/* set_values */
    XawSimpleMenuSetValuesHook,		/* set_values_hook */
    XtInheritSetValuesAlmost,		/* set_values_almost */
    NULL,				/* get_values_hook */
    NULL,				/* accept_focus */
    XtVersion,				/* intrinsics version */
    NULL,				/* callback offsets */
    defaultTranslations,		/* tm_table */
    NULL,				/* query_geometry */
    NULL,				/* display_accelerator */
    NULL,				/* extension */
  },
  /* composite */
  {
    XawSimpleMenuGeometryManager,	/* geometry_manager */
    XawSimpleMenuChangeManaged,		/* change_managed */
    XtInheritInsertChild,		/* insert_child */
    XtInheritDeleteChild,		/* delete_child */
    NULL,				/* extension */
  },
  /* shell */
  {
    NULL,				/* extension */
  },
  /* override */
  {
    NULL,				/* extension */
  },
  /* simple_menu */
  {
    NULL,				/* extension */
  },
};

WidgetClass simpleMenuWidgetClass = (WidgetClass)&simpleMenuClassRec;

/*
 * Implementation
 */
/*
 * Function:
 *	XawSimpleMenuClassInitialize
 *
 * Description:
 *	Class Initialize routine, called only once.
 */
static void
XawSimpleMenuClassInitialize(void)
{
    XawInitializeWidgetSet();
    XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
		   NULL, 0);
    XtSetTypeConverter(XtRBackingStore, XtRString, XmuCvtBackingStoreToString,
		       NULL, 0, XtCacheNone, NULL);
    XmuAddInitializer(AddPositionAction, NULL);
}

/*
 * Function:
 *	XawSimpleMenuClassPartInitialize
 *      Arguments: wc - the widget class of the subclass.
 *
 * Description:
 *	  Class Part Initialize routine, called for every subclass.  Makes
 *	sure that the subclasses pick up the extension record.
 */
static void
XawSimpleMenuClassPartInitialize(WidgetClass wc)
{
    SimpleMenuWidgetClass smwc = (SimpleMenuWidgetClass)wc;

    /*
     * Make sure that our subclass gets the extension rec too
     */
    extension_rec.next_extension = smwc->composite_class.extension;
    smwc->composite_class.extension = (XtPointer) &extension_rec;
}

/*
 *  Function:
 *	XawSimpleMenuInitialize
 *
 * Parameters:
 *	request - widget requested by the argument list
 *	cnew	- new widget with both resource and non resource values
 *
 * Description:
 *	Initializes the simple menu widget.
 */
/*ARGSUSED*/
static void
XawSimpleMenuInitialize(Widget request, Widget cnew,
			ArgList args, Cardinal *num_args)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)cnew;
    Dimension width, height;

    XmuCallInitializers(XtWidgetToApplicationContext(cnew));

    if (smw->simple_menu.label_class == NULL) 
	smw->simple_menu.label_class = smeBSBObjectClass;

    smw->simple_menu.label = NULL;
    smw->simple_menu.entry_set = NULL;
    smw->simple_menu.recursive_set_values = False;
#ifndef OLDXAW
    smw->simple_menu.sub_menu = NULL;
    smw->simple_menu.state = 0;

    XtAddCallback(cnew, XtNpopupCallback, PopupCB, NULL);
#endif

    if (smw->simple_menu.label_string != NULL)
	CreateLabel(cnew);

    width = height = 0;
    CalculateNewSize(cnew, &width, &height);

    smw->simple_menu.menu_width = True;

    if (XtWidth(smw) == 0) {
	smw->simple_menu.menu_width = False;
	XtWidth(smw) = width;
    }

    smw->simple_menu.menu_height = True;

    if (XtHeight(smw) == 0) {
	smw->simple_menu.menu_height = False;
	XtHeight(smw) = height;
    }

    /*
     * Add a popup_callback routine for changing the cursor
     */
    XtAddCallback(cnew, XtNpopupCallback, ChangeCursorOnGrab, NULL);
}

/*
 * Function:
 *	XawSimpleMenuRedisplay
 *
 * Parameters:
 *	w      - simple menu widget
 *	event  - X event that caused this redisplay
 *	region - region the needs to be repainted
 *
 * Description:
 *	Redisplays the contents of the widget.
 */
/*ARGSUSED*/
static void
XawSimpleMenuRedisplay(Widget w, XEvent *event, Region region)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
    SmeObject *entry;
    SmeObjectClass cclass;

    if (region == NULL)
	XClearWindow(XtDisplay(w), XtWindow(w));

#ifndef OLDXAW
    if (smw->simple_menu.display_list)
      XawRunDisplayList(w, smw->simple_menu.display_list, event, region);
#endif

    /*
     * Check and Paint each of the entries - including the label
     */
    ForAllChildren(smw, entry) {
	if (!XtIsManaged((Widget)*entry))
	    continue;

	if (region != NULL) 
	    switch(XRectInRegion(region, XtX(*entry),XtY(*entry),
				 XtWidth(*entry), XtHeight(*entry))) {
		case RectangleIn:
		case RectanglePart:
		    break;
		default:
		    continue;
	    }

	cclass = (SmeObjectClass)(*entry)->object.widget_class;

	if (cclass->rect_class.expose != NULL)
	    (cclass->rect_class.expose)((Widget)*entry, NULL, NULL);
    }
}

/*
 * Function:
 *	XawSimpleMenuRealize
 *
 * Parameters:
 *	w     - simple menu widget
 *	mask  - value mask for the window to create
 *	attrs - attributes for the window to create
 *
 * Description:
 *	Realizes the widget.
 */
static void
XawSimpleMenuRealize(Widget w, XtValueMask *mask, XSetWindowAttributes *attrs)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
#ifndef OLDXAW
    XawPixmap *pixmap;
#endif

    attrs->cursor = smw->simple_menu.cursor;
    *mask |= CWCursor;
    if (smw->simple_menu.backing_store == Always ||
	smw->simple_menu.backing_store == NotUseful ||
	smw->simple_menu.backing_store == WhenMapped) {
	*mask |= CWBackingStore;
	attrs->backing_store = smw->simple_menu.backing_store;
    }
    else
	*mask &= ~CWBackingStore;

    (*Superclass->core_class.realize)(w, mask, attrs);

#ifndef OLDXAW
    if (w->core.background_pixmap > XtUnspecifiedPixmap) {
	pixmap = XawPixmapFromXPixmap(w->core.background_pixmap, XtScreen(w),
				      w->core.colormap, w->core.depth);
	if (pixmap && pixmap->mask)
	    XawReshapeWidget(w, pixmap);
    }
#endif
}

/*
 * Function:
 *	XawSimpleMenuResize
 *
 * Parameters:
 *	w - simple menu widget
 *
 * Description:
 *	Handle the menu being resized.
 */
static void
XawSimpleMenuResize(Widget w)
{
    if (!XtIsRealized(w))
	return;

    Layout(w, NULL, NULL);

    XawSimpleMenuRedisplay(w, NULL, NULL);
}

/*
 * Function:
 *	XawSimpleMenuSetValues
 *
 * Parameters:
 *	current - current state of the widget
 *	request - what was requested
 *	cnew    - what the widget will become
 *
 * Description:
 *	Relayout the menu when one of the resources is changed.
 */
/*ARGSUSED*/
static Boolean
XawSimpleMenuSetValues(Widget current, Widget request, Widget cnew,
		       ArgList args, Cardinal *num_args)
{
    SimpleMenuWidget smw_old = (SimpleMenuWidget)current;
    SimpleMenuWidget smw_new = (SimpleMenuWidget)cnew;
    Boolean ret_val = False, layout = False;

    if (!XtIsRealized(current))
	return (False);

    if (!smw_new->simple_menu.recursive_set_values) {
	if (XtWidth(smw_new) != XtWidth(smw_old)) {
	    smw_new->simple_menu.menu_width = XtWidth(smw_new) != 0;
	    layout = True;
	}
	if (XtHeight(smw_new) != XtHeight(smw_old)) {
	    smw_new->simple_menu.menu_height = XtHeight(smw_new) != 0;
	    layout = True;
	}
    }

    if (smw_old->simple_menu.cursor != smw_new->simple_menu.cursor)
	XDefineCursor(XtDisplay(cnew), XtWindow(cnew),
		      smw_new->simple_menu.cursor);

    if (smw_old->simple_menu.label_string !=smw_new->simple_menu.label_string) {
	if (smw_new->simple_menu.label_string == NULL)	    /* Destroy */
	    XtDestroyWidget((Widget)smw_old->simple_menu.label);
	else if (smw_old->simple_menu.label_string == NULL) /* Create */
	    CreateLabel(cnew);
	else {						    /* Change */
	    Arg arglist[1];
	    
	    XtSetArg(arglist[0], XtNlabel, smw_new->simple_menu.label_string);
	    XtSetValues((Widget)smw_new->simple_menu.label, arglist, ONE);
	}
    }

    if (smw_old->simple_menu.label_class != smw_new->simple_menu.label_class)
	XtAppWarning(XtWidgetToApplicationContext(cnew),
		     "No Dynamic class change of the SimpleMenu Label.");
    
    if (smw_old->simple_menu.top_margin != smw_new->simple_menu.top_margin
	|| smw_old->simple_menu.bottom_margin
	!= smw_new->simple_menu.bottom_margin) {
	layout = True;
	ret_val = True;
    }

#ifndef OLDXAW
    if (smw_old->core.background_pixmap != smw_new->core.background_pixmap) {
	XawPixmap *opix, *npix;

	opix = XawPixmapFromXPixmap(smw_old->core.background_pixmap,
				    XtScreen(smw_old), smw_old->core.colormap,
				    smw_old->core.depth);
	npix = XawPixmapFromXPixmap(smw_new->core.background_pixmap,
				    XtScreen(smw_new), smw_new->core.colormap,
				    smw_new->core.depth);
	if ((npix && npix->mask) || (opix && opix->mask))
	    XawReshapeWidget(cnew, npix);
    }
#endif

    if (layout)
	Layout(cnew, NULL, NULL);

    return (ret_val);
}

/* 
 * Function:
 *	XawSimpleMenuSetValuesHook
 *
 * Parameters:
 *	w	 - menu widget
 *	arglist	 - argument list passed to XtSetValues
 *	num_args - number of args
 *
 * Description:
 *	To handle a special case, this is passed the actual arguments.
 */
static Boolean
XawSimpleMenuSetValuesHook(Widget w, ArgList arglist, Cardinal *num_args)
{
    Cardinal i;
    Dimension width, height;
    
    width = XtWidth(w);
    height = XtHeight(w);
    
    for (i = 0 ; i < *num_args ; i++) {
	if (streq(arglist[i].name, XtNwidth))
	    width = (Dimension)arglist[i].value;
	if (streq(arglist[i].name, XtNheight))
	    height = (Dimension) arglist[i].value;
    }

    if (width != XtWidth(w) || height != XtHeight(w))
	MakeSetValuesRequest(w, width, height);

    return (False);
}

/*
 * Geometry Management routines
 */
/*
 * Function:
 *	XawSimpleMenuGeometryManager
 *
 * Parameters:
 *	w	- Menu Entry making the request
 *	request - requested new geometry
 *                 reply - the allowed geometry.
 *
 * Description:
 *	This is the SimpleMenu Widget's Geometry Manager.
 *
 * Returns:
 *	XtGeometry{Yes, No, Almost}
 */
static XtGeometryResult
XawSimpleMenuGeometryManager(Widget w, XtWidgetGeometry *request,
			     XtWidgetGeometry *reply)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)XtParent(w);
    SmeObject entry = (SmeObject)w;
    XtGeometryMask mode = request->request_mode;
    XtGeometryResult answer;
    Dimension old_height, old_width;

    if (!(mode & CWWidth) && !(mode & CWHeight))
	return (XtGeometryNo);

    reply->width = request->width;
    reply->height = request->height;

    old_width = XtWidth(entry);
    old_height = XtHeight(entry);

    Layout(w, &reply->width, &reply->height);

    /*
     * Since we are an override shell and have no parent there is no one to
     * ask to see if this geom change is okay, so I am just going to assume
     * we can do whatever we want.  If you subclass be very careful with this
     * assumption, it could bite you.
     *
     * Chris D. Peterson - Sept. 1989.
     */
    if ((!(mode & CWWidth) || reply->width == request->width)
	&& (!(mode & CWHeight) || reply->height == request->height)) {
	if (mode & XtCWQueryOnly) {	/* Actually perform the layout */
	    XtWidth(entry) = old_width;
	    XtHeight(entry) = old_height;
	}
	else
	    Layout((Widget)smw, NULL, NULL);
	answer = XtGeometryDone;
    }
    else {
	XtWidth(entry) = old_width;
	XtHeight(entry) = old_height;

	if ((reply->width == request->width && !(mode & CWHeight))
	    || (reply->height == request->height && !(mode & CWWidth))
	    || (reply->width == request->width
	    && reply->height == request->height))
	    answer = XtGeometryNo;
	else {
	    answer = XtGeometryAlmost;
	    reply->request_mode = 0;
	    if (reply->width != request->width)
		reply->request_mode |= CWWidth;
	    if (reply->height != request->height)
		reply->request_mode |= CWHeight;
	}
    }

    return (answer);
}

/*
 * Function:
 *	XawSimpleMenuChangeManaged
 *
 * Parameters:
 *	w - simple menu widget
 *
 * Description:
 *	Called whenever a new child is managed.
 */
static void
XawSimpleMenuChangeManaged(Widget w)
{
    Layout(w, NULL, NULL);
}

/*
 * Global Action Routines
 * 
 * These actions routines will be added to the application's
 * global action list
 */
/*
 * Function:
 *	PositionMenuAction
 * 
 * Parameters:
 *	w	   - a widget (no the simple menu widget)
 *	event	   - the event that caused this action
 *	params	   - parameters passed to the routine.
 *                                      we expect the name of the menu here.
 *	num_params - ""
 *
 * Description:
 *	Positions the simple menu widget.
 */
/*ARGSUSED*/
static void
PositionMenuAction(Widget w, XEvent *event,
		   String *params, Cardinal *num_params)
{ 
    Widget menu;
    XPoint loc;

    if (*num_params != 1) {
	XtAppWarning(XtWidgetToApplicationContext(w),
		     "SimpleMenuWidget: position menu action expects "
		     "only one parameter which is the name of the menu.");
	return;
    }

    if ((menu = FindMenu(w, params[0])) == NULL) {
	char error_buf[BUFSIZ];

	(void)XmuSnprintf(error_buf, sizeof(error_buf),
			  "SimpleMenuWidget: could not find menu named %s.",
			  params[0]);
	XtAppWarning(XtWidgetToApplicationContext(w), error_buf);
	return;
    }
  
    switch (event->type) {
	case ButtonPress:
	case ButtonRelease:
	    loc.x = event->xbutton.x_root;
	    loc.y = event->xbutton.y_root;
	    PositionMenu(menu, &loc);
	    break;
	case EnterNotify:
	case LeaveNotify:
	    loc.x = event->xcrossing.x_root;
	    loc.y = event->xcrossing.y_root;
	    PositionMenu(menu, &loc);
	    break;
	case MotionNotify:
	    loc.x = event->xmotion.x_root;
	    loc.y = event->xmotion.y_root;
	    PositionMenu(menu, &loc);
	    break;
	default:
	    PositionMenu(menu, NULL);
	    break;
    }
}  

/*
 * Widget Action Routines
 */
/*
 * Function:
 *	Unhighlight
 *
 * Parameters:
 *	w	   - simple menu widget
 *	event	   - event that caused this action
 *	params	   - not used
 *	num_params - ""
 * 
 * Description:
 *	Unhighlights current entry.
 */
/*ARGSUSED*/
static void
Unhighlight(Widget w, XEvent *event, String *params, Cardinal *num_params)
{ 
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
    SmeObject entry = smw->simple_menu.entry_set;
 
    if (entry == NULL)
	return;

#ifndef OLDXAW
    if (!smw->simple_menu.sub_menu)
#endif
    {
	SmeObjectClass cclass;

	smw->simple_menu.entry_set = NULL;
	cclass = (SmeObjectClass)entry->object.widget_class;
	(cclass->sme_class.unhighlight)((Widget)entry);
    }
}

/*
 * Function:
 *	Highlight
 *
 * Parameters:
 *	w	   - simple menu widget
 *	event	   - event that caused this action
 *	params	   - not used
 *	num_params - ""
 *
 * Description:
 *	Highlights current entry.
 */
/*ARGSUSED*/
static void
Highlight(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
    SmeObject entry;

    if (!XtIsSensitive(w))
	return;

    entry = GetEventEntry(w, event);

    if (entry == smw->simple_menu.entry_set)
	return;

#ifndef OLDXAW
    if (!smw->simple_menu.sub_menu)
#endif
	Unhighlight(w, event, params, num_params);

    if (entry == NULL)
	return;

    if (!XtIsSensitive((Widget)entry))
	return;

#ifndef OLDXAW
    if (smw->simple_menu.sub_menu)
	PopdownSubMenu(smw);
#endif

    Unhighlight(w, event, params, num_params);

#ifndef OLDXAW
    if (!(smw->simple_menu.state & SMW_UNMAPPING))
#endif
    {
	SmeObjectClass cclass;

	smw->simple_menu.entry_set = entry;
	cclass = (SmeObjectClass)entry->object.widget_class;

	(cclass->sme_class.highlight)((Widget)entry);

#ifndef OLDXAW
	if (XtIsSubclass((Widget)entry, smeBSBObjectClass))
	    PopupSubMenu(smw);
#endif
    }
}

/*
 * Function:
 *	Notify
 *
 * Parameters:
 *	w	   - simple menu widget
 *	event	   - event that caused this action
 *	params	   - not used
 *	num_params - ""
 *
 * Description:
 *	Notify user of current entry.
 */
/*ARGSUSED*/
static void
Notify(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    SmeObject entry;
    SmeObjectClass cclass;

    /* may be a propagated event from a sub menu, need to check it */
    if (XtWindow(w) != event->xany.window)
	return;
    entry = GetEventEntry(w, event);
    if (entry == NULL || !XtIsSensitive((Widget)entry))
	return;

    cclass = (SmeObjectClass) entry->object.widget_class;
    (cclass->sme_class.notify)((Widget)entry);
}

/*
 * Public Functions
 */
/*
 * Function:
 *	XawSimpleMenuAddGlobalActions
 *
 * Arguments:
 *	app_con - appcontext
 *
 * Description:
 *	Adds the global actions to the simple menu widget.
 */
void
XawSimpleMenuAddGlobalActions(XtAppContext app_con)
{
    XtInitializeWidgetClass(simpleMenuWidgetClass);
    XmuCallInitializers(app_con);
} 

/*
 * Function:
 *	XawSimpleMenuGetActiveEntry
 *
 * Parameters:
 *	w - smw widget
 *
 * Description:
 *	Gets the currently active (set) entry.
 *
 * Returns:
 *	The currently set entry or NULL if none is set
 */
Widget
XawSimpleMenuGetActiveEntry(Widget w)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;

    return ((Widget)smw->simple_menu.entry_set);
} 

/*
 * Function:
 *	XawSimpleMenuClearActiveEntry
 *
 * Parameters:
 *	w - smw widget
 *
 * Description:
 *	Unsets the currently active (set) entry.
 */
void
XawSimpleMenuClearActiveEntry(Widget w)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;

    smw->simple_menu.entry_set = NULL;
} 

/*
 * Private Functions
 */
/*
 * Function:
 *	CreateLabel
 *
 * Parameters:
 *	w - smw widget
 * 
 * Description:
 * Creates the label object and makes sure it is the first child in
 * in the list.
 */
static void
CreateLabel(Widget w)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
    Widget *child, *next_child;
    int i;
    Arg args[2];

    if (smw->simple_menu.label_string == NULL ||
	smw->simple_menu.label != NULL) {
	XtAppWarning(XtWidgetToApplicationContext(w),
		     "Xaw Simple Menu Widget: label string is NULL or "
		     "label already exists, no label is being created.");
	return;
    }

    XtSetArg(args[0], XtNlabel, smw->simple_menu.label_string);
    XtSetArg(args[1], XtNjustify, XtJustifyCenter);
    smw->simple_menu.label = (SmeObject) 
	XtCreateManagedWidget("menuLabel", 
			      smw->simple_menu.label_class, w, args, TWO);

    next_child = NULL;
    for (child = smw->composite.children + smw->composite.num_children,
	 i = smw->composite.num_children; i > 0; i--, child--) {
	if (next_child != NULL)
	    *next_child = *child;
	next_child = child;
    }
    *child = (Widget)smw->simple_menu.label;
}

/*
 * Function:
 *	Layout
 *
 * Arguments:
 *	w	   - See below
 *	width_ret  - returned width
 *	height_ret - returned height
 *
 * Note:
 * if width == NULL || height == NULL then it assumes the you do not care
 * about the return values, and just want a relayout.
 *
 * if this is not the case then it will set width_ret and height_ret
 * to be width and height that the child would get if it were layed out
 * at this time.
 *
 *	"w" can be the simple menu widget or any of its object children.
 */
static void
Layout(Widget w, Dimension *width_ret, Dimension *height_ret)
{
    SmeObject current_entry;
    SimpleMenuWidget smw;
    Dimension width, height;
    Boolean allow_change_size;
    Widget kid;
    Cardinal i, count, n;
    int width_kid, height_kid, tmp_w, tmp_h;
    short vadd, hadd, x_ins, y_ins;
    Dimension *widths;

    height = 0;

    if (XtIsSubclass(w, simpleMenuWidgetClass)) {
	smw = (SimpleMenuWidget)w;
	current_entry = NULL;
    }
    else {
	smw = (SimpleMenuWidget)XtParent(w);
	current_entry = (SmeObject)w;
    }

    allow_change_size = (!XtIsRealized((Widget)smw)
			 || smw->shell.allow_shell_resize);

    for (i = smw->simple_menu.label ? 1 : 0;
	 i < smw->composite.num_children;
	 i++) {
	XtWidgetGeometry preferred;

	kid = smw->composite.children[i];
	if (!XtIsManaged(kid))
	    continue;
	if (smw->simple_menu.row_height != 0)
	    XtHeight(kid) = smw->simple_menu.row_height;
	XtQueryGeometry(kid, NULL, &preferred);
	if (preferred.request_mode & CWWidth)
	    XtWidth(kid) = preferred.width;
    }

    if (smw->simple_menu.label
	&& XtIsManaged((Widget)smw->simple_menu.label)) {
	XtWidgetGeometry preferred;

	kid = (Widget)smw->simple_menu.label;
	XtQueryGeometry(kid, NULL, &preferred);
	if (preferred.request_mode & CWWidth)
	    XtWidth(kid) = preferred.width;
	if (preferred.request_mode & CWHeight)
	    XtHeight(kid) = preferred.height;
    }

    /* reset */
    if (!smw->simple_menu.menu_width)
	XtWidth(smw) = 0;
    if (!smw->simple_menu.menu_height)
	XtHeight(smw) = 0;
    if (!XtWidth(smw) || !XtHeight(smw))
	MakeResizeRequest((Widget)smw);

    widths = (Dimension *)XtMalloc(sizeof(Dimension));
#ifndef OLDXAW
    hadd = smw->simple_menu.left_margin;
#else
    hadd = 0;
#endif
    vadd = smw->simple_menu.top_margin;
    if (smw->simple_menu.label)
	vadd += XtHeight(smw->simple_menu.label);

    count = 1;
    width = tmp_w = tmp_h = n = 0;
    height = vadd;

    for (i = smw->simple_menu.label ? 1 : 0;
	 i < smw->composite.num_children;
	 i++) {
	kid = smw->composite.children[i];
	if (!XtIsManaged(kid))
	    continue;
	width_kid = XtWidth(kid);
	height_kid = XtHeight(kid);

	if (n && (height + height_kid + smw->simple_menu.bottom_margin
		  > XtHeight(smw))) {
	    ++count;
	    widths = (Dimension *)XtRealloc((char *)widths,
					    sizeof(Dimension) * count);
	    widths[count - 1] = width_kid;
	    width += tmp_w;
	    tmp_w = width_kid;
	    height = height_kid + vadd;
	}
	else
	    height += height_kid;
	if (height > tmp_h)
	    tmp_h = height;
	if (width_kid > tmp_w)
	    widths[count - 1] = tmp_w = width_kid;
	++n;
    }

    height = tmp_h + smw->simple_menu.bottom_margin;
    width += tmp_w;

    if (smw->simple_menu.label && width < XtWidth(smw->simple_menu.label)) {
	float inc;

	inc = (XtWidth(smw->simple_menu.label) - width) / (float)count;
	width = XtWidth(smw->simple_menu.label);
	for (n = 0; n < count; n++)
	    widths[n] += inc;
    }

#ifndef OLDXAW
    width += hadd + smw->simple_menu.right_margin;
#endif

    x_ins = n = count = 0;
    tmp_w = widths[0];
    tmp_h = vadd;

    for (i = smw->simple_menu.label ? 1 : 0;
	 i < smw->composite.num_children;
	 i++) {
	kid = smw->composite.children[i];
	if (!XtIsManaged(kid))
	    continue;

	height_kid = XtHeight(kid);

	if (n && (tmp_h + height_kid + smw->simple_menu.bottom_margin
		  > XtHeight(smw))) {
	    x_ins = tmp_w;
	    y_ins = vadd;
	    ++count;
	    tmp_w += widths[count];
	    tmp_h = height_kid + vadd;
	}
	else {
	    y_ins = tmp_h;
	    tmp_h += height_kid;
	}
	++n;

	XtX(kid) = x_ins + hadd;
	XtY(kid) = y_ins;
	XtWidth(kid) = widths[count];
    }

    XtFree((char *)widths);

    if (allow_change_size)
	MakeSetValuesRequest((Widget) smw, width, height);

    if (smw->simple_menu.label) {
	XtX(smw->simple_menu.label) = 0;
	XtY(smw->simple_menu.label) = smw->simple_menu.top_margin;
	XtWidth(smw->simple_menu.label) = XtWidth(smw)
#ifndef OLDXAW
	    - (smw->simple_menu.left_margin + smw->simple_menu.right_margin)
#endif
	    ;
    }
    if (current_entry) {
	if (width_ret)
	    *width_ret = XtWidth(current_entry);
	if (height_ret)
	    *height_ret = XtHeight(current_entry);
    }
}
    
/*
 * Function:
 *	AddPositionAction
 *
 * Parameters:
 *	app_con - application context
 *	data	- (not used)
 *
 * Description:
 *	  Adds the XawPositionSimpleMenu action to the global
 *                   action list for this appcon.
 */
/*ARGSUSED*/
static void
AddPositionAction(XtAppContext app_con, XPointer data)
{
    static XtActionsRec pos_action[] = {
	{"XawPositionSimpleMenu",	PositionMenuAction},
    };

    XtAppAddActions(app_con, pos_action, XtNumber(pos_action));
}

/*
 * Function:
 *	FindMenu
 *
 * Parameters:
 *	widget - reference widget
 *	name   - menu widget's name
 *
 * Description:
 *	Find the menu give a name and reference widget
 *
 * Returns:
 *	The menu widget or NULL.
 */
static Widget 
FindMenu(Widget widget, String name)
{
    Widget w, menu;
    
    for (w = widget; w != NULL; w = XtParent(w))
	if ((menu = XtNameToWidget(w, name)) != NULL)
	    return (menu);

    return (NULL);
}

/*
 * Function:
 *	PositionMenu
 *
 * Parameters:
 *	w	 - simple menu widget
 *	location - pointer the the position or NULL
 *
 * Description:
 *	Places the menu
 */
static void
PositionMenu(Widget w, XPoint *location)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
    SmeObject entry;
    XPoint t_point;
    
    if (location == NULL) {
	Window temp1, temp2;
	int root_x, root_y, tempX, tempY;
	unsigned int tempM;
	
	location = &t_point;
	if (XQueryPointer(XtDisplay(w), XtWindow(w), &temp1, &temp2,
			  &root_x, &root_y, &tempX, &tempY, &tempM) == False) {
	    XtAppWarning(XtWidgetToApplicationContext(w),
			 "Xaw Simple Menu Widget: "
			 "Could not find location of mouse pointer");
	    return;
	}
	location->x = (short) root_x;
	location->y = (short) root_y;
    }
    
    /*
     * The width will not be correct unless it is realized
     */
    XtRealizeWidget(w);
    
    location->x -= XtWidth(w) >> 1;
    
    if (smw->simple_menu.popup_entry == NULL)
	entry = smw->simple_menu.label;
    else
	entry = smw->simple_menu.popup_entry;

    if (entry != NULL)
      location->y -= XtY(entry) + (XtHeight(entry) >> 1);

    MoveMenu(w, location->x, location->y);
}

/*
 * Function:
 *	MoveMenu
 *
 * Parameters:
 *	w - simple menu widget
 *	x - current location of the widget
 *	y - ""
 *
 * Description:
 *	  Actually moves the menu, may force it to
 *	to be fully visable if menu_on_screen is True.
 */
static void
MoveMenu(Widget w, int x, int y)
{
    Arg arglist[2];
    Cardinal num_args = 0;
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
    
    if (smw->simple_menu.menu_on_screen) {
	int width = XtWidth(w) + (XtBorderWidth(w) << 1);
	int height = XtHeight(w) + (XtBorderWidth(w) << 1);
	
	if (x >= 0) {
	    int scr_width = WidthOfScreen(XtScreen(w));

	    if (x + width > scr_width)
		x = scr_width - width;
	}
	if (x < 0) 
	    x = 0;
	
	if (y >= 0) {
	    int scr_height = HeightOfScreen(XtScreen(w));

	    if (y + height > scr_height)
		y = scr_height - height;
	}
	if (y < 0)
	    y = 0;
    }
    
    XtSetArg(arglist[num_args], XtNx, x); num_args++;
    XtSetArg(arglist[num_args], XtNy, y); num_args++;
    XtSetValues(w, arglist, num_args);
}

/*
 * Function:
 *	ChangeCursorOnGrab
 *
 * Parameters:
 *	w     - menu widget
 *	temp1 - not used
 *	temp2 - ""
 *
 * Description:
 *	  Changes the cursor on the active grab to the one
 *                   specified in out resource list.
 */
/*ARGSUSED*/
static void
ChangeCursorOnGrab(Widget w, XtPointer temp1, XtPointer temp2)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
    
    /*
     * The event mask here is what is currently in the MIT implementation.
     * There really needs to be a way to get the value of the mask out
     * of the toolkit (CDP 5/26/89).
     */
    XChangeActivePointerGrab(XtDisplay(w), ButtonPressMask | ButtonReleaseMask,
			     smw->simple_menu.cursor, 
			     XtLastTimestampProcessed(XtDisplay(w)));
}

/*
 * Function:
 *	MakeSetValuesRequest
 *
 * Parameters:
 *	w      - simple menu widget
 *	width  - size requested
 *	height - ""
 */
static void
MakeSetValuesRequest(Widget w, unsigned int width, unsigned int height)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
    Arg arglist[2];
    Cardinal num_args = 0;
    
    if (!smw->simple_menu.recursive_set_values) {
	if (XtWidth(smw) != width || XtHeight(smw) != height) {
	    smw->simple_menu.recursive_set_values = True;
	    XtSetArg(arglist[num_args], XtNwidth, width);   num_args++;
	    XtSetArg(arglist[num_args], XtNheight, height); num_args++;
	    XtSetValues(w, arglist, num_args);
	}
	else if (XtIsRealized((Widget)smw))
	    XawSimpleMenuRedisplay((Widget)smw, NULL, NULL);
    }
    smw->simple_menu.recursive_set_values = False;
}

static SmeObject
DoGetEventEntry(Widget w, int x_loc, int y_loc)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
    SmeObject *entry;

    ForAllChildren(smw, entry) {
	if (!XtIsManaged((Widget)*entry))
	    continue;

	if (x_loc > XtX(*entry)
	    && x_loc <= XtX(*entry) + XtWidth(*entry)
	    && y_loc > XtY(*entry)
	    &&  y_loc <= XtY(*entry) + XtHeight(*entry)) {
	    if (*entry == smw->simple_menu.label)
		return (NULL);	/* cannot select the label */
	    else
		return (*entry);
	}
    }
    
    return (NULL);
}

/*
 * Function:
 *	GetEventEntry
 *
 * Parameters:
 *	w     - simple menu widget
 *	event - X event
 *
 * Description:
 *	Gets an entry given an event that has X and Y coords.
 *
 * Returns:
 *	The entry that this point is in
 */
static SmeObject
GetEventEntry(Widget w, XEvent *event)
{
    int x_loc, y_loc, x_root;
    SimpleMenuWidget smw = (SimpleMenuWidget)w;
    SmeObject entry;
    int warp, move;

    switch (event->type) {
	case MotionNotify:
	    x_loc = event->xmotion.x;
	    y_loc = event->xmotion.y;
	    x_root = event->xmotion.x_root;
	    break;
	case EnterNotify:
	case LeaveNotify:
	    x_loc = event->xcrossing.x;
	    y_loc = event->xcrossing.y;
	    x_root = event->xcrossing.x_root;
	    break;
	case ButtonPress:
	case ButtonRelease:
	    x_loc = event->xbutton.x;
	    y_loc = event->xbutton.y;
	    x_root = event->xbutton.x_root;
	    break;
	default:
	    XtAppError(XtWidgetToApplicationContext(w),
		       "Unknown event type in GetEventEntry().");
	    return (NULL);
    }
    
    if (x_loc < 0 || x_loc >= XtWidth(smw) ||
	y_loc < 0 || y_loc >= XtHeight(smw))
	return (NULL);

    /* Move the menu if it's outside the screen, does not check
     * smw->simple_menu.menu_on_screen because menus is bigger than screen
     */
    if (x_root == WidthOfScreen(XtScreen(w)) - 1 &&
	XtX(w) + XtWidth(w) + (XtBorderWidth(w)) > x_root) {
	warp = -8;
	if (smw->simple_menu.entry_set) {
	    entry = DoGetEventEntry(w,
				    XtX(smw->simple_menu.entry_set)
				    + XtWidth(smw->simple_menu.entry_set) + 1,
				    y_loc);
	    Unhighlight(w, event, NULL, NULL);
	    if (entry) {
		warp = -(int)XtWidth(entry) >> 1;
		move = x_loc - XtWidth(entry) - XtX(entry) + XtBorderWidth(w);
	    }
	    else {
		warp = 0;
		move = WidthOfScreen(XtScreen(w)) -
		       (XtX(w) + XtWidth(w) + (XtBorderWidth(w) << 1));
	    }
	}
	else {
	    warp = 0;
	    move = WidthOfScreen(XtScreen(w)) -
		   (XtX(w) + XtWidth(w) + (XtBorderWidth(w) << 1));
	}
    }
    else if (x_root == 0 && XtX(w) < 0) {
	warp = 8;
	if (smw->simple_menu.entry_set) {
	    entry = DoGetEventEntry(w, XtX(smw->simple_menu.entry_set) - 1,
				    y_loc);
	    Unhighlight(w, event, NULL, NULL);
	    if (entry) {
		warp = XtWidth(entry) >> 1;
		move = x_loc - XtX(entry);
	    }
	    else
		move = x_loc + XtBorderWidth(w);
	}
	else
	    move = x_loc + XtBorderWidth(w);
    }
    else
	move = warp = 0;

    if (move)
	XtMoveWidget(w, XtX(w) + move, XtY(w));
    if (warp)
	XWarpPointer(XtDisplay(w), None, None, 0, 0, 0, 0, warp, 0);

    return (DoGetEventEntry(w, x_loc, y_loc));
}

static void
CalculateNewSize(Widget w, Dimension *width_return, Dimension *height_return)
{
    SimpleMenuWidget xaw = (SimpleMenuWidget)w;
    Widget kid;
    Cardinal i;
    int width_kid, height_kid;
    int width, height, tmp_w, tmp_h, max_dim;
    short vadd, hadd;
    int n, columns, test_h, num_children = 0;
    Boolean try_layout = False;

#ifndef OLDXAW
    hadd = xaw->simple_menu.left_margin + xaw->simple_menu.right_margin;
#else
    hadd = 0;
#endif
    vadd = xaw->simple_menu.top_margin + xaw->simple_menu.bottom_margin;
    if (xaw->simple_menu.label)
	vadd += XtHeight(xaw->simple_menu.label);

    if (*height_return)
	max_dim = *height_return;
    else if (!XtHeight(w)) {
	max_dim = HeightOfScreen(XtScreen(w));
	try_layout = True;
    }
    else
	max_dim = XtHeight(w);
    max_dim -= vadd;

    width = height = tmp_w = tmp_h = n = test_h = 0;
    columns = 1;
    for (i = xaw->simple_menu.label ? 1 : 0;
	 i < xaw->composite.num_children;
	 i++) {
	kid = xaw->composite.children[i];
	if (!XtIsManaged(kid))
	    continue;
	++num_children;
	width_kid = XtWidth(kid);
	height_kid = XtHeight(kid);

	if (try_layout) {
	    if (!test_h)
		test_h = height_kid;
	    else if (test_h != height_kid)
		try_layout = False;
	}

	if (n && (height + height_kid > max_dim)) {
	    ++columns;
	    width += tmp_w;
	    tmp_w = width_kid;
	    height = height_kid;
	}
	else
	    height += height_kid;
	if (height > tmp_h)
	    tmp_h = height;
	if (width_kid > tmp_w)
	    tmp_w = width_kid;
	++n;
    }

    height = tmp_h + vadd;
    width += tmp_w + hadd;

    if (xaw->simple_menu.label)
	width = XawMax(width, XtWidth(xaw->simple_menu.label) + hadd);

    *width_return = width;
    *height_return = height;

    if (try_layout && columns > 1 && num_children > 2) {
	int space;

	height = test_h * (xaw->simple_menu.label ?
			   num_children - 1 :
			   num_children);

	max_dim -= max_dim % test_h;
	space = max_dim - (height % max_dim);
	if (space >= test_h * columns) {
	    height = max_dim - space / columns;
	    if (height % test_h)
		height += test_h - (height % test_h);
	    *height_return = height + vadd;
	    CalculateNewSize(w, width_return, height_return);
	}
    }
}

static void
MakeResizeRequest(Widget w)
{
    int tries;
    Dimension width, height;
  
    width = XtWidth(w);
    height = XtHeight(w);

    for (tries = 0; tries < 100; tries++) {
	CalculateNewSize(w, &width, &height);
	if (width == XtWidth(w) && height == XtHeight(w))
	    break;
	if (XtMakeResizeRequest(w, width, height, &width, &height) ==
	    XtGeometryNo)
	break;
    }
}

#ifndef OLDXAW
static void
Popdown(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;

    while (XtParent(w) &&
	   XtIsSubclass(XtParent(w), simpleMenuWidgetClass)) {
	if (((SimpleMenuWidget)XtParent(w))->simple_menu.sub_menu == (Widget)w) {
	    w = XtParent(w);
	    smw = (SimpleMenuWidget)w;
	    smw->simple_menu.entry_set = NULL;
	}
	else
	    break;
    }

    smw->simple_menu.state |= SMW_UNMAPPING;
    if (smw->simple_menu.sub_menu)
	PopdownSubMenu(smw);
    XtCallActionProc(w, "XtMenuPopdown", event, params, *num_params);
}

static void
PopupSubMenu(SimpleMenuWidget smw)
{
    Arg args[2];
    Cardinal num_args;
    Widget menu;
    SmeBSBObject entry = (SmeBSBObject)smw->simple_menu.entry_set;
    Position menu_x, menu_y;
    Bool popleft;

    if (entry->sme_bsb.menu_name == NULL)
	return;

    if ((menu = FindMenu((Widget)smw, entry->sme_bsb.menu_name)) == NULL)
	return;

    smw->simple_menu.sub_menu = menu;

    if (!XtIsRealized(menu))
	XtRealizeWidget(menu);

    popleft = (smw->simple_menu.state & SMW_POPLEFT) != 0;

    if (popleft) 
	XtTranslateCoords((Widget)smw, -(int)XtWidth(menu),
			  XtY(entry) - XtBorderWidth(menu), &menu_x, &menu_y);
    else
	XtTranslateCoords((Widget)smw, XtWidth(smw), XtY(entry)
			  - XtBorderWidth(menu), &menu_x, &menu_y);

    if (!popleft && menu_x >= 0) {
	int scr_width = WidthOfScreen(XtScreen(menu));

	if (menu_x + XtWidth(menu) > scr_width) {
	    menu_x -= XtWidth(menu) + XtWidth(smw);
	    popleft = True;
	}
    }
    else if (popleft && menu_x < 0) {
	menu_x = 0;
	popleft = False;
    }
    if (menu_y >= 0) {
	int scr_height = HeightOfScreen(XtScreen(menu));

	if (menu_y + XtHeight(menu) > scr_height)
	    menu_y = scr_height - XtHeight(menu) - XtBorderWidth(menu);
    }
    if (menu_y < 0)
	menu_y = 0;

    num_args = 0;
    XtSetArg(args[num_args], XtNx, menu_x);	num_args++;
    XtSetArg(args[num_args], XtNy, menu_y);	num_args++;
    XtSetValues(menu, args, num_args);

    if (popleft)
	((SimpleMenuWidget)menu)->simple_menu.state |= SMW_POPLEFT;
    else
	((SimpleMenuWidget)menu)->simple_menu.state &= ~SMW_POPLEFT;

    XtPopup(menu, XtGrabNone);
}

static void
PopdownSubMenu(SimpleMenuWidget smw)
{
    SimpleMenuWidget menu = (SimpleMenuWidget)smw->simple_menu.sub_menu;

    if (!menu)
	return;

    menu->simple_menu.state |= SMW_UNMAPPING;
    PopdownSubMenu(menu);

    XtPopdown((Widget)menu);

    smw->simple_menu.sub_menu = NULL;
}

/*ARGSUSED*/
static void
PopupCB(Widget w, XtPointer client_data, XtPointer call_data)
{
    SimpleMenuWidget smw = (SimpleMenuWidget)w;

    smw->simple_menu.state &= ~(SMW_UNMAPPING | SMW_POPLEFT);
}
#endif /* OLDXAW */
