/* $Xorg: SimpleMenu.c,v 1.3 2000/08/17 19:45:37 cpqbld Exp $ */ /* Copyright 1989, 1994, 1998 The Open Group All Rights Reserved. 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 #include #include #include #include #include #include #include #include #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[] = ":" "highlight()\n" ":" "unhighlight()\n" ":" "highlight()\n" #ifndef OLDXAW ":" "popdown() notify() unhighlight()\n" #else ":" "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 */