/* * @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 * $Log$ * Revision 1.1.3.1 1994/08/01 19:57:51 deblois * no change. * [1994/08/01 19:57:31 deblois] * * moved from ../DataSpace/ * [1994/08/01 19:09:18 deblois] * * Revision 1.3.2.2 1993/07/30 14:02:20 yak * Expanded copyright marker * [1993/07/30 13:44:14 yak] * * Revision 1.3 1992/03/13 18:33:15 devsrc * Converted to ODE * * $EndLog$ */ #ifdef REV_INFO #ifndef lint static char rcsid[] = "$XConsortium: RowCol1_1.c /main/7 1996/10/30 11:17:38 drk $" #endif #endif /* * (c) Copyright 1989, DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASS. */ /* * (c) Copyright 1987, 1988, 1989, 1990, HEWLETT-PACKARD COMPANY */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define UNDEFINED_TYPE -1 #define POST_TIME_OUT 3 /* sec */ * 1000 #define Double(x) ((x) << 1) #define Half(x) ((x) >> 1) #define IsSensitive(r) XtIsSensitive(r) #define IsManaged(w) XtIsManaged(w) #define IsNull(p) ((p) == NULL) #define PackTight(m) (RC_Packing (m) == XmPACK_TIGHT) #define PackColumn(m) (RC_Packing (m) == XmPACK_COLUMN) #define PackNone(m) (RC_Packing (m) == XmPACK_NONE) #define Asking(i) ((i) == 0) #define IsVertical(m) \ (((XmRowColumnWidget) (m))->row_column.orientation == XmVERTICAL) #define IsHorizontal(m) \ (((XmRowColumnWidget) (m))->row_column.orientation == XmHORIZONTAL) #define IsAligned(m) \ (((XmRowColumnWidget) (m))->row_column.do_alignment) #define IsPopup(m) \ (((XmRowColumnWidget) (m))->row_column.type == XmMENU_POPUP) #define IsPulldown(m) \ (((XmRowColumnWidget) (m))->row_column.type == XmMENU_PULLDOWN) #define IsOption(m) \ (((XmRowColumnWidget) (m))->row_column.type == XmMENU_OPTION) #define IsBar(m) \ (((XmRowColumnWidget) (m))->row_column.type == XmMENU_BAR) #define IsWorkArea(m) \ (((XmRowColumnWidget) (m))->row_column.type == XmWORK_AREA) #define IsRadio(m) \ ((((XmRowColumnWidget) (m))->row_column.type == XmWORK_AREA) && \ (((XmRowColumnWidget) (m)->row_column.radio))) #define IsHelp(m,w) ((w) == RC_HelpPb (m)) #define WasManaged(w) \ (((XmRowColumnConstraintRec *) ((w)->core.constraints))-> \ row_column.was_managed) #define BX(b) ((b)->x) #define BY(b) ((b)->y) #define BWidth(b) ((b)->width) #define BHeight(b) ((b)->height) #define BBorder(b) ((b)->border_width) #define SetPosition(b,x,y) { BX (b) = x; BY (b) = y; } #define ChangeMargin(margin,new,sum) \ if ((margin) != new) \ { \ sum += new - (margin); \ (margin) = new; \ } #define ForAllChildren(m, i, q) \ for (i = 0, q = m->composite.children; \ i < m->composite.num_children; \ i++, q++) #define ForManagedChildren(m, i, q) \ for (i = 0, q = m->composite.children; \ i < m->composite.num_children; \ i++, q++) \ \ if (XtIsManaged(*q)) /* Warning Messages */ #define BadWidthMsg \ "Attempt to set width to zero: defaulting to 16" #define BadWidthSVMsg \ "Attempt to set width to zero ignored" #define BadHeightMsg \ "Attempt to set height to zero: defaulting to 16" #define BadHeightSVMsg \ "Attempt to set height to zero ignored" #define BadPopupHelpMsg \ "XmNhelpWidget not used by PopUps: forced to NULL" #define BadPulldownHelpMsg \ "XmNhelpWidget not used by Pulldowns: forced to NULL" #define BadOptionHelpMsg \ "XmNhelpWidget not used by Option menus: forced to NULL" #define BadWorkAreaHelpMsg \ "XmNhelpWidget not used by Work Areas: forced to NULL" #define BadTypeMsg \ "Unknown value of XmNrowColumnType: defaulting to WorkArea" #define BadTypeParentMsg \ "Widget hierarchy not appropriate for this XmNrowColumnType:\n\ defaulting to WorkArea" #define BadTypeSVMsg \ "Attempt to change XmNrowColumnType after initialization: ignored" #define BadOrientationMsg \ "Unknown value of XmNorientation: using default instead" #define BadOrientationSVMsg \ "Attempt to set XmNorientation to unknown value ignored" #define BadPackingMsg \ "Unknown value of XmNpacking: using default instead" #define BadPackingSVMsg \ "Attempt to set XmNpacking to unknown value ignored" #define BadAlignmentMsg \ "Unknown value of XmNentryAlignment: using default instead" #define BadAlignmentSVMsg \ "Attempt to set XmNentryAlignment to unknown value ignored" #define BadMenuBarHomogenousSVMsg \ "Attempt to set XmNisHomogenous to FALSE for a RowColumn widget of type \ XmMENU_BAR ignored" #define BadMenuBarEntryClassSVMsg \ "Attempt to change XmNentryClass for a RowColumn widget of type \ XmMENU_BAR ignored" #define BadPulldownWhichButtonSVMsg \ "Attempt to change XmNwhichButton via XtSetValues for a RowColumn widget \ of type XmMENU_PULLDOWN ignored" #define BadPulldownMenuPostSVMsg \ "Attempt to change XmNmenuPost via XtSetValues for a RowColumn widget \ of type XmMENU_PULLDOWN ignored" #define BadMenuPostMsg \ "Attempt to set XmNpostMenu to an illegal value ignored" #define BadShadowThicknessSVMsg \ "Attempt to change XmNshadowThickness for a RowColumn widget not of type \ XmMENU_PULLDOWN or XmMENU_POPUP ignored" #define BadOptionOrientationSVMsg \ "Attempt to change XmNorientation for a RowColumn widget of type \ XmMENU_OPTION ignored" #define WrongMenuChildMsg \ "Attempt to add wrong type child to a menu (i.e. RowColumn) widget" #define WrongChildMsg \ "Attempt to add wrong type child to a homogeneous RowColumn widget" /* * forward declarations */ /* class support procs */ static Boolean SetValues(); static int AddKid(); static XtGeometryResult QueryGeometry(); static void Redisplay(); static void ManagedSetChanged(); static void ClassInitialize(); static void ClassPartInitialize(); static void Initialize(); static void Resize(); static XtGeometryResult GeometryManager(); static void RemoveChild(); static void Realize(); static void Destroy(); static void ConstraintDestroy(); static void ConstraintInitialize(); static void _XmMenuTraversalHandler(); static void MoveLeft(); static void MoveRight(); static void Move_Up_Down(); static void FindPrevMenuBarCascade(); static void FindNextMenuBarCascade(); static void FindPrevMenuBarItem(); static void FindNextMenuBarItem(); static void DoNothing(); static void PreparePostFromList(); /* action procs */ static void _XmMenuBtnUp(); static void _XmMenuBtnDown(); static void _XmGetGadget(); static void _XmRC_Unmap(); static void _XmRC_Enter(); static void _XmRC_FocusIn(); static void _XmRC_FocusOut(); static void _XmRC_GadgetEscape(); static void _XmRC_GadgetTraverseLeft(); static void _XmRC_GadgetTraverseRight(); static void _XmRC_GadgetTraverseUp(); static void _XmRC_GadgetTraverseDown(); static Boolean ShouldDispatchFocusOut(); static void _XmMenuTraverseLeft(); static void _XmMenuTraverseRight(); static void _XmMenuTraverseUp(); static void _XmMenuTraverseDown(); static void _XmMenuEscape(); static void _XmMenuUnmap(); static void _XmMenuFocusOut(); static void _XmMenuFocusIn(); static void Noop(); void _XmGetActiveTopLevelMenu(); void XmAddToPostFromList (); void XmRemoveFromPostFromList (); static void AddToPostFromList (); static void RemoveFromPostFromList (); /* RowColumnClass methods */ static void PositionMenu(); static void SetCascadeField(); static void ArmAndActivate(); static void MenuPopDown(); static void MenuArm(); static void SetMenuTraversal(); static void MenuDisarm(); static void MenuBarCleanup(); static void MenuProcedureEntry(); /* utility procs */ static void FixEventBindings(); static void FixVisual(); static void FixCallback(); static void LocatePulldown(); static void PreferredSize(); static void ChildsActivateCallback(); static void EntryFired(); static void AdaptToSize(); static void DoMarginAdjustment(); static void SetMenuHistory(); static void Layout(); static void DoProcessMenuTree(); static int _XmMatchInKeyboardList(); static Boolean _XmAllWidgetsAccessible(); static Boolean CheckKey(); static Boolean ProcessKey(); static void GrabKeyOnAssocWidgets (); static int OnPostFromList(); static void GetTopManager(); static void ProcessMenuTree(); static void ProcessSingleWidget(); static void AddToKeyboardList(); static void RemoveFromKeyboardList(); static void KeyboardInputHandler(); static void SetTraversal(); static void UpdateOptionMenuCBG (); static Widget find_first_managed_child(); extern Widget _XmGetTabGroup(); extern Widget _XmFindTopMostShell(); extern void _XmGadgetArm(); extern void _XmGadgetActivate(); extern void _XmGadgetSelect(); /* * event translation tables for a menu widget, we use the parameters to * signal that this widget invoking the action proc is the menu, not a * child of the menu */ static XtTranslations menu_traversal_parsed; #ifndef MCCABE static char menu_traversal_table [] = "osfHelp: Help()\n\ osfLeft: MenuGadgetTraverseLeft()\n\ osfRight: MenuGadgetTraverseRight()\n\ osfUp: MenuGadgetTraverseUp()\n\ osfDown: MenuGadgetTraverseDown()\n\ : MenuUnmap()\n\ : MenuFocusIn()\n\ : MenuFocusOut()\n\ Normal: MenuEnter()"; #else static char menu_traversal_table []; #endif static XtTranslations option_parsed; #ifndef MCCABE static char option_table [] = "osfSelect: ManagerGadgetSelect()\n\ osfActivate: ManagerGadgetSelect()\n\ osfHelp: Help()\n\ ~Shift ~Meta ~AltReturn: ManagerGadgetSelect()\n\ ~Shift ~Meta ~Altspace: ManagerGadgetSelect()\n\ : MenuBtnDown()\n\ : MenuBtnUp()"; #else static char option_table []; #endif static XtTranslations menu_parsed; #ifndef MCCABE static char menu_table [] = "osfSelect: ManagerGadgetSelect()\n\ osfActivate: ManagerGadgetSelect()\n\ osfHelp: Help()\n\ osfCancel: MenuGadgetEscape()\n\ ~Shift ~Meta ~AltReturn: ManagerGadgetSelect() \n\ ~Shift ~Meta ~Altspace: ManagerGadgetSelect() \n\ : MenuBtnDown()\n\ : MenuBtnUp()"; #else static char menu_table []; #endif /* this is used exclusively for the help system */ static XmGadget ActiveGadgetChild = NULL; /* * action binding table for row column widget */ static XtActionsRec action_table [] = { {"Help", (XtActionProc) _XmManagerHelp}, {"MenuBtnDown", (XtActionProc) _XmMenuBtnDown}, {"MenuBtnUp", (XtActionProc) _XmMenuBtnUp}, {"PulldownBtnDown", (XtActionProc) _XmMenuBtnDown}, {"PulldownBtnUp", (XtActionProc) _XmMenuBtnUp}, {"PopupBtnDown", (XtActionProc) _XmMenuBtnDown}, {"PopupBtnUp", (XtActionProc) _XmMenuBtnUp}, {"MenuBarBtnDown", (XtActionProc) _XmMenuBtnDown}, {"MenuBarBtnUp", (XtActionProc) _XmMenuBtnUp}, {"WorkAreaBtnDown", (XtActionProc) _XmGadgetArm}, {"WorkAreaBtnUp", (XtActionProc) _XmGadgetActivate}, /* One step from removal in 1.2 */ {"_MenuGetGadget", (XtActionProc) _XmGetGadget}, {"FocusOut", (XtActionProc) _XmMenuFocusOut}, {"FocusIn", (XtActionProc) _XmMenuFocusIn}, {"Unmap", (XtActionProc) _XmMenuUnmap}, {"Noop", (XtActionProc) Noop}, {"MenuTraverseLeft", (XtActionProc) _XmMenuTraverseLeft}, {"MenuTraverseRight", (XtActionProc) _XmMenuTraverseRight}, {"MenuTraverseUp", (XtActionProc) _XmMenuTraverseUp}, {"MenuTraverseDown", (XtActionProc) _XmMenuTraverseDown}, {"MenuEscape", (XtActionProc) _XmMenuEscape}, {"MenuFocusIn", (XtActionProc) _XmRC_FocusIn}, {"MenuFocusOut", (XtActionProc) _XmRC_FocusOut}, {"MenuUnmap", (XtActionProc) _XmRC_Unmap}, {"MenuEnter", (XtActionProc) _XmRC_Enter}, {"MenuGadgetReturn", (XtActionProc) _XmGadgetSelect}, {"MenuGadgetEscape", (XtActionProc) _XmRC_GadgetEscape}, {"MenuGadgetTraverseLeft", (XtActionProc) _XmRC_GadgetTraverseLeft}, {"MenuGadgetTraverseRight", (XtActionProc) _XmRC_GadgetTraverseRight}, {"MenuGadgetTraverseUp", (XtActionProc) _XmRC_GadgetTraverseUp}, {"MenuGadgetTraverseDown", (XtActionProc) _XmRC_GadgetTraverseDown}, { NULL, NULL} }; /* * define the resourse stuff for a rowcolumn widget */ static int resource_0_int = 0; static Widget resource_0_widget = 0; static Boolean resource_False_boolean = 0; static Boolean resource_True_boolean = 1; static short resource_1_short = 1; static Dimension resource_min_width = 16; /* 'cuz it's the size of */ static Dimension resource_min_height = 16; /* a hot spot... */ static unsigned char resource_type = XmWORK_AREA; static unsigned char resource_alignment = XmALIGNMENT_BEGINNING; static unsigned char resource_packing = XmNO_PACKING; static unsigned char resource_orient = XmNO_ORIENTATION; static XtResource resources[] = { { XmNresizeWidth, XmCResizeWidth, XmRBoolean, sizeof(Boolean), XtOffset(XmRowColumnWidget, row_column.resize_width), XmRImmediate, (caddr_t) TRUE }, { XmNresizeHeight, XmCResizeHeight, XmRBoolean, sizeof(Boolean), XtOffset(XmRowColumnWidget, row_column.resize_height), XmRImmediate, (caddr_t) TRUE }, { XmNwhichButton, XmCWhichButton, XmRWhichButton, sizeof(unsigned int), XtOffset(XmRowColumnWidget, row_column.postButton), XmRImmediate, (caddr_t) -1, }, { XmNmenuPost, XmCMenuPost, XmRString, sizeof(String), XtOffset(XmRowColumnWidget, row_column.menuPost), XmRString, NULL, }, { XmNadjustLast, XmCAdjustLast, XmRBoolean, sizeof(Boolean), XtOffset(XmRowColumnWidget, row_column.adjust_last), XmRImmediate, (caddr_t) TRUE, }, { XmNmarginWidth, XmCMarginWidth, XmRHorizontalDimension, sizeof (Dimension), XtOffset (XmRowColumnWidget, row_column.margin_width), XmRImmediate, (caddr_t) XmINVALID_DIMENSION }, { XmNmarginHeight, XmCMarginHeight, XmRVerticalDimension, sizeof (Dimension), XtOffset (XmRowColumnWidget, row_column.margin_height), XmRImmediate, (caddr_t) XmINVALID_DIMENSION }, { XmNentryCallback, XmCCallback, XmRCallback, sizeof (caddr_t), XtOffset (XmRowColumnWidget, row_column.entry_callback), XmRCallback, NULL }, { XmNmapCallback, XmCCallback, XmRCallback, sizeof (caddr_t), XtOffset (XmRowColumnWidget, row_column.map_callback), XmRCallback, NULL }, { XmNunmapCallback, XmCCallback, XmRCallback, sizeof (caddr_t), XtOffset (XmRowColumnWidget, row_column.unmap_callback), XmRCallback, NULL }, { XmNorientation, XmCOrientation, XmROrientation, sizeof(unsigned char), XtOffset (XmRowColumnWidget, row_column.orientation), XmROrientation, (caddr_t) &resource_orient }, { XmNspacing, XmCSpacing, XmRHorizontalDimension, sizeof(Dimension), XtOffset (XmRowColumnWidget, row_column.spacing), XmRImmediate, (caddr_t) XmINVALID_DIMENSION }, { XmNentryBorder, /* border width of all the */ XmCEntryBorder, /* entries, always uniform */ XmRHorizontalDimension, sizeof(Dimension), XtOffset (XmRowColumnWidget, row_column.entry_border), XmRImmediate, (caddr_t) 0 }, { XmNisAligned, /* T/F, do all entrys have */ XmCIsAligned, /* same alignment */ XmRBoolean, sizeof(Boolean), XtOffset(XmRowColumnWidget, row_column.do_alignment), XmRBoolean, (caddr_t) &resource_True_boolean }, { XmNentryAlignment, /* how entries are to be */ XmCAlignment, /* aligned */ XmRAlignment, sizeof(unsigned char), XtOffset(XmRowColumnWidget, row_column.entry_alignment), XmRAlignment, (caddr_t) &resource_alignment }, { XmNadjustMargin, /* should all entries have */ XmCAdjustMargin, /* the same label margins */ XmRBoolean, sizeof(Boolean), XtOffset(XmRowColumnWidget, row_column.adjust_margin), XmRBoolean, (caddr_t) &resource_True_boolean }, { XmNpacking, /* how to pack menu entries */ XmCPacking, /* Tight, Column, None */ XmRPacking, sizeof (unsigned char), XtOffset(XmRowColumnWidget, row_column.packing), XmRPacking, (caddr_t) &resource_packing }, { XmNnumColumns, /* if packing columnar then */ XmCNumColumns, /* this is how many */ XmRShort, sizeof (short), XtOffset(XmRowColumnWidget, row_column.num_columns), XmRShort, (caddr_t) &resource_1_short }, { XmNradioBehavior, /* should the menu enforce */ XmCRadioBehavior, /* toggle button exclusivity, */ XmRBoolean, /* ie, radio buttons */ sizeof (Boolean), XtOffset(XmRowColumnWidget, row_column.radio), XmRBoolean, (caddr_t) &resource_False_boolean }, { XmNradioAlwaysOne, /* should there always be one */ XmCRadioAlwaysOne, /* radio button on. */ XmRBoolean, sizeof (Boolean), XtOffset(XmRowColumnWidget, row_column.radio_one), XmRBoolean, (caddr_t) &resource_True_boolean }, { XmNisHomogeneous, /* should we enforce the */ XmCIsHomogeneous, /* rule that only one type of */ XmRBoolean, /* entry is allow in the menu */ sizeof (Boolean), XtOffset(XmRowColumnWidget, row_column.homogeneous), XmRBoolean, (caddr_t) &resource_False_boolean }, { XmNentryClass, /* if enforcing homogeneous */ XmCEntryClass, /* menu, this tells the class */ XmRInt, sizeof (WidgetClass), XtOffset(XmRowColumnWidget, row_column.entry_class), XmRWidgetClass, (caddr_t) NULL }, { XmNrowColumnType, /* warning - non-standard resource */ XmCRowColumnType, XmRRowColumnType, sizeof(unsigned char), XtOffset (XmRowColumnWidget, row_column.type), XmRRowColumnType, (caddr_t) &resource_type }, { XmNmenuHelpWidget, /* which widget is the help */ XmCMenuWidget, /* widget */ XmRMenuWidget, sizeof (Widget), XtOffset (XmRowColumnWidget, row_column.help_pushbutton), XmRMenuWidget, (caddr_t) &resource_0_widget }, { XmNlabelString, /* option menus have a label */ XmCString, XmRXmString, sizeof(_XmString), XtOffset (XmRowColumnWidget, row_column.option_label), XmRImmediate, (caddr_t)NULL }, { XmNsubMenuId, /* option menus have built-in */ XmCMenuWidget, /* submenu */ XmRMenuWidget, sizeof (Widget), XtOffset (XmRowColumnWidget, row_column.option_submenu), XmRMenuWidget, (caddr_t) &resource_0_widget }, { XmNmenuHistory, /* pretend a subwidget fired */ XmCMenuWidget, /* off, used to pre-load the */ XmRMenuWidget, /* option menu and popup menu */ sizeof (Widget), /* mouse/muscle memory */ XtOffset (XmRowColumnWidget, row_column.memory_subwidget), XmRMenuWidget, (caddr_t) &resource_0_widget }, { XmNpopupEnabled, /* are accelerator enabled */ XmCPopupEnabled, /* in the popup menu? */ XmRBoolean, sizeof (Boolean), XtOffset(XmRowColumnWidget, row_column.popup_enabled), XmRBoolean, (caddr_t) &resource_True_boolean }, { XmNmenuAccelerator, /* popup menu accelerator */ XmCAccelerators, XmRString, sizeof (char *), XtOffset(XmRowColumnWidget, row_column.menu_accelerator), XmRString, (caddr_t) "" }, { XmNmnemonic, /* option menu mnemonic */ XmCMnemonic, XmRKeySym, sizeof (KeySym), XtOffset(XmRowColumnWidget, row_column.mnemonic), XmRImmediate, (caddr_t) NULL }, { XmNmnemonicCharSet, XmCMnemonicCharSet, XmRString, sizeof(XmStringCharSet), XtOffset(XmRowColumnWidget,row_column.mnemonicCharSet), XmRImmediate, (caddr_t) XmSTRING_DEFAULT_CHARSET }, { XmNshadowThickness, XmCShadowThickness, XmRHorizontalDimension, sizeof (Dimension), XtOffset (XmRowColumnWidget, manager.shadow_thickness), XmRImmediate, (caddr_t) XmINVALID_DIMENSION }, { XmNpostFromList, XmCPostFromList, XmRWidgetList, sizeof (Widget *), XtOffset (XmRowColumnWidget, row_column.postFromList), XmRWidgetList, (caddr_t) NULL, }, { XmNpostFromCount, XmCPostFromCount, XmRInt, sizeof (int), XtOffset (XmRowColumnWidget, row_column.postFromCount), XmRImmediate, (caddr_t) -1 }, { XmNnavigationType, XmCNavigationType, XmRNavigationType, sizeof (unsigned char), XtOffset(XmManagerWidget, manager.navigation_type), XmRImmediate, (caddr_t) XmTAB_GROUP, }, }; static XmSyntheticResource get_resources[] = { { XmNspacing, sizeof(Dimension), XtOffset(XmRowColumnWidget,row_column.spacing), _XmFromHorizontalPixels, _XmToHorizontalPixels, }, { XmNmarginHeight, sizeof(Dimension), XtOffset (XmRowColumnWidget, row_column.margin_height), _XmFromVerticalPixels, _XmToVerticalPixels, }, { XmNmarginWidth, sizeof(Dimension), XtOffset (XmRowColumnWidget, row_column.margin_width), _XmFromHorizontalPixels, _XmToHorizontalPixels, }, { XmNentryBorder, sizeof(Dimension), XtOffset (XmRowColumnWidget, row_column.entry_border), _XmFromHorizontalPixels, _XmToHorizontalPixels, }, }; /* * static initialization of the row column widget class record, must do * each field */ externaldef(xmrowcolumnclassrec) XmRowColumnClassRec xmRowColumnClassRec = { { /* core class record */ (WidgetClass)&xmManagerClassRec, /* superclass ptr */ "XmRowColumn", /* class_name */ sizeof (XmRowColumnWidgetRec), /* size of widget instance */ ClassInitialize, /* class init proc */ ClassPartInitialize, /* class part init */ FALSE, /* class is not init'ed */ Initialize, /* widget init proc*/ NULL, /* init_hook proc */ Realize, /* widget realize proc */ action_table, /* class action table */ XtNumber (action_table), resources, /* this class's resource list */ XtNumber (resources), /* " " resource_count */ NULLQUARK, /* xrm_class */ TRUE, /* don't compress motion */ XtExposeCompressMaximal, /* do compress exposure */ FALSE, /* don't compress enter-leave */ FALSE, /* no VisibilityNotify */ Destroy, /* class destroy proc */ Resize, /* class resize proc */ Redisplay, /* class expose proc */ SetValues, /* class set_value proc */ NULL, /* set_value_hook proc */ XtInheritSetValuesAlmost, /* set_value_almost proc */ NULL, /* get_values_hook */ NULL, /* class accept focus proc */ XtVersion, /* current version */ NULL, /* callback offset list */ NULL, /* translation table */ QueryGeometry, /* query geo proc */ NULL, /* display accelerator */ NULL, /* extension */ }, { /* composite class record */ GeometryManager, /* childrens geo mgr proc */ ManagedSetChanged, /* set changed proc */ (XtArgsProc)AddKid, /* add a child */ RemoveChild, /* remove a child */ NULL, /* extension */ }, { /* constraint class record */ NULL, /* constraint resources */ 0, /* constraint resource_count */ sizeof(XmRowColumnConstraintRec), /* constraint_size */ ConstraintInitialize, /* initialize */ ConstraintDestroy, /* destroy */ NULL, /* set_values */ NULL, /* extension */ }, { /* manager class record */ XtInheritTranslations, /* translations */ get_resources, /* syn_resources */ XtNumber(get_resources), /* num_syn_resources */ NULL, /* syn_constraint_resources */ 0, /* num_syn_constraint_resources */ XmInheritParentProcess, /* parent_process */ NULL, /* extension */ }, { /* row column class record */ MenuProcedureEntry, /* proc to interface with menu widgets */ ArmAndActivate, /* proc to arm&activate menu */ _XmMenuTraversalHandler, /* traversal handler */ NULL, /* extension */ } }; /* * now make a public symbol that points to this class record */ externaldef(xmrowcolumnwidgetclass) WidgetClass xmRowColumnWidgetClass = (WidgetClass) &xmRowColumnClassRec; static Widget lastSelectToplevel = NULL; /* REQUIRES FIX FOR MULTI SCREEN!!! */ XmButtonEventStatusRec _XmButtonEventStatus = {-1,FALSE,TRUE, NULL}; /*ARGSUSED*/ static XtTimerCallbackProc PostTimeOut (popup, id) XmRowColumnWidget popup; XtIntervalId *id; { if (_XmButtonEventStatus.waiting_to_be_managed) MenuPopDown (popup, &_XmButtonEventStatus.event); _XmButtonEventStatus.waiting_to_be_managed = FALSE; } /* * ButtonEventHandler is inserted at the head of the event handlers. We must * pre-verify the events that popup a menupane. When the application manages * the popup menupane, MenuShell's managed_set_changed(), checks the * verification. */ /* ARGSUSED */ static void ButtonEventHandler(w, popup, event) Widget w; XmRowColumnWidget popup; XEvent * event; { XButtonEvent *xbutton_event = (XButtonEvent *)event; _XmButtonEventStatus.time = xbutton_event->time; _XmButtonEventStatus.verified = _XmMatchBtnEvent( event, RC_PostEventType(popup), RC_PostButton(popup), RC_PostModifiers(popup)); if (xbutton_event->type == ButtonPress) { _XmButtonEventStatus.waiting_to_be_managed = TRUE; XtAppAddTimeOut(XtWidgetToApplicationContext(popup), (unsigned long) POST_TIME_OUT, (XtTimerCallbackProc) PostTimeOut, (caddr_t) popup); _XmButtonEventStatus.event = *xbutton_event; } } static void AddHandlersToPostFromWidget (popup, widget) Widget popup; Widget widget; { Cursor cursor; cursor = XmGetMenuCursor(XtDisplay(popup)); XtInsertEventHandler(widget, ButtonPressMask|ButtonReleaseMask, False, ButtonEventHandler, popup, XtListHead); XtAddEventHandler(widget, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, popup); /* * Add an event handler on the associated widget for ButtonRelease * events. This is so that a quick press/release pair does not get * lost if the release is processed before our pointer grab is made. * This will guarantee that the associated widget gets the button * release event; it would be discarded if the widget was not selecting * for button release events. */ XtAddEventHandler(widget, ButtonReleaseMask, False, DoNothing, NULL); /* * Must add a passive grab, so that owner_events is set to True * when the button grab is activated; this is so that enter/leave * events get dispatched by the server to the client. */ XtGrabButton (widget, RC_PostButton(popup), RC_PostModifiers(popup), TRUE, ButtonReleaseMask, GrabModeSync, GrabModeSync, None, cursor); } static void RemoveHandlersFromPostFromWidget (popup, widget) Widget popup; Widget widget; { XtRemoveEventHandler(widget, ButtonPressMask|ButtonReleaseMask, False, ButtonEventHandler, popup); XtRemoveEventHandler(widget, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, popup); XtRemoveEventHandler(widget, ButtonReleaseMask, False, DoNothing, NULL); /* Remove our passive grab */ XtUngrabButton (widget, RC_PostButton(popup), AnyModifier); } /* * Add the Popup Menu Event Handlers needed for posting and accelerators */ static void AddPopupEventHandlers (pane) XmRowColumnWidget pane; { int i; /* to myself for gadgets */ XtAddEventHandler(pane, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, pane); /* Add to Our shell parent */ XtAddEventHandler(XtParent(pane), KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, pane); /* add to all of the widgets in the postFromList*/ for (i=0; i < pane->row_column.postFromCount; i++) { AddHandlersToPostFromWidget (pane, pane->row_column.postFromList[i]); } } /* * Remove the Popup Menu Event Handlers needed for posting and accelerators */ static void RemovePopupEventHandlers (pane) XmRowColumnWidget pane; { int i; /* Remove it from us */ XtRemoveEventHandler(pane, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, pane); /* Remove it from our shell parent */ XtRemoveEventHandler(XtParent(pane), KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, pane); /* Remove it from the postFrom widgets */ for (i=0; i < pane->row_column.postFromCount; i++) { RemoveHandlersFromPostFromWidget (pane, pane->row_column.postFromList[i]); } } /* * Destroy the widget, and any subwidgets there are */ static void Destroy (m) XmRowColumnWidget m; { Widget topManager; int i; XtRemoveAllCallbacks (m, XmNentryCallback); XtRemoveAllCallbacks (m, XmNmapCallback); XtRemoveAllCallbacks (m, XmNunmapCallback); XtFree(MGR_KeyboardList(m)); /* * If we had added any event handlers for processing accelerators or * mnemonics, then we must remove them now. */ if (IsPopup(m)) { if (RC_PopupEnabled(m)) RemovePopupEventHandlers (m); } else if (IsOption(m) || IsBar(m)) { /* Remove it from the associated widget */ GetTopManager (m, &topManager); XtRemoveEventHandler(topManager, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, m); /* Remove it from us */ XtRemoveEventHandler(m, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, m); } /* * If we're still connected to a cascade button, then we need to break * that link, so that the cascade button doesn't attempt to reference * us again, and also so that accelerators and mnemonics can be cleared up. */ else { Arg args[1]; for (i=0; i < m->row_column.postFromCount; i++) { if (! m->row_column.postFromList[i]->core.being_destroyed) { XtSetArg (args[0], XmNsubMenuId, NULL); XtSetValues (m->row_column.postFromList[i], args, 1); } } } /* free allocated postFromList for popups and pulldowns */ if (IsPopup(m) || IsPulldown(m)) XtFree (m->row_column.postFromList); if ((IsPopup(m) && RC_PopupEnabled(m)) || (IsBar(m) && RC_MenuAccelerator(m)) || (IsOption(m) && RC_Mnemonic(m))) { Cardinal num_children; /* * By the time we reach here, our children are destroyed, but * the children's list is bogus; so we need to temporarily zero * out our num_children field, so DoProcessMenuTree() will not * attempt to process our children. */ num_children = m->composite.num_children; m->composite.num_children = 0; DoProcessMenuTree(m, XmDELETE); m->composite.num_children = num_children; } } /* * Destroy any keyboard grabs/entries for the child */ static void ConstraintDestroy (w) Widget w; { if (!XtIsRectObj(w)) return; DoProcessMenuTree(w, XmDELETE); } /* * do all the stuff needed to make a subwidget of a menu work correctly */ static int fix_widget (m, w) XmRowColumnWidget m; Widget w; { /* * now patchup the event binding table for the subwidget so that * it acts the way we want it to */ FixEventBindings (m, w); /* * and patch the visual aspects of the subwidget */ FixVisual (m, w); /* * and patch the callback list so that we will be called whenever * he fires off */ FixCallback (m, w); } /* * Add a child to this row column widget */ static int AddKid (w) Widget w; { XmRowColumnWidget m = (XmRowColumnWidget) XtParent(w); if (!IsWorkArea(m) /* it's a menu */ && (XtClass(w) != xmLabelGadgetClass) && (XtIsRectObj(w)) && !XmIsPushButtonGadget(w) && !XmIsCascadeButtonGadget(w) && !XmIsToggleButtonGadget(w) && !XmIsSeparatorGadget(w) && (XtClass(w) != xmLabelWidgetClass) && !XmIsPushButton(w) && !XmIsCascadeButton(w) && !XmIsToggleButton(w) && !XmIsSeparator(w)) _XmWarning(m,WrongMenuChildMsg); /* * if the rowcolumn is homogeneous, make sure that class matches * the entry class. Two exceptions are made: 1) if the entry class is * CascadeButton or CascadeButtonGadget, either of those classes are * allowed. 2) if the entry class is ToggleButton or ToggleButtonGadget, * either of those classes are allowed. */ if (XtIsRectObj(w) && RC_IsHomogeneous(m) && (RC_EntryClass(m) != XtClass(w))) { if (! ((RC_EntryClass(m) == xmCascadeButtonWidgetClass && XmIsCascadeButtonGadget(w)) || (RC_EntryClass(m) == xmCascadeButtonGadgetClass && XmIsCascadeButton(w)) || (RC_EntryClass(m) == xmToggleButtonGadgetClass && XmIsToggleButton(w)) || (RC_EntryClass(m) == xmToggleButtonWidgetClass && XmIsToggleButtonGadget(w)))) { _XmWarning (m, WrongChildMsg); } } /* * use composite class insert proc to do all the dirty work */ (*((XmManagerWidgetClass)xmManagerWidgetClass)->composite_class. insert_child) (w); /* * now change the subwidget so that it acts the way we want it to */ fix_widget (m, w); return (TRUE); } /* * delete a single widget from a parent widget */ static void RemoveChild (child) Widget child; { XmRowColumnWidget m = (XmRowColumnWidget) XtParent(child); if (child == RC_HelpPb (m)) RC_HelpPb (m) = NULL; else if (child == RC_MemWidget(m)) { RC_MemWidget(m) = NULL; } /* * If this child is in a top level menupane, then we want to remove * the event handler we added for catching keyboard input. */ if (XtIsWidget(child) && ((IsPopup(m) || IsBar(m) || IsPulldown(m)) && XmIsLabel(child) && (child->core.widget_class != xmLabelWidgetClass))) { XtRemoveEventHandler(child, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, m); } /* * use composite class insert proc to do all the dirty work */ (*((CompositeWidgetClass)compositeWidgetClass)->composite_class. delete_child) (child); } /* * The set of our managed children changed, so maybe change the size of the * row column widget to fit them; there is no instigator of this change, and * ignore any dimensional misfit of the row column widget and the entries, * which is a result of our geometry mgr being nasty. Get it laid out. */ static void ManagedSetChanged (m) XmRowColumnWidget m; { Widget *q; int i; Dimension w = 0; Dimension h = 0; Boolean any_changed = FALSE; /* * We have to manage the "was_managed" field of the * constraint record. */ ForAllChildren(m, i, q) { if (WasManaged(*q) != IsManaged(*q)) { any_changed = TRUE; } WasManaged(*q) = IsManaged(*q); } if (!any_changed) { /* Must have been a popup child -- we don't really care */ return; } DoMarginAdjustment (m); /* * find out what size we need to be with the current set of kids */ PreferredSize (m, &w, &h); /* * now decide if the menu needs to change size */ if ((w != XtWidth (m)) || (h != XtHeight (m))) { XtWidgetGeometry menu_desired, menu_allowed; menu_desired.request_mode = 0; if (w != XtWidth (m)) { menu_desired.width = w; menu_desired.request_mode |= CWWidth; } if (h != XtHeight (m)) { menu_desired.height = h; menu_desired.request_mode |= CWHeight; } XtMakeGeometryRequest(m,&menu_desired,&menu_allowed); } /* * if we get to here the row column widget has been changed and his * window has been resized, so effectively we need to do a Resize. */ AdaptToSize (m, NULL, NULL); /* Clear shadow if necessary. */ if (m->row_column.old_shadow_thickness) _XmClearShadowType (m, m->row_column.old_width, m->row_column.old_height, m->row_column.old_shadow_thickness, 0); m->row_column.old_width = m->core.width; m->row_column.old_height = m->core.height; m->row_column.old_shadow_thickness = m->manager.shadow_thickness; _XmNavigChangeManaged(m); } /* * make the row column widget appear */ static void Realize (m, window_mask, window_attributes) XmRowColumnWidget m; Mask *window_mask; XSetWindowAttributes *window_attributes; { if (IsOption(m)) { XmRowColumnWidget sm = (XmRowColumnWidget) RC_OptionSubMenu(m); int i; Dimension w=0, h=0; if (!IsNull(sm)) { /* if there is no memory widget, set it up */ if (!RC_MemWidget(m)) { if (!RC_MemWidget(sm)) { /* submenu does not have a memory widget */ for(i = 0;i < sm->composite.num_children; i++) { if (XtIsManaged(sm->composite.children[i])) { /* choose first managed child as memory widget */ RC_MemWidget(m) = sm->composite.children[i]; RC_MemWidget(sm) = RC_MemWidget(m); break; } } } else /* use submenu's memory widget */ RC_MemWidget(m) = RC_MemWidget(sm); /* update option menu label */ if ( RC_MemWidget(m) ) /* in case it doesn't exist! */ { for (i = 0; i < m->composite.num_children; i++) { if (XmIsCascadeButtonGadget(m->composite.children[i])) { UpdateOptionMenuCBG (m->composite.children[i], RC_MemWidget(m)); break; } } } } /* find out what size we need to be */ PreferredSize (m, &w, &h); /* now decide if the menu needs to change size */ if ((w != XtWidth (m)) || (h != XtHeight (m))) { XtWidgetGeometry menu_desired, menu_allowed; menu_desired.request_mode = 0; if (w != XtWidth (m)) { menu_desired.width = w; menu_desired.request_mode |= CWWidth; } if (h != XtHeight (m)) { menu_desired.height = h; menu_desired.request_mode |= CWHeight; } XtMakeGeometryRequest(m, &menu_desired,&menu_allowed); } AdaptToSize (m, NULL, NULL); } } /* fix menu window so that any button down is OwnerEvent true. */ if (!IsWorkArea(m)) { /* * Originally, we simply set the OwnerGrabButtonMask in our * event mask. Unfortunately, if the application ever modifies * our translations or adds an event handler which caused the * intrinsics to regenerate our X event mask, this bit was * lost. So .. we add a dummy event handler for this mask bit, * thus guaranteeing that it is always part of our event mask. */ window_attributes->event_mask |= OwnerGrabButtonMask; XtAddEventHandler(m, OwnerGrabButtonMask, False, Noop, NULL); } /* * Don't propagate events for row column widgets * and set bit gravity to NW */ (*window_mask) |= CWDontPropagate | CWBitGravity; window_attributes->bit_gravity = NorthWestGravity; window_attributes->do_not_propagate_mask = ButtonPressMask| ButtonReleaseMask|KeyPressMask|KeyReleaseMask|PointerMotionMask; XtCreateWindow ( m, InputOutput, CopyFromParent, *window_mask, window_attributes); /* * Keep menus which are a child of shell widgets mapped at all times. * Mapping is now done by the menu shell widget. */ if (XmIsMenuShell (XtParent(m))) m->core.mapped_when_managed = FALSE; } /* * utilities for setvalue procs */ static Boolean do_entry_stuff (old, new) XmRowColumnWidget old, new; { XtWidgetGeometry desired; Boolean need_expose = FALSE; if ((RC_EntryBorder (old) != RC_EntryBorder (new)) && (RC_EntryBorder(new))) { Widget *p; int i; desired.request_mode = CWBorderWidth; desired.border_width = RC_EntryBorder (new); ForAllChildren (new, i, p) { _XmConfigureObject(*p,(*p)->core.x,(*p)->core.y, (*p)->core.width, (*p)->core.height, desired.border_width); } need_expose = TRUE; } if ((RC_EntryAlignment (old) != RC_EntryAlignment (new)) && (IsAligned (new)) && (!IsOption(new))) { Widget *p; Arg al[2]; int i; XtSetArg (al[0], XmNalignment, RC_EntryAlignment(new)); ForAllChildren (new, i, p) { XtSetValues (*p, al, 1); } need_expose = TRUE; } return (need_expose); } static void do_size (old, new) XmRowColumnWidget old, new; { Widget *p; int i; int orient = RC_Orientation (old) != RC_Orientation (new); Dimension w; Dimension h; if (orient) /* flip all the separator */ { /* widgets too */ Arg al[2]; int ac = 0; XtSetArg (al[ac], XmNorientation, (IsVertical (new) ? XmHORIZONTAL : XmVERTICAL)); ForAllChildren (new, i, p) { if (XmIsSeparator(*p) || XmIsSeparatorGadget(*p)) XtSetValues (*p, al, 1); } } if ((!XtWidth(new)) || (XtWidth (new) != XtWidth(old)) || (!XtHeight(new)) || (XtHeight (new) != XtHeight(old)) || (orient || ((IsPopup(new) || IsPulldown(new) || IsBar(new)) && (MGR_ShadowThickness(new) != MGR_ShadowThickness(old))) || (RC_EntryBorder (old) != RC_EntryBorder (new)) || (RC_MarginW (old) != RC_MarginW (new)) || (RC_MarginH (old) != RC_MarginH (new)) || (RC_Spacing (old) != RC_Spacing (new)) || (RC_Packing (old) != RC_Packing (new)) || (RC_NCol (old) != RC_NCol (new)) || (RC_AdjLast (old) != RC_AdjLast (new)) || (RC_AdjMargin (old) != RC_AdjMargin (new)) || (RC_HelpPb (old) != RC_HelpPb (new)))) { if (!RC_ResizeWidth(new) && RC_ResizeHeight(new)) { w = new->core.width; h = 0; } else if (RC_ResizeWidth(new) && !RC_ResizeHeight(new)) { w = 0; h = new->core.height; } else if (RC_ResizeWidth(new) && RC_ResizeHeight(new)) { w = 0; h = 0; } else { AdaptToSize(new,NULL,NULL); return; } PreferredSize (new, &w, &h); AdaptToSize(new,NULL,NULL); XtWidth(new) = w; XtHeight(new) = h; } } static Boolean set_values_non_popup (old, new) XmRowColumnWidget old; /* the old state widget */ XmRowColumnWidget new; /* the new, real widget */ { Widget child; Arg args[4]; int n; Boolean need_expose = FALSE; if (IsBar(new)) new->manager.traversal_on = old->manager.traversal_on; /* fdt : should this only be done for a menubar?? */ need_expose |= RC_HelpPb (old) != RC_HelpPb (new); /* * If we are an option menu, then we must check to see if our mnemonic * has changed. If we're a menubar, then see if our accelerator has * changed. */ if (IsOption(new)) { if (RC_OptionSubMenu(new) != RC_OptionSubMenu(old)) { XtSetArg(args[0], XmNsubMenuId, RC_OptionSubMenu(new)); if (child = XmOptionButtonGadget(new)) XtSetValues(child, args, 1); if (child = find_first_managed_child(RC_OptionSubMenu(new), FIRST_BUTTON)) { RC_MemWidget (new) = child; } } if (RC_MemWidget (old) != RC_MemWidget (new)) SetMenuHistory (new, RC_MemWidget (new)); n = 0; if (RC_OptionLabel(new) != RC_OptionLabel(old)) { XtSetArg(args[n], XmNlabelString, RC_OptionLabel(new)); n++; XtSetArg(args[n], XmNlabelType, XmSTRING); n++; } if ((RC_Mnemonic(new) != RC_Mnemonic(old)) || (RC_MnemonicCharSet(new) != RC_MnemonicCharSet(old))) { XtSetArg(args[n], XmNmnemonic, RC_Mnemonic(new)); n++; XtSetArg(args[n], XmNmnemonicCharSet, RC_MnemonicCharSet(new)); n++; } if (n && (child = XmOptionLabelGadget(new))) XtSetValues(child, args, n); DoProcessMenuTree(new, XmREPLACE); } else if (IsBar(new) && (RC_MenuAccelerator(new) != RC_MenuAccelerator(old))) { if (RC_MenuAccelerator(new)) { RC_MenuAccelerator(new) = (String)strcpy(XtMalloc( XmStrlen( RC_MenuAccelerator(new)) + 1), RC_MenuAccelerator(new)); } DoProcessMenuTree(new, XmREPLACE); if (RC_MenuAccelerator(old)) XtFree(RC_MenuAccelerator(old)); } /* * Moved here in case Option Menu geometry changed */ need_expose |= do_entry_stuff (old, new); do_size (old, new); return (need_expose); } static Boolean set_values_popup (old, new) XmRowColumnWidget old; /* the old state widget */ XmRowColumnWidget new; /* the new, real widget */ { int need_expose = FALSE; new->manager.traversal_on = old->manager.traversal_on; need_expose |= do_entry_stuff (old, new); do_size (old, new); if ((XtX (old) != XtX (new)) || /* signal the shell that it */ (XtY (old) != XtY (new))) /* had better move itself */ { /* to the menu's location */ RC_SetWidgetMoved (new, TRUE); /* and that it has to move */ RC_SetWindowMoved (new, TRUE); /* the menu's window back */ } /* * If we are a popup menu, then we need to check the * state of the popupEnabled resource; we may need to add or remove the * event handler we use to catch accelerators and mnemonics. */ if (IsPopup(new)) { if (RC_PopupEnabled(new) != RC_PopupEnabled(old)) { if (RC_PopupEnabled(new)) { AddPopupEventHandlers(new); DoProcessMenuTree(new, XmADD); } else { RemovePopupEventHandlers (new); DoProcessMenuTree(new, XmDELETE); } } /* See if our accelerator has changed */ if (RC_PopupEnabled(new) && (RC_MenuAccelerator(new) != RC_MenuAccelerator(old))) { if (RC_MenuAccelerator(new)) { RC_MenuAccelerator(new) = (String)strcpy(XtMalloc( XmStrlen( RC_MenuAccelerator(new)) + 1), RC_MenuAccelerator(new)); } DoProcessMenuTree(new, XmREPLACE); if (RC_MenuAccelerator(old)) XtFree(RC_MenuAccelerator(old)); } } return (need_expose); } /* * Empty event handler, added to the associated widget for a popup menu. * Forces the widget to select for button release events. */ /* ARGSUSED */ static void DoNothing (w, client_data, event) Widget w; caddr_t client_data; XEvent * event; { } static void set_values_passive_grab (old, new) XmRowColumnWidget old; /* the old state widget */ XmRowColumnWidget new; /* the new, real widget */ { int i; Cursor cursor; if (IsPopup(old)) { /* Keep our passive grab up to date. */ if (RC_PopupEnabled(old)) { /* Remove it from the postFrom widgets */ for (i=0; i < old->row_column.postFromCount; i++) { /* Remove our passive grab */ if (XtIsRealized(old->row_column.postFromList[i])) { XtUngrabButton (old->row_column.postFromList[i], RC_PostButton(old), RC_PostModifiers(old)); } } if (RC_PopupEnabled(new)) { cursor = XmGetMenuCursor(XtDisplay(new)); /* add to all of the widgets in the postFromList*/ for (i=0; i < new->row_column.postFromCount; i++) { /* * Must add a passive grab, so that owner_events is * set to True when the button grab is activated * this is so that enter/leave * events get dispatched by the server to the client. */ XtGrabButton (new->row_column.postFromList[i], RC_PostButton(new), RC_PostModifiers(new), TRUE, ButtonReleaseMask, GrabModeSync, GrabModeSync, None, cursor); } } } } } static Boolean SetValues (old, req, new) XmRowColumnWidget old; /* the real widget */ XmRowColumnWidget req; /* after arglist */ XmRowColumnWidget new; /* after superclasses */ { int i; int need_expose = FALSE; if (!XtWidth(req)) { _XmWarning(req,BadWidthSVMsg); XtWidth(req) = 16; } if (!XtHeight(req)) { _XmWarning(req,BadHeightSVMsg); XtHeight(req) = 16; } switch (RC_Orientation(req)) { case XmVERTICAL: case XmHORIZONTAL: break; case XmNO_ORIENTATION: default: _XmWarning(req,BadOrientationSVMsg); RC_Orientation(new) = RC_Orientation(old); break; } switch (RC_Packing(req)) { case XmNO_PACKING: case XmPACK_TIGHT: case XmPACK_COLUMN: case XmPACK_NONE: break; default: _XmWarning(req,BadPackingSVMsg); RC_Packing(new) = RC_Packing(old); break; } if (RC_Type(req) != RC_Type(old)) { /* Type CANNOT be changed after initialization */ _XmWarning(new,BadTypeSVMsg); RC_Type(new) = RC_Type(old); } switch (RC_EntryAlignment(req)) { case XmALIGNMENT_BEGINNING: case XmALIGNMENT_CENTER: case XmALIGNMENT_END: break; default: _XmWarning(req,BadAlignmentSVMsg); RC_EntryAlignment(new) = RC_EntryAlignment(old); break; } if (IsBar(new)) { if (RC_IsHomogeneous(req) != RC_IsHomogeneous(old)) { /* can't change this for menu bars */ _XmWarning(new,BadMenuBarHomogenousSVMsg); RC_IsHomogeneous(new) = TRUE; } if (RC_EntryClass(req) != RC_EntryClass(old)) { /* can't change this for menu bars */ _XmWarning(new,BadMenuBarEntryClassSVMsg); RC_EntryClass(new) = xmCascadeButtonWidgetClass; } } if (RC_MenuPost(new) != RC_MenuPost(old)) { if (IsPulldown(new)) { /* MenuPost cannot be changed via SetValues for Pulldowns */ _XmWarning(new,BadPulldownMenuPostSVMsg); /* just in case WhichButton was set */ RC_PostButton(new) = RC_PostButton(old); } else { if (_XmMapBtnEvent(RC_MenuPost(new), &RC_PostEventType(new), &RC_PostButton(new), &RC_PostModifiers(new)) == FALSE) { _XmWarning(new,BadMenuPostMsg); /* Do Nothing - No change to postButton/Modifiers/EventType */ } else set_values_passive_grab(old, new); } } else /* For backwards compatibility... */ if (RC_PostButton(new) != RC_PostButton(old)) { if (IsPulldown(new)) { /* WhichButton cannot be changed via SetValues for Pulldowns */ _XmWarning(new,BadPulldownWhichButtonSVMsg); RC_PostButton(new) = RC_PostButton(old); } else { RC_PostModifiers(new) = AnyModifier; RC_PostEventType(new) = ButtonPress; set_values_passive_grab(old, new); } } /* * Shadow thickness is forced to zero for all types except * pulldown, popup, and menubar */ if (IsPulldown(new) || IsPopup(new) || IsBar(new)) { if (MGR_ShadowThickness(req) != MGR_ShadowThickness(old)) need_expose |= TRUE; } else if (MGR_ShadowThickness(req) != MGR_ShadowThickness(old)) { _XmWarning(new,BadShadowThicknessSVMsg); MGR_ShadowThickness(new) = 0; } if (IsOption(new) && (RC_Orientation(req) != RC_Orientation(old))) { _XmWarning(new,BadOptionOrientationSVMsg); RC_Orientation(new) = XmHORIZONTAL; } /* postFromList changes, popups and pulldowns only */ if (IsPopup(new) || IsPulldown(new)) { if ((new->row_column.postFromList != old->row_column.postFromList) || (new->row_column.postFromCount != old->row_column.postFromCount)) { /* use temp - postFromCount decremented in RemoveFromPostFromList() */ int cnt; if (old->row_column.postFromList) { cnt = old->row_column.postFromCount; for (i=0; i < cnt; i++) { RemoveHandlersFromPostFromWidget(new, old->row_column.postFromList[i]); } XtFree(old->row_column.postFromList); } PreparePostFromList(new); } } if (IsBar (new) || IsWorkArea (new) || IsOption (new)) need_expose |= set_values_non_popup (old, new); else need_expose |= set_values_popup (old, new); return (need_expose); } static char *GetRealKey(rc, str) XmRowColumnWidget rc; char *str; { KeySym keysym; Modifiers mods; char tmp[128]; char *ks; keysym = XStringToKeysym(str); if (keysym == NoSymbol) return(NULL); _XmVirtualToActualKeysym(XtDisplay(rc), keysym, &keysym, &mods); if (!(ks = XKeysymToString(keysym))) return(NULL); tmp[0] = '\0'; if (mods & ControlMask) strcpy(tmp, "Ctrl "); if (mods & ShiftMask) strcat(tmp, "Shift "); strcat(tmp,""); strcat(tmp, ks); return(XtNewString(tmp)); } static void MenuBarInitialize (bar) XmRowColumnWidget bar; { Widget topManager; RC_IsHomogeneous(bar) = TRUE; RC_EntryClass(bar) = xmCascadeButtonWidgetClass; RC_SetArmed(bar,FALSE); bar->manager.traversal_on = False; bar->row_column.lastSelectToplevel = (Widget) bar; if (RC_PostButton(bar) == -1) RC_PostButton(bar) = Button1; if (RC_Packing(bar) == XmNO_PACKING) RC_Packing(bar) = XmPACK_TIGHT; if (RC_Orientation(bar) == XmNO_ORIENTATION) RC_Orientation(bar) = XmHORIZONTAL; if (RC_Spacing(bar) == XmINVALID_DIMENSION) RC_Spacing(bar) = 0; if (bar->core.tm.translations == NULL) bar->core.tm.translations = menu_parsed; XtOverrideTranslations(bar, menu_traversal_parsed); if (RC_MenuAccelerator(bar) && (*RC_MenuAccelerator(bar) == '\0')) if (!(RC_MenuAccelerator(bar) = GetRealKey(bar, "osfMenuBar"))) RC_MenuAccelerator(bar) = "F10"; /* * Add an event handler to both us and the associated widget; we * need one in case we have gadget children. */ GetTopManager (bar, &topManager); XtAddEventHandler(bar, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, bar); XtAddEventHandler(topManager, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, bar); if (RC_MenuAccelerator(bar)) DoProcessMenuTree(bar, XmADD); } /* * prepare postFromList: if its at its default state, its parent should * be in the list. If a list has been specified but the count has not, * then set the count to 0. This is only useful for Popup and Pulldown panes. */ static void PreparePostFromList(rowcol) XmRowColumnWidget rowcol; { Widget * tempPtr; Boolean forceParent = FALSE; int i; if (rowcol->row_column.postFromCount < 0) { if (IsPopup(rowcol) && rowcol->row_column.postFromList == NULL) { /* default state for popups, set to parent */ rowcol->row_column.postFromCount = 1; forceParent = True; } else /* user provided a list but no count, default count to 0 */ rowcol->row_column.postFromCount = 0; } /* malloc enough space for 1 more addition to the list */ rowcol->row_column.postFromListSize = rowcol->row_column.postFromCount + 1; tempPtr = rowcol->row_column.postFromList; rowcol->row_column.postFromList = (Widget *) XtMalloc (rowcol->row_column.postFromListSize * sizeof(Widget)); if (tempPtr) { /* use temp - postFromCount incremented in AddToPostFromList() */ int cnt = rowcol->row_column.postFromCount; /* reset the postFromCount for correct AddToPostFromList() assignment */ rowcol->row_column.postFromCount = 0; for (i=0; i < cnt; i++) { XmAddToPostFromList (rowcol, tempPtr[i]); } } else if (forceParent) { /* no postFromList, then parent of Popup is on this list */ rowcol->row_column.postFromList[0] = XtParent(XtParent(rowcol)); } } static void PopupInitialize (popup) XmRowColumnWidget popup; { popup->manager.traversal_on = False; popup->row_column.lastSelectToplevel = (Widget) popup; if (RC_PostButton(popup) == -1) RC_PostButton(popup) = Button3; if (RC_Packing(popup) == XmNO_PACKING) RC_Packing(popup) = XmPACK_TIGHT; if (RC_Orientation(popup) == (char) XmNO_ORIENTATION) RC_Orientation(popup) = XmVERTICAL; if (RC_HelpPb(popup) != NULL) { _XmWarning(popup, BadPopupHelpMsg); RC_HelpPb(popup) = NULL; } if (RC_Spacing(popup) == XmINVALID_DIMENSION) RC_Spacing(popup) = 0; if (popup->core.tm.translations == NULL) popup->core.tm.translations = menu_parsed; XtOverrideTranslations(popup, menu_traversal_parsed); /* If no accelerator specified, use the default */ if (RC_MenuAccelerator(popup) && (*RC_MenuAccelerator(popup) == '\0')) if (!(RC_MenuAccelerator(popup) = GetRealKey(popup, "osfMenu"))) RC_MenuAccelerator(popup) = "F4"; /* Save a copy of the accelerator string */ if (RC_MenuAccelerator(popup)) RC_MenuAccelerator(popup) = (String) strcpy (XtMalloc(XmStrlen(RC_MenuAccelerator(popup)) + 1), RC_MenuAccelerator(popup)); PreparePostFromList(popup); /* Add event handlers to all appropriate widgets */ if (RC_PopupEnabled(popup)) { AddPopupEventHandlers (popup); /* Register all accelerators */ DoProcessMenuTree(popup, XmADD); } } static void PulldownInitialize (pulldown) XmRowColumnWidget pulldown; { pulldown->manager.traversal_on = False; pulldown->row_column.lastSelectToplevel = (Widget) NULL; if (RC_Packing(pulldown) == XmNO_PACKING) RC_Packing(pulldown) = XmPACK_TIGHT; if (RC_Orientation(pulldown) == (char) XmNO_ORIENTATION) RC_Orientation(pulldown) = XmVERTICAL; if (RC_HelpPb(pulldown) != NULL) { _XmWarning(pulldown, BadPulldownHelpMsg); RC_HelpPb(pulldown) = NULL; } if (RC_Spacing(pulldown) == XmINVALID_DIMENSION) RC_Spacing(pulldown) = 0; if (pulldown->core.tm.translations == NULL) pulldown->core.tm.translations = menu_parsed; XtOverrideTranslations(pulldown, menu_traversal_parsed); RC_MenuAccelerator(pulldown) = NULL; PreparePostFromList(pulldown); /* add event handler to myself for gadgets */ XtAddEventHandler(pulldown, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, pulldown); } static void OptionInitialize (option) XmRowColumnWidget option; { char b[200]; int n; Arg args[15]; Widget topManager; Widget child; if (RC_HelpPb(option) != NULL) { _XmWarning(option, BadOptionHelpMsg); RC_HelpPb(option) = NULL; } RC_Packing(option) = XmPACK_TIGHT; RC_NCol(option) = 2; RC_Orientation(option) = XmHORIZONTAL; option->row_column.lastSelectToplevel = (Widget) option; if (RC_PostButton(option) == -1) RC_PostButton(option) = Button1; if (RC_Spacing(option) == XmINVALID_DIMENSION) RC_Spacing(option) = 3; if (option->core.tm.translations == NULL) option->core.tm.translations = option_parsed; XtOverrideTranslations(option, ((XmManagerClassRec *)XtClass(option))->manager_class.translations); /* Create the label widget portion of the option menu */ n = 0; XtSetArg(args[n], XmNlabelString, RC_OptionLabel(option)); n++; child = XmCreateLabelGadget(option,"",args,n); XtManageChild (child); /* Create the cascade button widget portion of the option menu */ sprintf (b, "%s_cascadeBtn", option->core.name); n = 0; XtSetArg(args[n], XmNsubMenuId, RC_OptionSubMenu(option)); n++; XtSetArg(args[n], XmNshadowThickness, MGR_ShadowThickness(option)); n++; XtSetArg(args[n], XmNmarginWidth, 0); n++; XtSetArg(args[n], XmNmarginLeft, 0); n++; XtSetArg(args[n], XmNmarginHeight, 0); n++; XtSetArg(args[n], XmNmarginTop, 0); n++; XtSetArg(args[n], XmNmarginBottom, 0); n++; XtSetArg(args[n], XmNalignment, XmALIGNMENT_CENTER); n++; child = XmCreateCascadeButtonGadget(option,b,args,n); XtManageChild (child); RC_MenuAccelerator(option) = NULL; /* Add event handlers for catching keyboard input */ GetTopManager (option, &topManager); XtAddEventHandler(option, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, option); XtAddEventHandler(topManager, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, option); if (RC_Mnemonic(option)) DoProcessMenuTree(option, XmADD); /* This forces tab group - should probably make this a dynamic default! */ option->manager.navigation_type = XmNONE; } static void WorkAreaInitialize (work) XmRowColumnWidget work; { MGR_ShadowThickness(work) = 0; if (RC_PostButton(work) == -1) RC_PostButton(work) = Button1; if (RC_Packing(work) == XmNO_PACKING) RC_Packing(work) = XmPACK_TIGHT; if (RC_Orientation(work) == (char) XmNO_ORIENTATION) RC_Orientation(work) = XmVERTICAL; if (RC_HelpPb(work) != NULL) { _XmWarning(work, BadWorkAreaHelpMsg); RC_HelpPb(work) = NULL; } if (work->row_column.radio && (RC_Packing(work) == XmNO_PACKING)) RC_Packing(work) = XmPACK_TIGHT; if (RC_Spacing(work) == XmINVALID_DIMENSION) RC_Spacing(work) = 3; if (work->core.tm.translations == NULL) work->core.tm.translations = (XtTranslations) xmManagerClassRec.core_class.tm_table; XtOverrideTranslations(work, ((XmManagerClassRec *)XtClass(work))-> manager_class.translations); RC_MenuAccelerator(work) = NULL; } /* * Initialize a row column widget */ static void Initialize (req, m) XmRowColumnWidget req; /* build from arglist */ XmRowColumnWidget m; /* after superclass */ { if (!XtWidth(req)) { XtWidth(m) = 16; } if (!XtHeight(req)) { XtHeight(m) = 16; } if (IsPulldown(m) || IsPopup(m)) { if (RC_MarginW(m) == XmINVALID_DIMENSION) RC_MarginW(m) = 0; if (RC_MarginH(m) == XmINVALID_DIMENSION) RC_MarginH(m) = 0; } else { if (RC_MarginW(m) == XmINVALID_DIMENSION) RC_MarginW(m) = 3; if (RC_MarginH(m) == XmINVALID_DIMENSION) RC_MarginH(m) = 3; } switch (RC_Orientation(req)) { case XmNO_ORIENTATION: case XmVERTICAL: case XmHORIZONTAL: break; default: _XmWarning(m,BadOrientationMsg); RC_Orientation(m) = XmNO_ORIENTATION; break; } switch (RC_Packing(req)) { case XmNO_PACKING: case XmPACK_TIGHT: case XmPACK_COLUMN: case XmPACK_NONE: break; default: _XmWarning(m,BadPackingMsg); RC_Packing(m) = XmNO_PACKING; break; } switch (RC_Type(req)) { case XmWORK_AREA: case XmMENU_BAR: case XmMENU_OPTION: break; case XmMENU_POPUP: case XmMENU_PULLDOWN: if (!XmIsMenuShell(XtParent(req)) || !XtParent(XtParent(req))) { _XmWarning(m,BadTypeParentMsg); RC_Type(m) = XmWORK_AREA; } break; default: _XmWarning(m,BadTypeMsg); RC_Type(m) = XmWORK_AREA; break; } switch (RC_EntryAlignment(req)) { case XmALIGNMENT_BEGINNING: case XmALIGNMENT_CENTER: case XmALIGNMENT_END: break; default: _XmWarning(m,BadAlignmentMsg); RC_EntryAlignment(m) = XmALIGNMENT_BEGINNING; break; } RC_CascadeBtn(m) = NULL; RC_Boxes(m) = NULL; RC_SetExpose (m, TRUE); /* and ready to paint gadgets */ RC_SetWidgetMoved (m, TRUE); /* and menu and shell are not */ RC_SetWindowMoved (m, TRUE); /* in synch, positiongally */ RC_SetArmed (m, TRUE); /* are always armed */ RC_SetPoppingDown (m, FALSE); /* not popping down */ RC_PopupPosted(m) = NULL; /* no popup submenus posted */ /* create the menu cursor for this display, if there isn't one already */ _XmCreateMenuCursor(m); if (m->manager.shadow_thickness == XmINVALID_DIMENSION) m->manager.shadow_thickness = 2; m->row_column.old_width = XtWidth(m); m->row_column.old_height = XtHeight(m); m->row_column.old_shadow_thickness = m->manager.shadow_thickness; /* Post initialization for whichButton - done before PopupInitialize * because RC_PostModifiers used in eventual XtGrabButton() */ RC_PostModifiers(m) = AnyModifier; RC_PostEventType(m) = ButtonPress; if (IsBar(m)) MenuBarInitialize(m); else if (IsPopup(m)) PopupInitialize(m); else if (IsPulldown(m)) PulldownInitialize(m); else if (IsOption(m)) OptionInitialize(m); else WorkAreaInitialize(m); /* allow menuPost override */ if ((RC_MenuPost(m) != NULL) && !IsPulldown(m)) { if (_XmMapBtnEvent(RC_MenuPost(m), &RC_PostEventType(m), &RC_PostButton(m), &RC_PostModifiers(m)) == FALSE) { _XmWarning(m,BadMenuPostMsg); } } SetMenuHistory (m, RC_MemWidget (m)); } /* ARGSUSED */ static void ConstraintInitialize (req,new) Widget req; /* build from arglist */ Widget new; /* after superclass */ { if (!XtIsRectObj(new)) return; WasManaged(new) = False; } /* * the main create section, mostly just tacks on the type to the arg * list */ static Widget create (p, name, old_al, old_ac, type, is_radio) Widget p; /* parent widget */ char *name; ArgList old_al; Cardinal old_ac; int type; /* menu kind to create */ int is_radio; /* the radio flag */ { Arg al[50]; Widget m; int i, ac = 0; if (is_radio) /* get ours in ahead of the */ { /* caller's, so his override */ XtSetArg (al[ac], XmNpacking, XmPACK_COLUMN); ac++; XtSetArg (al[ac], XmNradioBehavior, is_radio); ac++; XtSetArg (al[ac], XmNisHomogeneous, TRUE); ac++; XtSetArg (al[ac], XmNentryClass, xmToggleButtonGadgetClass); ac++; } for (i=0; icore.num_popups; i++) { if ((XmIsMenuShell(pw->core.popup_list[i])) && (((XmMenuShellWidget)pw->core.popup_list[i])->menu_shell. private_shell) && (!(pw->core.popup_list[i])->core.being_destroyed)) { pop = (XmMenuShellWidget)pw->core.popup_list[i]; break; } } } /* No shell - create a new one */ if (pop == NULL) { XtSetArg (s_al[s_ac], XmNwidth, 5); s_ac++; XtSetArg (s_al[s_ac], XmNheight, 5); s_ac++; XtSetArg (s_al[s_ac], XmNallowShellResize, TRUE); s_ac++; XtSetArg (s_al[s_ac], XtNoverrideRedirect, TRUE); s_ac++; sprintf (b, "popup_%s", name); pop = (XmMenuShellWidget)XtCreatePopupShell(b, xmMenuShellWidgetClass, pw, s_al, s_ac); /* Mark the shell as having been created by us */ pop->menu_shell.private_shell = True; } m = XtCreateWidget (name, xmRowColumnWidgetClass, pop, al, ac); } else m = XtCreateWidget (name, xmRowColumnWidgetClass, p, al, ac); return (m); } /* ************************************************************************* * * Public Routines * ************************************************************************* */ void XmMenuPosition (Widget p, XButtonPressedEvent *event) { PositionMenu (p, event); } Widget XmCreateRowColumn (Widget p, char *name, ArgList al, Cardinal ac) { return (create (p, name, al, ac, UNDEFINED_TYPE, FALSE)); } Widget XmCreateWorkArea (Widget p, char *name, ArgList al, Cardinal ac) { return (create (p, name, al, ac, XmWORK_AREA, FALSE)); } Widget XmCreateRadioBox (Widget p, char *name, ArgList al, Cardinal ac) { return (create (p, name, al, ac, XmWORK_AREA, TRUE)); } Widget XmCreateOptionMenu (Widget p, char *name, ArgList al, Cardinal ac) { return (create (p, name, al, ac, XmMENU_OPTION, FALSE)); } Widget XmOptionLabelGadget (Widget m) { int i; Widget child; if (XmIsRowColumn(m) && IsOption(m)) { XmRowColumnWidget rowcol = (XmRowColumnWidget) m; for (i = 0; i < rowcol->composite.num_children; i++) { child = rowcol->composite.children[i]; if (XtClass(child) == xmLabelGadgetClass) return (child); } } /* did not find a label gadget in the child list */ return (NULL); } Widget XmOptionButtonGadget (Widget m) { int i; Widget child; if (XmIsRowColumn(m) && IsOption(m)) { XmRowColumnWidget rowcol = (XmRowColumnWidget) m; for (i = 0; i < rowcol->composite.num_children; i++) { child = rowcol->composite.children[i]; if (XmIsCascadeButtonGadget(child)) return (child); } } /* did not find a cascadebuttongadget in the child list */ return (NULL); } Widget XmCreateMenuBar (Widget p, char *name, ArgList al, Cardinal ac) { return (create (p, name, al, ac, XmMENU_BAR, FALSE)); } Widget XmCreatePopupMenu (Widget p, char *name, ArgList al, Cardinal ac) { return (create (p, name, al, ac, XmMENU_POPUP, FALSE)); } Widget XmCreatePulldownMenu (Widget p, char *name, ArgList al, Cardinal ac) { return (create (p, name, al, ac, XmMENU_PULLDOWN, FALSE)); } void XmAddToPostFromList (XmRowColumnWidget menu, Widget widget) { Arg args[1]; /* only continue if its a vailid widget and a popup or pulldown menu */ if (! XmIsRowColumn(menu) || ! (IsPopup(menu) || IsPulldown(menu)) || ! widget) return; if (OnPostFromList(menu, widget) == -1) { if (IsPulldown(menu)) { XtSetArg (args[0], XmNsubMenuId, menu); XtSetValues (widget, args, 1); } else { AddToPostFromList (menu, widget); AddHandlersToPostFromWidget (menu, widget); } } } void XmRemoveFromPostFromList (XmRowColumnWidget menu, Widget widget) { Arg args[1]; /* only continue if its a vailid widget and a popup or pulldown menu */ if (! XmIsRowColumn(menu) || ! (IsPopup(menu) || IsPulldown(menu)) || ! widget) return; if ((OnPostFromList(menu, widget)) == -1) { if (IsPulldown(menu)) { XtSetArg (args[0], XmNsubMenuId, NULL); XtSetValues (widget, args, 1); } else { RemoveFromPostFromList (menu, widget); RemoveHandlersFromPostFromWidget (menu, widget); } } } /* * Return the widget which the menu was posted from. If this is in a popup, * it is the widget which initiated the post (via positioning & managing or * via armAndActivate). If it is in a pulldown from a menubar or option menu, * then the returned widget is the menubar or option menu. */ Widget XmGetPostedFromWidget (Widget menu) { Widget toplevel = ((XmRowColumnWidget) menu)->row_column.lastSelectToplevel; if (XmIsRowColumn(menu)) { if (IsPopup(toplevel)) { /* active widget is kept in cascadeBtn field for popups */ return (RC_CascadeBtn(toplevel)); } else return (toplevel); } return (NULL); } /* * class initialization */ /* * needed for funky menubar mode so that the traversal can be restored * to the correct tabgroup when we are done. */ static Widget tabGroup; static void ClassInitialize () { /* * parse the various translation tables */ menu_parsed = XtParseTranslationTable (menu_table); option_parsed = XtParseTranslationTable (option_table); menu_traversal_parsed = XtParseTranslationTable (menu_traversal_table); /* initialize the menu cursor context */ _XmInitializeMenuCursor(); /* set up the menu procedure entry for button children to access */ _XmSaveMenuProcContext( (caddr_t) MenuProcedureEntry); tabGroup = NULL; } static void ClassPartInitialize (rcc) XmRowColumnWidgetClass rcc; { _XmFastSubclassInit(rcc,XmROW_COLUMN_BIT); } /*************************************************************************** * * * next section is action routines, these are called by the row column event * handler in response to events. The handler parses its way through * the actions table calling these routines as directed. It is these * routines which will typically decide the application should be * called via a callback. This is where callback reasons are produced. * */ static XmRowColumnWidget find_menu (w) Widget w; { if (XmIsRowColumn(w)) return ((XmRowColumnWidget) w); /* row column itself */ else return ((XmRowColumnWidget) XtParent (w)); /* subwidget */ } /* * popdown anything that should go away */ static void MenuPopDown (w, event) Widget w; XEvent * event; { XmRowColumnWidget rc = find_menu(w); /* * find the top of this cascade and popdown anything posted on it */ _XmGetActiveTopLevelMenu (rc, &rc); if (IsPopup(rc)) (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)-> menu_shell_class.popdownDone))(rc, event); else if (RC_PopupPosted(rc)) { (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)-> menu_shell_class.popdownDone))(RC_PopupPosted(rc), event); /* This kludges a fix so that F10 followed by mnemonic selection * doesn't call MenuFocusIn and leave the cascade button highlighted. */ if (IsBar(rc)) (void)XmProcessTraversal(rc, XmTRAVERSE_CURRENT); } } static void MenuArm (w) Widget w; { XmRowColumnWidget m = find_menu(w); if (!RC_IsArmed(m)) { if (IsBar(m)) { /* * Menubars need their own exclusive/SL grab, so that they will * still get input even when a cascade button without a submenu * has the focus. */ _XmSetTransientFlag(m, True); XtAddGrab(m, True, True); RC_SetBeingArmed(m, True); } RC_SetArmed (m, True); } } static void MenuDisarm (w) Widget w; { XmRowColumnWidget m = find_menu(w); if (RC_IsArmed(m)) { if (IsBar(m)) { _XmSetTransientFlag(m, False); XtRemoveGrab(m); RC_SetBeingArmed(m, False); } RC_SetArmed (m, FALSE); } } /********************************************************************** * * next section knows how to composite row column entries */ static void FixEventBindings (m, w) XmRowColumnWidget m; /* row column (parent) widget */ Widget w; /* subwidget */ { if (XtIsWidget(w) && ((IsPopup(m) || IsBar(m) || IsPulldown(m)) && XmIsLabel(w) && (w->core.widget_class != xmLabelWidgetClass))) { XtAddEventHandler(w, KeyPressMask|KeyReleaseMask, False, KeyboardInputHandler, m); } /* set up accelerators and mnemonics */ ProcessSingleWidget (w, XmADD); } /* * Action routines for controlling traversal. */ static void KickOnTraversal (m) XmRowColumnWidget m; { XEvent event; Widget menupane; if (XmIsRowColumn(m)) { if (RC_PopupPosted(m)) { menupane = ((XmMenuShellWidget) RC_PopupPosted(m))->composite.children[0]; event.type = FocusIn; event.xfocus.send_event = True; (void) XmProcessTraversal (menupane, XmTRAVERSE_CURRENT); _XmManagerFocusInInternal(menupane, &event); if (!IsBar(m)) m->manager.active_child = RC_CascadeBtn(menupane); return; } /* Set it to ourselves */ event.type = FocusIn; event.xfocus.send_event = True; (void) XmProcessTraversal (m, XmTRAVERSE_CURRENT); _XmManagerFocusInInternal(m, &event); } } static void KickOffTraversal (m) XmRowColumnWidget m; { Widget rc; XmGadget gadget; CompositeWidget shell; CompositeWidget oldShell = NULL; shell = (CompositeWidget) RC_PopupPosted(m); /* find last posted menushell */ while (oldShell != shell) { oldShell = shell; if (RC_PopupPosted(shell->composite.children[0])) { shell = (CompositeWidget) RC_PopupPosted(shell->composite.children[0]); } } if (shell) rc = shell->composite.children[0]; else rc = (Widget) m; /* * Clear focus entries for each posted menupane. */ while (rc && ((RC_Type(rc) == XmMENU_POPUP) || (RC_Type(rc) == XmMENU_PULLDOWN))) { /* * Because a gadget will not get focus events, and because the * parent may also not get one (because of a toolkit optimization), * we need to handle gadget specially. */ gadget = (XmGadget)((XmManagerWidget)rc)->manager.active_child; /* Inform the gadget that it should unhilite & clean itself up */ if (gadget && XmIsGadget(gadget)) { /* Direct focus to rc to handle focus events for gadgets */ _XmSetFocusResetFlag(rc, True); (void) XmProcessTraversal (rc, XmTRAVERSE_CURRENT); _XmSetFocusResetFlag(rc, False); if (ShouldDispatchFocusOut(gadget)) { /* have to set active_child to NULL for CascadeBG to unhighlight */ ((XmManagerWidget)rc)->manager.active_child = NULL; _XmDispatchGadgetInput(gadget, NULL, XmFOCUS_OUT_EVENT); gadget->gadget.have_traversal = False; } } /* clears the focus_item so that next TraverseToChild() will work */ _XmClearFocusPath(rc); if (! IsPopup(rc) && RC_CascadeBtn(rc)) rc = XtParent(RC_CascadeBtn(rc)); else rc = NULL; } } /* * When we drop back into traversal mode, we need to set the active child * field for each visible menupane. */ static void SetActiveChildren (toplevel) XmRowColumnWidget toplevel; { XmRowColumnWidget rc; XmRowColumnWidget subpane; if (IsOption(toplevel)) { /* Link option submenu and its memory widget */ rc = (XmRowColumnWidget)toplevel->row_column.option_submenu; rc->manager.active_child = (Widget)RC_MemWidget(rc); return; } while (1) { if (RC_PopupPosted(toplevel)) { subpane = (XmRowColumnWidget) ((CompositeWidget) RC_PopupPosted(toplevel))->composite.children[0]; if (! IsBar(toplevel)) { toplevel->manager.active_child = RC_CascadeBtn(subpane); } toplevel = subpane; } else { /* No shells popped up; use first available widget */ toplevel->manager.active_child = NULL; return; } } } /* * Class function used to enable or disable traversal. This is called * when the menu system is active. */ static void SetMenuTraversal (m, on) XmRowColumnWidget m; Boolean on; { XmRowColumnWidget topLevel; XmRowColumnWidget pane; _XmGetActiveTopLevelMenu (m, &topLevel); if (on) { /* The order here is important */ SetActiveChildren(topLevel); SetTraversal(topLevel, True); KickOnTraversal(m); } else { /* Do something only if traversal is currently enabled */ if (((IsBar(topLevel) || IsPopup(topLevel)) && topLevel->manager.traversal_on) || (IsOption(topLevel) && (pane = (XmRowColumnWidget)topLevel->row_column.option_submenu) && (pane->manager.traversal_on))) { /* The order here is important */ KickOffTraversal(m); SetTraversal(topLevel, False); } } } /* * Class function which is used to clean up the menubar when it is leaving * the mode where the user has pressed F10 to start traversal in the * menubar. */ static void MenuBarCleanup (rc) XmRowColumnWidget rc; { /* * We can tell if this mode is active by looking at the 'active_child' * field in the menubar; it should only be set when we are in this mode. */ if (rc->manager.active_child) { if (XmIsPrimitive(rc->manager.active_child)) { (*(((XmPrimitiveClassRec *)XtClass(rc->manager.active_child))-> primitive_class.border_unhighlight))(rc->manager.active_child); } else if (XmIsGadget(rc->manager.active_child)) { (*(((XmGadgetClassRec *)XtClass(rc->manager.active_child))-> gadget_class.border_unhighlight))(rc->manager.active_child); } _XmSetFocusResetFlag(rc, True); (void) XmProcessTraversal (rc, XmTRAVERSE_CURRENT); _XmSetFocusResetFlag(rc, False); rc->manager.active_child = NULL; } /* * restore the tabgroup to the widget that had the focus BEFORE the * menubar mode was entered. */ if (tabGroup) { (void) XmProcessTraversal (tabGroup, XmTRAVERSE_CURRENT); tabGroup = NULL; } } /* ARGSUSED */ void _XmMenuFocus (Widget w, int operation, Time _time) { static int oldFocus = NULL; static int oldRevert = 0; switch (operation) { case XmMENU_END: if (oldFocus) { XSetInputFocus(XtDisplay(w), oldFocus, oldRevert, CurrentTime); oldFocus = oldRevert = NULL; XtUngrabKeyboard(w, CurrentTime); } break; case XmMENU_BEGIN: /* We must grab the keyboard before the InputFocus is set for mwm * to work correctly. */ XtGrabKeyboard(w, True, GrabModeSync, GrabModeSync, CurrentTime); XGetInputFocus(XtDisplay(w), &oldFocus, &oldRevert); XSetInputFocus(XtDisplay(w), XtWindow(w), oldRevert, CurrentTime); /* These two round trips are to support the broken >= R4 server * which does not unfreeze properly when XAllowEvents is called * with AsyncBoth */ XAllowEvents(XtDisplay(w), AsyncKeyboard, CurrentTime); XAllowEvents(XtDisplay(w), AsyncPointer, CurrentTime); XFlush(XtDisplay(w)); break; case XmMENU_MIDDLE: XSetInputFocus(XtDisplay(w), XtWindow(w), oldRevert, CurrentTime); break; } } /* * Class function which is invoked when the post accelerator is received * for a popup menu or the menubar, or the post mnemonic is received for * an option menu. */ static void ArmAndActivate (m, event) XmRowColumnWidget m; XKeyPressedEvent * event; { int i; XmCascadeButtonWidget child; XRectangle visRect; Cursor cursor; if (IsPopup(m)) { if (!XtIsManaged(m)) { Position x, y; /* the posted from widget is saved in RC_CascadeBtn */ RC_CascadeBtn(m) = XtWindowToWidget(XtDisplay(m), event->window); /* Position & post menupane; then enable traversal */ RC_SetWidgetMoved(m, True); /* Position the pane off of the parent of this rowcolumn's menushell. * Place it in the upper left corner. */ XtTranslateCoords(XtParent(XtParent(m)), 0, 0, &x, &y); /* Verify popup for MenuShell's manage_set_changed() */ _XmButtonEventStatus.time = event->time; _XmButtonEventStatus.verified = True; XtX(m) = x; XtY(m) = y; XtManageChild(m); SetActiveChildren(m); SetTraversal(m, True); KickOnTraversal(m); } else { /* Let the menushell widget clean things up */ (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)-> menu_shell_class.popdownDone))(XtParent(m), event); } } else if (IsOption(m)) { XmGadget g = (XmGadget) XmOptionButtonGadget(m); /* Let the cascade button gadget do the work */ (*(((XmGadgetClassRec *)XtClass(g))->gadget_class. arm_and_activate)) (g, event); } else if (IsBar(m)) { if (RC_IsArmed(m)) { /* * If the menubar is already armed, then F10 unposts and disarms * the menu system. Handle PM menubar mode specially. */ if (!m->row_column.popupPosted) { /* No submenus posted; must clean up ourselves */ MenuDisarm(m); _XmMenuFocus(m, XmMENU_END, CurrentTime); XtUngrabKeyboard(m, CurrentTime); XtUngrabPointer(m, CurrentTime); SetMenuTraversal(m, False); MenuBarCleanup(m); } else { /* Submenus are posted; let MenuPopDown() clean up */ MenuPopDown(m, event); } } else { /* * If the menubar is not armed, then look to see if there is a * cascade button with a submenu; if none found, then return. * Otherwise, highlight and arm that button. */ _XmCreateVisibilityRect(m, &visRect); for (i = 0; i < m->composite.num_children; i++) { child = (XmCascadeButtonWidget)m->composite.children[i]; /* You can't traverse to a button which has no submenu */ if ((XmIsCascadeButton(child) && (CB_Submenu(child) == NULL)) || (XmIsCascadeButtonGadget(child) && (CBG_Submenu(child) == NULL))) continue; if (_XmTestTraversability(child, &visRect)) break; } /* See if we found one */ if (i >= m->composite.num_children) return; /* * Menubars need their own exclusive/SL grab, so that they will * still get input even when a cascade button without a submenu * has the focus. * * We can't call MenuArm() here, because it does the pointer grab * before our keyboard grab. */ _XmSetTransientFlag(m, True); XtAddGrab(m, True, True); RC_SetArmed(m, True); /* Force the application to unhilite itself */ m->manager.active_child = m->composite.children[i]; /* * save the current tabgroup so that it can be restored after * the menubar traversal mode is finished. */ tabGroup = _XmGetTabGroup(m); (void) XmProcessTraversal (m, XmTRAVERSE_CURRENT); if (XtIsWidget(m->manager.active_child)) (void) XmProcessTraversal (m->manager.active_child, XmTRAVERSE_CURRENT); cursor = XmGetMenuCursor(XtDisplay(m)); _XmMenuFocus(m, XmMENU_BEGIN, CurrentTime); XtGrabPointer (m, True, ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask, GrabModeAsync, GrabModeAsync, None, cursor, CurrentTime); SetTraversal(m, True); if (XmIsPrimitive(m->manager.active_child)) { (*(((XmPrimitiveClassRec *)XtClass(m->manager.active_child))-> primitive_class.border_highlight)) (m->manager.active_child); } else if (XmIsGadget(m->manager.active_child)) { (*(((XmGadgetClassRec *)XtClass(m->manager.active_child))-> gadget_class.border_highlight)) (m->manager.active_child); } } } else if (IsPulldown(m)) /* Catch the Escape in a cascading menu! */ { /* Let the menushell widget clean things up */ (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)-> menu_shell_class.popdownOne))(XtParent(m), event); } } /* * The following functions are used to manipulate lists of keyboard events * which are of interest to the menu system; i.e. accelerators and mnemonics. * The top level component within a menu system (the menubar, or the popup * menupane, or the option menu, or the work area menu) keeps a list of * the events it cares about. Items are only added to the list when the * associated menu component has a complete path to the top level component. * Items are removed when the complete path is broken. * * The times at which a complete path may be formed is 1) When a button * in a menupane becomes managed, when a pulldown menupane is attached * to a cascading button, when the application sets the popupEnabled * resource to True, or when an option menu is created. A complete path * may be broken when 1) a button in a menupane is unmanaged, a pulldown * menupane is detached from a cascading button, when the application clears * the popupEnabled resource or an option menu is destroyed. * * The event handler for catching keyboard events is added by the row column * widget during its initialize process, and removed when the row column * widget is destroyed. Keyboard grabs, which are needed to make accelerators * work, are added either when the accelerator is registered, or when the * associated widget is realized; grabs cannot be added to a widget which * does not have a window! */ /* * This function is used both by the row column widget, and components which * are contained within a menu (toggle, pushbutton and cascadebutton). The * row column widget uses it to process a tree of menu components; when a * menupane is linked to a cascade button, the new menupane, along with any * submenus cascading from it, will be processed. The row column widget * will use the XmADD or XmDELETE mode to to this. When a menu component * needs to change its accelerator or mnemonics, it will use the XmREPLACE * mode. * * When this function is used to delete a tree of keyboard actions, the * link between the menupane at the root (parameter 'w') and the cascade * button it is attached to must not yet have been broken. This allows * the function to trace up the hierarchy and find the top level widget. */ static void DoProcessMenuTree (w, mode) Widget w; int mode; /* Add, Replace or Delete */ { /* * Depending upon the type of widget 'w' is, we may end up adding/deleting * keyboard actions for no widgets, the specified widget only, or the * specified widget and all others which cascade from it. */ if (XmIsCascadeButton(w) || XmIsCascadeButtonGadget(w)) { /* If our parent is not a row column, then abort */ if (XmIsRowColumn(XtParent(w))) { if (IsOption(XtParent(w))) { if (mode == XmREPLACE) return; /* * A cascade button in an option menu does not have an * accelerator or a mnemonic associated with it. However, * its submenu may, so we need to process it. */ if (XmIsCascadeButtonGadget(w)) w = (Widget)CBG_Submenu(w); } else if (IsWorkArea(XtParent(w))) { /* * Since work area menus do not support cascade buttons * with submenus, we will treat this like a pushbutton. */ if (mode == XmREPLACE) { /* Remove old one, if it exists */ ProcessSingleWidget(w, XmDELETE); mode = XmADD; } ProcessSingleWidget(w, mode); return; } else if (IsBar(XtParent(w)) || IsPopup(XtParent(w)) || IsPulldown(XtParent(w))) { if (mode == XmREPLACE) { /* When replacing, don't worry about submenus */ ProcessSingleWidget(w, XmDELETE); ProcessSingleWidget(w, XmADD); return; } /* * If we are in a menubar, a popup menupane or a pulldown * menupane, then we need to not only modify our keyboard * event, but also any which are defined in our submenu. */ ProcessSingleWidget(w, mode); if (XmIsCascadeButtonGadget(w)) w = (Widget)CBG_Submenu(w); else w = (Widget)CB_Submenu(w); } } } else if (XmIsToggleButtonGadget(w) || XmIsToggleButton(w) || XmIsPushButtonGadget(w) || XmIsPushButton(w)) { if (mode == XmREPLACE) { /* Remove old one */ ProcessSingleWidget(w, XmDELETE); mode = XmADD; } /* * In both of these cases, we need only modify the keyboard * event associated with this widget. */ ProcessSingleWidget(w, mode); return; } else if (XmIsRowColumn(w)) { /* * When the popupEnabled resource is enabled for a popup menupane, * we need to add the accelerator associated with the menu, followed * by all keyboard events associated with submenus. The reverse * happens when the resource is disabled. * * When an option menu is created, we will add its posting mnemonic; * its submenu is taken care of when it is attached to the cascading * button. */ if (IsPopup(w)) { if (mode == XmREPLACE) { /* Don't worry about submenus during a replace operation */ ProcessSingleWidget(w, XmDELETE); ProcessSingleWidget(w, XmADD); return; } ProcessSingleWidget(w, mode); } else if (IsOption(w) || IsBar(w)) { if (mode == XmREPLACE) { ProcessSingleWidget(w, XmDELETE); mode = XmADD; } ProcessSingleWidget(w, mode); return; } } else { /* Unknown widget type; do nothing */ return; } /* Process any submenus */ ProcessMenuTree(w, mode); } /* * Given a row column widget, all keyboard events are processed * for the items within the row column widget, and then recursively * for any submenus cascading from this row column widget. */ static void ProcessMenuTree (w, mode) XmRowColumnWidget w; int mode; { int i; Widget child; if (w == NULL) return; for (i = 0; i < w->composite.num_children; i++) { if (XtIsManaged((child = w->composite.children[i]))) { ProcessSingleWidget(child, mode); if (XmIsCascadeButtonGadget(child)) { ProcessMenuTree(CBG_Submenu(child), mode); } else if (XmIsCascadeButton(child)) { ProcessMenuTree(CB_Submenu((XmCascadeButtonWidget)child), mode); } } } } /* * This function adds/deletes the mnemonic and/or accelerator associated * with the specified widget. The work that is done is dependent both * on the widget in question, and sometimes the parent of the widget. * * When adding a keyboard event, we first check the component to see if * it has any keyboard events defined; if not, then nothing is done. However, * when removing a keyboard event, we simply attempt to remove the entry for * the specified widget; we can't check to see if the widget had one defined, * because the instance structure may no longer contain the information. */ static void ProcessSingleWidget (w, mode) Widget w; int mode; { char buf[20]; Arg args[2]; Widget child; if (XmIsCascadeButtonGadget(w)) { XmCascadeButtonGadget c = (XmCascadeButtonGadget)w; if (XmIsRowColumn(XtParent(w)) && (IsBar(XtParent(w)))) { if (mode == XmADD) { /* Menubar mnemonics are prefixed with the Mod1 modifier */ if (LabG_Mnemonic(c) != NULL) { strcpy (buf, "Mod1"); strcat (buf, XKeysymToString(LabG_Mnemonic(c))); /* although this is technically a mnemonic, it will be * treated like an acclerator */ AddToKeyboardList(w, buf, True, False); /* save it again as a mnemonic so that it is available w/o * the Mod1 when the menu system is posted */ strcpy (buf, ""); strcat (buf, XKeysymToString(LabG_Mnemonic(c))); AddToKeyboardList(w, buf, False, True); } } else RemoveFromKeyboardList(w); } else { if (mode == XmADD) { /* All other mnemonics are done without any modifiers */ if (LabG_Mnemonic(c) != NULL) { strcpy (buf, ""); strcat (buf, XKeysymToString(LabG_Mnemonic(c))); AddToKeyboardList(w, buf, False, True); } } else RemoveFromKeyboardList(w); } } else if (XmIsCascadeButton(w)) { XmCascadeButtonWidget c = (XmCascadeButtonWidget)w; if (XmIsRowColumn(XtParent(w)) && (IsBar(XtParent(w)))) { if (mode == XmADD) { /* Menubar mnemonics are prefixed with the Mod1 modifier */ if (Lab_Mnemonic(c) != NULL) { strcpy (buf, "Mod1"); strcat (buf, XKeysymToString(Lab_Mnemonic(c))); /* save as an accelerator since it is available anytime */ AddToKeyboardList(w, buf, True, False); /* save again as a mnemonic so its available w/o Mod1 * once the menu system is active */ strcpy (buf, ""); strcat (buf, XKeysymToString(Lab_Mnemonic(c))); AddToKeyboardList(w, buf, False, True); } } else RemoveFromKeyboardList(w); } else { if (mode == XmADD) { /* All other mnemonics are done without any modifiers */ if (Lab_Mnemonic(c) != NULL) { strcpy (buf, ""); strcat (buf, XKeysymToString(Lab_Mnemonic(c))); AddToKeyboardList(w, buf, False, True); } } else RemoveFromKeyboardList(w); } } else if (XmIsToggleButtonGadget(w) || XmIsPushButtonGadget(w)) { XmLabelGadget l = (XmLabelGadget) w; if (mode == XmADD) { /* These can have both an accelerator and a mnemonic */ if (LabG_Mnemonic(l) != NULL) { strcpy (buf, ""); strcat (buf, XKeysymToString(LabG_Mnemonic(l))); AddToKeyboardList(w, buf, False, True); } if (LabG_Accelerator(l) && (strlen(LabG_Accelerator(l)) > 0)) { AddToKeyboardList(w, LabG_Accelerator(l), True, False); } } else RemoveFromKeyboardList(w); } else if (XmIsToggleButton(w) || XmIsPushButton(w)) { XmLabelWidget l = (XmLabelWidget) w; if (mode == XmADD) { /* These can have both an accelerator and a mnemonic */ if (Lab_Mnemonic(l) != NULL) { strcpy (buf, ""); strcat (buf, XKeysymToString(Lab_Mnemonic(l))); AddToKeyboardList(w, buf, False, True); } if (Lab_Accelerator(l) && (strlen(Lab_Accelerator(l)) > 0)) { AddToKeyboardList(w, Lab_Accelerator(l), True, False); } } else RemoveFromKeyboardList(w); } else if (XmIsRowColumn(w)) { XmRowColumnWidget m = (XmRowColumnWidget) w; if (IsPopup(m) || IsBar(m)) { /* * Popup Menus and the menubar may have an accelerator associated * with them */ if (mode == XmADD) { if (RC_MenuAccelerator(m) && (strlen(RC_MenuAccelerator(m)) > 0)) { AddToKeyboardList(w, RC_MenuAccelerator(m), True, False); } } else RemoveFromKeyboardList(w); } else if (IsOption(m)) { /* Option menus may have a mnemonics associated with them */ if (mode == XmADD) { if (RC_Mnemonic(m)) { strcpy (buf, "Mod1"); strcat (buf, XKeysymToString(RC_Mnemonic(m))); AddToKeyboardList(w, buf, True, True); /* Tell the label gadget */ XtSetArg(args[0], XmNmnemonic, RC_Mnemonic(m)); XtSetArg(args[1], XmNmnemonicCharSet, RC_MnemonicCharSet(m)); if (child = XmOptionLabelGadget(m)) XtSetValues(child, args, 2); } } else { RemoveFromKeyboardList(w); /* Tell the label gadget */ if (child = XmOptionLabelGadget(m)) { XtSetArg(args[0], XmNmnemonic, '\0'); XtSetValues(child, args, 1); } } } } } /* * This function actually does the work of converting the accelerator * or mnemonic string into a workable format, and registering the keyboard * grab, if possible. */ static void AddToKeyboardList (w, kbdEvent, needGrab, isMnemonic) Widget w; char * kbdEvent; Boolean needGrab; Boolean isMnemonic; { Widget rowcol; unsigned int eventType; KeySym keysym; unsigned int modifiers; KeyCode detail; XmKeyboardData * list; int i; if ((kbdEvent == NULL) || (_XmMapKeyEvent(kbdEvent, &eventType, &keysym, &modifiers) == False)) { return; } /* Convert keysym to keycode; needed by X grab call */ if ((detail = XKeysymToKeycode(XtDisplay(w), keysym)) == NoSymbol) { return; } if (XmIsRowColumn(w)) rowcol = w; else rowcol = XtParent(w); /* Add to the list of keyboard entries */ if (MGR_NumKeyboardEntries(rowcol) >= MGR_SizeKeyboardList(rowcol)) { /* Grow list */ MGR_SizeKeyboardList(rowcol) += 10; MGR_KeyboardList(rowcol) = (XmKeyboardData *)XtRealloc(MGR_KeyboardList(rowcol), (MGR_SizeKeyboardList(rowcol) * sizeof(XmKeyboardData))); } list = MGR_KeyboardList(rowcol); i = MGR_NumKeyboardEntries(rowcol); list[i].eventType = eventType; list[i].keysym = keysym; list[i].key = detail; list[i].modifiers = isMnemonic ? (modifiers & ~(ShiftMask | LockMask)) : modifiers; list[i].component = w; list[i].needGrab = needGrab; list[i].isMnemonic = isMnemonic; MGR_NumKeyboardEntries(rowcol)++; if (needGrab) { GrabKeyOnAssocWidgets (rowcol, detail, modifiers); } } /* * This function removes all keyboard entries associated with a particular * component within a row column widget. */ static void RemoveFromKeyboardList (w) Widget w; { Widget rowcol; XmKeyboardData * list; int count; int i, j; if (XmIsRowColumn(w)) rowcol = w; else rowcol = XtParent(w); list = MGR_KeyboardList(rowcol); count = MGR_NumKeyboardEntries(rowcol); for (i = 0; i < count; ) { if (list[i].component == w) { /* NOTE that the ungrabs on the associate widgets are not done * for completeness, they probably should be. The problem is that * it is difficult to tell whether an item should really be * ungrabbed since the sharing of menupanes could mean that this * item exists somewhere else on this hierarchy. */ /* Move the rest of the entries up 1 slot */ for (j = i; j < count -1; j++) list[j] = list[j+1]; MGR_NumKeyboardEntries(rowcol) = MGR_NumKeyboardEntries(rowcol)-1; count--; } else i++; } } /* * This function searches the list of keyboard events associated with the * specified row column widget to see if any of them match the * passed in X event. This function can be called multiple times, to get * all entries which match. */ static int _XmMatchInKeyboardList (rowcol, event, startIndex) XmRowColumnWidget rowcol; XKeyEvent * event; int startIndex; { XmKeyboardData * list = MGR_KeyboardList(rowcol); int count = MGR_NumKeyboardEntries(rowcol); int i; if (list == NULL) return(-1); for (i = startIndex; i < count; i++) { /* * We want to ignore shift and shift-lock for mnemonics. So, OR the * event's two bits with the (previously two bits initialized to zero) * list.modifier */ if (_XmMatchKeyEvent(event, list[i].eventType, list[i].key, list[i].isMnemonic ? list[i].modifiers | (event->state & (ShiftMask | LockMask)) : list[i].modifiers)) { return(i); } } /* No match */ return (-1); } /* * search the postFromList and return the index of the found widget. If it * is not found, return -1 */ static int OnPostFromList (menu, widget) XmRowColumnWidget menu; Widget widget; { int i; for (i = 0; i < menu->row_column.postFromCount; i++) { if (menu->row_column.postFromList[i] == widget) return (i); } return (-1); } /* * Useful for MenuBars and Option Menus to determine where to set up the * event handlers and grabs. */ static void GetTopManager (w, topManager) Widget w; Widget * topManager; { while (XmIsManager(XtParent(w))) w = XtParent(w); * topManager = w; } /* * Returns the toplevel menu widget in an acive menu hierarchy. * * This function is only useful when the menu system is active. That is * the only time that the CascadeBtn field in the RowColumn in guarrenteed * to be valid. */ void _XmGetActiveTopLevelMenu (XmRowColumnWidget w, XmRowColumnWidget *topLevel) { /* * find toplevel by following up the chain. Popups use CascadeBtn to * keep the active widget in the postFromList. */ while (RC_CascadeBtn(w) && (!IsPopup(w))) w = (XmRowColumnWidget) XtParent(RC_CascadeBtn(w)); * topLevel = w; } /* * set up the grabs on the appropriate assoc widgets. For a popup, this * is all of the widgets on the postFromList. For a menubar and option * menu, this is the top manager widget in their hierarchy. For a * pulldown, the assoc widgets can only be determined by following the * chain up the postFromList. */ static void GrabKeyOnAssocWidgets (rowcol, detail, modifiers) XmRowColumnWidget rowcol; KeyCode detail; unsigned int modifiers; { Widget topManager; int i; if (IsPopup(rowcol)) { for (i=0; i < rowcol->row_column.postFromCount; i++) XtGrabKey(rowcol->row_column.postFromList[i], detail, modifiers, False, GrabModeAsync, GrabModeAsync); } else if (IsBar(rowcol) || IsOption(rowcol)) { GetTopManager (rowcol, &topManager); XtGrabKey(topManager, detail, modifiers, False, GrabModeAsync, GrabModeAsync); } else if (IsPulldown(rowcol)) { for (i=0; irow_column.postFromCount; i++) GrabKeyOnAssocWidgets (XtParent(rowcol->row_column.postFromList[i]), detail, modifiers); } } /* * Given a menupane, this function traverses down through any posted submenus, * setting the state of the traversal_on field to the indicated value. */ static void SetTraversal (m, traversalOn) XmRowColumnWidget m; Boolean traversalOn; { if (m == NULL) return; if (!IsOption(m)) m->manager.traversal_on = traversalOn; if (XmIsMenuShell(XtParent(m))) MS_FocusPolicy(XtParent(m)) = traversalOn ? XmEXPLICIT : XmPOINTER; if (RC_PopupPosted(m)) SetTraversal(((CompositeWidget)RC_PopupPosted(m))-> composite.children[0], traversalOn); } /* * This is the event handler which catches, verifies and dispatches all * accelerators and mnemonics defined for a given menu hierarchy. It * is attached to the menu's associated widget, along with an assortment * of other widgets. */ static void KeyboardInputHandler (reportingWidget, topLevel, event) Widget reportingWidget; XmRowColumnWidget topLevel; XEvent * event; { /* Process the event only if not already processed */ if (!_XmIsEventUnique(event)) return; if (IsBar(topLevel) || IsOption(topLevel)) if (! _XmAllWidgetsAccessible(topLevel)) return; /* * XmGetPostFromWidget() requires help to identify the topLevel widget * when a menupane is posted via accelerators. */ if (IsBar(topLevel) || IsOption(topLevel)) lastSelectToplevel = (Widget) topLevel; else if (IsPopup(topLevel)) { lastSelectToplevel = reportingWidget; /* popup */ } else lastSelectToplevel = NULL; ProcessKey (topLevel, event); lastSelectToplevel = NULL; /* reset toplevel "accelerator" state to NULL */ } /* * try to find a match in the menu for the key event. Cascade down the * submenus if necessary */ static Boolean ProcessKey (rowcol, event) XmRowColumnWidget rowcol; XEvent * event; { Boolean found = FALSE; int i; Widget child; /* Try to use it on the current rowcol */ if (! CheckKey (rowcol, event)) { /* not used, try moving down the cascade */ for (i=0; (i < rowcol->composite.num_children) && (! found); i++) { child = rowcol->composite.children[i]; /* only check sensitive and managed cascade buttons */ if (XtIsSensitive(child) && XtIsManaged(child)) { if (XmIsCascadeButtonGadget(child)) { if (CBG_Submenu(child)) { /* Build the menu cascade for menuHistory */ RC_CascadeBtn(CBG_Submenu(child)) = child; found = ProcessKey (((XmCascadeButtonGadget)child)->cascade_button.submenu, event); } } else if (XmIsCascadeButton(child)) { if (CB_Submenu(child)) { RC_CascadeBtn(CB_Submenu(child)) = child; found = ProcessKey (((XmCascadeButtonWidget)child)->cascade_button.submenu, event); } } } } return (found); } else return (True); } /* * Check if the key event is used in the rowcol */ static Boolean CheckKey (rowcol, event) XmRowColumnWidget rowcol; XEvent * event; { int menu_index = 0; XmKeyboardData * entry; ShellWidget shell; /* Process all matching key events */ while ((menu_index = _XmMatchInKeyboardList(rowcol, event, menu_index)) != -1) { entry = MGR_KeyboardList(rowcol) + menu_index; /* Ignore this entry if it is not accessible to the user */ if (XmIsRowColumn(entry->component)) { /* * Rowcols are not accessible if they are insensitive or * if menubars or optionmenus are unmanaged. */ if (! XtIsSensitive(entry->component) || ((RC_Type(entry->component) != XmMENU_POPUP) && (RC_Type(entry->component) != XmMENU_PULLDOWN) && (! XtIsManaged(entry->component)))) { menu_index++; continue; } } /* buttons are not accessible if they are insensitive or unmanaged */ else if (! XtIsSensitive(entry->component) || ! XtIsManaged(entry->component)) { menu_index++; continue; } /* * For a mnemonic, the associated component must be visible, and * it must be in the last menupane posted. * This only needs to be checked for a popup or pulldown menu pane. */ if (entry->isMnemonic) { if ((XmIsLabel(entry->component) || XmIsLabelGadget(entry->component))) { if (IsBar(XtParent(entry->component)) && ! RC_PopupPosted(XtParent(entry->component)) && ((XmManagerWidget) XtParent(entry->component))-> manager.active_child == NULL) { menu_index++; continue; } else if (IsPopup(XtParent(entry->component)) || IsPulldown(XtParent(entry->component))) { /* See if the associated shell is visible */ shell = (ShellWidget)XtParent(XtParent(entry->component)); /* * Verify the pane is popped up, and the active pane is our * parent (this is necessary because of shared menupanes. */ if ((!shell->shell.popped_up) || (shell->composite.children[0] != XtParent(entry->component))) { menu_index++; continue; } /* Verify we are the last pane */ if (RC_PopupPosted(shell->composite.children[0])) { menu_index++; continue; } } } else if (XmIsRowColumn(entry->component)) { /* * Ignore the posting mnemonic for an option menu, if its * menupane is already posted. */ if (RC_PopupPosted(entry->component)) { menu_index++; continue; } } } /* Perform the action associated with the keyboard event */ if (XmIsPrimitive(entry->component)) { XmPrimitiveClassRec * prim; prim = (XmPrimitiveClassRec *)XtClass(entry->component); (*(prim->primitive_class.arm_and_activate)) (entry->component, event); } else if (XmIsGadget(entry->component)) { XmGadgetClassRec * gadget; gadget = (XmGadgetClassRec *)XtClass(entry->component); (*(gadget->gadget_class.arm_and_activate)) (entry->component, event); } else if (XmIsRowColumn(entry->component)) { XmRowColumnClassRec * rc; rc = (XmRowColumnClassRec *)XtClass(entry->component); (*(rc->row_column_class.armAndActivate)) (entry->component, event); } /* used the key */ _XmRecordEvent(event); return (True); } /* did not use the key */ return (False); } static void AddToPostFromList (m, widget) XmRowColumnWidget m; Widget widget; { if (m->row_column.postFromListSize == m->row_column.postFromCount) { /* increase the size to fit the new one and one more */ m->row_column.postFromListSize += 2; m->row_column.postFromList = (Widget *) XtRealloc (m->row_column.postFromList, m->row_column.postFromListSize * sizeof(Widget)); } m->row_column.postFromList[m->row_column.postFromCount++] = widget; } static void RemoveFromPostFromList (m, widget) XmRowColumnWidget m; Widget widget; { int i; Boolean found = False; for (i=0; i < m->row_column.postFromCount; i++) { if (!found) { if (widget == m->row_column.postFromList[i]) { /* remove this entry */ found = True; } } else m->row_column.postFromList[i-1] = m->row_column.postFromList[i]; } if (found) m->row_column.postFromCount--; } /* * This is a class function exported by the RowColumn widget. It is used * by the CascadeButton widget to signal that a menupane has either been * attached to a cascade button widget, or detached from a cascade button * widget. */ static void SetCascadeField (m, cascadeBtn, attach) XmRowColumnWidget m; Widget cascadeBtn; Boolean attach; { int mode; if (attach) { mode = XmADD; if (OnPostFromList (m, cascadeBtn) != -1) /* already in the list, this means no work to do */ return; AddToPostFromList (m, cascadeBtn); /* if being attached to an option menu, set the option menus submenu */ if (RC_Type(XtParent(cascadeBtn)) == XmMENU_OPTION) RC_OptionSubMenu(XtParent(cascadeBtn)) = (Widget) m; } else { mode = XmDELETE; RemoveFromPostFromList (m, cascadeBtn); /* if being removed from an option menu, set the option menus submenu */ if (RC_Type(XtParent(cascadeBtn)) == XmMENU_OPTION) RC_OptionSubMenu(XtParent(cascadeBtn)) = (Widget) NULL; } /* process the accelerators and mnemonics */ DoProcessMenuTree(m, mode); } /* * This function determines if the widget to which a menu is * attached is accessible to the user. The widget is considered * accessible if it, and its ancestors, are both sensitive and * managed. This is useful for MenuBars and Option Menus only. */ static Boolean _XmAllWidgetsAccessible (w) Widget w; { while (w && XtParent(w) && !XtIsShell(w)) { if (!XtIsSensitive(w) || !XtIsManaged(w) || !w->core.mapped_when_managed) return (False); w = XtParent(w); } return (True); } /* * Button Action Procs */ static void _XmMenuBtnDown (w, event) XmRowColumnWidget w; XButtonPressedEvent *event; { XmGadget gadget; if (! _XmMatchBtnEvent( event, RC_PostEventType(w), RC_PostButton(w), RC_PostModifiers(w)) || ! _XmIsEventUnique(event)) return; /* Overload _XmButtonEventStatus's time for MenuShell's managed_set_changed * routine to determine if an option menu is trying to post using BSelect * Click. _XmButtonEventStatus's verified is irrelevant. */ if (IsOption(w)) { _XmButtonEventStatus.time = event->time; } /* * It's possible that this event has been passed to this widget/window * due to a grabbed pointer. If so, it's probable that it doesn't belong * to any gadget children of the event's window (widget) where the pointer * is currently positioned. */ if (w->core.window == event->window) gadget = _XmInputInGadget(w, event->x, event->y); else gadget = NULL; if (gadget != NULL) { _XmDispatchGadgetInput(gadget, event, XmARM_EVENT); } /* * We'll popdown the menubar on the button up transition. Don't set * menu traversal off for the armed menubar. This it to make * sure that the up event is used by the menubar and not allowed to * accidentally be passed on to a possibly unarmed widget for activation. */ else if (!(IsBar(w) && RC_IsArmed(w))) SetMenuTraversal(w, False); } static void _XmMenuBtnUp (w, event) XmRowColumnWidget w; XButtonPressedEvent *event; { XmGadget gadget; if (! _XmIsEventUnique(event) || ! _XmMatchBtnEvent( event, XmIGNORE_EVENTTYPE, RC_PostButton(w), RC_PostModifiers(w)) || (IsBar(w) && ! RC_IsArmed(w))) return; if (w->core.window == event->window) gadget = _XmInputInGadget(w, event->x, event->y); else gadget = NULL; if (gadget != NULL) { _XmDispatchGadgetInput(gadget, event, XmACTIVATE_EVENT); } else if (IsBar(w)) { /* Only drop in here when no other widget took the event */ MenuPopDown(w, event); MenuBarCleanup(w); MenuDisarm(w); _XmMenuFocus(w, XmMENU_END, CurrentTime); XtUngrabPointer(w, CurrentTime); } } static void UpdateOptionMenuCBG (cbg, memWidget) Widget cbg; Widget memWidget; { char *thing = NULL, *thing_name = NULL; int thing_type; Boolean freeThing = FALSE; Arg al[4]; int ac = 0; if (XmIsLabelGadget(memWidget)) { XmLabelGadget lg = (XmLabelGadget) memWidget; if (LabG_IsText (lg)) { thing = (char *) _XmStringCreateExternal(LabG_Font(lg), LabG__label(lg)); thing_type = XmSTRING; thing_name = XmNlabelString; freeThing = True; } else { thing = (char *) LabG_Pixmap(lg); thing_type = XmPIXMAP; thing_name = XmNlabelPixmap; } } else if (XmIsLabel(memWidget)) { XmLabelWidget lw = (XmLabelWidget) memWidget; if (Lab_IsText (lw)) { thing = (char *) _XmStringCreateExternal(lw->label.font, lw->label._label); thing_type = XmSTRING; thing_name = XmNlabelString; freeThing = True; } else { thing = (char *)lw->label.pixmap; thing_type = XmPIXMAP; thing_name = XmNlabelPixmap; } } if ( ! IsNull (thing)) { XtSetArg (al[ac], XmNlabelType, thing_type); ac++; XtSetArg (al[ac], thing_name, thing); ac++; XtSetValues (cbg, al, ac); } if (freeThing) XmStringFree(thing); } static int is_in_widget_list (m, w) register XmRowColumnWidget m; RectObj w; { register Widget *q; register int i; if ((m == NULL) || (w == NULL)) return (FALSE); for (i = 0, q = m->composite.children; i < m->composite.num_children; i++, q++) if ((*q == (Widget) w) && IsManaged (*q)) return (TRUE); return (FALSE); } static int in_menu (search_m, parent_m, child, w) XmRowColumnWidget search_m; XmRowColumnWidget *parent_m; RectObj child; Widget *w; { if (is_in_widget_list (search_m, child)) { *parent_m = search_m; *w = (Widget) child; return (TRUE); } return (FALSE); } static Boolean search_menu (search_m, parent_m, child, w) XmRowColumnWidget search_m; XmRowColumnWidget *parent_m; RectObj child; Widget *w; { register Widget *q; register int i; if ( ! in_menu (search_m, parent_m, child, w)) { for (i = 0, q = search_m->composite.children; i < search_m->composite.num_children; i++, q++) { if (XtIsManaged(*q)) { if (XmIsCascadeButtonGadget(*q)) { XmCascadeButtonGadget p = (XmCascadeButtonGadget) *q; if (CBG_Submenu(p) && search_menu (CBG_Submenu(p),parent_m,child,w)) { return (TRUE); } } else if (XmIsCascadeButton(*q)) { XmCascadeButtonWidget p = (XmCascadeButtonWidget) *q; if (CB_Submenu(p) && search_menu (CB_Submenu(p),parent_m,child,w)) { return (TRUE); } } } } return (FALSE); } return (TRUE); } static void lota_magic (m, child, parent_m, w) XmRowColumnWidget m; RectObj child; XmRowColumnWidget *parent_m; Widget *w; { *parent_m = NULL; *w = NULL; search_menu (m, parent_m, child, w); } /* * called by the buttons to verify that the passed in event is one that * should be acted upon. This is called through the menuProcs handle */ static void VerifyMenuButton (w, event, valid) Widget w; XEvent * event; Boolean * valid; { *valid = event && _XmMatchBtnEvent( event, XmIGNORE_EVENTTYPE, RC_PostButton(w), RC_PostModifiers(w)); } /* * This routine is called at Initialize or SetValues time. It updates * the memory widget in the current rowcolumn and up to the top level(s) * menus. If there is a postFromList on the pulldown, it goes up each * branch. If an option menu is found at the top, then its cascadebutton * is updated with the latest stuff. */ static void UpdateMenuHistory (menu, child) XmRowColumnWidget menu; Widget child; { int i; RC_MemWidget(menu) = child; if (IsOption(menu)) { for (i = 0; i < menu->composite.num_children; i++) { if (XmIsCascadeButtonGadget(menu->composite.children[i])) { UpdateOptionMenuCBG (menu->composite.children[i], child); return; } } } else if (IsPulldown(menu)) { for (i=0; i < menu->row_column.postFromCount; i++) { UpdateMenuHistory (XtParent(menu->row_column.postFromList[i]), child); } } } /* * this is a mess... the menu spec'd is the menu to set the history for; * the child spec'd is the child who we are pretending fired-off. The * problem is the child may be in any sub-menu of this cascade. This is * called by Initialize or SetValues. */ static void SetMenuHistory (m, child) XmRowColumnWidget m; RectObj child; { XmRowColumnWidget parent_m; Widget w; if (IsNull (child)) return; /* make sure that the child is in the menu hierarchy */ lota_magic (m, child, &parent_m, &w); if (w) UpdateMenuHistory (parent_m, w); } static void all_off_except (m, w) XmRowColumnWidget m; Widget w; { register Widget *q; register int i; if (w) /* then all widgets except this one go off */ { ForManagedChildren (m, i, q) { if (*q != w) { if (XmIsToggleButtonGadget(*q)) { if (XmToggleButtonGadgetGetState (*q)) XmToggleButtonGadgetSetState (*q, FALSE, TRUE); } else if (XmIsToggleButton(*q)) { if (XmToggleButtonGetState (*q)) XmToggleButtonSetState (*q, FALSE, TRUE); } } } } } static int no_toggles_on (m) XmRowColumnWidget m; { register Widget *q; register int i; ForManagedChildren (m, i, q) { if (XmIsToggleButtonGadget(*q)) { if (XmToggleButtonGadgetGetState (*q)) return (FALSE); } else if (XmIsToggleButton(*q)) { if (XmToggleButtonGetState (*q)) return (FALSE); } } return (TRUE); } /* * note that this is potentially recursive, setting the state of a * toggle in this row column widget may re-enter this routine... */ static void RadioBehaviorAndMenuHistory (m, w) XmRowColumnWidget m; Widget w; { XmRowColumnWidget menu; Widget cb; Boolean done = FALSE; if (! IsManaged(w)) return; if (RC_RadioBehavior(m)) { if (XmIsToggleButtonGadget(w)) { /* turned on */ if (XmToggleButtonGadgetGetState (w)) all_off_except (m, w); /* he turned off */ else { if (RC_RadioAlwaysOne(m)) if (no_toggles_on (m)) /* can't allow that */ XmToggleButtonGadgetSetState (w, TRUE, TRUE); } } else if (XmIsToggleButton (w)) { /* turned on */ if (XmToggleButtonGetState (w)) all_off_except (m, w); /* turned off */ else { if (RC_RadioAlwaysOne(m)) if (no_toggles_on (m)) /* can't allow that */ XmToggleButtonSetState (w, TRUE, TRUE); } } /* record for posterity */ RC_MemWidget (m) = w; } /* record the mouse memory and history widget all the way up the cascade */ menu = m; while ( ! done) { RC_MemWidget (menu) = w; if (! IsPopup(menu) && RC_CascadeBtn(menu)) { cb = RC_CascadeBtn(menu); menu = (XmRowColumnWidget) XtParent (cb); } else done = TRUE; } /* option menu cascade button gadget must be updated */ if (IsOption(menu)) UpdateOptionMenuCBG (cb, w); } static char * which_callback (w) Widget w; { if (XmIsPushButtonGadget(w) || XmIsPushButton(w) || XmIsCascadeButton(w) || XmIsCascadeButtonGadget(w) || XmIsDrawnButton(w)) return (XmNactivateCallback); if (XmIsToggleButtonGadget(w) || XmIsToggleButton(w)) return (XmNvalueChangedCallback); return (NULL); } /* * This routine is used to emulate the override callback functionality that * was available in the R3 library used by Xm and to do the radio behavior * and menu history functionality for RowColumns. The buttons call this * function through the MenuProcs interface. */ static void ChildsActivateCallback (rowcol, child, call_value) XmRowColumnWidget rowcol; Widget child; Opaque call_value; { Arg arg[1]; int i; XtCallbackList callbacks; XmRowColumnWidget topLevel; char *c = which_callback (child); /* which callback to use */ /* * Save the last selected toplevel in each menupane. * "lastSelectToplevel" only set for accelerators via KeyboardInputHandler(). * Do this before entrycallback because the application may call * XmGetPostedFromWidget() from there. */ if (lastSelectToplevel) rowcol->row_column.lastSelectToplevel = lastSelectToplevel; else { _XmGetActiveTopLevelMenu (rowcol, &topLevel); rowcol->row_column.lastSelectToplevel = (Widget) topLevel; } if (rowcol->row_column.entry_callback != NULL) { XtSetArg (arg[0], c, &callbacks); XtGetValues (child, arg, 1); /* make sure the all of the drawing has been done before the callback */ XFlush (XtDisplay (rowcol)); /* * cycle through the list and call the entry fired routine for each * entry in this callback list, sending in the data for each entry. * If the list is NULL, or empty, call the entry fired function once. */ if ((callbacks == NULL) || (callbacks[0].callback == NULL)) EntryFired (child, NULL, call_value); else { for (i=0; callbacks[i].callback != NULL; i++) EntryFired (child, callbacks[i].closure, call_value); } } else /* no entry callback, but must do radio behavior & menu history */ EntryFired (child, NULL, call_value); } /* * This is the callback for widgets which are composited into row column * widgets. It notifies the menu that some individual widget fired off; * this allows * the row column widget to tell the application if it wants * to know. also to do various other automagical things */ static void EntryFired (w, client_data, callback) Widget w; /* ptr to widget that fired */ caddr_t client_data; XmAnyCallbackStruct *callback; { XmRowColumnWidget m = (XmRowColumnWidget) XtParent (w); XmRowColumnCallbackStruct mr; mr.reason = XmCR_ACTIVATE; /* menu activated */ mr.widget = w; mr.data = client_data; mr.callbackstruct = (char *) callback; /* subwidget structure */ mr.event = callback->event; XtCallCallbackList ((Widget) NULL, m->row_column.entry_callback, &mr); RadioBehaviorAndMenuHistory (m, w); } /************************************************************************* * * this section is all the layout stuff, the whole thing is messy because * it has to operate in two different modes, one: a read-only mode which * is nice for making decisions about the size of the row column vs. the size * of the children. two: a change everything mode which implements the * change. * * further complicated by the xtoolkit restriction that a subwidget making * a geo request (referred to as the 'instigator') of the row column may not * have his resize proc called but all other widget children must. * * this is done by building a set of XtWidgetGeometry request blocks, one * for each child (widget and gadget), which holds the changes we would like to * make for this child. If needed then another pass is made over the requests * to actually implement the changes. */ /* * count the widest & tallest entry dimensions * and compute entries per row/column */ static int get_info (m, w, h, items_per) XmRowColumnWidget m; Dimension *w, *h; int *items_per; { XtWidgetGeometry *b; int i, n = 0;; *w = *h = 0; for (i=0; RC_Boxes (m) [i].kid != NULL; i++) { b = &(RC_Boxes (m) [i].box); n++; if (*w < BWidth (b)) *w = BWidth (b); if (*h < BHeight (b)) *h = BHeight (b); } *items_per = n / RC_NCol (m); /* calc column size */ if ((n % RC_NCol (m)) != 0) /* some left overs */ (*items_per)++; /* add another row/col */ } /* * Make sure that entries in the right most column/row extend all the * way to the right/bottom edge of the row column widget. This keeps * 'dead space' in the row column widget to a minimum. For single * column widgets, the only column is the right most. * */ static int adjust_last (m, start_i, w, h) XmRowColumnWidget m; int start_i; Dimension w, h; { XmKidGeometry kg = RC_Boxes (m); XtWidgetGeometry *b; Dimension a; register Dimension subtrahend; for ( ; kg [start_i].kid != NULL; start_i++) { b = &(kg[start_i].box); if (IsVertical (m)) { subtrahend = MGR_ShadowThickness(m) + RC_MarginW (m) + BX (b) + Double (BBorder (b)); /* if w (rowcol width) is greater than subtrahend (the smallest * width of the child, we'll guarantee at least a width of 1. */ if (w > subtrahend) BWidth (b) = w-subtrahend; } else { subtrahend = MGR_ShadowThickness(m) + RC_MarginH (m) + BY (b) + Double (BBorder (b)); if (h > subtrahend) BHeight (b) = h-subtrahend; } } } /* * decide exactly the dimensions of the row column widget we will return to * an asking caller based on the accumulated layout information. */ static void set_asking (m, m_width, m_height, b, max_x, max_y, x, y, w, h) XmRowColumnWidget m; Dimension *m_width, *m_height; /* if 0 then caller's asking */ Dimension b; Position max_x, max_y, x, y; Dimension w, h; { if (IsVertical (m)) /* tell caller what he wants */ { if (Asking (*m_width)) *m_width = x + w + b /* right edge of last child */ + MGR_ShadowThickness(m) + RC_MarginW (m); /* plus margin on right */ if (Asking (*m_height)) *m_height = max_y /* last unused y */ - RC_Spacing (m) /* up by unused spacing */ + MGR_ShadowThickness(m) + RC_MarginH (m); /* plus margin on bottom */ } else { if (Asking (*m_width)) *m_width = max_x - RC_Spacing (m) + MGR_ShadowThickness(m) + RC_MarginW (m); if (Asking (*m_height)) *m_height = y + h + b + MGR_ShadowThickness(m) + RC_MarginH (m); } } /* * Decide where to put the help child. He better be the last one * 'cuz we may trash the x, y's */ static void calc_help (m, m_width, m_height, b, max_x, max_y, x, y, w, h) XmRowColumnWidget m; Dimension *m_width, *m_height, /* if 0 then caller's asking */ b; Position max_x, max_y, *x, *y; Dimension w, h; { register Dimension subtrahend; if (IsVertical (m)) /* glue to bottom edge of ... */ { if (Asking (*m_height)) { if (RC_NCol (m) == 1) /* just use max_y */ *y = max_y; else /* go up from max_y */ { subtrahend = RC_Spacing (m) + h + b; *y = (max_y > subtrahend) ? max_y - subtrahend : 0; } } else { subtrahend = MGR_ShadowThickness(m) + RC_MarginH (m) + h + b; *y = (*m_height > subtrahend) ? *m_height - subtrahend : 0; } } else /* glue to right edge of ... */ { if (Asking (*m_width)) { if (RC_NCol (m) == 1) *x = max_x; else { subtrahend = RC_Spacing (m) + w + b; *x = (max_x > subtrahend) ? max_x - subtrahend : 0; } } else { subtrahend = MGR_ShadowThickness(m) + RC_MarginW (m) + w + b; *x = (*m_width > subtrahend) ? *m_width - subtrahend : 0; } } } /* * figure out where all the children of a column style widget go. The * border widths are already set. * * In columnar mode, all heights and widths are identical. They are the * size of the largest item. * * For vertical widgets the items are laid out in columns, going down the * first column and then down the second. For horizonatal, think of the * columns as rows. * * By convention incoming row column size can be zero, indicating a request * for preferred size, this means lay it out and record the needed size. * * NOTE: the layout is predicated on the help child being the last one since * it messes up the x, y for a following child. */ static int layout_column (m, m_width, m_height) XmRowColumnWidget m; Dimension *m_width, *m_height; /* if 0 then caller's asking */ { XmKidGeometry kg = RC_Boxes (m); XtWidgetGeometry *bx; Position x, y, max_x = 0, max_y = 0; int items_per_column, child_i = 0, /* which child we are doing */ col_c = 0, /* items in col being done */ start_i = 0; /* index of first item in col */ Dimension w, h; Dimension b = Double (RC_EntryBorder (m)); /* loc of first item */ x = MGR_ShadowThickness(m) + RC_MarginW (m); y = MGR_ShadowThickness(m) + RC_MarginH (m); get_info (m, &w, &h, &items_per_column); for (child_i=0; kg [child_i].kid != NULL; child_i++) { bx = &(kg[child_i].box); if (!RC_EntryBorder(m) && XtIsWidget(kg[child_i].kid)) b = Double(kg[child_i].kid->core.border_width); BWidth (bx) = w; /* all have same dimensions */ BHeight (bx) = h; if (++col_c > items_per_column) /* start a new column */ { if (IsVertical (m)) /* calc loc of new column */ { x += w + b + RC_Spacing (m); /* to the right */ /*back at top of menu */ y = MGR_ShadowThickness(m) + RC_MarginH (m); } else /* calc loc of new row */ { /* back to left edge */ x = MGR_ShadowThickness(m) + RC_MarginW (m); /* down a row */ y += h + b + RC_Spacing (m); } col_c = 1; /* already doing this one */ start_i = child_i; /* record index */ } if (IsHelp (m, ((Widget) kg[child_i].kid))) calc_help (m, m_width, m_height, b, max_x, max_y, &x, &y, w, h); SetPosition (bx, x, y); /* plunk him down */ if (IsVertical (m)) /* get ready for next item */ y += h + b + RC_Spacing (m); else x += w + b + RC_Spacing (m); if (max_y < y) max_y = y; /* record for use later */ if (max_x < x) max_x = x; } set_asking (m, m_width, m_height, b, max_x, max_y, x, y, w, h); if (RC_AdjLast(m)) adjust_last (m, start_i, *m_width, *m_height); } /* * do a vertical tight (non-column) layout. * * In a tight layout one dimension of the items is left alone and the other * is kept uniform. In a vertical row column widgets, the widths of each child * are uniform for each column, the heights are never changed. In a horiz * row column widget, the widths are never changed and the heights are kept * uniform for each row. * * It gets messy w.r.t. the help child because we don't know if there will * be room in the last column/row for it. If there isn't room then a whole * new column/row has to be added. * * NOTE: the layout is predicated on the help child being the last one since * it messes up the x, y for a following child. */ static int layout_vertical_tight (m, m_width, m_height) XmRowColumnWidget m; Dimension *m_width, *m_height; /* if 0 then caller's asking */ { XmKidGeometry kg = RC_Boxes (m); XtWidgetGeometry *bx; Position x, y, max_y = 0; Dimension h; Dimension w = 0; /* widest item width in col */ int child_i = 0, start_i = 0; /* index of first item in col */ Dimension b = Double (RC_EntryBorder (m)); /* first item location */ x = MGR_ShadowThickness(m) + RC_MarginW (m); y = MGR_ShadowThickness(m) + RC_MarginH (m); for (child_i=0; kg [child_i].kid != NULL; child_i++) { bx = &(kg[child_i].box); if (!RC_EntryBorder(m) && XtIsWidget(kg[child_i].kid)) b = Double(kg[child_i].kid->core.border_width); h = BHeight (bx) + b; /* calc this item's height */ if (((y + h) > *m_height) && ( ! Asking (*m_height)) && (child_i)) { /* start new column */ while (start_i < child_i) kg[start_i++].box.width = w; /* set uniform width */ x += w + b + MGR_ShadowThickness(m) + RC_MarginW (m); /* go right and */ y = MGR_ShadowThickness(m) + RC_MarginH (m); /* back to top of menu */ w = BWidth (bx); /* reset for new column */ } if (IsHelp (m, ((Widget) kg[child_i].kid))) calc_help (m, m_width, m_height, b, 0, max_y, &x, &y, w, h); SetPosition (bx, x, y); if (w < BWidth (bx)) w = BWidth (bx); y += h + RC_Spacing (m); /* loc of next item */ if (max_y < y) max_y = y; /* record for use later */ } set_asking (m, m_width, m_height, b, 0, max_y, x, y, w, h); if (RC_AdjLast(m)) adjust_last (m, start_i, *m_width, *m_height); else while (start_i < child_i) kg[start_i++].box.width = w; /* set uniform width */ } static int layout_horizontal_tight (m, m_width, m_height) XmRowColumnWidget m; Dimension *m_width, *m_height; /* if 0 then caller's asking */ { XmKidGeometry kg = RC_Boxes (m); XtWidgetGeometry *bx; Position x, y, max_x = 0; Dimension w; Dimension h = 0; /* tallest item height in row */ int child_i = 0, start_i = 0; /* index of first item in row */ Dimension b = Double (RC_EntryBorder (m)); /* first item location */ x = MGR_ShadowThickness(m) + RC_MarginW (m); y = MGR_ShadowThickness(m) + RC_MarginH (m); for (child_i=0; kg [child_i].kid != NULL; child_i++) { bx = &(kg[child_i].box); if (!RC_EntryBorder(m) && XtIsWidget(kg[child_i].kid)) b = Double(kg[child_i].kid->core.border_width); w = BWidth (bx) + b; /* item's width */ if (((x + w) > *m_width) && ( ! Asking (*m_width)) && (child_i)) { /* start a new row */ while (start_i < child_i) kg[start_i++].box.height = h; /* set uniform height */ x = MGR_ShadowThickness(m) + RC_MarginW (m); /* left edge of menu */ y += h + b + MGR_ShadowThickness(m) + RC_MarginH (m); /* down to top of next row */ h = BHeight (bx); /* reset for this row */ } if (IsHelp (m, ((Widget) kg[child_i].kid))) calc_help (m, m_width, m_height, b, max_x, 0, &x, &y, w, h); SetPosition (bx, x, y); if (h < BHeight (bx)) h = BHeight (bx); x += w + RC_Spacing (m); /* loc of next item */ if (max_x < x) max_x = x; /* record for use later */ } set_asking (m, m_width, m_height, b, max_x, 0, x, y, w, h); if (RC_AdjLast(m)) adjust_last (m, start_i, *m_width, *m_height); else while (start_i < child_i) kg[start_i++].box.height = h; /* set uniform height */ } static int layout_vertical (m, m_width, m_height) XmRowColumnWidget m; Dimension *m_width, *m_height; /* if 0 then caller's asking */ { if (PackColumn (m)) layout_column (m, m_width, m_height); else layout_vertical_tight (m, m_width, m_height); } static int layout_horizontal (m, m_width, m_height) XmRowColumnWidget m; Dimension *m_width, *m_height; /* if 0 then caller's asking */ { if (PackColumn (m)) layout_column (m, m_width, m_height); else layout_horizontal_tight (m, m_width, m_height); } /* * wrap a box around the entries, used with packing mode of none. * * we ignore negative positioning, ie. only worry about being wide enough * for the right edge of the rightmost entry (similarly for height) */ static int bound_entries (m, m_width, m_height) XmRowColumnWidget m; Dimension *m_width, *m_height; /* if 0 then caller's asking */ { XtWidgetGeometry *b; XmKidGeometry kg = RC_Boxes (m); int i = 0; Dimension w, max_w = 0, max_h = 0; Dimension bw = Double(RC_EntryBorder(m)); for (i=0; kg [i].kid != NULL; i++) { b = &(kg[i].box); if (!RC_EntryBorder(m) && XtIsWidget(kg[i].kid)) bw = Double(kg[i].kid->core.border_width); if (Asking (*m_width)) { w = BWidth (b) + bw + BX (b); if (w > max_w) max_w = w; } if (Asking (*m_height)) { w = BHeight (b) + Double (bw) + BY (b); if (w > max_h) max_h = w; } } if (Asking (*m_width)) *m_width = max_w; if (Asking (*m_height)) *m_height = max_h; } /* This routine (option_layout) does the layout for option menus. This layout is a tiled layout with the option label widget to the left of the option button widget. */ /* ARGSUSED */ static void option_layout (menu, width, height) XmRowColumnWidget menu; Dimension *width, *height; { Dimension h; XmKidGeometry kg; XtWidgetGeometry *label_box, *button_box; register int i; register Dimension c_width=0; register Dimension c_height=0; register XmRowColumnWidget p = (XmRowColumnWidget) RC_OptionSubMenu(menu); Widget cb = XmOptionButtonGadget(menu); /* Find the interesting boxes */ kg = RC_Boxes(menu); label_box = &(kg[0].box); button_box = &(kg[1].box); if (p && p->composite.num_children) { for(i=0; i < p->composite.num_children; i++) if (XtIsManaged(p->composite.children[i])) { if ((p->composite.children[i])->core.width > c_width) c_width = (p->composite.children[i])->core.width; if ((p->composite.children[i])->core.height > c_height) c_height = (p->composite.children[i])->core.height; } /* * make sure the width of the cascade button will hold the longest * label plus the cascade pixmap. */ BWidth(button_box) = c_width + Double(G_HighlightThickness(cb)) + LabG_MarginRight(cb); BHeight(button_box) = c_height + Double(G_HighlightThickness(cb)); } else { /* Option menu draws a toggle indicator with a childless submenu */ if (p && !p->composite.num_children && IsOption(menu)) BWidth(button_box) = LabG_TextRect(cb).width + LabG_MarginRight(cb) + LabG_MarginLeft(cb) + (2*(LabG_MarginWidth(cb) + G_ShadowThickness(cb) + G_HighlightThickness(cb))); else BWidth(button_box) = XtWidth(cb); BHeight(button_box) = XtHeight(cb); } /* Set the height to the highest of the two */ h = MAX(BHeight(label_box),BHeight(button_box)); BHeight(label_box) = h; BHeight(button_box) = h; /* The label box is placed at... */ BX(label_box) = RC_MarginW(menu); BY(label_box) = RC_MarginH(menu); /* The button is placed just next to the label */ BX(button_box) = BX(label_box) + BWidth(label_box) + RC_Spacing(menu); BY(button_box) = RC_MarginH(menu); } static void Layout (m, w, h) XmRowColumnWidget m; Dimension *w, *h; { if (IsOption(m)) option_layout(m,w,h); else if (IsVertical (m)) layout_vertical (m, w, h); else layout_horizontal (m, w, h); } /************************************************************************** * * class support procedures */ /* * position then row column widget where it wants to be; normally, this * is used only for popup or pulldown menupanes. */ static void PositionMenu (m, event) register XmRowColumnWidget m; XButtonPressedEvent *event; { XmRowColumnWidget root = NULL; XmCascadeButtonWidget p; if (m == NULL) return; switch (m->row_column.type) { case XmMENU_OPTION: case XmMENU_BAR: case XmWORK_AREA: break; /* * remaining cases take advantage of the fact that positioning of * a popup shells' only child is not normal. Just change the widget * itself, when popped up the shell will put itself at this location * and move the child to 0,0. Saves a bunch of thrashing about. */ case XmMENU_POPUP: /* position near mouse */ XtX (m) = event->x_root; XtY (m) = event->y_root; RC_SetWidgetMoved (m, TRUE); /* popups keep active widget in postFromLIst in cascadeBtn field */ RC_CascadeBtn(m) = XtWindowToWidget (XtDisplay(m), event->window); break; case XmMENU_PULLDOWN: p = (XmCascadeButtonWidget) m->row_column.cascadeBtn; if (p != NULL) root = (XmRowColumnWidget) XtParent (p); else return; if (! XmIsRowColumn(root)) root = NULL; LocatePulldown( root, /* menu above */ p, /* pulldown linking the two */ m, /* menu to position */ event); /* event causing pulldown */ if ((XtX (m) == XtX (XtParent(m))) && (XtY (m) == XtY (XtParent(m)))) { XtX (m) = 0; XtY (m) = 0; } else RC_SetWidgetMoved (m, TRUE); break; } } static Widget find_first_managed_child(m, first_button) CompositeWidget m; Boolean first_button; { register Widget *kid = m->composite.children; register int i = 0; register int n = m->composite.num_children; while( (i < n) && (!XtIsManaged(*kid) || (first_button && !(XmIsPushButton(*kid) || XmIsPushButtonGadget(*kid) || XmIsToggleButton(*kid) || XmIsToggleButtonGadget(*kid)) )) ) kid++, i++; if (i >= n) return(NULL); else return(*kid); } /* * called by cascadebutton (or CBG) before cascading a menu. The interface * is through the menuProcs handle. */ static void PrepareToCascade (cb, submenu, event) Widget cb; XmRowColumnWidget submenu; XEvent * event; { RC_CascadeBtn(submenu) = cb; RC_PostButton(submenu) = RC_PostButton(XtParent(cb)); RC_PostModifiers(submenu) = RC_PostModifiers(XtParent(cb)); RC_PostEventType(submenu) = RC_PostEventType(XtParent(cb)); RC_PopupPosted(XtParent(cb)) = XtParent(submenu); if (IsOption(XtParent(cb))) RC_MemWidget(submenu) = RC_MemWidget(XtParent(cb)); PositionMenu (submenu, event); submenu->manager.traversal_on = ((XmManagerWidget)XtParent(cb))->manager.traversal_on; if (!submenu->manager.traversal_on) submenu->manager.active_child = NULL; MS_FocusPolicy(XtParent(submenu)) = submenu->manager.traversal_on ? XmEXPLICIT : XmPOINTER; } /* ARGSUSED */ static void LocatePulldown (root, p, m, event) XmRowColumnWidget root; /* menu holding pulldown */ XmCascadeButtonWidget p; /* cascadebutton doing the pulling */ XmRowColumnWidget m; /* sub-menu to position */ { Position x, y, x1, y1; if (root == NULL) return; x = y = 0; /* punt, don't know */ if (IsOption (root)) /* near hot spot */ { /* of option button (p) */ if (! XtIsRealized (m)) XtRealizeWidget (m); x = 0; y = RC_MemWidget(m) ? (Half (XtHeight (p)) - (XtY(RC_MemWidget(m)) + Half(XtHeight(RC_MemWidget(m))))) : XtY(p); } else if (IsBar (root)) /* aligned with left edge of */ { /* cascade button, below cascade button */ x = 0; y = XtHeight (p); } else if (XmIsCascadeButtonGadget(p) && CBG_HasCascade(p)) { Widget first; first = find_first_managed_child(m, ANY_CHILD); /* gadget; have to use parent as base for coordinates */ x = XtX(p) + CBG_Cascade_x(p) + CBG_Cascade_width(p); y = XtY(p) + CBG_Cascade_y(p) + Half(CBG_Cascade_height(p)) - (first ? (XtY(first) + Half(XtHeight(first))) : 0); /* cast only to eliminate warning */ p = (XmCascadeButtonWidget)XtParent(p); } else if (XmIsCascadeButton(p) && CB_HasCascade(p)) { Widget first; first = find_first_managed_child(m, ANY_CHILD); x = CB_Cascade_x(p) + CB_Cascade_width(p); y = CB_Cascade_y(p) + Half(CB_Cascade_height(p)) - (first ? (XtY(first) + Half(XtHeight(first))) : 0); } /* * Take these co-ords which are in the cascade button * widget's co-ord system and convert them to the root * window co-ords. */ XtTranslateCoords(p, x, y, &x1, &y1); XtX (m) = x1; XtY (m) = y1; } /* * lay out the row column widget to find it's best size, this is a * read-only operation */ /* ARGSUSED */ static void think_about_option_size(m, w, h, instigator, request) register XmRowColumnWidget m; Dimension *w, *h; Widget instigator; XtWidgetGeometry *request; { register int i; Dimension width=0, height=0; register XmRowColumnWidget p = (XmRowColumnWidget) RC_OptionSubMenu(m); Widget cb = XmOptionButtonGadget(m); /* * The box list was built in the order that the children were * created, so the label is first and the cascade button is second * (any other children are erroneous). */ RC_Boxes(m)[0].box.x = RC_MarginW(m); RC_Boxes(m)[0].box.y = RC_MarginH(m); *w = RC_MarginW(m) + RC_Boxes(m)[0].box.width + RC_Spacing(m); *h = RC_Boxes(m)[0].box.height; /* * Cascade Button should really prefer to be the size neccessary to * display the submenu data, but that's not implemented yet so we * just have to kludge a bit. */ if (p && p->composite.num_children) { for(i=0; i < p->composite.num_children; i++) { if (XtIsManaged(p->composite.children[i])) { if ((p->composite.children[i])->core.width > width) width = (p->composite.children[i])->core.width; if ((p->composite.children[i])->core.height >height) height =(p->composite.children[i])->core.height; } } /* * make sure the width of the cascade button will hold the longest * label plus the cascade pixmap. */ width += LabG_MarginRight(cb); } else { /* Option menu draws a toggle indicator with a childless submenu */ if (p && !p->composite.num_children && IsOption(m)) width = LabG_TextRect(cb).width + LabG_MarginRight(cb) + LabG_MarginLeft(cb) + (2*(LabG_MarginWidth(cb) + G_ShadowThickness(cb))); else width = RC_Boxes(m)[1].box.width; height = RC_Boxes(m)[1].box.height; } height += Double(G_HighlightThickness(cb)); RC_Boxes(m)[1].box.x = *w; RC_Boxes(m)[1].box.y = RC_MarginH(m); RC_Boxes(m)[1].box.width = width; RC_Boxes(m)[1].box.height = height; *w += width + RC_MarginW(m) + Double(G_HighlightThickness(cb)); if (height > *h) { RC_Boxes(m)[0].box.height = height; *h = height; } *h += Double(RC_MarginH(m)); } static void think_about_size (m, w, h, instigator, request) register XmRowColumnWidget m; Dimension *w, *h; Widget instigator; XtWidgetGeometry *request; { if (IsOption(m)) { think_about_option_size(m, w, h, instigator, request); return; } if (PackNone (m)) bound_entries (m, w, h); else Layout (m, w, h); if (!RC_ResizeWidth(m)) *w = XtWidth (m); if (!RC_ResizeHeight(m)) *h = XtHeight (m); if (!RC_ResizeHeight(m) && !RC_ResizeWidth(m)) return; if (*w < resource_min_width) *w = resource_min_width; if (*h < resource_min_height) *h = resource_min_height; } static void PreferredSize (m, w, h) XmRowColumnWidget m; Dimension *w, *h; { /* * get array built for both widgets and gadgets layout is based only on * this array, adjust width margins & adjust height margins */ RC_Boxes(m)= (XmKidGeometry)_XmGetKidGeo (m, NULL, NULL, RC_EntryBorder(m), RC_EntryBorder (m), (IsVertical (m) && RC_DoMarginAdjust (m)), (IsHorizontal (m) && RC_DoMarginAdjust (m)), RC_HelpPb (m), XmGET_PREFERRED_SIZE); think_about_size (m, w, h, NULL, NULL); XtFree (RC_Boxes(m)); } static XtGeometryResult QueryGeometry (m, intended, reply) XmRowColumnWidget m; /* me */ XtWidgetGeometry *intended, *reply; { Dimension w, h; if (GMode (intended) & ( ~ (CWWidth | CWHeight))) return (XtGeometryYes); /* * pre-load the reply with input values or flag to preferred_size proc * that we are asking for a value */ if ((GMode (intended) & CWWidth) && !(GMode (intended) & CWHeight)) { w = intended->width; h = 0; PreferredSize (m, &w, &h); GMode(reply) = (CWHeight | CWWidth); reply->width = w; reply->height = h; return(XtGeometryAlmost); } else if ((GMode (intended) & CWHeight) && !(GMode (intended) & CWWidth)) { w = 0; h = intended->width; PreferredSize (m, &w, &h); GMode(reply) = (CWHeight | CWWidth); reply->width = w; reply->height = h; return(XtGeometryAlmost); } else /* both width and height */ { w = 0; h = 0; PreferredSize (m, &w, &h); if ((w == m->core.width) && (h == m->core.height)) { if (GMode(intended) == (CWWidth | CWHeight)) return(XtGeometryNo); else { GMode(reply) = (CWHeight | CWWidth); reply->width = w; reply->height = h; return(XtGeometryAlmost); } } else if ((w == intended->width) && (h == intended->height)) return(XtGeometryYes); else { GMode(reply) = (CWHeight | CWWidth); reply->width = w; reply->height = h; return(XtGeometryAlmost); } } } /* * Layout the row column widget to fit it's current size; ignore possible * non-fitting of the entries into a too small row column widget. * * Don't forget the instigator, */ static void AdaptToSize (m, instigator, request) XmRowColumnWidget m; Widget instigator; XtWidgetGeometry *request; { Dimension w = XtWidth (m); Dimension h = XtHeight (m); /* * get array built for both widgets and gadgets, * layout is based only on this array, * adjust width margins and adjust height margins */ RC_Boxes(m) = (XmKidGeometry)_XmGetKidGeo (m, instigator, request, RC_EntryBorder(m), RC_EntryBorder (m), (IsVertical (m) && RC_DoMarginAdjust (m)), (IsHorizontal (m) && RC_DoMarginAdjust (m)), RC_HelpPb (m), (IsOption(m) ? XmGET_ACTUAL_SIZE : XmGET_PREFERRED_SIZE)); if (!PackNone (m)) Layout (m, &w, &h); _XmSetKidGeo (RC_Boxes(m), instigator); XtFree (RC_Boxes(m)); } /* * Resize the row column widget, and any subwidgets there are. * Since the gravity is set to NW, handle shrinking when there may not * be a redisplay. */ static void Resize(m) XmRowColumnWidget m; { Boolean draw_shadow = False; /* clear the shadow if its needed (will check if its now larger) */ _XmClearShadowType (m, m->row_column.old_width, m->row_column.old_height, m->row_column.old_shadow_thickness, 0); /* * if it is now smaller, redraw the shadow since there may not be a * redisplay - DON'T draw shadows for OPTION MENUS! */ if (!IsOption(m) && (m->row_column.old_height > m->core.height || m->row_column.old_width > m->core.width)) draw_shadow = True; m->row_column.old_width = m->core.width; m->row_column.old_height = m->core.height; m->row_column.old_shadow_thickness = m->manager.shadow_thickness; AdaptToSize (m, NULL, NULL); if (draw_shadow && XtIsRealized (m) && m->manager.shadow_thickness ) /* pop-out not pop-in */ _XmDrawShadow (XtDisplay (m), XtWindow (m), m->manager.top_shadow_GC, m->manager.bottom_shadow_GC, m->manager.shadow_thickness, 0, 0, m->core.width, m->core.height); } /* * class Redisplay proc */ static void Redisplay (m, event, region) XmRowColumnWidget m; XEvent *event; Region region; { XEvent tempEvent; /* Ignore exposures generated as we unpost */ if ((IsPopup (m) || IsPulldown (m)) && !((XmMenuShellWidget)XtParent(m))->shell.popped_up) { RC_SetExpose (m, TRUE); return; } if (RC_DoExpose (m)) /* a one-shot set on popup */ { /* so we ignore next expose */ if (event == NULL) /* Fast exposure is happening */ { event = &tempEvent; event->xexpose.x = 0; event->xexpose.y = 0; event->xexpose.width = m->core.width; event->xexpose.height = m->core.height; } _XmRedisplayGadgets(m,event,region); if (IsPopup (m) || IsPulldown (m) || IsBar(m)) { if (MGR_ShadowThickness(m)) _XmDrawShadow (XtDisplay (m), XtWindow (m), /* pop-out not pop-in */ m->manager.top_shadow_GC, m->manager.bottom_shadow_GC, m->manager.shadow_thickness, 0, 0, m->core.width, m->core.height); } } RC_SetExpose (m, TRUE); } /* * Geometry manager for subwidgets of a row column widget; be accomdating, * try to say yes, and then deal with our parent's geometry mgr. * * I'm not even going to try to figure out what to do with an Almost * response from the row column widget's parent; just take it as a 'No'. */ static XtGeometryResult GeometryManager (instigator, desired, allowed) Widget instigator; XtWidgetGeometry *desired, *allowed; { XmRowColumnWidget m = (XmRowColumnWidget) XtParent(instigator); Dimension w = 0; Dimension h = 0; XtGeometryResult result = XtGeometryYes; /* * find out what size we need to be with this new widget */ RC_Boxes(m) = (XmKidGeometry) _XmGetKidGeo (m, instigator, desired, RC_EntryBorder(m), RC_EntryBorder (m), (IsVertical (m) && RC_DoMarginAdjust (m)), (IsHorizontal (m) && RC_DoMarginAdjust (m)), RC_HelpPb (m), XmGET_PREFERRED_SIZE); think_about_size(m,&w,&h,instigator,desired); if (IsOption(m)) { if (instigator == XmOptionButtonGadget(m)) { /* * Grow only */ if ((desired->request_mode & CWWidth) && (desired->width < RC_Boxes(m)[1].box.width)) { allowed->width = RC_Boxes(m)[1].box.width; allowed->height = RC_Boxes(m)[1].box.height; allowed->request_mode = (CWHeight | CWWidth); result = XtGeometryAlmost; } if ((desired->request_mode & CWHeight) && (desired->height < RC_Boxes(m)[1].box.height)) { allowed->width = RC_Boxes(m)[1].box.width; allowed->height = RC_Boxes(m)[1].box.height; allowed->request_mode = (CWHeight | CWWidth); result = XtGeometryAlmost; } if (result != XtGeometryYes) { XtFree(RC_Boxes(m)); return(result); } } if (instigator == XmOptionLabelGadget(m)) { /* * Can't get shorter */ if ((desired->request_mode & CWHeight) && (desired->height < RC_Boxes(m)[0].box.height)) { allowed->width = RC_Boxes(m)[0].box.width; allowed->height = RC_Boxes(m)[0].box.height; allowed->request_mode = (CWHeight | CWWidth); result = XtGeometryAlmost; } if (result != XtGeometryYes) { XtFree(RC_Boxes(m)); return(result); } } } /* * now decide if the menu needs to change size. */ XtFree(RC_Boxes(m)); if ((w != XtWidth (m)) || (h != XtHeight (m))) { XtWidgetGeometry menu_desired, menu_allowed; menu_desired.request_mode = 0; if (w != XtWidth (m)) { menu_desired.width = w; menu_desired.request_mode |= CWWidth; } if (h != XtHeight (m)) { menu_desired.height = h; menu_desired.request_mode |= CWHeight; } result = XtMakeGeometryRequest(m,&menu_desired,&menu_allowed); switch (result) { case XtGeometryAlmost: case XtGeometryNo: /* give it up, don't change */ return (XtGeometryNo); /* us or our children */ default: /* fall out */ break; } } AdaptToSize(m,instigator,desired); /* Clear shadow if necessary. */ if (m->row_column.old_shadow_thickness) _XmClearShadowType (m, m->row_column.old_width, m->row_column.old_height, m->row_column.old_shadow_thickness, 0); m->row_column.old_width = m->core.width; m->row_column.old_height = m->core.height; m->row_column.old_shadow_thickness = m->manager.shadow_thickness; return(XtGeometryDone); } /* * fix the visual attributes of the subwidget to be what we like * * 1. make border width uniform * 2. maybe set the label alignment */ static void FixVisual (m, w) XmRowColumnWidget m; Widget w; { Arg al[10]; int ac; if (RC_EntryBorder(m)) { _XmConfigureObject(w,w->core.x,w->core.y, w->core.width, w->core.height, RC_EntryBorder(m)); } if (IsOption(m)) return; if (XmIsLabelGadget(w)) { if (IsAligned (m)) { if (IsWorkArea(m) || ((w->core.widget_class != xmLabelWidgetClass) && (w->core.widget_class != xmLabelGadgetClass))) { ac = 0; XtSetArg (al[ac], XmNalignment, RC_EntryAlignment (m)); ac++; XtSetValues (w, al, ac); } } } else if (XmIsLabel (w)) { if (IsAligned (m)) { if ((w->core.widget_class != xmLabelWidgetClass) || IsWorkArea(m)) { ac = 0; XtSetArg (al[ac], XmNalignment, RC_EntryAlignment (m)); ac++; XtSetValues (w, al, ac); } } } } /* * If an entryCallback exists, set a flag in the buttons to not do * their activate callbacks. */ static void FixCallback (m, w) XmRowColumnWidget m; Widget w; { char *c = which_callback (w); /* which callback to use */ if (c == NULL) return; /* can't do it to this guy */ if (m->row_column.entry_callback) { /* * Disable the buttons activate callbacks */ if (XmIsLabelGadget(w)) (*(((XmLabelGadgetClassRec *)XtClass(w))-> label_class.setOverrideCallback)) (w); else (*(((XmLabelClassRec *)XtClass(w))-> label_class.setOverrideCallback)) (w); } } static void XmGetMenuKidMargins (m, width, height, left, right, top, bottom) XmRowColumnWidget m; Dimension *width, *height, *left, *right, *top, *bottom; { register int i; *width = *height = *left = *right = *top = *bottom = 0; for (i = 0; i < m->composite.num_children; i++) { Widget p = (Widget) m->composite.children[i]; if (IsManaged (p)) { if (XmIsLabelGadget(p)) { register XmLabelGadget lg = (XmLabelGadget) p; if (LabG_MarginWidth (lg) > *width) *width = LabG_MarginWidth (lg); if (LabG_MarginHeight(lg) > *height) *height = LabG_MarginHeight (lg); if (LabG_MarginLeft (lg) > *left) *left = LabG_MarginLeft (lg); if (LabG_MarginRight (lg) > *right) *right = LabG_MarginRight (lg); if (LabG_MarginTop (lg) > *top) *top = LabG_MarginTop (lg); if (LabG_MarginBottom(lg) > *bottom) *bottom = LabG_MarginBottom (lg); } else if (XmIsLabel(p)) { register XmLabelWidget lw = (XmLabelWidget) p; if (Lab_MarginWidth (lw) > *width) *width = Lab_MarginWidth (lw); if (Lab_MarginHeight(lw) > *height) *height = Lab_MarginHeight (lw); if (Lab_MarginLeft (lw) > *left) *left = Lab_MarginLeft (lw); if (Lab_MarginRight (lw) > *right) *right = Lab_MarginRight (lw); if (Lab_MarginTop (lw) > *top) *top = Lab_MarginTop (lw); if (Lab_MarginBottom(lw) > *bottom) *bottom = Lab_MarginBottom (lw); } } } } /* * Toggle buttons have this thingy hanging off the left of the * widget, before the text. This dimension is known as the MarginLeft. * Pulldown's have hot spots in the MarginRight, accelerators go in the * marginRight also. * * For generality's sake we should insure that all * of the current label subclass widgets in the menu have the * margins set to the same value. */ static void DoMarginAdjustment (m) XmRowColumnWidget m; { register Widget *p; register int i; Dimension m_w, m_h, m_l, m_r, m_t, m_b; Dimension w, h; if ((!RC_DoMarginAdjust (m)) || (IsOption(m))) return; /* * this should almost be part * of the layout process, except this requires a setvalue not a resize... */ XmGetMenuKidMargins (m, &m_w, &m_h, &m_l, &m_r, &m_t, &m_b); ForManagedChildren (m, i, p) { if (XmIsLabelGadget(*p)) { XmLabelGadget q; /* * If in a popup or pulldown pane, * don't do labels; i.e. only do buttons. */ if (((*p)->core.widget_class == xmLabelGadgetClass) && (IsPulldown(m) || IsPopup(m))) continue; w = XtWidth (*p); h = XtHeight (*p); q = (XmLabelGadget) (*p); if (IsVertical (m)) { /* change horiz margins to be uniform */ if (LabG_MarginLeft(q) != m_l) { w += LabG_MarginLeft(q) - m_l; _XmAssignLabG_MarginLeft(q, m_l); } if (LabG_MarginRight(q) != m_r) { w += LabG_MarginRight(q) - m_r; _XmAssignLabG_MarginRight(q, m_r); } if (LabG_MarginWidth(q) != m_w) { w += LabG_MarginWidth(q) - m_w; _XmAssignLabG_MarginWidth(q, m_w); } _XmReCacheLabG(q); if (q->rectangle.width != w) { _XmConfigureObject(q,q->rectangle.x,q->rectangle.y, w, q->rectangle.height, q->rectangle.border_width); } } else { /* change vert margins */ if (LabG_MarginTop(q) != m_t) { h += LabG_MarginTop(q) - m_t; _XmAssignLabG_MarginTop(q, m_t); } if (LabG_MarginBottom(q) != m_b) { h += LabG_MarginBottom(q) - m_b; _XmAssignLabG_MarginBottom(q, m_b); } if (LabG_MarginHeight(q) != m_h) { h += LabG_MarginHeight(q) - m_h; _XmAssignLabG_MarginHeight(q, m_h); } _XmReCacheLabG(q); if (q->rectangle.width != h) { _XmConfigureObject(q,q->rectangle.x,q->rectangle.y, q->rectangle.width, h, q->rectangle.border_width); } } } else if (XmIsLabel(*p)) { XmLabelWidget lw; /* * If in a popup or pulldown pane, * don't do labels; i.e. only do buttons. */ if (((*p)->core.widget_class == xmLabelWidgetClass) && (IsPulldown(m) || IsPopup(m))) continue; w = XtWidth (*p); h = XtHeight (*p); lw = (XmLabelWidget) (*p); if (IsVertical (m)) /* change horiz margins to */ { /* be uniform */ ChangeMargin (Lab_MarginLeft (lw), m_l, w); ChangeMargin (Lab_MarginRight (lw), m_r, w); ChangeMargin (Lab_MarginWidth (lw), m_w, w); if (XtWidth (lw) != w) { _XmConfigureObject(lw,lw->core.x,lw->core.y, w, lw->core.height, lw->core.border_width); } } else /* change vert margins */ { ChangeMargin (Lab_MarginTop (lw), m_t, h); ChangeMargin (Lab_MarginBottom (lw), m_b, h); ChangeMargin (Lab_MarginHeight (lw), m_h, h); if (XtHeight (lw) != h) { _XmConfigureObject(lw,lw->core.x,lw->core.y, lw->core.width, h, lw->core.border_width); } } } } } /* * Action routines specific to traversal. */ /* ARGSUSED */ static void _XmMenuUnmap (cb, event, param, num_param) XmCascadeButtonWidget cb; XEvent *event; char **param; int *num_param; { /* * Typically, when a cascade button is in a transient menupane, we just * want to ignore unmap requests. However, when it is in a menubar, we * want it handled normally. */ if (RC_Type(XtParent(cb)) == XmMENU_BAR) _XmPrimitiveUnmap(cb, event); } /* ARGSUSED */ static void _XmMenuFocusOut (cb, event, param, num_param) XmCascadeButtonWidget cb; XEvent *event; char **param; int *num_param; { /* * During traversal, when the focus is moved to the next menupane, * the cascade button to which the menupane is attached will get a * FocusOut event, thus causing it to unhighlight. To prevent this, * we will discard the FocusOut event if we are moving to a new submenu. */ if (!ShouldDispatchFocusOut(cb)) return; /* Forward the event for normal processing */ _XmPrimitiveFocusOut(cb, event); } /* * The normal primitive FocusIn procedure does nothing if a transient * operation is happening. Since we are part of the transient operation, * and we still want focus to work, we need to use the internal function. */ /* ARGSUSED */ static void _XmMenuFocusIn (cb, event, param, num_param) XmCascadeButtonWidget cb; XEvent *event; char **param; int *num_param; { /* Forward the event for normal processing */ _XmPrimitiveFocusInInternal(cb, event); } /* ARGSUSED */ static void Noop (cb, event, param, num_param) XmCascadeButtonWidget cb; XEvent *event; char **param; int *num_param; { /* * Do nothing; the purpose is to override the actions taken by the * Primitive translations. */ } static void MenuTraverse (w, event, direction) XmCascadeButtonWidget w; XEvent * event; int direction; { XmManagerWidget parent; /* * The case may occur where the reporting widget is in fact the * RowColumn widget, and not a child. This will occur if the * RowColumn has not traversable children. */ if (XmIsRowColumn(w)) parent = (XmManagerWidget) w; else if (XmIsRowColumn(XtParent(w))) parent = (XmManagerWidget) XtParent(w); else return; if ((RC_Type(parent) == XmMENU_POPUP) || (RC_Type(parent) == XmMENU_PULLDOWN) || (RC_Type(parent) == XmMENU_BAR)) { _XmRecordEvent(event); (*(((XmRowColumnWidgetClass)XtClass(parent))->row_column_class. traversalHandler))(parent, w, direction); } } /* ARGSUSED */ static void _XmMenuTraverseLeft (cb, event, param, num_param) XmCascadeButtonWidget cb; XEvent *event; char **param; int *num_param; { XmRowColumnWidget rc; if (_XmIsEventUnique(event)) { if (XmIsRowColumn(rc = (XmRowColumnWidget)XtParent(cb)) && !IsBar(rc) && (rc->row_column.orientation == XmHORIZONTAL)) MenuTraverse(cb, event, XmTRAVERSE_UP); else MenuTraverse(cb, event, XmTRAVERSE_LEFT); } } /* ARGSUSED */ static void _XmMenuTraverseRight (cb, event, param, num_param) XmCascadeButtonWidget cb; XEvent *event; char **param; int *num_param; { XmRowColumnWidget rc; if (_XmIsEventUnique(event)) { if (XmIsRowColumn(rc = (XmRowColumnWidget)XtParent(cb)) && !IsBar(rc) && (rc->row_column.orientation == XmHORIZONTAL)) MenuTraverse(cb, event, XmTRAVERSE_DOWN); else MenuTraverse(cb, event, XmTRAVERSE_RIGHT); } } /* ARGSUSED */ static void _XmMenuTraverseUp (cb, event, param, num_param) XmCascadeButtonWidget cb; XEvent *event; char **param; int *num_param; { XmRowColumnWidget rc; if (_XmIsEventUnique(event)) { if (XmIsRowColumn(rc = (XmRowColumnWidget)XtParent(cb)) && !IsBar(rc) && (rc->row_column.orientation == XmHORIZONTAL)) MenuTraverse(cb, event, XmTRAVERSE_LEFT); else MenuTraverse(cb, event, XmTRAVERSE_UP); } } /* ARGSUSED */ static void _XmMenuTraverseDown (cb, event, param, num_param) XmCascadeButtonWidget cb; XEvent *event; char **param; int *num_param; { XmRowColumnWidget rc; if (_XmIsEventUnique(event)) { if (XmIsRowColumn(rc = (XmRowColumnWidget)XtParent(cb)) && !IsBar(rc) && (rc->row_column.orientation == XmHORIZONTAL)) MenuTraverse(cb, event, XmTRAVERSE_RIGHT); else MenuTraverse(cb, event, XmTRAVERSE_DOWN); } } /* ARGSUSED */ static void _XmMenuEscape (w, event) Widget w; XEvent *event; { /* Process the event only if not already processed */ if (!_XmIsEventUnique(event)) return; /* Let the menushell widget clean things up */ (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)-> menu_shell_class.popdownOne))(w, event); } static void _XmRC_GadgetTraverseDown (rc, event, param, num_param) XmRowColumnWidget rc; XEvent *event; char **param; int *num_param; { XmGadget gadget = (XmGadget)rc->manager.active_child; if (gadget && XmIsGadget(gadget)) _XmMenuTraverseDown(gadget, event, param, num_param); } static void _XmRC_GadgetTraverseUp (rc, event, param, num_param) XmRowColumnWidget rc; XEvent *event; char **param; int *num_param; { XmGadget gadget = (XmGadget)rc->manager.active_child; if (gadget && XmIsGadget(gadget)) _XmMenuTraverseUp(gadget, event, param, num_param); } static void _XmRC_GadgetTraverseLeft (rc, event, param, num_param) XmRowColumnWidget rc; XEvent *event; char **param; int *num_param; { XmGadget gadget = (XmGadget)rc->manager.active_child; /* * If there is not active child, then this RowColumn has * not traversable children, so it's fielding traversal * requests itself. */ if (gadget && XmIsGadget(gadget)) _XmMenuTraverseLeft(gadget, event, param, num_param); else if (gadget == NULL) _XmMenuTraverseLeft(rc, event, param, num_param); } static void _XmRC_GadgetTraverseRight (rc, event, param, num_param) XmRowColumnWidget rc; XEvent *event; char **param; int *num_param; { XmGadget gadget = (XmGadget)rc->manager.active_child; /* * If there is not active child, then this RowColumn has * not traversable children, so it's fielding traversal * requests itself. */ if (gadget && XmIsGadget(gadget)) _XmMenuTraverseRight(gadget, event, param, num_param); else if (gadget == NULL) _XmMenuTraverseRight(rc, event, param, num_param); } /* * In case we've moved into our out of a gadget, we need to take care * of the highlighting ourselves, since the gadget will not get a focus * event. */ static void GadgetCleanup (rc, oldActiveChild) XmRowColumnWidget rc; XmGadget oldActiveChild; { XmGadget newActiveChild = (XmGadget)rc->manager.active_child; if (oldActiveChild != newActiveChild) { if (XmIsGadget(oldActiveChild)) { _XmDispatchGadgetInput(oldActiveChild, NULL, XmFOCUS_OUT_EVENT); oldActiveChild->gadget.have_traversal = False; } } } static void _XmMenuTraversalHandler (rc, pw, direction) XmRowColumnWidget rc; Widget pw; int direction; { Widget currentActiveChild; if (!rc->manager.traversal_on) return; switch (direction) { case XmTRAVERSE_UP: { if (!IsBar(rc)) { currentActiveChild = rc->manager.active_child; _XmProcessTraversal(pw, XmTRAVERSE_PREV, True); GadgetCleanup(rc, currentActiveChild); } else Move_Up_Down (rc, pw); break; } case XmTRAVERSE_DOWN: { if (!IsBar(rc)) { currentActiveChild = rc->manager.active_child; _XmProcessTraversal(pw, XmTRAVERSE_NEXT, True); GadgetCleanup(rc, currentActiveChild); } else Move_Up_Down (rc, pw); break; } case XmTRAVERSE_LEFT: { MoveLeft(rc, pw); break; } case XmTRAVERSE_RIGHT: { MoveRight(rc, pw); break; } } } /* * When the PM menubar mode is active, either the up or down arrow will * cause us to post the menupane associated with the active cascade button * in the menubar. */ static void Move_Up_Down (rc, pw) XmRowColumnWidget rc; Widget pw; { if (rc->manager.active_child == NULL) return; if (XmIsPrimitive(pw)) { XmPrimitiveClassRec * prim; prim = (XmPrimitiveClassRec *)XtClass(pw); (*(prim->primitive_class.arm_and_activate)) (pw, NULL); } else if (XmIsGadget(pw)) { XmGadgetClassRec * gad; gad = (XmGadgetClassRec *)XtClass(pw); (*(gad->gadget_class.arm_and_activate)) (pw, NULL); } } /* ARGSUSED */ static void MoveLeft (rc, pw) XmRowColumnWidget rc; Widget pw; { if (IsBar(rc)) { /* Move to the previous item in the menubar */ FindPrevMenuBarItem(rc); } else if (IsPulldown(rc)) { /* * If we're the topmost pulldown menupane, then unpost and move to * the next available item in the menubar, and post its submenu. */ if (RC_Type(XtParent(RC_CascadeBtn(rc))) == XmMENU_BAR) { FindPrevMenuBarCascade(XtParent(RC_CascadeBtn(rc))); } else if (RC_Type(XtParent(RC_CascadeBtn(rc))) != XmMENU_OPTION) { /* Simply unpost the last menupane */ (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)-> menu_shell_class.popdownEveryone)) (XtParent(rc), NULL); } } } static void MoveRight (rc, pw) XmRowColumnWidget rc; Widget pw; { XmRowColumnWidget topLevel; /* * If the primitive widget is a cascade button, then post its * submenu, if there is one. */ if (!IsBar(rc) && XmIsCascadeButtonGadget(pw) && CBG_Submenu(pw)) { (*(((XmGadgetClassRec *)XtClass(pw))->gadget_class. arm_and_activate)) (pw, NULL); } else if (!IsBar(rc) && XmIsCascadeButton(pw) && CB_Submenu(pw)) { (*(((XmPrimitiveClassRec *)XtClass(pw))->primitive_class. arm_and_activate)) (pw, NULL); } else if (IsBar(rc)) { /* Move to the next item in the menubar */ FindNextMenuBarItem(rc); } else if (IsPulldown(rc)) { /* * For most buttons, requesting to move right is a Noop. The * exception is when we are working with a menubar. Then, we * want to unpost the current menu hierarchy, and post the * hierarchy associated with the next button in the menubar. */ _XmGetActiveTopLevelMenu (rc, &topLevel); if (RC_Type(topLevel) == XmMENU_BAR) { FindNextMenuBarCascade(topLevel); } } } /* * Find the next cascade button in the menubar which can be traversed to. */ static void FindNextMenuBarItem (menubar) XmRowColumnWidget menubar; { Widget child; register int i, j; int upper_limit; Widget active_child; XRectangle visRect; /* * We're not in the PM menubar mode if we don't have an active child. */ if (menubar->manager.active_child == NULL) return; _XmCreateVisibilityRect(menubar, &visRect); upper_limit = menubar->composite.num_children; active_child = menubar->manager.active_child; /* Find the index of the currently active item */ for (i = 0; i < upper_limit; i++) { if (menubar->composite.children[i] == active_child) break; } /* Start looking at the next child */ for (j = 0, i++; j < upper_limit - 1; j++, i++) { /* Wrap, if necessary */ if (i >= upper_limit) i = 0; child = menubar->composite.children[i]; /* You can't traverse to a button which has no submenu */ if ((XmIsCascadeButton(child) && (CB_Submenu(child) == NULL)) || (XmIsCascadeButtonGadget(child)) && (CBG_Submenu(child) == NULL)) continue; if (_XmTestTraversability(child, &visRect)) { /* Unhighlight the old active child */ if (XmIsPrimitive(active_child)) { XmPrimitiveClassRec * prim; prim = (XmPrimitiveClassRec *)XtClass(active_child); (*(prim->primitive_class.border_unhighlight)) (active_child); } else if (XmIsGadget(active_child)) { XmGadgetClassRec * gadget; gadget = (XmGadgetClassRec *)XtClass(active_child); (*(gadget->gadget_class.border_unhighlight)) (active_child); } /* Highlight the child */ menubar->manager.active_child = child; (void) XmProcessTraversal (child, XmTRAVERSE_CURRENT); if (XmIsPrimitive(child)) { XmPrimitiveClassRec * prim; prim = (XmPrimitiveClassRec *)XtClass(child); (*(prim->primitive_class.border_highlight)) (child); } else if (XmIsGadget(child)) { XmGadgetClassRec * gadget; gadget = (XmGadgetClassRec *)XtClass(child); (*(gadget->gadget_class.border_highlight)) (child); } return; } } } /* * Find the previous cascade button in the menubar which can be traversed to. */ static void FindPrevMenuBarItem(menubar) XmRowColumnWidget menubar; { Widget child; register int i, j; int upper_limit; Widget active_child; XRectangle visRect; /* We're not in the PM menubar mode if we don't have an active child */ if (menubar->manager.active_child == NULL) return; _XmCreateVisibilityRect(menubar, &visRect); upper_limit = menubar->composite.num_children; active_child = menubar->manager.active_child; /* Find the index of the currently active item */ for (i = 0; i < upper_limit; i++) { if (menubar->composite.children[i] == active_child) break; } /* Start looking at the previous child */ for (j = 0, --i; j < upper_limit - 1; j++, --i) { /* Wrap, if necessary */ if (i < 0) i = upper_limit - 1; child = menubar->composite.children[i]; /* You can't traverse to a button which has no submenu */ if ((XmIsCascadeButton(child) && (CB_Submenu(child) == NULL)) || (XmIsCascadeButtonGadget(child)) && (CBG_Submenu(child) == NULL)) continue; if (_XmTestTraversability(child, &visRect)) { /* Unhighlight the old active child */ if (XmIsPrimitive(active_child)) { XmPrimitiveClassRec * prim; prim = (XmPrimitiveClassRec *)XtClass(active_child); (*(prim->primitive_class.border_unhighlight)) (active_child); } else if (XmIsGadget(active_child)) { XmGadgetClassRec * gadget; gadget = (XmGadgetClassRec *)XtClass(active_child); (*(gadget->gadget_class.border_unhighlight)) (active_child); } /* Highlight the new active child */ menubar->manager.active_child = (Widget)child; (void) XmProcessTraversal (child, XmTRAVERSE_CURRENT); if (XmIsPrimitive(child)) { XmPrimitiveClassRec * prim; prim = (XmPrimitiveClassRec *)XtClass(child); (*(prim->primitive_class.border_highlight)) (child); } else if (XmIsGadget(child)) { XmGadgetClassRec * gadget; gadget = (XmGadgetClassRec *)XtClass(child); (*(gadget->gadget_class.border_highlight)) (child); } return; } } } /* * Find the next hierarchy in the menubar which can be traversed to. */ static void FindNextMenuBarCascade (menubar) XmRowColumnWidget menubar; { Widget child; register int i, j; int upper_limit; XRectangle visRect; ShellWidget shell; _XmCreateVisibilityRect(menubar, &visRect); upper_limit = menubar->composite.num_children; /* * Determine which child is popped up. */ shell = (ShellWidget) RC_PopupPosted(menubar); child = RC_CascadeBtn(shell->composite.children[0]); /* Find the index of the currently active item */ for (i = 0; i < upper_limit; i++) { if (menubar->composite.children[i] == child) break; } /* Start looking at the next child */ for (j = 0, i++; j < upper_limit - 1; j++, i++) { /* Wrap, if necessary */ if (i >= upper_limit) i = 0; child = menubar->composite.children[i]; if (XmIsCascadeButtonGadget(child)) { /* You can't traverse to a button which has no submenu */ if (XmIsCascadeButtonGadget(child) && (CBG_Submenu(child) == NULL)) continue; if (_XmTestTraversability(child, &visRect)) { XmGadgetClassRec * gadget; gadget = (XmGadgetClassRec *)XtClass(child); (*(gadget->gadget_class.arm_and_activate)) (child, NULL); return; } } else if (XmIsCascadeButton(child)) { /* You can't traverse to a button which has no submenu */ if (XmIsCascadeButton(child) && (CB_Submenu(child) == NULL)) continue; if (_XmTestTraversability(child, &visRect)) { XmPrimitiveClassRec * prim; prim = (XmPrimitiveClassRec *)XtClass(child); (*(prim->primitive_class.arm_and_activate)) (child, NULL); return; } } } } /* * Find the previous hierarchy in the menubar which can be traversed to. */ static void FindPrevMenuBarCascade (menubar) XmRowColumnWidget menubar; { Widget child; register int i, j; int upper_limit; XRectangle visRect; ShellWidget shell; _XmCreateVisibilityRect(menubar, &visRect); upper_limit = menubar->composite.num_children; /* Determine which child is popped up */ shell = (ShellWidget) RC_PopupPosted(menubar); child = RC_CascadeBtn(shell->composite.children[0]); /* Find the index of the currently active item */ for (i = 0; i < upper_limit; i++) { if (menubar->composite.children[i] == child) break; } /* Start looking at the previous child */ for (j = 0, --i; j < upper_limit - 1; j++, --i) { /* Wrap, if necessary */ if (i < 0) i = upper_limit - 1; child = menubar->composite.children[i]; if (XmIsCascadeButtonGadget(child)) { /* You can't traverse to a button which has no submenu */ if (CBG_Submenu(child) == NULL) continue; if (_XmTestTraversability(child, &visRect)) { XmGadgetClassRec * gadget; gadget = (XmGadgetClassRec *)XtClass(child); (*(gadget->gadget_class.arm_and_activate)) (child, NULL); return; } } else if (XmIsCascadeButton (child)) { /* You can't traverse to a button which has no submenu */ if (CB_Submenu(child) == NULL) continue; if (_XmTestTraversability(child, &visRect)) { XmPrimitiveClassRec * prim; prim = (XmPrimitiveClassRec *)XtClass(child); (*(prim->primitive_class.arm_and_activate)) (child, NULL); return; } } } } /* ARGSUSED */ static void _XmRC_FocusIn (rc, event, params, num_params) XmRowColumnWidget rc; XEvent *event; char **params; int *num_params; { /* * For popup and pulldown menupanes, we want to ignore focusIn request * which occur when we are not visible. */ if (IsBar(rc)) _XmManagerFocusIn(rc, event); else if ((((XmMenuShellWidget)XtParent(rc))->shell.popped_up) && (rc->manager.traversal_on)) _XmManagerFocusInInternal(rc, event); } /* ARGSUSED */ static void _XmRC_FocusOut (rc, event, params, num_params) XmRowColumnWidget rc; XEvent *event; char **params; int *num_params; { _XmManagerFocusOut(rc, event); } /* ARGSUSED */ static void _XmRC_Unmap (rc, event, params, num_params) XmRowColumnWidget rc; XEvent *event; char **params; int *num_params; { /* * For popup and pulldown menupanes, we never care about being notified * when we are unmapped. For menubars, we want normal unmapping * processing to occur. */ if (IsBar(rc)) _XmManagerUnmap(rc, event); } static void _XmRC_Enter (rc, event) XmRowColumnWidget rc; XEvent *event; { if (IsBar(rc) && RC_IsArmed(rc)) return; _XmManagerEnter(rc, event); } /* * Catch an 'Escape' which occurs within a gadget, and bring down the * menu system. */ static void _XmRC_GadgetEscape (rc, event) XmRowColumnWidget rc; XEvent *event; { /* Process the event only if not already processed */ if (!_XmIsEventUnique(event)) return; if (IsBar(rc)) { /* * We're in the PM menubar mode, so let our own arm and activate * procedure clean things up . */ if (RC_IsArmed(rc)) ArmAndActivate(rc, NULL); } else { /* Let the menushell widget clean things up */ (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)-> menu_shell_class.popdownOne))(XtParent(rc), event); } _XmRecordEvent(event); } /* * When the user requests help (F1) for a gadget in a menu, by the time * the menu widget gets notified, the menu system has already been cleaned * up; the information describing which gadget was active is lost. So, we * will grab this information before disabling the menu system. */ /* ARGSUSED */ static void _XmGetGadget (rc, event, params, num_params) XmRowColumnWidget rc; XEvent *event; char **params; int *num_params; { if ((rc->manager.active_child) && XmIsGadget(rc->manager.active_child)) ActiveGadgetChild = (XmGadget)rc->manager.active_child; else ActiveGadgetChild = NULL; } /* * During traversal, when the focus is moved to the next menupane, * the cascade button to which the menupane is attached will get a * FocusOut event, thus causing it to unhighlight. To prevent this, * we will discard the FocusOut event if we are moving to a new submenu. */ static Boolean ShouldDispatchFocusOut (widget) Widget widget; { XmManagerWidget parent = (XmManagerWidget)XtParent(widget); if (XmIsRowColumn(parent) && ((RC_Type(parent) == XmMENU_BAR) || (RC_Type(parent) == XmMENU_POPUP) || (RC_Type(parent) == XmMENU_PULLDOWN))) { if (XmIsCascadeButton(widget)) { if (CB_Submenu(widget) && ((XmMenuShellWidget)XtParent(CB_Submenu(widget)))->shell.popped_up) { return(False); } } else if (XmIsCascadeButtonGadget(widget)) { if (CBG_Submenu(widget) && ((XmMenuShellWidget)XtParent(CBG_Submenu(widget)))->shell. popped_up) { return(False); } } } return (True); } /* * this entry is set in label and label gadget's class field so that * all communication from the buttons to the RowColumn are done through * this entry and then revectored to the appropriate routine. */ static void MenuProcedureEntry (proc, widget, flag, data, data2) int proc; Widget widget; Boolean flag; caddr_t data; caddr_t data2; { switch (proc) { case XmMENU_CASCADING: PrepareToCascade(widget, data, data2); break; case XmMENU_POPDOWN: MenuPopDown (widget, data); break; case XmMENU_SHELL_POPDOWN: (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)-> menu_shell_class.popdownEveryone))(widget, data); break; case XmMENU_BUTTON: VerifyMenuButton (widget, data, data2); break; case XmMENU_CALLBACK: /* data points to the widget which was activated */ ChildsActivateCallback (widget, data, data2); break; case XmMENU_TRAVERSAL: /* data points to a boolean */ SetMenuTraversal (widget, flag); break; case XmMENU_SUBMENU: SetCascadeField (widget, data, flag); break; case XmMENU_PROCESS_TREE: DoProcessMenuTree (widget, XmREPLACE); break; case XmMENU_ARM: MenuArm (widget); break; case XmMENU_DISARM: MenuDisarm (widget); break; case XmMENU_BAR_CLEANUP: MenuBarCleanup (widget); break; default: break; } }