2 * (c) Copyright 1989, 1990, 1991, 1992, 1993 OPEN SOFTWARE FOUNDATION, INC.
10 static char rcsid[] = "$XConsortium: WmMenu.c /main/15 1996/11/20 15:20:17 rswiston $"
14 * (c) Copyright 1987, 1988, 1989, 1990 HEWLETT-PACKARD COMPANY */
16 * (c) Copyright 1987, 1988 DIGITAL EQUIPMENT CORPORATION */
18 * (c) Copyright 1988 MASSACHUSETTS INSTITUTE OF TECHNOLOGY */
26 #include "WmResource.h"
27 #include "WmResParse.h"
28 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
30 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
33 #include <X11/Shell.h>
37 #include <Xm/CascadeB.h>
38 #include <Xm/CascadeBG.h>
40 #include <Xm/LabelG.h>
41 #include <Xm/MenuShell.h>
43 #include <Xm/PushBG.h>
44 #include <Xm/RowColumn.h>
45 #include <Xm/RowColumnP.h>
46 #include <Xm/Separator.h>
47 #include <Xm/SeparatoG.h>
49 #define SHELL_NAME "menu"
50 #define SEPARATOR_NAME "separator"
51 #define TITLE_NAME "title_name"
52 #define CASCADE_BTN_NAME "cascadebutton"
53 #define PUSH_BTN_NAME "pushbutton"
55 #define CHILDREN_CACHE 22
56 #define MENU_BUTTON_INC 5
59 * include extern functions
63 #include "WmColormap.h"
65 #include "WmFunction.h"
66 #include "WmIconBox.h"
70 #include "WmWrkspace.h"
74 static void UnmapCallback (Widget w, XtPointer client_data,
76 static MenuItem *DuplicateMenuItems (MenuItem *menuItems);
78 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
79 static MenuExclusion *DuplicateMenuExclusions(MenuExclusion *exclusions);
80 static Boolean FindClientCommandMatch (MenuSpec *menuSpec,
83 static void InsertTreeOnClient (WmScreenData *pSD, ClientData *pCD,
85 MatchList **client_match_list,
86 MatchList **global_match_list,
87 MenuSpec *menuSpec, MenuItem *template,
88 String command_so_far,
89 Boolean duplicate_globals, Atom selection,
90 Context greyed_context, Boolean inLine);
91 static MenuSpec *MakeMenuSpec (String menuName, CARD32 commandID);
92 static void UnmapPulldownCallback (Widget w, XtPointer client_data,
94 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
98 /*************************************<->*************************************
100 * MakeMenu (menuName, initialContext, accelContext, moreMenuItems,
106 * This function makes a menu widget.
111 * menuName = name of the top-level menu pane for the menu
112 * initialContext = initial context for menuitem sensitivity
113 * accelContext = accelerator context
114 * moreMenuItems = additional menuitems for custom menu.
115 * fSystemMenu = TRUE iff the menu is a client system menu.
120 * Return = pointer to a MenuSpec structure with updated currentContext,
121 * menuWidget, and menuButtons members.
126 * If moreMenuItems is nonNULL, a custom MenuSpec will be created, with
127 * menuItem member pointing to moreMenuItems. The menuItems for the
128 * standard MenuSpec of the same name and the moreMenuItems list will be
129 * used to create menubuttons, and the menu widget will be separate from
130 * any existing standard menu widget.
132 * When the client is destroyed, this custom MenuSpec, its menuItem and
133 * menuButton lists, and its menu widget should be freed.
135 *************************************<->***********************************/
136 MenuSpec *MakeMenu (WmScreenData *pSD,
137 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
139 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
140 String menuName, Context initialContext,
141 Context accelContext, MenuItem *moreMenuItems,
146 MenuSpec *newMenuSpec;
148 KeySpec *accelKeySpec;
150 if ((menuName == NULL) || (pSD == NULL))
156 * Look for the menu specification:
159 menuSpec = pSD->menuSpecs;
162 if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
163 /* Found the menu pane. */
167 menuSpec = menuSpec->nextMenuSpec;
170 if (menuSpec == NULL)
171 /* the menuSpecs list is exhausted */
173 MWarning(((char *)GETMESSAGE(48, 1, "Menu specification %s not found\n")), menuName);
178 * The top-level menu pane specification was found.
179 * Adjust the menu accelerator context?
186 else if (accelContext & F_CONTEXT_ROOT)
187 /* root context accelerators apply everywhere */
189 accelContext = F_CONTEXT_ALL;
193 * If making a custom menu, create a custom copy of the specification with
194 * which to build the custom menu.
195 * Otherwise, if the menu widget exists, possibly modify the accelerator
196 * contexts and return the specification.
199 if (moreMenuItems != NULL)
201 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
202 if ((newMenuSpec = DuplicateMenuSpec(menuSpec)) == (MenuSpec *)NULL)
205 if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
206 /* Handle insufficent memory */
208 MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
211 newMenuSpec->name = NULL; /* distinguishes this as custom */
212 newMenuSpec->whichButton = SELECT_BUTTON;
213 newMenuSpec->height = 0;
214 newMenuSpec->menuItems = menuSpec->menuItems; /* temporary */
215 newMenuSpec->accelContext = menuSpec->accelContext;
216 newMenuSpec->accelKeySpecs = NULL;
217 newMenuSpec->nextMenuSpec = NULL;
220 menuSpec = newMenuSpec;
222 else if (menuSpec->menuWidget)
225 * OR the accelContext into the accelerators, if necessary.
227 if (accelContext != (menuSpec->accelContext & accelContext))
229 menuSpec->accelContext |= accelContext;
230 accelKeySpec = menuSpec->accelKeySpecs;
233 accelKeySpec->context |= accelContext;
234 accelKeySpec = accelKeySpec->nextKeySpec;
241 * We have a menu specification with which to build the menu.
242 * Set the initial and accelerator contexts -- they are needed within
246 menuSpec->currentContext = initialContext;
247 menuSpec->accelContext = accelContext;
250 * Scan the toplevel MenuSpec and create its initial menuButtons array
251 * if any of its items will need to be included. This array will be
252 * created or enlarged within CreateMenuWidget below if necessary.
256 menuItem = menuSpec->menuItems;
259 if ((menuItem->greyedContext) || (menuItem->mgtMask))
263 menuItem = menuItem->nextMenuItem;
265 menuItem = moreMenuItems;
268 if ((menuItem->greyedContext) || (menuItem->mgtMask))
272 menuItem = menuItem->nextMenuItem;
276 if ((menuSpec->menuButtons =
277 (MenuButton *) XtMalloc (n * sizeof(MenuButton))) == NULL)
278 /* insufficent memory */
280 MWarning(((char *)GETMESSAGE(48, 3, "Insufficient memory for menu %s\n")), menuName);
283 menuSpec->menuButtonSize = n;
287 menuSpec->menuButtons = NULL;
288 menuSpec->menuButtonSize = 0;
290 menuSpec->menuButtonCount = 0;
293 * Create a PopupShell widget as a child of the workspace manager widget
294 * and a PopupMenu as a child of the shell.
295 * Fill the PopupMenu with the menu items.
298 menuSpec->menuWidget = CREATE_MENU_WIDGET (pSD, pCD, menuName,
299 pSD->screenTopLevelW,
300 TRUE, menuSpec, moreMenuItems);
301 if (menuSpec->menuWidget == NULL)
304 * Could not make the top-level menu pane.
309 _XmSetPopupMenuClick(menuSpec->menuWidget, False);
311 /* Return the top MenuSpec */
315 } /* END OF FUNCTION MakeMenu */
319 /*************************************<->***********************************/
320 void CheckTerminalSeparator(menuSpec, buttonWidget, manage)
330 if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
335 cw = (CompositeWidget)menuSpec->menuWidget;
336 children = cw->composite.children;
338 for (wPos = 0; wPos < cw->composite.num_children; wPos++)
340 if((Widget)children[wPos] == buttonWidget)
348 XtClass((Widget) children[wPos -1]) == xmSeparatorGadgetClass)
352 if (!(XtIsManaged((Widget)children[wPos -1])))
354 XtManageChild((Widget)children[wPos -1]);
359 if (XtIsManaged((Widget)children[wPos -1]))
361 XtUnmanageChild((Widget)children[wPos -1]);
366 } /* END OF FUNCTION CheckTerminalSeparator */
370 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
371 /*************************************<->*************************************
373 * MakeMenuSpec (menuName, commandID)
379 * This function creates and returns a MenuSpec structure.
384 * menuName = name of the menu specification
385 * commandID = client command id of the menu item to build.
386 * 0 if not for a client command.
391 * Return = pointer to a MenuSpec structure with zero'ed fields.
396 * A new MenuSpec structure is allocated. The name is set to the
397 * menuName argument. The menuItems list, menuButtons list and
398 * accelerator related fields are zero'ed out to NULL values.
400 *************************************<->***********************************/
402 MakeMenuSpec (String menuName, CARD32 commandID)
406 if ((menuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
407 /* Handle insufficent memory */
409 MWarning(((char *)GETMESSAGE(48, 2,
410 "Insufficient memory for menu %s\n")), menuName);
414 menuSpec->name = XtNewString(menuName);
415 menuSpec->currentContext = F_CONTEXT_ALL;
416 menuSpec->menuWidget = (Widget) NULL;
417 menuSpec->whichButton = SELECT_BUTTON;
418 menuSpec->height = 0;
419 menuSpec->menuItems = (MenuItem *) NULL;
420 menuSpec->menuButtons = (MenuButton *) NULL;
421 menuSpec->menuButtonSize = 0;
422 menuSpec->menuButtonCount = 0;
423 menuSpec->accelContext = F_CONTEXT_ALL;
424 menuSpec->accelKeySpecs = (KeySpec *) NULL;
425 menuSpec->exclusions = (MenuExclusion *) NULL;
426 menuSpec->clientLocal = FALSE;
427 menuSpec->commandID = commandID;
428 menuSpec->nextMenuSpec = (MenuSpec *) NULL;
434 /*************************************<->*************************************
436 * DuplicateMenuItems (menuItems)
442 * This function creates an indentical duplicate of the given menuItems
448 * menuItems = the linked list of menuItems to duplicate
453 * Return = pointer to a new MenuItems list, identical to the original
459 *************************************<->***********************************/
461 DuplicateMenuItems (MenuItem *menuItems)
463 MenuItem *newMenuItem = (MenuItem *) NULL, *returnMenuItem, *curMenuItem;
465 for (curMenuItem = menuItems;
466 curMenuItem != (MenuItem *) NULL;
467 curMenuItem = curMenuItem->nextMenuItem)
469 /* If its the first one ... */
470 if (newMenuItem == (MenuItem *) NULL)
472 newMenuItem = (MenuItem *)XtMalloc(sizeof(MenuItem));
473 returnMenuItem = newMenuItem;
475 else /* ... otherwise, get the next menuItem. */
477 newMenuItem->nextMenuItem =
478 (MenuItem *)XtMalloc(sizeof(MenuItem));
479 newMenuItem = newMenuItem->nextMenuItem;
482 newMenuItem->labelType = curMenuItem->labelType;
483 if (curMenuItem->label != (String) NULL)
484 newMenuItem->label = XtNewString(curMenuItem->label);
486 newMenuItem->label = NULL;
487 newMenuItem->labelBitmapIndex = curMenuItem->labelBitmapIndex;
488 newMenuItem->mnemonic = curMenuItem->mnemonic;
489 newMenuItem->accelState = curMenuItem->accelState;
490 newMenuItem->accelKeyCode = curMenuItem->accelKeyCode;
491 if (curMenuItem->accelText != (String) NULL)
492 newMenuItem->accelText = XtNewString(curMenuItem->accelText);
494 newMenuItem->accelText = NULL;
495 newMenuItem->wmFunction = curMenuItem->wmFunction;
497 if ((curMenuItem->wmFunction == F_Send_Msg)
499 || (curMenuItem->wmFunction == F_Set_Context)
502 * NOTE: For now, in dtwm this function is used only
503 * to copy the FrontPanel menu. So, we know that
504 * curMenuItem->wmFuncArgs isn't going anywhere,
505 * so it's safe to simply point at it. If at some
506 * point it becomes possible that curMenuItem->wmFuncArgs
507 * can go away, we'll need to make a (deep) copy of
508 * the WmActionArg. 11/20/96
510 || (curMenuItem->wmFunction == F_Action)
511 # endif /* PANELIST */
514 newMenuItem->wmFuncArgs = curMenuItem->wmFuncArgs;
515 else if (curMenuItem->wmFuncArgs != (String) NULL)
516 newMenuItem->wmFuncArgs = XtNewString(curMenuItem->wmFuncArgs);
518 newMenuItem->wmFuncArgs = NULL;
520 newMenuItem->greyedContext = curMenuItem->greyedContext;
521 newMenuItem->mgtMask = curMenuItem->mgtMask;
522 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
523 newMenuItem->clientCommandName =
524 XtNewString(curMenuItem->clientCommandName);
525 newMenuItem->clientCommandID = curMenuItem->clientCommandID;
527 newMenuItem->nextMenuItem = (MenuItem *) NULL;
530 return(returnMenuItem);
534 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
535 /*************************************<->*************************************
537 * DuplicateMenuExclusions (exclusions)
543 * This function creates an indentical duplicate of the given menu exclusions
549 * exclusions = the linked list of menu exclusions to duplicate
554 * Return = pointer to a new MenuExclusion list, identical to the original
560 *************************************<->***********************************/
561 static MenuExclusion *
562 DuplicateMenuExclusions (MenuExclusion *exclusions)
564 MenuExclusion *newMenuExclusion = (MenuExclusion *) NULL;
565 MenuExclusion *returnMenuExclusion = (MenuExclusion *) NULL;
566 MenuExclusion *curMenuExclusion = (MenuExclusion *) NULL;
568 for (curMenuExclusion = exclusions;
569 curMenuExclusion != (MenuExclusion *) NULL;
570 curMenuExclusion = curMenuExclusion->nextExclusion)
572 /* If its the first one ... */
573 if (newMenuExclusion == (MenuExclusion *) NULL)
576 (MenuExclusion *)XtMalloc(sizeof(MenuExclusion));
577 returnMenuExclusion = newMenuExclusion;
579 else /* ... otherwise, get the next menuExclusion. */
581 newMenuExclusion->nextExclusion =
582 (MenuExclusion *)XtMalloc(sizeof(MenuExclusion));
583 newMenuExclusion = newMenuExclusion->nextExclusion;
586 newMenuExclusion->command_string =
587 XtNewString(curMenuExclusion->command_string);
590 /* Make sure we properly NULL terminate the list. */
591 if (newMenuExclusion != (MenuExclusion *) NULL)
592 newMenuExclusion->nextExclusion = (MenuExclusion *) NULL;
594 return(returnMenuExclusion);
598 /*************************************<->*************************************
600 * DuplicateMenuSpec (menuSpec)
606 * This function creates an indentical duplicate of the given menuSpec.
607 * The menuItems list in the menuSpec is also duplicated.
612 * menuSpec = the menuSpec to duplicate
617 * Return = pointer to a new MenuSpec structure with the same field
618 * values as the original
623 * A new MenuSpec structure is allocated. Most of he fields of the new
624 * structure are set to the same values as the passed in menuSpec.
625 * There are some differences between the two final structures.
626 * One difference: any fields related to push buttons and other
627 * widgets are left blank in the new MenuSpec to be filled in later.
629 *************************************<->***********************************/
631 DuplicateMenuSpec (MenuSpec *menuSpec)
633 MenuSpec *newMenuSpec;
635 if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
636 /* Handle insufficent memory */
638 Warning((char *)GETMESSAGE(48, 9,
639 "Insufficient memory for menu specification\n"));
642 newMenuSpec->name = XtNewString(menuSpec->name);
643 newMenuSpec->currentContext = menuSpec->currentContext;
644 newMenuSpec->menuWidget = (Widget) NULL;
645 newMenuSpec->whichButton = menuSpec->whichButton;
646 newMenuSpec->height = menuSpec->height;
647 newMenuSpec->menuItems = DuplicateMenuItems(menuSpec->menuItems);
648 newMenuSpec->menuButtons = (MenuButton *) NULL;
649 newMenuSpec->menuButtonSize = 0;
650 newMenuSpec->menuButtonCount = 0;
651 newMenuSpec->accelContext = menuSpec->accelContext;
652 newMenuSpec->accelKeySpecs = (KeySpec *) NULL;
653 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
654 newMenuSpec->exclusions = DuplicateMenuExclusions(menuSpec->exclusions);
655 newMenuSpec->clientLocal = TRUE;
656 newMenuSpec->commandID = menuSpec->commandID;
658 newMenuSpec->nextMenuSpec = (MenuSpec *) NULL;
664 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
665 /*************************************<->*************************************
667 * MakeMenuItem (label, wmFunction, funcArgs, mnemonic, accelText)
672 * This function creates and returns a MenuItem structure.
677 * label = the display name of the menu item
678 * wmFunction = the wm function to invoke
679 * funcArgs = the function arguments to pass to the wm function
680 * mnemonic = the mnemonic keysym
681 * accelText = the accelerator text
685 * Return = pointer to a new MenuItem structure with fields filled
686 * in as per passed arguments
691 * This function is actually used as the underlying mechanism for
692 * MenuItem creation by MakeMenuItemFromTemplate and
693 * MakeClientCommandMenuItem.
697 * This function assumes that ParseWmAccelerator simply takes a pointer
698 * to a string and parses the accelerator found in it. If ParseWmAccelerator
699 * is ever modified to call GetString to get more text from the parse
700 * stream (as other parse functions do) then this code will break.
702 *************************************<->***********************************/
704 MakeMenuItem (String label, WmFunction wmFunction, String funcArgs,
705 KeySym mnemonic, unsigned int accelState,
706 KeyCode accelKeyCode, String accelText)
710 unsigned char *copy_of_accelText;
713 if ((menuItem = (MenuItem *) XtMalloc (sizeof (MenuItem))) == NULL)
714 /* Handle insufficent memory */
716 MWarning(((char *)GETMESSAGE(48, 10,
717 "Insufficient memory for menu item %s\n")), label);
721 menuItem->labelType = XmSTRING;
722 menuItem->label = XtNewString(label);
723 menuItem->labelBitmapIndex = -1;
724 menuItem->mnemonic = mnemonic;
725 menuItem->clientCommandName = NULL;
726 menuItem->clientCommandID = 0;
729 copy_of_accelText = (unsigned char *)XtNewString(accelText);
730 ParseWmAccelerator(©_of_accelText, menuItem);
733 menuItem->accelState = accelState;
734 menuItem->accelKeyCode = accelKeyCode;
735 menuItem->accelText = XtNewString(accelText);
737 menuItem->wmFunction = wmFunction;
738 menuItem->wmFuncArgs = XtNewString(funcArgs);
739 SetGreyedContextAndMgtMask(menuItem, wmFunction);
740 menuItem->nextMenuItem = (MenuItem *) NULL;
746 /*************************************<->*************************************
748 * MakeMenuItemFromTemplate (template, name, funcArgs)
753 * This function creates and returns a MenuItem structure.
758 * template = a template menuItem used to fill in fields of the
760 * name = the display name this item should have
761 * funcArgs = the function arguments to pass to the wm function
765 * Return = pointer to a new MenuItem structure with fields filled
766 * in as per template MenuItem and funcargs
771 * This function uses the values in the template MenuItem to create
772 * a new copy of the template with the given funcArgs.
774 *************************************<->***********************************/
776 static MenuItem *MakeMenuItemFromTemplate (MenuItem *template, String name,
779 if (template->clientCommandName == (String) NULL)
780 return(MakeMenuItem(name, template->wmFunction, funcArgs,
781 template->mnemonic, template->accelState,
782 template->accelKeyCode, template->accelText));
784 return(MakeMenuItem(template->clientCommandName, template->wmFunction,
785 funcArgs, template->mnemonic, template->accelState,
786 template->accelKeyCode, template->accelText));
790 /*************************************<->*************************************
792 * MakeClientCommandMenuItem (label, funcArgs)
797 * This function creates and returns a MenuItem structure filled as
798 * appropriate for client command menu items using the given label
804 * label = the display label for this menu item
805 * funcArgs = the function arguments to pass to the wm function
809 * Return = pointer to a new MenuItem structure with fields filled
810 * in as per arguments and client command specs
815 * This function will fill in a new MenuItem structure as appropriate for
816 * client commands. This function is used when you want to insert a client
817 * command into a menu without using a MenuItem template constructed from
820 *************************************<->***********************************/
822 static MenuItem *MakeClientCommandMenuItem (String label, String funcArgs)
824 return(MakeMenuItem(label, F_InvokeCommand, funcArgs,
825 (KeySym) NULL, (unsigned int)0,
826 (KeyCode) NULL, (String)NULL));
830 /*************************************<->*************************************
832 * PerformClientCommandMatch (clientCommand, menuItem, bestMatchSoFar)
837 * This function determines whether the menuItemCommand specification
838 * matches the clientCommand.
842 * clientCommand = the clientCommand that we want to find a specification for
843 * menuItem = the menu item we will look in for a specification
844 * bestMatchSoFar = the menu item we will return if the given menu item is
845 * not a match or not a better match
849 * Return = pointer to the given menuItem if it contains a client command
850 * specification and if that specification matches the given
851 * clientCommand *and* if the match is a better match than
852 * the bestMatchSoFar. Otherwise, the bestMatchSoFar is returned,
853 * which could possibly be NULL.
857 * If the menuItem does match, it also determines whether it is
858 * a better match than the bestMatchSoFar. If so, the menuItemCommand is
859 * returned. Otherwise, bestMatchSoFar is returned.
861 * Best matching is defined as follows:
862 * 1. A specification with fewer wildcards is considered a better
863 * match than one with more wildcards.
864 * 2. Given two specifications with the same number of wildcards,
865 * the specification with its wildcards more towards the right
866 * than the left is considered a better match.
868 *************************************<->***********************************/
870 /* @RGC: This is kind of arbitrary, but I can't imagine there being more
871 than a small number of segments in any given command specification. */
872 #define MAXSEGMENTS 100
874 static MenuItem *PerformClientCommandMatch (String clientCommand,
876 MenuItem *bestMatchSoFar)
878 String menuItemCommand, bestMatchStr;
880 int segments = 0, wildcards = 0, wildcardPositions[MAXSEGMENTS];
881 int bestSegments = 0, bestWildcards = 0;
882 int bestWildcardPositions[MAXSEGMENTS];
883 Boolean foundWildcard = FALSE;
885 if (menuItem == (MenuItem *) NULL)
886 return(bestMatchSoFar);
887 menuItemCommand = menuItem->label;
889 /* Skip any modifier characters at the beginning of the
890 menu items client command. */
891 /* @RGC: This is kind of kludgy. We shouldn't have to know
892 the specifics of command parsing here. */
893 if (menuItemCommand[0] == '~')
895 else if (menuItemCommand[0] == '=' && menuItemCommand[1] == '>')
896 menuItemCommand += 2;
897 else if (menuItemCommand[0] == '=')
899 else if (menuItemCommand[0] == '-' && menuItemCommand[1] == '>')
900 menuItemCommand += 2;
902 /* If the menu item doesn't even contain a client command spec,
903 then just return the existing best match. */
904 if (*menuItemCommand != '<') return(bestMatchSoFar);
906 /* Run down the clientCommand and the menuItemCommand together,
907 matching along the way. If matching fails at any point, then
908 return the bestMatchSoFar. */
910 *menuItemCommand != '\0' && *clientCommand != '\0';
913 /* Skip past the '<' at the beginning of the next segment and
915 ++menuItemCommand; ++clientCommand;
916 while (isspace(*menuItemCommand)) ++menuItemCommand;
917 while (isspace(*clientCommand)) ++clientCommand;
919 /* First check whether the current menuItemCommand segment is
921 if (*menuItemCommand == '*')
923 /* Since the menuItemCommand segment is a wildcard, skip
924 it and the current segment of the client command since
925 the wildcard has to match at least one segment in
926 the client command. */
927 wildcardPositions[wildcards++] = segments;
929 while (isspace(*menuItemCommand)) ++menuItemCommand;
930 while (*clientCommand != '>' && *clientCommand != '\0')
932 foundWildcard = TRUE;
936 /* Calculate how long the current segment of the
937 menuItemCommand is */
939 menuItemCommand[seglength] != '>' &&
940 menuItemCommand[seglength] != '\0';
944 /* If we are pointing at '\0', then this isn't a match */
945 if (menuItemCommand[seglength] == '\0') return(bestMatchSoFar);
947 /* Get rid of trailing white space on the segment. */
948 for (; seglength > 0; --seglength)
950 if (!isspace(menuItemCommand[seglength - 1]))
954 /* Now string compare this segment with the clientCommand
955 segment, up to the number of characters in the menu
957 if (strncmp(menuItemCommand, clientCommand, seglength) == 0)
959 /* So far so good. Just make sure clientCommand doesn't
960 have anything but whitespace after its seglength
962 clientCommand += seglength;
963 while (isspace(*clientCommand)) ++clientCommand;
964 if (*clientCommand != '>') return(bestMatchSoFar);
966 /* We have a match. Clear the foundWildcard since we
967 have sync'ed up and keep trying to match. */
968 foundWildcard = FALSE;
969 menuItemCommand += seglength;
970 while (isspace(*menuItemCommand)) ++menuItemCommand;
972 else if (foundWildcard == FALSE)
974 /* We didn't match and there wasn't wildcard to
975 swallow the discrepancy. Therefore, this is not
977 return(bestMatchSoFar);
981 /* We finished the current segments, we should be looking at
982 a close bracket and a following period or a close bracket and
983 a following NULL. Skip past the close brackets and optional
984 period. If we don't see those, then this isn't a match. */
985 if (menuItemCommand[0] == '>' && menuItemCommand[1] == '\0' &&
986 clientCommand[0] == '>' && clientCommand[1] == '\0')
988 ++menuItemCommand; ++clientCommand;
990 else if (menuItemCommand[0] == '>' && menuItemCommand[1] == '.' &&
991 clientCommand[0] == '>' && clientCommand[1] == '.')
993 menuItemCommand += 2;
996 else return(bestMatchSoFar);
999 /* If we terminated the loop because only one of the two commands being
1000 compared was empty, then we don't have a complete match. Return the
1001 best match so far. */
1002 if (*menuItemCommand != '\0' || *clientCommand != '\0')
1003 return(bestMatchSoFar);
1005 /* So the menuItemCommand must have matched. If the current best
1006 match is NULL, then just return the menuItem. Otherwise calculate some
1007 matching quality metrics for the bestMatchSoFar and compare them
1008 to the menuItemCommand metrics to decide which of the two to
1010 if (bestMatchSoFar == (MenuItem *) NULL)
1013 bestMatchStr = bestMatchSoFar->label;
1015 /* Skip any modifier characters at the beginning of the
1016 best match client command. */
1017 /* @RGC: This is kind of kludgy. We shouldn't have to know
1018 the specifics of command parsing here. */
1019 if (bestMatchStr[0] == '~')
1021 else if (bestMatchStr[0] == '=' && bestMatchStr[1] == '>')
1023 else if (bestMatchStr[0] == '=')
1025 else if (bestMatchStr[0] == '-' && bestMatchStr[1] == '>')
1028 /* If the best match doesn't even contain a client command spec,
1029 then just return the new match as the best match. */
1030 if (*bestMatchStr != '<') return(menuItem);
1032 for (bestSegments = 0;
1033 *bestMatchStr != '\0';
1036 /* Skip past the '<' at the beginning of the next segment and
1039 while (isspace(*bestMatchStr)) ++bestMatchStr;
1041 /* First check whether the current bestMatchStr segment is
1042 a wildcard. @RGC: We are assuming that there is nothing
1043 but possible whitespace after the *. */
1044 if (*bestMatchStr == '*')
1045 bestWildcardPositions[bestWildcards++] = bestSegments;
1046 while (*bestMatchStr != '>' && *bestMatchStr != '\0')
1049 /* Check for the closing > and . or close > and NULL. If they
1050 do not both appear then the bestMatch is bad and we should
1051 return the menuItem. */
1052 if (bestMatchStr[0] == '>' && bestMatchStr[1] == '\0')
1054 else if (bestMatchStr[0] == '>' && bestMatchStr[1] == '.')
1056 else return(menuItem);
1059 /* Now compare the best match metrics with the menu item metrics to
1060 determine who should be returned. */
1061 if (bestWildcards != wildcards)
1063 /* Return the menuItem with the fewest wildcards. */
1064 return(bestWildcards < wildcards ? bestMatchSoFar : menuItem);
1068 /* Find which menu item has the earliest wild card and return
1070 for (i = 0; i < wildcards; ++i)
1071 if (wildcardPositions[i] != bestWildcardPositions[i])
1073 return(bestWildcardPositions[i] < wildcardPositions[i] ?
1074 bestMatchSoFar : menuItem);
1077 /* If we got this far, then the two specifications are too
1078 close to call. Return bestMatchSoFar. */
1079 return(bestMatchSoFar);
1084 /*************************************<->*************************************
1086 * ExcludeClientCommand (menuSpec, clientCommand)
1094 * menuSpec = the menuSpec whose menuItems we want to search through
1095 * clientCommand = the clientCommand that we want to find an exclusion for
1099 * Return = TRUE if the command must be excluded from the menuSpec.
1100 * FALSE if there is no exclusion preventing the insertion.
1105 *************************************<->***********************************/
1107 static Boolean ExcludeClientCommand (MenuSpec *menuSpec, String clientCommand)
1109 MenuItem placeholder;
1110 MenuExclusion *curExclusion;
1112 /* Search for an exclusion that would cause this command to be
1113 excluded, if any such exclusion exists. */
1114 for (curExclusion = menuSpec->exclusions;
1115 curExclusion != (MenuExclusion *) NULL;
1116 curExclusion = curExclusion->nextExclusion)
1118 /* We don't have menu items for exclusions so just use a bogus
1119 placeholder menu item with the label field set to the string
1120 found in the exclusion. */
1121 placeholder.label = curExclusion->command_string;
1123 /* If we don't get NULL back, then this exclusion matches. */
1124 if (PerformClientCommandMatch(clientCommand,
1125 &placeholder, NULL) != (MenuItem *) NULL)
1134 /*************************************<->*************************************
1136 * ForceInLineToCascade (menuSpec, clientCommand, bestMatch)
1144 * menuSpec = the menuSpec whose menuItems we want to search through
1145 * clientCommand = the clientCommand that we want to find an force for
1146 * bestMatch = the best matching menu item that was found
1150 * Return = TRUE if the command set must be cascaded
1151 * FALSE if there is no forced cascade
1156 *************************************<->***********************************/
1158 static Boolean ForceInLineToCascade (MenuSpec *menuSpec,
1159 String clientCommand,
1160 MenuItem **bestMatch)
1162 /* First find the best match in the menu spec. */
1163 FindClientCommandMatch(menuSpec, clientCommand, bestMatch);
1165 /* If the best match is not NULL, then check whether it forces
1166 the client command to cascade. */
1167 if (*bestMatch != (MenuItem *) NULL)
1169 /* If there is a force cascade modifier, then return TRUE. */
1170 if ((strncmp((*bestMatch)->label, "->", 2) == 0) ||
1171 (strncmp((*bestMatch)->label, "=>", 2) == 0))
1175 /* If the best match is NULL, then return FALSE. We have been
1176 given no indication that the inLine command should be forced
1182 /*************************************<->*************************************
1184 * FindClientCommandMatch (menuSpec, clientCommand, menuItem)
1189 * This function searches through the list of menuItems in the given
1190 * menuSpec, searching for ones which have a client command specification
1191 * and, for each one that does, whether that specification matches the
1192 * given client. The best matching menuItem out of all that matched
1197 * menuSpec = the menuSpec whose menuItems we want to search through
1198 * clientCommand = the clientCommand that we want to find a specification for
1199 * menuItem = the best matching menu item
1203 * Return = TRUE if the command may be inserted into the menuSpec.
1204 * FALSE if there is an exclusion preventing the insertion.
1205 * Also return the best matching menu item in the menuItem
1206 * buffer argument. NULL is returned if no matching MenuItem
1211 * Best matching is defined as follows:
1212 * 1. A specification with fewer wildcards is considered a better
1213 * match than one with more wildcards.
1214 * 2. Given two specifications with the same number of wildcards,
1215 * the specification with its wildcards more towards the right
1216 * than the left is considered a better match.
1218 *************************************<->***********************************/
1220 static Boolean FindClientCommandMatch (MenuSpec *menuSpec,
1221 String clientCommand,
1222 MenuItem **menuItem)
1224 MenuItem *bestMatch = (MenuItem *) NULL, *curMenuItem, placeholder;
1225 MenuItem *bestExclusionItem = (MenuItem *) NULL;
1226 MenuExclusion *curExclusion;
1227 String bestExclusionStr = (String) NULL;
1229 /* First search for a match in the menu items of the menu spec. */
1230 for (curMenuItem = menuSpec->menuItems;
1231 curMenuItem != (MenuItem *) NULL;
1232 curMenuItem = curMenuItem->nextMenuItem)
1235 PerformClientCommandMatch(clientCommand, curMenuItem, bestMatch);
1238 /* Now search for the best exclusion that would cause this match to be
1239 excluded, if any such exclusion exists. */
1240 for (curExclusion = menuSpec->exclusions;
1241 curExclusion != (MenuExclusion *) NULL;
1242 curExclusion = curExclusion->nextExclusion)
1244 /* We don't have menu items for exclusions so just use a bogus
1245 placeholder menu item with the label field set to the string
1246 found in the exclusion. */
1247 placeholder.label = curExclusion->command_string;
1249 /* Find the best exclusion string in the bunch. */
1251 PerformClientCommandMatch(clientCommand, &placeholder,
1254 /* Save the best exclusion string since we are going to reuse
1255 the placeholder menu item. */
1256 if (bestExclusionItem != (MenuItem *) NULL)
1257 bestExclusionStr = bestExclusionItem->label;
1260 /* Okay, now if we found an exclusion, we need to determine if the
1261 exclusion was a better match than the best match that we found.
1262 If so, the item is *really* exclude. Otherwise, we return the
1263 best match and let the item be included. */
1264 placeholder.label = bestExclusionStr;
1265 if (bestExclusionStr == (String) NULL ||
1266 PerformClientCommandMatch(clientCommand, bestMatch, &placeholder) ==
1269 *menuItem = bestMatch;
1281 /*************************************<->*************************************
1283 * PerformInsertionsOnMatchList (matchlist)
1300 *************************************<->***********************************/
1302 static void PerformInsertionsOnMatchList (MatchList **matchlist)
1304 MatchList *curmatch;
1305 MenuItem *newMenuItem, *curitem;
1307 if (*matchlist == (MatchList *) NULL)
1310 if ((*matchlist)->menuspec == (MenuSpec *) NULL)
1312 /* should never get here, but if we do, then we can't
1313 continue in this routine since mwm will dump. This
1314 may be caused by the cci code duplicating a global
1315 menu for a client when it shouldn't be duplicated.
1316 If we skip this routine, the cci command will not
1317 be added which is far less disturbing than a dump. */
1322 for (curmatch = *matchlist;
1323 curmatch != (MatchList *) NULL;
1324 curmatch = curmatch->next)
1326 if (curmatch->menuitem != (MenuItem *) NULL)
1328 /* Find this menu item within the menuspec. */
1329 for (curitem = curmatch->menuspec->menuItems;
1330 curitem != curmatch->menuitem &&
1331 curitem != (MenuItem *) NULL;
1332 curitem = curitem->nextMenuItem)
1335 /* If we didn't find the menuitem in the menuspec, then
1336 don't do this match. */
1337 if (curitem == (MenuItem *) NULL) continue;
1340 MakeMenuItemFromTemplate(curmatch->menuitem,
1341 curmatch->treenode->defaultName,
1342 curmatch->funcargs);
1343 newMenuItem->wmFunction = curmatch->function;
1344 newMenuItem->greyedContext = curmatch->greyed_context;
1345 newMenuItem->nextMenuItem = curitem->nextMenuItem;
1346 newMenuItem->clientCommandID = curmatch->treenode->commandID;
1347 curitem->nextMenuItem = newMenuItem;
1351 MenuItem *last = (MenuItem *) NULL;
1353 if (curmatch->menuspec != NULL)
1355 /* Find the last menu item in the menuspec */
1356 for (last = curmatch->menuspec->menuItems;
1357 last != (MenuItem *) NULL &&
1358 last->nextMenuItem != (MenuItem *) NULL;
1359 last = last->nextMenuItem)
1361 /* If the next item is f.quit and it is the last
1362 item, then stop searching now. We don't want
1363 to insert after a trailing f.kill (i.e. Close). */
1364 if ((last->nextMenuItem->wmFunction == F_Kill) &&
1365 (last->nextMenuItem->nextMenuItem == (MenuItem *) NULL))
1370 /* Create a new client command menu item */
1372 MakeClientCommandMenuItem
1373 (XtNewString(curmatch->treenode->defaultName),
1374 XtNewString(curmatch->funcargs));
1375 newMenuItem->wmFunction = curmatch->function;
1376 newMenuItem->greyedContext = curmatch->greyed_context;
1377 newMenuItem->clientCommandID = curmatch->treenode->commandID;
1379 /* Insert the new menu item at the end of the list */
1380 if (last == (MenuItem *) NULL)
1382 newMenuItem->nextMenuItem = (MenuItem *) NULL;
1383 if (curmatch->menuspec != NULL)
1384 curmatch->menuspec->menuItems = newMenuItem;
1387 /* again, should never get here... */
1393 newMenuItem->nextMenuItem = last->nextMenuItem;
1394 last->nextMenuItem = newMenuItem;
1401 /*************************************<->*************************************
1404 * DestroyMenuSpecWidgets (menuSpec)
1413 * menuSpec = pointer to MenuSpec structure
1421 * Destroys all the menuspec widgets so that we can rebuild the menu from
1424 *************************************<->***********************************/
1426 void DestroyMenuSpecWidgets (MenuSpec *menuSpec)
1428 /* check for bad input value - shouldn't happen. */
1429 if (menuSpec == (MenuSpec *) NULL) return;
1431 /* Destroy the menu widget */
1432 if (menuSpec->menuWidget != (Widget) NULL)
1434 XtDestroyWidget(XtParent(menuSpec->menuWidget));
1435 menuSpec->menuWidget = (Widget) NULL;
1438 /* Destroy the menu buttons array */
1439 if (menuSpec->menuButtonSize != 0)
1441 XtFree((char *)menuSpec->menuButtons);
1442 menuSpec->menuButtons = (MenuButton *) NULL;
1445 /* Reset the counters */
1446 menuSpec->menuButtonSize = 0;
1447 menuSpec->menuButtonCount = 0;
1449 /* Clear the flag that says we have processed this menu spec for
1450 widget creation. (We probably don't need to do this after all
1451 since CreateMenuWidgets clears it when done.) */
1452 if (menuSpec->currentContext & CR_MENU_MARK)
1453 menuSpec->currentContext &= ~(CR_MENU_MARK);
1459 /*************************************<->*************************************
1462 * DestroyMenuSpec (pSD, commandID)
1471 * pSD = screen data pointer of screen with command to remove
1472 * commandID = command id of the menuspec to be removed.
1473 * if no match is found, then no removal is done.
1480 * Destroy the specified menuSpec from the list of menuspecs on the
1481 * specified screen. Note, there may be more than one copy of the
1482 * spec floating around since duplications may have been done for
1485 *************************************<->***********************************/
1487 void DestroyMenuSpec (WmScreenData *pSD, CARD32 commandID)
1489 MenuSpec *msToKill = NULL, *pMS;
1490 ClientListEntry *curClient;
1492 /* Scan through global menu specs. */
1493 if (pSD != NULL && pSD->menuSpecs != NULL && commandID != 0)
1495 /* Scan through the list of menuSpecs and pull the mathing one
1498 if (commandID == pSD->menuSpecs->commandID)
1500 /* match at head of menuSpec list. */
1501 msToKill = pSD->menuSpecs;
1502 pSD->menuSpecs = pSD->menuSpecs->nextMenuSpec;
1503 msToKill->nextMenuSpec = NULL;
1507 for (pMS = pSD->menuSpecs;
1508 (pMS->nextMenuSpec != NULL &&
1509 pMS->nextMenuSpec->commandID != commandID);
1510 pMS = pMS->nextMenuSpec)
1513 if (pMS->nextMenuSpec != NULL)
1515 msToKill = pMS->nextMenuSpec;
1516 pMS->nextMenuSpec = msToKill->nextMenuSpec;
1517 msToKill->nextMenuSpec = NULL;
1521 /* found it - now remove the menuSpec. */
1522 if (msToKill != NULL)
1523 FreeCustomMenuSpec(msToKill);
1527 /* Check each client's menu spec list. Stop searching if global. */
1528 for (curClient = pSD->clientList;
1529 curClient != (ClientListEntry *)NULL;
1530 curClient = curClient->nextSibling)
1533 * Check the first position.
1534 * If matched, then we're done with this client.
1536 if (commandID == pSD->menuSpecs->commandID)
1538 msToKill = curClient->pCD->systemMenuSpec;
1539 curClient->pCD->systemMenuSpec = msToKill->nextMenuSpec;
1540 msToKill->nextMenuSpec = NULL;
1543 /* Check the rest of the list. */
1546 for (pMS = curClient->pCD->systemMenuSpec;
1547 (pMS->nextMenuSpec != (MenuSpec *)NULL) &&
1548 (pMS->nextMenuSpec->commandID != commandID) &&
1550 pMS = pMS->nextMenuSpec)
1553 if ((pMS->nextMenuSpec != (MenuSpec *)NULL) &&
1554 (pMS->nextMenuSpec->commandID != commandID))
1556 msToKill = pMS->nextMenuSpec;
1557 pMS->nextMenuSpec = msToKill->nextMenuSpec;
1558 msToKill->nextMenuSpec = NULL;
1564 if (msToKill != NULL)
1565 FreeCustomMenuSpec(msToKill);
1572 /*************************************<->*************************************
1574 * ReplaceMenuSpecForClient (menuspec, pCD)
1579 * Duplicates the given menuspec and replaces the given menuspec if
1580 * found in the clients menuspec list with the duplicate.
1587 * Return = the duplicate menuspec
1593 *************************************<->***********************************/
1595 static MenuSpec *ReplaceMenuSpecForClient (MenuSpec *menuSpec, ClientData *pCD)
1597 MenuSpec *newMenuSpec, *curMenuSpec;
1599 /* Duplicate the menu spec */
1600 newMenuSpec = DuplicateMenuSpec(menuSpec);
1602 /* Try to find this menuspec in the list of client
1603 menuspecs. If we find it then we want to replace it with
1605 if (pCD->systemMenuSpec == menuSpec)
1607 /* It was the head of the list. We need to handle that
1609 newMenuSpec->nextMenuSpec = pCD->systemMenuSpec->nextMenuSpec;
1610 pCD->systemMenuSpec = newMenuSpec;
1614 /* Search through the list until we find the menuspec or
1615 the end of the list. */
1616 for (curMenuSpec = pCD->systemMenuSpec;
1617 curMenuSpec->nextMenuSpec != (MenuSpec *) NULL;
1618 curMenuSpec = curMenuSpec->nextMenuSpec)
1620 if (curMenuSpec->nextMenuSpec == menuSpec)
1622 newMenuSpec->nextMenuSpec =
1623 curMenuSpec->nextMenuSpec->nextMenuSpec;
1624 curMenuSpec->nextMenuSpec = newMenuSpec;
1625 /* We found it and replaced it. Now get out of
1630 if (curMenuSpec->nextMenuSpec == (MenuSpec *) NULL)
1632 /* We didn't find it. Just stick it at the end? We
1633 should have found it. I'm not sure how to handle
1635 curMenuSpec->nextMenuSpec = newMenuSpec;
1636 newMenuSpec = (MenuSpec *) NULL;
1640 return(newMenuSpec);
1644 /*************************************<->*************************************
1646 * FindLastMenuSpecToModify (menuspec, command_id)
1657 * Return = the last menu spec that would be affected by modifications
1658 * to the given command id
1664 *************************************<->***********************************/
1666 static MenuSpec * FindLastMenuSpecToModify(MenuSpec *menuSpec,
1669 MenuSpec *curMenuSpec, *lastToModify = (MenuSpec *) NULL;
1672 /* Search through all the menu specs in the list starting with
1673 the passed in menuSpec */
1674 for (curMenuSpec = menuSpec;
1675 curMenuSpec != (MenuSpec *) NULL;
1676 curMenuSpec = curMenuSpec->nextMenuSpec)
1678 /* Try to find a menu item in this menu spec with the
1679 command_id that will require modification */
1680 for (curItem = curMenuSpec->menuItems;
1681 curItem != (MenuItem *) NULL;
1682 curItem = curItem->nextMenuItem)
1684 if (curItem->clientCommandID == command_id)
1688 /* If we found a menu item that needs changing, then this
1689 menu spec will need changing. Set the lastToModify to
1690 point to this menu spec. If we find no other menu spec
1691 that needs changing, then this will be the last one
1692 in the list that needs changing. */
1693 if (curItem != (MenuItem *) NULL)
1694 lastToModify = curMenuSpec;
1697 /* We've looked through all the menu specs starting with menuSpec
1698 and we've looked at all the menu items in all of those menu
1699 specs. The lastToModify variable should be set to the last
1700 menu spec we saw that needed modification. Return it. */
1701 return(lastToModify);
1705 /*************************************<->*************************************
1707 * RecreateMenuWidgets (matchlist)
1724 *************************************<->***********************************/
1726 static void RecreateMenuWidgets (WmScreenData *pSD, ClientData *pCD,
1727 MatchList **matchlist)
1731 MenuSpec **to_change;
1733 /* First count how many menu specs we need to recreate widgets for */
1734 for (current = *matchlist;
1735 current != (MatchList *) NULL;
1736 current = current->next)
1739 /* If there are no affected menuspecs, then just return. */
1740 if (count == 0) return;
1742 /* Allocate an array of menuspec pointers that is the size of the
1743 number of menu specs we need to recreate widgets for */
1744 to_change = (MenuSpec **)XtMalloc(sizeof(MenuSpec *) * count);
1745 for (i = 0; i < count; ++i)
1746 to_change[i] = (MenuSpec *) NULL;
1748 /* Now run through all the matchlist items, storing menuspecs in
1749 that array. If the menuspec is already there, then don't store
1751 for (current = *matchlist;
1752 current != (MatchList *) NULL;
1753 current = current->next)
1755 for (i = 0; i < count; ++i)
1757 if (to_change[i] == current->menuspec) break;
1758 else if (to_change[i] == (MenuSpec *) NULL)
1760 to_change[i] = current->menuspec;
1766 /* Run through the array, destroy all existing widgets for each
1768 for (i = 0; i < count && to_change[i] != (MenuSpec *) NULL ; ++i)
1770 DestroyMenuSpecWidgets(to_change[i]);
1773 /* Run through the array again creating widgets for all the menuspecs */
1774 for (i = 0; i < count && to_change[i] != (MenuSpec *) NULL; ++i)
1776 to_change[i]->menuWidget =
1777 CreateMenuWidget (pSD, pCD, to_change[i]->name, pSD->screenTopLevelW,
1778 TRUE, to_change[i], NULL);
1781 /* Free the array. We're done. */
1782 XtFree((char *) to_change);
1786 /*************************************<->*************************************
1788 * FreeMatchList (matchlist)
1805 *************************************<->***********************************/
1807 static void FreeMatchList (MatchList **matchlist)
1809 MatchList *current, *next;
1811 current = *matchlist;
1813 while (current != (MatchList *) NULL)
1815 next = current->next;
1816 XtFree(current->command_string);
1817 XtFree(current->funcargs);
1818 XtFree((char *)current);
1822 *matchlist = (MatchList *) NULL;
1826 /*************************************<->*************************************
1828 * StoreMatchedCommand (matchlist, menuSpec, menuItem, command_string,
1829 * treenode, function, funcargs)
1845 * If the existing match has NULL for the menuitem, then get rid of
1846 * it and replace with proposed match.
1848 *************************************<->***********************************/
1850 static void StoreMatchedCommand (MatchList **matchlist, MenuSpec *menuSpec,
1851 MenuItem *menuItem, String command_string,
1852 CmdTree *treenode, WmFunction function,
1853 String funcargs, Context greyed_context)
1855 MatchList *current, *new;
1857 /* If this entry does not already exist in the match list, then insert
1858 it. This implies that we first have to perform a search of the list.
1859 The search is very easy. We can simply compare the tuple of
1860 <menuSpec,command_string> with each entry in the matchlist
1861 to see if we already have that tuple stored. We can do straight
1862 pointer value matching for the menuSpec and strcmp for the
1864 for (current = *matchlist;
1865 current != (MatchList *) NULL;
1866 current = current->next)
1868 if (current->menuspec == menuSpec &&
1869 strcmp(current->command_string, command_string) == 0)
1871 /* If the currently stored menu item is NULL,
1872 then replace with the new menuitem and return. */
1873 if (current->menuitem == (MenuItem *) NULL)
1875 current->menuitem = menuItem;
1878 /* Otherwise, we have alreay inserted this
1879 command into this menuspec so don't allow
1880 another insertion. */
1885 /* Well, we didn't find a match, so store the entry */
1886 new = (MatchList *)XtMalloc(sizeof(MatchList));
1887 new->menuspec = menuSpec;
1888 new->menuitem = menuItem;
1889 new->command_string = XtNewString(command_string);
1890 new->treenode = treenode;
1891 new->function = function;
1892 new->funcargs = XtNewString(funcargs);
1893 new->greyed_context = greyed_context;
1894 new->next = (MatchList *) NULL;
1896 /* Stick it at the head of the list. It's easier. */
1897 new->next = *matchlist;
1902 /*************************************<->*************************************
1904 * SearchForOtherMatches (pSD, pCD, treenode,
1905 * client_match_list, global_match_list,
1906 * menuSpec, command_string,
1907 * function, funcargs, duplicate_globals, selection,
1913 * menuSpec = menu spec to exclude from search
1926 *************************************<->***********************************/
1928 static void SearchForOtherMatches (WmScreenData *pSD, ClientData *pCD,
1930 MatchList **client_match_list,
1931 MatchList **global_match_list,
1932 MenuSpec *menuSpec, String command_string,
1933 WmFunction function, String funcargs,
1934 Boolean duplicate_globals, Atom selection,
1935 Context greyed_context, Boolean inLine)
1937 MenuSpec *current, *newMenuSpec;
1940 /* Search through all of the clients menuspecs first */
1941 for (current = (pCD == NULL ? NULL : pCD->systemMenuSpec);
1942 current != (MenuSpec *) NULL;
1943 current = current->nextMenuSpec)
1945 /* If the current menu spec is a global, then just quit
1946 this loop. Any menu specs from this point on will
1947 have a next pointer that is still in the global list. */
1948 if (menuSpec->clientLocal != TRUE) break;
1949 FindClientCommandMatch(current, command_string, &match);
1950 if (match != (MenuItem *) NULL)
1952 if (treenode->subTrees != (CmdTree *) NULL && inLine &&
1953 (strncmp(match->label, "->", 2) == 0 ||
1954 strncmp(match->label, "=>", 2) == 0))
1957 for (tree = treenode->subTrees;
1958 tree != (CmdTree *) NULL;
1961 char new_command_str[1024];
1962 char new_funcargs[1024];
1963 WmFunction inLine_function;
1965 if (command_string == NULL)
1966 sprintf(new_command_str, "<%s>", tree->name);
1968 sprintf(new_command_str, "%s.<%s>", command_string,
1970 if (tree->subTrees != (CmdTree *) NULL)
1972 /* menu to cascade to */
1973 sprintf(new_funcargs, "<%s>", tree->name);
1974 inLine_function = F_Menu;
1978 sprintf(new_funcargs, "%d %ld %ld", tree->commandID,
1979 pCD->client, selection);
1980 inLine_function = F_InvokeCommand;
1982 StoreMatchedCommand(client_match_list, current, match,
1983 new_command_str, tree,
1984 inLine_function, new_funcargs,
1990 StoreMatchedCommand(client_match_list, current, match,
1991 command_string, treenode, function,
1992 funcargs, greyed_context);
1997 /* Search through all of the global menuspecs also. */
1998 for (current = pSD->menuSpecs;
1999 current != (MenuSpec *) NULL;
2000 current = current->nextMenuSpec)
2002 FindClientCommandMatch(current, command_string, &match);
2003 if (match != (MenuItem *) NULL)
2005 if (duplicate_globals == TRUE)
2007 /* Create a duplicate of the current menuspec and
2008 store that away in the client instead of the current
2010 newMenuSpec = ReplaceMenuSpecForClient(current, pCD);
2012 /* Now store enough information so that we can actually
2013 create the insertion later. */
2014 StoreMatchedCommand(client_match_list, newMenuSpec, NULL,
2015 command_string, treenode, function,
2016 funcargs, greyed_context);
2018 else /* Change global menu */
2020 StoreMatchedCommand(global_match_list, current, match,
2021 command_string, treenode, function,
2022 funcargs, greyed_context);
2029 /*************************************<->*************************************
2031 * InsertTreeOnClient (pSD, pCD, tree, client_match_list, global_match_list,
2032 * menuSpec, templateMenuItem, command_so_far,
2033 * duplicate_globals, selection, greyed_context, inLine)
2049 * If duplicate_globals is TRUE, then pCD cannot be NULL.
2051 *************************************<->***********************************/
2053 static void InsertTreeOnClient (WmScreenData *pSD, ClientData *pCD,
2055 MatchList **client_match_list,
2056 MatchList **global_match_list,
2057 MenuSpec *menuSpec, MenuItem *template,
2058 String command_so_far,
2059 Boolean duplicate_globals, Atom selection,
2060 Context greyed_context, Boolean inLine)
2062 String new_command_str;
2064 char funcarg_buf[256];
2065 MenuSpec *newMenuSpec, *last, *dupMenuSpec;
2067 MenuItem *bestMatch = (MenuItem *) NULL;
2069 /* If the menuSpec we were given is NULL, then just return. We need to
2070 at least have a starting menuSpec. */
2071 if (menuSpec == (MenuSpec *) NULL)
2074 /* If we want global menus duplicated for a client, then the pCD
2075 had better not be NULL. */
2076 if (duplicate_globals && pCD == (ClientData *) NULL)
2079 while (tree != (CmdTree *) NULL)
2081 /* The "4" below is to allow for brackets to surround the
2082 tree->name, the period to separate it from the command
2083 so far and a NULL. */
2084 length = (command_so_far != NULL ? strlen(command_so_far) : 0) +
2085 (tree->name != NULL ? strlen(tree->name) : 0) + 4;
2086 new_command_str = XtMalloc(sizeof(unsigned char) * length);
2087 if (command_so_far != (String) NULL)
2088 sprintf(new_command_str, "%s.<%s>", command_so_far, tree->name);
2090 sprintf(new_command_str, "<%s>", tree->name);
2092 /* If there is an exclusion preventing this command from being
2093 inserted, then just continue the loop. @RGC: This is wrong.
2094 We still want to search for other matches if there is an
2095 exclusion. We just don't want to allow one of those other
2096 matches to be this menuSpec. */
2097 if (ExcludeClientCommand(menuSpec, new_command_str))
2100 XtFree(new_command_str);
2104 /* If tree is a command set and the inLine flag is TRUE then
2105 * we need to insert the command sets commands in the current
2106 * menu spec instead of creating a cascade.
2108 if (tree->subTrees != (CmdTree *) NULL && inLine == TRUE &&
2109 ForceInLineToCascade(menuSpec, new_command_str,
2110 &bestMatch) == FALSE)
2112 /* Recursively process subtrees */
2113 for (subtree = tree->subTrees;
2114 subtree != (CmdTree *) NULL;
2115 subtree = subtree->next)
2117 /* Pass along the bestMatch. If it is a valid menu item
2118 then we want the insertion to occur at that menuitem
2119 instead of at the end of the menu. */
2120 InsertTreeOnClient(pSD, pCD, subtree, client_match_list,
2121 global_match_list, menuSpec, bestMatch,
2122 new_command_str, duplicate_globals,
2123 selection, greyed_context, inLine);
2125 /* We don't want to search for other matches because we
2126 want the items to be inserted inLine. Any other matches
2127 will be found in the recursive calls. (??? or am I wrong?) */
2129 /* If tree is a command set then we need to create a new
2131 else if (tree->subTrees != (CmdTree *) NULL)
2133 /* Create the name of the menu for the f.menu command. */
2134 sprintf(funcarg_buf, "<%s>", tree->name);
2136 /* Store the cascade button information so it can be
2138 StoreMatchedCommand(
2139 (menuSpec->clientLocal ? client_match_list : global_match_list),
2140 menuSpec, template, new_command_str, tree, F_Menu, funcarg_buf,
2143 /* We need to create a menu spec for the menu that this cascade
2144 button will cascade to. Try to find one in the clients menu
2145 spec list, stopping the first time we hit a global menu. If we
2146 can't find one there and if we are *not* supposed to duplicate
2147 globals, then try to find it in the global list. In all other
2148 cases, create a new one using the funcarg_buf that we created
2149 above as the name of the menuspec. */
2150 for (newMenuSpec = (pCD == NULL ? NULL : pCD->systemMenuSpec);
2151 newMenuSpec != (MenuSpec *) NULL;
2152 newMenuSpec = newMenuSpec->nextMenuSpec)
2154 if (newMenuSpec->clientLocal == FALSE)
2156 newMenuSpec = (MenuSpec *) NULL;
2159 if (strcmp(newMenuSpec->name, funcarg_buf) == 0)
2163 /* If we didn't find it in the client list, maybe we should
2164 look in the global list. */
2165 if (newMenuSpec == (MenuSpec *) NULL && duplicate_globals == FALSE)
2167 for (newMenuSpec = pSD->menuSpecs;
2168 newMenuSpec != (MenuSpec *) NULL;
2169 newMenuSpec = newMenuSpec->nextMenuSpec)
2171 if (strcmp(newMenuSpec->name, funcarg_buf) == 0)
2176 /* If we still don't have a menu spec, then create a new one. */
2177 if (newMenuSpec == (MenuSpec *) NULL)
2179 newMenuSpec = MakeMenuSpec(funcarg_buf,
2180 tree == NULL ? (CARD32)NULL
2182 if (duplicate_globals) newMenuSpec->clientLocal = TRUE;
2183 else newMenuSpec->clientLocal = FALSE;
2185 /* If we are duplicating globals, then add the new menu spec
2186 to the client list. Otherwise add it to the global list. */
2187 if (duplicate_globals)
2188 last = pCD->systemMenuSpec;
2190 last = pSD->menuSpecs;
2192 /* Find the last menu spec in the list. */
2193 while (last != (MenuSpec *) NULL &&
2194 last->nextMenuSpec != (MenuSpec *) NULL)
2195 last = last->nextMenuSpec;
2197 /* Put the new menu spec at the end of the list. */
2198 if (last == (MenuSpec *) NULL)
2200 if (duplicate_globals)
2201 pCD->systemMenuSpec = newMenuSpec;
2203 pSD->menuSpecs = newMenuSpec;
2205 else last->nextMenuSpec = newMenuSpec;
2208 /* Recursively process subtrees */
2209 for (subtree = tree->subTrees;
2210 subtree != (CmdTree *) NULL;
2211 subtree = subtree->next)
2213 InsertTreeOnClient(pSD, pCD, subtree, client_match_list,
2214 global_match_list, newMenuSpec, NULL,
2215 new_command_str, duplicate_globals,
2216 selection, greyed_context, inLine);
2219 /* Search for any other matches in the existing menu specs
2220 for this command, excluding newMenuSpec. */
2221 SearchForOtherMatches(pSD, pCD, tree,
2222 client_match_list, global_match_list,
2223 newMenuSpec, new_command_str,
2224 F_Menu, funcarg_buf,
2225 duplicate_globals, selection,
2226 greyed_context, inLine);
2229 else /* the tree is a simple command */
2231 /* Store away the push button information so it can be
2233 sprintf(funcarg_buf, "%d %ld %ld", tree->commandID,
2234 (pCD == NULL ? None : pCD->client), selection);
2236 /* If the menuSpec is global and we are supposed to be
2237 duplicating global menu specs, then create a duplicate
2238 and replace the menuspec with the duplicate for this
2240 if (duplicate_globals)
2241 dupMenuSpec = ReplaceMenuSpecForClient(menuSpec, pCD);
2243 dupMenuSpec = menuSpec;
2245 /* Now store the match away in the appropriate match list */
2246 StoreMatchedCommand((dupMenuSpec->clientLocal ?
2247 client_match_list : global_match_list),
2248 dupMenuSpec, template, new_command_str, tree,
2249 F_InvokeCommand, funcarg_buf, greyed_context);
2251 /* Search for any other matches in the existing menu specs
2252 for this command, excluding newMenuSpec. */
2254 SearchForOtherMatches(pSD, pCD, tree,
2255 client_match_list, global_match_list,
2256 dupMenuSpec, new_command_str,
2257 F_InvokeCommand, funcarg_buf,
2258 FALSE, /* Don't duplicate globals not associated
2259 with this pCD. CR 9623 */
2261 greyed_context, inLine);
2264 /* Move on to the next tree item at this level */
2266 XtFree(new_command_str);
2271 /*************************************<->*************************************
2273 * InsertTreeOnAllClients (pSD, tree, selection, active_context, inLine)
2281 * pSD = per screen data
2282 * tree = command tree
2283 * selection = owned by inserting client
2293 *************************************<->***********************************/
2294 void InsertTreeOnAllClients (WmScreenData *pSD, CmdTree *tree, Atom selection,
2295 Context active_context, Boolean inLine)
2297 ClientListEntry *current;
2298 MatchList *global_matchlist = (MatchList *) NULL;
2299 MatchList *client_matchlist = (MatchList *) NULL;
2300 Context greyed_context = F_CONTEXT_ALL;
2302 /* If there aren't any clients, then there's nothing to do. */
2303 if (pSD->clientList == (ClientListEntry *) NULL)
2306 /* Setup the greyed context based on the active context */
2307 if (active_context & F_CONTEXT_WINDOW)
2308 greyed_context &= ~(F_CONTEXT_WINDOW);
2309 if (active_context & F_CONTEXT_ICON)
2310 greyed_context &= ~(F_CONTEXT_ICON);
2312 for (current = pSD->clientList;
2313 current != (ClientListEntry *) NULL;
2314 current = current->nextSibling)
2316 /* Ignore client list entries for icons. */
2317 if (current->type == MINIMIZED_STATE)
2319 InsertTreeOnClient(pSD, current->pCD, tree, &client_matchlist,
2320 &global_matchlist, current->pCD->systemMenuSpec,
2322 selection, greyed_context, inLine);
2323 PerformInsertionsOnMatchList(&client_matchlist);
2324 RecreateMenuWidgets(pSD, current->pCD, &client_matchlist);
2325 FreeMatchList(&client_matchlist);
2327 PerformInsertionsOnMatchList(&global_matchlist);
2328 RecreateMenuWidgets(pSD, NULL /* no pcd */, &global_matchlist);
2329 FreeMatchList(&global_matchlist);
2333 /*************************************<->*************************************
2335 * InsertTreeOnSingleClient (pSD, pCD, tree, selection, inLine)
2343 * pSD = per screen data
2344 * tree = command tree
2345 * selection = owned by inserting client
2356 *************************************<->***********************************/
2357 void InsertTreeOnSingleClient (WmScreenData *pSD, ClientData *pCD,
2358 CmdTree *tree, Atom selection,
2359 Context active_context, Boolean inLine)
2361 MatchList *global_matchlist = (MatchList *) NULL;
2362 MatchList *client_matchlist = (MatchList *) NULL;
2363 Context greyed_context = F_CONTEXT_ALL;
2365 /* A quick sanity check */
2366 if (pCD == (ClientData *) NULL)
2369 /* Setup the greyed context based on the active context */
2370 if (active_context & F_CONTEXT_WINDOW)
2371 greyed_context &= ~(F_CONTEXT_WINDOW);
2372 if (active_context & F_CONTEXT_ICON)
2373 greyed_context &= ~(F_CONTEXT_ICON);
2375 InsertTreeOnClient(pSD, pCD, tree, &client_matchlist,
2376 &global_matchlist, pCD->systemMenuSpec,
2377 NULL, NULL, TRUE, selection, greyed_context, inLine);
2378 PerformInsertionsOnMatchList(&client_matchlist);
2379 RecreateMenuWidgets(pSD, pCD, &client_matchlist);
2380 FreeMatchList(&client_matchlist);
2384 /*************************************<->*************************************
2386 * InsertTreeOnRootMenu (pSD, tree, selection, active_context, inLine)
2394 * pSD = per screen data
2395 * tree = command tree
2396 * selection = owned by inserting client
2407 *************************************<->***********************************/
2408 void InsertTreeOnRootMenu (WmScreenData *pSD, CmdTree *tree, Atom selection,
2411 MatchList *global_matchlist = (MatchList *) NULL;
2412 MatchList *client_matchlist = (MatchList *) NULL;
2413 Context greyed_context = F_CONTEXT_WINDOW | F_CONTEXT_ICON;
2416 /* Find the root menu spec */
2417 for (rootMenu = pSD->menuSpecs;
2418 rootMenu != (MenuSpec *) NULL;
2419 rootMenu = rootMenu->nextMenuSpec)
2421 if (strcmp(rootMenu->name, pSD->rootMenu) == 0)
2425 /* If we couldn't find the root menu, then do nothing. */
2426 if (rootMenu == (MenuSpec *) NULL) return;
2428 InsertTreeOnClient(pSD, NULL, tree, &client_matchlist,
2429 &global_matchlist, rootMenu,
2430 NULL, NULL, FALSE, selection, greyed_context, inLine);
2431 PerformInsertionsOnMatchList(&client_matchlist);
2432 RecreateMenuWidgets(pSD, NULL, &client_matchlist);
2433 FreeMatchList(&client_matchlist);
2434 PerformInsertionsOnMatchList(&global_matchlist);
2435 RecreateMenuWidgets(pSD, NULL, &global_matchlist);
2436 FreeMatchList(&global_matchlist);
2440 /*************************************<->*************************************
2442 * RemoveClientCommandFromMenuSpec (menuSpec, id)
2457 *************************************<->***********************************/
2458 static Boolean RemoveClientCommandFromMenuSpec (MenuSpec *menuSpec,
2461 MenuItem *curMenuItem, *prevMenuItem = (MenuItem *) NULL;
2462 MenuItem *tmpMenuItem;
2463 Boolean was_changed = FALSE;
2465 curMenuItem = menuSpec->menuItems;
2466 while (curMenuItem != (MenuItem *) NULL)
2468 if (curMenuItem->clientCommandID == id)
2470 tmpMenuItem = curMenuItem;
2471 curMenuItem = curMenuItem->nextMenuItem;
2472 if (prevMenuItem == (MenuItem *) NULL)
2473 menuSpec->menuItems = tmpMenuItem->nextMenuItem;
2475 prevMenuItem->nextMenuItem = tmpMenuItem->nextMenuItem;
2476 FreeMenuItem(tmpMenuItem);
2481 prevMenuItem = curMenuItem;
2482 curMenuItem = curMenuItem->nextMenuItem;
2485 return(was_changed);
2489 /*************************************<->*************************************
2491 * ModifyClientCommandForMenuSpec (menuSpec, id, modifier, context, newname)
2506 *************************************<->***********************************/
2507 static Boolean ModifyClientCommandForMenuSpec (MenuSpec *menuSpec,
2509 CmdModifier modifier,
2513 MenuItem *curMenuItem;
2514 Boolean was_changed = FALSE;
2515 int i, freebutton, buttoncount;
2517 /* If the menuspec doesn't have any buttons or a valid menu widget
2518 then we don't want to search it. */
2519 if (menuSpec->menuWidget == (Widget) NULL ||
2520 menuSpec->menuButtons == (MenuButton *) NULL ||
2521 menuSpec->menuButtonCount == 0)
2524 /* Search through all the menu buttons of the menuspec for buttons
2525 which match the command ID to be removed. */
2526 for (i = 0; i < menuSpec->menuButtonCount; ++i)
2528 curMenuItem = menuSpec->menuButtons[i].menuItem;
2530 if ((curMenuItem->clientCommandID == id) &&
2531 (curMenuItem->wmFunction == F_InvokeCommand))
2536 /* "context" is an active context */
2537 curMenuItem->greyedContext &= ~(context);
2538 /* Adjust the pushbutton to the state it would have had
2539 given the last posting context. */
2540 if (menuSpec->currentContext & curMenuItem->greyedContext)
2541 XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2544 XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2548 /* "context" is a greyed context */
2549 curMenuItem->greyedContext |= context;
2550 /* Adjust the pushbutton to the state it would have had
2551 given the last posting context. */
2552 if (menuSpec->currentContext & curMenuItem->greyedContext)
2553 XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2556 XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2560 if (newname != NULL && *newname != '\0')
2562 /* When renaming a command, we shouldn't cause the
2563 * entire menu to be recreated. Recreating the menu
2564 * will cause problems with tearoffs since the menu
2565 * will disappear when it is destroyed. CR 9719
2567 XmString labelString;
2569 /* Change the label of the menu item */
2570 XtFree(curMenuItem->label);
2571 /* Give the menu item the new name */
2572 curMenuItem->label = XtNewString(newname);
2573 was_changed = False; /* all taken care of here. */
2575 /* This is needed when changing the label since
2576 * mwm will wait for a geometry reply from itself which
2577 * it can never service. CR 9719
2579 XtVaSetValues(XtParent(XtParent(menuSpec->menuButtons[i].buttonWidget)),
2580 XmNuseAsyncGeometry, TRUE, NULL);
2582 labelString = XmStringGenerate(curMenuItem->label,
2583 XmFONTLIST_DEFAULT_TAG,
2584 XmCHARSET_TEXT, NULL);
2585 XtVaSetValues(menuSpec->menuButtons[i].buttonWidget,
2586 XmNlabelString, labelString,
2588 XmStringFree(labelString);
2592 XtDestroyWidget(menuSpec->menuButtons[i].buttonWidget);
2593 menuSpec->menuButtons[i].managed = FALSE;
2594 menuSpec->menuButtons[i].menuItem = (MenuItem *) NULL;
2595 menuSpec->menuButtons[i].buttonWidget = (Widget) NULL;
2601 /* If we are being asked to remove a client command, then we need
2602 * to search through all the menu items as well as the buttons.
2603 * Do the menu items here.
2605 if (modifier == REMOVE)
2606 was_changed = RemoveClientCommandFromMenuSpec(menuSpec, id);
2608 /* Compact the menu buttons array. */
2609 buttoncount = menuSpec->menuButtonCount;
2611 for (i = 0; i < buttoncount; ++i)
2613 if (menuSpec->menuButtons[i].buttonWidget == (Widget) NULL)
2614 --menuSpec->menuButtonCount;
2617 menuSpec->menuButtons[freebutton].menuItem =
2618 menuSpec->menuButtons[i].menuItem;
2619 menuSpec->menuButtons[freebutton].buttonWidget =
2620 menuSpec->menuButtons[i].buttonWidget;
2621 menuSpec->menuButtons[freebutton].managed =
2622 menuSpec->menuButtons[i].managed;
2626 return(was_changed);
2630 /*************************************<->*************************************
2632 * ModifyClientCommandID (pSD, pCD, range, id, modifier, context, newname)
2647 *************************************<->***********************************/
2648 static void ModifyClientCommandID (WmScreenData *pSD,
2652 CmdModifier modifier,
2656 MenuSpec *curMenuSpec;
2657 ClientListEntry *curClient;
2662 /* Search through all the menu specs of all the clients. */
2663 for (curClient = pSD->clientList;
2664 curClient != (ClientListEntry *) NULL;
2665 curClient = curClient->nextSibling)
2667 for (curMenuSpec = curClient->pCD->systemMenuSpec;
2668 curMenuSpec != (MenuSpec *) NULL;
2669 curMenuSpec = curMenuSpec->nextMenuSpec)
2671 /* If the menu spec is global then stop searching
2673 if (curMenuSpec->clientLocal == FALSE)
2675 if (ModifyClientCommandForMenuSpec(curMenuSpec, id,
2679 DestroyMenuSpecWidgets(curMenuSpec);
2680 curMenuSpec->menuWidget =
2681 CreateMenuWidget (pSD, curClient->pCD, curMenuSpec->name,
2682 pSD->screenTopLevelW, TRUE,
2687 /* Search through all the global menu specs. */
2688 for (curMenuSpec = pSD->menuSpecs;
2689 curMenuSpec != (MenuSpec *) NULL;
2690 curMenuSpec = curMenuSpec->nextMenuSpec)
2692 if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2693 context, newname) == TRUE)
2695 DestroyMenuSpecWidgets(curMenuSpec);
2696 curMenuSpec->menuWidget =
2697 CreateMenuWidget (pSD, NULL, curMenuSpec->name,
2698 pSD->screenTopLevelW, TRUE,
2706 * This section was changed to search the entire global menu list.
2707 * This was done to allow access to menu entries included using the
2708 * cci/.mwmrc interface. Before, only the actual root menu could
2709 * be modified; however, the user could still include commands in
2710 * other menus specified in the .mwmrc file using the f.cci command.
2714 MenuSpec *curMenuSpec;
2716 /* Search through all the global menu specs. */
2717 for (curMenuSpec = pSD->menuSpecs;
2718 curMenuSpec != (MenuSpec *) NULL;
2719 curMenuSpec = curMenuSpec->nextMenuSpec)
2721 if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2722 context, newname) == TRUE)
2724 DestroyMenuSpecWidgets(curMenuSpec);
2725 curMenuSpec->menuWidget =
2726 CreateMenuWidget (pSD, NULL, curMenuSpec->name,
2727 pSD->screenTopLevelW, TRUE,
2734 /* If we weren't passed a valid pCD, then just return. */
2735 if (pCD == (ClientData *) NULL) return;
2737 /* Search through the clients menu specs. If we find one that
2738 is global then stop search if we are ENABLING or DISABLING.
2739 If we are REMOVING and we find a global, we may need to
2740 perform some menu spec replacing to make the menu spec that
2741 needs modification local to the client. */
2742 for (curMenuSpec = pCD->systemMenuSpec;
2743 curMenuSpec != (MenuSpec *) NULL;
2744 curMenuSpec = curMenuSpec->nextMenuSpec)
2746 if (curMenuSpec->clientLocal == FALSE)
2748 MenuSpec *last, *cur;
2750 /* Find the last global menuspec in the clients list
2751 that needs to be changed and return it. Replace
2752 all menuspecs between the current one and the
2753 "last" one that needs changing. All the replaced
2754 menuspecs will be marked as local, so that next
2755 time clientLocal is FALSE in the enclosing for
2756 loop above, there will be no global menu specs
2757 needing changes. In other words, all the required
2758 menu spec replacing will occur the first time we
2759 find a global menu spec. */
2760 last = FindLastMenuSpecToModify(curMenuSpec, id);
2761 if (last != (MenuSpec *) NULL)
2763 MenuSpec *newMenuSpec = (MenuSpec *) NULL;
2764 MenuSpec *firstMenuSpec = (MenuSpec *) NULL;
2765 MenuSpec *lastMenuSpec = (MenuSpec *) NULL;
2767 /* Replace all the global menu specs with local
2769 for (cur = curMenuSpec;
2770 cur != (MenuSpec *) NULL && cur != last->nextMenuSpec;
2771 cur = cur->nextMenuSpec)
2773 newMenuSpec = ReplaceMenuSpecForClient(cur, pCD);
2774 if (cur == curMenuSpec)
2775 curMenuSpec = firstMenuSpec = newMenuSpec;
2776 /* If there is only one menu spec to change,
2777 the first will also be the last. */
2779 lastMenuSpec = newMenuSpec;
2782 /* Now that we have replaced all the menu specs,
2783 recreate all the widgets for the new menu specs. */
2784 for (cur = firstMenuSpec;
2785 cur != (MenuSpec *) NULL &&
2786 cur != lastMenuSpec->nextMenuSpec;
2787 cur = cur->nextMenuSpec)
2789 DestroyMenuSpecWidgets(newMenuSpec);
2790 newMenuSpec->menuWidget =
2791 CreateMenuWidget(pSD, pCD, newMenuSpec->name,
2792 pSD->screenTopLevelW,
2793 TRUE, newMenuSpec, NULL);
2797 /* None of the globals need changing. */
2800 if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2801 context, newname) == TRUE)
2803 DestroyMenuSpecWidgets(curMenuSpec);
2804 curMenuSpec->menuWidget =
2805 CreateMenuWidget (pSD, pCD, curMenuSpec->name,
2806 pSD->screenTopLevelW, TRUE,
2815 /*************************************<->*************************************
2817 * ModifyClientCommandTree (pSD, pCD, range, tree, modifier, context, newname)
2832 *************************************<->***********************************/
2833 void ModifyClientCommandTree (WmScreenData *pSD,
2837 CmdModifier modifier,
2844 /* Run through the top level of the tree. */
2845 for (curTree = tree; curTree != (CmdTree *) NULL; curTree = curTree->next)
2847 cmdID = curTree->commandID;
2848 ModifyClientCommandID(pSD, pCD, range, cmdID, modifier,
2850 if (curTree->subTrees != (CmdTree *) NULL)
2851 ModifyClientCommandTree(pSD, pCD, range, curTree->subTrees,
2852 modifier, context, newname);
2855 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
2857 /*************************************<->*************************************
2860 * AdjustPBs (menuSpec, pCD, newContext)
2865 * This procedure adjusts menu PushButton sensitivities and manage/unmanaged
2866 * status for a toplevel menu.
2871 * menuSpec = nonNULL toplevel menu specification with gadget
2873 * newContext = context that the menu is to be posted under.
2878 * menuSpec = menu specification with modifications
2879 * Return = TRUE iff at least one menu item changed manage status.
2884 * Adjusts PushButton sensitivity according to context and function type.
2885 * Manages/Unmanages PushButtons according to clientFunction resource.
2887 *************************************<->***********************************/
2888 static Boolean AdjustPBs (MenuSpec *menuSpec, ClientData *pCD,
2891 MenuButton *menuButton;
2897 Boolean fChangeManaged = FALSE;
2900 * Set PushButton sensitivity.
2901 * Set f.send_msg button sensitivity according to context and client
2902 * message list. Adjust other button sensitivities only for context.
2905 /* check for bad input value - shouldn't happen. */
2906 if (menuSpec == NULL) return (FALSE);
2908 for (n = 0, menuButton = menuSpec->menuButtons;
2909 n < menuSpec->menuButtonCount;
2912 menuItem = menuButton->menuItem;
2913 if (menuItem->wmFunction == F_Send_Msg)
2914 /* f.send_msg button: set according to context and message. */
2916 if ((newContext & menuItem->greyedContext) ||
2917 !(pCD && pCD->mwmMessagesCount && pCD->mwmMessages))
2918 /* insensitive context or empty client message list */
2920 XtSetSensitive (menuButton->buttonWidget, FALSE);
2924 * Have a context sensitive f.send_msg item and a client with a
2925 * nonempty message list. Set sensitive only if the message is
2926 * supported by this client. Otherwise set insensitive.
2929 msgc = pCD->mwmMessagesCount;
2930 pMsg = pCD->mwmMessages;
2933 /* scan nonempty message list */
2935 if (*pMsg == (long) menuItem->wmFuncArgs)
2941 pMsg++; /* next message in list */
2943 XtSetSensitive (menuButton->buttonWidget, fSupported);
2948 * Non f.send_msg button:
2949 * Adjust sensitivity according to context.
2950 * Manage/Unmanage according to clientFunction.
2953 if (menuSpec->currentContext & menuItem->greyedContext)
2954 /* button is currently insensitive */
2956 if (!(newContext & menuItem->greyedContext))
2957 /* insensitive -> sensitive */
2959 XtSetSensitive (menuButton->buttonWidget, TRUE);
2963 /* button is currently sensitive */
2965 if (newContext & menuItem->greyedContext)
2966 /* sensitive -> insensitive */
2968 XtSetSensitive (menuButton->buttonWidget, FALSE);
2972 if (menuItem->wmFunction == F_Remove)
2975 * Only allow remove from workspace if the client
2976 * is in more than one workspace
2978 fSupported = (pCD && (pCD->numInhabited > 1));
2979 XtSetSensitive (menuButton->buttonWidget, fSupported);
2983 if ((menuItem->mgtMask) && pCD)
2984 /* PushButton might not apply */
2987 if ((pCD->clientFunctions & menuItem->mgtMask & MWM_MGT_MASK) ||
2988 (pCD->dtwmFunctions & menuItem->mgtMask & DTWM_MGT_MASK))
2990 if (pCD->clientFunctions & menuItem->mgtMask)
2992 /* function applies -- manage it */
2994 if (!menuButton->managed)
2995 /* unmanaged -> managed */
2997 XtManageChild (menuButton->buttonWidget);
2998 menuButton->managed = TRUE;
2999 fChangeManaged = TRUE;
3000 if (n == menuSpec->menuButtonCount - 1)
3003 * last item, if it has a separator before
3004 * it, manage the separator
3007 CheckTerminalSeparator(menuSpec,
3008 menuButton->buttonWidget,
3014 /* function does not apply -- unmanage it */
3016 if (menuButton->managed)
3017 /* managed -> unmanaged */
3019 XtUnmanageChild (menuButton->buttonWidget);
3020 menuButton->managed = FALSE;
3021 fChangeManaged = TRUE;
3023 if (n == menuSpec->menuButtonCount - 1)
3026 * last item, if it has a separator before
3027 * it, unmanage the separator
3029 CheckTerminalSeparator(menuSpec,
3030 menuButton->buttonWidget,
3037 else if (!menuButton->managed)
3038 /* unmanaged PushButton applies */
3040 XtManageChild (menuButton->buttonWidget);
3041 menuButton->managed = TRUE;
3042 fChangeManaged = TRUE;
3047 return (fChangeManaged);
3049 } /* END OF FUNCTION AdjustPBs */
3053 /*************************************<->*************************************
3056 * SavePBInfo (topMenuSpec, menuItem, itemW)
3061 * Fills a MenuButton structure for a PushButton.
3062 * If necessary, mallocs or reallocs the menuButtons array in the toplevel
3068 * topMenuSpec = pointer to toplevel MenuSpec structure
3069 * menuItem = pointer to PushButton MenuItem structure
3070 * itemW = PushButton gadget
3071 * topMenuSpec->menuButtons[]
3072 * topMenuSpec->menuButtonSize
3073 * topMenuSpec->menuButtonCount
3078 * Return = FALSE iff insufficient memory for malloc or realloc
3079 * or bad input value forces exit.
3080 * topMenuSpec->menuButtons[]
3081 * topMenuSpec->menuButtonSize
3082 * topMenuSpec->menuButtonCount
3087 * The initial managed status of PushButtons is TRUE.
3089 *************************************<->***********************************/
3090 static Boolean SavePBInfo (MenuSpec *topMenuSpec, MenuItem *menuItem,
3093 MenuButton *menuButton;
3096 /* check for bad input value - shouldn't happen. */
3097 if (topMenuSpec == NULL) return (FALSE);
3099 if (topMenuSpec->menuButtonSize == 0)
3100 /* need to create array */
3102 topMenuSpec->menuButtonSize = MENU_BUTTON_INC;
3103 topMenuSpec->menuButtons =
3104 (MenuButton *) XtMalloc (MENU_BUTTON_INC * sizeof(MenuButton));
3106 else if (topMenuSpec->menuButtonCount == topMenuSpec->menuButtonSize)
3107 /* need larger array */
3109 topMenuSpec->menuButtonSize += MENU_BUTTON_INC;
3110 topMenuSpec->menuButtons = (MenuButton *)
3111 XtRealloc ((char*)topMenuSpec->menuButtons,
3112 topMenuSpec->menuButtonSize * sizeof(MenuButton));
3115 if (topMenuSpec->menuButtons == NULL)
3116 /* insufficent memory */
3118 topMenuSpec->menuButtonSize = 0;
3119 topMenuSpec->menuButtonCount = 0;
3123 menuButton = &(topMenuSpec->menuButtons[topMenuSpec->menuButtonCount]);
3124 topMenuSpec->menuButtonCount++;
3126 menuButton->menuItem = menuItem;
3127 menuButton->buttonWidget = itemW;
3128 menuButton->managed = TRUE;
3135 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3136 /*************************************<->*************************************
3138 * AdjustTearOffControl (cascade, closure, cbackdata)
3152 * returns true iff the tearoff control was enabled or diabled
3153 * resulting in a change in height.
3158 *************************************<->***********************************/
3160 AdjustTearOffControl (Widget cascade,
3162 XtPointer cbackdata)
3164 Widget submenu = (Widget) closure;
3167 unsigned char tearoff_model;
3171 XtSetArg(args[argn], XmNtearOffModel, &tearoff_model); ++argn;
3172 XtGetValues(submenu, args, argn);
3174 /* Is this a root menu or a cascade of a root menu? */
3175 /* If cbackdata is not null, then we got here by cascading. */
3176 /* Cascade menus from a tearoff menu-pane are not allowed. */
3177 /* there is no way to know if the cascade is from a tearoff */
3178 /* or from a cascade on a system menu. */
3179 if ((wmGD.menuClient == NULL) && (cbackdata == NULL))
3184 if ((tearoff_model == XmTEAR_OFF_ENABLED) && !isMwmMenu)
3186 PRINT("Disabling the tear off\n");
3188 XtSetArg(args[argn], XmNtearOffModel, XmTEAR_OFF_DISABLED); ++argn;
3189 XtSetValues(submenu, args, argn);
3194 /* If this was invoked as a cascadingCallback and not by hand and if
3195 the menuActive field of the global data has not yet been set, then
3196 we can safely assume that we have just cascaded off of a torn off
3197 menu. In that case, set the menuActive field to be the menu spec of
3198 the torn off menu and register an unmap callback on the cascaded
3199 menu that will clear the menuActive field. */
3200 if (cbackdata != (XtPointer) NULL && wmGD.menuActive == (MenuSpec *) NULL)
3203 Widget tearoff_widget = XtParent(cascade);
3205 for (menuspec = wmGD.Screens[0].menuSpecs;
3206 menuspec != (MenuSpec *) NULL;
3207 menuspec = menuspec->nextMenuSpec)
3209 if (tearoff_widget == menuspec->menuWidget)
3211 wmGD.menuActive = menuspec;
3216 /* If we can't find a menuspec for the torn off menu, then just
3217 take the first menu spec in the list of menuSpecs for the
3218 active pSD. NOTE: THIS SHOULD NEVER HAPPEN. In fact if it
3219 does, I'm not sure how mwm will behave having been given
3220 the wrong menu spec as the active menu. */
3221 if (wmGD.menuActive == (MenuSpec *) NULL)
3223 wmGD.menuActive = ACTIVE_PSD->menuSpecs;
3224 PRINT("Couldn't find menu spec for tear off\n");
3227 /* Add a callback that will clear menuActive when this cascade
3230 XtAddCallback (submenu, XmNunmapCallback,
3232 XtAddCallback (XtParent(submenu), XmNpopdownCallback,
3234 UnmapPulldownCallback,
3242 /*************************************<->*************************************
3245 * CreateClientCommandSeparator (menuItem, child_position, last_child,
3262 *************************************<->***********************************/
3263 static Boolean CreateClientCommandSeparator (MenuItem *menuItem,
3266 MenuItem **newMenuItem)
3268 MenuItem *curMenuItem;
3270 /* If it is a client command, then we only want to create the
3271 * separator under particular circumstances. Specifically, we
3272 * want to make sure that:
3273 * 1. a separator doesn't directly precede this one
3274 * 2. a separator doesn't directly follow this one
3275 * 3. this separator won't be the first or last item in the menu
3276 * 4. the client command that this separator surrounds actually
3277 * matched something and is not an unmatched template
3280 /* Check if a separator directly precedes this one. */
3281 if (child_position > 0 && last_child != (Widget) NULL &&
3282 XmIsSeparator(last_child))
3285 /* Check if a separator directly follows this one. */
3286 if (menuItem->nextMenuItem != (MenuItem *) NULL &&
3287 menuItem->nextMenuItem->wmFunction == F_Separator &&
3288 IsClientCommand(menuItem->nextMenuItem->label) == FALSE)
3291 /* Make sure this separator won't be the first item in the menu. */
3292 if (child_position == 0) return(FALSE);
3294 /* Make sure this separator won't be the last item in the menu. */
3295 if (menuItem->nextMenuItem == (MenuItem *) NULL)
3298 /* Make sure that the client command this separator surrounds actually
3299 matches something. We only do this check if the separator is the
3300 TOP separator in the separator pair. If we are looking at a bottom
3301 separator then we can safely assume something matched, otherwise
3302 we would have passed over it when we look at the corresponding top
3304 if (menuItem->labelType == TOP_SEPARATOR)
3306 /* If we find a real menu item (not just a template) before we find
3307 a bottom separator, then create the separator. */
3308 for (curMenuItem = menuItem;
3309 curMenuItem != (MenuItem *) NULL;
3310 curMenuItem = curMenuItem->nextMenuItem)
3312 /* If we found the closing separator, then return FALSE and
3313 our new menu item position. */
3314 if (curMenuItem->wmFunction == F_Separator &&
3315 IsClientCommand(curMenuItem->label) &&
3316 curMenuItem->labelType == BOTTOM_SEPARATOR)
3318 *newMenuItem = curMenuItem;
3321 /* If we found a real menu item, then return TRUE. */
3322 if (curMenuItem->wmFunction != F_Separator &&
3323 !IsClientCommand(curMenuItem->label))
3328 /* If by some bizarre chance we get to the end of the list
3329 without finding either, then return FALSE. Something is wrong. */
3330 if (curMenuItem == (MenuItem *) NULL) return(FALSE);
3333 /* Well, nothing failed so let's create it. */
3336 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3338 /*************************************<->*************************************
3340 * CreateMenuWidget (pSD, menuName, parent, fTopLevelPane, topMenuSpec,
3346 * Creates a MenuShell as a child of the specified parent widget, and a
3347 * PopupMenu or PulldownMenu as a child of the shell. Fill the menu with
3348 * the named menupane items.
3353 * pSD ---------- pointer to screen data
3354 * menuName ----- the name of the menu specification to be used to create
3356 * parent -------- parent of popup shell
3357 * fTopLevelPane - TRUE iff the menupane is a top level one
3358 * topMenuSpec --- pointer to the top menu specification.
3359 * moreMenuItems - pointer to additional menu items for custom menu.
3364 * Return = created PopupMenu or PulldownMenu widget, or NULL.
3369 * We attach a popdowncallback to the menu to set wmGD.menuActive to NULL,
3370 * allowing us to not dispatch key events separately from the toolkit
3373 *************************************<->***********************************/
3375 typedef struct _StrList
3378 struct _StrList *next;
3381 Widget CreateMenuWidget (WmScreenData *pSD,
3382 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3384 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3385 String menuName, Widget parent,
3386 Boolean fTopLevelPane, MenuSpec *topMenuSpec,
3387 MenuItem *moreMenuItems)
3392 MenuSpec *menuSpec = (MenuSpec *)NULL;
3397 Widget children[CHILDREN_CACHE];
3399 KeySpec *accelKeySpec;
3400 Dimension menuHeight;
3401 Boolean fUseTitleSep = False;
3402 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3403 Boolean labelIsClientCommand = False;
3404 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3405 StrList *stringsToFree = NULL, *sPtr;
3412 /* check for bad input values. */
3413 if ((menuName == NULL) || (pSD == NULL))
3419 * Find the menu pane specifications for menuName.
3420 * The top-level menu specification is passed as an argument (it may
3421 * be custom). A submenu specification must be found and might not exist.
3422 * Return NULL if a submenu specification is not found.
3426 menuSpec = topMenuSpec;
3430 menuSpec = pSD->menuSpecs;
3433 if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
3435 break; /* found menuName's specification */
3437 menuSpec = menuSpec->nextMenuSpec; /* keep looking */
3441 if (menuSpec == NULL)
3442 /* (submenu) specification not found */
3444 MWarning(((char *)GETMESSAGE(48, 4, "Menu specification %s not found\n")), menuName);
3449 * If menuSpec is marked, we have menu recursion => fail.
3450 * Otherwise, mark it.
3453 if (menuSpec->currentContext & CR_MENU_MARK) /* marked? */
3454 /* menu recursion */
3456 MWarning(((char *)GETMESSAGE(48, 5, "Menu recursion detected for %s\n")), menuName);
3459 menuSpec->currentContext |= CR_MENU_MARK; /* no, mark it */
3462 * Create a PopupShell widget.
3463 * If the parent of the specified parent ("grandparent") is a MenuShell
3464 * widget, then use the grandparent as the parent of the PopupShell.
3465 * Otherwise, use the specified parent.
3468 XtSetArg (args[i], XmNwidth, (XtArgVal) 5); i++;
3469 XtSetArg (args[i], XmNheight, (XtArgVal) 5); i++;
3470 XtSetArg (args[i], XmNallowShellResize, (XtArgVal) TRUE); i++;
3471 XtSetArg (args[i], XtNoverrideRedirect, (XtArgVal) TRUE); i++;
3472 XtSetArg (args[i], XtNdepth,
3473 (XtArgVal) DefaultDepth(XtDisplay(parent), pSD->screen)); i++;
3474 XtSetArg (args[i], XtNscreen,
3475 (XtArgVal) ScreenOfDisplay(XtDisplay(parent), pSD->screen)); i++;
3477 if ((XtParent (parent) != NULL) && XmIsMenuShell (XtParent (parent)))
3479 parent = XtParent (parent);
3482 menuShellW = XtCreatePopupShell (SHELL_NAME, xmMenuShellWidgetClass,
3483 parent, (ArgList) args, i);
3486 * Create a RowColumn widget as a child of the shell for the menu pane.
3487 * If the menu pane is top-level, create a popup menu for it and attach
3488 * the unmap callback to it.
3489 * Otherwise, create a pulldown menu for it.
3493 XtSetArg (args[i], XmNborderWidth, (XtArgVal) 0); i++;
3494 XtSetArg (args[i], XmNwhichButton, (XtArgVal) SELECT_BUTTON); i++;
3495 XtSetArg (args[i], XmNadjustMargin, (XtArgVal) TRUE); i++;
3499 XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_POPUP); i++;
3500 XtSetArg (args[i], XmNpopupEnabled, (XtArgVal) TRUE); i++;
3501 menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
3503 XtAddCallback (menuW, XmNunmapCallback, UnmapCallback,
3504 (XtPointer) menuSpec);
3508 XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_PULLDOWN); i++;
3509 menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
3514 * Create the specified menu entries as children of the menupane.
3515 * Menus may contain the following widgets:
3522 * Add separator gadgets around menu titles.
3525 XtSetArg (sepArgs[0], XmNseparatorType, (XtArgVal) XmDOUBLE_LINE);
3528 menuItem = menuSpec->menuItems;
3529 if ((menuItem == NULL) && (moreMenuItems != NULL))
3530 /* handle custom menu with empty standard specification */
3532 menuSpec->menuItems = menuItem = moreMenuItems;
3533 moreMenuItems = NULL;
3539 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3540 labelIsClientCommand = IsClientCommand(menuItem->label);
3541 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3543 if (menuItem->wmFunction == F_Separator)
3545 * Add a Separator gadget for a menu separator.
3546 * An immediately following title will not have a top separator.
3549 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3550 /* If it is a client command, then we only want to create the
3551 * separator under particular circumstances. Specifically, we
3552 * want to make sure that:
3553 * 1. a separator doesn't directly precede this one
3554 * 2. a separator doesn't directly follow this one
3555 * 3. this separator won't be the first or last item in the menu
3557 if (labelIsClientCommand)
3559 if (CreateClientCommandSeparator(menuItem, n,
3560 (n > 0 ? children[n - 1] :
3564 /* Increment the counter here because we only increment
3565 at the end of the loop if the item is not a client
3566 command item (i.e. labelIsClientCommand == FALSE) */
3568 XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3570 fUseTitleSep = FALSE;
3574 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3577 XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3579 fUseTitleSep = FALSE;
3584 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3585 if (!labelIsClientCommand)
3586 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3588 * We will use one of:
3596 * Construct the label
3598 if ((menuItem->labelType == XmPIXMAP) &&
3600 MakeCachedLabelPixmap (pSD, menuW,
3601 menuItem->labelBitmapIndex)))
3603 XtSetArg (args[i], XmNlabelType, (XtArgVal) XmPIXMAP); i++;
3604 XtSetArg (args[i], XmNlabelPixmap, (XtArgVal) labelPixmap); i++;
3605 XtSetArg (args[i], XmNlabelInsensitivePixmap,
3606 (XtArgVal) labelPixmap); i++;
3610 XtSetArg (args[i], XmNlabelType, (XtArgVal) XmSTRING); i++;
3611 XtSetArg (args[i], XmNlabelString, (XtArgVal)
3612 (tmpstr = XmStringCreateLocalized(menuItem->label))); i++;
3613 sPtr = (StrList *) XtMalloc(sizeof(StrList));
3616 MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
3621 sPtr->string = tmpstr;
3622 sPtr->next = stringsToFree;
3623 stringsToFree = sPtr;
3627 if (menuItem->wmFunction == F_Title)
3629 * Add a centered Label gadget for a menu title.
3630 * Include separators above and below the title.
3631 * Don't include the top one if the title is the first pane item
3632 * or immediately follows a user-supplied separator.
3638 XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3642 XtSetArg (args[i], XmNalignment, XmALIGNMENT_CENTER); i++;
3643 children[n] = XmCreateLabelGadget (menuW, TITLE_NAME,
3644 (ArgList) args, i); n++;
3645 children[n] = XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3649 * A following title will have both separators.
3652 fUseTitleSep = TRUE;
3657 * We will use one of:
3662 * Both support mnemonics; only PushButtons support accelerators.
3666 * Align text on the left.
3667 * Set any mnemonic text.
3669 XtSetArg (args[i], XmNalignment, XmALIGNMENT_BEGINNING); i++;
3671 if (menuItem->mnemonic)
3673 XtSetArg (args[i], XmNmnemonic,
3674 (XtArgVal) menuItem->mnemonic); i++;
3677 if (menuItem->wmFunction == F_Menu)
3679 * Create a PopupShell and PulldownMenu for a submenu (the
3680 * menushells are linked together).
3681 * Create a CascadeButton Widget
3682 * The submenu widget is attached to the CascadeButton gadget
3683 * using the subMenuId resource.
3684 * Make the CascadeButton insensitive if the submenu cannot be
3688 subMenuW = CREATE_MENU_WIDGET (pSD, pCD,
3689 menuItem->wmFuncArgs, menuW,
3694 * Attach submenu to cascade button.
3697 XtSetArg (args[i], XmNsubMenuId, (XtArgVal) subMenuW);
3699 children[n] = XmCreateCascadeButtonGadget (menuW,
3700 CASCADE_BTN_NAME, (ArgList) args, i);
3701 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3702 XtAddCallback(children[n], XmNcascadingCallback,
3703 (XtCallbackProc)AdjustTearOffControl,
3704 (XtPointer)subMenuW);
3705 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3709 * Unable to create submenupane: make the entry insensitive.
3712 children[n] = XmCreateCascadeButtonGadget (menuW,
3713 CASCADE_BTN_NAME, (ArgList) args, i);
3714 XtSetSensitive (children[n], FALSE);
3718 * A following title will have both separators.
3721 fUseTitleSep = TRUE;
3726 * Create a PushButton gadget.
3730 * If an accelerator is specified, set acceleratorText,
3731 * then create an accelerator KeySpec and insert it at the
3732 * head of the toplevel MenuSpec's list.
3734 if (menuItem->accelText)
3736 XtSetArg (args[i], XmNacceleratorText, (XtArgVal)
3737 (tmpstr = XmStringCreateLocalized(menuItem->accelText))); i++;
3738 sPtr = (StrList *) XtMalloc(sizeof(StrList));
3741 MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
3746 sPtr->string = tmpstr;
3747 sPtr->next = stringsToFree;
3748 stringsToFree = sPtr;
3751 if ((accelKeySpec = (KeySpec *)
3752 XtMalloc (sizeof (KeySpec ))) == NULL)
3753 /* Handle insufficent memory */
3755 MWarning (((char *)GETMESSAGE(48, 6, "Insufficient memory for menu %s\n")),
3757 menuSpec->currentContext &= ~CR_MENU_MARK;
3761 accelKeySpec->state = menuItem->accelState;
3762 accelKeySpec->keycode = menuItem->accelKeyCode;
3763 accelKeySpec->context = topMenuSpec->accelContext;
3764 accelKeySpec->subContext = 0;
3765 accelKeySpec->wmFunction = menuItem->wmFunction;
3766 accelKeySpec->wmFuncArgs = menuItem->wmFuncArgs;
3767 accelKeySpec->nextKeySpec = topMenuSpec->accelKeySpecs;
3768 topMenuSpec->accelKeySpecs = accelKeySpec;
3771 children[n] = XmCreatePushButtonGadget (menuW,
3772 PUSH_BTN_NAME, (ArgList) args, i);
3775 * Set sensitivity. Initially we only consider the context
3776 * of the top level menupane.
3779 if (menuItem->greyedContext & topMenuSpec->currentContext)
3780 /* insensitive button in this context*/
3782 XtSetSensitive (children[n], FALSE);
3785 /* sensitive button in this context*/
3787 XtSetSensitive (children[n], TRUE);
3791 * If necessary, fill a menuButtons element for this
3792 * PushButton. Malloc or Realloc the array if necessary.
3794 if ((menuItem->greyedContext) || (menuItem->mgtMask))
3796 if (!SavePBInfo (topMenuSpec, menuItem, children[n]))
3798 MWarning(((char *)GETMESSAGE(48, 7, "Insufficient memory for menu %s\n")),
3800 menuSpec->currentContext &= ~CR_MENU_MARK;
3806 * Set up the function callback.
3807 * A following title will have both separators.
3810 XtAddCallback (children[n], XmNactivateCallback,
3811 (XtCallbackProc)ActivateCallback,
3812 (XtPointer) menuItem);
3814 fUseTitleSep = TRUE;
3820 * Increment the children array count if we actually
3821 * created a new child.
3823 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3824 if (!labelIsClientCommand)
3825 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3829 * Next menu item: handle custom items and full children[].
3831 menuItem = menuItem->nextMenuItem;
3832 if ((menuItem == NULL) && (moreMenuItems != NULL))
3834 menuSpec->menuItems = menuItem = moreMenuItems;
3835 moreMenuItems = NULL;
3837 if (n >= CHILDREN_CACHE - 2) /* leave room for title separators */
3839 XtManageChildren (children, n);
3846 XtManageChildren (children, n);
3850 * Get the initial height of the top level menu pane shell.
3851 * The actual height will change according to clientFunctions.
3856 XtSetArg (args[i], XtNheight, &menuHeight); i++;
3857 XtGetValues (menuW, (ArgList)args, i);
3858 topMenuSpec->height = (unsigned int) menuHeight;
3863 * Check if the menu that's been created is higher than the screen.
3864 * If it is, force it to wrap. Taken straight from the 1.1 fix.
3868 XtSetArg (args[i], XtNheight, &menuHeight); i++;
3869 XtGetValues (menuW, (ArgList)args, i);
3870 scr = XtScreen (menuW);
3871 if (menuHeight > (Dimension)scr->height) {
3873 XtSetArg (args[i], XmNresizeHeight, (XtArgVal) FALSE); i++;
3874 XtSetArg (args[i], XmNpacking, (XtArgVal) XmPACK_TIGHT); i++;
3875 XtSetArg (args[i], XmNorientation, (XtArgVal) XmVERTICAL); i++;
3876 XtSetArg (args[i], XmNheight, scr->height); i++;
3877 XtSetValues (menuW, (ArgList)args, i);
3879 #endif /* IBM_151913 */
3881 /* free the string that may have been created earlier. */
3882 for (sPtr = stringsToFree; sPtr != NULL; )
3884 stringsToFree = stringsToFree->next;
3885 XmStringFree(sPtr->string);
3886 XtFree((char *)sPtr);
3887 sPtr = stringsToFree;
3891 /* Unmark the menu specification and return. */
3892 menuSpec->currentContext &= ~CR_MENU_MARK;
3895 } /* END OF FUNCTION CreateMenuWidget */
3899 /*************************************<->*************************************
3901 * PostMenu (menuSpec, pCD, x, y, button, newContext, flags, passedInEvent)
3906 * This function is used to post a menu at a particular location.
3911 * menuSpec = menu specification
3913 * x,y = position to post the menu if (flags & POST_AT_XY) set
3914 * button = button number posting the menu or NoButton (WmGlobal.h) if
3916 * newContext = context that the menu is to be posted under.
3917 * flags = POST_AT_XY bit set iff x,y are valid, else compute from pCD
3918 * POST_TRAVERSAL_ON bit set if set traversal on
3922 * menuSpec = menu specification with modifications
3923 * wmGD.menuClient = pCD
3924 * wmGD.menuActive = menuSpec
3929 * Accepts x,y only if POST_AT_XY flag bit set. Otherwise, computes from pCD.
3930 * Adjusts PushButton sensitivity according to context and function type.
3931 * Manages/Unmanages PushButtons according to clientFunction resource.
3932 * Sets traversal on if button==NoButton or POST_TRAVERSAL_ON flag bit set.
3934 *************************************<->***********************************/
3936 void PostMenu (MenuSpec *menuSpec, ClientData *pCD, int x, int y, unsigned int button, Context newContext, long flags, XEvent *passedInEvent)
3940 unsigned int whichButton;
3941 Dimension menuHeight;
3942 XButtonPressedEvent event;
3944 Display *saveDisplay;
3945 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3946 Boolean menuAdjusted;
3947 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3949 if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
3956 * Don't post a menu from an icon in the iconbox if the
3957 * icon is not visible
3959 if((newContext == F_SUBCONTEXT_IB_WICON ||
3960 newContext == F_SUBCONTEXT_IB_IICON) &&
3961 (!(IconVisible(pCD))))
3967 * Set grabContext to be used in GrabWin when no event is passed
3971 wmGD.grabContext = newContext;
3974 * Adjust PushButton sensitivity and manage/unmanage status.
3975 * If the manage status of the system menu has changed,
3976 * then get the height of the top level menu pane shell and
3977 * cache it in its MenuSpec.
3980 * Adjust the tear off control. If we are posting this menu from
3981 * a client then force the tear off to be disabled. NOTE: This must
3982 * be done after wmGD.menuClient has been set.
3983 * Since turning off the tear-off control could result in a height
3984 * change, we may need to remeasure things. (CR 9316)
3988 if(pCD && pCD->clientFlags & ICON_BOX)
3990 newContext |= F_CONTEXT_ICONBOX;
3995 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3997 AdjustTearOffControl(NULL, (XtPointer) (menuSpec->menuWidget), NULL);
3998 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3999 if (AdjustPBs (menuSpec, pCD, newContext)
4000 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
4002 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
4006 XtSetArg (args[i], XtNheight, &menuHeight); i++;
4007 XtGetValues (menuSpec->menuWidget, (ArgList)args, i);
4008 menuSpec->height = (unsigned int) menuHeight;
4010 menuSpec->currentContext = newContext;
4013 * Adjust the whichButton resource if necessary.
4014 * Use SELECT_BUTTON for NoButton.
4017 whichButton = (button == NoButton) ? SELECT_BUTTON : button;
4018 if (whichButton != menuSpec->whichButton)
4021 XtSetArg (args[i], XmNwhichButton, (XtArgVal) whichButton); i++;
4022 XtSetValues (menuSpec->menuWidget, args, i);
4023 menuSpec->whichButton = whichButton;
4027 * Determine the position of the popup menu.
4028 * Compute position if necessary (system menu).
4031 if (!(flags & POST_AT_XY))
4032 /* compute the position */
4034 GetSystemMenuPosition (pCD, &x, &y, menuSpec->height, newContext);
4039 XmMenuPosition (menuSpec->menuWidget, &event);
4041 wmGD.menuClient = pCD;
4042 wmGD.menuActive = menuSpec; /* set to NULL within UnmapCallback() */
4045 * Post the menu by managing its top-level RowColumn.
4047 * First dispatch the event to set the time stamp in the toolkit
4052 saveWindow = passedInEvent->xany.window;
4053 saveDisplay = passedInEvent->xany.display;
4054 passedInEvent->xany.window = 0;
4055 passedInEvent->xany.display = XtDisplay(menuSpec->menuWidget);
4057 XtDispatchEvent(passedInEvent);
4058 passedInEvent->xany.window = saveWindow;
4059 passedInEvent->xany.display = saveDisplay;
4061 /* If menu posted by ButtonPress/ButtonRelease, release grabs. */
4062 if ((passedInEvent->type == ButtonPress) ||
4063 (passedInEvent->type == ButtonRelease))
4064 XUngrabPointer(passedInEvent->xany.display,
4065 passedInEvent->xbutton.time);
4068 #ifndef ALTERNATE_POSTMENU
4070 XtManageChild (menuSpec->menuWidget);
4073 if (flags & POST_STICKY)
4075 _XmSetPopupMenuClick(menuSpec->menuWidget, True);
4079 _XmSetPopupMenuClick(menuSpec->menuWidget, False);
4083 * Post the menu by calling the convenience routine that verifies
4084 * the button event, updates the Xt timestamp, and finally manages
4088 _XmPostPopupMenu( menuSpec->menuWidget, passedInEvent);
4093 * set the traversal state.
4096 if ((button == NoButton) || (flags & POST_TRAVERSAL_ON))
4097 /* turn traversal on */
4099 TraversalOn (menuSpec);
4102 /* turn traversal off */
4104 TraversalOff (menuSpec);
4107 } /* END OF FUNCTION PostMenu */
4111 /*************************************<->*************************************
4113 * UnpostMenu (menuSpec)
4118 * This function is used to unpost a menu.
4123 * menuSpec = menu specification
4132 * wmGD.menuActive and wmGD.menuUnpostKey are set to NULL within
4135 *************************************<->***********************************/
4137 void UnpostMenu (MenuSpec *menuSpec)
4139 if (menuSpec && (menuSpec->menuWidget))
4141 * Unpost the menu by unmanaging its top-level RowColumn.
4144 XtUnmanageChild (menuSpec->menuWidget);
4145 #ifndef OLD_COLORMAP
4146 ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
4150 } /* END OF FUNCTION UnpostMenu */
4154 /*************************************<->*************************************
4156 * ActivateCallback (w, client_data, call_data)
4161 * This function is called whenever a menu item is selected.
4166 * w = menubuttonWidget
4167 * client_data = pointer to menu button's MenuItem structure
4168 * call_data = not used
4169 * wmGD.menuClient = pointer to client's ClientData structure
4181 *************************************<->***********************************/
4183 void ActivateCallback (Widget w, caddr_t client_data, caddr_t call_data)
4187 /* set active screen */
4188 pSD = GetScreenForWindow (XtWindow(w));
4189 if (pSD) SetActiveScreen (pSD);
4191 ((MenuItem *)client_data)->wmFunction (
4192 ((MenuItem *)client_data)->wmFuncArgs, wmGD.menuClient, NULL);
4194 } /* END OF FUNCTION ActivateCallback */
4198 /*************************************<->*************************************
4200 * UnmapCallback (w, client_data, call_data)
4205 * This function is called whenever a toplevel RowColumn is unmapped.
4211 * client_data = not used
4212 * call_data = not used
4213 * wmGD.gadgetClient = last client with depressed client
4218 * wmGD.menuActive = NULL
4219 * wmGD.menuUnpostKeySpec = NULL
4220 * wmGD.checkHotspot = FALSE
4227 *************************************<->***********************************/
4229 static void UnmapCallback (Widget w, XtPointer client_data,
4230 XtPointer call_data)
4232 wmGD.menuActive = NULL;
4233 wmGD.menuUnpostKeySpec = NULL;
4234 wmGD.checkHotspot = FALSE;
4236 if (wmGD.gadgetClient)
4238 PopGadgetOut(wmGD.gadgetClient, FRAME_SYSTEM);
4241 #ifndef OLD_COLORMAP
4242 ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
4244 PullExposureEvents();
4246 } /* END OF FUNCTION UnmapCallback */
4249 /*************************************<->*************************************
4251 * MWarning (message)
4256 * This function lists a message to stderr.
4261 * format = pointer to a format string
4262 * message = pointer to a message string
4264 *************************************<->***********************************/
4266 void MWarning (char *format, char *message)
4269 if (strlen(format) + strlen(message) < (size_t) MAXWMPATH)
4271 char pch[MAXWMPATH+1];
4273 sprintf (pch, format, message);
4277 } /* END OF FUNCTION MWarning */
4281 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
4282 /*************************************<->*************************************
4284 * UnmapPulldownCallback (w, client_data, call_data)
4300 *************************************<->***********************************/
4302 static void UnmapPulldownCallback (Widget w, XtPointer client_data,
4303 XtPointer call_data)
4305 wmGD.menuActive = (MenuSpec *) NULL;
4306 } /* END OF FUNCTION UnmapPulldownCallback */
4307 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
4310 /*************************************<->*************************************
4312 * TraversalOff (menuSpec)
4317 * This function turns menu traversal off.
4322 * menuSpec = menu specification
4334 *************************************<->***********************************/
4336 void TraversalOff (MenuSpec *menuSpec)
4338 if (menuSpec && (menuSpec->menuWidget))
4340 /* function pointer */
4341 (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
4342 ->row_column_class.menuProcedures)
4344 (XmMENU_TRAVERSAL, menuSpec->menuWidget, False, NULL, NULL);
4347 } /* END OF FUNCTION TraversalOff */
4351 /*************************************<->*************************************
4353 * TraversalOn (menuSpec)
4358 * This function turns menu traversal on.
4363 * menuSpec = menu specification
4375 *************************************<->***********************************/
4377 void TraversalOn (MenuSpec *menuSpec)
4380 if (menuSpec && (menuSpec->menuWidget))
4382 /* function pointer */
4383 (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
4384 ->row_column_class.menuProcedures)
4386 (XmMENU_TRAVERSAL, menuSpec->menuWidget, True, NULL, NULL);
4389 } /* END OF FUNCTION TraversalOn */
4393 /*************************************<->*************************************
4395 * FreeCustomMenuSpec (menuSpec)
4400 * This procedure destroys a custom MenuSpec structure and its associated
4401 * menu widget, menuItems list, menuButtons array, and menu accelerator list.
4406 * menuSpec = MenuSpec structure
4416 * Assumes that a MenuSpec is custom iff its name is NULL.
4418 * Assumes that ParseWmFuncStr() has parsed a menu item's function
4419 * argument only for F_Exec and F_Menu. If it is used for other functions,
4420 * be sure to include them here!
4422 *************************************<->***********************************/
4424 void FreeCustomMenuSpec (MenuSpec *menuSpec)
4427 MenuItem *nextMenuItem;
4428 KeySpec *accelKeySpec;
4429 KeySpec *nextAccelKeySpec;
4431 if ((menuSpec == NULL) || (menuSpec->name != NULL))
4432 /* we only destroy custom menus! */
4438 * Fix for CR 5450 - If the custom menu is the same as wmGD.menuActive, call
4439 * the UnmapCallback directly to clean things up. Since
4440 * the menu is going to be destroyed, this callback will
4441 * not get called, leaving MWM in a failure state.
4443 if (wmGD.menuActive == menuSpec)
4444 UnmapCallback((Widget)NULL, (caddr_t)NULL, (caddr_t)NULL);
4446 * End fix for CR 5450
4449 menuItem = menuSpec->menuItems;
4452 nextMenuItem = menuItem->nextMenuItem;
4453 FreeMenuItem (menuItem);
4454 menuItem = nextMenuItem;
4457 if (menuSpec->menuButtons)
4459 XtFree ((char *)menuSpec->menuButtons);
4462 accelKeySpec = menuSpec->accelKeySpecs;
4463 while (accelKeySpec)
4465 nextAccelKeySpec = accelKeySpec->nextKeySpec;
4466 XtFree ((char *)accelKeySpec);
4467 accelKeySpec = nextAccelKeySpec;
4470 if (menuSpec->menuWidget)
4471 /* destroy all children of the menu's MenuShell parent */
4473 XtDestroyWidget (XtParent(menuSpec->menuWidget));
4476 XtFree ((char *)menuSpec);
4478 } /* END OF FUNCTION FreeCustomMenuSpec */