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 librararies 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.
32 static char rcsid[] = "$XConsortium: WmMenu.c /main/15 1996/11/20 15:20:17 rswiston $"
36 * (c) Copyright 1987, 1988, 1989, 1990 HEWLETT-PACKARD COMPANY */
38 * (c) Copyright 1987, 1988 DIGITAL EQUIPMENT CORPORATION */
40 * (c) Copyright 1988 MASSACHUSETTS INSTITUTE OF TECHNOLOGY */
48 #include "WmResource.h"
49 #include "WmResParse.h"
50 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
52 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
55 #include <X11/Shell.h>
59 #include <Xm/CascadeB.h>
60 #include <Xm/CascadeBG.h>
62 #include <Xm/LabelG.h>
63 #include <Xm/MenuShell.h>
65 #include <Xm/PushBG.h>
66 #include <Xm/RowColumn.h>
67 #include <Xm/RowColumnP.h>
68 #include <Xm/Separator.h>
69 #include <Xm/SeparatoG.h>
71 #define SHELL_NAME "menu"
72 #define SEPARATOR_NAME "separator"
73 #define TITLE_NAME "title_name"
74 #define CASCADE_BTN_NAME "cascadebutton"
75 #define PUSH_BTN_NAME "pushbutton"
77 #define CHILDREN_CACHE 22
78 #define MENU_BUTTON_INC 5
81 * include extern functions
85 #include "WmColormap.h"
87 #include "WmFunction.h"
88 #include "WmIconBox.h"
92 #include "WmWrkspace.h"
96 static void UnmapCallback (Widget w, XtPointer client_data,
98 static MenuItem *DuplicateMenuItems (MenuItem *menuItems);
100 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
101 static MenuExclusion *DuplicateMenuExclusions(MenuExclusion *exclusions);
102 static Boolean FindClientCommandMatch (MenuSpec *menuSpec,
103 String clientCommand,
104 MenuItem **menuItem);
105 static void InsertTreeOnClient (WmScreenData *pSD, ClientData *pCD,
107 MatchList **client_match_list,
108 MatchList **global_match_list,
109 MenuSpec *menuSpec, MenuItem *template,
110 String command_so_far,
111 Boolean duplicate_globals, Atom selection,
112 Context greyed_context, Boolean inLine);
113 static MenuSpec *MakeMenuSpec (String menuName, CARD32 commandID);
114 static void UnmapPulldownCallback (Widget w, XtPointer client_data,
115 XtPointer call_data);
116 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
120 /*************************************<->*************************************
122 * MakeMenu (menuName, initialContext, accelContext, moreMenuItems,
128 * This function makes a menu widget.
133 * menuName = name of the top-level menu pane for the menu
134 * initialContext = initial context for menuitem sensitivity
135 * accelContext = accelerator context
136 * moreMenuItems = additional menuitems for custom menu.
137 * fSystemMenu = TRUE iff the menu is a client system menu.
142 * Return = pointer to a MenuSpec structure with updated currentContext,
143 * menuWidget, and menuButtons members.
148 * If moreMenuItems is nonNULL, a custom MenuSpec will be created, with
149 * menuItem member pointing to moreMenuItems. The menuItems for the
150 * standard MenuSpec of the same name and the moreMenuItems list will be
151 * used to create menubuttons, and the menu widget will be separate from
152 * any existing standard menu widget.
154 * When the client is destroyed, this custom MenuSpec, its menuItem and
155 * menuButton lists, and its menu widget should be freed.
157 *************************************<->***********************************/
158 MenuSpec *MakeMenu (WmScreenData *pSD,
159 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
161 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
162 String menuName, Context initialContext,
163 Context accelContext, MenuItem *moreMenuItems,
168 MenuSpec *newMenuSpec;
170 KeySpec *accelKeySpec;
172 if ((menuName == NULL) || (pSD == NULL))
178 * Look for the menu specification:
181 menuSpec = pSD->menuSpecs;
184 if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
185 /* Found the menu pane. */
189 menuSpec = menuSpec->nextMenuSpec;
192 if (menuSpec == NULL)
193 /* the menuSpecs list is exhausted */
195 MWarning(((char *)GETMESSAGE(48, 1, "Menu specification %s not found\n")), menuName);
200 * The top-level menu pane specification was found.
201 * Adjust the menu accelerator context?
208 else if (accelContext & F_CONTEXT_ROOT)
209 /* root context accelerators apply everywhere */
211 accelContext = F_CONTEXT_ALL;
215 * If making a custom menu, create a custom copy of the specification with
216 * which to build the custom menu.
217 * Otherwise, if the menu widget exists, possibly modify the accelerator
218 * contexts and return the specification.
221 if (moreMenuItems != NULL)
223 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
224 if ((newMenuSpec = DuplicateMenuSpec(menuSpec)) == (MenuSpec *)NULL)
227 if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
228 /* Handle insufficent memory */
230 MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
233 newMenuSpec->name = NULL; /* distinguishes this as custom */
234 newMenuSpec->whichButton = SELECT_BUTTON;
235 newMenuSpec->height = 0;
236 newMenuSpec->menuItems = menuSpec->menuItems; /* temporary */
237 newMenuSpec->accelContext = menuSpec->accelContext;
238 newMenuSpec->accelKeySpecs = NULL;
239 newMenuSpec->nextMenuSpec = NULL;
242 menuSpec = newMenuSpec;
244 else if (menuSpec->menuWidget)
247 * OR the accelContext into the accelerators, if necessary.
249 if (accelContext != (menuSpec->accelContext & accelContext))
251 menuSpec->accelContext |= accelContext;
252 accelKeySpec = menuSpec->accelKeySpecs;
255 accelKeySpec->context |= accelContext;
256 accelKeySpec = accelKeySpec->nextKeySpec;
263 * We have a menu specification with which to build the menu.
264 * Set the initial and accelerator contexts -- they are needed within
268 menuSpec->currentContext = initialContext;
269 menuSpec->accelContext = accelContext;
272 * Scan the toplevel MenuSpec and create its initial menuButtons array
273 * if any of its items will need to be included. This array will be
274 * created or enlarged within CreateMenuWidget below if necessary.
278 menuItem = menuSpec->menuItems;
281 if ((menuItem->greyedContext) || (menuItem->mgtMask))
285 menuItem = menuItem->nextMenuItem;
287 menuItem = moreMenuItems;
290 if ((menuItem->greyedContext) || (menuItem->mgtMask))
294 menuItem = menuItem->nextMenuItem;
298 if ((menuSpec->menuButtons =
299 (MenuButton *) XtMalloc (n * sizeof(MenuButton))) == NULL)
300 /* insufficent memory */
302 MWarning(((char *)GETMESSAGE(48, 3, "Insufficient memory for menu %s\n")), menuName);
305 menuSpec->menuButtonSize = n;
309 menuSpec->menuButtons = NULL;
310 menuSpec->menuButtonSize = 0;
312 menuSpec->menuButtonCount = 0;
315 * Create a PopupShell widget as a child of the workspace manager widget
316 * and a PopupMenu as a child of the shell.
317 * Fill the PopupMenu with the menu items.
320 menuSpec->menuWidget = CREATE_MENU_WIDGET (pSD, pCD, menuName,
321 pSD->screenTopLevelW,
322 TRUE, menuSpec, moreMenuItems);
323 if (menuSpec->menuWidget == NULL)
326 * Could not make the top-level menu pane.
331 _XmSetPopupMenuClick(menuSpec->menuWidget, False);
333 /* Return the top MenuSpec */
337 } /* END OF FUNCTION MakeMenu */
341 /*************************************<->***********************************/
342 void CheckTerminalSeparator(menuSpec, buttonWidget, manage)
352 if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
357 cw = (CompositeWidget)menuSpec->menuWidget;
358 children = cw->composite.children;
360 for (wPos = 0; wPos < cw->composite.num_children; wPos++)
362 if((Widget)children[wPos] == buttonWidget)
370 XtClass((Widget) children[wPos -1]) == xmSeparatorGadgetClass)
374 if (!(XtIsManaged((Widget)children[wPos -1])))
376 XtManageChild((Widget)children[wPos -1]);
381 if (XtIsManaged((Widget)children[wPos -1]))
383 XtUnmanageChild((Widget)children[wPos -1]);
388 } /* END OF FUNCTION CheckTerminalSeparator */
392 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
393 /*************************************<->*************************************
395 * MakeMenuSpec (menuName, commandID)
401 * This function creates and returns a MenuSpec structure.
406 * menuName = name of the menu specification
407 * commandID = client command id of the menu item to build.
408 * 0 if not for a client command.
413 * Return = pointer to a MenuSpec structure with zero'ed fields.
418 * A new MenuSpec structure is allocated. The name is set to the
419 * menuName argument. The menuItems list, menuButtons list and
420 * accelerator related fields are zero'ed out to NULL values.
422 *************************************<->***********************************/
424 MakeMenuSpec (String menuName, CARD32 commandID)
428 if ((menuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
429 /* Handle insufficent memory */
431 MWarning(((char *)GETMESSAGE(48, 2,
432 "Insufficient memory for menu %s\n")), menuName);
436 menuSpec->name = XtNewString(menuName);
437 menuSpec->currentContext = F_CONTEXT_ALL;
438 menuSpec->menuWidget = (Widget) NULL;
439 menuSpec->whichButton = SELECT_BUTTON;
440 menuSpec->height = 0;
441 menuSpec->menuItems = (MenuItem *) NULL;
442 menuSpec->menuButtons = (MenuButton *) NULL;
443 menuSpec->menuButtonSize = 0;
444 menuSpec->menuButtonCount = 0;
445 menuSpec->accelContext = F_CONTEXT_ALL;
446 menuSpec->accelKeySpecs = (KeySpec *) NULL;
447 menuSpec->exclusions = (MenuExclusion *) NULL;
448 menuSpec->clientLocal = FALSE;
449 menuSpec->commandID = commandID;
450 menuSpec->nextMenuSpec = (MenuSpec *) NULL;
456 /*************************************<->*************************************
458 * DuplicateMenuItems (menuItems)
464 * This function creates an indentical duplicate of the given menuItems
470 * menuItems = the linked list of menuItems to duplicate
475 * Return = pointer to a new MenuItems list, identical to the original
481 *************************************<->***********************************/
483 DuplicateMenuItems (MenuItem *menuItems)
485 MenuItem *newMenuItem = (MenuItem *) NULL, *returnMenuItem, *curMenuItem;
487 for (curMenuItem = menuItems;
488 curMenuItem != (MenuItem *) NULL;
489 curMenuItem = curMenuItem->nextMenuItem)
491 /* If its the first one ... */
492 if (newMenuItem == (MenuItem *) NULL)
494 newMenuItem = (MenuItem *)XtMalloc(sizeof(MenuItem));
495 returnMenuItem = newMenuItem;
497 else /* ... otherwise, get the next menuItem. */
499 newMenuItem->nextMenuItem =
500 (MenuItem *)XtMalloc(sizeof(MenuItem));
501 newMenuItem = newMenuItem->nextMenuItem;
504 newMenuItem->labelType = curMenuItem->labelType;
505 if (curMenuItem->label != (String) NULL)
506 newMenuItem->label = XtNewString(curMenuItem->label);
508 newMenuItem->label = NULL;
509 newMenuItem->labelBitmapIndex = curMenuItem->labelBitmapIndex;
510 newMenuItem->mnemonic = curMenuItem->mnemonic;
511 newMenuItem->accelState = curMenuItem->accelState;
512 newMenuItem->accelKeyCode = curMenuItem->accelKeyCode;
513 if (curMenuItem->accelText != (String) NULL)
514 newMenuItem->accelText = XtNewString(curMenuItem->accelText);
516 newMenuItem->accelText = NULL;
517 newMenuItem->wmFunction = curMenuItem->wmFunction;
519 if ((curMenuItem->wmFunction == F_Send_Msg)
521 || (curMenuItem->wmFunction == F_Set_Context)
524 * NOTE: For now, in dtwm this function is used only
525 * to copy the FrontPanel menu. So, we know that
526 * curMenuItem->wmFuncArgs isn't going anywhere,
527 * so it's safe to simply point at it. If at some
528 * point it becomes possible that curMenuItem->wmFuncArgs
529 * can go away, we'll need to make a (deep) copy of
530 * the WmActionArg. 11/20/96
532 || (curMenuItem->wmFunction == F_Action)
533 # endif /* PANELIST */
536 newMenuItem->wmFuncArgs = curMenuItem->wmFuncArgs;
537 else if (curMenuItem->wmFuncArgs != (String) NULL)
538 newMenuItem->wmFuncArgs = XtNewString(curMenuItem->wmFuncArgs);
540 newMenuItem->wmFuncArgs = NULL;
542 newMenuItem->greyedContext = curMenuItem->greyedContext;
543 newMenuItem->mgtMask = curMenuItem->mgtMask;
544 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
545 newMenuItem->clientCommandName =
546 XtNewString(curMenuItem->clientCommandName);
547 newMenuItem->clientCommandID = curMenuItem->clientCommandID;
549 newMenuItem->nextMenuItem = (MenuItem *) NULL;
552 return(returnMenuItem);
556 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
557 /*************************************<->*************************************
559 * DuplicateMenuExclusions (exclusions)
565 * This function creates an indentical duplicate of the given menu exclusions
571 * exclusions = the linked list of menu exclusions to duplicate
576 * Return = pointer to a new MenuExclusion list, identical to the original
582 *************************************<->***********************************/
583 static MenuExclusion *
584 DuplicateMenuExclusions (MenuExclusion *exclusions)
586 MenuExclusion *newMenuExclusion = (MenuExclusion *) NULL;
587 MenuExclusion *returnMenuExclusion = (MenuExclusion *) NULL;
588 MenuExclusion *curMenuExclusion = (MenuExclusion *) NULL;
590 for (curMenuExclusion = exclusions;
591 curMenuExclusion != (MenuExclusion *) NULL;
592 curMenuExclusion = curMenuExclusion->nextExclusion)
594 /* If its the first one ... */
595 if (newMenuExclusion == (MenuExclusion *) NULL)
598 (MenuExclusion *)XtMalloc(sizeof(MenuExclusion));
599 returnMenuExclusion = newMenuExclusion;
601 else /* ... otherwise, get the next menuExclusion. */
603 newMenuExclusion->nextExclusion =
604 (MenuExclusion *)XtMalloc(sizeof(MenuExclusion));
605 newMenuExclusion = newMenuExclusion->nextExclusion;
608 newMenuExclusion->command_string =
609 XtNewString(curMenuExclusion->command_string);
612 /* Make sure we properly NULL terminate the list. */
613 if (newMenuExclusion != (MenuExclusion *) NULL)
614 newMenuExclusion->nextExclusion = (MenuExclusion *) NULL;
616 return(returnMenuExclusion);
620 /*************************************<->*************************************
622 * DuplicateMenuSpec (menuSpec)
628 * This function creates an indentical duplicate of the given menuSpec.
629 * The menuItems list in the menuSpec is also duplicated.
634 * menuSpec = the menuSpec to duplicate
639 * Return = pointer to a new MenuSpec structure with the same field
640 * values as the original
645 * A new MenuSpec structure is allocated. Most of he fields of the new
646 * structure are set to the same values as the passed in menuSpec.
647 * There are some differences between the two final structures.
648 * One difference: any fields related to push buttons and other
649 * widgets are left blank in the new MenuSpec to be filled in later.
651 *************************************<->***********************************/
653 DuplicateMenuSpec (MenuSpec *menuSpec)
655 MenuSpec *newMenuSpec;
657 if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
658 /* Handle insufficent memory */
660 Warning((char *)GETMESSAGE(48, 9,
661 "Insufficient memory for menu specification\n"));
664 newMenuSpec->name = XtNewString(menuSpec->name);
665 newMenuSpec->currentContext = menuSpec->currentContext;
666 newMenuSpec->menuWidget = (Widget) NULL;
667 newMenuSpec->whichButton = menuSpec->whichButton;
668 newMenuSpec->height = menuSpec->height;
669 newMenuSpec->menuItems = DuplicateMenuItems(menuSpec->menuItems);
670 newMenuSpec->menuButtons = (MenuButton *) NULL;
671 newMenuSpec->menuButtonSize = 0;
672 newMenuSpec->menuButtonCount = 0;
673 newMenuSpec->accelContext = menuSpec->accelContext;
674 newMenuSpec->accelKeySpecs = (KeySpec *) NULL;
675 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
676 newMenuSpec->exclusions = DuplicateMenuExclusions(menuSpec->exclusions);
677 newMenuSpec->clientLocal = TRUE;
678 newMenuSpec->commandID = menuSpec->commandID;
680 newMenuSpec->nextMenuSpec = (MenuSpec *) NULL;
686 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
687 /*************************************<->*************************************
689 * MakeMenuItem (label, wmFunction, funcArgs, mnemonic, accelText)
694 * This function creates and returns a MenuItem structure.
699 * label = the display name of the menu item
700 * wmFunction = the wm function to invoke
701 * funcArgs = the function arguments to pass to the wm function
702 * mnemonic = the mnemonic keysym
703 * accelText = the accelerator text
707 * Return = pointer to a new MenuItem structure with fields filled
708 * in as per passed arguments
713 * This function is actually used as the underlying mechanism for
714 * MenuItem creation by MakeMenuItemFromTemplate and
715 * MakeClientCommandMenuItem.
719 * This function assumes that ParseWmAccelerator simply takes a pointer
720 * to a string and parses the accelerator found in it. If ParseWmAccelerator
721 * is ever modified to call GetString to get more text from the parse
722 * stream (as other parse functions do) then this code will break.
724 *************************************<->***********************************/
726 MakeMenuItem (String label, WmFunction wmFunction, String funcArgs,
727 KeySym mnemonic, unsigned int accelState,
728 KeyCode accelKeyCode, String accelText)
732 unsigned char *copy_of_accelText;
735 if ((menuItem = (MenuItem *) XtMalloc (sizeof (MenuItem))) == NULL)
736 /* Handle insufficent memory */
738 MWarning(((char *)GETMESSAGE(48, 10,
739 "Insufficient memory for menu item %s\n")), label);
743 menuItem->labelType = XmSTRING;
744 menuItem->label = XtNewString(label);
745 menuItem->labelBitmapIndex = -1;
746 menuItem->mnemonic = mnemonic;
747 menuItem->clientCommandName = NULL;
748 menuItem->clientCommandID = 0;
751 copy_of_accelText = (unsigned char *)XtNewString(accelText);
752 ParseWmAccelerator(©_of_accelText, menuItem);
755 menuItem->accelState = accelState;
756 menuItem->accelKeyCode = accelKeyCode;
757 menuItem->accelText = XtNewString(accelText);
759 menuItem->wmFunction = wmFunction;
760 menuItem->wmFuncArgs = XtNewString(funcArgs);
761 SetGreyedContextAndMgtMask(menuItem, wmFunction);
762 menuItem->nextMenuItem = (MenuItem *) NULL;
768 /*************************************<->*************************************
770 * MakeMenuItemFromTemplate (template, name, funcArgs)
775 * This function creates and returns a MenuItem structure.
780 * template = a template menuItem used to fill in fields of the
782 * name = the display name this item should have
783 * funcArgs = the function arguments to pass to the wm function
787 * Return = pointer to a new MenuItem structure with fields filled
788 * in as per template MenuItem and funcargs
793 * This function uses the values in the template MenuItem to create
794 * a new copy of the template with the given funcArgs.
796 *************************************<->***********************************/
798 static MenuItem *MakeMenuItemFromTemplate (MenuItem *template, String name,
801 if (template->clientCommandName == (String) NULL)
802 return(MakeMenuItem(name, template->wmFunction, funcArgs,
803 template->mnemonic, template->accelState,
804 template->accelKeyCode, template->accelText));
806 return(MakeMenuItem(template->clientCommandName, template->wmFunction,
807 funcArgs, template->mnemonic, template->accelState,
808 template->accelKeyCode, template->accelText));
812 /*************************************<->*************************************
814 * MakeClientCommandMenuItem (label, funcArgs)
819 * This function creates and returns a MenuItem structure filled as
820 * appropriate for client command menu items using the given label
826 * label = the display label for this menu item
827 * funcArgs = the function arguments to pass to the wm function
831 * Return = pointer to a new MenuItem structure with fields filled
832 * in as per arguments and client command specs
837 * This function will fill in a new MenuItem structure as appropriate for
838 * client commands. This function is used when you want to insert a client
839 * command into a menu without using a MenuItem template constructed from
842 *************************************<->***********************************/
844 static MenuItem *MakeClientCommandMenuItem (String label, String funcArgs)
846 return(MakeMenuItem(label, F_InvokeCommand, funcArgs,
847 (KeySym) NULL, (unsigned int)0,
848 (KeyCode) NULL, (String)NULL));
852 /*************************************<->*************************************
854 * PerformClientCommandMatch (clientCommand, menuItem, bestMatchSoFar)
859 * This function determines whether the menuItemCommand specification
860 * matches the clientCommand.
864 * clientCommand = the clientCommand that we want to find a specification for
865 * menuItem = the menu item we will look in for a specification
866 * bestMatchSoFar = the menu item we will return if the given menu item is
867 * not a match or not a better match
871 * Return = pointer to the given menuItem if it contains a client command
872 * specification and if that specification matches the given
873 * clientCommand *and* if the match is a better match than
874 * the bestMatchSoFar. Otherwise, the bestMatchSoFar is returned,
875 * which could possibly be NULL.
879 * If the menuItem does match, it also determines whether it is
880 * a better match than the bestMatchSoFar. If so, the menuItemCommand is
881 * returned. Otherwise, bestMatchSoFar is returned.
883 * Best matching is defined as follows:
884 * 1. A specification with fewer wildcards is considered a better
885 * match than one with more wildcards.
886 * 2. Given two specifications with the same number of wildcards,
887 * the specification with its wildcards more towards the right
888 * than the left is considered a better match.
890 *************************************<->***********************************/
892 /* @RGC: This is kind of arbitrary, but I can't imagine there being more
893 than a small number of segments in any given command specification. */
894 #define MAXSEGMENTS 100
896 static MenuItem *PerformClientCommandMatch (String clientCommand,
898 MenuItem *bestMatchSoFar)
900 String menuItemCommand, bestMatchStr;
902 int segments = 0, wildcards = 0, wildcardPositions[MAXSEGMENTS];
903 int bestSegments = 0, bestWildcards = 0;
904 int bestWildcardPositions[MAXSEGMENTS];
905 Boolean foundWildcard = FALSE;
907 if (menuItem == (MenuItem *) NULL)
908 return(bestMatchSoFar);
909 menuItemCommand = menuItem->label;
911 /* Skip any modifier characters at the beginning of the
912 menu items client command. */
913 /* @RGC: This is kind of kludgy. We shouldn't have to know
914 the specifics of command parsing here. */
915 if (menuItemCommand[0] == '~')
917 else if (menuItemCommand[0] == '=' && menuItemCommand[1] == '>')
918 menuItemCommand += 2;
919 else if (menuItemCommand[0] == '=')
921 else if (menuItemCommand[0] == '-' && menuItemCommand[1] == '>')
922 menuItemCommand += 2;
924 /* If the menu item doesn't even contain a client command spec,
925 then just return the existing best match. */
926 if (*menuItemCommand != '<') return(bestMatchSoFar);
928 /* Run down the clientCommand and the menuItemCommand together,
929 matching along the way. If matching fails at any point, then
930 return the bestMatchSoFar. */
932 *menuItemCommand != '\0' && *clientCommand != '\0';
935 /* Skip past the '<' at the beginning of the next segment and
937 ++menuItemCommand; ++clientCommand;
938 while (isspace(*menuItemCommand)) ++menuItemCommand;
939 while (isspace(*clientCommand)) ++clientCommand;
941 /* First check whether the current menuItemCommand segment is
943 if (*menuItemCommand == '*')
945 /* Since the menuItemCommand segment is a wildcard, skip
946 it and the current segment of the client command since
947 the wildcard has to match at least one segment in
948 the client command. */
949 wildcardPositions[wildcards++] = segments;
951 while (isspace(*menuItemCommand)) ++menuItemCommand;
952 while (*clientCommand != '>' && *clientCommand != '\0')
954 foundWildcard = TRUE;
958 /* Calculate how long the current segment of the
959 menuItemCommand is */
961 menuItemCommand[seglength] != '>' &&
962 menuItemCommand[seglength] != '\0';
966 /* If we are pointing at '\0', then this isn't a match */
967 if (menuItemCommand[seglength] == '\0') return(bestMatchSoFar);
969 /* Get rid of trailing white space on the segment. */
970 for (; seglength > 0; --seglength)
972 if (!isspace(menuItemCommand[seglength - 1]))
976 /* Now string compare this segment with the clientCommand
977 segment, up to the number of characters in the menu
979 if (strncmp(menuItemCommand, clientCommand, seglength) == 0)
981 /* So far so good. Just make sure clientCommand doesn't
982 have anything but whitespace after its seglength
984 clientCommand += seglength;
985 while (isspace(*clientCommand)) ++clientCommand;
986 if (*clientCommand != '>') return(bestMatchSoFar);
988 /* We have a match. Clear the foundWildcard since we
989 have sync'ed up and keep trying to match. */
990 foundWildcard = FALSE;
991 menuItemCommand += seglength;
992 while (isspace(*menuItemCommand)) ++menuItemCommand;
994 else if (foundWildcard == FALSE)
996 /* We didn't match and there wasn't wildcard to
997 swallow the discrepancy. Therefore, this is not
999 return(bestMatchSoFar);
1003 /* We finished the current segments, we should be looking at
1004 a close bracket and a following period or a close bracket and
1005 a following NULL. Skip past the close brackets and optional
1006 period. If we don't see those, then this isn't a match. */
1007 if (menuItemCommand[0] == '>' && menuItemCommand[1] == '\0' &&
1008 clientCommand[0] == '>' && clientCommand[1] == '\0')
1010 ++menuItemCommand; ++clientCommand;
1012 else if (menuItemCommand[0] == '>' && menuItemCommand[1] == '.' &&
1013 clientCommand[0] == '>' && clientCommand[1] == '.')
1015 menuItemCommand += 2;
1018 else return(bestMatchSoFar);
1021 /* If we terminated the loop because only one of the two commands being
1022 compared was empty, then we don't have a complete match. Return the
1023 best match so far. */
1024 if (*menuItemCommand != '\0' || *clientCommand != '\0')
1025 return(bestMatchSoFar);
1027 /* So the menuItemCommand must have matched. If the current best
1028 match is NULL, then just return the menuItem. Otherwise calculate some
1029 matching quality metrics for the bestMatchSoFar and compare them
1030 to the menuItemCommand metrics to decide which of the two to
1032 if (bestMatchSoFar == (MenuItem *) NULL)
1035 bestMatchStr = bestMatchSoFar->label;
1037 /* Skip any modifier characters at the beginning of the
1038 best match client command. */
1039 /* @RGC: This is kind of kludgy. We shouldn't have to know
1040 the specifics of command parsing here. */
1041 if (bestMatchStr[0] == '~')
1043 else if (bestMatchStr[0] == '=' && bestMatchStr[1] == '>')
1045 else if (bestMatchStr[0] == '=')
1047 else if (bestMatchStr[0] == '-' && bestMatchStr[1] == '>')
1050 /* If the best match doesn't even contain a client command spec,
1051 then just return the new match as the best match. */
1052 if (*bestMatchStr != '<') return(menuItem);
1054 for (bestSegments = 0;
1055 *bestMatchStr != '\0';
1058 /* Skip past the '<' at the beginning of the next segment and
1061 while (isspace(*bestMatchStr)) ++bestMatchStr;
1063 /* First check whether the current bestMatchStr segment is
1064 a wildcard. @RGC: We are assuming that there is nothing
1065 but possible whitespace after the *. */
1066 if (*bestMatchStr == '*')
1067 bestWildcardPositions[bestWildcards++] = bestSegments;
1068 while (*bestMatchStr != '>' && *bestMatchStr != '\0')
1071 /* Check for the closing > and . or close > and NULL. If they
1072 do not both appear then the bestMatch is bad and we should
1073 return the menuItem. */
1074 if (bestMatchStr[0] == '>' && bestMatchStr[1] == '\0')
1076 else if (bestMatchStr[0] == '>' && bestMatchStr[1] == '.')
1078 else return(menuItem);
1081 /* Now compare the best match metrics with the menu item metrics to
1082 determine who should be returned. */
1083 if (bestWildcards != wildcards)
1085 /* Return the menuItem with the fewest wildcards. */
1086 return(bestWildcards < wildcards ? bestMatchSoFar : menuItem);
1090 /* Find which menu item has the earliest wild card and return
1092 for (i = 0; i < wildcards; ++i)
1093 if (wildcardPositions[i] != bestWildcardPositions[i])
1095 return(bestWildcardPositions[i] < wildcardPositions[i] ?
1096 bestMatchSoFar : menuItem);
1099 /* If we got this far, then the two specifications are too
1100 close to call. Return bestMatchSoFar. */
1101 return(bestMatchSoFar);
1106 /*************************************<->*************************************
1108 * ExcludeClientCommand (menuSpec, clientCommand)
1116 * menuSpec = the menuSpec whose menuItems we want to search through
1117 * clientCommand = the clientCommand that we want to find an exclusion for
1121 * Return = TRUE if the command must be excluded from the menuSpec.
1122 * FALSE if there is no exclusion preventing the insertion.
1127 *************************************<->***********************************/
1129 static Boolean ExcludeClientCommand (MenuSpec *menuSpec, String clientCommand)
1131 MenuItem placeholder;
1132 MenuExclusion *curExclusion;
1134 /* Search for an exclusion that would cause this command to be
1135 excluded, if any such exclusion exists. */
1136 for (curExclusion = menuSpec->exclusions;
1137 curExclusion != (MenuExclusion *) NULL;
1138 curExclusion = curExclusion->nextExclusion)
1140 /* We don't have menu items for exclusions so just use a bogus
1141 placeholder menu item with the label field set to the string
1142 found in the exclusion. */
1143 placeholder.label = curExclusion->command_string;
1145 /* If we don't get NULL back, then this exclusion matches. */
1146 if (PerformClientCommandMatch(clientCommand,
1147 &placeholder, NULL) != (MenuItem *) NULL)
1156 /*************************************<->*************************************
1158 * ForceInLineToCascade (menuSpec, clientCommand, bestMatch)
1166 * menuSpec = the menuSpec whose menuItems we want to search through
1167 * clientCommand = the clientCommand that we want to find an force for
1168 * bestMatch = the best matching menu item that was found
1172 * Return = TRUE if the command set must be cascaded
1173 * FALSE if there is no forced cascade
1178 *************************************<->***********************************/
1180 static Boolean ForceInLineToCascade (MenuSpec *menuSpec,
1181 String clientCommand,
1182 MenuItem **bestMatch)
1184 /* First find the best match in the menu spec. */
1185 FindClientCommandMatch(menuSpec, clientCommand, bestMatch);
1187 /* If the best match is not NULL, then check whether it forces
1188 the client command to cascade. */
1189 if (*bestMatch != (MenuItem *) NULL)
1191 /* If there is a force cascade modifier, then return TRUE. */
1192 if ((strncmp((*bestMatch)->label, "->", 2) == 0) ||
1193 (strncmp((*bestMatch)->label, "=>", 2) == 0))
1197 /* If the best match is NULL, then return FALSE. We have been
1198 given no indication that the inLine command should be forced
1204 /*************************************<->*************************************
1206 * FindClientCommandMatch (menuSpec, clientCommand, menuItem)
1211 * This function searches through the list of menuItems in the given
1212 * menuSpec, searching for ones which have a client command specification
1213 * and, for each one that does, whether that specification matches the
1214 * given client. The best matching menuItem out of all that matched
1219 * menuSpec = the menuSpec whose menuItems we want to search through
1220 * clientCommand = the clientCommand that we want to find a specification for
1221 * menuItem = the best matching menu item
1225 * Return = TRUE if the command may be inserted into the menuSpec.
1226 * FALSE if there is an exclusion preventing the insertion.
1227 * Also return the best matching menu item in the menuItem
1228 * buffer argument. NULL is returned if no matching MenuItem
1233 * Best matching is defined as follows:
1234 * 1. A specification with fewer wildcards is considered a better
1235 * match than one with more wildcards.
1236 * 2. Given two specifications with the same number of wildcards,
1237 * the specification with its wildcards more towards the right
1238 * than the left is considered a better match.
1240 *************************************<->***********************************/
1242 static Boolean FindClientCommandMatch (MenuSpec *menuSpec,
1243 String clientCommand,
1244 MenuItem **menuItem)
1246 MenuItem *bestMatch = (MenuItem *) NULL, *curMenuItem, placeholder;
1247 MenuItem *bestExclusionItem = (MenuItem *) NULL;
1248 MenuExclusion *curExclusion;
1249 String bestExclusionStr = (String) NULL;
1251 /* First search for a match in the menu items of the menu spec. */
1252 for (curMenuItem = menuSpec->menuItems;
1253 curMenuItem != (MenuItem *) NULL;
1254 curMenuItem = curMenuItem->nextMenuItem)
1257 PerformClientCommandMatch(clientCommand, curMenuItem, bestMatch);
1260 /* Now search for the best exclusion that would cause this match to be
1261 excluded, if any such exclusion exists. */
1262 for (curExclusion = menuSpec->exclusions;
1263 curExclusion != (MenuExclusion *) NULL;
1264 curExclusion = curExclusion->nextExclusion)
1266 /* We don't have menu items for exclusions so just use a bogus
1267 placeholder menu item with the label field set to the string
1268 found in the exclusion. */
1269 placeholder.label = curExclusion->command_string;
1271 /* Find the best exclusion string in the bunch. */
1273 PerformClientCommandMatch(clientCommand, &placeholder,
1276 /* Save the best exclusion string since we are going to reuse
1277 the placeholder menu item. */
1278 if (bestExclusionItem != (MenuItem *) NULL)
1279 bestExclusionStr = bestExclusionItem->label;
1282 /* Okay, now if we found an exclusion, we need to determine if the
1283 exclusion was a better match than the best match that we found.
1284 If so, the item is *really* exclude. Otherwise, we return the
1285 best match and let the item be included. */
1286 placeholder.label = bestExclusionStr;
1287 if (bestExclusionStr == (String) NULL ||
1288 PerformClientCommandMatch(clientCommand, bestMatch, &placeholder) ==
1291 *menuItem = bestMatch;
1303 /*************************************<->*************************************
1305 * PerformInsertionsOnMatchList (matchlist)
1322 *************************************<->***********************************/
1324 static void PerformInsertionsOnMatchList (MatchList **matchlist)
1326 MatchList *curmatch;
1327 MenuItem *newMenuItem, *curitem;
1329 if (*matchlist == (MatchList *) NULL)
1332 if ((*matchlist)->menuspec == (MenuSpec *) NULL)
1334 /* should never get here, but if we do, then we can't
1335 continue in this routine since mwm will dump. This
1336 may be caused by the cci code duplicating a global
1337 menu for a client when it shouldn't be duplicated.
1338 If we skip this routine, the cci command will not
1339 be added which is far less disturbing than a dump. */
1344 for (curmatch = *matchlist;
1345 curmatch != (MatchList *) NULL;
1346 curmatch = curmatch->next)
1348 if (curmatch->menuitem != (MenuItem *) NULL)
1350 /* Find this menu item within the menuspec. */
1351 for (curitem = curmatch->menuspec->menuItems;
1352 curitem != curmatch->menuitem &&
1353 curitem != (MenuItem *) NULL;
1354 curitem = curitem->nextMenuItem)
1357 /* If we didn't find the menuitem in the menuspec, then
1358 don't do this match. */
1359 if (curitem == (MenuItem *) NULL) continue;
1362 MakeMenuItemFromTemplate(curmatch->menuitem,
1363 curmatch->treenode->defaultName,
1364 curmatch->funcargs);
1365 newMenuItem->wmFunction = curmatch->function;
1366 newMenuItem->greyedContext = curmatch->greyed_context;
1367 newMenuItem->nextMenuItem = curitem->nextMenuItem;
1368 newMenuItem->clientCommandID = curmatch->treenode->commandID;
1369 curitem->nextMenuItem = newMenuItem;
1373 MenuItem *last = (MenuItem *) NULL;
1375 if (curmatch->menuspec != NULL)
1377 /* Find the last menu item in the menuspec */
1378 for (last = curmatch->menuspec->menuItems;
1379 last != (MenuItem *) NULL &&
1380 last->nextMenuItem != (MenuItem *) NULL;
1381 last = last->nextMenuItem)
1383 /* If the next item is f.quit and it is the last
1384 item, then stop searching now. We don't want
1385 to insert after a trailing f.kill (i.e. Close). */
1386 if ((last->nextMenuItem->wmFunction == F_Kill) &&
1387 (last->nextMenuItem->nextMenuItem == (MenuItem *) NULL))
1392 /* Create a new client command menu item */
1394 MakeClientCommandMenuItem
1395 (XtNewString(curmatch->treenode->defaultName),
1396 XtNewString(curmatch->funcargs));
1397 newMenuItem->wmFunction = curmatch->function;
1398 newMenuItem->greyedContext = curmatch->greyed_context;
1399 newMenuItem->clientCommandID = curmatch->treenode->commandID;
1401 /* Insert the new menu item at the end of the list */
1402 if (last == (MenuItem *) NULL)
1404 newMenuItem->nextMenuItem = (MenuItem *) NULL;
1405 if (curmatch->menuspec != NULL)
1406 curmatch->menuspec->menuItems = newMenuItem;
1409 /* again, should never get here... */
1415 newMenuItem->nextMenuItem = last->nextMenuItem;
1416 last->nextMenuItem = newMenuItem;
1423 /*************************************<->*************************************
1426 * DestroyMenuSpecWidgets (menuSpec)
1435 * menuSpec = pointer to MenuSpec structure
1443 * Destroys all the menuspec widgets so that we can rebuild the menu from
1446 *************************************<->***********************************/
1448 void DestroyMenuSpecWidgets (MenuSpec *menuSpec)
1450 /* check for bad input value - shouldn't happen. */
1451 if (menuSpec == (MenuSpec *) NULL) return;
1453 /* Destroy the menu widget */
1454 if (menuSpec->menuWidget != (Widget) NULL)
1456 XtDestroyWidget(XtParent(menuSpec->menuWidget));
1457 menuSpec->menuWidget = (Widget) NULL;
1460 /* Destroy the menu buttons array */
1461 if (menuSpec->menuButtonSize != 0)
1463 XtFree((char *)menuSpec->menuButtons);
1464 menuSpec->menuButtons = (MenuButton *) NULL;
1467 /* Reset the counters */
1468 menuSpec->menuButtonSize = 0;
1469 menuSpec->menuButtonCount = 0;
1471 /* Clear the flag that says we have processed this menu spec for
1472 widget creation. (We probably don't need to do this after all
1473 since CreateMenuWidgets clears it when done.) */
1474 if (menuSpec->currentContext & CR_MENU_MARK)
1475 menuSpec->currentContext &= ~(CR_MENU_MARK);
1481 /*************************************<->*************************************
1484 * DestroyMenuSpec (pSD, commandID)
1493 * pSD = screen data pointer of screen with command to remove
1494 * commandID = command id of the menuspec to be removed.
1495 * if no match is found, then no removal is done.
1502 * Destroy the specified menuSpec from the list of menuspecs on the
1503 * specified screen. Note, there may be more than one copy of the
1504 * spec floating around since duplications may have been done for
1507 *************************************<->***********************************/
1509 void DestroyMenuSpec (WmScreenData *pSD, CARD32 commandID)
1511 MenuSpec *msToKill = NULL, *pMS;
1512 ClientListEntry *curClient;
1514 /* Scan through global menu specs. */
1515 if (pSD != NULL && pSD->menuSpecs != NULL && commandID != 0)
1517 /* Scan through the list of menuSpecs and pull the mathing one
1520 if (commandID == pSD->menuSpecs->commandID)
1522 /* match at head of menuSpec list. */
1523 msToKill = pSD->menuSpecs;
1524 pSD->menuSpecs = pSD->menuSpecs->nextMenuSpec;
1525 msToKill->nextMenuSpec = NULL;
1529 for (pMS = pSD->menuSpecs;
1530 (pMS->nextMenuSpec != NULL &&
1531 pMS->nextMenuSpec->commandID != commandID);
1532 pMS = pMS->nextMenuSpec)
1535 if (pMS->nextMenuSpec != NULL)
1537 msToKill = pMS->nextMenuSpec;
1538 pMS->nextMenuSpec = msToKill->nextMenuSpec;
1539 msToKill->nextMenuSpec = NULL;
1543 /* found it - now remove the menuSpec. */
1544 if (msToKill != NULL)
1545 FreeCustomMenuSpec(msToKill);
1549 /* Check each client's menu spec list. Stop searching if global. */
1550 for (curClient = pSD->clientList;
1551 curClient != (ClientListEntry *)NULL;
1552 curClient = curClient->nextSibling)
1555 * Check the first position.
1556 * If matched, then we're done with this client.
1558 if (commandID == pSD->menuSpecs->commandID)
1560 msToKill = curClient->pCD->systemMenuSpec;
1561 curClient->pCD->systemMenuSpec = msToKill->nextMenuSpec;
1562 msToKill->nextMenuSpec = NULL;
1565 /* Check the rest of the list. */
1568 for (pMS = curClient->pCD->systemMenuSpec;
1569 (pMS->nextMenuSpec != (MenuSpec *)NULL) &&
1570 (pMS->nextMenuSpec->commandID != commandID) &&
1572 pMS = pMS->nextMenuSpec)
1575 if ((pMS->nextMenuSpec != (MenuSpec *)NULL) &&
1576 (pMS->nextMenuSpec->commandID != commandID))
1578 msToKill = pMS->nextMenuSpec;
1579 pMS->nextMenuSpec = msToKill->nextMenuSpec;
1580 msToKill->nextMenuSpec = NULL;
1586 if (msToKill != NULL)
1587 FreeCustomMenuSpec(msToKill);
1594 /*************************************<->*************************************
1596 * ReplaceMenuSpecForClient (menuspec, pCD)
1601 * Duplicates the given menuspec and replaces the given menuspec if
1602 * found in the clients menuspec list with the duplicate.
1609 * Return = the duplicate menuspec
1615 *************************************<->***********************************/
1617 static MenuSpec *ReplaceMenuSpecForClient (MenuSpec *menuSpec, ClientData *pCD)
1619 MenuSpec *newMenuSpec, *curMenuSpec;
1621 /* Duplicate the menu spec */
1622 newMenuSpec = DuplicateMenuSpec(menuSpec);
1624 /* Try to find this menuspec in the list of client
1625 menuspecs. If we find it then we want to replace it with
1627 if (pCD->systemMenuSpec == menuSpec)
1629 /* It was the head of the list. We need to handle that
1631 newMenuSpec->nextMenuSpec = pCD->systemMenuSpec->nextMenuSpec;
1632 pCD->systemMenuSpec = newMenuSpec;
1636 /* Search through the list until we find the menuspec or
1637 the end of the list. */
1638 for (curMenuSpec = pCD->systemMenuSpec;
1639 curMenuSpec->nextMenuSpec != (MenuSpec *) NULL;
1640 curMenuSpec = curMenuSpec->nextMenuSpec)
1642 if (curMenuSpec->nextMenuSpec == menuSpec)
1644 newMenuSpec->nextMenuSpec =
1645 curMenuSpec->nextMenuSpec->nextMenuSpec;
1646 curMenuSpec->nextMenuSpec = newMenuSpec;
1647 /* We found it and replaced it. Now get out of
1652 if (curMenuSpec->nextMenuSpec == (MenuSpec *) NULL)
1654 /* We didn't find it. Just stick it at the end? We
1655 should have found it. I'm not sure how to handle
1657 curMenuSpec->nextMenuSpec = newMenuSpec;
1658 newMenuSpec = (MenuSpec *) NULL;
1662 return(newMenuSpec);
1666 /*************************************<->*************************************
1668 * FindLastMenuSpecToModify (menuspec, command_id)
1679 * Return = the last menu spec that would be affected by modifications
1680 * to the given command id
1686 *************************************<->***********************************/
1688 static MenuSpec * FindLastMenuSpecToModify(MenuSpec *menuSpec,
1691 MenuSpec *curMenuSpec, *lastToModify = (MenuSpec *) NULL;
1694 /* Search through all the menu specs in the list starting with
1695 the passed in menuSpec */
1696 for (curMenuSpec = menuSpec;
1697 curMenuSpec != (MenuSpec *) NULL;
1698 curMenuSpec = curMenuSpec->nextMenuSpec)
1700 /* Try to find a menu item in this menu spec with the
1701 command_id that will require modification */
1702 for (curItem = curMenuSpec->menuItems;
1703 curItem != (MenuItem *) NULL;
1704 curItem = curItem->nextMenuItem)
1706 if (curItem->clientCommandID == command_id)
1710 /* If we found a menu item that needs changing, then this
1711 menu spec will need changing. Set the lastToModify to
1712 point to this menu spec. If we find no other menu spec
1713 that needs changing, then this will be the last one
1714 in the list that needs changing. */
1715 if (curItem != (MenuItem *) NULL)
1716 lastToModify = curMenuSpec;
1719 /* We've looked through all the menu specs starting with menuSpec
1720 and we've looked at all the menu items in all of those menu
1721 specs. The lastToModify variable should be set to the last
1722 menu spec we saw that needed modification. Return it. */
1723 return(lastToModify);
1727 /*************************************<->*************************************
1729 * RecreateMenuWidgets (matchlist)
1746 *************************************<->***********************************/
1748 static void RecreateMenuWidgets (WmScreenData *pSD, ClientData *pCD,
1749 MatchList **matchlist)
1753 MenuSpec **to_change;
1755 /* First count how many menu specs we need to recreate widgets for */
1756 for (current = *matchlist;
1757 current != (MatchList *) NULL;
1758 current = current->next)
1761 /* If there are no affected menuspecs, then just return. */
1762 if (count == 0) return;
1764 /* Allocate an array of menuspec pointers that is the size of the
1765 number of menu specs we need to recreate widgets for */
1766 to_change = (MenuSpec **)XtMalloc(sizeof(MenuSpec *) * count);
1767 for (i = 0; i < count; ++i)
1768 to_change[i] = (MenuSpec *) NULL;
1770 /* Now run through all the matchlist items, storing menuspecs in
1771 that array. If the menuspec is already there, then don't store
1773 for (current = *matchlist;
1774 current != (MatchList *) NULL;
1775 current = current->next)
1777 for (i = 0; i < count; ++i)
1779 if (to_change[i] == current->menuspec) break;
1780 else if (to_change[i] == (MenuSpec *) NULL)
1782 to_change[i] = current->menuspec;
1788 /* Run through the array, destroy all existing widgets for each
1790 for (i = 0; i < count && to_change[i] != (MenuSpec *) NULL ; ++i)
1792 DestroyMenuSpecWidgets(to_change[i]);
1795 /* Run through the array again creating widgets for all the menuspecs */
1796 for (i = 0; i < count && to_change[i] != (MenuSpec *) NULL; ++i)
1798 to_change[i]->menuWidget =
1799 CreateMenuWidget (pSD, pCD, to_change[i]->name, pSD->screenTopLevelW,
1800 TRUE, to_change[i], NULL);
1803 /* Free the array. We're done. */
1804 XtFree((char *) to_change);
1808 /*************************************<->*************************************
1810 * FreeMatchList (matchlist)
1827 *************************************<->***********************************/
1829 static void FreeMatchList (MatchList **matchlist)
1831 MatchList *current, *next;
1833 current = *matchlist;
1835 while (current != (MatchList *) NULL)
1837 next = current->next;
1838 XtFree(current->command_string);
1839 XtFree(current->funcargs);
1840 XtFree((char *)current);
1844 *matchlist = (MatchList *) NULL;
1848 /*************************************<->*************************************
1850 * StoreMatchedCommand (matchlist, menuSpec, menuItem, command_string,
1851 * treenode, function, funcargs)
1867 * If the existing match has NULL for the menuitem, then get rid of
1868 * it and replace with proposed match.
1870 *************************************<->***********************************/
1872 static void StoreMatchedCommand (MatchList **matchlist, MenuSpec *menuSpec,
1873 MenuItem *menuItem, String command_string,
1874 CmdTree *treenode, WmFunction function,
1875 String funcargs, Context greyed_context)
1877 MatchList *current, *new;
1879 /* If this entry does not already exist in the match list, then insert
1880 it. This implies that we first have to perform a search of the list.
1881 The search is very easy. We can simply compare the tuple of
1882 <menuSpec,command_string> with each entry in the matchlist
1883 to see if we already have that tuple stored. We can do straight
1884 pointer value matching for the menuSpec and strcmp for the
1886 for (current = *matchlist;
1887 current != (MatchList *) NULL;
1888 current = current->next)
1890 if (current->menuspec == menuSpec &&
1891 strcmp(current->command_string, command_string) == 0)
1893 /* If the currently stored menu item is NULL,
1894 then replace with the new menuitem and return. */
1895 if (current->menuitem == (MenuItem *) NULL)
1897 current->menuitem = menuItem;
1900 /* Otherwise, we have alreay inserted this
1901 command into this menuspec so don't allow
1902 another insertion. */
1907 /* Well, we didn't find a match, so store the entry */
1908 new = (MatchList *)XtMalloc(sizeof(MatchList));
1909 new->menuspec = menuSpec;
1910 new->menuitem = menuItem;
1911 new->command_string = XtNewString(command_string);
1912 new->treenode = treenode;
1913 new->function = function;
1914 new->funcargs = XtNewString(funcargs);
1915 new->greyed_context = greyed_context;
1916 new->next = (MatchList *) NULL;
1918 /* Stick it at the head of the list. It's easier. */
1919 new->next = *matchlist;
1924 /*************************************<->*************************************
1926 * SearchForOtherMatches (pSD, pCD, treenode,
1927 * client_match_list, global_match_list,
1928 * menuSpec, command_string,
1929 * function, funcargs, duplicate_globals, selection,
1935 * menuSpec = menu spec to exclude from search
1948 *************************************<->***********************************/
1950 static void SearchForOtherMatches (WmScreenData *pSD, ClientData *pCD,
1952 MatchList **client_match_list,
1953 MatchList **global_match_list,
1954 MenuSpec *menuSpec, String command_string,
1955 WmFunction function, String funcargs,
1956 Boolean duplicate_globals, Atom selection,
1957 Context greyed_context, Boolean inLine)
1959 MenuSpec *current, *newMenuSpec;
1962 /* Search through all of the clients menuspecs first */
1963 for (current = (pCD == NULL ? NULL : pCD->systemMenuSpec);
1964 current != (MenuSpec *) NULL;
1965 current = current->nextMenuSpec)
1967 /* If the current menu spec is a global, then just quit
1968 this loop. Any menu specs from this point on will
1969 have a next pointer that is still in the global list. */
1970 if (menuSpec->clientLocal != TRUE) break;
1971 FindClientCommandMatch(current, command_string, &match);
1972 if (match != (MenuItem *) NULL)
1974 if (treenode->subTrees != (CmdTree *) NULL && inLine &&
1975 (strncmp(match->label, "->", 2) == 0 ||
1976 strncmp(match->label, "=>", 2) == 0))
1979 for (tree = treenode->subTrees;
1980 tree != (CmdTree *) NULL;
1983 char new_command_str[1024];
1984 char new_funcargs[1024];
1985 WmFunction inLine_function;
1987 if (command_string == NULL)
1988 sprintf(new_command_str, "<%s>", tree->name);
1990 sprintf(new_command_str, "%s.<%s>", command_string,
1992 if (tree->subTrees != (CmdTree *) NULL)
1994 /* menu to cascade to */
1995 sprintf(new_funcargs, "<%s>", tree->name);
1996 inLine_function = F_Menu;
2000 sprintf(new_funcargs, "%d %ld %ld", tree->commandID,
2001 pCD->client, selection);
2002 inLine_function = F_InvokeCommand;
2004 StoreMatchedCommand(client_match_list, current, match,
2005 new_command_str, tree,
2006 inLine_function, new_funcargs,
2012 StoreMatchedCommand(client_match_list, current, match,
2013 command_string, treenode, function,
2014 funcargs, greyed_context);
2019 /* Search through all of the global menuspecs also. */
2020 for (current = pSD->menuSpecs;
2021 current != (MenuSpec *) NULL;
2022 current = current->nextMenuSpec)
2024 FindClientCommandMatch(current, command_string, &match);
2025 if (match != (MenuItem *) NULL)
2027 if (duplicate_globals == TRUE)
2029 /* Create a duplicate of the current menuspec and
2030 store that away in the client instead of the current
2032 newMenuSpec = ReplaceMenuSpecForClient(current, pCD);
2034 /* Now store enough information so that we can actually
2035 create the insertion later. */
2036 StoreMatchedCommand(client_match_list, newMenuSpec, NULL,
2037 command_string, treenode, function,
2038 funcargs, greyed_context);
2040 else /* Change global menu */
2042 StoreMatchedCommand(global_match_list, current, match,
2043 command_string, treenode, function,
2044 funcargs, greyed_context);
2051 /*************************************<->*************************************
2053 * InsertTreeOnClient (pSD, pCD, tree, client_match_list, global_match_list,
2054 * menuSpec, templateMenuItem, command_so_far,
2055 * duplicate_globals, selection, greyed_context, inLine)
2071 * If duplicate_globals is TRUE, then pCD cannot be NULL.
2073 *************************************<->***********************************/
2075 static void InsertTreeOnClient (WmScreenData *pSD, ClientData *pCD,
2077 MatchList **client_match_list,
2078 MatchList **global_match_list,
2079 MenuSpec *menuSpec, MenuItem *template,
2080 String command_so_far,
2081 Boolean duplicate_globals, Atom selection,
2082 Context greyed_context, Boolean inLine)
2084 String new_command_str;
2086 char funcarg_buf[256];
2087 MenuSpec *newMenuSpec, *last, *dupMenuSpec;
2089 MenuItem *bestMatch = (MenuItem *) NULL;
2091 /* If the menuSpec we were given is NULL, then just return. We need to
2092 at least have a starting menuSpec. */
2093 if (menuSpec == (MenuSpec *) NULL)
2096 /* If we want global menus duplicated for a client, then the pCD
2097 had better not be NULL. */
2098 if (duplicate_globals && pCD == (ClientData *) NULL)
2101 while (tree != (CmdTree *) NULL)
2103 /* The "4" below is to allow for brackets to surround the
2104 tree->name, the period to separate it from the command
2105 so far and a NULL. */
2106 length = (command_so_far != NULL ? strlen(command_so_far) : 0) +
2107 (tree->name != NULL ? strlen(tree->name) : 0) + 4;
2108 new_command_str = XtMalloc(sizeof(unsigned char) * length);
2109 if (command_so_far != (String) NULL)
2110 sprintf(new_command_str, "%s.<%s>", command_so_far, tree->name);
2112 sprintf(new_command_str, "<%s>", tree->name);
2114 /* If there is an exclusion preventing this command from being
2115 inserted, then just continue the loop. @RGC: This is wrong.
2116 We still want to search for other matches if there is an
2117 exclusion. We just don't want to allow one of those other
2118 matches to be this menuSpec. */
2119 if (ExcludeClientCommand(menuSpec, new_command_str))
2122 XtFree(new_command_str);
2126 /* If tree is a command set and the inLine flag is TRUE then
2127 * we need to insert the command sets commands in the current
2128 * menu spec instead of creating a cascade.
2130 if (tree->subTrees != (CmdTree *) NULL && inLine == TRUE &&
2131 ForceInLineToCascade(menuSpec, new_command_str,
2132 &bestMatch) == FALSE)
2134 /* Recursively process subtrees */
2135 for (subtree = tree->subTrees;
2136 subtree != (CmdTree *) NULL;
2137 subtree = subtree->next)
2139 /* Pass along the bestMatch. If it is a valid menu item
2140 then we want the insertion to occur at that menuitem
2141 instead of at the end of the menu. */
2142 InsertTreeOnClient(pSD, pCD, subtree, client_match_list,
2143 global_match_list, menuSpec, bestMatch,
2144 new_command_str, duplicate_globals,
2145 selection, greyed_context, inLine);
2147 /* We don't want to search for other matches because we
2148 want the items to be inserted inLine. Any other matches
2149 will be found in the recursive calls. (??? or am I wrong?) */
2151 /* If tree is a command set then we need to create a new
2153 else if (tree->subTrees != (CmdTree *) NULL)
2155 /* Create the name of the menu for the f.menu command. */
2156 sprintf(funcarg_buf, "<%s>", tree->name);
2158 /* Store the cascade button information so it can be
2160 StoreMatchedCommand(
2161 (menuSpec->clientLocal ? client_match_list : global_match_list),
2162 menuSpec, template, new_command_str, tree, F_Menu, funcarg_buf,
2165 /* We need to create a menu spec for the menu that this cascade
2166 button will cascade to. Try to find one in the clients menu
2167 spec list, stopping the first time we hit a global menu. If we
2168 can't find one there and if we are *not* supposed to duplicate
2169 globals, then try to find it in the global list. In all other
2170 cases, create a new one using the funcarg_buf that we created
2171 above as the name of the menuspec. */
2172 for (newMenuSpec = (pCD == NULL ? NULL : pCD->systemMenuSpec);
2173 newMenuSpec != (MenuSpec *) NULL;
2174 newMenuSpec = newMenuSpec->nextMenuSpec)
2176 if (newMenuSpec->clientLocal == FALSE)
2178 newMenuSpec = (MenuSpec *) NULL;
2181 if (strcmp(newMenuSpec->name, funcarg_buf) == 0)
2185 /* If we didn't find it in the client list, maybe we should
2186 look in the global list. */
2187 if (newMenuSpec == (MenuSpec *) NULL && duplicate_globals == FALSE)
2189 for (newMenuSpec = pSD->menuSpecs;
2190 newMenuSpec != (MenuSpec *) NULL;
2191 newMenuSpec = newMenuSpec->nextMenuSpec)
2193 if (strcmp(newMenuSpec->name, funcarg_buf) == 0)
2198 /* If we still don't have a menu spec, then create a new one. */
2199 if (newMenuSpec == (MenuSpec *) NULL)
2201 newMenuSpec = MakeMenuSpec(funcarg_buf,
2202 tree == NULL ? (CARD32)NULL
2204 if (duplicate_globals) newMenuSpec->clientLocal = TRUE;
2205 else newMenuSpec->clientLocal = FALSE;
2207 /* If we are duplicating globals, then add the new menu spec
2208 to the client list. Otherwise add it to the global list. */
2209 if (duplicate_globals)
2210 last = pCD->systemMenuSpec;
2212 last = pSD->menuSpecs;
2214 /* Find the last menu spec in the list. */
2215 while (last != (MenuSpec *) NULL &&
2216 last->nextMenuSpec != (MenuSpec *) NULL)
2217 last = last->nextMenuSpec;
2219 /* Put the new menu spec at the end of the list. */
2220 if (last == (MenuSpec *) NULL)
2222 if (duplicate_globals)
2223 pCD->systemMenuSpec = newMenuSpec;
2225 pSD->menuSpecs = newMenuSpec;
2227 else last->nextMenuSpec = newMenuSpec;
2230 /* Recursively process subtrees */
2231 for (subtree = tree->subTrees;
2232 subtree != (CmdTree *) NULL;
2233 subtree = subtree->next)
2235 InsertTreeOnClient(pSD, pCD, subtree, client_match_list,
2236 global_match_list, newMenuSpec, NULL,
2237 new_command_str, duplicate_globals,
2238 selection, greyed_context, inLine);
2241 /* Search for any other matches in the existing menu specs
2242 for this command, excluding newMenuSpec. */
2243 SearchForOtherMatches(pSD, pCD, tree,
2244 client_match_list, global_match_list,
2245 newMenuSpec, new_command_str,
2246 F_Menu, funcarg_buf,
2247 duplicate_globals, selection,
2248 greyed_context, inLine);
2251 else /* the tree is a simple command */
2253 /* Store away the push button information so it can be
2255 sprintf(funcarg_buf, "%d %ld %ld", tree->commandID,
2256 (pCD == NULL ? None : pCD->client), selection);
2258 /* If the menuSpec is global and we are supposed to be
2259 duplicating global menu specs, then create a duplicate
2260 and replace the menuspec with the duplicate for this
2262 if (duplicate_globals)
2263 dupMenuSpec = ReplaceMenuSpecForClient(menuSpec, pCD);
2265 dupMenuSpec = menuSpec;
2267 /* Now store the match away in the appropriate match list */
2268 StoreMatchedCommand((dupMenuSpec->clientLocal ?
2269 client_match_list : global_match_list),
2270 dupMenuSpec, template, new_command_str, tree,
2271 F_InvokeCommand, funcarg_buf, greyed_context);
2273 /* Search for any other matches in the existing menu specs
2274 for this command, excluding newMenuSpec. */
2276 SearchForOtherMatches(pSD, pCD, tree,
2277 client_match_list, global_match_list,
2278 dupMenuSpec, new_command_str,
2279 F_InvokeCommand, funcarg_buf,
2280 FALSE, /* Don't duplicate globals not associated
2281 with this pCD. CR 9623 */
2283 greyed_context, inLine);
2286 /* Move on to the next tree item at this level */
2288 XtFree(new_command_str);
2293 /*************************************<->*************************************
2295 * InsertTreeOnAllClients (pSD, tree, selection, active_context, inLine)
2303 * pSD = per screen data
2304 * tree = command tree
2305 * selection = owned by inserting client
2315 *************************************<->***********************************/
2316 void InsertTreeOnAllClients (WmScreenData *pSD, CmdTree *tree, Atom selection,
2317 Context active_context, Boolean inLine)
2319 ClientListEntry *current;
2320 MatchList *global_matchlist = (MatchList *) NULL;
2321 MatchList *client_matchlist = (MatchList *) NULL;
2322 Context greyed_context = F_CONTEXT_ALL;
2324 /* If there aren't any clients, then there's nothing to do. */
2325 if (pSD->clientList == (ClientListEntry *) NULL)
2328 /* Setup the greyed context based on the active context */
2329 if (active_context & F_CONTEXT_WINDOW)
2330 greyed_context &= ~(F_CONTEXT_WINDOW);
2331 if (active_context & F_CONTEXT_ICON)
2332 greyed_context &= ~(F_CONTEXT_ICON);
2334 for (current = pSD->clientList;
2335 current != (ClientListEntry *) NULL;
2336 current = current->nextSibling)
2338 /* Ignore client list entries for icons. */
2339 if (current->type == MINIMIZED_STATE)
2341 InsertTreeOnClient(pSD, current->pCD, tree, &client_matchlist,
2342 &global_matchlist, current->pCD->systemMenuSpec,
2344 selection, greyed_context, inLine);
2345 PerformInsertionsOnMatchList(&client_matchlist);
2346 RecreateMenuWidgets(pSD, current->pCD, &client_matchlist);
2347 FreeMatchList(&client_matchlist);
2349 PerformInsertionsOnMatchList(&global_matchlist);
2350 RecreateMenuWidgets(pSD, NULL /* no pcd */, &global_matchlist);
2351 FreeMatchList(&global_matchlist);
2355 /*************************************<->*************************************
2357 * InsertTreeOnSingleClient (pSD, pCD, tree, selection, inLine)
2365 * pSD = per screen data
2366 * tree = command tree
2367 * selection = owned by inserting client
2378 *************************************<->***********************************/
2379 void InsertTreeOnSingleClient (WmScreenData *pSD, ClientData *pCD,
2380 CmdTree *tree, Atom selection,
2381 Context active_context, Boolean inLine)
2383 MatchList *global_matchlist = (MatchList *) NULL;
2384 MatchList *client_matchlist = (MatchList *) NULL;
2385 Context greyed_context = F_CONTEXT_ALL;
2387 /* A quick sanity check */
2388 if (pCD == (ClientData *) NULL)
2391 /* Setup the greyed context based on the active context */
2392 if (active_context & F_CONTEXT_WINDOW)
2393 greyed_context &= ~(F_CONTEXT_WINDOW);
2394 if (active_context & F_CONTEXT_ICON)
2395 greyed_context &= ~(F_CONTEXT_ICON);
2397 InsertTreeOnClient(pSD, pCD, tree, &client_matchlist,
2398 &global_matchlist, pCD->systemMenuSpec,
2399 NULL, NULL, TRUE, selection, greyed_context, inLine);
2400 PerformInsertionsOnMatchList(&client_matchlist);
2401 RecreateMenuWidgets(pSD, pCD, &client_matchlist);
2402 FreeMatchList(&client_matchlist);
2406 /*************************************<->*************************************
2408 * InsertTreeOnRootMenu (pSD, tree, selection, active_context, inLine)
2416 * pSD = per screen data
2417 * tree = command tree
2418 * selection = owned by inserting client
2429 *************************************<->***********************************/
2430 void InsertTreeOnRootMenu (WmScreenData *pSD, CmdTree *tree, Atom selection,
2433 MatchList *global_matchlist = (MatchList *) NULL;
2434 MatchList *client_matchlist = (MatchList *) NULL;
2435 Context greyed_context = F_CONTEXT_WINDOW | F_CONTEXT_ICON;
2438 /* Find the root menu spec */
2439 for (rootMenu = pSD->menuSpecs;
2440 rootMenu != (MenuSpec *) NULL;
2441 rootMenu = rootMenu->nextMenuSpec)
2443 if (strcmp(rootMenu->name, pSD->rootMenu) == 0)
2447 /* If we couldn't find the root menu, then do nothing. */
2448 if (rootMenu == (MenuSpec *) NULL) return;
2450 InsertTreeOnClient(pSD, NULL, tree, &client_matchlist,
2451 &global_matchlist, rootMenu,
2452 NULL, NULL, FALSE, selection, greyed_context, inLine);
2453 PerformInsertionsOnMatchList(&client_matchlist);
2454 RecreateMenuWidgets(pSD, NULL, &client_matchlist);
2455 FreeMatchList(&client_matchlist);
2456 PerformInsertionsOnMatchList(&global_matchlist);
2457 RecreateMenuWidgets(pSD, NULL, &global_matchlist);
2458 FreeMatchList(&global_matchlist);
2462 /*************************************<->*************************************
2464 * RemoveClientCommandFromMenuSpec (menuSpec, id)
2479 *************************************<->***********************************/
2480 static Boolean RemoveClientCommandFromMenuSpec (MenuSpec *menuSpec,
2483 MenuItem *curMenuItem, *prevMenuItem = (MenuItem *) NULL;
2484 MenuItem *tmpMenuItem;
2485 Boolean was_changed = FALSE;
2487 curMenuItem = menuSpec->menuItems;
2488 while (curMenuItem != (MenuItem *) NULL)
2490 if (curMenuItem->clientCommandID == id)
2492 tmpMenuItem = curMenuItem;
2493 curMenuItem = curMenuItem->nextMenuItem;
2494 if (prevMenuItem == (MenuItem *) NULL)
2495 menuSpec->menuItems = tmpMenuItem->nextMenuItem;
2497 prevMenuItem->nextMenuItem = tmpMenuItem->nextMenuItem;
2498 FreeMenuItem(tmpMenuItem);
2503 prevMenuItem = curMenuItem;
2504 curMenuItem = curMenuItem->nextMenuItem;
2507 return(was_changed);
2511 /*************************************<->*************************************
2513 * ModifyClientCommandForMenuSpec (menuSpec, id, modifier, context, newname)
2528 *************************************<->***********************************/
2529 static Boolean ModifyClientCommandForMenuSpec (MenuSpec *menuSpec,
2531 CmdModifier modifier,
2535 MenuItem *curMenuItem;
2536 Boolean was_changed = FALSE;
2537 int i, freebutton, buttoncount;
2539 /* If the menuspec doesn't have any buttons or a valid menu widget
2540 then we don't want to search it. */
2541 if (menuSpec->menuWidget == (Widget) NULL ||
2542 menuSpec->menuButtons == (MenuButton *) NULL ||
2543 menuSpec->menuButtonCount == 0)
2546 /* Search through all the menu buttons of the menuspec for buttons
2547 which match the command ID to be removed. */
2548 for (i = 0; i < menuSpec->menuButtonCount; ++i)
2550 curMenuItem = menuSpec->menuButtons[i].menuItem;
2552 if ((curMenuItem->clientCommandID == id) &&
2553 (curMenuItem->wmFunction == F_InvokeCommand))
2558 /* "context" is an active context */
2559 curMenuItem->greyedContext &= ~(context);
2560 /* Adjust the pushbutton to the state it would have had
2561 given the last posting context. */
2562 if (menuSpec->currentContext & curMenuItem->greyedContext)
2563 XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2566 XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2570 /* "context" is a greyed context */
2571 curMenuItem->greyedContext |= context;
2572 /* Adjust the pushbutton to the state it would have had
2573 given the last posting context. */
2574 if (menuSpec->currentContext & curMenuItem->greyedContext)
2575 XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2578 XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2582 if (newname != NULL && *newname != '\0')
2584 /* When renaming a command, we shouldn't cause the
2585 * entire menu to be recreated. Recreating the menu
2586 * will cause problems with tearoffs since the menu
2587 * will disappear when it is destroyed. CR 9719
2589 XmString labelString;
2591 /* Change the label of the menu item */
2592 XtFree(curMenuItem->label);
2593 /* Give the menu item the new name */
2594 curMenuItem->label = XtNewString(newname);
2595 was_changed = False; /* all taken care of here. */
2597 /* This is needed when changing the label since
2598 * mwm will wait for a geometry reply from itself which
2599 * it can never service. CR 9719
2601 XtVaSetValues(XtParent(XtParent(menuSpec->menuButtons[i].buttonWidget)),
2602 XmNuseAsyncGeometry, TRUE, NULL);
2604 labelString = XmStringGenerate(curMenuItem->label,
2605 XmFONTLIST_DEFAULT_TAG,
2606 XmCHARSET_TEXT, NULL);
2607 XtVaSetValues(menuSpec->menuButtons[i].buttonWidget,
2608 XmNlabelString, labelString,
2610 XmStringFree(labelString);
2614 XtDestroyWidget(menuSpec->menuButtons[i].buttonWidget);
2615 menuSpec->menuButtons[i].managed = FALSE;
2616 menuSpec->menuButtons[i].menuItem = (MenuItem *) NULL;
2617 menuSpec->menuButtons[i].buttonWidget = (Widget) NULL;
2623 /* If we are being asked to remove a client command, then we need
2624 * to search through all the menu items as well as the buttons.
2625 * Do the menu items here.
2627 if (modifier == REMOVE)
2628 was_changed = RemoveClientCommandFromMenuSpec(menuSpec, id);
2630 /* Compact the menu buttons array. */
2631 buttoncount = menuSpec->menuButtonCount;
2633 for (i = 0; i < buttoncount; ++i)
2635 if (menuSpec->menuButtons[i].buttonWidget == (Widget) NULL)
2636 --menuSpec->menuButtonCount;
2639 menuSpec->menuButtons[freebutton].menuItem =
2640 menuSpec->menuButtons[i].menuItem;
2641 menuSpec->menuButtons[freebutton].buttonWidget =
2642 menuSpec->menuButtons[i].buttonWidget;
2643 menuSpec->menuButtons[freebutton].managed =
2644 menuSpec->menuButtons[i].managed;
2648 return(was_changed);
2652 /*************************************<->*************************************
2654 * ModifyClientCommandID (pSD, pCD, range, id, modifier, context, newname)
2669 *************************************<->***********************************/
2670 static void ModifyClientCommandID (WmScreenData *pSD,
2674 CmdModifier modifier,
2678 MenuSpec *curMenuSpec;
2679 ClientListEntry *curClient;
2684 /* Search through all the menu specs of all the clients. */
2685 for (curClient = pSD->clientList;
2686 curClient != (ClientListEntry *) NULL;
2687 curClient = curClient->nextSibling)
2689 for (curMenuSpec = curClient->pCD->systemMenuSpec;
2690 curMenuSpec != (MenuSpec *) NULL;
2691 curMenuSpec = curMenuSpec->nextMenuSpec)
2693 /* If the menu spec is global then stop searching
2695 if (curMenuSpec->clientLocal == FALSE)
2697 if (ModifyClientCommandForMenuSpec(curMenuSpec, id,
2701 DestroyMenuSpecWidgets(curMenuSpec);
2702 curMenuSpec->menuWidget =
2703 CreateMenuWidget (pSD, curClient->pCD, curMenuSpec->name,
2704 pSD->screenTopLevelW, TRUE,
2709 /* Search through all the global menu specs. */
2710 for (curMenuSpec = pSD->menuSpecs;
2711 curMenuSpec != (MenuSpec *) NULL;
2712 curMenuSpec = curMenuSpec->nextMenuSpec)
2714 if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2715 context, newname) == TRUE)
2717 DestroyMenuSpecWidgets(curMenuSpec);
2718 curMenuSpec->menuWidget =
2719 CreateMenuWidget (pSD, NULL, curMenuSpec->name,
2720 pSD->screenTopLevelW, TRUE,
2728 * This section was changed to search the entire global menu list.
2729 * This was done to allow access to menu entries included using the
2730 * cci/.mwmrc interface. Before, only the actual root menu could
2731 * be modified; however, the user could still include commands in
2732 * other menus specified in the .mwmrc file using the f.cci command.
2736 MenuSpec *curMenuSpec;
2738 /* Search through all the global menu specs. */
2739 for (curMenuSpec = pSD->menuSpecs;
2740 curMenuSpec != (MenuSpec *) NULL;
2741 curMenuSpec = curMenuSpec->nextMenuSpec)
2743 if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2744 context, newname) == TRUE)
2746 DestroyMenuSpecWidgets(curMenuSpec);
2747 curMenuSpec->menuWidget =
2748 CreateMenuWidget (pSD, NULL, curMenuSpec->name,
2749 pSD->screenTopLevelW, TRUE,
2756 /* If we weren't passed a valid pCD, then just return. */
2757 if (pCD == (ClientData *) NULL) return;
2759 /* Search through the clients menu specs. If we find one that
2760 is global then stop search if we are ENABLING or DISABLING.
2761 If we are REMOVING and we find a global, we may need to
2762 perform some menu spec replacing to make the menu spec that
2763 needs modification local to the client. */
2764 for (curMenuSpec = pCD->systemMenuSpec;
2765 curMenuSpec != (MenuSpec *) NULL;
2766 curMenuSpec = curMenuSpec->nextMenuSpec)
2768 if (curMenuSpec->clientLocal == FALSE)
2770 MenuSpec *last, *cur;
2772 /* Find the last global menuspec in the clients list
2773 that needs to be changed and return it. Replace
2774 all menuspecs between the current one and the
2775 "last" one that needs changing. All the replaced
2776 menuspecs will be marked as local, so that next
2777 time clientLocal is FALSE in the enclosing for
2778 loop above, there will be no global menu specs
2779 needing changes. In other words, all the required
2780 menu spec replacing will occur the first time we
2781 find a global menu spec. */
2782 last = FindLastMenuSpecToModify(curMenuSpec, id);
2783 if (last != (MenuSpec *) NULL)
2785 MenuSpec *newMenuSpec = (MenuSpec *) NULL;
2786 MenuSpec *firstMenuSpec = (MenuSpec *) NULL;
2787 MenuSpec *lastMenuSpec = (MenuSpec *) NULL;
2789 /* Replace all the global menu specs with local
2791 for (cur = curMenuSpec;
2792 cur != (MenuSpec *) NULL && cur != last->nextMenuSpec;
2793 cur = cur->nextMenuSpec)
2795 newMenuSpec = ReplaceMenuSpecForClient(cur, pCD);
2796 if (cur == curMenuSpec)
2797 curMenuSpec = firstMenuSpec = newMenuSpec;
2798 /* If there is only one menu spec to change,
2799 the first will also be the last. */
2801 lastMenuSpec = newMenuSpec;
2804 /* Now that we have replaced all the menu specs,
2805 recreate all the widgets for the new menu specs. */
2806 for (cur = firstMenuSpec;
2807 cur != (MenuSpec *) NULL &&
2808 cur != lastMenuSpec->nextMenuSpec;
2809 cur = cur->nextMenuSpec)
2811 DestroyMenuSpecWidgets(newMenuSpec);
2812 newMenuSpec->menuWidget =
2813 CreateMenuWidget(pSD, pCD, newMenuSpec->name,
2814 pSD->screenTopLevelW,
2815 TRUE, newMenuSpec, NULL);
2819 /* None of the globals need changing. */
2822 if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2823 context, newname) == TRUE)
2825 DestroyMenuSpecWidgets(curMenuSpec);
2826 curMenuSpec->menuWidget =
2827 CreateMenuWidget (pSD, pCD, curMenuSpec->name,
2828 pSD->screenTopLevelW, TRUE,
2837 /*************************************<->*************************************
2839 * ModifyClientCommandTree (pSD, pCD, range, tree, modifier, context, newname)
2854 *************************************<->***********************************/
2855 void ModifyClientCommandTree (WmScreenData *pSD,
2859 CmdModifier modifier,
2866 /* Run through the top level of the tree. */
2867 for (curTree = tree; curTree != (CmdTree *) NULL; curTree = curTree->next)
2869 cmdID = curTree->commandID;
2870 ModifyClientCommandID(pSD, pCD, range, cmdID, modifier,
2872 if (curTree->subTrees != (CmdTree *) NULL)
2873 ModifyClientCommandTree(pSD, pCD, range, curTree->subTrees,
2874 modifier, context, newname);
2877 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
2879 /*************************************<->*************************************
2882 * AdjustPBs (menuSpec, pCD, newContext)
2887 * This procedure adjusts menu PushButton sensitivities and manage/unmanaged
2888 * status for a toplevel menu.
2893 * menuSpec = nonNULL toplevel menu specification with gadget
2895 * newContext = context that the menu is to be posted under.
2900 * menuSpec = menu specification with modifications
2901 * Return = TRUE iff at least one menu item changed manage status.
2906 * Adjusts PushButton sensitivity according to context and function type.
2907 * Manages/Unmanages PushButtons according to clientFunction resource.
2909 *************************************<->***********************************/
2910 static Boolean AdjustPBs (MenuSpec *menuSpec, ClientData *pCD,
2913 MenuButton *menuButton;
2919 Boolean fChangeManaged = FALSE;
2922 * Set PushButton sensitivity.
2923 * Set f.send_msg button sensitivity according to context and client
2924 * message list. Adjust other button sensitivities only for context.
2927 /* check for bad input value - shouldn't happen. */
2928 if (menuSpec == NULL) return (FALSE);
2930 for (n = 0, menuButton = menuSpec->menuButtons;
2931 n < menuSpec->menuButtonCount;
2934 menuItem = menuButton->menuItem;
2935 if (menuItem->wmFunction == F_Send_Msg)
2936 /* f.send_msg button: set according to context and message. */
2938 if ((newContext & menuItem->greyedContext) ||
2939 !(pCD && pCD->mwmMessagesCount && pCD->mwmMessages))
2940 /* insensitive context or empty client message list */
2942 XtSetSensitive (menuButton->buttonWidget, FALSE);
2946 * Have a context sensitive f.send_msg item and a client with a
2947 * nonempty message list. Set sensitive only if the message is
2948 * supported by this client. Otherwise set insensitive.
2951 msgc = pCD->mwmMessagesCount;
2952 pMsg = pCD->mwmMessages;
2955 /* scan nonempty message list */
2957 if (*pMsg == (long) menuItem->wmFuncArgs)
2963 pMsg++; /* next message in list */
2965 XtSetSensitive (menuButton->buttonWidget, fSupported);
2970 * Non f.send_msg button:
2971 * Adjust sensitivity according to context.
2972 * Manage/Unmanage according to clientFunction.
2975 if (menuSpec->currentContext & menuItem->greyedContext)
2976 /* button is currently insensitive */
2978 if (!(newContext & menuItem->greyedContext))
2979 /* insensitive -> sensitive */
2981 XtSetSensitive (menuButton->buttonWidget, TRUE);
2985 /* button is currently sensitive */
2987 if (newContext & menuItem->greyedContext)
2988 /* sensitive -> insensitive */
2990 XtSetSensitive (menuButton->buttonWidget, FALSE);
2994 if (menuItem->wmFunction == F_Remove)
2997 * Only allow remove from workspace if the client
2998 * is in more than one workspace
3000 fSupported = (pCD && (pCD->numInhabited > 1));
3001 XtSetSensitive (menuButton->buttonWidget, fSupported);
3005 if ((menuItem->mgtMask) && pCD)
3006 /* PushButton might not apply */
3009 if ((pCD->clientFunctions & menuItem->mgtMask & MWM_MGT_MASK) ||
3010 (pCD->dtwmFunctions & menuItem->mgtMask & DTWM_MGT_MASK))
3012 if (pCD->clientFunctions & menuItem->mgtMask)
3014 /* function applies -- manage it */
3016 if (!menuButton->managed)
3017 /* unmanaged -> managed */
3019 XtManageChild (menuButton->buttonWidget);
3020 menuButton->managed = TRUE;
3021 fChangeManaged = TRUE;
3022 if (n == menuSpec->menuButtonCount - 1)
3025 * last item, if it has a separator before
3026 * it, manage the separator
3029 CheckTerminalSeparator(menuSpec,
3030 menuButton->buttonWidget,
3036 /* function does not apply -- unmanage it */
3038 if (menuButton->managed)
3039 /* managed -> unmanaged */
3041 XtUnmanageChild (menuButton->buttonWidget);
3042 menuButton->managed = FALSE;
3043 fChangeManaged = TRUE;
3045 if (n == menuSpec->menuButtonCount - 1)
3048 * last item, if it has a separator before
3049 * it, unmanage the separator
3051 CheckTerminalSeparator(menuSpec,
3052 menuButton->buttonWidget,
3059 else if (!menuButton->managed)
3060 /* unmanaged PushButton applies */
3062 XtManageChild (menuButton->buttonWidget);
3063 menuButton->managed = TRUE;
3064 fChangeManaged = TRUE;
3069 return (fChangeManaged);
3071 } /* END OF FUNCTION AdjustPBs */
3075 /*************************************<->*************************************
3078 * SavePBInfo (topMenuSpec, menuItem, itemW)
3083 * Fills a MenuButton structure for a PushButton.
3084 * If necessary, mallocs or reallocs the menuButtons array in the toplevel
3090 * topMenuSpec = pointer to toplevel MenuSpec structure
3091 * menuItem = pointer to PushButton MenuItem structure
3092 * itemW = PushButton gadget
3093 * topMenuSpec->menuButtons[]
3094 * topMenuSpec->menuButtonSize
3095 * topMenuSpec->menuButtonCount
3100 * Return = FALSE iff insufficient memory for malloc or realloc
3101 * or bad input value forces exit.
3102 * topMenuSpec->menuButtons[]
3103 * topMenuSpec->menuButtonSize
3104 * topMenuSpec->menuButtonCount
3109 * The initial managed status of PushButtons is TRUE.
3111 *************************************<->***********************************/
3112 static Boolean SavePBInfo (MenuSpec *topMenuSpec, MenuItem *menuItem,
3115 MenuButton *menuButton;
3118 /* check for bad input value - shouldn't happen. */
3119 if (topMenuSpec == NULL) return (FALSE);
3121 if (topMenuSpec->menuButtonSize == 0)
3122 /* need to create array */
3124 topMenuSpec->menuButtonSize = MENU_BUTTON_INC;
3125 topMenuSpec->menuButtons =
3126 (MenuButton *) XtMalloc (MENU_BUTTON_INC * sizeof(MenuButton));
3128 else if (topMenuSpec->menuButtonCount == topMenuSpec->menuButtonSize)
3129 /* need larger array */
3131 topMenuSpec->menuButtonSize += MENU_BUTTON_INC;
3132 topMenuSpec->menuButtons = (MenuButton *)
3133 XtRealloc ((char*)topMenuSpec->menuButtons,
3134 topMenuSpec->menuButtonSize * sizeof(MenuButton));
3137 if (topMenuSpec->menuButtons == NULL)
3138 /* insufficent memory */
3140 topMenuSpec->menuButtonSize = 0;
3141 topMenuSpec->menuButtonCount = 0;
3145 menuButton = &(topMenuSpec->menuButtons[topMenuSpec->menuButtonCount]);
3146 topMenuSpec->menuButtonCount++;
3148 menuButton->menuItem = menuItem;
3149 menuButton->buttonWidget = itemW;
3150 menuButton->managed = TRUE;
3157 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3158 /*************************************<->*************************************
3160 * AdjustTearOffControl (cascade, closure, cbackdata)
3174 * returns true iff the tearoff control was enabled or diabled
3175 * resulting in a change in height.
3180 *************************************<->***********************************/
3182 AdjustTearOffControl (Widget cascade,
3184 XtPointer cbackdata)
3186 Widget submenu = (Widget) closure;
3189 unsigned char tearoff_model;
3193 XtSetArg(args[argn], XmNtearOffModel, &tearoff_model); ++argn;
3194 XtGetValues(submenu, args, argn);
3196 /* Is this a root menu or a cascade of a root menu? */
3197 /* If cbackdata is not null, then we got here by cascading. */
3198 /* Cascade menus from a tearoff menu-pane are not allowed. */
3199 /* there is no way to know if the cascade is from a tearoff */
3200 /* or from a cascade on a system menu. */
3201 if ((wmGD.menuClient == NULL) && (cbackdata == NULL))
3206 if ((tearoff_model == XmTEAR_OFF_ENABLED) && !isMwmMenu)
3208 PRINT("Disabling the tear off\n");
3210 XtSetArg(args[argn], XmNtearOffModel, XmTEAR_OFF_DISABLED); ++argn;
3211 XtSetValues(submenu, args, argn);
3216 /* If this was invoked as a cascadingCallback and not by hand and if
3217 the menuActive field of the global data has not yet been set, then
3218 we can safely assume that we have just cascaded off of a torn off
3219 menu. In that case, set the menuActive field to be the menu spec of
3220 the torn off menu and register an unmap callback on the cascaded
3221 menu that will clear the menuActive field. */
3222 if (cbackdata != (XtPointer) NULL && wmGD.menuActive == (MenuSpec *) NULL)
3225 Widget tearoff_widget = XtParent(cascade);
3227 for (menuspec = wmGD.Screens[0].menuSpecs;
3228 menuspec != (MenuSpec *) NULL;
3229 menuspec = menuspec->nextMenuSpec)
3231 if (tearoff_widget == menuspec->menuWidget)
3233 wmGD.menuActive = menuspec;
3238 /* If we can't find a menuspec for the torn off menu, then just
3239 take the first menu spec in the list of menuSpecs for the
3240 active pSD. NOTE: THIS SHOULD NEVER HAPPEN. In fact if it
3241 does, I'm not sure how mwm will behave having been given
3242 the wrong menu spec as the active menu. */
3243 if (wmGD.menuActive == (MenuSpec *) NULL)
3245 wmGD.menuActive = ACTIVE_PSD->menuSpecs;
3246 PRINT("Couldn't find menu spec for tear off\n");
3249 /* Add a callback that will clear menuActive when this cascade
3252 XtAddCallback (submenu, XmNunmapCallback,
3254 XtAddCallback (XtParent(submenu), XmNpopdownCallback,
3256 UnmapPulldownCallback,
3264 /*************************************<->*************************************
3267 * CreateClientCommandSeparator (menuItem, child_position, last_child,
3284 *************************************<->***********************************/
3285 static Boolean CreateClientCommandSeparator (MenuItem *menuItem,
3288 MenuItem **newMenuItem)
3290 MenuItem *curMenuItem;
3292 /* If it is a client command, then we only want to create the
3293 * separator under particular circumstances. Specifically, we
3294 * want to make sure that:
3295 * 1. a separator doesn't directly precede this one
3296 * 2. a separator doesn't directly follow this one
3297 * 3. this separator won't be the first or last item in the menu
3298 * 4. the client command that this separator surrounds actually
3299 * matched something and is not an unmatched template
3302 /* Check if a separator directly precedes this one. */
3303 if (child_position > 0 && last_child != (Widget) NULL &&
3304 XmIsSeparator(last_child))
3307 /* Check if a separator directly follows this one. */
3308 if (menuItem->nextMenuItem != (MenuItem *) NULL &&
3309 menuItem->nextMenuItem->wmFunction == F_Separator &&
3310 IsClientCommand(menuItem->nextMenuItem->label) == FALSE)
3313 /* Make sure this separator won't be the first item in the menu. */
3314 if (child_position == 0) return(FALSE);
3316 /* Make sure this separator won't be the last item in the menu. */
3317 if (menuItem->nextMenuItem == (MenuItem *) NULL)
3320 /* Make sure that the client command this separator surrounds actually
3321 matches something. We only do this check if the separator is the
3322 TOP separator in the separator pair. If we are looking at a bottom
3323 separator then we can safely assume something matched, otherwise
3324 we would have passed over it when we look at the corresponding top
3326 if (menuItem->labelType == TOP_SEPARATOR)
3328 /* If we find a real menu item (not just a template) before we find
3329 a bottom separator, then create the separator. */
3330 for (curMenuItem = menuItem;
3331 curMenuItem != (MenuItem *) NULL;
3332 curMenuItem = curMenuItem->nextMenuItem)
3334 /* If we found the closing separator, then return FALSE and
3335 our new menu item position. */
3336 if (curMenuItem->wmFunction == F_Separator &&
3337 IsClientCommand(curMenuItem->label) &&
3338 curMenuItem->labelType == BOTTOM_SEPARATOR)
3340 *newMenuItem = curMenuItem;
3343 /* If we found a real menu item, then return TRUE. */
3344 if (curMenuItem->wmFunction != F_Separator &&
3345 !IsClientCommand(curMenuItem->label))
3350 /* If by some bizarre chance we get to the end of the list
3351 without finding either, then return FALSE. Something is wrong. */
3352 if (curMenuItem == (MenuItem *) NULL) return(FALSE);
3355 /* Well, nothing failed so let's create it. */
3358 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3360 /*************************************<->*************************************
3362 * CreateMenuWidget (pSD, menuName, parent, fTopLevelPane, topMenuSpec,
3368 * Creates a MenuShell as a child of the specified parent widget, and a
3369 * PopupMenu or PulldownMenu as a child of the shell. Fill the menu with
3370 * the named menupane items.
3375 * pSD ---------- pointer to screen data
3376 * menuName ----- the name of the menu specification to be used to create
3378 * parent -------- parent of popup shell
3379 * fTopLevelPane - TRUE iff the menupane is a top level one
3380 * topMenuSpec --- pointer to the top menu specification.
3381 * moreMenuItems - pointer to additional menu items for custom menu.
3386 * Return = created PopupMenu or PulldownMenu widget, or NULL.
3391 * We attach a popdowncallback to the menu to set wmGD.menuActive to NULL,
3392 * allowing us to not dispatch key events separately from the toolkit
3395 *************************************<->***********************************/
3397 typedef struct _StrList
3400 struct _StrList *next;
3403 Widget CreateMenuWidget (WmScreenData *pSD,
3404 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3406 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3407 String menuName, Widget parent,
3408 Boolean fTopLevelPane, MenuSpec *topMenuSpec,
3409 MenuItem *moreMenuItems)
3414 MenuSpec *menuSpec = (MenuSpec *)NULL;
3419 Widget children[CHILDREN_CACHE];
3421 KeySpec *accelKeySpec;
3422 Dimension menuHeight;
3423 Boolean fUseTitleSep = False;
3424 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3425 Boolean labelIsClientCommand = False;
3426 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3427 StrList *stringsToFree = NULL, *sPtr;
3434 /* check for bad input values. */
3435 if ((menuName == NULL) || (pSD == NULL))
3441 * Find the menu pane specifications for menuName.
3442 * The top-level menu specification is passed as an argument (it may
3443 * be custom). A submenu specification must be found and might not exist.
3444 * Return NULL if a submenu specification is not found.
3448 menuSpec = topMenuSpec;
3452 menuSpec = pSD->menuSpecs;
3455 if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
3457 break; /* found menuName's specification */
3459 menuSpec = menuSpec->nextMenuSpec; /* keep looking */
3463 if (menuSpec == NULL)
3464 /* (submenu) specification not found */
3466 MWarning(((char *)GETMESSAGE(48, 4, "Menu specification %s not found\n")), menuName);
3471 * If menuSpec is marked, we have menu recursion => fail.
3472 * Otherwise, mark it.
3475 if (menuSpec->currentContext & CR_MENU_MARK) /* marked? */
3476 /* menu recursion */
3478 MWarning(((char *)GETMESSAGE(48, 5, "Menu recursion detected for %s\n")), menuName);
3481 menuSpec->currentContext |= CR_MENU_MARK; /* no, mark it */
3484 * Create a PopupShell widget.
3485 * If the parent of the specified parent ("grandparent") is a MenuShell
3486 * widget, then use the grandparent as the parent of the PopupShell.
3487 * Otherwise, use the specified parent.
3490 XtSetArg (args[i], XmNwidth, (XtArgVal) 5); i++;
3491 XtSetArg (args[i], XmNheight, (XtArgVal) 5); i++;
3492 XtSetArg (args[i], XmNallowShellResize, (XtArgVal) TRUE); i++;
3493 XtSetArg (args[i], XtNoverrideRedirect, (XtArgVal) TRUE); i++;
3494 XtSetArg (args[i], XtNdepth,
3495 (XtArgVal) DefaultDepth(XtDisplay(parent), pSD->screen)); i++;
3496 XtSetArg (args[i], XtNscreen,
3497 (XtArgVal) ScreenOfDisplay(XtDisplay(parent), pSD->screen)); i++;
3499 if ((XtParent (parent) != NULL) && XmIsMenuShell (XtParent (parent)))
3501 parent = XtParent (parent);
3504 menuShellW = XtCreatePopupShell (SHELL_NAME, xmMenuShellWidgetClass,
3505 parent, (ArgList) args, i);
3508 * Create a RowColumn widget as a child of the shell for the menu pane.
3509 * If the menu pane is top-level, create a popup menu for it and attach
3510 * the unmap callback to it.
3511 * Otherwise, create a pulldown menu for it.
3515 XtSetArg (args[i], XmNborderWidth, (XtArgVal) 0); i++;
3516 XtSetArg (args[i], XmNwhichButton, (XtArgVal) SELECT_BUTTON); i++;
3517 XtSetArg (args[i], XmNadjustMargin, (XtArgVal) TRUE); i++;
3521 XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_POPUP); i++;
3522 XtSetArg (args[i], XmNpopupEnabled, (XtArgVal) TRUE); i++;
3523 menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
3525 XtAddCallback (menuW, XmNunmapCallback, UnmapCallback,
3526 (XtPointer) menuSpec);
3530 XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_PULLDOWN); i++;
3531 menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
3536 * Create the specified menu entries as children of the menupane.
3537 * Menus may contain the following widgets:
3544 * Add separator gadgets around menu titles.
3547 XtSetArg (sepArgs[0], XmNseparatorType, (XtArgVal) XmDOUBLE_LINE);
3550 menuItem = menuSpec->menuItems;
3551 if ((menuItem == NULL) && (moreMenuItems != NULL))
3552 /* handle custom menu with empty standard specification */
3554 menuSpec->menuItems = menuItem = moreMenuItems;
3555 moreMenuItems = NULL;
3561 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3562 labelIsClientCommand = IsClientCommand(menuItem->label);
3563 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3565 if (menuItem->wmFunction == F_Separator)
3567 * Add a Separator gadget for a menu separator.
3568 * An immediately following title will not have a top separator.
3571 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3572 /* If it is a client command, then we only want to create the
3573 * separator under particular circumstances. Specifically, we
3574 * want to make sure that:
3575 * 1. a separator doesn't directly precede this one
3576 * 2. a separator doesn't directly follow this one
3577 * 3. this separator won't be the first or last item in the menu
3579 if (labelIsClientCommand)
3581 if (CreateClientCommandSeparator(menuItem, n,
3582 (n > 0 ? children[n - 1] :
3586 /* Increment the counter here because we only increment
3587 at the end of the loop if the item is not a client
3588 command item (i.e. labelIsClientCommand == FALSE) */
3590 XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3592 fUseTitleSep = FALSE;
3596 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3599 XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3601 fUseTitleSep = FALSE;
3606 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3607 if (!labelIsClientCommand)
3608 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3610 * We will use one of:
3618 * Construct the label
3620 if ((menuItem->labelType == XmPIXMAP) &&
3622 MakeCachedLabelPixmap (pSD, menuW,
3623 menuItem->labelBitmapIndex)))
3625 XtSetArg (args[i], XmNlabelType, (XtArgVal) XmPIXMAP); i++;
3626 XtSetArg (args[i], XmNlabelPixmap, (XtArgVal) labelPixmap); i++;
3627 XtSetArg (args[i], XmNlabelInsensitivePixmap,
3628 (XtArgVal) labelPixmap); i++;
3632 XtSetArg (args[i], XmNlabelType, (XtArgVal) XmSTRING); i++;
3633 XtSetArg (args[i], XmNlabelString, (XtArgVal)
3634 (tmpstr = XmStringCreateLocalized(menuItem->label))); i++;
3635 sPtr = (StrList *) XtMalloc(sizeof(StrList));
3638 MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
3643 sPtr->string = tmpstr;
3644 sPtr->next = stringsToFree;
3645 stringsToFree = sPtr;
3649 if (menuItem->wmFunction == F_Title)
3651 * Add a centered Label gadget for a menu title.
3652 * Include separators above and below the title.
3653 * Don't include the top one if the title is the first pane item
3654 * or immediately follows a user-supplied separator.
3660 XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3664 XtSetArg (args[i], XmNalignment, XmALIGNMENT_CENTER); i++;
3665 children[n] = XmCreateLabelGadget (menuW, TITLE_NAME,
3666 (ArgList) args, i); n++;
3667 children[n] = XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3671 * A following title will have both separators.
3674 fUseTitleSep = TRUE;
3679 * We will use one of:
3684 * Both support mnemonics; only PushButtons support accelerators.
3688 * Align text on the left.
3689 * Set any mnemonic text.
3691 XtSetArg (args[i], XmNalignment, XmALIGNMENT_BEGINNING); i++;
3693 if (menuItem->mnemonic)
3695 XtSetArg (args[i], XmNmnemonic,
3696 (XtArgVal) menuItem->mnemonic); i++;
3699 if (menuItem->wmFunction == F_Menu)
3701 * Create a PopupShell and PulldownMenu for a submenu (the
3702 * menushells are linked together).
3703 * Create a CascadeButton Widget
3704 * The submenu widget is attached to the CascadeButton gadget
3705 * using the subMenuId resource.
3706 * Make the CascadeButton insensitive if the submenu cannot be
3710 subMenuW = CREATE_MENU_WIDGET (pSD, pCD,
3711 menuItem->wmFuncArgs, menuW,
3716 * Attach submenu to cascade button.
3719 XtSetArg (args[i], XmNsubMenuId, (XtArgVal) subMenuW);
3721 children[n] = XmCreateCascadeButtonGadget (menuW,
3722 CASCADE_BTN_NAME, (ArgList) args, i);
3723 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3724 XtAddCallback(children[n], XmNcascadingCallback,
3725 (XtCallbackProc)AdjustTearOffControl,
3726 (XtPointer)subMenuW);
3727 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3731 * Unable to create submenupane: make the entry insensitive.
3734 children[n] = XmCreateCascadeButtonGadget (menuW,
3735 CASCADE_BTN_NAME, (ArgList) args, i);
3736 XtSetSensitive (children[n], FALSE);
3740 * A following title will have both separators.
3743 fUseTitleSep = TRUE;
3748 * Create a PushButton gadget.
3752 * If an accelerator is specified, set acceleratorText,
3753 * then create an accelerator KeySpec and insert it at the
3754 * head of the toplevel MenuSpec's list.
3756 if (menuItem->accelText)
3758 XtSetArg (args[i], XmNacceleratorText, (XtArgVal)
3759 (tmpstr = XmStringCreateLocalized(menuItem->accelText))); i++;
3760 sPtr = (StrList *) XtMalloc(sizeof(StrList));
3763 MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
3768 sPtr->string = tmpstr;
3769 sPtr->next = stringsToFree;
3770 stringsToFree = sPtr;
3773 if ((accelKeySpec = (KeySpec *)
3774 XtMalloc (sizeof (KeySpec ))) == NULL)
3775 /* Handle insufficent memory */
3777 MWarning (((char *)GETMESSAGE(48, 6, "Insufficient memory for menu %s\n")),
3779 menuSpec->currentContext &= ~CR_MENU_MARK;
3783 accelKeySpec->state = menuItem->accelState;
3784 accelKeySpec->keycode = menuItem->accelKeyCode;
3785 accelKeySpec->context = topMenuSpec->accelContext;
3786 accelKeySpec->subContext = 0;
3787 accelKeySpec->wmFunction = menuItem->wmFunction;
3788 accelKeySpec->wmFuncArgs = menuItem->wmFuncArgs;
3789 accelKeySpec->nextKeySpec = topMenuSpec->accelKeySpecs;
3790 topMenuSpec->accelKeySpecs = accelKeySpec;
3793 children[n] = XmCreatePushButtonGadget (menuW,
3794 PUSH_BTN_NAME, (ArgList) args, i);
3797 * Set sensitivity. Initially we only consider the context
3798 * of the top level menupane.
3801 if (menuItem->greyedContext & topMenuSpec->currentContext)
3802 /* insensitive button in this context*/
3804 XtSetSensitive (children[n], FALSE);
3807 /* sensitive button in this context*/
3809 XtSetSensitive (children[n], TRUE);
3813 * If necessary, fill a menuButtons element for this
3814 * PushButton. Malloc or Realloc the array if necessary.
3816 if ((menuItem->greyedContext) || (menuItem->mgtMask))
3818 if (!SavePBInfo (topMenuSpec, menuItem, children[n]))
3820 MWarning(((char *)GETMESSAGE(48, 7, "Insufficient memory for menu %s\n")),
3822 menuSpec->currentContext &= ~CR_MENU_MARK;
3828 * Set up the function callback.
3829 * A following title will have both separators.
3832 XtAddCallback (children[n], XmNactivateCallback,
3833 (XtCallbackProc)ActivateCallback,
3834 (XtPointer) menuItem);
3836 fUseTitleSep = TRUE;
3842 * Increment the children array count if we actually
3843 * created a new child.
3845 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3846 if (!labelIsClientCommand)
3847 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3851 * Next menu item: handle custom items and full children[].
3853 menuItem = menuItem->nextMenuItem;
3854 if ((menuItem == NULL) && (moreMenuItems != NULL))
3856 menuSpec->menuItems = menuItem = moreMenuItems;
3857 moreMenuItems = NULL;
3859 if (n >= CHILDREN_CACHE - 2) /* leave room for title separators */
3861 XtManageChildren (children, n);
3868 XtManageChildren (children, n);
3872 * Get the initial height of the top level menu pane shell.
3873 * The actual height will change according to clientFunctions.
3878 XtSetArg (args[i], XtNheight, &menuHeight); i++;
3879 XtGetValues (menuW, (ArgList)args, i);
3880 topMenuSpec->height = (unsigned int) menuHeight;
3885 * Check if the menu that's been created is higher than the screen.
3886 * If it is, force it to wrap. Taken straight from the 1.1 fix.
3890 XtSetArg (args[i], XtNheight, &menuHeight); i++;
3891 XtGetValues (menuW, (ArgList)args, i);
3892 scr = XtScreen (menuW);
3893 if (menuHeight > (Dimension)scr->height) {
3895 XtSetArg (args[i], XmNresizeHeight, (XtArgVal) FALSE); i++;
3896 XtSetArg (args[i], XmNpacking, (XtArgVal) XmPACK_TIGHT); i++;
3897 XtSetArg (args[i], XmNorientation, (XtArgVal) XmVERTICAL); i++;
3898 XtSetArg (args[i], XmNheight, scr->height); i++;
3899 XtSetValues (menuW, (ArgList)args, i);
3901 #endif /* IBM_151913 */
3903 /* free the string that may have been created earlier. */
3904 for (sPtr = stringsToFree; sPtr != NULL; )
3906 stringsToFree = stringsToFree->next;
3907 XmStringFree(sPtr->string);
3908 XtFree((char *)sPtr);
3909 sPtr = stringsToFree;
3913 /* Unmark the menu specification and return. */
3914 menuSpec->currentContext &= ~CR_MENU_MARK;
3917 } /* END OF FUNCTION CreateMenuWidget */
3921 /*************************************<->*************************************
3923 * PostMenu (menuSpec, pCD, x, y, button, newContext, flags, passedInEvent)
3928 * This function is used to post a menu at a particular location.
3933 * menuSpec = menu specification
3935 * x,y = position to post the menu if (flags & POST_AT_XY) set
3936 * button = button number posting the menu or NoButton (WmGlobal.h) if
3938 * newContext = context that the menu is to be posted under.
3939 * flags = POST_AT_XY bit set iff x,y are valid, else compute from pCD
3940 * POST_TRAVERSAL_ON bit set if set traversal on
3944 * menuSpec = menu specification with modifications
3945 * wmGD.menuClient = pCD
3946 * wmGD.menuActive = menuSpec
3951 * Accepts x,y only if POST_AT_XY flag bit set. Otherwise, computes from pCD.
3952 * Adjusts PushButton sensitivity according to context and function type.
3953 * Manages/Unmanages PushButtons according to clientFunction resource.
3954 * Sets traversal on if button==NoButton or POST_TRAVERSAL_ON flag bit set.
3956 *************************************<->***********************************/
3958 void PostMenu (MenuSpec *menuSpec, ClientData *pCD, int x, int y, unsigned int button, Context newContext, long flags, XEvent *passedInEvent)
3962 unsigned int whichButton;
3963 Dimension menuHeight;
3964 XButtonPressedEvent event;
3966 Display *saveDisplay;
3967 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3968 Boolean menuAdjusted;
3969 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3971 if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
3978 * Don't post a menu from an icon in the iconbox if the
3979 * icon is not visible
3981 if((newContext == F_SUBCONTEXT_IB_WICON ||
3982 newContext == F_SUBCONTEXT_IB_IICON) &&
3983 (!(IconVisible(pCD))))
3989 * Set grabContext to be used in GrabWin when no event is passed
3993 wmGD.grabContext = newContext;
3996 * Adjust PushButton sensitivity and manage/unmanage status.
3997 * If the manage status of the system menu has changed,
3998 * then get the height of the top level menu pane shell and
3999 * cache it in its MenuSpec.
4002 * Adjust the tear off control. If we are posting this menu from
4003 * a client then force the tear off to be disabled. NOTE: This must
4004 * be done after wmGD.menuClient has been set.
4005 * Since turning off the tear-off control could result in a height
4006 * change, we may need to remeasure things. (CR 9316)
4010 if(pCD && pCD->clientFlags & ICON_BOX)
4012 newContext |= F_CONTEXT_ICONBOX;
4017 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
4019 AdjustTearOffControl(NULL, (XtPointer) (menuSpec->menuWidget), NULL);
4020 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
4021 if (AdjustPBs (menuSpec, pCD, newContext)
4022 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
4024 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
4028 XtSetArg (args[i], XtNheight, &menuHeight); i++;
4029 XtGetValues (menuSpec->menuWidget, (ArgList)args, i);
4030 menuSpec->height = (unsigned int) menuHeight;
4032 menuSpec->currentContext = newContext;
4035 * Adjust the whichButton resource if necessary.
4036 * Use SELECT_BUTTON for NoButton.
4039 whichButton = (button == NoButton) ? SELECT_BUTTON : button;
4040 if (whichButton != menuSpec->whichButton)
4043 XtSetArg (args[i], XmNwhichButton, (XtArgVal) whichButton); i++;
4044 XtSetValues (menuSpec->menuWidget, args, i);
4045 menuSpec->whichButton = whichButton;
4049 * Determine the position of the popup menu.
4050 * Compute position if necessary (system menu).
4053 if (!(flags & POST_AT_XY))
4054 /* compute the position */
4056 GetSystemMenuPosition (pCD, &x, &y, menuSpec->height, newContext);
4061 XmMenuPosition (menuSpec->menuWidget, &event);
4063 wmGD.menuClient = pCD;
4064 wmGD.menuActive = menuSpec; /* set to NULL within UnmapCallback() */
4067 * Post the menu by managing its top-level RowColumn.
4069 * First dispatch the event to set the time stamp in the toolkit
4074 saveWindow = passedInEvent->xany.window;
4075 saveDisplay = passedInEvent->xany.display;
4076 passedInEvent->xany.window = 0;
4077 passedInEvent->xany.display = XtDisplay(menuSpec->menuWidget);
4079 XtDispatchEvent(passedInEvent);
4080 passedInEvent->xany.window = saveWindow;
4081 passedInEvent->xany.display = saveDisplay;
4083 /* If menu posted by ButtonPress/ButtonRelease, release grabs. */
4084 if ((passedInEvent->type == ButtonPress) ||
4085 (passedInEvent->type == ButtonRelease))
4086 XUngrabPointer(passedInEvent->xany.display,
4087 passedInEvent->xbutton.time);
4090 #ifndef ALTERNATE_POSTMENU
4092 XtManageChild (menuSpec->menuWidget);
4095 if (flags & POST_STICKY)
4097 _XmSetPopupMenuClick(menuSpec->menuWidget, True);
4101 _XmSetPopupMenuClick(menuSpec->menuWidget, False);
4105 * Post the menu by calling the convenience routine that verifies
4106 * the button event, updates the Xt timestamp, and finally manages
4110 _XmPostPopupMenu( menuSpec->menuWidget, passedInEvent);
4115 * set the traversal state.
4118 if ((button == NoButton) || (flags & POST_TRAVERSAL_ON))
4119 /* turn traversal on */
4121 TraversalOn (menuSpec);
4124 /* turn traversal off */
4126 TraversalOff (menuSpec);
4129 } /* END OF FUNCTION PostMenu */
4133 /*************************************<->*************************************
4135 * UnpostMenu (menuSpec)
4140 * This function is used to unpost a menu.
4145 * menuSpec = menu specification
4154 * wmGD.menuActive and wmGD.menuUnpostKey are set to NULL within
4157 *************************************<->***********************************/
4159 void UnpostMenu (MenuSpec *menuSpec)
4161 if (menuSpec && (menuSpec->menuWidget))
4163 * Unpost the menu by unmanaging its top-level RowColumn.
4166 XtUnmanageChild (menuSpec->menuWidget);
4167 #ifndef OLD_COLORMAP
4168 ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
4172 } /* END OF FUNCTION UnpostMenu */
4176 /*************************************<->*************************************
4178 * ActivateCallback (w, client_data, call_data)
4183 * This function is called whenever a menu item is selected.
4188 * w = menubuttonWidget
4189 * client_data = pointer to menu button's MenuItem structure
4190 * call_data = not used
4191 * wmGD.menuClient = pointer to client's ClientData structure
4203 *************************************<->***********************************/
4205 void ActivateCallback (Widget w, caddr_t client_data, caddr_t call_data)
4209 /* set active screen */
4210 pSD = GetScreenForWindow (XtWindow(w));
4211 if (pSD) SetActiveScreen (pSD);
4213 ((MenuItem *)client_data)->wmFunction (
4214 ((MenuItem *)client_data)->wmFuncArgs, wmGD.menuClient, NULL);
4216 } /* END OF FUNCTION ActivateCallback */
4220 /*************************************<->*************************************
4222 * UnmapCallback (w, client_data, call_data)
4227 * This function is called whenever a toplevel RowColumn is unmapped.
4233 * client_data = not used
4234 * call_data = not used
4235 * wmGD.gadgetClient = last client with depressed client
4240 * wmGD.menuActive = NULL
4241 * wmGD.menuUnpostKeySpec = NULL
4242 * wmGD.checkHotspot = FALSE
4249 *************************************<->***********************************/
4251 static void UnmapCallback (Widget w, XtPointer client_data,
4252 XtPointer call_data)
4254 wmGD.menuActive = NULL;
4255 wmGD.menuUnpostKeySpec = NULL;
4256 wmGD.checkHotspot = FALSE;
4258 if (wmGD.gadgetClient)
4260 PopGadgetOut(wmGD.gadgetClient, FRAME_SYSTEM);
4263 #ifndef OLD_COLORMAP
4264 ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
4266 PullExposureEvents();
4268 } /* END OF FUNCTION UnmapCallback */
4271 /*************************************<->*************************************
4273 * MWarning (message)
4278 * This function lists a message to stderr.
4283 * format = pointer to a format string
4284 * message = pointer to a message string
4286 *************************************<->***********************************/
4288 void MWarning (char *format, char *message)
4291 if (strlen(format) + strlen(message) < (size_t) MAXWMPATH)
4293 char pch[MAXWMPATH+1];
4295 sprintf (pch, format, message);
4299 } /* END OF FUNCTION MWarning */
4303 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
4304 /*************************************<->*************************************
4306 * UnmapPulldownCallback (w, client_data, call_data)
4322 *************************************<->***********************************/
4324 static void UnmapPulldownCallback (Widget w, XtPointer client_data,
4325 XtPointer call_data)
4327 wmGD.menuActive = (MenuSpec *) NULL;
4328 } /* END OF FUNCTION UnmapPulldownCallback */
4329 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
4332 /*************************************<->*************************************
4334 * TraversalOff (menuSpec)
4339 * This function turns menu traversal off.
4344 * menuSpec = menu specification
4356 *************************************<->***********************************/
4358 void TraversalOff (MenuSpec *menuSpec)
4360 if (menuSpec && (menuSpec->menuWidget))
4362 /* function pointer */
4363 (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
4364 ->row_column_class.menuProcedures)
4366 (XmMENU_TRAVERSAL, menuSpec->menuWidget, False, NULL, NULL);
4369 } /* END OF FUNCTION TraversalOff */
4373 /*************************************<->*************************************
4375 * TraversalOn (menuSpec)
4380 * This function turns menu traversal on.
4385 * menuSpec = menu specification
4397 *************************************<->***********************************/
4399 void TraversalOn (MenuSpec *menuSpec)
4402 if (menuSpec && (menuSpec->menuWidget))
4404 /* function pointer */
4405 (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
4406 ->row_column_class.menuProcedures)
4408 (XmMENU_TRAVERSAL, menuSpec->menuWidget, True, NULL, NULL);
4411 } /* END OF FUNCTION TraversalOn */
4415 /*************************************<->*************************************
4417 * FreeCustomMenuSpec (menuSpec)
4422 * This procedure destroys a custom MenuSpec structure and its associated
4423 * menu widget, menuItems list, menuButtons array, and menu accelerator list.
4428 * menuSpec = MenuSpec structure
4438 * Assumes that a MenuSpec is custom iff its name is NULL.
4440 * Assumes that ParseWmFuncStr() has parsed a menu item's function
4441 * argument only for F_Exec and F_Menu. If it is used for other functions,
4442 * be sure to include them here!
4444 *************************************<->***********************************/
4446 void FreeCustomMenuSpec (MenuSpec *menuSpec)
4449 MenuItem *nextMenuItem;
4450 KeySpec *accelKeySpec;
4451 KeySpec *nextAccelKeySpec;
4453 if ((menuSpec == NULL) || (menuSpec->name != NULL))
4454 /* we only destroy custom menus! */
4460 * Fix for CR 5450 - If the custom menu is the same as wmGD.menuActive, call
4461 * the UnmapCallback directly to clean things up. Since
4462 * the menu is going to be destroyed, this callback will
4463 * not get called, leaving MWM in a failure state.
4465 if (wmGD.menuActive == menuSpec)
4466 UnmapCallback((Widget)NULL, (caddr_t)NULL, (caddr_t)NULL);
4468 * End fix for CR 5450
4471 menuItem = menuSpec->menuItems;
4474 nextMenuItem = menuItem->nextMenuItem;
4475 FreeMenuItem (menuItem);
4476 menuItem = nextMenuItem;
4479 if (menuSpec->menuButtons)
4481 XtFree ((char *)menuSpec->menuButtons);
4484 accelKeySpec = menuSpec->accelKeySpecs;
4485 while (accelKeySpec)
4487 nextAccelKeySpec = accelKeySpec->nextKeySpec;
4488 XtFree ((char *)accelKeySpec);
4489 accelKeySpec = nextAccelKeySpec;
4492 if (menuSpec->menuWidget)
4493 /* destroy all children of the menu's MenuShell parent */
4495 XtDestroyWidget (XtParent(menuSpec->menuWidget));
4498 XtFree ((char *)menuSpec);
4500 } /* END OF FUNCTION FreeCustomMenuSpec */