/* * @OPENGROUP_COPYRIGHT@ * COPYRIGHT NOTICE * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for * the full copyright text. * * This software is subject to an open license. It may only be * used on, with or for operating systems which are themselves open * source systems. You must contact The Open Group for a license * allowing distribution and sublicensing of this software on, with, * or for operating systems which are not Open Source programs. * * See http://www.opengroup.org/openmotif/license for full * details of the license agreement. Any use, reproduction, or * distribution of the program constitutes recipient's acceptance of * this agreement. * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. */ /* * HISTORY */ #ifdef REV_INFO #ifndef lint static char rcsid[] = "$TOG: MenuShell.c /main/24 1999/07/08 16:49:59 vipin $" #endif #endif /* (c) Copyright 1989, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */ /* (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 HEWLETT-PACKARD COMPANY */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "BaseClassI.h" #include "GadgetUtiI.h" #include "MapEventsI.h" #include "MenuShellI.h" #include "MenuStateI.h" #include "MenuUtilI.h" #include "MessagesI.h" #include "RCMenuI.h" #include "RowColumnI.h" #include "TearOffI.h" #include "TraversalI.h" #include "UniqueEvnI.h" #include "VendorSI.h" #include "ResIndI.h" #include "XmI.h" #define Events ((unsigned int) (ButtonPressMask | ButtonReleaseMask | \ EnterWindowMask | LeaveWindowMask)) /* Warning messages */ #define ChildMsg _XmMMsgMenuShell_0000 #define ManageMsg _XmMMsgMenuShell_0001 #define MESSAGE2 _XmMMsgMenuShell_0002 #define MESSAGE3 _XmMMsgMenuShell_0003 #define MESSAGE4 _XmMMsgMenuShell_0004 #define MESSAGE5 _XmMMsgMenuShell_0005 #define MESSAGE6 _XmMMsgMenuShell_0006 #define MESSAGE7 _XmMMsgMenuShell_0007 #define MESSAGE8 _XmMMsgMenuShell_0008 #define MESSAGE9 _XmMMsgMenuShell_0009 #define default_translations _XmMenuShell_translations /******** Static Function Declarations ********/ static void _XmFastExpose( register XmManagerWidget rowcol) ; static void _XmFastPopdown( XmMenuShellWidget shell) ; static void PostMenuShell( XmMenuShellWidget menuShell, XtGrabKind grab_kind, #if NeedWidePrototypes int spring_loaded) ; #else Boolean spring_loaded) ; #endif /* NeedWidePrototypes */ static void ClassInitialize( void ) ; static void ClassPartInitialize( WidgetClass wc) ; static void Initialize( Widget req, Widget new_w, ArgList args, Cardinal *num_args) ; static void StructureNotifyHandler( Widget wid, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch) ; static Boolean SetValues( Widget cw, Widget rw, Widget nw, ArgList args, Cardinal *num_args) ; static void Resize( Widget wid) ; static void DeleteChild( Widget widget) ; static void InsertChild( Widget widget) ; static void ForceMenuPaneOnScreen( register XmRowColumnWidget rowcol, register Position *x, register Position *y) ; static void PopupSharedMenuShell( Widget cbwid, Widget smwid, XEvent *event) ; static XtGeometryResult GeometryManager( Widget wid, XtWidgetGeometry *request, XtWidgetGeometry *reply) ; static void ChangeManaged( Widget w) ; static void Popdown( XmMenuShellWidget menushell, XEvent *event) ; static void PopdownKids( XmMenuShellWidget menushell, XEvent *event) ; static int SkipPopdown( XmCascadeButtonWidget cascade) ; static void PopdownOne( Widget widget, XEvent *event, String *params, Cardinal *num_params) ; static void PopdownEveryone( Widget widget, XEvent *event, String *params, Cardinal *num_params) ; static void PopdownDone( Widget widget, XEvent *event, String *params, Cardinal *num_params) ; static void ClearTraversalInternal( XmMenuShellWidget menushell, XEvent *event) ; static void Destroy( Widget wid) ; static Widget _XmFindPopup( Widget widget, String name) ; static void _XmMenuPopupAction( Widget widget, XEvent *event, String *params, Cardinal *num_params) ; static void _XmMenuPopdownAction( Widget widget, XEvent *event, String *params, Cardinal *num_params) ; static XmFontList GetTable(Widget wid, XtEnum type); static void CheckSetRenderTables(Widget wid, int offset, XrmValue *value); static XmDirection GetDirection(Widget); /******** End Static Function Declarations ********/ static XmConst char tk_error[] = "XtToolkitError" ; static XtActionsRec actionsList[] = { {"MenuShellPopdownOne", PopdownOne}, {"MenuShellPopdownDone", PopdownDone}, {"XtMenuPopup", _XmMenuPopupAction}, {"XtMenuPopdown", _XmMenuPopdownAction}, {"MenuEscape", _XmMenuEscape}, {"ClearTraversal", _XmClearTraversal} }; static XtResource resources[] = { { XmNdefaultFontList, XmCDefaultFontList, XmRFontList, sizeof(XmFontList), XtOffsetOf( struct _XmMenuShellRec, menu_shell.default_font_list), XmRString, (XtPointer) NULL }, { XmNlabelFontList, XmCLabelFontList, XmRLabelRenderTable, sizeof(XmFontList), XtOffsetOf( struct _XmMenuShellRec, menu_shell.label_font_list), XmRCallProc, (XtPointer) CheckSetRenderTables }, { XmNbuttonFontList, XmCButtonFontList, XmRButtonRenderTable, sizeof(XmFontList), XtOffsetOf( struct _XmMenuShellRec, menu_shell.button_font_list), XmRCallProc, (XtPointer) CheckSetRenderTables }, { XmNlabelRenderTable, XmCLabelRenderTable, XmRLabelRenderTable, sizeof(XmRenderTable), XtOffsetOf( struct _XmMenuShellRec, menu_shell.label_font_list), XmRCallProc, (XtPointer)CheckSetRenderTables }, { XmNbuttonRenderTable, XmCButtonRenderTable, XmRButtonRenderTable, sizeof(XmRenderTable), XtOffsetOf( struct _XmMenuShellRec, menu_shell.button_font_list), XmRCallProc, (XtPointer)CheckSetRenderTables }, { XmNlayoutDirection, XmCLayoutDirection, XmRDirection, sizeof(XmDirection), XtOffsetOf( struct _XmMenuShellRec, menu_shell.layout_direction), XmRCallProc, (XtPointer) _XmDirectionDefault }, { /* default visual dynamically, see _XmDefaultVisualResources */ XtNvisual, XtCVisual, XtRVisual, sizeof(Visual*), XtOffsetOf(ShellRec, shell.visual), XtRImmediate, (XtPointer)INVALID_VISUAL }, }; static XmBaseClassExtRec baseClassExtRec = { NULL, NULLQUARK, XmBaseClassExtVersion, sizeof(XmBaseClassExtRec), (XtInitProc)NULL, /* InitializePrehook */ (XtSetValuesFunc)NULL, /* SetValuesPrehook */ (XtInitProc)NULL, /* InitializePosthook */ (XtSetValuesFunc)NULL, /* SetValuesPosthook */ NULL, /* secondaryObjectClass */ (XtInitProc)NULL, /* secondaryCreate */ (XmGetSecResDataFunc)NULL, /* getSecRes data */ { 0 }, /* fastSubclass flags */ (XtArgsProc)NULL, /* get_values_prehook */ (XtArgsProc)NULL, /* get_values_posthook */ /* ... */ }; externaldef(xmmenushellclassrec) XmMenuShellClassRec xmMenuShellClassRec = { { (WidgetClass) & overrideShellClassRec, /* superclass */ "XmMenuShell", /* class_name */ sizeof (XmMenuShellWidgetRec), /* widget size */ ClassInitialize, /* Class Initializer */ ClassPartInitialize, /* chained class init */ FALSE, Initialize, /* instance init proc */ (XtArgsProc)NULL, /* init_hook proc */ XtInheritRealize, /* instance realize proc */ actionsList, /* actions */ XtNumber(actionsList), /* num_actions */ resources, /* resource list */ XtNumber(resources), /* resource_count */ NULLQUARK, /* xrm_class */ True, /* do compress_motion */ XtExposeCompressMaximal, /* do compress_exposure */ TRUE, /* do compress enter-leave */ FALSE, /* do have visible_interest */ Destroy, /* instance widget destroyb */ Resize, /* resize */ XtInheritExpose, /* expose */ SetValues, /* set values proc */ (XtArgsFunc)NULL, /* set values hook proc */ XtInheritSetValuesAlmost, /* set_values_almost proc */ (XtArgsProc)NULL, /* get_values_hook */ (XtAcceptFocusProc)NULL, /* accept_focus proc */ XtVersion, /* current version */ NULL, /* callback offset */ default_translations, /* translation table */ (XtGeometryHandler)NULL, /* query geo proc */ (XtStringProc)NULL, /* disp accelerator */ (XtPointer)&baseClassExtRec, /* extension */ }, { /* composite class record */ /* BEGIN OSF fix pir 2131 */ GeometryManager, /* geo mgr */ /* END OSF fix pir 2131 */ ChangeManaged, /* have our own */ InsertChild, /* and this too */ DeleteChild, NULL, /* Extension */ }, { /* shell class record */ NULL, /* extension */ }, { /* override shell class record */ NULL, /* extension */ }, { /* menushell class record */ PopdownOne, PopdownEveryone, PopdownDone, PopupSharedMenuShell, NULL, /* extension */ }, }; externaldef(xmmenushellwidgetclass) WidgetClass xmMenuShellWidgetClass = (WidgetClass) &xmMenuShellClassRec; /* Trait record for MenuShell specify render table */ static XmConst XmSpecRenderTraitRec menushellSRT = { 0, /* version */ GetTable, }; static XmConst XmSpecifyLayoutDirectionTraitRec LayoutDirection = { 0, /* version */ GetDirection }; /* Save pointers for renderTable XmRCallProc */ static XmMenuShellWidget check_set_save = NULL; static int check_set_offset1 = 0; static int check_set_offset2 = 0; /* * When using an override redirect window, it is safe to draw to the * window as soon as you have mapped it; you need not wait for exposure * events to arrive. So ... to force menupanes to post quickly, we will * redraw all of the items now, and ignore the exposure events we receive * later. */ static void _XmFastExpose( register XmManagerWidget rowcol ) { register int i; register Widget child; /* Process the menupane */ RC_SetExpose(rowcol, True); (*(XtClass(rowcol)->core_class.expose))((Widget) rowcol, NULL, NULL); /* Process each windowed child */ for (i = 0; i < rowcol->composite.num_children; i++) { child = rowcol->composite.children[i]; if (XtIsWidget(child) && XtIsManaged(child)) { (*(XtClass(child)->core_class.expose))(child, NULL, NULL); } } XFlush(XtDisplay(rowcol)); /* Set one-shot, so menupane will ignore next expose */ RC_SetExpose(rowcol, False); } /* * When unposting a group of cascading menupanes, we will first unmap all * of the MenuShell widgets, and then take care of the other work needed * to get the job done. We do it this way, so that we can give the user * the fastest possible feedback. */ static void _XmFastPopdown( XmMenuShellWidget shell ) { if (RC_PopupPosted(shell->composite.children[0])) _XmFastPopdown((XmMenuShellWidget) RC_PopupPosted(shell->composite.children[0])); XtUnmapWidget(shell); } static void _XmPopupI( Widget widget, XtGrabKind grab_kind, Boolean spring_loaded ) { register ShellWidget shell_widget = (ShellWidget) widget; if (! XtIsShell(widget)) { XtAppErrorMsg(XtWidgetToApplicationContext(widget), "invalidClass", "xmPopup", tk_error, MESSAGE2, (String *)NULL, (Cardinal *)NULL); } if (! shell_widget->shell.popped_up) { XtGrabKind call_data = grab_kind; XtCallCallbacks(widget, XtNpopupCallback, (XtPointer)&call_data); shell_widget->shell.popped_up = TRUE; shell_widget->shell.grab_kind = grab_kind; shell_widget->shell.spring_loaded = spring_loaded; if (shell_widget->shell.create_popup_child_proc != NULL) { (*(shell_widget->shell.create_popup_child_proc))(widget); } if (grab_kind == XtGrabExclusive) { _XmAddGrab(widget, TRUE, spring_loaded); } else if (grab_kind == XtGrabNonexclusive) { _XmAddGrab(widget, FALSE, spring_loaded); } XtRealizeWidget(widget); XMapRaised(XtDisplay(widget), XtWindow(widget)); } else XRaiseWindow(XtDisplay(widget), XtWindow(widget)); } /* _XmPopupI */ void _XmPopupSpringLoaded( Widget shell) { _XmPopupI( shell, XtGrabExclusive, True) ; } void _XmPopup( Widget shell, XtGrabKind grab_kind) { _XmPopupI( shell, grab_kind, FALSE) ; } /* * Post the requested menu shell widget, */ static void PostMenuShell( XmMenuShellWidget menuShell, XtGrabKind grab_kind, #if NeedWidePrototypes int spring_loaded ) #else Boolean spring_loaded ) #endif /* NeedWidePrototypes */ { XEvent event; XEvent *lastEvent; /* Remember the next event from the queue so we don't popdown on the replay */ event.xbutton.type = ButtonPress; if ((lastEvent = XtLastEventProcessed(XtDisplay((Widget) menuShell))) == (XEvent *)NULL) { event.xbutton.serial = XLastKnownRequestProcessed(XtDisplay((Widget) menuShell)); } else { event.xbutton.serial = lastEvent->xany.serial; } event.xbutton.send_event = 0; event.xbutton.time = XtLastTimestampProcessed(XtDisplay((Widget) menuShell)); event.xbutton.display = XtDisplay((Widget)menuShell); _XmRecordEvent((XEvent *) &event); if (spring_loaded) { /* XmPopupSpringLoaded ((Widget) menuShell); */ /* Since the modality implementation requires the use of * _XmAddGrab() instead of XtAddGrab(), XtPopupSpringLoaded() * must be replaced with _XmPopupSpringLoaded(), which uses * the appropriate AddGrab routine. */ _XmPopupSpringLoaded( (Widget) menuShell) ; } else _XmPopup ((Widget) menuShell, grab_kind); /* mark the row column as NOT popping down */ RC_SetPoppingDown(menuShell->composite.children[0], False); } /* * Class Initialize. Set up fast subclassing. */ static void ClassInitialize( void ) { _XmInitializeExtensions(); baseClassExtRec.record_type = XmQmotif; } static XmDirection GetDirection(Widget w) { return ((XmMenuShellWidget)w)->menu_shell.layout_direction; } static void ClassPartInitialize( WidgetClass wc ) { _XmFastSubclassInit (wc, XmMENU_SHELL_BIT); /* Install the specifyrendertable trait for all subclasses */ XmeTraitSet((XtPointer)wc, XmQTspecifyRenderTable, (XtPointer) &menushellSRT); XmeTraitSet(wc, XmQTspecifyLayoutDirection, (XtPointer) &LayoutDirection); } /* * Initialize routine */ /* ARGSUSED */ static void Initialize( Widget req, Widget new_w, ArgList args, Cardinal *num_args ) { XmFontList defaultFont; XmMenuShellWidget ms = (XmMenuShellWidget) new_w; new_w->core.background_pixmap = None; XtBorderWidth (new_w) = 0; /* Style Guide says so */ ms->menu_shell.focus_data = (XmFocusData)_XmCreateFocusData(); ms->menu_shell.focus_policy = XmEXPLICIT; ms->shell.allow_shell_resize = TRUE; /* get reasonable defaults for visual, depth and colormap */ _XmDefaultVisualResources(new_w); /* Fix 7961 - realization ordering causing badmatch */ XtRealizeWidget (new_w); /* Assume, for now, that the application created this shell */ ms->menu_shell.private_shell = False; defaultFont = ms->menu_shell.button_font_list; if ( !defaultFont ) { /* backward compatibility */ defaultFont = ms->menu_shell.default_font_list; if ( !defaultFont ) defaultFont = XmeGetDefaultRenderTable( (Widget) ms, XmBUTTON_FONTLIST); } ms->menu_shell.button_font_list = XmFontListCopy (defaultFont); defaultFont = ms->menu_shell.label_font_list; if ( !defaultFont ) { /* backward compatibility */ defaultFont = ms->menu_shell.default_font_list; if ( !defaultFont ) defaultFont = XmeGetDefaultRenderTable( (Widget) ms, XmLABEL_FONTLIST); } ms->menu_shell.label_font_list = XmFontListCopy (defaultFont); _XmSetSwallowEventHandler((Widget) ms, True); /* * Take over EventHandler in Shell, to dump the configure notify * events since it is not smart enough to ignore stale events. */ XtInsertEventHandler((Widget) ms, (EventMask) StructureNotifyMask, True, StructureNotifyHandler, (XtPointer) NULL, XtListHead); } /* ARGSUSED */ static void StructureNotifyHandler( Widget wid, XtPointer closure, XEvent *event, Boolean *continue_to_dispatch ) { /* * This event handler is inserted at the head of the list so that * the configure notify events will be ignored. This is because the * configures are done directly when needed, not when the event handler * is called. */ if (event->type == ConfigureNotify) { /* all the configure notify events should be ignored */ *continue_to_dispatch = False; } } /* * set values * * Don't allow the allowShellResize flag to be set false */ /* ARGSUSED */ static Boolean SetValues( Widget cw, Widget rw, Widget nw, ArgList args, Cardinal *num_args ) { XmMenuShellWidget new_w = (XmMenuShellWidget) nw ; XmMenuShellWidget old_w = (XmMenuShellWidget) cw ; XmFontList defaultFont; /* CR 7124: XmNlayoutDirection is a CG resource. */ if (old_w->menu_shell.layout_direction != new_w->menu_shell.layout_direction) { XmeWarning(nw, MESSAGE9); new_w->menu_shell.layout_direction = old_w->menu_shell.layout_direction; } if (new_w->menu_shell.button_font_list != old_w->menu_shell.button_font_list) { XmFontListFree(old_w->menu_shell.button_font_list); defaultFont = new_w->menu_shell.button_font_list; if (!defaultFont) { defaultFont = new_w->menu_shell.default_font_list; if (!defaultFont ) defaultFont = XmeGetDefaultRenderTable( (Widget) new_w, XmBUTTON_FONTLIST); } new_w->menu_shell.button_font_list = XmFontListCopy (defaultFont); } if (new_w->menu_shell.label_font_list != old_w->menu_shell.label_font_list) { XmFontListFree(old_w->menu_shell.label_font_list); defaultFont = new_w->menu_shell.label_font_list; if (!defaultFont) { defaultFont = new_w->menu_shell.default_font_list; if (!defaultFont ) defaultFont = XmeGetDefaultRenderTable( (Widget) new_w, XmLABEL_FONTLIST); } new_w->menu_shell.label_font_list = XmFontListCopy (defaultFont); } new_w->shell.allow_shell_resize = TRUE; return (TRUE); } /* * Resize our first child * * The statement pertaining to shared menupanes is commented out because * if the user is rapidly moving thru the menubar, an old configure notify * event may come in, cause the shell to resize the wrong child. */ static void Resize( Widget wid ) { XmMenuShellWidget ms = (XmMenuShellWidget) wid ; if (((ms->composite.num_children == 1) && (XtIsManaged(ms->composite.children[0])))) /* || ((ms->composite.num_children > 1) && (ms->shell.popped_up))) */ { Widget child = ms->composite.children[0]; XmeConfigureObject(child, -ms->core.border_width, -ms->core.border_width, ms->core.width, ms->core.height, ms->core.border_width); } } /* * DeleteChild routine */ static void DeleteChild( Widget widget ) { XmMenuShellWidget parent = (XmMenuShellWidget)XtParent(widget); /* Remove the child as our tab group */ XmRemoveTabGroup(widget); /* Let composite class finish the work */ (*(((CompositeWidgetClass)compositeWidgetClass)->composite_class. delete_child))(widget); /* * If we had multiple children, but we've now dropped down to 1 child, * then we need to unmanage the child. */ if (parent->composite.num_children == 1) { Widget baseChild = parent->composite.children[0]; if (!parent->shell.popped_up && !(XmIsRowColumn(baseChild) && RC_TearOffActive(baseChild))) baseChild->core.managed = False; XMapWindow(XtDisplay(parent), XtWindow(baseChild)); } else if ((parent->composite.num_children == 0) && (parent->menu_shell.private_shell) && (!parent->core.being_destroyed)) { /* Destroy private shells, when they have no more children */ XtDestroyWidget((Widget) parent); } } /* * InsertChild routine */ static void InsertChild( Widget widget ) { CompositeWidget parent = (CompositeWidget) widget->core.parent; int num_real_children, i; if (XmIsRowColumn (widget)) { if ((RC_Type(widget) == XmMENU_PULLDOWN) || (RC_Type(widget) == XmMENU_POPUP)) { XtAddEventHandler (widget, EnterWindowMask, FALSE, _XmEnterRowColumn, FALSE); } /* * add to composite list */ (*((CompositeWidgetClass)compositeWidgetClass)->composite_class. insert_child) (widget); XtRealizeWidget(widget); /* Find the number of children excluding those being destroyed. * This fixes a problem with menus being dynamically destroyed * and added. Before this fix the new menu was never mapped * because the menu being destroyed was still counted as a child. */ for (i = 0, num_real_children = 0; i < parent->composite.num_children; i++) if (!parent->composite.children[i]->core.being_destroyed) num_real_children++; /* * If only one child, then we will manage and unmanage it when we * want it to post or unpost. If there are more than one child, then * we will have them always managed, and we will use a different * technique for posting and unposting. */ if (num_real_children == 1) { XtSetKeyboardFocus(( Widget) parent, widget); } else if (num_real_children == 2) { /* if one is torn off, they don't have the same parent, so manage ** separately. */ if (parent->composite.children[0] != parent->composite.children[1]) { XtManageChild(parent->composite.children[0]); XtManageChild(parent->composite.children[1]); } else { XtManageChildren(parent->composite.children, 2); } } else { XtManageChild(widget); } if (num_real_children == 1) { XMapWindow(XtDisplay(widget), XtWindow(widget)); } } else XmeWarning(widget, ChildMsg); } static void ForceMenuPaneOnScreen( register XmRowColumnWidget rowcol, register Position *x, register Position *y ) { Position rightEdgeOfMenu, bottomEdgeOfMenu; Dimension dispWidth, dispHeight; Widget pulldown_button = RC_CascadeBtn(rowcol); Dimension RowColBorderWidth = rowcol->core.border_width << 1; Dimension CascadeBorderWidth = 0; if (pulldown_button) CascadeBorderWidth = pulldown_button->core.border_width << 1; /* Force the rowcol to be completely visible */ rightEdgeOfMenu = *x + RowColBorderWidth + rowcol->core.width; bottomEdgeOfMenu = *y + RowColBorderWidth + rowcol->core.height; dispWidth = WidthOfScreen (XtScreen(rowcol)); dispHeight = HeightOfScreen (XtScreen(rowcol)); /* * For OPTION menus, if the submenu is [partially] offscreen, offset it * off the button. */ if (pulldown_button && XtParent(pulldown_button) && XmIsRowColumn(XtParent(pulldown_button)) && (RC_Type(XtParent(pulldown_button)) == XmMENU_OPTION)) { Position old_x = *x; if (bottomEdgeOfMenu >= (Position)dispHeight) { *y = dispHeight - rowcol->core.height - RowColBorderWidth - 1; if (LayoutIsRtoLM(rowcol)) *x = old_x - rowcol->core.width - (rowcol->core.border_width <<1); else *x = old_x + pulldown_button->core.width + CascadeBorderWidth; rightEdgeOfMenu = *x + RowColBorderWidth + rowcol->core.width; bottomEdgeOfMenu = *y + RowColBorderWidth + rowcol->core.height; } if (*y < 0) { *y = 0; /* Consider CascadeBtn as well as RowCol width to allow multi * column RowColumn */ if (LayoutIsRtoLM(rowcol)) *x = old_x - rowcol->core.width - (rowcol->core.border_width <<1); else *x = old_x + pulldown_button->core.width + CascadeBorderWidth; rightEdgeOfMenu = *x + RowColBorderWidth + rowcol->core.width; bottomEdgeOfMenu = *y + RowColBorderWidth + rowcol->core.height; } if (rightEdgeOfMenu >= (Position)dispWidth) { *x = old_x - rowcol->core.width + RowColBorderWidth; rightEdgeOfMenu = *x + RowColBorderWidth + rowcol->core.width; } if (*x < 0) { if (LayoutIsRtoLM(rowcol)) *x = old_x + pulldown_button->core.width + CascadeBorderWidth; else *x = old_x + pulldown_button->core.width + CascadeBorderWidth; rightEdgeOfMenu = *x + RowColBorderWidth + rowcol->core.width; } } /* * If the submenu is offscreen force it completely on. */ if (rightEdgeOfMenu >= (Position)dispWidth) *x -= (rightEdgeOfMenu - dispWidth + 1); if (bottomEdgeOfMenu >= (Position)dispHeight) { if (pulldown_button && XtParent(pulldown_button) && (RC_Type(XtParent(pulldown_button)) == XmMENU_BAR)) { Position y_temp = *y; /* this menu pane is being pulled down from a bar */ /* it doesn't fit, so we have to place it above */ /* the bar -- if it will fit */ y_temp -= (CascadeBorderWidth + pulldown_button->core.height + RowColBorderWidth + rowcol->core.height + 1); if (y_temp > 0) *y = y_temp; } else *y -= (bottomEdgeOfMenu - dispHeight + 1); } /* Make sure that the top left corner os on screen! */ if (*x < 0) *x = 0; if (*y < 0) *y = 0; } /* * Method for posting a multi-paned menushell */ static void PopupSharedMenuShell( Widget cbwid, Widget smwid, XEvent *event ) { XmCascadeButtonWidget cascadebtn = (XmCascadeButtonWidget) cbwid ; XmRowColumnWidget submenu = (XmRowColumnWidget) smwid ; register XmMenuShellWidget popup = (XmMenuShellWidget) XtParent(submenu); XmRowColumnWidget parent_menu; Position x, y; Dimension height, width; int _index = 0; register int i; Boolean popped_up = popup->shell.popped_up; XmRowColumnWidget old_rowcol = NULL; XmCascadeButtonWidget old_cascadebtn = NULL; XmMenuState mst = _XmGetMenuState((Widget)submenu); Time _time = _XmGetDefaultTime(cbwid, event); XmMenuSystemTrait menuSTrait; menuSTrait = (XmMenuSystemTrait) XmeTraitGet((XtPointer) XtClass((Widget) submenu), XmQTmenuSystem); /* Find out which child is trying to get posted */ for (i = 0; i < popup->composite.num_children; i++) { if (popup->composite.children[i] == (Widget)submenu) { _index = i; break; } } /* Swap only if the submenu is not already child[0] */ if (_index != 0) { /* Swap places in the children list */ old_rowcol = (XmRowColumnWidget) popup->composite.children[0]; old_cascadebtn = (XmCascadeButtonWidget)RC_CascadeBtn(old_rowcol); RC_SetPoppingDown(old_rowcol, True); menuSTrait -> disarm((Widget) old_rowcol); /* RC_SetArmed (old_rowcol, False); */ popup->composite.children[_index] = (Widget)old_rowcol; popup->composite.children[0] = (Widget)submenu; RC_SetPoppingDown(submenu, False); if (RC_TornOff(old_rowcol)) _XmRestoreTearOffToToplevelShell((Widget)old_rowcol, event); else XUnmapWindow(XtDisplay(old_rowcol), XtWindow(old_rowcol)); } else if (cascadebtn != (XmCascadeButtonWidget) RC_CascadeBtn(popup->composite.children[0])) { /* If the 2 different cascade buttons popped up this submenu, * unmap it in preparation for the 'move'. Also save the old * cascade so it can be unhighlighted. */ old_cascadebtn = (XmCascadeButtonWidget)RC_CascadeBtn(submenu); XUnmapWindow(XtDisplay(submenu), XtWindow(submenu)); } if (popped_up || (old_cascadebtn && RC_TornOff(XtParent(old_cascadebtn)) && !XmIsMenuShell(XtParent(XtParent(old_cascadebtn))))) { XmCascadeButtonHighlight( (Widget) old_cascadebtn, False); } if (submenu->core.being_destroyed) return; /* It seems that menupanes that share their menushell are always managed. * When reposting a tear off, the restoration code unmanages the pane. * So set it here! */ submenu->core.managed = TRUE; if (menuSTrait) menuSTrait -> cascade((Widget) submenu, (Widget) cascadebtn, event); /* * Set us as the active tab group for our parent */ _XmSetActiveTabGroup(popup->menu_shell.focus_data, (Widget)submenu); /* * get the shell widget in sync with the menu widget * We keep the menu at 0,0 and shell at the menu's position. * the menu's position is just used as a place to keep shell's loc * * NOTE: the menu may be moved down slightly to take into account the * tear off control. */ /* Always adjust to the size of the menupane */ width = XtWidth(submenu); height = XtHeight(submenu); if (RC_WidgetHasMoved (submenu)) /* but make sure it didn't */ { /* just move to where it */ /* is now. Happens with */ /* pulldowns a lot */ x = XtX(submenu); y = XtY(submenu); ForceMenuPaneOnScreen(submenu,&x,&y); XtX (submenu) = XtY (submenu) = (-1 * XtBorderWidth(submenu)); if (RC_WindowHasMoved(submenu)) { XMoveWindow (XtDisplay(submenu), XtWindow(submenu), XtX(submenu), XtY(submenu)); RC_SetWindowMoved (submenu, FALSE); } RC_SetWidgetMoved (submenu, FALSE); } else { x = XtX(popup); y = XtY(popup); } XmeConfigureObject((Widget) popup, x, y, width, height, popup->core.border_width); XMapWindow(XtDisplay(submenu), XtWindow(submenu)); if (popped_up && !((old_rowcol == submenu) && (cascadebtn == old_cascadebtn))) _XmCallRowColumnUnmapCallback((Widget)old_rowcol, event); _XmCallRowColumnMapCallback((Widget)submenu, event); /* * if there is a tear off control, set the initial focus to the first * real child. If none of the children is traversible, let the * traversal code set it to the toc. */ if (RC_TearOffControl(submenu) && XtIsManaged(RC_TearOffControl(submenu))) { for (i=0; i < submenu->composite.num_children; i++) { if (XmIsTraversable(submenu->composite.children[i])) { _XmSetInitialOfTabGroup( (Widget) submenu, submenu->composite.children[i]); break; } } } if (menuSTrait) menuSTrait -> arm((Widget) submenu); if (popped_up) _XmFastExpose((XmManagerWidget) submenu); else { /* * we are the root of the chain and better do the exclusive grab */ parent_menu = (XmRowColumnWidget) XtParent (cascadebtn); if (RC_Type(parent_menu) == XmMENU_OPTION) { /* Don't post the menu if the menu cannot control grabs! Note: * grabs done twice. You cannot grab on a non viewable window. */ if (_XmMenuGrabKeyboardAndPointer((Widget)parent_menu, _time) != GrabSuccess) { return; } /* Remember the time in case this is just a BSelect Click */ if ((event->type == ButtonPress) || (event->type == ButtonRelease)) mst->MS_LastManagedMenuTime = event->xbutton.time; PostMenuShell(popup, XtGrabExclusive, True); _XmFastExpose((XmManagerWidget) submenu); _XmMenuFocus(XtParent(submenu), XmMENU_BEGIN, _time); /** the real grab ***/ _XmMenuGrabKeyboardAndPointer((Widget)submenu, _time); } else { if ((RC_Type(parent_menu) == XmMENU_BAR) && (RC_BeingArmed(parent_menu))) { /* Don't post the menu if the menu cannot control grabs! */ if (_XmMenuGrabKeyboardAndPointer((Widget)parent_menu, _time) != GrabSuccess) { return; } RC_SetBeingArmed(parent_menu, False); } PostMenuShell(popup, XtGrabNonexclusive, False); _XmFastExpose((XmManagerWidget) submenu); /* We always must move the focuse for the leaf menu pane */ _XmMenuFocus(XtParent(submenu), XmMENU_MIDDLE, _time); } } /* * now highlight the pulldown entry widget that * pulled down the menu */ XmCascadeButtonHighlight ((Widget) cascadebtn, TRUE); if (popped_up) { XmGadget activeChild; activeChild = (XmGadget)old_rowcol->manager.active_child; /* * If the active child was a gadget, then we need to * handle the sending of the focus out ourselves. */ if (activeChild && XmIsGadget(activeChild)) { _XmDispatchGadgetInput((Widget) activeChild, NULL, XmFOCUS_OUT_EVENT); } } } /* BEGIN OSF fix pir 2131 */ /* This is ripped off from Xt/Shell.c and modified to also deal with X and Y * requests. */ /* * This is gross, I can't wait to see if the change happened so I will ask * the window manager to change my size and do the appropriate X work. * I will then tell the requester that he can. Care must be taken because * it is possible that some time in the future the request will be * asynchronusly denied and the window reverted to it's old size/shape. */ static XtGeometryResult GeometryManager( Widget wid, XtWidgetGeometry *request, XtWidgetGeometry *reply ) { ShellWidget shell = (ShellWidget)(wid->core.parent); XtWidgetGeometry my_request; if(shell->shell.allow_shell_resize == FALSE && XtIsRealized(wid)) return(XtGeometryNo); if(!XtIsRealized((Widget)shell)){ if (request->request_mode & (CWX | CWY)) { return(XtGeometryNo); } *reply = *request; if(request->request_mode & CWWidth) wid->core.width = shell->core.width = request->width; if(request->request_mode & CWHeight) wid->core.height = shell->core.height = request->height; if(request->request_mode & CWBorderWidth) wid->core.border_width = shell->core.border_width = request->border_width; return(XtGeometryYes); } /* %%% worry about XtCWQueryOnly */ my_request.request_mode = 0; if (request->request_mode & CWX) { my_request.x = request->x; my_request.request_mode |= CWX; } if (request->request_mode & CWY) { my_request.y = request->y; my_request.request_mode |= CWY; } if (request->request_mode & CWWidth) { my_request.width = request->width; my_request.request_mode |= CWWidth; } if (request->request_mode & CWHeight) { my_request.height = request->height; my_request.request_mode |= CWHeight; } if (request->request_mode & CWBorderWidth) { my_request.border_width = request->border_width; my_request.request_mode |= CWBorderWidth; } /* * Don't make geometry request if the shared menupane has a * visible pulldown, which isn't the current pulldown widget. This prevents * another pulldown menu to resize the menu shell's width and height, thus * dynamically resizing the visible pulldown. */ if ((wid != shell->composite.children[0]) || ((wid == shell->composite.children[0]) && (XtMakeGeometryRequest((Widget)shell, &my_request, NULL) == XtGeometryYes))) { if (request->request_mode & CWX) { wid->core.x = 0; } if (request->request_mode & CWY) { wid->core.y = 0; } if (request->request_mode & CWWidth) { wid->core.width = request->width; } if (request->request_mode & CWHeight) { wid->core.height = request->height; } if (request->request_mode & CWBorderWidth) { wid->core.border_width = request->border_width; wid->core.x = wid->core.y = -request->border_width; } return XtGeometryYes; } else return XtGeometryNo; } /* END OSF fix pir 2131 */ /* * ChangeManaged */ static void ChangeManaged( Widget w ) { register XmMenuShellWidget popup = (XmMenuShellWidget) w; XmRowColumnWidget parent_menu; Position x, y; Dimension height, width; XmCascadeButtonWidget cascadebtn; register Widget child; register XmRowColumnWidget rowcol = (XmRowColumnWidget)popup->composite.children[0]; int i; XmMenuState mst = _XmGetMenuState((Widget)w); XmMenuSystemTrait menuSTrait; Time _time = XtLastTimestampProcessed(XtDisplay(w)); mst->RC_ButtonEventStatus.waiting_to_be_managed = FALSE; /* Don't handle multi-paned shells here */ if (popup->composite.num_children > 1) return; if (rowcol->core.being_destroyed) return; if (XtIsManaged ((Widget)rowcol)) { if ((RC_Type(rowcol) == XmMENU_PULLDOWN) && (! RC_CascadeBtn(rowcol))) { /* * this pulldown does not have a complete path to * a toplevel pane, so it can not be managed */ XmeWarning(w, ManageMsg); XtUnmanageChild ((Widget) rowcol); return; } else if (RC_Type(rowcol) == XmMENU_POPUP) { /* Verify MenuPost/WhichButton */ if ((mst->RC_ButtonEventStatus.time == XtLastTimestampProcessed(XtDisplay(rowcol))) && !mst->RC_ButtonEventStatus.verified) { mst->RC_ButtonEventStatus.verified = False; XtUnmanageChild ((Widget) rowcol); return; } /* Remember the time in case this is just a BMenu Click */ mst->MS_LastManagedMenuTime = mst->RC_ButtonEventStatus.time; } /* Set us as the active tab group for our parent */ _XmSetActiveTabGroup(popup->menu_shell.focus_data, (Widget)rowcol); /* make the callback now to give the application a crack at * repositioning the pane. */ _XmCallRowColumnMapCallback((Widget)rowcol, (XEvent *)&mst->RC_ButtonEventStatus.event); /* * get the shell widget in sync with the menu widget * We keep the menu at 0,0 and shell at the menu's position. * the menu's position is just used as a place to keep shell's loc * * If a tear off control is relevant, move the pane down to allow for * its height. */ width = XtWidth(rowcol); height = XtHeight(rowcol); if (RC_WidgetHasMoved (rowcol)) { x = XtX(rowcol); y = XtY(rowcol); ForceMenuPaneOnScreen(rowcol,&x,&y); XtX (rowcol) = XtY (rowcol) = (-1 * XtBorderWidth(rowcol)); if (RC_WindowHasMoved(rowcol)) { XMoveWindow (XtDisplay(rowcol), XtWindow(rowcol), XtX(rowcol), XtY(rowcol)); RC_SetWindowMoved (rowcol, FALSE); } RC_SetWidgetMoved (rowcol, FALSE); } else { x = XtX(popup); y = XtY(popup); } XmeConfigureObject((Widget) popup, x, y, width, height, popup->core.border_width); /* * if there is a tear off control, set the initial focus to the first * real child. If none of the children is traversible, let the * traversal code set it to the toc. */ if (RC_TearOffControl(rowcol) && XtIsManaged(RC_TearOffControl(rowcol))) { for (i=0; i < rowcol->composite.num_children; i++) { if (XmIsTraversable(rowcol->composite.children[i])) { _XmSetInitialOfTabGroup( (Widget) rowcol, rowcol->composite.children[i]); break; } } } menuSTrait = (XmMenuSystemTrait) XmeTraitGet((XtPointer) XtClass((Widget) rowcol), XmQTmenuSystem); menuSTrait -> arm((Widget) rowcol); /* * now figure out exactly how to pop it up */ switch (RC_Type(rowcol)) { case XmMENU_POPUP: /* Don't post the menu if the menu cannot control grabs! Note: * grabs done twice. You cannot grab on a non viewable window. */ if (RC_CascadeBtn(rowcol)) /* Doggone loophole! */ { if (_XmMenuGrabKeyboardAndPointer(RC_CascadeBtn(rowcol), _time) != GrabSuccess) { /** this is the fix for cmvc 6688 (CDExc15902) ***/ XtUnmanageChild((Widget) rowcol); /** end of fix for 6688 ****/ return; } } PostMenuShell((XmMenuShellWidget) w, XtGrabExclusive, True); _XmFastExpose((XmManagerWidget) rowcol); /** the real grab ***/ _XmMenuGrabKeyboardAndPointer((Widget)rowcol, _time); _XmMenuFocus(XtParent(rowcol), XmMENU_BEGIN, _time); /* To support menu replay, keep the pointer in sync mode */ XAllowEvents(XtDisplay(rowcol), SyncPointer, CurrentTime); menuSTrait -> controlTraversal((Widget) rowcol, FALSE); break; case XmMENU_PULLDOWN: cascadebtn = (XmCascadeButtonWidget) RC_CascadeBtn(rowcol); parent_menu = (XmRowColumnWidget) XtParent (cascadebtn); /* Make sure the correct item gets the focus */ if (!_XmGetInDragMode((Widget) rowcol)) { if (RC_MemWidget(rowcol) && (RC_Type(parent_menu) == XmMENU_OPTION)) { if (XtParent(RC_MemWidget(rowcol)) == (Widget)rowcol) _XmSetInitialOfTabGroup( (Widget) rowcol, RC_MemWidget(rowcol)); else { /* Find our link to the memory widget */ child = (Widget)RC_MemWidget(rowcol); while (child) { if (XtParent(child) == (Widget)rowcol) break; child = (Widget)RC_CascadeBtn(XtParent(child)); } rowcol->manager.active_child = child; } } else rowcol->manager.active_child = NULL; if ((parent_menu->manager.active_child != (Widget)cascadebtn) && ((RC_Type(parent_menu) == XmMENU_POPUP) || (RC_Type(parent_menu) == XmMENU_PULLDOWN))) { /* Move the focus to the child */ _XmMgrTraversal((Widget) cascadebtn, XmTRAVERSE_CURRENT); } } if (RC_Type(parent_menu) == XmMENU_OPTION) { /* Don't post the menu if the menu cannot control grabs! Note: * grabs done twice. You cannot grab on a non viewable window. */ if (_XmMenuGrabKeyboardAndPointer((Widget)parent_menu, _time) != GrabSuccess) { XtUnmanageChild((Widget) rowcol); return; } /* Remember the time in case this is just a BSelect Click */ mst->MS_LastManagedMenuTime = mst->RC_ButtonEventStatus.time; PostMenuShell((XmMenuShellWidget) w, XtGrabExclusive, True); _XmFastExpose((XmManagerWidget) rowcol); _XmMenuFocus(XtParent(rowcol), XmMENU_BEGIN, _time); /**the real grab *****/ _XmMenuGrabKeyboardAndPointer((Widget)rowcol, _time); _XmMenuFocus(XtParent(rowcol), XmMENU_BEGIN, _time); /* To support menu replay, keep the pointer in sync mode */ XAllowEvents(XtDisplay(rowcol), SyncPointer, CurrentTime); } else { /* For a menubar, put the pointer grab on the menubar. */ if ((RC_Type(parent_menu) == XmMENU_BAR) && (RC_BeingArmed(parent_menu))) { if (_XmMenuGrabKeyboardAndPointer((Widget)parent_menu, _time) !=GrabSuccess) { XtUnmanageChild((Widget) rowcol); return; } RC_SetBeingArmed(parent_menu, False); } PostMenuShell((XmMenuShellWidget) w, XtGrabNonexclusive, False); _XmFastExpose((XmManagerWidget) rowcol); /* We always must grab the keyboard for the leaf menu pane */ _XmMenuFocus(XtParent(rowcol), XmMENU_MIDDLE, _time); XtSetKeyboardFocus(XtParent(rowcol), (Widget) rowcol); /* * now highlight the pulldown entry widget that * pulled down the menu */ XmCascadeButtonHighlight ((Widget) cascadebtn, TRUE); break; } } } else { /* popdown anything that is still up */ _XmMenuFocus(w, XmMENU_END, _time); (* (((XmMenuShellClassRec *)(popup-> core.widget_class))->menu_shell_class.popdownEveryone)) ((Widget) popup, NULL, NULL, NULL); if (RC_Type(rowcol) == XmMENU_POPUP) { XtUngrabPointer(w, _time); } } mst->RC_ButtonEventStatus.verified = False; } void _XmPopdown( Widget widget) { register ShellWidget shell_widget = (ShellWidget) widget; if (! XtIsShell(widget)) { XtAppErrorMsg(XtWidgetToApplicationContext(widget), "invalidClass", "xmPopdown", tk_error, MESSAGE3, (String *)NULL, (Cardinal *)NULL); } if (shell_widget->shell.popped_up) { XtGrabKind grab_kind = shell_widget->shell.grab_kind ; XWithdrawWindow( XtDisplay( shell_widget), XtWindow( shell_widget), XScreenNumberOfScreen( XtScreen( shell_widget))) ; if( grab_kind != XtGrabNone ) { _XmRemoveGrab((Widget) shell_widget) ; } shell_widget->shell.popped_up = FALSE ; XtCallCallbacks((Widget) shell_widget, XtNpopdownCallback, (XtPointer) &grab_kind) ; } } /* * actually popdown a menuShell widget, also flag as unmanaged * its menu child and unhighlight the cascadebutton that popped up this * widget */ static void Popdown( XmMenuShellWidget menushell, XEvent *event ) { XmRowColumnWidget rowcol = (XmRowColumnWidget) menushell->composite.children[0]; XmMenuSystemTrait menuSTrait; if (menushell->shell.popped_up) { /* mark the row column as popping down */ RC_SetPoppingDown (rowcol, True); /* XmPopdown ((Widget) menushell); */ /* Since the MenuShell may be popped up spring-loaded, * and since XmPopdown will remove the grab using XtRemoveGrab * if the shell is spring loaded, and since we must use * _XmRemoveGrab because of the modality implementation, we * must use our own version of _XmPopdown to pop down the * MenuShell. */ _XmPopdown( (Widget) menushell) ; menuSTrait = (XmMenuSystemTrait) XmeTraitGet((XtPointer) XtClass((Widget) rowcol), XmQTmenuSystem); menuSTrait -> disarm((Widget) rowcol); ClearTraversalInternal(menushell, event); /* Don't unmanage if we're sharing the menu shell */ if (menushell->composite.num_children == 1) rowcol->core.managed = FALSE; _XmCallRowColumnUnmapCallback((Widget)rowcol, event); /* Restore tear offs - except for Popups 'cause the ungrabs are * yet to occur */ if ((RC_Type(rowcol) != XmMENU_POPUP) || RC_TornOff(rowcol)) _XmRestoreTearOffToToplevelShell((Widget)rowcol, event); } } /* * Popdown all the popup kids of this widget, do bottom to top popdown. */ static void PopdownKids( XmMenuShellWidget menushell, XEvent *event ) { ShellWidget subShell; if ((subShell = (ShellWidget) RC_PopupPosted(menushell->composite.children[0])) != NULL) { Widget rowcol = subShell->composite.children[0]; /* mark this row colum as popping down */ RC_SetPoppingDown (rowcol, True); PopdownKids ((XmMenuShellWidget) subShell, event); Popdown ((XmMenuShellWidget) subShell, event); } } /* * This routine determines if there is an enter event pending which will * just pop back up the row column that is about to be popped down. */ static int SkipPopdown( XmCascadeButtonWidget cascade ) { XEvent event; /* * check if an enter event is pending. */ if (XPending (XtDisplay (cascade))) { XPeekEvent (XtDisplay (cascade), &event); if (event.type == EnterNotify) { XEnterWindowEvent * enterevent = (XEnterWindowEvent *) &event; if (XtWindow (cascade) == enterevent->window) return (TRUE); } } return (FALSE); } /* * event handler for entering on a row column widget. The widget must * be either a pulldown or popup menu. */ /* ARGSUSED */ void _XmEnterRowColumn( Widget widget, XtPointer closure, XEvent *event, Boolean *cont ) { XmRowColumnWidget cascade; Widget cascadebtn; Time _time = _XmGetDefaultTime(widget, event); XmRowColumnWidget rowcol = (XmRowColumnWidget) widget; XmMenuShellWidget menushell = (XmMenuShellWidget) XtParent(rowcol); XEnterWindowEvent *enterevent = (XEnterWindowEvent *) event; /* * Ignore popdown requests if traversal is on, or we're no longer * visible, or we don't have any submenus up. */ if (!_XmGetInDragMode(widget) || (!menushell->shell.popped_up) || (! RC_PopupPosted(rowcol))) return; cascade = (XmRowColumnWidget) ((CompositeWidget) RC_PopupPosted(rowcol))->composite.children[0]; cascadebtn = RC_CascadeBtn (cascade); /* * Make sure that popdown should not be skipped. It should be skipped * if the mouse has moved into the cascadebutton (widget or gadget) * that will just pop it back up again. */ if (XmIsCascadeButtonGadget(cascadebtn) && (cascadebtn == XmObjectAtPoint( (Widget) rowcol, enterevent->x, enterevent->y))) { return; } else if (XmIsCascadeButton(cascadebtn)) { if (SkipPopdown ((XmCascadeButtonWidget) cascadebtn)) return; else { Position x, y; XtTranslateCoords(cascadebtn, 0, 0, &x, &y); if ( (enterevent->x_root >= x) && (enterevent->x_root < (x + XtWidth(cascadebtn))) && (enterevent->y_root >= y) && (enterevent->y_root < (y + XtHeight(cascadebtn)))) { return; } } } _XmMenuFocus(XtParent(rowcol), XmMENU_MIDDLE, _time); PopdownKids (menushell, event); } static void PopdownOne( Widget widget, XEvent *event, String *params, Cardinal *num_params ) { XmMenuShellWidget ms = (XmMenuShellWidget)widget; XmMenuShellWidget tms; XmRowColumnWidget rowcol; /* Find a menu shell, this may be a menushell, rowcol or button */ /* OR (!) a top level shell! */ while (ms && !XtIsShell(ms)) ms = (XmMenuShellWidget)XtParent(ms); if (ms && !XmIsMenuShell(ms)) { _XmDismissTearOff((Widget) ms, (XtPointer) event, (XtPointer) NULL); return; } if (ms == NULL) return; /* Find the toplevel MenuShell */ _XmGetActiveTopLevelMenu (ms->composite.children[0], (Widget *) &rowcol); tms = ms; if (RC_Type(rowcol) == XmMENU_BAR) tms = (XmMenuShellWidget) RC_PopupPosted(rowcol); else if ((RC_Type(rowcol) == XmMENU_POPUP) || ((RC_Type(rowcol) == XmMENU_PULLDOWN) && !XmIsMenuShell(XtParent( rowcol)))) { tms = (XmMenuShellWidget) XtParent(rowcol); if (!XmIsMenuShell(tms)) /* torn! */ { /* if the next pane up is the torn popup, bring down entire menu */ if (rowcol == (XmRowColumnWidget)XtParent( RC_CascadeBtn(ms->composite.children[0]))) ms = tms = (XmMenuShellWidget)RC_ParentShell(rowcol); } } if (ms == tms) (* (((XmMenuShellClassRec *)(ms->core.widget_class))-> menu_shell_class.popdownDone)) (widget, event, params, num_params); else { (* (((XmMenuShellClassRec *)(ms->core.widget_class))-> menu_shell_class.popdownEveryone)) ((Widget) ms, event, params, num_params); } if (event) _XmRecordEvent(event); } /* * Class function used to unpost all menupanes */ /* * popdown this all the popup children, and then me, ie. bottom to top * * called from ChangeManaged and PopdownDone */ /*ARGSUSED*/ static void PopdownEveryone( Widget widget, XEvent *event, String *params, /* unused */ Cardinal *num_params ) /* unused */ { Widget rowcol; XmMenuShellWidget shell = (XmMenuShellWidget) widget; /* Ignore this event, if already processed */ if ( event && !_XmIsEventUnique(event)) return; /* * If only a portion of the hierarchy is being popped down, then * make sure the keyboard grab gets reset to the right window. */ rowcol = shell->composite.children[0]; /* Only reset the focus if the this is not a tear off or the cascaded * submenu's parent is not a tear off. */ if ((RC_Type(rowcol) == XmMENU_PULLDOWN) && XmIsMenuShell(XtParent(rowcol)) && RC_CascadeBtn(rowcol) && XmIsMenuShell(XtParent(XtParent(RC_CascadeBtn(rowcol)))) ) { _XmMenuFocus(XtParent(XtParent(RC_CascadeBtn(rowcol))), XmMENU_MIDDLE, _XmGetDefaultTime(widget, event)); } /* mark this row colum as popping down */ RC_SetPoppingDown (rowcol, True); if (shell->shell.popped_up) { if (XmIsMenuShell(shell)) { _XmFastPopdown((XmMenuShellWidget) widget); XFlush(XtDisplay(widget)); PopdownKids ((XmMenuShellWidget) widget, event); /* do our kids */ Popdown ((XmMenuShellWidget) widget ,event); /* do us */ } else /* torn - don't popdown the popup! */ { PopdownKids ((XmMenuShellWidget) widget, event); /* do our kids */ } } } /* * Class function used to unpost all menupanes. * Note that to catch BSelect in a gadget and popdown correctly, widget param * must be the LEAF node menushell, rowcol, or button. */ static void PopdownDone( Widget widget, XEvent *event, String *params, Cardinal *num_params ) { XmMenuShellWidget ms = (XmMenuShellWidget)widget; XmRowColumnWidget rowcol; XmMenuState mst = _XmGetMenuState((Widget)widget); Time _time = _XmGetDefaultTime(widget, event); XmMenuSystemTrait menuSTrait; /* Ignore this event, if already processed */ if (event && !_XmIsEventUnique(event)) return; /* Find a menu shell, this may be a menushell, rowcol or button */ while (ms && !XmIsMenuShell(ms)) ms = (XmMenuShellWidget)XtParent(ms); if (ms == NULL) return; _XmGetActiveTopLevelMenu (ms->composite.children[0], (Widget *) &rowcol); menuSTrait = (XmMenuSystemTrait) XmeTraitGet((XtPointer) XtClass((Widget) rowcol), XmQTmenuSystem); /* We used to check the event, but not anymore (CUA) */ /* if (event && ((event->xbutton.type==ButtonPress) || */ /* (event->xbutton.type==ButtonRelease))) */ /* { */ /* if ( !(_XmMatchBtnEvent( event, XmIGNORE_EVENTTYPE, */ /* RC_PostButton(rowcol), RC_PostModifiers(rowcol)) || */ /* _XmMatchBSelectEvent(ms->composite.children[0], event )) ) */ /* { */ /* To support menu replay, keep the pointer in sync mode */ /* XAllowEvents(XtDisplay(rowcol), SyncPointer, _time); */ /* return; */ /* } */ /* } */ /* Take menus out of drag mode in either case of click into traversal or * unpost, unless the menu is a torn-off pane. */ if ((RC_Type(rowcol) == XmMENU_BAR) || (RC_Type(rowcol) == XmMENU_OPTION) || XmIsMenuShell(XtParent(rowcol))) { _XmSetInDragMode((Widget) rowcol, False); } /* This is to enable Option and Popup Menus to post on BMenu/BSelect Click. * By this time we know that the button is valid. We need to check * if the post event occured within a short time span. Next we force * the menu into traversal mode. And, of course, we return immediately * without popping down the pane. */ if (RC_popupMenuClick(rowcol) && event && ((event->type == ButtonPress) || (event->type == ButtonRelease)) && ((event->xbutton.time - mst->MS_LastManagedMenuTime) < XtGetMultiClickTime(XtDisplay(ms)))) /* or 150 ms? */ { if (RC_Type(rowcol) == XmMENU_OPTION) { if (!XmProcessTraversal(RC_MemWidget(rowcol), XmTRAVERSE_CURRENT)) XmProcessTraversal(RC_OptionSubMenu(rowcol), XmTRAVERSE_CURRENT); /* To support menu replay, keep the pointer in sync mode */ XAllowEvents(XtDisplay(rowcol), SyncPointer, CurrentTime); return; } else if (!rowcol->manager.highlighted_widget) { XmProcessTraversal((Widget) rowcol, XmTRAVERSE_CURRENT); /* To support menu replay, keep the pointer in sync mode */ XAllowEvents(XtDisplay(rowcol), SyncPointer, CurrentTime); return; } } /* * If in a menubar or popup, get the toplevel menushell * CHECK OPTION MENU esp w/ cascading submenus, JAY! */ if (RC_Type(rowcol) == XmMENU_BAR) { /* It's possible that we got here via an accelerator and the menu * isn't popped up - so ms could be NULL! */ if ( (ms = (XmMenuShellWidget) RC_PopupPosted(rowcol)) == NULL ) return; } else if ((RC_Type(rowcol) == XmMENU_POPUP) || RC_TornOff(rowcol)) ms = (XmMenuShellWidget) XtParent(rowcol); _XmMenuFocus( (Widget) ms, XmMENU_END, _time); if (XmIsMenuShell(ms)) { (* (((XmMenuShellClassRec *)(ms-> core.widget_class))->menu_shell_class.popdownEveryone)) ((Widget) ms, event, params, num_params); } else { (* (((XmMenuShellClassRec *)(RC_ParentShell(rowcol)-> core.widget_class))->menu_shell_class.popdownEveryone)) ((Widget) ms, event, params, num_params); } /* if its a menubar, clean it up and disarm it */ if (RC_Type(rowcol) == XmMENU_BAR) { menuSTrait -> menuBarCleanup((Widget) rowcol); } else if ((RC_Type(rowcol) == XmMENU_POPUP) && RC_TornOff(rowcol)) _XmRestoreTearOffToToplevelShell((Widget)rowcol, event); menuSTrait -> disarm((Widget) rowcol); /* Cleanup grabs */ XtUngrabPointer((Widget) ms, _time); } /*ARGSUSED*/ static void ClearTraversalInternal( XmMenuShellWidget menushell, XEvent *event) /* unused */ { Widget activeChild; XmRowColumnWidget rowcol; rowcol = (XmRowColumnWidget)menushell->composite.children[0]; /* Clean up traversal */ activeChild = rowcol->manager.active_child; if (activeChild) { if (XmIsGadget(activeChild)) { (*((XmLabelGadgetClass)XtClass(activeChild))-> gadget_class.border_unhighlight) (activeChild); } else { (*((XmLabelWidgetClass)XtClass(activeChild))-> primitive_class.border_unhighlight) (activeChild); } } /* * (Internal state update) Clear focus from unposting submenu. */ _XmClearFocusPath((Widget) rowcol); if ((RC_Type(rowcol) != XmMENU_POPUP) && RC_CascadeBtn(rowcol)) { /* We can't discern whether this is in traversal mode (or in traversal mode going into drag mode) so don't do the unhighlighting here. Push it up a level to PushB and ToggleB and ??? */ /* This unhighlight is a dragmode only requirement. */ if (_XmGetInDragMode((Widget)rowcol)) XmCascadeButtonHighlight (RC_CascadeBtn(rowcol), FALSE); RC_PopupPosted (XtParent(RC_CascadeBtn(rowcol))) = FALSE; } } /* * XmRCallProc routine for checking MenuShell render table resources * before setting them to NULL if no value is specified * for both XmNrenderTable and XmNfontList. * If save == msw and offset matches a previously set offset, then the * function has been called twice on same widget and resource offset, thus * resource needs to be set NULL, otherwise leave it alone. */ /* ARGSUSED */ static void CheckSetRenderTables(Widget wid, int offset, XrmValue *value ) { XmMenuShellWidget msw = (XmMenuShellWidget)wid; if (check_set_save == msw) { if ((offset == check_set_offset1) || (offset == check_set_offset2)) { value->addr = NULL; } else if (check_set_offset2 == 0) { check_set_offset2 = offset; value->addr = ((char *)msw + offset); } } else { check_set_offset1 = offset; value->addr = ((char *)msw + offset); check_set_offset2 = 0; } check_set_save = msw; } /**************************************************************** * * Trait method for specify render table * **************************************************************/ static XmFontList GetTable( Widget wid, XtEnum type) { XmMenuShellWidget menu = (XmMenuShellWidget) wid ; switch(type) { case XmLABEL_RENDER_TABLE : return menu->menu_shell.label_font_list ; case XmBUTTON_RENDER_TABLE : return menu->menu_shell.button_font_list ; case XmTEXT_RENDER_TABLE : return menu->menu_shell.default_font_list ; } return NULL ; } /* * Clear traversal in the associated menu hierarchy */ /*ARGSUSED*/ void _XmClearTraversal( Widget wid, XEvent *event, String *params, /* unused */ Cardinal *num_params ) /* unused */ { XmMenuShellWidget ms = (XmMenuShellWidget) wid ; XmRowColumnWidget rowcol; XmMenuState mst = _XmGetMenuState((Widget)wid); if (!_XmIsEventUnique(event)) return; /* if a valid event */ if ((ms->composite.num_children != 0) && (rowcol = (XmRowColumnWidget)ms->composite.children[0]) && (_XmMatchBtnEvent( event, RC_PostEventType(rowcol), RC_PostButton(rowcol), RC_PostModifiers(rowcol)) || (_XmMatchBSelectEvent((Widget)rowcol, event )))) { XButtonPressedEvent *xbutton = (XButtonPressedEvent *) event; /* * if this is the Popup's posting event, ignore it. It is just * being sent to the spring loaded widget by Xt's dispatch * after the post. This may not have been recorded. * * This is also reached through an Mwm edge case. Be sure to * thaw the queue or pointer deadlock occurs. */ if ((RC_Type(rowcol) == XmMENU_POPUP) && (mst->MS_LastManagedMenuTime == xbutton->time)) { XAllowEvents(XtDisplay(ms), SyncPointer, _XmGetDefaultTime(wid, event)); return; } _XmHandleMenuButtonPress (ms->composite.children[0], event); } else XAllowEvents(XtDisplay(ms), SyncPointer, CurrentTime); } /* * Public Routine */ Widget XmCreateMenuShell( Widget parent, char *name, ArgList al, Cardinal ac ) { return (XtCreatePopupShell (name, xmMenuShellWidgetClass, parent, al, ac)); } static void Destroy( Widget wid ) { XmMenuShellWidget ms = (XmMenuShellWidget) wid ; _XmDestroyFocusData(ms->menu_shell.focus_data); if (ms->menu_shell.button_font_list != NULL) XmFontListFree (ms->menu_shell.button_font_list); if (ms->menu_shell.label_font_list != NULL) XmFontListFree (ms->menu_shell.label_font_list); /* Clear pointers for renderTable XmRCallProc */ check_set_save = NULL; check_set_offset1 = 0; check_set_offset2 = 0; } static Widget _XmFindPopup( Widget widget, String name) { register Cardinal i; register XrmQuark q; register Widget w; q = XrmStringToQuark(name); for (w=widget; w != NULL; w=w->core.parent) for (i=0; icore.num_popups; i++) if (w->core.popup_list[i]->core.xrm_name == q) return w->core.popup_list[i]; return NULL; } static void _XmMenuPopupAction( Widget widget, XEvent *event, String *params, Cardinal *num_params) { Boolean spring_loaded; register Widget popup_shell; if (*num_params != 1) { XtAppWarningMsg(XtWidgetToApplicationContext(widget), "invalidParameters", "xtMenuPopupAction", tk_error, MESSAGE4, (String *)NULL, (Cardinal *)NULL); return; } if (event->type == ButtonPress) spring_loaded = True; else if (event->type == KeyPress || event->type == EnterNotify) spring_loaded = False; else { XtAppWarningMsg(XtWidgetToApplicationContext(widget), "invalidPopup","unsupportedOperation",tk_error, MESSAGE5, (String *)NULL, (Cardinal *)NULL); spring_loaded = False; } popup_shell = _XmFindPopup(widget, params[0]); if (popup_shell == NULL) { XtAppWarningMsg(XtWidgetToApplicationContext(widget), "invalidPopup","xtMenuPopup",tk_error, MESSAGE6, params, num_params); return; } if (spring_loaded) _XmPopupI(popup_shell, XtGrabExclusive, TRUE); else _XmPopupI(popup_shell, XtGrabNonexclusive, FALSE); } /*ARGSUSED*/ static void _XmMenuPopdownAction( Widget widget, XEvent *event, /* unused */ String *params, Cardinal *num_params) { Widget popup_shell; if (*num_params == 0) { _XmPopdown(widget); } else if (*num_params == 1) { popup_shell = _XmFindPopup(widget, params[0]); if (popup_shell == NULL) { XtAppWarningMsg(XtWidgetToApplicationContext(widget), "invalidPopup","xtMenuPopdown",tk_error, MESSAGE7, params, num_params); return; } _XmPopdown(popup_shell); } else { XtAppWarningMsg(XtWidgetToApplicationContext(widget), "invalidParameters","xtMenuPopdown", tk_error, MESSAGE8, (String *)NULL, (Cardinal *)NULL); } }