2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these libraries and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
24 * (c) Copyright 1989, 1990, 1991, 1992, 1993 OPEN SOFTWARE FOUNDATION, INC.
31 * (c) Copyright 1987, 1988, 1989, 1990 HEWLETT-PACKARD COMPANY */
33 * (c) Copyright 1987, 1988 DIGITAL EQUIPMENT CORPORATION */
35 * (c) Copyright 1988 MASSACHUSETTS INSTITUTE OF TECHNOLOGY */
43 #include "WmResource.h"
44 #include "WmResParse.h"
47 #include <X11/Shell.h>
51 #include <Xm/CascadeB.h>
52 #include <Xm/CascadeBG.h>
54 #include <Xm/LabelG.h>
55 #include <Xm/MenuShell.h>
57 #include <Xm/PushBG.h>
58 #include <Xm/RowColumn.h>
59 #include <Xm/RowColumnP.h>
60 #include <Xm/Separator.h>
61 #include <Xm/SeparatoG.h>
63 #define SHELL_NAME "menu"
64 #define SEPARATOR_NAME "separator"
65 #define TITLE_NAME "title_name"
66 #define CASCADE_BTN_NAME "cascadebutton"
67 #define PUSH_BTN_NAME "pushbutton"
69 #define CHILDREN_CACHE 22
70 #define MENU_BUTTON_INC 5
73 * include extern functions
77 #include "WmColormap.h"
79 #include "WmFunction.h"
80 #include "WmIconBox.h"
83 #include "WmWrkspace.h"
86 static void UnmapCallback (Widget w, XtPointer client_data,
88 static MenuItem *DuplicateMenuItems (MenuItem *menuItems);
91 /*************************************<->*************************************
93 * MakeMenu (menuName, initialContext, accelContext, moreMenuItems,
99 * This function makes a menu widget.
104 * menuName = name of the top-level menu pane for the menu
105 * initialContext = initial context for menuitem sensitivity
106 * accelContext = accelerator context
107 * moreMenuItems = additional menuitems for custom menu.
108 * fSystemMenu = TRUE iff the menu is a client system menu.
113 * Return = pointer to a MenuSpec structure with updated currentContext,
114 * menuWidget, and menuButtons members.
119 * If moreMenuItems is nonNULL, a custom MenuSpec will be created, with
120 * menuItem member pointing to moreMenuItems. The menuItems for the
121 * standard MenuSpec of the same name and the moreMenuItems list will be
122 * used to create menubuttons, and the menu widget will be separate from
123 * any existing standard menu widget.
125 * When the client is destroyed, this custom MenuSpec, its menuItem and
126 * menuButton lists, and its menu widget should be freed.
128 *************************************<->***********************************/
129 MenuSpec *MakeMenu (WmScreenData *pSD,
130 String menuName, Context initialContext,
131 Context accelContext, MenuItem *moreMenuItems,
136 MenuSpec *newMenuSpec;
138 KeySpec *accelKeySpec;
140 if ((menuName == NULL) || (pSD == NULL))
146 * Look for the menu specification:
149 menuSpec = pSD->menuSpecs;
152 if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
153 /* Found the menu pane. */
157 menuSpec = menuSpec->nextMenuSpec;
160 if (menuSpec == NULL)
161 /* the menuSpecs list is exhausted */
163 MWarning(((char *)GETMESSAGE(48, 1, "Menu specification %s not found\n")), menuName);
168 * The top-level menu pane specification was found.
169 * Adjust the menu accelerator context?
176 else if (accelContext & F_CONTEXT_ROOT)
177 /* root context accelerators apply everywhere */
179 accelContext = F_CONTEXT_ALL;
183 * If making a custom menu, create a custom copy of the specification with
184 * which to build the custom menu.
185 * Otherwise, if the menu widget exists, possibly modify the accelerator
186 * contexts and return the specification.
189 if (moreMenuItems != NULL)
191 if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
192 /* Handle insufficent memory */
194 MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
197 newMenuSpec->name = NULL; /* distinguishes this as custom */
198 newMenuSpec->whichButton = SELECT_BUTTON;
199 newMenuSpec->height = 0;
200 newMenuSpec->menuItems = menuSpec->menuItems; /* temporary */
201 newMenuSpec->accelContext = menuSpec->accelContext;
202 newMenuSpec->accelKeySpecs = NULL;
203 newMenuSpec->nextMenuSpec = NULL;
205 menuSpec = newMenuSpec;
207 else if (menuSpec->menuWidget)
210 * OR the accelContext into the accelerators, if necessary.
212 if (accelContext != (menuSpec->accelContext & accelContext))
214 menuSpec->accelContext |= accelContext;
215 accelKeySpec = menuSpec->accelKeySpecs;
218 accelKeySpec->context |= accelContext;
219 accelKeySpec = accelKeySpec->nextKeySpec;
226 * We have a menu specification with which to build the menu.
227 * Set the initial and accelerator contexts -- they are needed within
231 menuSpec->currentContext = initialContext;
232 menuSpec->accelContext = accelContext;
235 * Scan the toplevel MenuSpec and create its initial menuButtons array
236 * if any of its items will need to be included. This array will be
237 * created or enlarged within CreateMenuWidget below if necessary.
241 menuItem = menuSpec->menuItems;
244 if ((menuItem->greyedContext) || (menuItem->mgtMask))
248 menuItem = menuItem->nextMenuItem;
250 menuItem = moreMenuItems;
253 if ((menuItem->greyedContext) || (menuItem->mgtMask))
257 menuItem = menuItem->nextMenuItem;
261 if ((menuSpec->menuButtons =
262 (MenuButton *) XtMalloc (n * sizeof(MenuButton))) == NULL)
263 /* insufficent memory */
265 MWarning(((char *)GETMESSAGE(48, 3, "Insufficient memory for menu %s\n")), menuName);
268 menuSpec->menuButtonSize = n;
272 menuSpec->menuButtons = NULL;
273 menuSpec->menuButtonSize = 0;
275 menuSpec->menuButtonCount = 0;
278 * Create a PopupShell widget as a child of the workspace manager widget
279 * and a PopupMenu as a child of the shell.
280 * Fill the PopupMenu with the menu items.
283 menuSpec->menuWidget = CREATE_MENU_WIDGET (pSD, pCD, menuName,
284 pSD->screenTopLevelW,
285 TRUE, menuSpec, moreMenuItems);
286 if (menuSpec->menuWidget == NULL)
289 * Could not make the top-level menu pane.
294 _XmSetPopupMenuClick(menuSpec->menuWidget, False);
296 /* Return the top MenuSpec */
300 } /* END OF FUNCTION MakeMenu */
304 /*************************************<->***********************************/
305 void CheckTerminalSeparator(MenuSpec *menuSpec, Widget buttonWidget, Boolean manage)
312 if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
317 cw = (CompositeWidget)menuSpec->menuWidget;
318 children = cw->composite.children;
320 for (wPos = 0; wPos < cw->composite.num_children; wPos++)
322 if((Widget)children[wPos] == buttonWidget)
330 XtClass((Widget) children[wPos -1]) == xmSeparatorGadgetClass)
334 if (!(XtIsManaged((Widget)children[wPos -1])))
336 XtManageChild((Widget)children[wPos -1]);
341 if (XtIsManaged((Widget)children[wPos -1]))
343 XtUnmanageChild((Widget)children[wPos -1]);
348 } /* END OF FUNCTION CheckTerminalSeparator */
351 /*************************************<->*************************************
353 * DuplicateMenuItems (menuItems)
359 * This function creates an indentical duplicate of the given menuItems
365 * menuItems = the linked list of menuItems to duplicate
370 * Return = pointer to a new MenuItems list, identical to the original
376 *************************************<->***********************************/
378 DuplicateMenuItems (MenuItem *menuItems)
380 MenuItem *newMenuItem = (MenuItem *) NULL, *returnMenuItem, *curMenuItem;
382 for (curMenuItem = menuItems;
383 curMenuItem != (MenuItem *) NULL;
384 curMenuItem = curMenuItem->nextMenuItem)
386 /* If its the first one ... */
387 if (newMenuItem == (MenuItem *) NULL)
389 newMenuItem = (MenuItem *)XtMalloc(sizeof(MenuItem));
390 returnMenuItem = newMenuItem;
392 else /* ... otherwise, get the next menuItem. */
394 newMenuItem->nextMenuItem =
395 (MenuItem *)XtMalloc(sizeof(MenuItem));
396 newMenuItem = newMenuItem->nextMenuItem;
399 newMenuItem->labelType = curMenuItem->labelType;
400 if (curMenuItem->label != (String) NULL)
401 newMenuItem->label = XtNewString(curMenuItem->label);
403 newMenuItem->label = NULL;
404 newMenuItem->labelBitmapIndex = curMenuItem->labelBitmapIndex;
405 newMenuItem->mnemonic = curMenuItem->mnemonic;
406 newMenuItem->accelState = curMenuItem->accelState;
407 newMenuItem->accelKeyCode = curMenuItem->accelKeyCode;
408 if (curMenuItem->accelText != (String) NULL)
409 newMenuItem->accelText = XtNewString(curMenuItem->accelText);
411 newMenuItem->accelText = NULL;
412 newMenuItem->wmFunction = curMenuItem->wmFunction;
414 if ((curMenuItem->wmFunction == F_Send_Msg)
415 || (curMenuItem->wmFunction == F_Set_Context)
417 * NOTE: For now, in dtwm this function is used only
418 * to copy the FrontPanel menu. So, we know that
419 * curMenuItem->wmFuncArgs isn't going anywhere,
420 * so it's safe to simply point at it. If at some
421 * point it becomes possible that curMenuItem->wmFuncArgs
422 * can go away, we'll need to make a (deep) copy of
423 * the WmActionArg. 11/20/96
425 || (curMenuItem->wmFunction == F_Action)
427 newMenuItem->wmFuncArgs = curMenuItem->wmFuncArgs;
428 else if (curMenuItem->wmFuncArgs != (String) NULL)
429 newMenuItem->wmFuncArgs = XtNewString(curMenuItem->wmFuncArgs);
431 newMenuItem->wmFuncArgs = NULL;
433 newMenuItem->greyedContext = curMenuItem->greyedContext;
434 newMenuItem->mgtMask = curMenuItem->mgtMask;
435 newMenuItem->nextMenuItem = (MenuItem *) NULL;
438 return(returnMenuItem);
441 /*************************************<->*************************************
443 * DuplicateMenuSpec (menuSpec)
449 * This function creates an indentical duplicate of the given menuSpec.
450 * The menuItems list in the menuSpec is also duplicated.
455 * menuSpec = the menuSpec to duplicate
460 * Return = pointer to a new MenuSpec structure with the same field
461 * values as the original
466 * A new MenuSpec structure is allocated. Most of he fields of the new
467 * structure are set to the same values as the passed in menuSpec.
468 * There are some differences between the two final structures.
469 * One difference: any fields related to push buttons and other
470 * widgets are left blank in the new MenuSpec to be filled in later.
472 *************************************<->***********************************/
474 DuplicateMenuSpec (MenuSpec *menuSpec)
476 MenuSpec *newMenuSpec;
478 if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
479 /* Handle insufficent memory */
481 Warning((char *)GETMESSAGE(48, 9,
482 "Insufficient memory for menu specification\n"));
485 newMenuSpec->name = XtNewString(menuSpec->name);
486 newMenuSpec->currentContext = menuSpec->currentContext;
487 newMenuSpec->menuWidget = (Widget) NULL;
488 newMenuSpec->whichButton = menuSpec->whichButton;
489 newMenuSpec->height = menuSpec->height;
490 newMenuSpec->menuItems = DuplicateMenuItems(menuSpec->menuItems);
491 newMenuSpec->menuButtons = (MenuButton *) NULL;
492 newMenuSpec->menuButtonSize = 0;
493 newMenuSpec->menuButtonCount = 0;
494 newMenuSpec->accelContext = menuSpec->accelContext;
495 newMenuSpec->accelKeySpecs = (KeySpec *) NULL;
496 newMenuSpec->nextMenuSpec = (MenuSpec *) NULL;
501 /*************************************<->*************************************
504 * AdjustPBs (menuSpec, pCD, newContext)
509 * This procedure adjusts menu PushButton sensitivities and manage/unmanaged
510 * status for a toplevel menu.
515 * menuSpec = nonNULL toplevel menu specification with gadget
517 * newContext = context that the menu is to be posted under.
522 * menuSpec = menu specification with modifications
523 * Return = TRUE iff at least one menu item changed manage status.
528 * Adjusts PushButton sensitivity according to context and function type.
529 * Manages/Unmanages PushButtons according to clientFunction resource.
531 *************************************<->***********************************/
532 static Boolean AdjustPBs (MenuSpec *menuSpec, ClientData *pCD,
535 MenuButton *menuButton;
541 Boolean fChangeManaged = FALSE;
544 * Set PushButton sensitivity.
545 * Set f.send_msg button sensitivity according to context and client
546 * message list. Adjust other button sensitivities only for context.
549 /* check for bad input value - shouldn't happen. */
550 if (menuSpec == NULL) return (FALSE);
552 for (n = 0, menuButton = menuSpec->menuButtons;
553 n < menuSpec->menuButtonCount;
556 menuItem = menuButton->menuItem;
557 if (menuItem->wmFunction == F_Send_Msg)
558 /* f.send_msg button: set according to context and message. */
560 if ((newContext & menuItem->greyedContext) ||
561 !(pCD && pCD->mwmMessagesCount && pCD->mwmMessages))
562 /* insensitive context or empty client message list */
564 XtSetSensitive (menuButton->buttonWidget, FALSE);
568 * Have a context sensitive f.send_msg item and a client with a
569 * nonempty message list. Set sensitive only if the message is
570 * supported by this client. Otherwise set insensitive.
573 msgc = pCD->mwmMessagesCount;
574 pMsg = pCD->mwmMessages;
577 /* scan nonempty message list */
579 if (*pMsg == (long) menuItem->wmFuncArgs)
585 pMsg++; /* next message in list */
587 XtSetSensitive (menuButton->buttonWidget, fSupported);
592 * Non f.send_msg button:
593 * Adjust sensitivity according to context.
594 * Manage/Unmanage according to clientFunction.
597 if (menuSpec->currentContext & menuItem->greyedContext)
598 /* button is currently insensitive */
600 if (!(newContext & menuItem->greyedContext))
601 /* insensitive -> sensitive */
603 XtSetSensitive (menuButton->buttonWidget, TRUE);
607 /* button is currently sensitive */
609 if (newContext & menuItem->greyedContext)
610 /* sensitive -> insensitive */
612 XtSetSensitive (menuButton->buttonWidget, FALSE);
616 if (menuItem->wmFunction == F_Remove)
619 * Only allow remove from workspace if the client
620 * is in more than one workspace
622 fSupported = (pCD && (pCD->numInhabited > 1));
623 XtSetSensitive (menuButton->buttonWidget, fSupported);
626 if ((menuItem->mgtMask) && pCD)
627 /* PushButton might not apply */
629 if ((pCD->clientFunctions & menuItem->mgtMask & MWM_MGT_MASK) ||
630 (pCD->dtwmFunctions & menuItem->mgtMask & DTWM_MGT_MASK))
631 /* function applies -- manage it */
633 if (!menuButton->managed)
634 /* unmanaged -> managed */
636 XtManageChild (menuButton->buttonWidget);
637 menuButton->managed = TRUE;
638 fChangeManaged = TRUE;
639 if (n == menuSpec->menuButtonCount - 1)
642 * last item, if it has a separator before
643 * it, manage the separator
646 CheckTerminalSeparator(menuSpec,
647 menuButton->buttonWidget,
653 /* function does not apply -- unmanage it */
655 if (menuButton->managed)
656 /* managed -> unmanaged */
658 XtUnmanageChild (menuButton->buttonWidget);
659 menuButton->managed = FALSE;
660 fChangeManaged = TRUE;
662 if (n == menuSpec->menuButtonCount - 1)
665 * last item, if it has a separator before
666 * it, unmanage the separator
668 CheckTerminalSeparator(menuSpec,
669 menuButton->buttonWidget,
676 else if (!menuButton->managed)
677 /* unmanaged PushButton applies */
679 XtManageChild (menuButton->buttonWidget);
680 menuButton->managed = TRUE;
681 fChangeManaged = TRUE;
686 return (fChangeManaged);
688 } /* END OF FUNCTION AdjustPBs */
692 /*************************************<->*************************************
695 * SavePBInfo (topMenuSpec, menuItem, itemW)
700 * Fills a MenuButton structure for a PushButton.
701 * If necessary, mallocs or reallocs the menuButtons array in the toplevel
707 * topMenuSpec = pointer to toplevel MenuSpec structure
708 * menuItem = pointer to PushButton MenuItem structure
709 * itemW = PushButton gadget
710 * topMenuSpec->menuButtons[]
711 * topMenuSpec->menuButtonSize
712 * topMenuSpec->menuButtonCount
717 * Return = FALSE iff insufficient memory for malloc or realloc
718 * or bad input value forces exit.
719 * topMenuSpec->menuButtons[]
720 * topMenuSpec->menuButtonSize
721 * topMenuSpec->menuButtonCount
726 * The initial managed status of PushButtons is TRUE.
728 *************************************<->***********************************/
729 static Boolean SavePBInfo (MenuSpec *topMenuSpec, MenuItem *menuItem,
732 MenuButton *menuButton;
735 /* check for bad input value - shouldn't happen. */
736 if (topMenuSpec == NULL) return (FALSE);
738 if (topMenuSpec->menuButtonSize == 0)
739 /* need to create array */
741 topMenuSpec->menuButtonSize = MENU_BUTTON_INC;
742 topMenuSpec->menuButtons =
743 (MenuButton *) XtMalloc (MENU_BUTTON_INC * sizeof(MenuButton));
745 else if (topMenuSpec->menuButtonCount == topMenuSpec->menuButtonSize)
746 /* need larger array */
748 topMenuSpec->menuButtonSize += MENU_BUTTON_INC;
749 topMenuSpec->menuButtons = (MenuButton *)
750 XtRealloc ((char*)topMenuSpec->menuButtons,
751 topMenuSpec->menuButtonSize * sizeof(MenuButton));
754 if (topMenuSpec->menuButtons == NULL)
755 /* insufficent memory */
757 topMenuSpec->menuButtonSize = 0;
758 topMenuSpec->menuButtonCount = 0;
762 menuButton = &(topMenuSpec->menuButtons[topMenuSpec->menuButtonCount]);
763 topMenuSpec->menuButtonCount++;
765 menuButton->menuItem = menuItem;
766 menuButton->buttonWidget = itemW;
767 menuButton->managed = TRUE;
772 /*************************************<->*************************************
774 * CreateMenuWidget (pSD, menuName, parent, fTopLevelPane, topMenuSpec,
780 * Creates a MenuShell as a child of the specified parent widget, and a
781 * PopupMenu or PulldownMenu as a child of the shell. Fill the menu with
782 * the named menupane items.
787 * pSD ---------- pointer to screen data
788 * menuName ----- the name of the menu specification to be used to create
790 * parent -------- parent of popup shell
791 * fTopLevelPane - TRUE iff the menupane is a top level one
792 * topMenuSpec --- pointer to the top menu specification.
793 * moreMenuItems - pointer to additional menu items for custom menu.
798 * Return = created PopupMenu or PulldownMenu widget, or NULL.
803 * We attach a popdowncallback to the menu to set wmGD.menuActive to NULL,
804 * allowing us to not dispatch key events separately from the toolkit
807 *************************************<->***********************************/
809 typedef struct _StrList
812 struct _StrList *next;
815 Widget CreateMenuWidget (WmScreenData *pSD,
816 String menuName, Widget parent,
817 Boolean fTopLevelPane, MenuSpec *topMenuSpec,
818 MenuItem *moreMenuItems)
823 MenuSpec *menuSpec = (MenuSpec *)NULL;
828 Widget children[CHILDREN_CACHE];
830 KeySpec *accelKeySpec;
831 Dimension menuHeight;
832 Boolean fUseTitleSep = False;
833 StrList *stringsToFree = NULL, *sPtr;
840 /* check for bad input values. */
841 if ((menuName == NULL) || (pSD == NULL))
847 * Find the menu pane specifications for menuName.
848 * The top-level menu specification is passed as an argument (it may
849 * be custom). A submenu specification must be found and might not exist.
850 * Return NULL if a submenu specification is not found.
854 menuSpec = topMenuSpec;
858 menuSpec = pSD->menuSpecs;
861 if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
863 break; /* found menuName's specification */
865 menuSpec = menuSpec->nextMenuSpec; /* keep looking */
869 if (menuSpec == NULL)
870 /* (submenu) specification not found */
872 MWarning(((char *)GETMESSAGE(48, 4, "Menu specification %s not found\n")), menuName);
877 * If menuSpec is marked, we have menu recursion => fail.
878 * Otherwise, mark it.
881 if (menuSpec->currentContext & CR_MENU_MARK) /* marked? */
884 MWarning(((char *)GETMESSAGE(48, 5, "Menu recursion detected for %s\n")), menuName);
887 menuSpec->currentContext |= CR_MENU_MARK; /* no, mark it */
890 * Create a PopupShell widget.
891 * If the parent of the specified parent ("grandparent") is a MenuShell
892 * widget, then use the grandparent as the parent of the PopupShell.
893 * Otherwise, use the specified parent.
896 XtSetArg (args[i], XmNwidth, (XtArgVal) 5); i++;
897 XtSetArg (args[i], XmNheight, (XtArgVal) 5); i++;
898 XtSetArg (args[i], XmNallowShellResize, (XtArgVal) TRUE); i++;
899 XtSetArg (args[i], XtNoverrideRedirect, (XtArgVal) TRUE); i++;
900 XtSetArg (args[i], XtNdepth,
901 (XtArgVal) DefaultDepth(XtDisplay(parent), pSD->screen)); i++;
902 XtSetArg (args[i], XtNscreen,
903 (XtArgVal) ScreenOfDisplay(XtDisplay(parent), pSD->screen)); i++;
905 if ((XtParent (parent) != NULL) && XmIsMenuShell (XtParent (parent)))
907 parent = XtParent (parent);
910 menuShellW = XtCreatePopupShell (SHELL_NAME, xmMenuShellWidgetClass,
911 parent, (ArgList) args, i);
914 * Create a RowColumn widget as a child of the shell for the menu pane.
915 * If the menu pane is top-level, create a popup menu for it and attach
916 * the unmap callback to it.
917 * Otherwise, create a pulldown menu for it.
921 XtSetArg (args[i], XmNborderWidth, (XtArgVal) 0); i++;
922 XtSetArg (args[i], XmNwhichButton, (XtArgVal) SELECT_BUTTON); i++;
923 XtSetArg (args[i], XmNadjustMargin, (XtArgVal) TRUE); i++;
927 XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_POPUP); i++;
928 XtSetArg (args[i], XmNpopupEnabled, (XtArgVal) TRUE); i++;
929 menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
931 XtAddCallback (menuW, XmNunmapCallback, UnmapCallback,
932 (XtPointer) menuSpec);
936 XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_PULLDOWN); i++;
937 menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
942 * Create the specified menu entries as children of the menupane.
943 * Menus may contain the following widgets:
950 * Add separator gadgets around menu titles.
953 XtSetArg (sepArgs[0], XmNseparatorType, (XtArgVal) XmDOUBLE_LINE);
956 menuItem = menuSpec->menuItems;
957 if ((menuItem == NULL) && (moreMenuItems != NULL))
958 /* handle custom menu with empty standard specification */
960 menuSpec->menuItems = menuItem = moreMenuItems;
961 moreMenuItems = NULL;
967 if (menuItem->wmFunction == F_Separator)
969 * Add a Separator gadget for a menu separator.
970 * An immediately following title will not have a top separator.
975 XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
977 fUseTitleSep = FALSE;
983 * We will use one of:
991 * Construct the label
993 if ((menuItem->labelType == XmPIXMAP) &&
995 MakeCachedLabelPixmap (pSD, menuW,
996 menuItem->labelBitmapIndex)))
998 XtSetArg (args[i], XmNlabelType, (XtArgVal) XmPIXMAP); i++;
999 XtSetArg (args[i], XmNlabelPixmap, (XtArgVal) labelPixmap); i++;
1000 XtSetArg (args[i], XmNlabelInsensitivePixmap,
1001 (XtArgVal) labelPixmap); i++;
1005 XtSetArg (args[i], XmNlabelType, (XtArgVal) XmSTRING); i++;
1006 XtSetArg (args[i], XmNlabelString, (XtArgVal)
1007 (tmpstr = XmStringCreateLocalized(menuItem->label))); i++;
1008 sPtr = (StrList *) XtMalloc(sizeof(StrList));
1011 MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
1016 sPtr->string = tmpstr;
1017 sPtr->next = stringsToFree;
1018 stringsToFree = sPtr;
1022 if (menuItem->wmFunction == F_Title)
1024 * Add a centered Label gadget for a menu title.
1025 * Include separators above and below the title.
1026 * Don't include the top one if the title is the first pane item
1027 * or immediately follows a user-supplied separator.
1033 XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
1037 XtSetArg (args[i], XmNalignment, XmALIGNMENT_CENTER); i++;
1038 children[n] = XmCreateLabelGadget (menuW, TITLE_NAME,
1039 (ArgList) args, i); n++;
1040 children[n] = XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
1044 * A following title will have both separators.
1047 fUseTitleSep = TRUE;
1052 * We will use one of:
1057 * Both support mnemonics; only PushButtons support accelerators.
1061 * Align text on the left.
1062 * Set any mnemonic text.
1064 XtSetArg (args[i], XmNalignment, XmALIGNMENT_BEGINNING); i++;
1066 if (menuItem->mnemonic)
1068 XtSetArg (args[i], XmNmnemonic,
1069 (XtArgVal) menuItem->mnemonic); i++;
1072 if (menuItem->wmFunction == F_Menu)
1074 * Create a PopupShell and PulldownMenu for a submenu (the
1075 * menushells are linked together).
1076 * Create a CascadeButton Widget
1077 * The submenu widget is attached to the CascadeButton gadget
1078 * using the subMenuId resource.
1079 * Make the CascadeButton insensitive if the submenu cannot be
1083 subMenuW = CREATE_MENU_WIDGET (pSD, pCD,
1084 menuItem->wmFuncArgs, menuW,
1089 * Attach submenu to cascade button.
1092 XtSetArg (args[i], XmNsubMenuId, (XtArgVal) subMenuW);
1094 children[n] = XmCreateCascadeButtonGadget (menuW,
1095 CASCADE_BTN_NAME, (ArgList) args, i);
1099 * Unable to create submenupane: make the entry insensitive.
1102 children[n] = XmCreateCascadeButtonGadget (menuW,
1103 CASCADE_BTN_NAME, (ArgList) args, i);
1104 XtSetSensitive (children[n], FALSE);
1108 * A following title will have both separators.
1111 fUseTitleSep = TRUE;
1116 * Create a PushButton gadget.
1120 * If an accelerator is specified, set acceleratorText,
1121 * then create an accelerator KeySpec and insert it at the
1122 * head of the toplevel MenuSpec's list.
1124 if (menuItem->accelText)
1126 XtSetArg (args[i], XmNacceleratorText, (XtArgVal)
1127 (tmpstr = XmStringCreateLocalized(menuItem->accelText))); i++;
1128 sPtr = (StrList *) XtMalloc(sizeof(StrList));
1131 MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
1136 sPtr->string = tmpstr;
1137 sPtr->next = stringsToFree;
1138 stringsToFree = sPtr;
1141 if ((accelKeySpec = (KeySpec *)
1142 XtMalloc (sizeof (KeySpec ))) == NULL)
1143 /* Handle insufficent memory */
1145 MWarning (((char *)GETMESSAGE(48, 6, "Insufficient memory for menu %s\n")),
1147 menuSpec->currentContext &= ~CR_MENU_MARK;
1151 accelKeySpec->state = menuItem->accelState;
1152 accelKeySpec->keycode = menuItem->accelKeyCode;
1153 accelKeySpec->context = topMenuSpec->accelContext;
1154 accelKeySpec->subContext = 0;
1155 accelKeySpec->wmFunction = menuItem->wmFunction;
1156 accelKeySpec->wmFuncArgs = menuItem->wmFuncArgs;
1157 accelKeySpec->nextKeySpec = topMenuSpec->accelKeySpecs;
1158 topMenuSpec->accelKeySpecs = accelKeySpec;
1161 children[n] = XmCreatePushButtonGadget (menuW,
1162 PUSH_BTN_NAME, (ArgList) args, i);
1165 * Set sensitivity. Initially we only consider the context
1166 * of the top level menupane.
1169 if (menuItem->greyedContext & topMenuSpec->currentContext)
1170 /* insensitive button in this context*/
1172 XtSetSensitive (children[n], FALSE);
1175 /* sensitive button in this context*/
1177 XtSetSensitive (children[n], TRUE);
1181 * If necessary, fill a menuButtons element for this
1182 * PushButton. Malloc or Realloc the array if necessary.
1184 if ((menuItem->greyedContext) || (menuItem->mgtMask))
1186 if (!SavePBInfo (topMenuSpec, menuItem, children[n]))
1188 MWarning(((char *)GETMESSAGE(48, 7, "Insufficient memory for menu %s\n")),
1190 menuSpec->currentContext &= ~CR_MENU_MARK;
1196 * Set up the function callback.
1197 * A following title will have both separators.
1200 XtAddCallback (children[n], XmNactivateCallback,
1201 (XtCallbackProc)ActivateCallback,
1202 (XtPointer) menuItem);
1204 fUseTitleSep = TRUE;
1210 * Increment the children array count if we actually
1211 * created a new child.
1216 * Next menu item: handle custom items and full children[].
1218 menuItem = menuItem->nextMenuItem;
1219 if ((menuItem == NULL) && (moreMenuItems != NULL))
1221 menuSpec->menuItems = menuItem = moreMenuItems;
1222 moreMenuItems = NULL;
1224 if (n >= CHILDREN_CACHE - 2) /* leave room for title separators */
1226 XtManageChildren (children, n);
1233 XtManageChildren (children, n);
1237 * Get the initial height of the top level menu pane shell.
1238 * The actual height will change according to clientFunctions.
1243 XtSetArg (args[i], XtNheight, &menuHeight); i++;
1244 XtGetValues (menuW, (ArgList)args, i);
1245 topMenuSpec->height = (unsigned int) menuHeight;
1250 * Check if the menu that's been created is higher than the screen.
1251 * If it is, force it to wrap. Taken straight from the 1.1 fix.
1255 XtSetArg (args[i], XtNheight, &menuHeight); i++;
1256 XtGetValues (menuW, (ArgList)args, i);
1257 scr = XtScreen (menuW);
1258 if (menuHeight > (Dimension)scr->height) {
1260 XtSetArg (args[i], XmNresizeHeight, (XtArgVal) FALSE); i++;
1261 XtSetArg (args[i], XmNpacking, (XtArgVal) XmPACK_TIGHT); i++;
1262 XtSetArg (args[i], XmNorientation, (XtArgVal) XmVERTICAL); i++;
1263 XtSetArg (args[i], XmNheight, scr->height); i++;
1264 XtSetValues (menuW, (ArgList)args, i);
1266 #endif /* IBM_151913 */
1268 /* free the string that may have been created earlier. */
1269 for (sPtr = stringsToFree; sPtr != NULL; )
1271 stringsToFree = stringsToFree->next;
1272 XmStringFree(sPtr->string);
1273 XtFree((char *)sPtr);
1274 sPtr = stringsToFree;
1278 /* Unmark the menu specification and return. */
1279 menuSpec->currentContext &= ~CR_MENU_MARK;
1282 } /* END OF FUNCTION CreateMenuWidget */
1286 /*************************************<->*************************************
1288 * PostMenu (menuSpec, pCD, x, y, button, newContext, flags, passedInEvent)
1293 * This function is used to post a menu at a particular location.
1298 * menuSpec = menu specification
1300 * x,y = position to post the menu if (flags & POST_AT_XY) set
1301 * button = button number posting the menu or NoButton (WmGlobal.h) if
1303 * newContext = context that the menu is to be posted under.
1304 * flags = POST_AT_XY bit set iff x,y are valid, else compute from pCD
1305 * POST_TRAVERSAL_ON bit set if set traversal on
1309 * menuSpec = menu specification with modifications
1310 * wmGD.menuClient = pCD
1311 * wmGD.menuActive = menuSpec
1316 * Accepts x,y only if POST_AT_XY flag bit set. Otherwise, computes from pCD.
1317 * Adjusts PushButton sensitivity according to context and function type.
1318 * Manages/Unmanages PushButtons according to clientFunction resource.
1319 * Sets traversal on if button==NoButton or POST_TRAVERSAL_ON flag bit set.
1321 *************************************<->***********************************/
1323 void PostMenu (MenuSpec *menuSpec, ClientData *pCD, int x, int y, unsigned int button, Context newContext, long flags, XEvent *passedInEvent)
1327 unsigned int whichButton;
1328 Dimension menuHeight;
1329 XButtonPressedEvent event;
1331 Display *saveDisplay;
1333 if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
1340 * Don't post a menu from an icon in the iconbox if the
1341 * icon is not visible
1343 if((newContext == F_SUBCONTEXT_IB_WICON ||
1344 newContext == F_SUBCONTEXT_IB_IICON) &&
1345 (!(IconVisible(pCD))))
1351 * Set grabContext to be used in GrabWin when no event is passed
1355 wmGD.grabContext = newContext;
1358 * Adjust PushButton sensitivity and manage/unmanage status.
1359 * If the manage status of the system menu has changed,
1360 * then get the height of the top level menu pane shell and
1361 * cache it in its MenuSpec.
1364 * Adjust the tear off control. If we are posting this menu from
1365 * a client then force the tear off to be disabled. NOTE: This must
1366 * be done after wmGD.menuClient has been set.
1367 * Since turning off the tear-off control could result in a height
1368 * change, we may need to remeasure things. (CR 9316)
1371 if(pCD && pCD->clientFlags & ICON_BOX)
1373 newContext |= F_CONTEXT_ICONBOX;
1377 if (AdjustPBs (menuSpec, pCD, newContext))
1380 XtSetArg (args[i], XtNheight, &menuHeight); i++;
1381 XtGetValues (menuSpec->menuWidget, (ArgList)args, i);
1382 menuSpec->height = (unsigned int) menuHeight;
1384 menuSpec->currentContext = newContext;
1387 * Adjust the whichButton resource if necessary.
1388 * Use SELECT_BUTTON for NoButton.
1391 whichButton = (button == NoButton) ? SELECT_BUTTON : button;
1392 if (whichButton != menuSpec->whichButton)
1395 XtSetArg (args[i], XmNwhichButton, (XtArgVal) whichButton); i++;
1396 XtSetValues (menuSpec->menuWidget, args, i);
1397 menuSpec->whichButton = whichButton;
1401 * Determine the position of the popup menu.
1402 * Compute position if necessary (system menu).
1405 if (pCD && !(flags & POST_AT_XY))
1406 /* compute the position */
1408 GetSystemMenuPosition (pCD, &x, &y, menuSpec->height, newContext);
1413 XmMenuPosition (menuSpec->menuWidget, &event);
1415 wmGD.menuClient = pCD;
1416 wmGD.menuActive = menuSpec; /* set to NULL within UnmapCallback() */
1419 * Post the menu by managing its top-level RowColumn.
1421 * First dispatch the event to set the time stamp in the toolkit
1426 saveWindow = passedInEvent->xany.window;
1427 saveDisplay = passedInEvent->xany.display;
1428 passedInEvent->xany.window = 0;
1429 passedInEvent->xany.display = XtDisplay(menuSpec->menuWidget);
1431 XtDispatchEvent(passedInEvent);
1432 passedInEvent->xany.window = saveWindow;
1433 passedInEvent->xany.display = saveDisplay;
1435 /* If menu posted by ButtonPress/ButtonRelease, release grabs. */
1436 if ((passedInEvent->type == ButtonPress) ||
1437 (passedInEvent->type == ButtonRelease))
1438 XUngrabPointer(passedInEvent->xany.display,
1439 passedInEvent->xbutton.time);
1442 #ifndef ALTERNATE_POSTMENU
1444 XtManageChild (menuSpec->menuWidget);
1447 if (flags & POST_STICKY)
1449 _XmSetPopupMenuClick(menuSpec->menuWidget, True);
1453 _XmSetPopupMenuClick(menuSpec->menuWidget, False);
1457 * Post the menu by calling the convenience routine that verifies
1458 * the button event, updates the Xt timestamp, and finally manages
1462 _XmPostPopupMenu( menuSpec->menuWidget, passedInEvent);
1467 * set the traversal state.
1470 if ((button == NoButton) || (flags & POST_TRAVERSAL_ON))
1471 /* turn traversal on */
1473 TraversalOn (menuSpec);
1476 /* turn traversal off */
1478 TraversalOff (menuSpec);
1481 } /* END OF FUNCTION PostMenu */
1485 /*************************************<->*************************************
1487 * UnpostMenu (menuSpec)
1492 * This function is used to unpost a menu.
1497 * menuSpec = menu specification
1506 * wmGD.menuActive and wmGD.menuUnpostKey are set to NULL within
1509 *************************************<->***********************************/
1511 void UnpostMenu (MenuSpec *menuSpec)
1513 if (menuSpec && (menuSpec->menuWidget))
1515 * Unpost the menu by unmanaging its top-level RowColumn.
1518 XtUnmanageChild (menuSpec->menuWidget);
1519 #ifndef OLD_COLORMAP
1520 ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
1524 } /* END OF FUNCTION UnpostMenu */
1528 /*************************************<->*************************************
1530 * ActivateCallback (w, client_data, call_data)
1535 * This function is called whenever a menu item is selected.
1540 * w = menubuttonWidget
1541 * client_data = pointer to menu button's MenuItem structure
1542 * call_data = not used
1543 * wmGD.menuClient = pointer to client's ClientData structure
1555 *************************************<->***********************************/
1557 void ActivateCallback (Widget w, caddr_t client_data, caddr_t call_data)
1561 /* set active screen */
1562 pSD = GetScreenForWindow (XtWindow(w));
1563 if (pSD) SetActiveScreen (pSD);
1565 ((MenuItem *)client_data)->wmFunction (
1566 ((MenuItem *)client_data)->wmFuncArgs, wmGD.menuClient, NULL);
1568 } /* END OF FUNCTION ActivateCallback */
1572 /*************************************<->*************************************
1574 * UnmapCallback (w, client_data, call_data)
1579 * This function is called whenever a toplevel RowColumn is unmapped.
1585 * client_data = not used
1586 * call_data = not used
1587 * wmGD.gadgetClient = last client with depressed client
1592 * wmGD.menuActive = NULL
1593 * wmGD.menuUnpostKeySpec = NULL
1594 * wmGD.checkHotspot = FALSE
1601 *************************************<->***********************************/
1603 static void UnmapCallback (Widget w, XtPointer client_data,
1604 XtPointer call_data)
1606 wmGD.menuActive = NULL;
1607 wmGD.menuUnpostKeySpec = NULL;
1608 wmGD.checkHotspot = FALSE;
1610 if (wmGD.gadgetClient)
1612 PopGadgetOut(wmGD.gadgetClient, FRAME_SYSTEM);
1615 #ifndef OLD_COLORMAP
1616 ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
1618 PullExposureEvents();
1620 } /* END OF FUNCTION UnmapCallback */
1623 /*************************************<->*************************************
1625 * MWarning (message)
1630 * This function lists a message to stderr.
1635 * format = pointer to a format string
1636 * message = pointer to a message string
1638 *************************************<->***********************************/
1640 void MWarning (char *format, char *message)
1643 if (strlen(format) + strlen(message) < (size_t) MAXWMPATH)
1645 char pch[MAXWMPATH+1];
1647 sprintf (pch, format, message);
1651 } /* END OF FUNCTION MWarning */
1655 /*************************************<->*************************************
1657 * TraversalOff (menuSpec)
1662 * This function turns menu traversal off.
1667 * menuSpec = menu specification
1679 *************************************<->***********************************/
1681 void TraversalOff (MenuSpec *menuSpec)
1683 if (menuSpec && (menuSpec->menuWidget))
1685 /* function pointer */
1686 (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
1687 ->row_column_class.menuProcedures)
1689 (XmMENU_TRAVERSAL, menuSpec->menuWidget, False, NULL, NULL);
1692 } /* END OF FUNCTION TraversalOff */
1696 /*************************************<->*************************************
1698 * TraversalOn (menuSpec)
1703 * This function turns menu traversal on.
1708 * menuSpec = menu specification
1720 *************************************<->***********************************/
1722 void TraversalOn (MenuSpec *menuSpec)
1725 if (menuSpec && (menuSpec->menuWidget))
1727 /* function pointer */
1728 (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
1729 ->row_column_class.menuProcedures)
1731 (XmMENU_TRAVERSAL, menuSpec->menuWidget, True, NULL, NULL);
1734 } /* END OF FUNCTION TraversalOn */
1738 /*************************************<->*************************************
1740 * FreeCustomMenuSpec (menuSpec)
1745 * This procedure destroys a custom MenuSpec structure and its associated
1746 * menu widget, menuItems list, menuButtons array, and menu accelerator list.
1751 * menuSpec = MenuSpec structure
1761 * Assumes that a MenuSpec is custom iff its name is NULL.
1763 * Assumes that ParseWmFuncStr() has parsed a menu item's function
1764 * argument only for F_Exec and F_Menu. If it is used for other functions,
1765 * be sure to include them here!
1767 *************************************<->***********************************/
1769 void FreeCustomMenuSpec (MenuSpec *menuSpec)
1772 MenuItem *nextMenuItem;
1773 KeySpec *accelKeySpec;
1774 KeySpec *nextAccelKeySpec;
1776 if ((menuSpec == NULL) || (menuSpec->name != NULL))
1777 /* we only destroy custom menus! */
1783 * Fix for CR 5450 - If the custom menu is the same as wmGD.menuActive, call
1784 * the UnmapCallback directly to clean things up. Since
1785 * the menu is going to be destroyed, this callback will
1786 * not get called, leaving MWM in a failure state.
1788 if (wmGD.menuActive == menuSpec)
1789 UnmapCallback((Widget)NULL, (caddr_t)NULL, (caddr_t)NULL);
1791 * End fix for CR 5450
1794 menuItem = menuSpec->menuItems;
1797 nextMenuItem = menuItem->nextMenuItem;
1798 FreeMenuItem (menuItem);
1799 menuItem = nextMenuItem;
1802 if (menuSpec->menuButtons)
1804 XtFree ((char *)menuSpec->menuButtons);
1807 accelKeySpec = menuSpec->accelKeySpecs;
1808 while (accelKeySpec)
1810 nextAccelKeySpec = accelKeySpec->nextKeySpec;
1811 XtFree ((char *)accelKeySpec);
1812 accelKeySpec = nextAccelKeySpec;
1815 if (menuSpec->menuWidget)
1816 /* destroy all children of the menu's MenuShell parent */
1818 XtDestroyWidget (XtParent(menuSpec->menuWidget));
1821 XtFree ((char *)menuSpec);
1823 } /* END OF FUNCTION FreeCustomMenuSpec */