/*************************************<+>************************************* ***************************************************************************** ** ** File: PopupMgr.c ** ** Project: X Widgets ** ** Description: Popup Menu Manager Widget ** ***************************************************************************** ** ** Copyright (c) 1988 by Hewlett-Packard Company ** Copyright (c) 1988 by the Massachusetts Institute of Technology ** ** Permission to use, copy, modify, and distribute this software ** and its documentation for any purpose and without fee is hereby ** granted, provided that the above copyright notice appear in all ** copies and that both that copyright notice and this permission ** notice appear in supporting documentation, and that the names of ** Hewlett-Packard or M.I.T. not be used in advertising or publicity ** pertaining to distribution of the software without specific, written ** prior permission. ** ***************************************************************************** *************************************<+>*************************************/ #include #include #include #include #include #include #include #include #include #include #include #include /* Standard Toolkit Routines */ static void Initialize(); static void Destroy(); static Boolean SetValues(); static void ClassPartInitialize(); /* Global Routines */ static void AttachPane(); static void DetachPane(); static void AddPane(); static void SetSelectAccelerator(); static void ClearSelectAccelerator(); static void AddButton(); static void Unpost(); static void Post(); static Boolean ProcessSelect(); static Boolean ValidEvent(); static Boolean DoICascade(); static void ClassPost(); static void ClassSelect(); static Boolean DoYouWantThisAccelerator(); static Boolean DoYouWantThisPost(); static void SetPostMnemonic(); static void ClearPostMnemonic(); static void SetSelectMnemonic(); static void ClearSelectMnemonic(); static void SetTitleAttributes(); static void TraverseLeft(); static void TraverseRight(); static void TraverseNext(); static void TraversePrev(); static void TraverseHome(); static void TraverseUp(); static void TraverseDown(); static void TraverseNextTop(); static void BtnSensitivityChanged(); static void PaneSensitivityChanged(); /* Action Routines */ static void MMPost(); static void MMAccelPost(); static void MMAccelSelect(); static void MMUnpost(); /* Callback Routines */ static void _XwMenuPaneCleanup(); static void _XwMenuButtonCleanup(); static void _XwCascadeUnselect(); static void _XwCascadeSelect(); /* Internal Support Routines */ static void RegisterTranslation(); static void ExposeEventHandler(); static void RegisterPostTranslation(); static void PositionCascade(); static Boolean ActionIsUsurpedByChild(); static void AddToPendingList(); static void SetButtonAccelerators(); static void SetTreeAccelerators(); static void ClearTreeAccelerators(); static Boolean CompletePath(); static Widget PendingAttach(); static void SetUpTranslation(); static void ForceMenuPaneOnScreen(); static void PaneManagedChildren(); static Boolean Visible(); static void SetMenuTranslations(); static void ManualPost(); static void SendFakeLeaveNotify(); static void SetUpStickyList(); static void DoDetach(); /**************************************************************** * * Popup Resources * ****************************************************************/ static XtResource resources[] = { {XtNstickyMenus, XtCStickyMenus, XtRBoolean, sizeof(Boolean), XtOffset(XwPopupMgrWidget, popup_mgr.stickyMode),XtRString,"FALSE"}, {XtNpostAccelerator, XtCPostAccelerator, XtRString, sizeof(String), XtOffset(XwPopupMgrWidget, popup_mgr.postAccelerator),XtRString, NULL} }; /**************************************************************** * * Miscellaneous Values * ****************************************************************/ /* Global Action Routines */ static XtActionsRec XwPopupActions[] = { {"PopupPostMenu", MMPost}, {"PopupUnpostMenu", MMUnpost}, {"PopupAccelPost", MMAccelPost}, {"PopupAccelSelect", MMAccelSelect} }; /* Translation Template Strings */ static String postTemplate = ": PopupPostMenu("; static String unpostTemplate = ": PopupUnpostMenu("; static String accelPostTemplate = ": PopupAccelPost("; static String accelSelectTemplate = ": PopupAccelSelect("; static String selectTemplate = ": select()"; static char workArea[300]; /* * Used when positioning a cascade such that the cursor will be * in the first item of the cascade. */ #define FUDGE_FACTOR 8 #define SHELL_PARENT 0 #define ULTIMATE_PARENT 1 /* * This mask is used during the setting and clearing of button grabs. * If the event being grabbed is a buttonUp event, then these bits * cause the grabs to never activate; so they need to be cleared. */ #define btnMask (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask) /**************************************************************** * * Full class record constant * ****************************************************************/ XwPopupMgrClassRec XwpopupmgrClassRec = { { /* core_class fields */ /* superclass */ (WidgetClass) &XwmenumgrClassRec, /* class_name */ "XwPopupMgr", /* widget_size */ sizeof(XwPopupMgrRec), /* class_initialize */ NULL, /* class_part_init */ ClassPartInitialize, /* class_inited */ FALSE, /* initialize */ Initialize, /* initialize_hook */ NULL, /* realize */ NULL, /* actions */ NULL, /* num_actions */ 0, /* resources */ resources, /* num_resources */ XtNumber(resources), /* xrm_class */ NULLQUARK, /* compress_motion */ TRUE, /* compress_exposure */ TRUE, /* compress_enterlv */ TRUE, /* visible_interest */ FALSE, /* destroy */ Destroy, /* resize */ NULL, /* expose */ NULL, /* set_values */ SetValues, /* set_values_hook */ NULL, /* set_values_almost */ XtInheritSetValuesAlmost, /* get_values_hook */ NULL, /* accept_focus */ NULL, /* version */ XtVersion, /* PRIVATE cb list */ NULL, /* tm_table */ NULL, /* query_geometry */ NULL, /* display_accelerator */ XtInheritDisplayAccelerator, /* extension */ NULL },{ /* composite_class fields */ /* geometry_manager */ NULL, /* change_managed */ NULL, /* insert_child */ XtInheritInsertChild, /* delete_child */ XtInheritDeleteChild, NULL, },{ /* constraint class fields */ /* resources */ NULL, /* num_resources */ 0, /* constraint_size */ 0, /* initialize */ NULL, /* destroy */ NULL, /* set_values */ NULL, NULL, },{ /* manager_class fields */ /* traversal handler */ (XwTraversalProc)XtInheritTraversalProc, /* translations */ NULL, },{ /* menu manager class */ /* attachPane */ AttachPane, /* detachPane */ DetachPane, /* addPane */ AddPane, /* setSelectAccel */ SetSelectAccelerator, /* clearSelectAccel */ ClearSelectAccelerator, /* setPostMnemonic */ SetPostMnemonic, /* clearPostMnemonic */ ClearPostMnemonic, /* addButton */ AddButton, /* processSelect */ (XwBooleanProc)ProcessSelect, /* validEvent */ (XwBooleanProc)ValidEvent, /* doIcascade */ DoICascade, /* setSelectMnemonic */ SetSelectMnemonic, /* clearSelectMnemon */ ClearSelectMnemonic, /* setTitleAttributes */ SetTitleAttributes, /* paneManagedChildren*/ PaneManagedChildren, /* traverseLeft */ TraverseLeft, /* traverseRight */ TraverseRight, /* traverseNext */ TraverseNext, /* traversePrev */ TraversePrev, /* traverseHome */ TraverseHome, /* traverseUp */ TraverseUp, /* traverseDown */ TraverseDown, /* traverseNextTop */ TraverseNextTop, /* btnSensitivityChan */ BtnSensitivityChanged, /* paneSensitivityCha */ PaneSensitivityChanged, },{ /* popup menu manager class - none */ /* manualPost */ (XwPostProc) ManualPost, } }; WidgetClass XwpopupmgrWidgetClass = (WidgetClass)&XwpopupmgrClassRec; WidgetClass XwpopupMgrWidgetClass = (WidgetClass)&XwpopupmgrClassRec; /*----------------------------------------------------------------------*/ /* I don't really know why PopupMgr needs to register its actions */ /* globally like this when all other widgets don't; regardless, */ /* running this in ClassInitialize() doesn't work because the app */ /* context can be destroyed and remade, and then it loses the actions, */ /* instead returning "Warning: Actions not found: PopupPostMenu". */ /* Furthermore, the "correct" implementation needs a value for "app", */ /* which must be declared global, which is clearly a hack. So I'm */ /* instead creating a world-accessible function which does what */ /* ClassInitialize() did, but it takes an argument "app" and must be */ /* called every time the application is (re)made. */ /*----------------------------------------------------------------------*/ void XwAppInitialize(XtAppContext app) { /* Register the global action routines */ XtAppAddActions (app, XwPopupActions, XtNumber(XwPopupActions)); } /*************************************<->************************************* * * Initialize * * * Description: * ----------- * Initialize the popup_mgr fields within the widget's instance structure. * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void Initialize (request, new) XwPopupMgrWidget request; register XwPopupMgrWidget new; { Widget grandparent; KeySym tempKeysym; /* * Clear the 'map when managed' flag, since this widget should * never be visible. */ new->core.mapped_when_managed = FALSE; /* Fill in our instance fields */ new->popup_mgr.topLevelPane = (Widget) NULL; new->popup_mgr.lastSelected = (Widget) NULL; new->popup_mgr.savedCascadeList = (Widget *) XtMalloc(25 * sizeof(Widget)); new->popup_mgr.numSavedCascades = 0; new->popup_mgr.sizeSavedCascadeList = 25; new->popup_mgr.currentCascadeList = (Widget *) XtMalloc(25 * sizeof(Widget)); new->popup_mgr.numCascades = 0; new->popup_mgr.sizeCascadeList = 25; new->popup_mgr.attachPane = (XwMenuPaneWidget) NULL; new->popup_mgr.detachPane = (XwMenuPaneWidget) NULL; new->popup_mgr.origMouseX = -1; new->popup_mgr.origMouseY = -1; /* Get the widget Id for the widget to which the menu is attached */ grandparent = (Widget) XtParent(XtParent(new)); /* * If a post accelerator is specified, then attempt to convert it to * a usable format (not a string); if the accelerator specification is * invalid, then use the default post accelerator (none). */ if ((new->popup_mgr.postAccelerator) && (strlen(new->popup_mgr.postAccelerator) > 0) && (_XwMapKeyEvent (new->popup_mgr.postAccelerator, &new->popup_mgr.accelEventType, &tempKeysym, &new->popup_mgr.accelModifiers))) { /* Valid accelerator string; save a copy */ new->popup_mgr.accelKey = XKeysymToKeycode(XtDisplay(new), tempKeysym); new->popup_mgr.postAccelerator = (String) strcpy (XtMalloc ( XwStrlen(new->popup_mgr. postAccelerator) + 1), new->popup_mgr.postAccelerator); /* Set up a translation in the widget */ RegisterTranslation (grandparent, new->popup_mgr.postAccelerator, accelPostTemplate, new); } else { if ((new->popup_mgr.postAccelerator) && (strlen(new->popup_mgr.postAccelerator) > 0)) { XtWarning ("PopupMgr: Invalid post accelerator; disabling feature"); } new->popup_mgr.postAccelerator = NULL; new->popup_mgr.accelEventType = 0; new->popup_mgr.accelKey = 0; new->popup_mgr.accelModifiers = 0; } /* Attach a posting translation to the widget */ if (new->menu_mgr.postString) { RegisterTranslation (grandparent, new->menu_mgr.postString, postTemplate, new); } /* Attach an unposting translation to the widget, if specified */ if (new->menu_mgr.unpostString) { RegisterTranslation (grandparent, new->menu_mgr.unpostString, unpostTemplate, new); } /* * If the children of the widget are to inherit this menu, then we * need to set up a button grab for the posting event; if the widget * is not yet realized, then we cannot set the grab, so we will * attach an exposure event handler to the widget, so that when it * does finally get mapped, we can set up any necessary grabs. */ if (XtIsRealized(grandparent)) { if (new->menu_mgr.associateChildren) { /* Set up a grab for the post event */ if (new->menu_mgr.postString) { XGrabButton (XtDisplay(grandparent), new->menu_mgr.postButton, (new->menu_mgr.postModifiers & ~btnMask), XtWindow(grandparent), False, ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, (Window)NULL, (Cursor)NULL); } /* Set up a grab for the post accelerator */ if (new->popup_mgr.postAccelerator) { XGrabKey (XtDisplay(grandparent), new->popup_mgr.accelKey, new->popup_mgr.accelModifiers, XtWindow(grandparent), False, GrabModeAsync, GrabModeAsync); } } } else { XtAddEventHandler (grandparent, ExposureMask|StructureNotifyMask, False, ExposeEventHandler, new); } } /* Initialize */ /*************************************<->************************************* * * SetValues() * * Description: * ----------- * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static Boolean SetValues (current, request, new) register XwPopupMgrWidget current; XwPopupMgrWidget request; register XwPopupMgrWidget new; { Widget grandparent; Boolean postGrabSet = FALSE; Boolean postGrabCleared = FALSE; Boolean postAccelGrabSet = FALSE; Boolean postAccelGrabCleared = FALSE; KeySym tempKeysym; XtTranslations translation; String workTemplate; register int i, j, k; grandparent = (Widget) XtParent(XtParent(current)); /* Process the post accelerator */ if (current->popup_mgr.postAccelerator != new->popup_mgr.postAccelerator) { if ((new->popup_mgr.postAccelerator) && (strlen(new->popup_mgr.postAccelerator) > 0)) { if (_XwMapKeyEvent (new->popup_mgr.postAccelerator, &new->popup_mgr.accelEventType, &tempKeysym, &new->popup_mgr.accelModifiers)) { /* Valid accelerator string; save a copy */ new->popup_mgr.accelKey = XKeysymToKeycode (XtDisplay(new), tempKeysym); new->popup_mgr.postAccelerator = (String) strcpy (XtMalloc ( XwStrlen(new->popup_mgr. postAccelerator) + 1), new->popup_mgr.postAccelerator); } else { /* Invalid; revert to previous setting */ XtWarning ("PopupMgr: Invalid post accelerator; using previous setting"); new->popup_mgr.postAccelerator = current->popup_mgr.postAccelerator; new->popup_mgr.accelEventType = current->popup_mgr.accelEventType; new->popup_mgr.accelKey = current->popup_mgr.accelKey; new->popup_mgr.accelModifiers = current->popup_mgr.accelModifiers; } } else { /* None specified, so disable the feature */ new->popup_mgr.postAccelerator = NULL; new->popup_mgr.accelEventType = 0; new->popup_mgr.accelKey = 0; new->popup_mgr.accelModifiers = 0; } /* * This duplicate check prevents us from doing alot of unnecessary * work in the case where an invalid string was specified, and thus * we reverted to the previous value. */ if (current->popup_mgr.postAccelerator != new->popup_mgr.postAccelerator) { /* Remove the old accelerator translation and grab */ if (current->popup_mgr.postAccelerator) { RegisterTranslation (grandparent, current->popup_mgr.postAccelerator, accelPostTemplate, NULL); if (XtIsRealized(grandparent) &¤t->menu_mgr.associateChildren) { XUngrabKey (XtDisplay(grandparent), current->popup_mgr.accelKey, current->popup_mgr.accelModifiers, XtWindow(grandparent)); postAccelGrabCleared = TRUE; } } /* Set the new accelerator translation and grab */ if (new->popup_mgr.postAccelerator) { RegisterTranslation (grandparent, new->popup_mgr.postAccelerator, accelPostTemplate, new); if (XtIsRealized(grandparent) && new->menu_mgr.associateChildren) { XGrabKey (XtDisplay(grandparent), new->popup_mgr.accelKey, new->popup_mgr.accelModifiers, XtWindow(grandparent), False, GrabModeAsync, GrabModeAsync); postAccelGrabSet = TRUE; } } /* Free up the buffer holding the old string */ XtFree (current->popup_mgr.postAccelerator); } } /* Process the post event */ if (current->menu_mgr.postString != new->menu_mgr.postString) { /* Remove the old post translation and grab */ if (current->menu_mgr.postString) { RegisterTranslation (grandparent, current->menu_mgr.postString, postTemplate, NULL); if (XtIsRealized(grandparent) && current->menu_mgr.associateChildren) { XUngrabButton (XtDisplay(grandparent), current->menu_mgr.postButton, (current->menu_mgr.postModifiers & ~btnMask), XtWindow(grandparent)); postGrabCleared = TRUE; } /* Free up the old string */ XtFree (current->menu_mgr.postString); } /* Set the new post translation and grab */ if (new->menu_mgr.postString) { RegisterTranslation (grandparent, new->menu_mgr.postString, postTemplate, new); if (XtIsRealized(grandparent) && new->menu_mgr.associateChildren) { XGrabButton (XtDisplay(grandparent), new->menu_mgr.postButton, (new->menu_mgr.postModifiers & ~btnMask), XtWindow(grandparent), False, ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, (Window)NULL, (Cursor)NULL); postGrabSet = TRUE; } } } /* Process the select event. */ if (current->menu_mgr.selectString != new->menu_mgr.selectString) { /* * Each menubutton and menupane must have a new 'select' translation * set up; no grabs are needed. The old ones do not need to be * removed, since ProcessSelect() will reject them. */ if (new->menu_mgr.selectString) { /* * Create the translation string of format: * * "!: select()" */ workTemplate = &workArea[0]; strcpy (workTemplate, "!"); strcat (workTemplate, new->menu_mgr.selectString); strcat (workTemplate, selectTemplate); translation = XtParseTranslationTable (workTemplate); SetMenuTranslations (new, translation); /* XtDestroyStateTable (XtClass(new), translation); */ } if (current->menu_mgr.selectString) XtFree (current->menu_mgr.selectString); } /* Process the unpost event. */ if (current->menu_mgr.unpostString != new->menu_mgr.unpostString) { /* * Each menubutton and menupane must have a new 'unpost' translation * set up; no grabs are needed. The old ones need to first be * removed. */ if (current->menu_mgr.unpostString) { /* * Create the translation string of format: * * "!: MMUnpost(mgrId)" */ workTemplate = &workArea[0]; strcpy (workTemplate, "!"); strcat (workTemplate, current->menu_mgr.unpostString); strcat (workTemplate, unpostTemplate); strcat (workTemplate, _XwMapToHex(NULL)); strcat (workTemplate, ")"); translation = XtParseTranslationTable (workTemplate); SetMenuTranslations (new, translation); /* XtDestroyStateTable (XtClass(new), translation); */ RegisterTranslation (grandparent, current->menu_mgr.unpostString, unpostTemplate, NULL); XtFree (current->menu_mgr.unpostString); } if (new->menu_mgr.unpostString) { /* * Create the translation string of format: * * "!: MMUnpost(mgrId)" */ workTemplate = &workArea[0]; strcpy (workTemplate, "!"); strcat (workTemplate, new->menu_mgr.unpostString); strcat (workTemplate, unpostTemplate); strcat (workTemplate, _XwMapToHex(new->core.self)); strcat (workTemplate, ")"); translation = XtParseTranslationTable (workTemplate); SetMenuTranslations (new, translation); /* XtDestroyStateTable (XtClass(new), translation); */ RegisterTranslation (grandparent, new->menu_mgr.unpostString, unpostTemplate, new); } } /* Process the keyboard select event. */ if (current->menu_mgr.kbdSelectString != new->menu_mgr.kbdSelectString) { /* * Each menubutton and menupane must have a new 'kbd select' translation * set up; no grabs are needed. The old ones do not need to be * removed, since ProcessSelect() will reject them. */ if (new->menu_mgr.kbdSelectString) { /* * Create the translation string of format: * * "!: select()" */ workTemplate = &workArea[0]; strcpy (workTemplate, "!"); strcat (workTemplate, new->menu_mgr.kbdSelectString); strcat (workTemplate, selectTemplate); translation = XtParseTranslationTable (workTemplate); /* * Since the menupanes are our popup grand children, we * will process them simply by traversing our popup children list. */ SetMenuTranslations (new, translation); /* XtDestroyStateTable (XtClass(new), translation); */ } if (current->menu_mgr.kbdSelectString) XtFree (current->menu_mgr.kbdSelectString); } /* Process the associateChildren flag */ if (current->menu_mgr.associateChildren != new->menu_mgr.associateChildren) { int i; /* * If the widget is realized, then we need to set/clear button * and key grabs for the post event, the post accelerator, and * all menubutton accelerators; the two post grabs will only be * set or cleared if they were not already done so earlier; this * would have happened if either of their event strings had * changed. */ if (XtIsRealized (grandparent)) { if (new->menu_mgr.associateChildren) { /* Set post grab */ if ((!postGrabSet) && (new->menu_mgr.postString)) { XGrabButton (XtDisplay(grandparent), new->menu_mgr.postButton, (new->menu_mgr.postModifiers & ~btnMask), XtWindow(grandparent), False, ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, (Window)NULL, (Cursor)NULL); } /* Set post accelerator grab */ if ((!postAccelGrabSet) && (new->popup_mgr.postAccelerator)) { XGrabKey (XtDisplay(grandparent), new->popup_mgr.accelKey, new->popup_mgr.accelModifiers, XtWindow(grandparent), False, GrabModeAsync, GrabModeAsync); } /* Set menubutton accelerator grabs */ for (i = 0; i < new->menu_mgr.numAccels; i++) { XGrabKey (XtDisplay(grandparent), new->menu_mgr.menuBtnAccelTable[i].accelKey, new->menu_mgr.menuBtnAccelTable[i].accelModifiers, XtWindow(grandparent), False, GrabModeAsync, GrabModeAsync); } } else { /* Clear post grab */ if ((!postGrabCleared) && (current->menu_mgr.postString)) { XUngrabButton (XtDisplay(grandparent), current->menu_mgr.postButton, (current->menu_mgr.postModifiers & ~btnMask), XtWindow(grandparent)); } /* Clear post accelerator grab */ if ((!postAccelGrabCleared) && (current->popup_mgr.postAccelerator)) { XUngrabKey (XtDisplay(grandparent), current->popup_mgr.accelKey, current->popup_mgr.accelModifiers, XtWindow(grandparent)); } /* Clear menubutton accelerator grabs */ for (i = 0; i < current->menu_mgr.numAccels; i++) { XUngrabKey (XtDisplay(grandparent), current->menu_mgr.menuBtnAccelTable[i].accelKey, current->menu_mgr.menuBtnAccelTable[i].accelModifiers, XtWindow(grandparent)); } } } } /* * Since all of the menu components inherit their traversal setting * from their menu manager, anytime the menu manager's traversal * state changes, we need to propogate this down to all of the other * menu components (panes, buttons, separaters). */ if (new->manager.traversal_on != current->manager.traversal_on) { int traversalType; if (new->manager.traversal_on) traversalType = XwHIGHLIGHT_TRAVERSAL; else traversalType = XwHIGHLIGHT_OFF; for (i = 0; i < current->core.num_popups; i++) { CompositeWidget shell; shell = (CompositeWidget) current->core.popup_list[i]; for (j = 0; j < shell->composite.num_children; j++) { XwMenuPaneWidget menupane; /* Here we set the traversal flag for the menupanes */ menupane = (XwMenuPaneWidget)shell->composite.children[j]; if (XtIsSubclass ((Widget)menupane, XwmenupaneWidgetClass)) { (*(((XwMenuPaneWidgetClass) XtClass(menupane))-> menu_pane_class.setTraversalFlag)) ((Widget)menupane, new->manager.traversal_on); } for (k = 0; k < menupane->manager.num_managed_children; k++) { XwMenuButtonWidget mbutton; /* Here we set the traversal flag for the menubuttons */ mbutton = (XwMenuButtonWidget)menupane->composite.children[k]; if (XtIsSubclass ((Widget)mbutton, XwmenubuttonWidgetClass)) { (*(((XwMenuButtonWidgetClass) XtClass(mbutton))-> menubutton_class.setTraversalType)) ((Widget)mbutton, traversalType); } } } } } /* Clear the sticky menu list, if sticky mode is disabled */ if (new->popup_mgr.stickyMode == False) new->popup_mgr.numSavedCascades = 0; return (FALSE); } /*************************************<->************************************* * * Destroy() * * Description: * ----------- * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void Destroy (mw) register XwPopupMgrWidget mw; { Widget grandparent; register int i; /* Get the widget Id for the widget to which the menus are attached */ grandparent = (Widget)XtParent(XtParent(mw)); /* Free up any memory we allocated */ XtFree ((char *)(mw->popup_mgr.savedCascadeList)); XtFree ((char *)(mw->popup_mgr.currentCascadeList)); /* Clean up the post and accelerated post translations */ if (mw->menu_mgr.postString) { RegisterTranslation (grandparent, mw->menu_mgr.postString, postTemplate, NULL); } if (mw->popup_mgr.postAccelerator) { RegisterTranslation (grandparent, mw->popup_mgr.postAccelerator, accelPostTemplate, NULL); } if (mw->menu_mgr.unpostString) { RegisterTranslation (grandparent, mw->menu_mgr.unpostString, unpostTemplate, NULL); } /*************** * THE FOLLOWING IS NOT NEEDED, SINCE BY THE TIME WE GET HERE, * ALL OF OUR CHILDREN HAVE BEEN DESTROYED, AND THEIR ACCELERATORS * ALREADY CLEARED. * * Remove translations for each menubutton accelerator * * for (i = 0; i < mw->menu_mgr.numAccels; i++) * { * RegisterTranslation (grandparent, * mw->menu_mgr.menuBtnAccelTable[i].accelString, * accelSelectTemplate, NULL); * } ****************/ /* Remove any grabs we have set on the widget */ if (XtIsRealized(grandparent)) { if (mw->menu_mgr.associateChildren) { /* Remove post grab */ if (mw->menu_mgr.postString) { XUngrabButton (XtDisplay(grandparent), mw->menu_mgr.postButton, (mw->menu_mgr.postModifiers & ~btnMask), XtWindow(grandparent)); } /* Remove post accelerator grab */ if (mw->popup_mgr.postAccelerator) { XUngrabKey (XtDisplay(grandparent), mw->popup_mgr.accelKey, mw->popup_mgr.accelModifiers, XtWindow(grandparent)); } /*************** * THE FOLLOWING IS NOT NEEDED, SINCE BY THE TIME WE GET HERE, * ALL OF OUR CHILDREN HAVE BEEN DESTROYED, AND THEIR ACCELERATORS * ALREADY CLEARED. * * Remove grabs for each menubutton accelerator * for (i = 0; i < mw->menu_mgr.numAccels; i++) * { * XUngrabKey (XtDisplay(grandparent), * mw->menu_mgr.menuBtnAccelTable[i].accelKey, * mw->menu_mgr.menuBtnAccelTable[i].accelModifiers, * XtWindow(grandparent)); * } ***************/ } } else { /* Simply remove the exposure handler we added at initialize time */ XtRemoveEventHandler (grandparent, ExposureMask|StructureNotifyMask, False, ExposeEventHandler, mw); } } /*************************************<->************************************* * * ClassPartInitialize(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void ClassPartInitialize (wc) register XwPopupMgrWidgetClass wc; { register XwPopupMgrWidgetClass super; super = (XwPopupMgrWidgetClass)wc->core_class.superclass; if (wc->menu_mgr_class.attachPane == XtInheritAttachPane) wc->menu_mgr_class.attachPane = super->menu_mgr_class.attachPane; if (wc->menu_mgr_class.detachPane == XtInheritDetachPane) wc->menu_mgr_class.detachPane = super->menu_mgr_class.detachPane; if (wc->menu_mgr_class.addPane == XtInheritAddPane) wc->menu_mgr_class.addPane = super->menu_mgr_class.addPane; if (wc->menu_mgr_class.setSelectAccelerator == XtInheritSetSelectAccelerator) wc->menu_mgr_class.setSelectAccelerator = super->menu_mgr_class.setSelectAccelerator; if (wc->menu_mgr_class.clearSelectAccelerator == XtInheritClearSelectAccelerator) wc->menu_mgr_class.clearSelectAccelerator = super->menu_mgr_class.clearSelectAccelerator; if (wc->menu_mgr_class.setPostMnemonic == XtInheritSetPostMnemonic) wc->menu_mgr_class.setPostMnemonic =super->menu_mgr_class.setPostMnemonic; if (wc->menu_mgr_class.clearPostMnemonic == XtInheritClearPostMnemonic) wc->menu_mgr_class.clearPostMnemonic = super->menu_mgr_class.clearPostMnemonic; if (wc->menu_mgr_class.setPostMnemonic == XtInheritSetPostMnemonic) wc->menu_mgr_class.setPostMnemonic =super->menu_mgr_class.setPostMnemonic; if (wc->menu_mgr_class.clearPostMnemonic==XtInheritClearPostMnemonic) wc->menu_mgr_class.clearPostMnemonic = super->menu_mgr_class.clearPostMnemonic; if (wc->menu_mgr_class.addButton == XtInheritAddButton) wc->menu_mgr_class.addButton = super->menu_mgr_class.addButton; if (wc->menu_mgr_class.setSelectMnemonic == XtInheritSetSelectMnemonic) wc->menu_mgr_class.setSelectMnemonic = super->menu_mgr_class.setSelectMnemonic; if (wc->menu_mgr_class.clearSelectMnemonic == XtInheritClearSelectMnemonic) wc->menu_mgr_class.clearSelectMnemonic = super->menu_mgr_class.clearSelectMnemonic; if (wc->menu_mgr_class.processSelect == XtInheritProcessSelect) wc->menu_mgr_class.processSelect = super->menu_mgr_class.processSelect; if (wc->menu_mgr_class.validEvent == XtInheritValidEvent) wc->menu_mgr_class.validEvent = super->menu_mgr_class.validEvent; if (wc->menu_mgr_class.doICascade == XtInheritDoICascade) wc->menu_mgr_class.doICascade = super->menu_mgr_class.doICascade; if (wc->menu_mgr_class.paneManagedChildren == XtInheritPaneManagedChildren) wc->menu_mgr_class.paneManagedChildren = super->menu_mgr_class.paneManagedChildren; if (wc->menu_mgr_class.setTitleAttributes == XtInheritSetTitleAttributes) wc->menu_mgr_class.setTitleAttributes = super->menu_mgr_class.setTitleAttributes; if (wc->menu_mgr_class.traverseLeft == XtInheritPopupTravLeft) wc->menu_mgr_class.traverseLeft = super->menu_mgr_class.traverseLeft; if (wc->menu_mgr_class.traverseRight == XtInheritPopupTravRight) wc->menu_mgr_class.traverseRight = super->menu_mgr_class.traverseRight; if (wc->menu_mgr_class.traverseNext == XtInheritPopupTravNext) wc->menu_mgr_class.traverseNext = super->menu_mgr_class.traverseNext; if (wc->menu_mgr_class.traversePrev == XtInheritPopupTravPrev) wc->menu_mgr_class.traversePrev = super->menu_mgr_class.traversePrev; if (wc->menu_mgr_class.traverseHome == XtInheritPopupTravHome) wc->menu_mgr_class.traverseHome = super->menu_mgr_class.traverseHome; if (wc->menu_mgr_class.traverseUp == XtInheritPopupTravUp) wc->menu_mgr_class.traverseUp = super->menu_mgr_class.traverseUp; if (wc->menu_mgr_class.traverseDown == XtInheritPopupTravDown) wc->menu_mgr_class.traverseDown = super->menu_mgr_class.traverseDown; if (wc->menu_mgr_class.traverseNextTop == XtInheritPopupTravNextTop) wc->menu_mgr_class.traverseNextTop=super->menu_mgr_class.traverseNextTop; if (wc->menu_mgr_class.btnSensitivityChanged==XtInheritBtnSensitivityChanged) wc->menu_mgr_class.btnSensitivityChanged = super->menu_mgr_class.btnSensitivityChanged; if (wc->menu_mgr_class.paneSensitivityChanged == XtInheritPaneSensitivityChanged) wc->menu_mgr_class.paneSensitivityChanged = super->menu_mgr_class.paneSensitivityChanged; } /*************************************<->************************************* * * RegisterTranslation (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void RegisterTranslation (widget, event, template, menuMgrId) Widget widget; String event; String template; Widget menuMgrId; { register String workTemplate; XtTranslations translations; workTemplate = &workArea[0]; /* * Construct the translation string, using the following format: * * "!: ActionProc(menuMgrId)" */ strcpy (workTemplate, "!"); strcat (workTemplate, event); strcat (workTemplate, template); if (menuMgrId) strcat (workTemplate, _XwMapToHex(menuMgrId->core.self)); else strcat (workTemplate, _XwMapToHex(NULL)); strcat (workTemplate, ")"); /* Compile the translation and attach to the widget */ translations = XtParseTranslationTable(workTemplate); XtOverrideTranslations (widget, translations); /* XtDestroyStateTable (XtClass(widget), translations); */ } /*************************************<->************************************* * * PositionCascade (parameters) * * Description: * ----------- * This routine positions a cacading submenu pane. When the new * menupane is posted, it will slightly overlap the previous menu * pane (in the x direction). In the y direction, the new menupane * will be positioned such that its first menubutton is centered on * the menubutton to which the menupane is attached. * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void PositionCascade (menubutton, menupane) register Widget menubutton; XwManagerWidget menupane; { Position x, y, yDelta; Widget menubuttonGrandparent; Widget menubuttonParent; Widget menupaneShell; menubuttonParent = (Widget)XtParent(menubutton); menubuttonGrandparent = (Widget)XtParent(menubuttonParent); menupaneShell = (Widget)XtParent(menupane); /* * In order for this algorithm to work, we need to make sure the * menupane has been realized; this is because its size is not * yet valid because it is not notified that it has any children * until it is realized. */ if (!XtIsRealized (menupaneShell)) XtRealizeWidget (menupaneShell); /* * Since the x and y need to be absolute positions, we need to * use the shell widget coordinates in portions of these calculations. */ x = menubuttonGrandparent->core.x + menubuttonParent->core.border_width + menubutton->core.border_width + menubutton->core.width - (XwCASCADEWIDTH + FUDGE_FACTOR + menupane->core.border_width); y = menubuttonGrandparent->core.y + menubuttonParent->core.border_width + menubutton->core.border_width + menubutton->core.y + (menubutton->core.height >> 1); /* Attempt to center on the first button in the new menupane */ if (menupane->manager.num_managed_children > 0) { Widget firstButton = menupane->manager.managed_children[0]; /* yDelta = firstButton->core.y + firstButton->core.border_width + (firstButton->core.height >> 1); */ yDelta = 0; } else yDelta = menupane->core.border_width + (menupane->core.height >> 1); y -= yDelta; ForceMenuPaneOnScreen (menupane, &x, &y); XtMoveWidget (menupaneShell, x, y); } /*************************************<->************************************* * * ExposeEventHandler (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void ExposeEventHandler (w, menuMgr, event) Widget w; register XwPopupMgrWidget menuMgr; XEvent * event; { register int i; /* * If the children inherit the menu tree, then set up grabs for * the post button, the post accelerator key, and each menubutton * accelerator. */ if (menuMgr->menu_mgr.associateChildren) { if (menuMgr->menu_mgr.postString) { XGrabButton (XtDisplay(menuMgr), menuMgr->menu_mgr.postButton, (menuMgr->menu_mgr.postModifiers & ~btnMask), XtWindow(w), False, ButtonPressMask|ButtonReleaseMask, GrabModeAsync, GrabModeAsync, (Window)NULL, (Cursor)NULL); } if (menuMgr->popup_mgr.postAccelerator) { XGrabKey (XtDisplay(menuMgr), menuMgr->popup_mgr.accelKey, menuMgr->popup_mgr.accelModifiers, XtWindow(w), False, GrabModeAsync, GrabModeAsync); } for (i = 0; i < menuMgr->menu_mgr.numAccels; i++) { XGrabKey (XtDisplay(menuMgr), menuMgr->menu_mgr.menuBtnAccelTable[i].accelKey, menuMgr->menu_mgr.menuBtnAccelTable[i].accelModifiers, XtWindow(w), False, GrabModeAsync, GrabModeAsync); } } XtRemoveEventHandler (w, ExposureMask|StructureNotifyMask, False, (XtEventHandler)ExposeEventHandler, menuMgr); } /*************************************<->************************************* * * _XwCascadeSelect(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void _XwCascadeSelect (menubutton, menupane, data) Widget menubutton; XwMenuPaneWidget menupane; caddr_t data; /* not used */ { register int i; register XwPopupMgrWidget menuMgr; menuMgr = (XwPopupMgrWidget) XtParent(XtParent(menupane)); /* If the pane is already posted, then do nothing but return */ for (i = 0; i < menuMgr->popup_mgr.numCascades; i++) { if (menupane==(XwMenuPaneWidget)menuMgr->popup_mgr.currentCascadeList[i]) return; } /* Position the cascading menupane */ PositionCascade (menubutton, menupane); /* Post the menupane */ Post (menuMgr, menupane, XtGrabNonexclusive); /* Set the traversal focus, if necessary */ if (menuMgr->manager.traversal_on) { /* Force the highlight to the first item */ menupane->manager.active_child = NULL; XtSetKeyboardFocus ((Widget)menupane, NULL); XwMoveFocus (menupane); SendFakeLeaveNotify(menubutton, SHELL_PARENT); } XFlush(XtDisplay(menuMgr)); } /*************************************<->************************************* * * _XwCascadeUnselect (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void _XwCascadeUnselect (menubutton, menupane, params) Widget menubutton; Widget menupane; register XwunselectParams * params; { XwPopupMgrWidget menuMgr; register Widget shell; menuMgr = (XwPopupMgrWidget) XtParent(XtParent(menupane)); shell = (Widget)XtParent(menupane); /* * Determine if the cursor left the cascade region and entered * the cascading menupane. If this happened, then tell the menu * button to remain highlighted. If this did not happen, then we * need to unpost the menupane, and tell the menubutton to unhighlight. * */ /* See if the cursor is in the menupane */ if ((params->rootX >= shell->core.x) && (params->rootX < (shell->core.x + shell->core.width + (shell->core.border_width << 1))) && (params->rootY >= shell->core.y) && (params->rootY < (shell->core.y + shell->core.height + (shell->core.border_width << 1)))) { /* Yes, we're in the cascading menupane */ params->remainHighlighted = TRUE; } else { /* No, we've left the cascade region; unpost the menupane */ Unpost (menuMgr, menupane); params->remainHighlighted = FALSE; } } /*************************************<->************************************* * * _XwMenuPaneCleanup (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void _XwMenuPaneCleanup (menupane, menuMgr, data) XwMenuPaneWidget menupane; Widget menuMgr; caddr_t data; /* not used */ { /* * If this menupane is currently attached to a menubutton, or if this * is the top level menupane, then we need to detach it before we * allow it to be destroyed. */ if (menupane->menu_pane.attach_to) { if (menupane->menu_pane.attachId == NULL) DetachPane (menuMgr, menupane, menupane->menu_pane.attach_to); else DoDetach (menuMgr, menupane->menu_pane.attachId, menupane); } } /*************************************<->************************************* * * _XwMenuButtonCleanup(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void _XwMenuButtonCleanup (menubutton, menuMgr, data) Widget menubutton; XwPopupMgrWidget menuMgr; caddr_t data; /* not used */ { Arg arg[1]; XwMenuPaneWidget cascadeOn; /* * Remove any accelerators associated with this menubutton, and if * there is a menupane cacading from us, then break that association, * and add the menupane back into the pending attach list. */ ClearSelectAccelerator (menuMgr, menubutton); XtSetArg (arg[0], XtNcascadeOn, (XtArgVal) &cascadeOn); XtGetValues (menubutton, arg, 1); if (cascadeOn != NULL) { /* Detach, and add back into pending attach list */ DoDetach (menuMgr, menubutton, cascadeOn); AddToPendingList (menuMgr, cascadeOn, cascadeOn->menu_pane.attach_to); } /* * If this menubutton had been the last item selected, and if sticky * menus is enabled, then we need to cleanup the saved cascade list, * so that we don't dump core the next time the menu is posted. */ if ((menuMgr->popup_mgr.stickyMode) && (menuMgr->popup_mgr.numSavedCascades > 0) && (menuMgr->popup_mgr.lastSelected == menubutton->core.self)) { menuMgr->popup_mgr.numSavedCascades = 0; } } /*************************************<->************************************* * * MMPost(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void MMPost (w, event, params, count) Widget w; XEvent * event; String * params; Cardinal count; { XwPopupMgrWidget menuMgr; /* Extract the menu manager widget id */ menuMgr = (XwPopupMgrWidget) _XwMapFromHex (params[0]); /* * If we have made it down here, then we know the post event is * ours to handle. */ if ((menuMgr) && (menuMgr->popup_mgr.topLevelPane)) ClassPost (menuMgr, event, TRUE); } /*************************************<->************************************* * * MMUnpost(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void MMUnpost (w, event, params, count) Widget w; XEvent * event; String * params; Cardinal count; { register XwPopupMgrWidget menuMgr; Boolean traversalOn; /* Extract the menu manager widget id */ menuMgr = (XwPopupMgrWidget) _XwMapFromHex (params[0]); /* * We need to use a temporary variable, because the application may * change this value from underneath us if it has attached any unpost * callbacks to any of the menupanes. */ traversalOn = menuMgr->manager.traversal_on; if ((menuMgr) && (menuMgr->menu_mgr.menuActive)) { /* * Unpost the menu. * To prevent undesirable side effects, the menuActive flag must * always be cleared before bring down the menu system. */ menuMgr->menu_mgr.menuActive = FALSE; Unpost (menuMgr, menuMgr->popup_mgr.topLevelPane); XUngrabPointer (XtDisplay(menuMgr), CurrentTime); /* * If traversal is on, and there are no sticky menupanes currently * being remembered, then we need to reset the focus item to the * first item in the menupane. */ if (traversalOn && (menuMgr->popup_mgr.numSavedCascades == 0)) { ((XwPopupMgrWidget)menuMgr->popup_mgr.topLevelPane)-> manager.active_child = NULL; XtSetKeyboardFocus (menuMgr->popup_mgr.topLevelPane, NULL); } /* * If we warped the mouse because traversal was on, then we need * to move it back to where it was. */ if (traversalOn && (menuMgr->popup_mgr.origMouseX != -1) && (menuMgr->popup_mgr.origMouseY != -1)) { XWarpPointer (XtDisplay (menuMgr), None, RootWindowOfScreen(menuMgr->core.screen), 0, 0, 0, 0, menuMgr->popup_mgr.origMouseX, menuMgr->popup_mgr.origMouseY); menuMgr->popup_mgr.origMouseX = -1; menuMgr->popup_mgr.origMouseY = -1; } } } /*************************************<->************************************* * * MMAccelPost (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void MMAccelPost (w, event, params, count) Widget w; XEvent * event; String * params; Cardinal count; { register XwPopupMgrWidget menuMgr; /* Extract the menu manager widget id */ menuMgr = (XwPopupMgrWidget) _XwMapFromHex (params[0]); /* * If we have made it down here, then we know the accelerator event is * ours to handle. */ if ((menuMgr) && (menuMgr->popup_mgr.topLevelPane)) ClassPost (menuMgr, event, FALSE); } /*************************************<->************************************* * * MMAccelSelect (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void MMAccelSelect (w, event, params, count) Widget w; XEvent * event; String * params; Cardinal count; { register XwPopupMgrWidget menuMgr; /* Extract the menu manager widget id */ menuMgr = (XwPopupMgrWidget) _XwMapFromHex (params[0]); if (menuMgr == NULL) return; /* * if menus are not inherited, throw out events that did occur * in the associated widget's children. This takes care of the case * where the child does not select for key events, so they propagate * to us. */ if (menuMgr->menu_mgr.associateChildren == FALSE) { if ((event->xkey.window == XtWindow (XtParent (XtParent (menuMgr)))) && (event->xkey.subwindow != 0) && (XtWindowToWidget (XtDisplay (menuMgr), event->xkey.subwindow))) return; } /* * If we have made it down here, then we know the accelerator event is * ours to handle. */ if (menuMgr->popup_mgr.topLevelPane) ClassSelect ((XwMenuMgrWidget)menuMgr, event); } /*************************************<->************************************* * * AttachPane(menuMgr, menupane, name) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void AttachPane (menuMgr, menupane, name) register XwPopupMgrWidget menuMgr; register XwMenuPaneWidget menupane; String name; { register XwMenuButtonWidget menubutton; Arg arg[1]; register int i, j; CompositeWidget shell, mpane; XrmName nameQuark; if (name == NULL) return; /* * Save a copy of the menupane Id, since this has been called as * a result of a SetValues being done on the menupane, and all of * our lists only contain the old widget pointer. */ menuMgr->popup_mgr.attachPane = menupane; /* * check if name is menu manager's name, then this is to be a top level */ if ((strcmp (name, menuMgr->core.name) == 0) && (XwStrlen(name) == XwStrlen(menuMgr->core.name))) { /* Remove the cascade list when the top level pane changes */ menuMgr->popup_mgr.numSavedCascades = 0; if (menuMgr->popup_mgr.topLevelPane != NULL) { /* Detach the previous top level pane */ XtSetArg (arg[0], XtNattachTo, (XtArgVal) NULL); XtSetValues (menuMgr->popup_mgr.topLevelPane, arg, 1); } /* * save menupane ID. Since this may be called from menupane's * SetValues routine, the widget passed may be the new structure. * Grab the self field in core to insure the right ID is used. */ menuMgr->popup_mgr.topLevelPane = menupane->core.self; menupane->menu_pane.attachId = NULL; SetTreeAccelerators (menuMgr, menupane); } else { /* * check if its a menubutton name in the menu system. */ nameQuark = XrmStringToQuark(name); for (i = 0; i < menuMgr->core.num_popups; i++) { /* * for each shell, if its child is a menupane and the menupane * has a menubutton of the passed in name, then do the attach */ shell = (CompositeWidget) menuMgr->core.popup_list[i]; if ((shell->composite.num_children == 1) && (XtIsSubclass(shell->composite.children[0], XwmenupaneWidgetClass))) { mpane = (CompositeWidget) shell->composite.children[0]; for (j = 0; j < mpane->composite.num_children; j++) { menubutton = (XwMenuButtonWidget)mpane->composite.children[j]; if (menubutton->core.xrm_name == nameQuark) { Widget dwidget = NULL; /* * If there is a menupane already attached to this * button, then we need to first detach it. */ XtSetArg (arg[0], XtNcascadeOn, (XtArgVal) &dwidget); XtGetValues ((Widget)menubutton, arg, 1); if (dwidget) { /* Detach the old pane */ Widget temppane = dwidget; XtSetArg (arg[0], XtNattachTo, (XtArgVal) NULL); XtSetValues (temppane, arg, 1); } XtSetArg (arg[0], XtNcascadeOn, (XtArgVal) menupane->core.self); XtSetValues ((Widget)menubutton, arg, 1); menupane->menu_pane.attachId = (Widget) menubutton; XtAddCallback ((Widget) menubutton, XtNcascadeSelect, (XtCallbackProc)_XwCascadeSelect, menupane->core.self); XtAddCallback ((Widget) menubutton, XtNcascadeUnselect, (XtCallbackProc)_XwCascadeUnselect, menupane->core.self); if (CompletePath(menuMgr, menupane)) SetTreeAccelerators (menuMgr, menupane); menuMgr->popup_mgr.attachPane = NULL; return; } } } } /* * Couldn't find a menubutton with this name */ AddToPendingList (menuMgr, menupane, name); } /* This always needs to be cleared out when we are done */ menuMgr->popup_mgr.attachPane = NULL; } /*************************************<->************************************* * * DetachPane (menuMgr, menupane, name) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void DetachPane (menuMgr, menupane, name) register XwPopupMgrWidget menuMgr; XwMenuPaneWidget menupane; String name; { register int i; Arg arg; XwMenuButtonWidget menubutton; CompositeWidget shell; if (name == NULL) return; /* * if menupane is on the pending attach list, remove it and return */ for (i=0; i < menuMgr->menu_mgr.numAttachReqs; i++) { if (menuMgr->menu_mgr.pendingAttachList[i].menupaneId == menupane->core.self) { menuMgr->menu_mgr.pendingAttachList[i] = menuMgr->menu_mgr.pendingAttachList [--menuMgr->menu_mgr.numAttachReqs]; return; } } /* * Save a copy of the menupane Id, since this has been called as * a result of a SetValues being done on the menupane, and all * of our lists only contain the old widget pointer. */ menuMgr->popup_mgr.detachPane = menupane; /* * if name is the menu manager's name, removing top level */ if ((strcmp (name, menuMgr->core.name) == 0) && (XwStrlen(name) == XwStrlen(menuMgr->core.name))) { menupane->menu_pane.attachId = (Widget) NULL; if (menupane->core.self == menuMgr->popup_mgr.topLevelPane) { ClearTreeAccelerators (menuMgr, menupane->core.self); menuMgr->popup_mgr.topLevelPane = NULL; } } else { /* * detach pane from menubutton. Look on each of the menu managers * popup children for a menupane which contains this named menubutton. */ for (i = 0; i < menuMgr->core.num_popups; i++) { shell = (CompositeWidget) menuMgr->core.popup_list[i]; if ((shell->composite.num_children == 1) && (XtIsSubclass(shell->composite.children[0], XwmenupaneWidgetClass))) { if ((menubutton = (XwMenuButtonWidget) XtNameToWidget(shell->composite.children[0], name)) != NULL) { /* * Found it! Detach it */ DoDetach (menuMgr, menubutton, menupane); menuMgr->popup_mgr.detachPane = NULL; return; } } } } /* This must always be cleared when we leave */ menuMgr->popup_mgr.detachPane = NULL; } /*************************************<->************************************* * * DoDetach(parameters) * * Description: * ----------- * This routine does the actual work of detaching a menupane from a * menubutton. It has been separated out from DetachPane() due to * a BUG in the X toolkits destroy scenario. When the toolkit is * destroying popup children, it replaces the widget Id entry within * the popup list with the window Id for that widget. DetachPane() * attempts to traverse this list, looking for the named menubutton. * Unfortunately, if this list contains any window Ids, then we may * get a core dump; this only can happen when the menu is being * destroyed. * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void DoDetach (menuMgr, menubutton, menupane) register XwPopupMgrWidget menuMgr; XwMenuButtonWidget menubutton; XwMenuPaneWidget menupane; { Arg arg[1]; register int i; /* Detach a menupane from a menubutton */ XtRemoveCallback ((Widget)menubutton, XtNcascadeSelect, (XtCallbackProc)_XwCascadeSelect, menupane->core.self); XtRemoveCallback ((Widget)menubutton, XtNcascadeUnselect, (XtCallbackProc)_XwCascadeUnselect, menupane->core.self); ClearTreeAccelerators (menuMgr, menupane->core.self); XtSetArg (arg[0], XtNcascadeOn, (XtArgVal) NULL); XtSetValues ((Widget)menubutton, arg, 1); menupane->menu_pane.attachId = NULL; /* * If this menupane is on the saved cascade list (because sticky * menus are enabled), then we need to clean up the list, so that * we don't dump core the next time we post. */ if ((menuMgr->popup_mgr.stickyMode) && (menuMgr->popup_mgr.numSavedCascades > 0)) { for (i = 0; i < menuMgr->popup_mgr.numSavedCascades; i++) { if (menuMgr->popup_mgr.savedCascadeList[i] == (Widget)menupane->core.self) { menuMgr->popup_mgr.numSavedCascades = 0; break; } } } } /*************************************<->************************************* * * AddPane(menuMgr, menupane) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void AddPane (menuMgr, menupane) register XwMenuMgrWidget menuMgr; XwMenuPaneWidget menupane; { register int k; register int traversalType; register XwMenuButtonWidget mbutton; XtAddCallback ((Widget)menupane, XtNdestroyCallback, (XtCallbackProc)_XwMenuPaneCleanup, menuMgr); if (menuMgr->menu_mgr.selectString) { SetUpTranslation (menupane, menuMgr->menu_mgr.selectString, selectTemplate); } if (menuMgr->menu_mgr.unpostString) { RegisterTranslation (menupane, menuMgr->menu_mgr.unpostString, unpostTemplate, menuMgr); } if (menuMgr->menu_mgr.kbdSelectString) { SetUpTranslation (menupane, menuMgr->menu_mgr.kbdSelectString, selectTemplate); } /* Propogate the state of our traversal flag */ if (XtIsSubclass ((Widget)menupane, XwmenupaneWidgetClass)) { (*(((XwMenuPaneWidgetClass) XtClass(menupane))-> menu_pane_class.setTraversalFlag)) ((Widget)menupane, menuMgr->manager.traversal_on); } /****************** * THE FOLLOWING IS NOT NEEDED, SINCE A MENUPANE CAN NEVER HAVE * ANY CHILDREN AT THIS TIME. * * traversalType = (menuMgr->manager.traversal_on) ? XwHIGHLIGHT_TRAVERSAL: * XwHIGHLIGHT_OFF; * * for (k = 0; k < menupane->manager.num_managed_children; k++) * { * /* Here we set the traversal flag for the menubuttons * mbutton = (XwMenuButtonWidget)menupane->composite.children[k]; * if (XtIsSubclass (mbutton, XwmenubuttonWidgetClass)) * { * (*(((XwMenuButtonWidgetClass) * XtClass(mbutton))-> menubutton_class.setTraversalType)) * (mbutton, traversalType); * } * } *********************/ } /*************************************<->************************************* * * SetSelectAccelerator (menuMgr, menubutton, accelString, accelEventType, * accelKey, accelModifiers) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void SetSelectAccelerator (menuMgr, menubutton, accelString, accelEventType, accelKey, accelModifiers) Widget menuMgr; Widget menubutton; String accelString; unsigned int accelEventType; unsigned int accelKey; unsigned int accelModifiers; { Widget widget; XwKeyAccel * tempAccel; if (CompletePath(menuMgr, menubutton->core.parent)) { ClearSelectAccelerator (menuMgr, menubutton->core.self); SetButtonAccelerators (menuMgr, menubutton->core.self, accelString, accelEventType, accelKey, accelModifiers); } } /*************************************<->************************************* * * ClearSelectAccelerator (menuMgr, menubutton) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void ClearSelectAccelerator (menuMgr, menubutton) register XwPopupMgrWidget menuMgr; Widget menubutton; { register int i; Widget widget; /* * search accelerator table for menubutton */ for (i=0; i < menuMgr->menu_mgr.numAccels; i++) { if (menuMgr->menu_mgr.menuBtnAccelTable[i].menuBtn == menubutton->core.self) { /* * remove translation in associated widget & toplevel pane * and remove grab */ widget = (Widget) XtParent (XtParent (menuMgr)); RegisterTranslation (widget, menuMgr->menu_mgr.menuBtnAccelTable[i].accelString, accelSelectTemplate, NULL); if (menuMgr->popup_mgr.topLevelPane) { RegisterTranslation (menuMgr->popup_mgr.topLevelPane, menuMgr->menu_mgr.menuBtnAccelTable[i].accelString, accelSelectTemplate, NULL); /* * Because of a short coming in the toolkit, we need to * potentially patch the top level widget's translations, * if it was in the middle of a setvalues. */ if ((menuMgr->popup_mgr.detachPane) && (menuMgr->popup_mgr.topLevelPane == menuMgr->popup_mgr.detachPane->core.self)) { menuMgr->popup_mgr.detachPane->core.tm = menuMgr->popup_mgr.topLevelPane->core.tm; menuMgr->popup_mgr.detachPane->core.event_table = menuMgr->popup_mgr.topLevelPane->core.event_table; } } if ((menuMgr->menu_mgr.associateChildren) && (XtIsRealized (widget))) { XUngrabKey (XtDisplay (widget), menuMgr->menu_mgr.menuBtnAccelTable[i].accelKey, menuMgr->menu_mgr.menuBtnAccelTable[i].accelModifiers, XtWindow (widget)); } /* * remove menubutton accelerator table entry */ menuMgr->menu_mgr.menuBtnAccelTable[i] = menuMgr->menu_mgr.menuBtnAccelTable[--menuMgr->menu_mgr.numAccels]; return; } } } /*************************************<->************************************* * * AddButton(menuMgr, menubutton) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void AddButton (menuMgr, menubutton) register XwMenuMgrWidget menuMgr; register Widget menubutton; { Arg args[2]; Widget pending; int traversalType; if (menuMgr->menu_mgr.selectString) { SetUpTranslation (menubutton, menuMgr->menu_mgr.selectString, selectTemplate); } if (menuMgr->menu_mgr.unpostString) { RegisterTranslation (menubutton, menuMgr->menu_mgr.unpostString, unpostTemplate, menuMgr); } if (menuMgr->menu_mgr.kbdSelectString) { SetUpTranslation (menubutton, menuMgr->menu_mgr.kbdSelectString, selectTemplate); } XtSetArg (args[0], XtNcascadeOn, (XtArgVal) NULL); XtSetArg (args[1], XtNmgrOverrideMnemonic, (XtArgVal) TRUE); XtSetValues (menubutton, args, XtNumber(args)); XtAddCallback (menubutton, XtNdestroyCallback, (XtCallbackProc)_XwMenuButtonCleanup, menuMgr); /* * Accelerators are now handled when the button is managed! */ /* * if this menubutton is on the pending attach list, do attach */ if (pending = PendingAttach (menuMgr, menubutton)) AttachPane (menuMgr, pending, menubutton->core.name); /* Propogate the our traversal state */ traversalType = (menuMgr->manager.traversal_on) ? XwHIGHLIGHT_TRAVERSAL: XwHIGHLIGHT_OFF; if (XtIsSubclass (menubutton, XwmenubuttonWidgetClass)) { (*(((XwMenuButtonWidgetClass) XtClass(menubutton))-> menubutton_class.setTraversalType)) (menubutton, traversalType); } } /*************************************<->************************************* * * Unpost (menuMgr, menupane) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void Unpost (menuMgr, menupane) register XwPopupMgrWidget menuMgr; XwMenuPaneWidget menupane; { register int i,j; register XwMenuPaneWidget pane; register Widget * currentCascadeList; register WidgetList managed_children; /* * To keep all window managers happy, move the focus before we pop * down the menupane with the current focus. */ if ((menupane != (XwMenuPaneWidget) menuMgr->popup_mgr.topLevelPane) && (menuMgr->manager.traversal_on)) { XwMoveFocus (XtParent(menupane->menu_pane.attachId)); } currentCascadeList = menuMgr->popup_mgr.currentCascadeList; /* * popdown any cascading submenus including menupane */ for (i = menuMgr->popup_mgr.numCascades - 1; i >= 0; i--) { /* * for each button in pane, unhighlight it and clear cascade flag */ pane = ((XwMenuPaneWidget) currentCascadeList[i]); XtRemoveGrab ((Widget)pane); XtPopdown (XtParent (pane)); managed_children = pane->manager.managed_children; for (j = 0; j < pane->manager.num_managed_children; j++) { if (XtIsSubclass (managed_children[j], XwmenubuttonWidgetClass)) { (*(((XwMenuButtonWidgetClass) XtClass(managed_children[j]))->menubutton_class.unhighlightProc)) (managed_children[j]); (*(((XwMenuButtonWidgetClass) XtClass(managed_children[j]))->menubutton_class.clearCascadeProc)) (managed_children[j]); } } --menuMgr->popup_mgr.numCascades; /* * if this is not the target pane then the next one must have had an enter * window event handler added when cascaded out of it. */ if ((pane != menupane) && (i > 0)) { if (menuMgr->manager.traversal_on == FALSE) { XtRemoveEventHandler (currentCascadeList[i-1], EnterWindowMask, FALSE, (XtEventHandler) ((XwMenuButtonWidgetClass) XtClass(pane->menu_pane.attachId))-> menubutton_class.enterParentProc, pane->menu_pane.attachId); } else { /* Kludge to force the grablist to get cleaned up */ XtSetKeyboardFocus (XtParent(pane), None); } } else { if (menuMgr->manager.traversal_on) { /* Kludge to force the grablist to get cleaned up */ XtSetKeyboardFocus (XtParent(pane), None); } XFlush (XtDisplay(menuMgr)); return; } } } /*************************************<->************************************* * * Post(menuMgr, menupane, grabtype) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * * _XtPopup * *************************************<->***********************************/ static void Post (menuMgr, menupane, grabtype) register XwPopupMgrWidget menuMgr; Widget menupane; XtGrabKind grabtype; { register int i; /* * if already posted, do nothing */ for (i=0; i < menuMgr->popup_mgr.numCascades; i++) if (menuMgr->popup_mgr.currentCascadeList[i] == menupane) return; /* * set up grabs */ if (grabtype == XtGrabNonexclusive) { _XtPopup (XtParent (menupane), grabtype, FALSE); XtAddGrab (menupane, FALSE, FALSE); } else { _XtPopup (XtParent (menupane), grabtype, TRUE); XtAddGrab (menupane, TRUE, TRUE); } /* * add menupane to current cascade list */ if (menuMgr->popup_mgr.numCascades == menuMgr->popup_mgr.sizeCascadeList) { menuMgr->popup_mgr.sizeCascadeList = 2 * menuMgr->popup_mgr.sizeCascadeList; menuMgr->popup_mgr.currentCascadeList = (Widget *) XtRealloc((char *)(menuMgr->popup_mgr.currentCascadeList), menuMgr->popup_mgr.sizeCascadeList * sizeof (Widget)); } menuMgr->popup_mgr.currentCascadeList[menuMgr->popup_mgr.numCascades++] = menupane; } /*************************************<->************************************* * * ProcessSelect(menuMgr, widget, event) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static Boolean ProcessSelect (menuMgr, widget, event) register XwPopupMgrWidget menuMgr; Widget widget; XEvent * event; { register XwKeyAccel * accelerator; register int i; Boolean found = FALSE; Widget assocWidget; Boolean traversalOn; /* * If the menu manager or the associated widget is insensitive, then * ignore the select request. */ assocWidget = XtParent(XtParent(menuMgr)); if (!XtIsSensitive((Widget)menuMgr) || !XtIsSensitive(assocWidget)) return (FALSE); /* * is this a valid button event? */ if ((event->xany.type==ButtonPress) || (event->xany.type==ButtonRelease)) { if (_XwMatchBtnEvent (event, menuMgr->menu_mgr.selectEventType, menuMgr->menu_mgr.selectButton, menuMgr->menu_mgr.selectModifiers)) { if (menuMgr->menu_mgr.menuActive == FALSE) return (FALSE); /* * During traversal, since menupanes are not unposted when * the mouse enters a different menupane, we need to ignore * selects which occur in a menubtn in one of these panes. */ if ((menuMgr->manager.traversal_on) && (XtIsSubclass (widget, XwmenubuttonWidgetClass)) && (XtParent(widget) != menuMgr->popup_mgr.currentCascadeList[ menuMgr->popup_mgr.numCascades - 1])) { return (FALSE); } SetUpStickyList (menuMgr, widget); } else return (FALSE); } /* * if its not key accelerator, return false */ else if ((event->xany.type == KeyPress) || (event->xany.type == KeyRelease)) { /* Check for the kbd select event */ if (_XwMatchKeyEvent (event, menuMgr->menu_mgr.kbdSelectEventType, menuMgr->menu_mgr.kbdSelectKey, menuMgr->menu_mgr.kbdSelectModifiers)) { SetUpStickyList (menuMgr, widget); } else { for (i=0; (i < menuMgr->menu_mgr.numAccels) && (found == FALSE); i++) { accelerator = menuMgr->menu_mgr.menuBtnAccelTable + i; found = _XwMatchKeyEvent (event, accelerator->accelEventType, accelerator->accelKey, accelerator->accelModifiers); } if (found == FALSE) return (FALSE); menuMgr->popup_mgr.numSavedCascades = 0; if (menuMgr->manager.traversal_on) { /* Force the first item to be highlighted next time */ ((XwPopupMgrWidget)menuMgr->popup_mgr.topLevelPane)-> manager.active_child = NULL; XtSetKeyboardFocus (menuMgr->popup_mgr.topLevelPane, NULL); } /* SetUpStickyList (menuMgr, widget); */ } } else return (FALSE); /* * if the menu system is active, bring it down */ if (menuMgr->menu_mgr.menuActive) { /* * We need to use a temporary variable because the application has * the chance to change this from underneath us if they have set up * an unpost callback. */ traversalOn = menuMgr->manager.traversal_on; menuMgr->menu_mgr.menuActive = FALSE; Unpost (menuMgr, menuMgr->popup_mgr.topLevelPane); /* We need to remove the grab we set in ClassPost() */ XUngrabPointer (XtDisplay(menuMgr), CurrentTime); /* * If we warped the mouse because traversal was on, then we need * to move it back to where it was. */ if (traversalOn && (menuMgr->popup_mgr.origMouseX != -1) && (menuMgr->popup_mgr.origMouseY != -1)) { XWarpPointer (XtDisplay (menuMgr), None, RootWindowOfScreen(menuMgr->core.screen), 0, 0, 0, 0, menuMgr->popup_mgr.origMouseX, menuMgr->popup_mgr.origMouseY); menuMgr->popup_mgr.origMouseX = -1; menuMgr->popup_mgr.origMouseY = -1; } } return (TRUE); } /*************************************<->************************************* * * ValidEvent(menuMgr, menubutton, event) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static Boolean ValidEvent (menuMgr, menubutton, event) XwPopupMgrWidget menuMgr; Widget menubutton; XEvent * event; { /* Ignore enter and leave events if traversal is active */ if (menuMgr->manager.traversal_on) return (FALSE); else return (TRUE); } /*************************************<->************************************* * * DoICascade (menuMgr) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static Boolean DoICascade (menuMgr, menuBtn) register XwPopupMgrWidget menuMgr; Widget menuBtn; { /* Ignore cascade events if traversal is currently active */ if ((menuMgr->menu_mgr.menuActive) && (menuMgr->manager.traversal_on == FALSE)) { Widget lastCascade = menuMgr->popup_mgr.currentCascadeList[ menuMgr->popup_mgr.numCascades -1]; if ((XtParent(menuBtn) == lastCascade) || (menuBtn == ((XwMenuPaneWidget)(lastCascade))->menu_pane.attachId)) { return (TRUE); } } return (FALSE); } /*************************************<->************************************* * * ClassPost(menuMgr, event, warpOn) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void ClassPost (menuMgr, event, warpOn) register XwPopupMgrWidget menuMgr; XEvent * event; Boolean warpOn; { register int i; Position posx, posy; int x, y, relativeX, relativeY; int yDelta; XButtonEvent * buttonEvent = (XButtonEvent *) event; Widget assocWidget, w; Widget menuBtn; XwMenuPaneWidget tempPane; XwManagerWidget topLevelPane; XWindowChanges windowChanges; ShellWidget shell; Window root, child; int rootx, rooty, childx, childy; unsigned int returnMask; /* * If either the menu manager or the associated widget is insensitive, * then ignore the post request. */ assocWidget = XtParent (XtParent (menuMgr)); if (!XtIsSensitive((Widget)menuMgr) || !XtIsSensitive(assocWidget)) return; /* * if menus are not inherited, throw out events that did occur * in the associated widget's children. */ if (menuMgr->menu_mgr.associateChildren == FALSE) { if ((event->xkey.window == XtWindow (XtParent (XtParent (menuMgr)))) && (event->xkey.subwindow != 0) && (XtWindowToWidget (XtDisplay (menuMgr), event->xkey.subwindow))) return; } /* * The following is a kludge fix to bypass a shortcoming within Xtk. * We need to manually inform the previous focus widget that it has * lost the cursor; this is because once we add our popups to the * grablist, the LeaveNotify will be ignored for the focus widget, * because it is not on the grablist. */ /* if (menuMgr->manager.traversal_on) */ SendFakeLeaveNotify(menuMgr, ULTIMATE_PARENT); /* Mark the menu system as 'active' */ menuMgr->menu_mgr.menuActive = TRUE; /* * Position the menupane's parent shell. If its a post, position to * the pointer position. If its an accelerator, position to the center * of the associated widget */ topLevelPane =(XwManagerWidget)menuMgr->popup_mgr.topLevelPane; /* * In order for this algorithm to work, we need to make sure the * menupane has been realized; this is because its size is not * yet valid because it is not notified that it has any children * until it is realized. */ if (!XtIsRealized (XtParent(topLevelPane ))) XtRealizeWidget (XtParent (topLevelPane )); if ((event->type == ButtonPress) || (event->type == ButtonRelease)) { /* * The server does an implicit grab, but doesn't do it in the * manner we need for menupanes and menubuttons to continue * to highlight and function. */ if (event->type == ButtonPress) XUngrabPointer (XtDisplay(menuMgr), CurrentTime); /* * If traversal is on, then save the current mouse position, * so that we can restore the mouse to its original position * when the menu is unposted. */ if (menuMgr->manager.traversal_on) { menuMgr->popup_mgr.origMouseX = buttonEvent->x_root; menuMgr->popup_mgr.origMouseY = buttonEvent->y_root; } x = buttonEvent->x_root - (XwCASCADEWIDTH + FUDGE_FACTOR + topLevelPane ->core.border_width); y = buttonEvent->y_root; /* Attempt to center on the first button in the new menupane */ { if (topLevelPane->manager.num_managed_children > 0) { Widget firstButton = topLevelPane->manager.managed_children[0]; /* yDelta = firstButton->core.y + firstButton->core.border_width + (firstButton->core.height >> 1); */ yDelta = 0; } else { yDelta = topLevelPane->core.border_width + (topLevelPane->core.height >> 1); } } y -= yDelta; } else { /* * center on the associated widget */ relativeX = (assocWidget->core.width>>1); relativeY = (assocWidget->core.height>>1); /* Get our coordinates, relative to the root window */ XTranslateCoordinates (XtDisplay(menuMgr), assocWidget->core.window, RootWindowOfScreen(XtScreen(menuMgr)), relativeX, relativeY, &x, &y, &child); /* * If traversal is on, then save the current mouse position, * so that we can restore the mouse to its original position * when the menu is unposted. */ if (menuMgr->manager.traversal_on) { if (XQueryPointer(XtDisplay(menuMgr), RootWindowOfScreen(menuMgr->core.screen), &root, &child, &rootx, &rooty, &childx, &childy, &returnMask)) { menuMgr->popup_mgr.origMouseX = rootx; menuMgr->popup_mgr.origMouseY = rooty; } } } posx = x; posy = y; ForceMenuPaneOnScreen (topLevelPane, &posx, &posy); x = posx; y = posy; XtMoveWidget (XtParent(topLevelPane), x, y); /* * This allows us to catch all selects, and unpost the menus * regardless where the select event occurs. */ XGrabPointer (XtDisplay(menuMgr), XtWindow(assocWidget), TRUE, ButtonPressMask|ButtonReleaseMask|EnterWindowMask|LeaveWindowMask| PointerMotionMask, GrabModeAsync, GrabModeAsync, None, (Cursor)NULL, CurrentTime); if ((menuMgr->popup_mgr.stickyMode) && (menuMgr->popup_mgr.numSavedCascades)) { /* * Attempt to gracefully handle the case where one of the menupanes * or menubuttons in the current cascade list has become insensitive * since the last posting. */ for (i=0; i < menuMgr->popup_mgr.numSavedCascades; i++) { tempPane = (XwMenuPaneWidget)menuMgr->popup_mgr.savedCascadeList[i]; if ((tempPane->menu_pane.attachId) && (!XtIsSensitive(tempPane->menu_pane.attachId))) { /* * We are cascading from an insensitive menubutton. * Stop at the preceeding menupane. */ menuMgr->popup_mgr.numSavedCascades = i; menuMgr->popup_mgr.lastSelected = tempPane->menu_pane.attachId; break; } else if (!XtIsSensitive((Widget)tempPane)) { /* * If this menupane is insensitive, then stop here. */ menuMgr->popup_mgr.numSavedCascades = i + 1; if (tempPane->manager.num_managed_children > 0) { menuMgr->popup_mgr.lastSelected = tempPane->manager. managed_children[0]; } else menuMgr->popup_mgr.lastSelected = (Widget)tempPane; break; } } /* Set up grab for the top level menupane */ XtAddGrab (XtParent(topLevelPane), TRUE, TRUE); XtAddGrab ((Widget)topLevelPane, TRUE, TRUE); menuMgr->popup_mgr.currentCascadeList[menuMgr->popup_mgr.numCascades++] = (Widget)topLevelPane; /* * position the menupanes on the sticky cascade list */ for (i=1; i < menuMgr->popup_mgr.numSavedCascades; i++) { tempPane = (XwMenuPaneWidget) menuMgr->popup_mgr.savedCascadeList[i]; PositionCascade (tempPane->menu_pane.attachId, tempPane); XtAddGrab (XtParent(tempPane), FALSE, FALSE); XtAddGrab ((Widget)tempPane, FALSE, FALSE); menuMgr->popup_mgr.currentCascadeList [menuMgr->popup_mgr.numCascades++] = (Widget)tempPane; } /* * warp the pointer to its final destination. This must be done * AFTER the panes are positioned, but BEFORE they are posted. */ if (warpOn) { XWarpPointer (XtDisplay (menuMgr), None, XtWindow(menuMgr->popup_mgr.lastSelected), 0, 0, 0, 0, 5, 5); } /* Post the last menupane */ shell = (ShellWidget)XtParent (menuMgr->popup_mgr.savedCascadeList [menuMgr->popup_mgr.numSavedCascades - 1]); shell->shell.popped_up = TRUE; shell->shell.grab_kind = XtGrabNonexclusive; shell->shell.spring_loaded = FALSE; if (!XtIsRealized((Widget)shell)) XtRealizeWidget ((Widget)shell); XMapRaised (XtDisplay(shell), XtWindow(shell)); /* * Post and then configure the menupanes and menubuttons. * THIS MUST BE DONE AFTER THE WARP POINTER!!! */ for (i= menuMgr->popup_mgr.numSavedCascades - 2; i >= 0; i--) { /* * Popup the pane and add to the grablist. * This cannot use _XtPopup() because it used XMapRaised(). */ tempPane = (XwMenuPaneWidget) menuMgr->popup_mgr.savedCascadeList[i]; shell = (ShellWidget) XtParent(tempPane); windowChanges.sibling = XtWindow(XtParent(menuMgr->popup_mgr. savedCascadeList[i+1])); windowChanges.stack_mode = Below; XConfigureWindow (XtDisplay(menuMgr), XtWindow(shell), CWSibling | CWStackMode, &windowChanges); shell->shell.popped_up = TRUE; shell->shell.grab_kind = XtGrabNonexclusive; shell->shell.spring_loaded = FALSE; if (!XtIsRealized ((Widget)shell)) XtRealizeWidget ((Widget)shell); XMapWindow (XtDisplay(shell), XtWindow(shell)); /* * highlight menubutton, set its cascade flag and set up event * handler on its parent */ menuBtn = (((XwMenuPaneWidget)(menuMgr->popup_mgr. savedCascadeList[i+1]))->menu_pane.attachId); if (menuMgr->manager.traversal_on == FALSE) { (*(((XwMenuButtonWidgetClass) XtClass(menuBtn))->menubutton_class.highlightProc)) (menuBtn); } (*(((XwMenuButtonWidgetClass) XtClass(menuBtn))-> menubutton_class.setCascadeProc)) (menuBtn); if (menuMgr->manager.traversal_on == FALSE) { XtAddEventHandler ((Widget)tempPane, EnterWindowMask, False, (XtEventHandler) ((XwMenuButtonWidgetClass) XtClass(menuBtn))-> menubutton_class.enterParentProc, menuBtn); } } /* * DON'T DO THE FOLLOWING; HAVE THE ITEM HILIGHT WHEN THE CURSOR * ENTERS IT. */ /******** * for keyboard posts, highlight the last selected menubutton. * *if ((warpOn == FALSE) && (menuMgr->manager.traversal_on == FALSE)) *{ * (*(((XwMenuButtonWidgetClass) * XtClass(menuMgr->popup_mgr.lastSelected))-> * menubutton_class.highlightProc)) * (menuMgr->popup_mgr.lastSelected); *} *******/ } else { /* * post only the toplevel. * post the toplevel menupane with exclusive grabs. All other panes * have nonexclusive grabs. */ Post (menuMgr, topLevelPane, XtGrabExclusive); } /* Set the traversal focus to the last pane, if necessary */ if (menuMgr->manager.traversal_on) { XwMoveFocus (menuMgr->popup_mgr.currentCascadeList [ menuMgr->popup_mgr.numCascades - 1]); } XFlush(XtDisplay(menuMgr)); } /*************************************<->************************************* * * ClassSelect(menuMgr, event) * * Description: * ----------- * Called only when a menubutton select accelerator is received. * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void ClassSelect (menuMgr, event) XwMenuMgrWidget menuMgr; XEvent * event; { register int i; Widget assocWidget; register XwKeyAccel * accelerator; /* * If either the menu manager or the associated widget is insensitive, * then ignore the select accelerator. */ assocWidget = XtParent(XtParent(menuMgr)); if (!XtIsSensitive((Widget)menuMgr) || !XtIsSensitive(assocWidget)) return; /* * map the event into an accelerator and call the menubuttons select rtn. */ for (i=0; i < menuMgr->menu_mgr.numAccels; i++) { accelerator = menuMgr->menu_mgr.menuBtnAccelTable + i; if (_XwMatchKeyEvent (event, accelerator->accelEventType, accelerator->accelKey, accelerator->accelModifiers)) { if (XtIsSensitive(accelerator->menuBtn) && _XwAllAttachesAreSensitive(accelerator->menuBtn)) { (*(((XwMenuButtonWidgetClass) XtClass (accelerator->menuBtn))-> primitive_class.select_proc)) (accelerator->menuBtn, event); } return; } } } /*************************************<->************************************* * * AddToPendingList(menuMgr, menupane, name) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void AddToPendingList (menuMgr, menupane, name) register XwMenuMgrWidget menuMgr; Widget menupane; String name; { register int nameLen; register int i; Arg arg[1]; register String button; /* * If there is already a request in the pending list for an attach * to the same menu button, then we need to remove the older one. */ nameLen = XwStrlen(name); for (i = 0; i < menuMgr->menu_mgr.numAttachReqs; i++) { button = menuMgr->menu_mgr.pendingAttachList[i].menuBtnName; if ((strcmp(name, button) == 0) && (nameLen == XwStrlen(button))) { /* Detach the older request */ XtSetArg (arg[0], XtNattachTo, (XtArgVal) NULL); XtSetValues (menuMgr->menu_mgr.pendingAttachList[i].menupaneId, arg, 1); break; } } if (menuMgr->menu_mgr.numAttachReqs == menuMgr->menu_mgr.sizeAttachList) { /* * resize the list */ menuMgr->menu_mgr.sizeAttachList = 2 * menuMgr->menu_mgr.sizeAttachList; menuMgr->menu_mgr.pendingAttachList = (XwAttachList *) XtRealloc((char *)(menuMgr->menu_mgr.pendingAttachList), menuMgr->menu_mgr.sizeAttachList * sizeof(XwAttachList)); } menuMgr->menu_mgr.pendingAttachList [menuMgr->menu_mgr.numAttachReqs].menuBtnName = name; menuMgr->menu_mgr.pendingAttachList [menuMgr->menu_mgr.numAttachReqs++].menupaneId = menupane->core.self; } /*************************************<->************************************* * * SetButtonAccelerators (menuMgr, menubutton, accelString, accelEventType, * accelKey, accelModifiers) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void SetButtonAccelerators (menuMgr, menubutton, accelString, accelEventType, accelKey, accelModifiers) register XwPopupMgrWidget menuMgr; Widget menubutton; String accelString; unsigned int accelEventType; unsigned int accelKey; unsigned int accelModifiers; { Widget widget; register XwKeyAccel * accelerator; /* * add entry to menubutton accelerator table */ if (menuMgr->menu_mgr.numAccels == menuMgr->menu_mgr.sizeAccelTable) { menuMgr->menu_mgr.sizeAccelTable = 2 * menuMgr->menu_mgr.sizeAccelTable; menuMgr->menu_mgr.menuBtnAccelTable = (XwKeyAccel *) XtRealloc((char *)(menuMgr->menu_mgr.menuBtnAccelTable), menuMgr->menu_mgr.sizeAccelTable * sizeof(XwKeyAccel)); } accelerator = menuMgr->menu_mgr.menuBtnAccelTable + menuMgr->menu_mgr.numAccels; accelerator->accelString = accelString; accelerator->accelEventType = accelEventType; accelerator->accelKey = accelKey; accelerator->accelModifiers = accelModifiers; accelerator->menuBtn = menubutton->core.self; menuMgr->menu_mgr.numAccels++; /* * set translation in associated widget & toplevel pane for accelerator */ widget = (Widget) XtParent (XtParent (menuMgr)); RegisterTranslation (widget, accelString, accelSelectTemplate, menuMgr); if (menuMgr->popup_mgr.topLevelPane) { RegisterTranslation (menuMgr->popup_mgr.topLevelPane, accelString, accelSelectTemplate, menuMgr); /* * Because of a short coming in the toolkit, we need to * potentially patch the top level widget's translations, * if it was in the middle of a setvalues. */ if ((menuMgr->popup_mgr.attachPane) && (menuMgr->popup_mgr.topLevelPane == menuMgr->popup_mgr.attachPane->core.self)) { menuMgr->popup_mgr.attachPane->core.tm = menuMgr->popup_mgr.topLevelPane->core.tm; menuMgr->popup_mgr.attachPane->core.event_table = menuMgr->popup_mgr.topLevelPane->core.event_table; } } /* * set up key grabs if possible */ if ((menuMgr->menu_mgr.associateChildren) && (XtIsRealized (widget))) XGrabKey (XtDisplay (widget), accelKey, accelModifiers, XtWindow (widget), False, GrabModeAsync, GrabModeAsync); } /*************************************<->************************************* * * SetTreeAccelerators (menuMgr, menupane) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void SetTreeAccelerators (menuMgr, menupane) XwMenuMgrWidget menuMgr; XwMenuPaneWidget menupane; { register int i; Arg args[2]; unsigned int eventType; KeyCode key; unsigned int modifiers; KeySym tempKeysym; register WidgetList managed_children; managed_children = menupane->manager.managed_children; for (i=0; i < menupane->manager.num_managed_children; i++) { if (XtIsSubclass (managed_children[i], XwmenubuttonWidgetClass)) { String dkey = NULL; Widget dtree = NULL; XtSetArg (args[0], XtNkbdAccelerator, (XtArgVal)(&dkey)); XtSetArg (args[1], XtNcascadeOn, (XtArgVal)(&dtree)); XtGetValues (managed_children[i], args, XtNumber(args)); /* * set up keyboard accelerators */ if (dkey) { _XwMapKeyEvent (dkey, &eventType, &tempKeysym, &modifiers); key = XKeysymToKeycode (XtDisplay (menupane), tempKeysym); SetButtonAccelerators (menuMgr, managed_children[i], dkey, eventType, key, modifiers); } /* * traverse any submenus */ if (dtree) SetTreeAccelerators (menuMgr, dtree); } } } /*************************************<->************************************* * * ClearTreeAccelerators (menuMgr, menupane) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void ClearTreeAccelerators (menuMgr, menupane) Widget menuMgr; XwMenuPaneWidget menupane; { register int i; Arg arg[1]; register WidgetList managed_children; managed_children = menupane->manager.managed_children; for (i=0; i < menupane->manager.num_managed_children; i++) { if (XtIsSubclass (managed_children[i], XwmenubuttonWidgetClass)) { Widget twidg = NULL; ClearSelectAccelerator(menuMgr, managed_children[i]); /* * clear accelerators from any submenu */ XtSetArg (arg[0], XtNcascadeOn, (XtArgVal) &twidg); XtGetValues (managed_children[i], arg, 1); if (twidg) ClearTreeAccelerators (menuMgr, twidg); } } } /*************************************<->************************************* * * CompletePath(menuMgr, menupane) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static Boolean CompletePath (menuMgr, menupane) XwPopupMgrWidget menuMgr; XwMenuPaneWidget menupane; { register XwMenuPaneWidget pane; if (menuMgr->popup_mgr.topLevelPane == FALSE) return (FALSE); for (pane = menupane; pane != NULL; pane = (XwMenuPaneWidget) XtParent (pane->menu_pane.attachId)) { if (pane == (XwMenuPaneWidget) menuMgr->popup_mgr.topLevelPane) return (TRUE); if (pane->menu_pane.attachId == NULL) return (FALSE); if (pane->core.managed == False) return (FALSE); if ((pane->menu_pane.attachId)->core.mapped_when_managed == FALSE) return (FALSE); if ((pane->menu_pane.attachId)->core.managed == FALSE) return (FALSE); } return (FALSE); } /*************************************<->************************************* * * PendingAttach (menuMgr, menubutton) * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static Widget PendingAttach (menuMgr, menubutton) XwMenuMgrWidget menuMgr; register Widget menubutton; { register int i; Widget id; register String buttonName = menubutton->core.name; register int buttonNameLen = XwStrlen(buttonName); register XwAttachList * pendingAttachList; pendingAttachList = menuMgr->menu_mgr.pendingAttachList; for (i=0; i < menuMgr->menu_mgr.numAttachReqs; i++) { if ((strcmp (pendingAttachList[i].menuBtnName, buttonName) == 0) && (XwStrlen(pendingAttachList[i].menuBtnName) == buttonNameLen)) { id = pendingAttachList[i].menupaneId; /* Remove from pending attach list */ pendingAttachList[i] = pendingAttachList[--menuMgr->menu_mgr. numAttachReqs]; return (id); } } return (NULL); } /*************************************<->************************************* * * SetUpTranslation (widget, event, action) * * Description: * ----------- * * Inputs: * ------ * * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void SetUpTranslation (widget, event, action) Widget widget; String event; String action; { register String workSpace; XtTranslations translations; workSpace = &workArea[0]; strcpy (workSpace, "!"); strcat (workSpace, event); strcat (workSpace, action); /* * compile the translation and attach to the menupane */ translations = XtParseTranslationTable(workSpace); XtOverrideTranslations (widget, translations); /* XtDestroyStateTable (XtClass(widget), translations); */ } /*************************************<->************************************* * * SetPostMnemonic (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void SetPostMnemonic (menuMgr, menupane, mnemonic) Widget menuMgr; Widget menupane; String mnemonic; { } /*************************************<->************************************* * * ClearPostMnemonic (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void ClearPostMnemonic (menuMgr, menupane) Widget menuMgr; Widget menupane; { } /*************************************<->************************************* * * SetTitleAttributes (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void SetTitleAttributes(w, x) Widget w, x; { } /*************************************<->************************************* * * ForceMenuPaneOnScreen (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void ForceMenuPaneOnScreen (menupane, x, y) register Widget menupane; register Position * x; register Position * y; { int rightEdgeOfMenu, dispWidth; int bottomEdgeOfMenu, dispHeight; /* * In order for this algorithm to work, we need to make sure the * menupane has been realized; this is because its size is not * yet valid because it is not notified that it has any children * until it is realized. */ if (!XtIsRealized (XtParent(menupane))) XtRealizeWidget (XtParent (menupane)); /* Force the menupane to be completely visible */ rightEdgeOfMenu = *x + (menupane->core.border_width << 1) + menupane->core.width; bottomEdgeOfMenu = *y + (menupane->core.border_width << 1) + menupane->core.height; dispWidth = WidthOfScreen (XtScreen(menupane)); dispHeight = HeightOfScreen (XtScreen(menupane)); if (*x < 0) *x = 0; if (*y < 0) *y = 0; if (rightEdgeOfMenu >= dispWidth) *x -= (rightEdgeOfMenu - dispWidth + 1); if (bottomEdgeOfMenu >= dispHeight) *y -= (bottomEdgeOfMenu - dispHeight + 1); } /*************************************<->************************************* * * SetSelectMnemonic (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void SetSelectMnemonic (menuMgr, menubutton, mnemonic) Widget menuMgr; Widget menubutton; String mnemonic; { } /*************************************<->************************************* * * ClearSelectMnemonic (parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void ClearSelectMnemonic (menuMgr, menupane) Widget menuMgr; Widget menupane; { } /*************************************<->************************************* * * OnCascadeList(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static Boolean OnCascadeList (menuMgr, menupane) register XwPopupMgrWidget menuMgr; register XwMenuPaneWidget menupane; { register int i; if ((menuMgr->popup_mgr.stickyMode) && (menuMgr->popup_mgr.numSavedCascades > 0)) { for (i=0; i < menuMgr->popup_mgr.numSavedCascades; i++) { if ((Widget)menupane->core.self == menuMgr->popup_mgr.savedCascadeList[i]) { return (True); } } } return (False); } /*************************************<->************************************* * * PaneManagedChildren() * * Description: * ----------- * * * Inputs: * ------ * * * Outputs: * ------- * * * Procedures Called * ----------------- * *************************************<->***********************************/ static void PaneManagedChildren (menuMgr, menupane) register XwPopupMgrWidget menuMgr; register XwMenuPaneWidget menupane; { register Widget child; Boolean * wasManaged; Arg args[2]; unsigned int event; unsigned int tempKeySym; unsigned int modifiers; KeyCode key; register int i; Boolean parentOnCascadeList; String dkey; Widget dtree; if (CompletePath (menuMgr, menupane)) { /* * If the parent menupane is in the sticky menu list, then we * need to remember this, so that we can check if any of its * children which are now being unmanaged are cascading to * another entry in the sticky menu list. We will then clean * up the list, if necessary. */ parentOnCascadeList = OnCascadeList (menuMgr, menupane); for (i=0; i < menupane->composite.num_children; i++) { child = menupane->composite.children[i]; wasManaged = (Boolean *) child->core.constraints; if ((*wasManaged == FALSE) && (child->core.managed == TRUE)) { dkey = NULL; dtree = NULL; /* * child has gone from unmanaged to managed */ *wasManaged = TRUE; XtSetArg (args[0], XtNkbdAccelerator, (XtArgVal) &dkey); XtSetArg (args[1], XtNcascadeOn, (XtArgVal) &dtree); XtGetValues (child, args, XtNumber(args)); /* * keyboard accelerator? */ if (dkey) { _XwMapKeyEvent (dkey, &event, &tempKeySym, &modifiers); key = XKeysymToKeycode (XtDisplay(menuMgr), tempKeySym); SetSelectAccelerator (menuMgr, child, dkey, event, key, modifiers); } /* * Does this menubutton cascade? */ if (dtree) SetTreeAccelerators (menuMgr, dtree); } else if ((*wasManaged == TRUE) && (child->core.managed == FALSE)) { dkey = NULL; dtree = NULL; /* * child went from managed to unmanaged */ *wasManaged = FALSE; XtSetArg (args[0], XtNkbdAccelerator, (XtArgVal) &dkey); XtSetArg (args[1], XtNcascadeOn, (XtArgVal) &dtree); XtGetValues (child, args, XtNumber(args)); /* * accelerator to clear out? */ if (dkey) ClearSelectAccelerator (menuMgr, child); /* * Does this menubutton cascade? */ if (dtree) { ClearTreeAccelerators (menuMgr, dtree); if (Visible (menuMgr, dtree)) Unpost (menuMgr, dtree); /* * If this button cascaded to a menupane which was on * the saved cascade list, then we need to clean up the * saved cascade list. */ if (parentOnCascadeList && OnCascadeList(menuMgr, dtree)) { parentOnCascadeList = False; menuMgr->popup_mgr.numSavedCascades = 0; } } /* * If this button was the last selected item on the saved * cascade list, then we need to clean up the list. */ if (parentOnCascadeList && (child == menuMgr->popup_mgr.lastSelected)) { parentOnCascadeList = False; menuMgr->popup_mgr.numSavedCascades = 0; } } } } } /*************************************<->************************************* * * Visible * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static Boolean Visible (menuMgr, menupane) XwPopupMgrWidget menuMgr; Widget menupane; { register int i; register Widget * currentCascadeList =menuMgr->popup_mgr.currentCascadeList; for (i=0; i < menuMgr->popup_mgr.numCascades; i++) if (currentCascadeList[i] == menupane) return (TRUE); return (FALSE); } /*************************************<->************************************* * * SetMenuTranslations * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void SetMenuTranslations (menuMgr, translation) XwPopupMgrWidget menuMgr; XtTranslations translation; { register int i, j, k; register CompositeWidget shell; register XwMenuPaneWidget menupane; /* * Since the menupanes are our popup grand children, we * will process them simply by traversing our popup children list. */ for (i = 0; i < menuMgr->core.num_popups; i++) { shell = (CompositeWidget) menuMgr->core.popup_list[i]; for (j = 0; j < shell->composite.num_children; j++) { /* Here we set the translation for the menupanes */ menupane = (XwMenuPaneWidget)shell->composite.children[j]; XtOverrideTranslations ((Widget)menupane, translation); for (k = 0; k < menupane->manager.num_managed_children; k++) { /* Here we set the translation for the menubuttons */ XtOverrideTranslations(menupane->manager.managed_children[k], translation); } } } } /*************************************<->************************************* * * TraverseRight(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void TraverseRight (menuMgr, w) register XwPopupMgrWidget menuMgr; XwMenuButtonWidget w; { XwMenuPaneWidget menupane; Arg arg[1]; if ((menuMgr->manager.traversal_on) && (menuMgr->menu_mgr.menuActive) && (XtParent(w) == menuMgr->popup_mgr.currentCascadeList[ menuMgr->popup_mgr.numCascades - 1])) { /* Cascade to the menupane attached to the specified menubutton */ XtSetArg (arg[0], XtNcascadeOn, (XtArgVal) &menupane); XtGetValues ((Widget)w, arg, XtNumber(arg)); /* * Only cascade if there is a traversable primitive widget in * the pane we would be cascading to. */ if (_XwFindTraversablePrim (menupane) == FALSE) return; if (menupane) _XwCascadeSelect (w, menupane, NULL); } } /*************************************<->************************************* * * TraverseLeft(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void TraverseLeft (menuMgr, w) register XwPopupMgrWidget menuMgr; XwMenuButtonWidget w; { /* Traverse to the previous menupane, if there is one */ if ((menuMgr->menu_mgr.menuActive) && (menuMgr->manager.traversal_on) && (menuMgr->popup_mgr.numCascades > 1) && (XtParent(w) == menuMgr->popup_mgr.currentCascadeList[ menuMgr->popup_mgr.numCascades - 1])) { /* Unpost() will set the traversal focus, if needed */ Unpost (menuMgr, menuMgr->popup_mgr.currentCascadeList[ menuMgr->popup_mgr.numCascades - 1]); } } /*************************************<->************************************* * * TraverseUp(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void TraverseUp (menuMgr, w) register XwPopupMgrWidget menuMgr; XwMenuButtonWidget w; { if ((menuMgr->manager.traversal_on) && (menuMgr->menu_mgr.menuActive) && (XtParent(w) == menuMgr->popup_mgr.currentCascadeList[ menuMgr->popup_mgr.numCascades - 1])) { XwProcessTraversal (w, XwTRAVERSE_UP, TRUE); } } /*************************************<->************************************* * * TraverseDown(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void TraverseDown (menuMgr, w) XwPopupMgrWidget menuMgr; XwMenuButtonWidget w; { if ((menuMgr->manager.traversal_on) && (menuMgr->menu_mgr.menuActive) && (XtParent(w) == menuMgr->popup_mgr.currentCascadeList[ menuMgr->popup_mgr.numCascades - 1])) { XwProcessTraversal (w, XwTRAVERSE_DOWN, TRUE); } } /*************************************<->************************************* * * TraverseNextTop(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void TraverseNextTop (menuMgr, w) XwPopupMgrWidget menuMgr; XwMenuButtonWidget w; { } /*************************************<->************************************* * * TraverseNext(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void TraverseNext (menuMgr, w) XwPopupMgrWidget menuMgr; XwMenuButtonWidget w; { } /*************************************<->************************************* * * TraversePrev(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void TraversePrev (menuMgr, w) XwPopupMgrWidget menuMgr; XwMenuButtonWidget w; { } /*************************************<->************************************* * * TraverseHome(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void TraverseHome (menuMgr, w) XwPopupMgrWidget menuMgr; XwMenuButtonWidget w; { } /*************************************<->************************************* * * ManualPost(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void ManualPost (menuMgr, pane, relativeTo, x, y) register XwPopupMgrWidget menuMgr; register Widget pane; Widget relativeTo; Position x; Position y; { int introotx, introoty, intchildx, intchildy; Position rootx, rooty; Position childx, childy; Widget assocWidget; Window root, child; unsigned int returnMask; if (pane == (Widget)NULL) pane = (Widget) menuMgr->popup_mgr.topLevelPane; /* Get the widget Id for the associated widget */ assocWidget = XtParent (XtParent (menuMgr)); /* * Do nothing if the menu is already active, there is no top level * menupane, the menu manager is insensitive, or the associated * widget is insensitive. */ if ((menuMgr->menu_mgr.menuActive == TRUE) || (pane == NULL) || !XtIsSensitive((Widget)menuMgr) || !XtIsSensitive(assocWidget)) { return; } /* * Determine where to post the menu and translate coordinates, * if necessary. */ if (relativeTo) { /* * If the coordinates are relative to a widget, then we need * to map them relative to the root window. */ /* XtTranslateCoords (relativeTo, x, y, &rootx, &rooty); */ XTranslateCoordinates (XtDisplay(relativeTo), XtWindow(relativeTo), RootWindowOfScreen(XtScreen(menuMgr)), x, y, &introotx, &introoty, &child); rootx = introotx; rooty = introoty; } else { /* * If no widget is specified, then the coordinates are assumed * to already be relative to the root window. */ rootx = x; rooty = y; } /* * The following is a kludge fix to bypass a shortcoming within Xtk. * We need to manually inform the previous focus widget that it has * lost the cursor; this is because once we add our popups to the * grablist, the LeaveNotify will be ignored for the focus widget, * because it is not on the grablist. */ /* if (menuMgr->manager.traversal_on) */ SendFakeLeaveNotify(menuMgr, ULTIMATE_PARENT); /* Mark the menu system as active */ menuMgr->menu_mgr.menuActive = TRUE; /* Position the menupane */ if (!XtIsRealized (XtParent(pane))) XtRealizeWidget (XtParent(pane)); ForceMenuPaneOnScreen (pane, &rootx, &rooty); XtMoveWidget (XtParent(pane), rootx, rooty); /* * Set up a pointer grab; this allows us to catch all selects and * to then unpost the menus, regardless as to where the select * occurred. */ XGrabPointer (XtDisplay(menuMgr), XtWindow(assocWidget), TRUE, ButtonPressMask|ButtonReleaseMask|EnterWindowMask|LeaveWindowMask| PointerMotionMask, GrabModeAsync, GrabModeAsync, None, (Cursor)NULL, CurrentTime); /* Post the menupane */ Post (menuMgr, pane, XtGrabExclusive); /* * If traversal is on, then save the current mouse position, * so that we can restore the mouse to its original position * when the menu is unposted. */ if (menuMgr->manager.traversal_on) { if (XQueryPointer(XtDisplay(menuMgr), RootWindowOfScreen(menuMgr->core.screen), &root, &child, &introotx, &introoty, &intchildx, &intchildy, &returnMask)) { menuMgr->popup_mgr.origMouseX = introotx; menuMgr->popup_mgr.origMouseY = introoty; } /* Set the traversal focus to the pane */ XwMoveFocus (pane); } } /*************************************<->************************************* * * XwPostPopup(parameters) * * Description: * ----------- * This is a utility routine which may be used by an application to * manually post a popup menu pane. * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ void XwPostPopup (menuMgr, pane, relativeTo, x, y) Widget menuMgr, pane, relativeTo; Position x, y; { /* Only popup a menu if it is a subclass of the popup menu manager */ /* Do nothing if invalid menu manager Id */ if (menuMgr == NULL) return; if (XtIsSubclass (menuMgr, XwpopupmgrWidgetClass)) { (*(((XwPopupMgrWidgetClass) XtClass(menuMgr))-> popup_mgr_class.manualPost)) (menuMgr, pane, relativeTo, x, y); } else { XtWarning ("PopupMgr: Widget is not a subclass of menuMgr"); } } /*************************************<->************************************* * * SendFakeLeaveNotify(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void SendFakeLeaveNotify (focus_widget, mode) Widget focus_widget; int mode; { XEvent event; if (mode == ULTIMATE_PARENT) { /* Search up to top level shell */ while (XtParent(focus_widget) != NULL) focus_widget = XtParent(focus_widget); } else { /* Search up to the shell in which this menubtn resides */ while ((!XtIsSubclass (focus_widget, shellWidgetClass)) && (XtParent(focus_widget) != NULL)) focus_widget = XtParent(focus_widget); } event.type = FocusOut; event.xfocus.serial = LastKnownRequestProcessed(XtDisplay(focus_widget)); event.xfocus.send_event = True; event.xfocus.display = XtDisplay(focus_widget); event.xfocus.window = XtWindow(focus_widget); event.xfocus.mode = NotifyNormal; event.xfocus.detail = NotifyAncestor; XtDispatchEvent (&event); } /*************************************<->************************************* * * SetUpStickyList(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void SetUpStickyList (menuMgr, widget) XwPopupMgrWidget menuMgr; Widget widget; { int i; menuMgr->popup_mgr.numSavedCascades = 0; if ((XtIsSubclass (widget, XwmenubuttonWidgetClass)) && (menuMgr->popup_mgr.stickyMode) && (XtParent(widget) == menuMgr->popup_mgr.currentCascadeList [menuMgr->popup_mgr.numCascades -1])) { if (menuMgr->popup_mgr.numCascades > menuMgr->popup_mgr.sizeSavedCascadeList) { menuMgr->popup_mgr.sizeSavedCascadeList = menuMgr->popup_mgr.sizeCascadeList; menuMgr->popup_mgr.savedCascadeList = (Widget *) XtRealloc((char *)(menuMgr->popup_mgr.savedCascadeList), menuMgr->popup_mgr.sizeSavedCascadeList * sizeof (Widget)); } menuMgr->popup_mgr.numSavedCascades = menuMgr->popup_mgr.numCascades; menuMgr->popup_mgr.lastSelected = widget; /* Set this widget as the traversal item */ if (menuMgr->manager.traversal_on) { XwMenuPaneWidget pane; pane = ((XwMenuPaneWidget)(menuMgr->popup_mgr.currentCascadeList [menuMgr->popup_mgr.numCascades -1])); pane->manager.active_child = widget; XtSetKeyboardFocus ((Widget)pane, widget); } for (i=0; i < menuMgr->popup_mgr.numSavedCascades; i++) menuMgr->popup_mgr.savedCascadeList[i] = menuMgr->popup_mgr.currentCascadeList[i]; } else if (menuMgr->manager.traversal_on) { /* Force the first item to be highlighted next time */ ((XwPopupMgrWidget)menuMgr->popup_mgr.topLevelPane)-> manager.active_child = NULL; XtSetKeyboardFocus (menuMgr->popup_mgr.topLevelPane, NULL); } } /*************************************<->************************************* * * BtnSensitivityChanged(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void BtnSensitivityChanged (menuMgr, btn) XwPopupMgrWidget menuMgr; Widget btn; { /* Noop */ } /*************************************<->************************************* * * PaneSensitivityChanged(parameters) * * Description: * ----------- * xxxxxxxxxxxxxxxxxxxxxxx * * * Inputs: * ------ * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Outputs: * ------- * xxxxxxxxxxxx = xxxxxxxxxxxxx * * Procedures Called * ----------------- * *************************************<->***********************************/ static void PaneSensitivityChanged (menuMgr, pane) XwPopupMgrWidget menuMgr; Widget pane; { /* Noop */ }