#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#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
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
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*);
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*);
#define offset(field) XtOffsetOf(SimpleMenuRec, simple_menu.field)
static XtResource resources[] = {
{
XtNlabel,
XtCLabel,
XtRString,
sizeof(String),
offset(label_string),
XtRString,
NULL
},
{
XtNlabelClass,
XtCLabelClass,
XtRPointer,
sizeof(WidgetClass),
offset(label_class),
XtRImmediate,
NULL
},
{
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
{
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,
NULLQUARK,
XtCompositeExtensionVersion,
sizeof(CompositeClassExtensionRec),
True,
};
#define Superclass (&overrideShellClassRec)
SimpleMenuClassRec simpleMenuClassRec = {
{
(WidgetClass)Superclass,
"SimpleMenu",
sizeof(SimpleMenuRec),
XawSimpleMenuClassInitialize,
XawSimpleMenuClassPartInitialize,
False,
XawSimpleMenuInitialize,
NULL,
XawSimpleMenuRealize,
actionsList,
XtNumber(actionsList),
resources,
XtNumber(resources),
NULLQUARK,
True,
True,
True,
False,
NULL,
XawSimpleMenuResize,
XawSimpleMenuRedisplay,
XawSimpleMenuSetValues,
XawSimpleMenuSetValuesHook,
XtInheritSetValuesAlmost,
NULL,
NULL,
XtVersion,
NULL,
defaultTranslations,
NULL,
NULL,
NULL,
},
{
XawSimpleMenuGeometryManager,
XawSimpleMenuChangeManaged,
XtInheritInsertChild,
XtInheritDeleteChild,
NULL,
},
{
NULL,
},
{
NULL,
},
{
NULL,
},
};
WidgetClass simpleMenuWidgetClass = (WidgetClass)&simpleMenuClassRec;
static void
XawSimpleMenuClassInitialize(void)
{
XawInitializeWidgetSet();
XtAddConverter(XtRString, XtRBackingStore, XmuCvtStringToBackingStore,
NULL, 0);
XtSetTypeConverter(XtRBackingStore, XtRString, XmuCvtBackingStoreToString,
NULL, 0, XtCacheNone, NULL);
XmuAddInitializer(AddPositionAction, NULL);
}
static void
XawSimpleMenuClassPartInitialize(WidgetClass wc)
{
SimpleMenuWidgetClass smwc = (SimpleMenuWidgetClass)wc;
extension_rec.next_extension = smwc->composite_class.extension;
smwc->composite_class.extension = (XtPointer) &extension_rec;
}
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;
}
XtAddCallback(cnew, XtNpopupCallback, ChangeCursorOnGrab, NULL);
}
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
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);
}
}
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
}
static void
XawSimpleMenuResize(Widget w)
{
if (!XtIsRealized(w))
return;
Layout(w, NULL, NULL);
XawSimpleMenuRedisplay(w, NULL, NULL);
}
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)
XtDestroyWidget((Widget)smw_old->simple_menu.label);
else if (smw_old->simple_menu.label_string == NULL)
CreateLabel(cnew);
else {
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);
}
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);
}
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);
if ((!(mode & CWWidth) || reply->width == request->width)
&& (!(mode & CWHeight) || reply->height == request->height)) {
if (mode & XtCWQueryOnly) {
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);
}
static void
XawSimpleMenuChangeManaged(Widget w)
{
Layout(w, NULL, NULL);
}
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;
}
}
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);
}
}
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
}
}
static void
Notify(Widget w, XEvent *event, String *params, Cardinal *num_params)
{
SmeObject entry;
SmeObjectClass cclass;
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);
}
void
XawSimpleMenuAddGlobalActions(XtAppContext app_con)
{
XtInitializeWidgetClass(simpleMenuWidgetClass);
XmuCallInitializers(app_con);
}
Widget
XawSimpleMenuGetActiveEntry(Widget w)
{
SimpleMenuWidget smw = (SimpleMenuWidget)w;
return ((Widget)smw->simple_menu.entry_set);
}
void
XawSimpleMenuClearActiveEntry(Widget w)
{
SimpleMenuWidget smw = (SimpleMenuWidget)w;
smw->simple_menu.entry_set = NULL;
}
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;
}
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;
}
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);
}
}
static void
AddPositionAction(XtAppContext app_con, XPointer data)
{
static XtActionsRec pos_action[] = {
{"XawPositionSimpleMenu", PositionMenuAction},
};
XtAppAddActions(app_con, pos_action, XtNumber(pos_action));
}
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);
}
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;
}
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);
}
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);
}
static void
ChangeCursorOnGrab(Widget w, XtPointer temp1, XtPointer temp2)
{
SimpleMenuWidget smw = (SimpleMenuWidget)w;
XChangeActivePointerGrab(XtDisplay(w), ButtonPressMask | ButtonReleaseMask,
smw->simple_menu.cursor,
XtLastTimestampProcessed(XtDisplay(w)));
}
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);
else
return (*entry);
}
}
return (NULL);
}
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);
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;
}
static void
PopupCB(Widget w, XtPointer client_data, XtPointer call_data)
{
SimpleMenuWidget smw = (SimpleMenuWidget)w;
smw->simple_menu.state &= ~(SMW_UNMAPPING | SMW_POPLEFT);
}
#endif