Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtwm / WmMenu.c
1 /* 
2  * (c) Copyright 1989, 1990, 1991, 1992, 1993 OPEN SOFTWARE FOUNDATION, INC. 
3  * ALL RIGHTS RESERVED 
4 */ 
5 /* 
6  * Motif Release 1.2.3
7 */ 
8 #ifdef REV_INFO
9 #ifndef lint
10 static char rcsid[] = "$XConsortium: WmMenu.c /main/15 1996/11/20 15:20:17 rswiston $"
11 #endif
12 #endif
13 /*
14  * (c) Copyright 1987, 1988, 1989, 1990 HEWLETT-PACKARD COMPANY */
15 /*
16  * (c) Copyright 1987, 1988 DIGITAL EQUIPMENT CORPORATION */
17 /*
18  * (c) Copyright 1988 MASSACHUSETTS INSTITUTE OF TECHNOLOGY */
19
20 /*
21  * Included Files:
22  */
23
24 #include "WmGlobal.h"
25 #include "WmCEvent.h"
26 #include "WmResource.h"
27 #include "WmResParse.h"
28 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
29 # include "WmDebug.h"
30 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
31 #include <stdio.h>
32
33 #include <X11/Shell.h>
34
35 #include <Xm/Xm.h>
36 #include <Xm/XmP.h>
37 #include <Xm/CascadeB.h>
38 #include <Xm/CascadeBG.h>
39 #include <Xm/Label.h>
40 #include <Xm/LabelG.h>
41 #include <Xm/MenuShell.h>
42 #include <Xm/PushB.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>
48
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"
54
55 #define CHILDREN_CACHE  22
56 #define MENU_BUTTON_INC 5
57
58 /*
59  * include extern functions
60  */
61 #include "WmMenu.h"
62 #include "WmCDecor.h"
63 #include "WmColormap.h"
64 #include "WmEvent.h"
65 #include "WmFunction.h"
66 #include "WmIconBox.h"
67 #include "WmImage.h"
68 #include "WmError.h"
69 #ifdef WSM
70 #include "WmWrkspace.h"
71 #endif /* WSM */
72
73
74 static void UnmapCallback (Widget w, XtPointer client_data,
75                            XtPointer call_data);
76 static MenuItem *DuplicateMenuItems (MenuItem *menuItems);
77
78 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
79 static MenuExclusion *DuplicateMenuExclusions(MenuExclusion *exclusions);
80 static Boolean FindClientCommandMatch (MenuSpec *menuSpec,
81                                        String clientCommand,
82                                        MenuItem **menuItem);
83 static void InsertTreeOnClient (WmScreenData *pSD, ClientData *pCD,
84                                 CmdTree *tree,
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,
93                                    XtPointer call_data);
94 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
95
96
97 \f
98 /*************************************<->*************************************
99  *
100  *  MakeMenu (menuName, initialContext, accelContext, moreMenuItems,
101  *            fSystemMenu)
102  *
103  *
104  *  Description:
105  *  -----------
106  *  This function makes a menu widget.
107  *
108  *
109  *  Inputs:
110  *  ------
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.
116  *
117  * 
118  *  Outputs:
119  *  -------
120  *  Return = pointer to a MenuSpec structure with updated currentContext,
121  *           menuWidget, and menuButtons members.
122  *
123  *
124  *  Comments:
125  *  --------
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.
131  *
132  *  When the client is destroyed, this custom MenuSpec, its menuItem and
133  *  menuButton lists, and its menu widget should be freed.
134  * 
135  *************************************<->***********************************/
136 MenuSpec *MakeMenu (WmScreenData *pSD,
137 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
138                     ClientData *pCD,
139 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
140                     String menuName, Context initialContext,
141                     Context accelContext, MenuItem *moreMenuItems,
142                     Boolean fSystemMenu)
143 {
144     unsigned int n;
145     MenuSpec     *menuSpec;
146     MenuSpec     *newMenuSpec;
147     MenuItem     *menuItem;
148     KeySpec      *accelKeySpec;
149     
150     if ((menuName == NULL) || (pSD == NULL))
151     {
152         return (NULL);
153     }
154
155     /*
156      * Look for the menu specification:
157      */
158
159     menuSpec = pSD->menuSpecs;
160     while (menuSpec)
161     {
162         if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
163         /* Found the menu pane. */
164         {
165             break;
166         }
167         menuSpec = menuSpec->nextMenuSpec;
168     }
169     
170     if (menuSpec == NULL)
171     /* the menuSpecs list is exhausted */
172     {
173         MWarning(((char *)GETMESSAGE(48, 1, "Menu specification %s not found\n")), menuName);
174         return (NULL);
175     }
176
177     /*
178      * The top-level menu pane specification was found.
179      * Adjust the menu accelerator context?
180      */
181
182     if (fSystemMenu)
183     {
184         accelContext = 0;
185     }
186     else if (accelContext & F_CONTEXT_ROOT)
187     /* root context accelerators apply everywhere */
188     {
189         accelContext = F_CONTEXT_ALL;
190     }
191
192     /*
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.
197      */
198
199     if (moreMenuItems != NULL)
200     {
201 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
202         if ((newMenuSpec = DuplicateMenuSpec(menuSpec)) == (MenuSpec *)NULL)
203             return NULL;
204 #else
205         if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
206         /* Handle insufficent memory */
207         {
208             MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
209             return (NULL);
210         }
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;
218 #endif
219
220         menuSpec = newMenuSpec;
221     }
222     else if (menuSpec->menuWidget)
223     {
224         /* 
225          * OR the accelContext into the accelerators, if necessary.
226          */
227         if (accelContext != (menuSpec->accelContext & accelContext))
228         {
229             menuSpec->accelContext |= accelContext;
230             accelKeySpec = menuSpec->accelKeySpecs;
231             while (accelKeySpec)
232             {
233                 accelKeySpec->context |= accelContext;
234                 accelKeySpec = accelKeySpec->nextKeySpec;
235             }
236         }
237         return (menuSpec);
238     }
239
240     /*
241      * We have a menu specification with which to build the menu.
242      * Set the initial and accelerator contexts -- they are needed within 
243      *   CreateMenuWidget.
244      */
245
246     menuSpec->currentContext = initialContext;
247     menuSpec->accelContext = accelContext;
248
249     /*
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.
253      */
254
255     n = 0;
256     menuItem = menuSpec->menuItems;
257     while (menuItem)
258     {
259         if ((menuItem->greyedContext) || (menuItem->mgtMask))
260         {
261             n++;
262         }
263         menuItem = menuItem->nextMenuItem;
264     }
265     menuItem = moreMenuItems;
266     while (menuItem)
267     {
268         if ((menuItem->greyedContext) || (menuItem->mgtMask))
269         {
270             n++;
271         }
272         menuItem = menuItem->nextMenuItem;
273     }
274     if (n)
275     {
276         if ((menuSpec->menuButtons =
277                (MenuButton *) XtMalloc (n * sizeof(MenuButton))) == NULL)
278         /* insufficent memory */
279         {
280             MWarning(((char *)GETMESSAGE(48, 3, "Insufficient memory for menu %s\n")), menuName);
281             return (NULL);
282         }
283         menuSpec->menuButtonSize = n;
284     }
285     else
286     {
287         menuSpec->menuButtons = NULL;
288         menuSpec->menuButtonSize = 0;
289     }
290     menuSpec->menuButtonCount = 0;
291
292     /*
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.
296      */
297
298     menuSpec->menuWidget = CREATE_MENU_WIDGET (pSD, pCD, menuName, 
299                                              pSD->screenTopLevelW,
300                                              TRUE, menuSpec, moreMenuItems);
301     if (menuSpec->menuWidget == NULL)
302     {
303         /*
304          *  Could not make the top-level menu pane.
305          */
306         return (NULL);
307     }
308 /*
309     _XmSetPopupMenuClick(menuSpec->menuWidget, False); 
310 */
311     /* Return the top MenuSpec */
312
313     return (menuSpec);
314
315 } /* END OF FUNCTION MakeMenu */
316
317
318 \f
319 /*************************************<->***********************************/
320 void CheckTerminalSeparator(menuSpec, buttonWidget, manage)
321      MenuSpec *menuSpec;
322      Widget buttonWidget;
323      Boolean manage;
324 {
325     CompositeWidget cw;
326     WidgetList      children;
327     Cardinal        wPos;
328
329
330     if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
331       {
332          return;
333       }
334
335     cw = (CompositeWidget)menuSpec->menuWidget;
336     children = cw->composite.children;
337
338     for (wPos = 0;  wPos < cw->composite.num_children; wPos++)
339     {
340         if((Widget)children[wPos] == buttonWidget)
341         {
342             break;
343         }
344     }
345     
346     
347     if(wPos > 0 &&
348        XtClass((Widget) children[wPos -1]) == xmSeparatorGadgetClass)
349     {
350         if(manage)
351         {
352             if (!(XtIsManaged((Widget)children[wPos -1])))
353             {
354                 XtManageChild((Widget)children[wPos -1]);
355             }
356         }
357         else
358         {
359             if (XtIsManaged((Widget)children[wPos -1]))
360             {
361                 XtUnmanageChild((Widget)children[wPos -1]);
362             }
363         }
364     }
365
366 } /* END OF FUNCTION CheckTerminalSeparator */
367
368
369 \f
370 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
371 /*************************************<->*************************************
372  *
373  *  MakeMenuSpec (menuName, commandID)
374  *  
375  *
376  *
377  *  Description:
378  *  -----------
379  *  This function creates and returns a MenuSpec structure.
380  *
381  *
382  *  Inputs:
383  *  ------
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.
387  *
388  * 
389  *  Outputs:
390  *  -------
391  *  Return = pointer to a MenuSpec structure with zero'ed fields.
392  *
393  *
394  *  Comments:
395  *  --------
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. 
399  * 
400  *************************************<->***********************************/
401 static MenuSpec *
402 MakeMenuSpec (String menuName, CARD32 commandID)
403 {
404     MenuSpec *menuSpec;
405     
406     if ((menuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
407       /* Handle insufficent memory */
408     {
409         MWarning(((char *)GETMESSAGE(48, 2,
410                  "Insufficient memory for menu %s\n")), menuName);
411         return (NULL);
412     }
413     
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;
429     
430     return(menuSpec);
431 }
432 #endif
433 \f
434 /*************************************<->*************************************
435  *
436  *  DuplicateMenuItems (menuItems)
437  *  
438  *
439  *
440  *  Description:
441  *  -----------
442  *  This function creates an indentical duplicate of the given menuItems
443  *  list.
444  *
445  *
446  *  Inputs:
447  *  ------
448  *  menuItems = the linked list of menuItems to duplicate
449  *
450  * 
451  *  Outputs:
452  *  -------
453  *  Return = pointer to a new MenuItems list, identical to the original
454  *
455  *
456  *  Comments:
457  *  --------
458  * 
459  *************************************<->***********************************/
460 static MenuItem *
461 DuplicateMenuItems (MenuItem *menuItems)
462 {
463     MenuItem *newMenuItem = (MenuItem *) NULL, *returnMenuItem, *curMenuItem;
464     
465     for (curMenuItem = menuItems;
466          curMenuItem != (MenuItem *) NULL;
467          curMenuItem = curMenuItem->nextMenuItem)
468     {
469         /* If its the first one ... */
470         if (newMenuItem == (MenuItem *) NULL)
471         {
472             newMenuItem = (MenuItem *)XtMalloc(sizeof(MenuItem));
473             returnMenuItem = newMenuItem;
474         }
475         else /* ... otherwise, get the next menuItem. */
476         {
477             newMenuItem->nextMenuItem =
478               (MenuItem *)XtMalloc(sizeof(MenuItem));
479             newMenuItem = newMenuItem->nextMenuItem;
480         }
481         
482         newMenuItem->labelType = curMenuItem->labelType;
483         if (curMenuItem->label != (String) NULL)
484           newMenuItem->label = XtNewString(curMenuItem->label);
485         else
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);
493         else
494           newMenuItem->accelText = NULL;
495         newMenuItem->wmFunction = curMenuItem->wmFunction;
496
497         if ((curMenuItem->wmFunction == F_Send_Msg)
498 #ifdef WSM
499             || (curMenuItem->wmFunction == F_Set_Context)
500 # ifdef PANELIST
501             /*
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
509              */
510             || (curMenuItem->wmFunction == F_Action)
511 # endif /* PANELIST */
512 #endif /* WSM */
513             )
514           newMenuItem->wmFuncArgs = curMenuItem->wmFuncArgs;
515         else if (curMenuItem->wmFuncArgs != (String) NULL)
516           newMenuItem->wmFuncArgs = XtNewString(curMenuItem->wmFuncArgs);
517         else
518           newMenuItem->wmFuncArgs = NULL;
519
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;
526 #endif
527         newMenuItem->nextMenuItem = (MenuItem *) NULL;
528     }
529     
530     return(returnMenuItem);
531 }
532
533 \f
534 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
535 /*************************************<->*************************************
536  *
537  *  DuplicateMenuExclusions (exclusions)
538  *  
539  *
540  *
541  *  Description:
542  *  -----------
543  *  This function creates an indentical duplicate of the given menu exclusions
544  *  list.
545  *
546  *
547  *  Inputs:
548  *  ------
549  *  exclusions = the linked list of menu exclusions to duplicate
550  *
551  * 
552  *  Outputs:
553  *  -------
554  *  Return = pointer to a new MenuExclusion list, identical to the original
555  *
556  *
557  *  Comments:
558  *  --------
559  * 
560  *************************************<->***********************************/
561 static MenuExclusion *
562 DuplicateMenuExclusions (MenuExclusion *exclusions)
563 {
564     MenuExclusion *newMenuExclusion = (MenuExclusion *) NULL;
565     MenuExclusion *returnMenuExclusion = (MenuExclusion *) NULL;
566     MenuExclusion *curMenuExclusion = (MenuExclusion *) NULL;
567     
568     for (curMenuExclusion = exclusions;
569          curMenuExclusion != (MenuExclusion *) NULL;
570          curMenuExclusion = curMenuExclusion->nextExclusion)
571     {
572         /* If its the first one ... */
573         if (newMenuExclusion == (MenuExclusion *) NULL)
574         {
575             newMenuExclusion =
576               (MenuExclusion *)XtMalloc(sizeof(MenuExclusion));
577             returnMenuExclusion = newMenuExclusion;
578         }
579         else /* ... otherwise, get the next menuExclusion. */
580         {
581             newMenuExclusion->nextExclusion =
582               (MenuExclusion *)XtMalloc(sizeof(MenuExclusion));
583             newMenuExclusion = newMenuExclusion->nextExclusion;
584         }
585
586         newMenuExclusion->command_string =
587           XtNewString(curMenuExclusion->command_string);
588     }
589
590     /* Make sure we properly NULL terminate the list. */
591     if (newMenuExclusion != (MenuExclusion *) NULL)
592       newMenuExclusion->nextExclusion = (MenuExclusion *) NULL;
593     
594     return(returnMenuExclusion);
595 }
596 #endif
597 \f
598 /*************************************<->*************************************
599  *
600  *  DuplicateMenuSpec (menuSpec)
601  *  
602  *
603  *
604  *  Description:
605  *  -----------
606  *  This function creates an indentical duplicate of the given menuSpec.
607  *  The menuItems list in the menuSpec is also duplicated. 
608  *
609  *
610  *  Inputs:
611  *  ------
612  *  menuSpec = the menuSpec to duplicate
613  *
614  * 
615  *  Outputs:
616  *  -------
617  *  Return = pointer to a new MenuSpec structure with the same field
618  *           values as the original
619  *
620  *
621  *  Comments:
622  *  --------
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.
628  * 
629  *************************************<->***********************************/
630 MenuSpec *
631 DuplicateMenuSpec (MenuSpec *menuSpec)
632 {
633     MenuSpec *newMenuSpec;
634     
635     if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
636       /* Handle insufficent memory */
637     {
638         Warning((char *)GETMESSAGE(48, 9,
639                  "Insufficient memory for menu specification\n"));
640         return (NULL);
641     }
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;
657 #endif
658     newMenuSpec->nextMenuSpec = (MenuSpec *) NULL;
659     
660     return(newMenuSpec);
661 }
662
663 \f
664 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
665 /*************************************<->*************************************
666  *
667  *  MakeMenuItem (label, wmFunction, funcArgs, mnemonic, accelText)
668  * 
669  *
670  *  Description:
671  *  -----------
672  *  This function creates and returns a MenuItem structure. 
673  *
674  *
675  *  Inputs:
676  *  ------
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
682  *
683  *  Outputs:
684  *  -------
685  *  Return = pointer to a new MenuItem structure with fields filled
686  *           in as per passed arguments
687  *
688  *
689  *  Comments:
690  *  --------
691  *  This function is actually used as the underlying mechanism for 
692  *  MenuItem creation by MakeMenuItemFromTemplate and 
693  *  MakeClientCommandMenuItem.
694  *
695  *  Assumptions:
696  *  -----------
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.
701  * 
702  *************************************<->***********************************/
703 static MenuItem *
704 MakeMenuItem (String label, WmFunction wmFunction, String funcArgs,
705               KeySym mnemonic, unsigned int accelState,
706               KeyCode accelKeyCode, String accelText)
707 {
708     MenuItem *menuItem;
709 /*
710     unsigned char *copy_of_accelText;
711 */
712     
713     if ((menuItem = (MenuItem *) XtMalloc (sizeof (MenuItem))) == NULL)
714       /* Handle insufficent memory */
715     {
716         MWarning(((char *)GETMESSAGE(48, 10,
717                   "Insufficient memory for menu item %s\n")), label);
718         return (NULL);
719     }
720     
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;
727
728 /*     
729     copy_of_accelText = (unsigned char *)XtNewString(accelText);
730     ParseWmAccelerator(&copy_of_accelText, menuItem);
731 */
732
733     menuItem->accelState = accelState;
734     menuItem->accelKeyCode = accelKeyCode;
735     menuItem->accelText = XtNewString(accelText);
736
737     menuItem->wmFunction = wmFunction;
738     menuItem->wmFuncArgs = XtNewString(funcArgs);
739     SetGreyedContextAndMgtMask(menuItem, wmFunction);
740     menuItem->nextMenuItem = (MenuItem *) NULL;
741     
742     return(menuItem);
743 }
744
745 \f
746 /*************************************<->*************************************
747  *
748  *  MakeMenuItemFromTemplate (template, name, funcArgs)
749  * 
750  *
751  *  Description:
752  *  -----------
753  *  This function creates and returns a MenuItem structure. 
754  *
755  *
756  *  Inputs:
757  *  ------
758  *  template   = a template menuItem used to fill in fields of the
759  *               new menu item
760  *  name       = the display name this item should have
761  *  funcArgs   = the function arguments to pass to the wm function
762  *
763  *  Outputs:
764  *  -------
765  *  Return = pointer to a new MenuItem structure with fields filled
766  *           in as per template MenuItem and funcargs
767  *
768  *
769  *  Comments:
770  *  --------
771  *  This function uses the values in the template MenuItem to create
772  *  a new copy of the template with the given funcArgs.
773  * 
774  *************************************<->***********************************/
775
776 static MenuItem *MakeMenuItemFromTemplate (MenuItem *template, String name,
777                                            String funcArgs)
778 {
779     if (template->clientCommandName == (String) NULL)
780       return(MakeMenuItem(name, template->wmFunction, funcArgs,
781                           template->mnemonic, template->accelState,
782                           template->accelKeyCode, template->accelText));
783
784     return(MakeMenuItem(template->clientCommandName, template->wmFunction,
785                         funcArgs, template->mnemonic, template->accelState,
786                         template->accelKeyCode, template->accelText));
787 }
788
789 \f
790 /*************************************<->*************************************
791  *
792  *  MakeClientCommandMenuItem (label, funcArgs)
793  * 
794  *
795  *  Description:
796  *  -----------
797  *  This function creates and returns a MenuItem structure filled as
798  *  appropriate for client command menu items using the given label
799  *  and funcArgs. 
800  *
801  *
802  *  Inputs:
803  *  ------
804  *  label      = the display label for this menu item
805  *  funcArgs   = the function arguments to pass to the wm function
806  *
807  *  Outputs:
808  *  -------
809  *  Return = pointer to a new MenuItem structure with fields filled
810  *           in as per arguments and client command specs
811  *
812  *
813  *  Comments:
814  *  --------
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
818  *  mwmrc.
819  * 
820  *************************************<->***********************************/
821
822 static MenuItem *MakeClientCommandMenuItem (String label, String funcArgs)
823 {
824     return(MakeMenuItem(label, F_InvokeCommand, funcArgs,
825                         (KeySym) NULL, (unsigned int)0,
826                         (KeyCode) NULL, (String)NULL));
827 }
828
829 \f
830 /*************************************<->*************************************
831  *
832  *  PerformClientCommandMatch (clientCommand, menuItem, bestMatchSoFar)
833  * 
834  *
835  *  Description:
836  *  -----------
837  *  This function determines whether the menuItemCommand specification 
838  *  matches the clientCommand.
839  *
840  *  Inputs:
841  *  ------
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
846  *
847  *  Outputs:
848  *  -------
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.
854  *
855  *  Comments:
856  *  --------
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.
860  *
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.
867  * 
868  *************************************<->***********************************/
869
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
873
874 static MenuItem *PerformClientCommandMatch (String clientCommand,
875                                      MenuItem *menuItem,
876                                      MenuItem *bestMatchSoFar)
877 {
878     String menuItemCommand, bestMatchStr;
879     int seglength, i;
880     int segments = 0, wildcards = 0, wildcardPositions[MAXSEGMENTS];
881     int bestSegments = 0, bestWildcards = 0;
882     int bestWildcardPositions[MAXSEGMENTS];
883     Boolean foundWildcard = FALSE;
884
885     if (menuItem == (MenuItem *) NULL)
886       return(bestMatchSoFar);
887     menuItemCommand = menuItem->label;
888     
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] == '~')
894       ++menuItemCommand;
895     else if (menuItemCommand[0] == '=' && menuItemCommand[1] == '>')
896       menuItemCommand += 2;
897     else if (menuItemCommand[0] == '=')
898       ++menuItemCommand;
899     else if (menuItemCommand[0] == '-' && menuItemCommand[1] == '>')
900       menuItemCommand += 2;
901     
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);
905     
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.  */
909     for (segments = 0;
910          *menuItemCommand != '\0' && *clientCommand != '\0';
911          ++segments)
912     {
913         /* Skip past the '<' at the beginning of the next segment and
914            any whitespace. */
915         ++menuItemCommand; ++clientCommand;
916         while (isspace(*menuItemCommand)) ++menuItemCommand;
917         while (isspace(*clientCommand)) ++clientCommand;
918         
919         /* First check whether the current menuItemCommand segment is
920            a wildcard. */
921         if (*menuItemCommand == '*')
922         {
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;
928             ++menuItemCommand;
929             while (isspace(*menuItemCommand)) ++menuItemCommand;
930             while (*clientCommand != '>' && *clientCommand != '\0')
931               ++clientCommand;
932             foundWildcard = TRUE;
933         }
934         else
935         {
936             /* Calculate how long the current segment of the
937                menuItemCommand is */
938             for (seglength = 0;
939                  menuItemCommand[seglength] != '>' &&
940                  menuItemCommand[seglength] != '\0';
941                  ++seglength)
942               /*EMPTY*/;
943             
944             /* If we are pointing at '\0', then this isn't a match */
945             if (menuItemCommand[seglength] == '\0') return(bestMatchSoFar);
946             
947             /* Get rid of trailing white space on the segment. */
948             for (; seglength > 0; --seglength)
949             {
950                 if (!isspace(menuItemCommand[seglength - 1]))
951                   break;
952             }
953             
954             /* Now string compare this segment with the clientCommand
955                segment, up to the number of characters in the menu
956                item segment. */
957             if (strncmp(menuItemCommand, clientCommand, seglength) == 0)
958             {
959                 /* So far so good. Just make sure clientCommand doesn't
960                    have anything but whitespace after its seglength
961                    character. */
962                 clientCommand += seglength;
963                 while (isspace(*clientCommand)) ++clientCommand;
964                 if (*clientCommand != '>') return(bestMatchSoFar);
965                 
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;
971             }
972             else if (foundWildcard == FALSE)
973             {
974                 /* We didn't match and there wasn't wildcard to 
975                    swallow the discrepancy. Therefore, this is not
976                    a match. */
977                 return(bestMatchSoFar);
978             }
979         }
980         
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')
987         {
988             ++menuItemCommand; ++clientCommand;
989         }
990         else if (menuItemCommand[0] == '>' && menuItemCommand[1] == '.' &&
991                  clientCommand[0] == '>'   && clientCommand[1] == '.')
992         {       
993             menuItemCommand += 2;
994             clientCommand += 2;
995         }
996         else return(bestMatchSoFar);
997     }
998
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);
1004     
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
1009        return. */
1010     if (bestMatchSoFar == (MenuItem *) NULL)
1011       return(menuItem);
1012     
1013     bestMatchStr = bestMatchSoFar->label;
1014     
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] == '~')
1020       ++bestMatchStr;
1021     else if (bestMatchStr[0] == '=' && bestMatchStr[1] == '>')
1022       bestMatchStr += 2;
1023     else if (bestMatchStr[0] == '=')
1024       ++bestMatchStr;
1025     else if (bestMatchStr[0] == '-' && bestMatchStr[1] == '>')
1026       bestMatchStr += 2;
1027     
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);
1031     
1032     for (bestSegments = 0;
1033          *bestMatchStr != '\0';
1034          ++bestSegments)
1035     {
1036         /* Skip past the '<' at the beginning of the next segment and
1037            any whitespace. */
1038         ++bestMatchStr;
1039         while (isspace(*bestMatchStr)) ++bestMatchStr;
1040         
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')
1047           ++bestMatchStr;
1048         
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')
1053           ++bestMatchStr;
1054         else if (bestMatchStr[0] == '>' && bestMatchStr[1] == '.')
1055           bestMatchStr += 2;
1056         else return(menuItem);
1057     }
1058     
1059     /* Now compare the best match metrics with the menu item metrics to
1060        determine who should be returned. */
1061     if (bestWildcards != wildcards)
1062     {
1063         /* Return the menuItem with the fewest wildcards. */
1064         return(bestWildcards < wildcards ? bestMatchSoFar : menuItem);
1065     }
1066     else
1067     {
1068         /* Find which menu item has the earliest wild card and return
1069            the other. */
1070         for (i = 0; i < wildcards; ++i)
1071           if (wildcardPositions[i] != bestWildcardPositions[i])
1072           {
1073               return(bestWildcardPositions[i] < wildcardPositions[i] ?
1074                      bestMatchSoFar : menuItem);
1075           }
1076         
1077         /* If we got this far, then the two specifications are too
1078            close to call. Return bestMatchSoFar. */
1079         return(bestMatchSoFar);
1080     }
1081 }
1082
1083 \f
1084 /*************************************<->*************************************
1085  *
1086  *  ExcludeClientCommand (menuSpec, clientCommand)
1087  * 
1088  *
1089  *  Description:
1090  *  -----------
1091  *
1092  *  Inputs:
1093  *  ------
1094  *  menuSpec      = the menuSpec whose menuItems we want to search through
1095  *  clientCommand = the clientCommand that we want to find an exclusion for
1096  *
1097  *  Outputs:
1098  *  -------
1099  *  Return = TRUE if the command must be excluded from the menuSpec.
1100  *           FALSE if there is no exclusion preventing the insertion.
1101  *
1102  *  Comments:
1103  *  --------
1104  * 
1105  *************************************<->***********************************/
1106
1107 static Boolean ExcludeClientCommand (MenuSpec *menuSpec, String clientCommand)
1108 {
1109     MenuItem placeholder;
1110     MenuExclusion *curExclusion;
1111
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)
1117     {
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;
1122         
1123         /* If we don't get NULL back, then this exclusion matches. */
1124         if (PerformClientCommandMatch(clientCommand,
1125                                       &placeholder, NULL) != (MenuItem *) NULL)
1126         {
1127             return(TRUE);
1128         }
1129     }
1130     return(FALSE);
1131 }
1132
1133 \f
1134 /*************************************<->*************************************
1135  *
1136  *  ForceInLineToCascade (menuSpec, clientCommand, bestMatch)
1137  * 
1138  *
1139  *  Description:
1140  *  -----------
1141  *
1142  *  Inputs:
1143  *  ------
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
1147  *
1148  *  Outputs:
1149  *  -------
1150  *  Return = TRUE if the command set must be cascaded
1151  *           FALSE if there is no forced cascade 
1152  *
1153  *  Comments:
1154  *  --------
1155  * 
1156  *************************************<->***********************************/
1157
1158 static Boolean ForceInLineToCascade (MenuSpec *menuSpec,
1159                                      String clientCommand,
1160                                      MenuItem **bestMatch)
1161 {
1162     /* First find the best match in the menu spec. */
1163     FindClientCommandMatch(menuSpec, clientCommand, bestMatch);
1164
1165     /* If the best match is not NULL, then check whether it forces
1166        the client command to cascade. */
1167     if (*bestMatch != (MenuItem *) NULL)
1168     {
1169         /* If there is a force cascade modifier, then return TRUE. */
1170         if ((strncmp((*bestMatch)->label, "->", 2) == 0) ||
1171             (strncmp((*bestMatch)->label, "=>", 2) == 0))
1172           return(TRUE);
1173     }
1174
1175     /* If the best match is NULL, then return FALSE. We have been
1176        given no indication that the inLine command should be forced
1177        to cascade. */
1178     return(FALSE);
1179 }
1180
1181 \f
1182 /*************************************<->*************************************
1183  *
1184  *  FindClientCommandMatch (menuSpec, clientCommand, menuItem)
1185  * 
1186  *
1187  *  Description:
1188  *  -----------
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
1193  *  is returned.
1194  *
1195  *  Inputs:
1196  *  ------
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
1200  *
1201  *  Outputs:
1202  *  -------
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
1207  *           can be found.
1208  *
1209  *  Comments:
1210  *  --------
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.
1217  * 
1218  *************************************<->***********************************/
1219
1220 static Boolean FindClientCommandMatch (MenuSpec *menuSpec,
1221                                        String clientCommand,
1222                                        MenuItem **menuItem)
1223 {
1224     MenuItem *bestMatch = (MenuItem *) NULL, *curMenuItem, placeholder;
1225     MenuItem *bestExclusionItem = (MenuItem *) NULL;
1226     MenuExclusion *curExclusion;
1227     String    bestExclusionStr = (String) NULL;
1228     
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)
1233     {
1234         bestMatch =
1235           PerformClientCommandMatch(clientCommand, curMenuItem, bestMatch);
1236     }
1237
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)
1243     {
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;
1248
1249         /* Find the best exclusion string in the bunch. */
1250         bestExclusionItem =
1251           PerformClientCommandMatch(clientCommand, &placeholder,
1252                                     bestExclusionItem);
1253
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;
1258     }
1259
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) ==
1267         bestMatch)
1268     {
1269         *menuItem = bestMatch;
1270         return(TRUE);
1271     }
1272     else
1273     {
1274         *menuItem = NULL;
1275         return(FALSE);
1276     }
1277 }
1278
1279
1280 \f
1281 /*************************************<->*************************************
1282  *
1283  *  PerformInsertionsOnMatchList (matchlist)
1284  *
1285  *
1286  *  Description:
1287  *  -----------
1288  *
1289  *  Inputs:
1290  *  ------
1291  * 
1292  *  Outputs:
1293  *  -------
1294  *  Return = 
1295  *
1296  *
1297  *  Comments:
1298  *  --------
1299  * 
1300  *************************************<->***********************************/
1301
1302 static void PerformInsertionsOnMatchList (MatchList **matchlist)
1303 {
1304     MatchList *curmatch;
1305     MenuItem *newMenuItem, *curitem;
1306     
1307     if (*matchlist == (MatchList *) NULL)
1308       return;
1309
1310     if ((*matchlist)->menuspec == (MenuSpec *) NULL)
1311       {
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. */
1318         return;
1319       }
1320        
1321     
1322     for (curmatch = *matchlist;
1323          curmatch != (MatchList *) NULL;
1324          curmatch = curmatch->next)
1325     {
1326       if (curmatch->menuitem != (MenuItem *) NULL)
1327         {
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)
1333             /*EMPTY*/;
1334           
1335           /* If we didn't find the menuitem in the menuspec, then
1336              don't do this match. */
1337           if (curitem == (MenuItem *) NULL) continue;
1338           
1339           newMenuItem =
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;
1348         }
1349       else
1350         {
1351           MenuItem *last = (MenuItem *) NULL;
1352           
1353           if (curmatch->menuspec != NULL)
1354             {
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)
1360                 {
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))
1366                     break;
1367                 }
1368             }
1369           
1370           /* Create a new client command menu item */
1371           newMenuItem =
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;
1378           
1379           /* Insert the new menu item at the end of the list */
1380           if (last == (MenuItem *) NULL)
1381             {
1382               newMenuItem->nextMenuItem = (MenuItem *) NULL;
1383               if (curmatch->menuspec != NULL)
1384                 curmatch->menuspec->menuItems = newMenuItem;
1385               else
1386                 {
1387                   /* again, should never get here... */
1388                   return;
1389                 }
1390             }
1391           else
1392             {
1393               newMenuItem->nextMenuItem = last->nextMenuItem;
1394               last->nextMenuItem = newMenuItem;
1395             }
1396         }
1397     }
1398 }
1399
1400 \f
1401 /*************************************<->*************************************
1402  *
1403  *  void
1404  *  DestroyMenuSpecWidgets (menuSpec)
1405  *
1406  *
1407  *  Description:
1408  *  -----------
1409  *
1410  *
1411  *  Inputs:
1412  *  ------
1413  *  menuSpec = pointer to MenuSpec structure
1414  *
1415  * 
1416  *  Outputs:
1417  *  -------
1418  *
1419  *  Comments:
1420  *  --------
1421  *  Destroys all the menuspec widgets so that we can rebuild the menu from
1422  *  scratch.
1423  * 
1424  *************************************<->***********************************/
1425
1426 void DestroyMenuSpecWidgets (MenuSpec *menuSpec)
1427 {
1428     /* check for bad input value - shouldn't happen. */
1429     if (menuSpec == (MenuSpec *) NULL) return;
1430
1431     /* Destroy the menu widget */
1432     if (menuSpec->menuWidget != (Widget) NULL)
1433     {
1434       XtDestroyWidget(XtParent(menuSpec->menuWidget));
1435       menuSpec->menuWidget = (Widget) NULL;
1436     }
1437
1438     /* Destroy the menu buttons array */
1439     if (menuSpec->menuButtonSize != 0)
1440     {  
1441       XtFree((char *)menuSpec->menuButtons);
1442       menuSpec->menuButtons = (MenuButton *) NULL;
1443     }
1444
1445     /* Reset the counters */
1446     menuSpec->menuButtonSize = 0;
1447     menuSpec->menuButtonCount = 0;
1448
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);
1454
1455     return;
1456 }
1457
1458 \f
1459 /*************************************<->*************************************
1460  *
1461  *  void
1462  *  DestroyMenuSpec (pSD, commandID)
1463  *
1464  *
1465  *  Description:
1466  *  -----------
1467  *
1468  *
1469  *  Inputs:
1470  *  ------
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.
1474  * 
1475  *  Outputs:
1476  *  -------
1477  *
1478  *  Comments:
1479  *  --------
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
1483  *  some clients.
1484  * 
1485  *************************************<->***********************************/
1486
1487 void DestroyMenuSpec (WmScreenData *pSD, CARD32 commandID)
1488 {
1489     MenuSpec *msToKill = NULL, *pMS;
1490     ClientListEntry *curClient;
1491
1492     /* Scan through global menu specs. */
1493     if (pSD != NULL  &&  pSD->menuSpecs != NULL  &&  commandID != 0)
1494       {
1495         /* Scan through the list of menuSpecs and pull the mathing one
1496          * out of the list.
1497          */
1498         if (commandID == pSD->menuSpecs->commandID)
1499           {
1500             /* match at head of menuSpec list. */
1501             msToKill = pSD->menuSpecs;
1502             pSD->menuSpecs = pSD->menuSpecs->nextMenuSpec;
1503             msToKill->nextMenuSpec = NULL;
1504           }
1505         else
1506           {
1507             for (pMS = pSD->menuSpecs;
1508                  (pMS->nextMenuSpec != NULL &&
1509                   pMS->nextMenuSpec->commandID != commandID);
1510                  pMS = pMS->nextMenuSpec)
1511               ;
1512
1513             if (pMS->nextMenuSpec != NULL)
1514               {
1515                 msToKill = pMS->nextMenuSpec;
1516                 pMS->nextMenuSpec = msToKill->nextMenuSpec;
1517                 msToKill->nextMenuSpec = NULL;
1518               }
1519           }
1520
1521         /* found it - now remove the menuSpec. */
1522         if (msToKill != NULL)
1523           FreeCustomMenuSpec(msToKill);
1524       }
1525
1526
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)
1531       {
1532         /*
1533          * Check the first position.
1534          * If matched, then we're done with this client.
1535          */
1536         if (commandID == pSD->menuSpecs->commandID)
1537           {
1538             msToKill = curClient->pCD->systemMenuSpec;
1539             curClient->pCD->systemMenuSpec = msToKill->nextMenuSpec;
1540             msToKill->nextMenuSpec = NULL;
1541           }
1542
1543         /* Check the rest of the list. */
1544         else
1545           {
1546             for (pMS = curClient->pCD->systemMenuSpec;
1547                  (pMS->nextMenuSpec != (MenuSpec *)NULL)     &&
1548                  (pMS->nextMenuSpec->commandID != commandID) &&
1549                  pMS->clientLocal;
1550                  pMS = pMS->nextMenuSpec)
1551               ;
1552
1553             if ((pMS->nextMenuSpec != (MenuSpec *)NULL)      &&
1554                 (pMS->nextMenuSpec->commandID != commandID))
1555               {
1556                 msToKill = pMS->nextMenuSpec;
1557                 pMS->nextMenuSpec = msToKill->nextMenuSpec;
1558                 msToKill->nextMenuSpec = NULL;
1559               }
1560             else
1561               msToKill = NULL;
1562           }
1563
1564         if (msToKill != NULL)
1565           FreeCustomMenuSpec(msToKill);
1566       }
1567
1568     return;
1569 }
1570
1571 \f
1572 /*************************************<->*************************************
1573  *
1574  *  ReplaceMenuSpecForClient (menuspec, pCD)
1575  *
1576  *
1577  *  Description:
1578  *  -----------
1579  *  Duplicates the given menuspec and replaces the given menuspec if
1580  *  found in the clients menuspec list with the duplicate.
1581  *
1582  *  Inputs:
1583  *  ------
1584  * 
1585  *  Outputs:
1586  *  -------
1587  *  Return = the duplicate menuspec
1588  *
1589  *
1590  *  Comments:
1591  *  --------
1592  * 
1593  *************************************<->***********************************/
1594
1595 static MenuSpec *ReplaceMenuSpecForClient (MenuSpec *menuSpec, ClientData *pCD)
1596 {
1597     MenuSpec *newMenuSpec, *curMenuSpec;
1598
1599     /* Duplicate the menu spec */
1600     newMenuSpec = DuplicateMenuSpec(menuSpec);
1601     
1602     /* Try to find this menuspec in the list of client
1603        menuspecs. If we find it then we want to replace it with
1604        the new one. */
1605     if (pCD->systemMenuSpec == menuSpec)
1606     {
1607         /* It was the head of the list. We need to handle that
1608            a little special */
1609         newMenuSpec->nextMenuSpec = pCD->systemMenuSpec->nextMenuSpec;
1610         pCD->systemMenuSpec = newMenuSpec;
1611     }
1612     else
1613     {
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)
1619         {
1620             if (curMenuSpec->nextMenuSpec == menuSpec)
1621             {
1622                 newMenuSpec->nextMenuSpec =
1623                   curMenuSpec->nextMenuSpec->nextMenuSpec;
1624                 curMenuSpec->nextMenuSpec = newMenuSpec;
1625                 /* We found it and replaced it. Now get out of
1626                    the loop. */
1627                 break;
1628             }
1629         }
1630         if (curMenuSpec->nextMenuSpec == (MenuSpec *) NULL)
1631         {
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
1634                this. */
1635             curMenuSpec->nextMenuSpec = newMenuSpec;
1636             newMenuSpec = (MenuSpec *) NULL;
1637         }
1638     }
1639
1640     return(newMenuSpec);
1641 }
1642
1643 \f
1644 /*************************************<->*************************************
1645  *
1646  *  FindLastMenuSpecToModify (menuspec, command_id)
1647  *
1648  *
1649  *  Description:
1650  *  -----------
1651  *
1652  *  Inputs:
1653  *  ------
1654  * 
1655  *  Outputs:
1656  *  -------
1657  *  Return = the last menu spec that would be affected by modifications
1658  *           to the given command id
1659  *
1660  *
1661  *  Comments:
1662  *  --------
1663  * 
1664  *************************************<->***********************************/
1665
1666 static MenuSpec * FindLastMenuSpecToModify(MenuSpec *menuSpec,
1667                                            CARD32 command_id)
1668 {
1669     MenuSpec *curMenuSpec, *lastToModify = (MenuSpec *) NULL;
1670     MenuItem *curItem;
1671
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)
1677     {
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)
1683         {
1684             if (curItem->clientCommandID == command_id)
1685               break;
1686         }
1687
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;
1695     }
1696
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);
1702 }
1703
1704 \f
1705 /*************************************<->*************************************
1706  *
1707  *  RecreateMenuWidgets (matchlist)
1708  *
1709  *
1710  *  Description:
1711  *  -----------
1712  *
1713  *  Inputs:
1714  *  ------
1715  * 
1716  *  Outputs:
1717  *  -------
1718  *  Return = 
1719  *
1720  *
1721  *  Comments:
1722  *  --------
1723  * 
1724  *************************************<->***********************************/
1725
1726 static void RecreateMenuWidgets (WmScreenData *pSD, ClientData *pCD,
1727                                  MatchList **matchlist)
1728 {
1729     MatchList *current;
1730     int count = 0, i;
1731     MenuSpec **to_change;
1732
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)
1737       ++count;
1738
1739     /* If there are no affected menuspecs, then just return. */
1740     if (count == 0) return;
1741
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;
1747
1748     /* Now run through all the matchlist items, storing menuspecs in
1749        that array. If the menuspec is already there, then don't store
1750        it again. */
1751     for (current = *matchlist;
1752          current != (MatchList *) NULL;
1753          current = current->next)
1754     {
1755       for (i = 0; i < count; ++i)
1756       {
1757         if (to_change[i] == current->menuspec) break;
1758         else if (to_change[i] == (MenuSpec *) NULL)
1759         {
1760           to_change[i] = current->menuspec;
1761           break;
1762         }
1763       }
1764     }
1765
1766     /* Run through the array, destroy all existing widgets for each
1767        menuspec */
1768     for (i = 0; i < count && to_change[i] != (MenuSpec *) NULL ; ++i)
1769     {
1770         DestroyMenuSpecWidgets(to_change[i]);
1771     }
1772
1773     /* Run through the array again creating widgets for all the menuspecs */
1774     for (i = 0; i < count && to_change[i] != (MenuSpec *) NULL; ++i)
1775     {
1776         to_change[i]->menuWidget =
1777           CreateMenuWidget (pSD, pCD, to_change[i]->name, pSD->screenTopLevelW,
1778                             TRUE, to_change[i], NULL);
1779     }
1780
1781     /* Free the array. We're done. */
1782     XtFree((char *) to_change);
1783 }
1784
1785 \f
1786 /*************************************<->*************************************
1787  *
1788  *  FreeMatchList (matchlist)
1789  *
1790  *
1791  *  Description:
1792  *  -----------
1793  *
1794  *  Inputs:
1795  *  ------
1796  * 
1797  *  Outputs:
1798  *  -------
1799  *  Return = 
1800  *
1801  *
1802  *  Comments:
1803  *  --------
1804  * 
1805  *************************************<->***********************************/
1806
1807 static void FreeMatchList (MatchList **matchlist)
1808 {
1809     MatchList *current, *next;
1810
1811     current = *matchlist;
1812         
1813     while (current != (MatchList *) NULL)
1814     {
1815         next = current->next;
1816         XtFree(current->command_string);
1817         XtFree(current->funcargs);
1818         XtFree((char *)current);
1819         current = next;
1820     }
1821
1822     *matchlist = (MatchList *) NULL;
1823 }
1824
1825 \f
1826 /*************************************<->*************************************
1827  *
1828  *  StoreMatchedCommand (matchlist, menuSpec, menuItem, command_string,
1829  *                       treenode, function, funcargs)
1830  *
1831  *
1832  *  Description:
1833  *  -----------
1834  *
1835  *  Inputs:
1836  *  ------
1837  * 
1838  *  Outputs:
1839  *  -------
1840  *  Return = 
1841  *
1842  *
1843  *  Comments:
1844  *  --------
1845  *  If the existing match has NULL for the menuitem, then get rid of
1846  *  it and replace with proposed match.
1847  * 
1848  *************************************<->***********************************/
1849
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)
1854 {
1855     MatchList *current, *new;
1856     
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
1863        command_string */
1864     for (current = *matchlist;
1865          current != (MatchList *) NULL;
1866          current = current->next)
1867     {
1868         if (current->menuspec == menuSpec &&
1869             strcmp(current->command_string, command_string) == 0)
1870         {
1871             /* If the currently stored menu item is NULL,
1872                then replace with the new menuitem and return. */
1873             if (current->menuitem == (MenuItem *) NULL)
1874             {
1875                 current->menuitem = menuItem;
1876                 return;
1877             }
1878             /* Otherwise, we have alreay inserted this
1879                command into this menuspec so don't allow
1880                another insertion. */
1881             else return;
1882         }
1883     }
1884     
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;
1895     
1896     /* Stick it at the head of the list. It's easier. */
1897     new->next = *matchlist;
1898     *matchlist = new;
1899 }
1900
1901 \f
1902 /*************************************<->*************************************
1903  *
1904  *  SearchForOtherMatches (pSD, pCD, treenode,
1905  *                         client_match_list, global_match_list,
1906  *                         menuSpec, command_string, 
1907  *                         function, funcargs, duplicate_globals, selection,
1908  *                         greyed_context)
1909  *
1910  *
1911  *  Description:
1912  *  -----------
1913  *  menuSpec = menu spec to exclude from search
1914  *
1915  *  Inputs:
1916  *  ------
1917  * 
1918  *  Outputs:
1919  *  -------
1920  *  Return = 
1921  *
1922  *
1923  *  Comments:
1924  *  --------
1925  * 
1926  *************************************<->***********************************/
1927
1928 static void SearchForOtherMatches (WmScreenData *pSD, ClientData *pCD,
1929                                    CmdTree *treenode,
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)
1936 {
1937     MenuSpec *current, *newMenuSpec;
1938     MenuItem *match;
1939
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)
1944     {
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)
1951         {
1952             if (treenode->subTrees != (CmdTree *) NULL && inLine &&
1953                 (strncmp(match->label, "->", 2) == 0 ||
1954                  strncmp(match->label, "=>", 2) == 0))
1955             {
1956                 CmdTree *tree;
1957                 for (tree = treenode->subTrees;
1958                      tree != (CmdTree *) NULL;
1959                      tree = tree->next)
1960                 {
1961                     char new_command_str[1024];
1962                     char new_funcargs[1024];
1963                     WmFunction inLine_function;
1964
1965                     if (command_string == NULL)
1966                       sprintf(new_command_str, "<%s>", tree->name);
1967                     else
1968                       sprintf(new_command_str, "%s.<%s>", command_string,
1969                               tree->name);
1970                     if (tree->subTrees != (CmdTree *) NULL)
1971                     {
1972                         /* menu to cascade to */
1973                         sprintf(new_funcargs, "<%s>", tree->name);
1974                         inLine_function = F_Menu;
1975                     }
1976                     else
1977                     {
1978                         sprintf(new_funcargs, "%d %ld %ld", tree->commandID,
1979                                 pCD->client, selection);
1980                         inLine_function = F_InvokeCommand;
1981                     }
1982                     StoreMatchedCommand(client_match_list, current, match,
1983                                         new_command_str, tree,
1984                                         inLine_function, new_funcargs,
1985                                         greyed_context);
1986                 }
1987             }
1988             else
1989             {
1990                 StoreMatchedCommand(client_match_list, current, match,
1991                                     command_string, treenode, function,
1992                                     funcargs, greyed_context);
1993             }
1994         }
1995     }
1996
1997     /* Search through all of the global menuspecs also. */
1998     for (current = pSD->menuSpecs;
1999          current != (MenuSpec *) NULL;
2000          current = current->nextMenuSpec)
2001     {
2002         FindClientCommandMatch(current, command_string, &match);
2003         if (match != (MenuItem *) NULL)
2004         {
2005             if (duplicate_globals == TRUE)
2006             {
2007                 /* Create a duplicate of the current menuspec and
2008                    store that away in the client instead of the current
2009                    menuspec. */
2010                 newMenuSpec = ReplaceMenuSpecForClient(current, pCD);
2011
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);
2017             }
2018             else /* Change global menu */
2019             {
2020                 StoreMatchedCommand(global_match_list, current, match,
2021                                     command_string, treenode, function,
2022                                     funcargs, greyed_context);
2023             }
2024         }
2025     }
2026 }
2027
2028 \f
2029 /*************************************<->*************************************
2030  *
2031  *  InsertTreeOnClient (pSD, pCD, tree, client_match_list, global_match_list,
2032  *                      menuSpec, templateMenuItem, command_so_far,
2033  *                      duplicate_globals, selection, greyed_context, inLine)
2034  *
2035  *
2036  *  Description:
2037  *  -----------
2038  *
2039  *  Inputs:
2040  *  ------
2041  * 
2042  *  Outputs:
2043  *  -------
2044  *  Return = 
2045  *
2046  *
2047  *  Comments:
2048  *  --------
2049  *  If duplicate_globals is TRUE, then pCD cannot be NULL.
2050  * 
2051  *************************************<->***********************************/
2052
2053 static void InsertTreeOnClient (WmScreenData *pSD, ClientData *pCD,
2054                                 CmdTree *tree,
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)
2061 {
2062     String new_command_str;
2063     int length;
2064     char funcarg_buf[256];
2065     MenuSpec *newMenuSpec, *last, *dupMenuSpec;
2066     CmdTree *subtree;
2067     MenuItem *bestMatch = (MenuItem *) NULL;
2068
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)
2072       return;
2073
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)
2077       return;
2078
2079     while (tree != (CmdTree *) NULL)
2080     {
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);
2089         else
2090           sprintf(new_command_str, "<%s>", tree->name);
2091
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))
2098         {
2099             tree = tree->next;
2100             XtFree(new_command_str);
2101             continue;
2102         }
2103
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.
2107          */
2108         if (tree->subTrees != (CmdTree *) NULL && inLine == TRUE &&
2109             ForceInLineToCascade(menuSpec, new_command_str,
2110                                  &bestMatch) == FALSE)
2111         {
2112             /* Recursively process subtrees */
2113             for (subtree = tree->subTrees;
2114                  subtree != (CmdTree *) NULL;
2115                  subtree = subtree->next)
2116             {
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);
2124             }
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?) */
2128         }
2129         /* If tree is a command set then we need to create a new
2130            menuSpec. */
2131         else if (tree->subTrees != (CmdTree *) NULL)
2132         {
2133             /* Create the name of the menu for the f.menu command. */
2134             sprintf(funcarg_buf, "<%s>", tree->name);
2135
2136             /* Store the cascade button information so it can be
2137                created later. */
2138             StoreMatchedCommand(
2139                (menuSpec->clientLocal ? client_match_list : global_match_list),
2140                menuSpec, template, new_command_str, tree, F_Menu, funcarg_buf,
2141                greyed_context);
2142
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)
2153             {
2154                 if (newMenuSpec->clientLocal == FALSE)
2155                 {
2156                     newMenuSpec = (MenuSpec *) NULL;
2157                     break;
2158                 }
2159                 if (strcmp(newMenuSpec->name, funcarg_buf) == 0)
2160                   break;
2161             }
2162
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)
2166             {
2167                 for (newMenuSpec = pSD->menuSpecs;
2168                      newMenuSpec != (MenuSpec *) NULL;
2169                      newMenuSpec = newMenuSpec->nextMenuSpec)
2170                 {
2171                     if (strcmp(newMenuSpec->name, funcarg_buf) == 0)
2172                       break;
2173                 }
2174             }
2175
2176             /* If we still don't have a menu spec, then create a new one. */
2177             if (newMenuSpec == (MenuSpec *) NULL)
2178             {
2179                 newMenuSpec = MakeMenuSpec(funcarg_buf,
2180                                            tree == NULL ? (CARD32)NULL
2181                                                         : tree->commandID);
2182                 if (duplicate_globals) newMenuSpec->clientLocal = TRUE;
2183                 else                   newMenuSpec->clientLocal = FALSE;
2184
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;
2189                 else
2190                   last = pSD->menuSpecs;
2191
2192                 /* Find the last menu spec in the list. */
2193                 while (last != (MenuSpec *) NULL &&
2194                        last->nextMenuSpec != (MenuSpec *) NULL)
2195                   last = last->nextMenuSpec;
2196
2197                 /* Put the new menu spec at the end of the list. */
2198                 if (last == (MenuSpec *) NULL)
2199                 {
2200                     if (duplicate_globals)
2201                       pCD->systemMenuSpec = newMenuSpec;
2202                     else
2203                       pSD->menuSpecs = newMenuSpec;
2204                 }
2205                 else last->nextMenuSpec = newMenuSpec;
2206             }
2207
2208             /* Recursively process subtrees */
2209             for (subtree = tree->subTrees;
2210                  subtree != (CmdTree *) NULL;
2211                  subtree = subtree->next)
2212             {
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);
2217             }
2218
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);
2227             
2228         }
2229         else /* the tree is a simple command */
2230         {
2231             /* Store away the push button information so it can be 
2232                created later. */
2233             sprintf(funcarg_buf, "%d %ld %ld", tree->commandID, 
2234                     (pCD == NULL ? None : pCD->client), selection);
2235
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
2239                client. */
2240             if (duplicate_globals)
2241               dupMenuSpec = ReplaceMenuSpecForClient(menuSpec, pCD);
2242             else
2243               dupMenuSpec = menuSpec;
2244
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);
2250
2251             /* Search for any other matches in the existing menu specs
2252                for this command, excluding newMenuSpec. */
2253             
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 */
2260                                   selection,
2261                                   greyed_context, inLine);
2262         }
2263
2264         /* Move on to the next tree item at this level */
2265         tree = tree->next;
2266         XtFree(new_command_str);
2267     }
2268 }
2269
2270 \f
2271 /*************************************<->*************************************
2272  *
2273  *  InsertTreeOnAllClients (pSD, tree, selection, active_context, inLine)
2274  *
2275  *
2276  *  Description:
2277  *  -----------
2278  *
2279  *  Inputs:
2280  *  ------
2281  *  pSD       = per screen data
2282  *  tree      = command tree
2283  *  selection = owned by inserting client
2284  * 
2285  *  Outputs:
2286  *  -------
2287  *  Return = 
2288  *
2289  *
2290  *  Comments:
2291  *  --------
2292  * 
2293  *************************************<->***********************************/
2294 void InsertTreeOnAllClients (WmScreenData *pSD, CmdTree *tree, Atom selection,
2295                              Context active_context, Boolean inLine)
2296 {
2297     ClientListEntry *current;
2298     MatchList *global_matchlist = (MatchList *) NULL;
2299     MatchList *client_matchlist = (MatchList *) NULL;
2300     Context greyed_context = F_CONTEXT_ALL;
2301
2302     /* If there aren't any clients, then there's nothing to do. */
2303     if (pSD->clientList == (ClientListEntry *) NULL)
2304       return;
2305
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);
2311
2312     for (current = pSD->clientList;
2313          current != (ClientListEntry *) NULL;
2314          current = current->nextSibling)
2315     {
2316         /* Ignore client list entries for icons. */
2317         if (current->type == MINIMIZED_STATE)
2318           continue;
2319         InsertTreeOnClient(pSD, current->pCD, tree, &client_matchlist,
2320                            &global_matchlist, current->pCD->systemMenuSpec,
2321                            NULL, NULL, FALSE, 
2322                            selection, greyed_context, inLine);
2323         PerformInsertionsOnMatchList(&client_matchlist);
2324         RecreateMenuWidgets(pSD, current->pCD, &client_matchlist);
2325         FreeMatchList(&client_matchlist);
2326     }
2327     PerformInsertionsOnMatchList(&global_matchlist);
2328     RecreateMenuWidgets(pSD, NULL /* no pcd */, &global_matchlist);
2329     FreeMatchList(&global_matchlist);
2330 }
2331
2332 \f
2333 /*************************************<->*************************************
2334  *
2335  *  InsertTreeOnSingleClient (pSD, pCD, tree, selection, inLine)
2336  *
2337  *
2338  *  Description:
2339  *  -----------
2340  *
2341  *  Inputs:
2342  *  ------
2343  *  pSD       = per screen data
2344  *  tree      = command tree
2345  *  selection = owned by inserting client
2346  *
2347  * 
2348  *  Outputs:
2349  *  -------
2350  *  Return = 
2351  *
2352  *
2353  *  Comments:
2354  *  --------
2355  * 
2356  *************************************<->***********************************/
2357 void InsertTreeOnSingleClient (WmScreenData *pSD, ClientData *pCD,
2358                                CmdTree *tree, Atom selection,
2359                                Context active_context, Boolean inLine)
2360 {
2361     MatchList *global_matchlist = (MatchList *) NULL;
2362     MatchList *client_matchlist = (MatchList *) NULL;
2363     Context greyed_context = F_CONTEXT_ALL;
2364
2365     /* A quick sanity check */
2366     if (pCD == (ClientData *) NULL)
2367       return;
2368
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);
2374
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);
2381 }
2382
2383 \f
2384 /*************************************<->*************************************
2385  *
2386  *  InsertTreeOnRootMenu (pSD, tree, selection, active_context, inLine)
2387  *
2388  *
2389  *  Description:
2390  *  -----------
2391  *
2392  *  Inputs:
2393  *  ------
2394  *  pSD       = per screen data
2395  *  tree      = command tree
2396  *  selection = owned by inserting client
2397  *
2398  * 
2399  *  Outputs:
2400  *  -------
2401  *  Return = 
2402  *
2403  *
2404  *  Comments:
2405  *  --------
2406  * 
2407  *************************************<->***********************************/
2408 void InsertTreeOnRootMenu (WmScreenData *pSD, CmdTree *tree, Atom selection,
2409                            Boolean inLine)
2410 {
2411     MatchList *global_matchlist = (MatchList *) NULL;
2412     MatchList *client_matchlist = (MatchList *) NULL;
2413     Context greyed_context = F_CONTEXT_WINDOW | F_CONTEXT_ICON;
2414     MenuSpec *rootMenu;
2415
2416     /* Find the root menu spec */
2417     for (rootMenu = pSD->menuSpecs;
2418          rootMenu != (MenuSpec *) NULL;
2419          rootMenu = rootMenu->nextMenuSpec)
2420     {
2421         if (strcmp(rootMenu->name, pSD->rootMenu) == 0)
2422           break;
2423     }
2424     
2425     /* If we couldn't find the root menu, then do nothing. */
2426     if (rootMenu == (MenuSpec *) NULL) return;
2427     
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);
2437 }
2438
2439 \f
2440 /*************************************<->*************************************
2441  *
2442  *  RemoveClientCommandFromMenuSpec (menuSpec, id)
2443  *
2444  *
2445  *  Description:
2446  *  -----------
2447  *
2448  *  Inputs:
2449  *  ------
2450  * 
2451  *  Outputs:
2452  *  -------
2453  *
2454  *  Comments:
2455  *  --------
2456  * 
2457  *************************************<->***********************************/
2458 static Boolean RemoveClientCommandFromMenuSpec (MenuSpec *menuSpec,
2459                                                 CARD32 id)
2460 {
2461     MenuItem *curMenuItem, *prevMenuItem = (MenuItem *) NULL;
2462     MenuItem *tmpMenuItem;
2463     Boolean was_changed = FALSE;
2464
2465     curMenuItem = menuSpec->menuItems;
2466     while (curMenuItem != (MenuItem *) NULL)
2467     {
2468         if (curMenuItem->clientCommandID == id)
2469         {
2470             tmpMenuItem = curMenuItem;
2471             curMenuItem = curMenuItem->nextMenuItem;
2472             if (prevMenuItem == (MenuItem *) NULL)
2473                 menuSpec->menuItems = tmpMenuItem->nextMenuItem;
2474             else
2475                 prevMenuItem->nextMenuItem = tmpMenuItem->nextMenuItem;
2476             FreeMenuItem(tmpMenuItem);
2477             was_changed = TRUE;
2478         }
2479         else
2480         {
2481             prevMenuItem = curMenuItem;
2482             curMenuItem = curMenuItem->nextMenuItem;
2483         }
2484     }
2485     return(was_changed);
2486 }
2487
2488 \f
2489 /*************************************<->*************************************
2490  *
2491  *  ModifyClientCommandForMenuSpec (menuSpec, id, modifier, context, newname)
2492  *
2493  *
2494  *  Description:
2495  *  -----------
2496  *
2497  *  Inputs:
2498  *  ------
2499  * 
2500  *  Outputs:
2501  *  -------
2502  *
2503  *  Comments:
2504  *  --------
2505  * 
2506  *************************************<->***********************************/
2507 static Boolean ModifyClientCommandForMenuSpec (MenuSpec *menuSpec,
2508                                                CARD32 id,
2509                                                CmdModifier modifier,
2510                                                Context context,
2511                                                String newname)
2512 {
2513     MenuItem *curMenuItem;
2514     Boolean was_changed = FALSE;
2515     int i, freebutton, buttoncount;
2516
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)
2522       return(FALSE);
2523
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)
2527     {
2528         curMenuItem = menuSpec->menuButtons[i].menuItem;
2529
2530         if ((curMenuItem->clientCommandID == id) &&
2531             (curMenuItem->wmFunction == F_InvokeCommand))
2532         {
2533             switch(modifier)
2534             {
2535               case ENABLE:
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,
2542                                  FALSE);
2543                 else
2544                   XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2545                                  TRUE);
2546                 break;
2547               case DISABLE:
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,
2554                                  FALSE);
2555                 else
2556                   XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2557                                  TRUE);
2558                 break;
2559               case RENAME:
2560                 if (newname != NULL && *newname != '\0')
2561                 {
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
2566                    */
2567                   XmString labelString;
2568
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. */
2574                   
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
2578                    */
2579                   XtVaSetValues(XtParent(XtParent(menuSpec->menuButtons[i].buttonWidget)),
2580                                 XmNuseAsyncGeometry, TRUE, NULL);
2581
2582                   labelString = XmStringGenerate(curMenuItem->label,
2583                                                  XmFONTLIST_DEFAULT_TAG, 
2584                                                  XmCHARSET_TEXT, NULL);
2585                   XtVaSetValues(menuSpec->menuButtons[i].buttonWidget,
2586                                 XmNlabelString, labelString,
2587                                 NULL);
2588                   XmStringFree(labelString);
2589                 }
2590                 break;
2591               case REMOVE:
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;
2596                 break;
2597             }
2598         }
2599     }
2600
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.
2604      */
2605     if (modifier == REMOVE)
2606         was_changed = RemoveClientCommandFromMenuSpec(menuSpec, id);
2607
2608     /* Compact the menu buttons array. */
2609     buttoncount = menuSpec->menuButtonCount;
2610     freebutton = 0;
2611     for (i = 0; i < buttoncount; ++i)
2612     {
2613         if (menuSpec->menuButtons[i].buttonWidget == (Widget) NULL)
2614             --menuSpec->menuButtonCount;
2615         else
2616         {
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;
2623             ++freebutton;
2624         }
2625     }
2626     return(was_changed);
2627 }
2628
2629 \f
2630 /*************************************<->*************************************
2631  *
2632  *  ModifyClientCommandID (pSD, pCD, range, id, modifier, context, newname)
2633  *
2634  *
2635  *  Description:
2636  *  -----------
2637  *
2638  *  Inputs:
2639  *  ------
2640  * 
2641  *  Outputs:
2642  *  -------
2643  *
2644  *  Comments:
2645  *  --------
2646  * 
2647  *************************************<->***********************************/
2648 static void ModifyClientCommandID (WmScreenData *pSD,
2649                                    ClientData *pCD,
2650                                    OpRange range,
2651                                    CARD32 id,
2652                                    CmdModifier modifier,
2653                                    Context context,
2654                                    String newname)
2655 {
2656     MenuSpec *curMenuSpec;
2657     ClientListEntry *curClient;
2658
2659     switch(range)
2660     {
2661       case ALL:
2662         /* Search through all the menu specs of all the clients. */
2663         for (curClient = pSD->clientList;
2664              curClient != (ClientListEntry *) NULL;
2665              curClient = curClient->nextSibling)
2666         {
2667             for (curMenuSpec = curClient->pCD->systemMenuSpec;
2668                  curMenuSpec != (MenuSpec *) NULL;
2669                  curMenuSpec = curMenuSpec->nextMenuSpec)
2670             {
2671                 /* If the menu spec is global then stop searching
2672                    for this client. */
2673                 if (curMenuSpec->clientLocal == FALSE)
2674                   break;
2675                 if (ModifyClientCommandForMenuSpec(curMenuSpec, id,
2676                                                    modifier, context,
2677                                                    newname) == TRUE)
2678                 {
2679                     DestroyMenuSpecWidgets(curMenuSpec);
2680                     curMenuSpec->menuWidget =
2681                       CreateMenuWidget (pSD, curClient->pCD, curMenuSpec->name,
2682                                         pSD->screenTopLevelW, TRUE,
2683                                         curMenuSpec, NULL);
2684                 }
2685             }
2686         }
2687         /* Search through all the global menu specs. */
2688         for (curMenuSpec = pSD->menuSpecs;
2689              curMenuSpec != (MenuSpec *) NULL;
2690              curMenuSpec = curMenuSpec->nextMenuSpec)
2691         {
2692             if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2693                                                context, newname) == TRUE)
2694             {
2695                 DestroyMenuSpecWidgets(curMenuSpec);
2696                 curMenuSpec->menuWidget =
2697                   CreateMenuWidget (pSD, NULL, curMenuSpec->name,
2698                                     pSD->screenTopLevelW, TRUE,
2699                                     curMenuSpec, NULL);
2700             }
2701         }
2702         break;
2703       case ROOT:
2704         {
2705             /*
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.
2711              */
2712
2713
2714             MenuSpec *curMenuSpec;
2715
2716             /* Search through all the global menu specs. */
2717             for (curMenuSpec = pSD->menuSpecs;
2718                  curMenuSpec != (MenuSpec *) NULL;
2719                  curMenuSpec = curMenuSpec->nextMenuSpec)
2720             {
2721               if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2722                                                  context, newname) == TRUE)
2723                 {
2724                   DestroyMenuSpecWidgets(curMenuSpec);
2725                   curMenuSpec->menuWidget =
2726                     CreateMenuWidget (pSD, NULL, curMenuSpec->name,
2727                                       pSD->screenTopLevelW, TRUE,
2728                                       curMenuSpec, NULL);
2729                 }
2730             }
2731         }
2732         break;
2733       case SINGLE:
2734         /* If we weren't passed a valid pCD, then just return. */
2735         if (pCD == (ClientData *) NULL) return;
2736
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)
2745         {
2746             if (curMenuSpec->clientLocal == FALSE)
2747             {
2748                 MenuSpec *last, *cur;
2749
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)
2762                 {
2763                     MenuSpec *newMenuSpec = (MenuSpec *) NULL;
2764                     MenuSpec *firstMenuSpec = (MenuSpec *) NULL;
2765                     MenuSpec *lastMenuSpec = (MenuSpec *) NULL;
2766
2767                     /* Replace all the global menu specs with local 
2768                        ones. */
2769                     for (cur = curMenuSpec;
2770                          cur != (MenuSpec *) NULL && cur != last->nextMenuSpec;
2771                          cur = cur->nextMenuSpec)
2772                     {
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. */
2778                         if (cur == last)
2779                           lastMenuSpec = newMenuSpec;
2780                     }
2781
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)
2788                     {
2789                         DestroyMenuSpecWidgets(newMenuSpec);
2790                         newMenuSpec->menuWidget = 
2791                           CreateMenuWidget(pSD, pCD, newMenuSpec->name,
2792                                            pSD->screenTopLevelW,
2793                                            TRUE, newMenuSpec, NULL);
2794                     }
2795
2796                 }
2797                 /* None of the globals need changing. */
2798                 else break;
2799             }
2800             if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2801                                                context, newname) == TRUE)
2802             {
2803                 DestroyMenuSpecWidgets(curMenuSpec);
2804                 curMenuSpec->menuWidget =
2805                   CreateMenuWidget (pSD, pCD, curMenuSpec->name,
2806                                     pSD->screenTopLevelW, TRUE,
2807                                     curMenuSpec, NULL);
2808             }
2809         }
2810         break;
2811     }
2812 }
2813
2814 \f
2815 /*************************************<->*************************************
2816  *
2817  *  ModifyClientCommandTree (pSD, pCD, range, tree, modifier, context, newname)
2818  *
2819  *
2820  *  Description:
2821  *  -----------
2822  *
2823  *  Inputs:
2824  *  ------
2825  * 
2826  *  Outputs:
2827  *  -------
2828  *
2829  *  Comments:
2830  *  --------
2831  * 
2832  *************************************<->***********************************/
2833 void ModifyClientCommandTree (WmScreenData *pSD,
2834                               ClientData *pCD,
2835                               OpRange range,
2836                               CmdTree *tree,
2837                               CmdModifier modifier,
2838                               Context context,
2839                               String newname)
2840 {
2841     CmdTree *curTree;
2842     CARD32 cmdID;
2843
2844     /* Run through the top level of the tree. */
2845     for (curTree = tree; curTree != (CmdTree *) NULL; curTree = curTree->next)
2846     {
2847         cmdID = curTree->commandID;
2848         ModifyClientCommandID(pSD, pCD, range, cmdID, modifier,
2849                               context, newname);
2850         if (curTree->subTrees != (CmdTree *) NULL)
2851           ModifyClientCommandTree(pSD, pCD, range, curTree->subTrees,
2852                                   modifier, context, newname);
2853     }
2854 }
2855 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
2856 \f
2857 /*************************************<->*************************************
2858  *
2859  *  static Boolean
2860  *  AdjustPBs (menuSpec, pCD, newContext)
2861  *
2862  *
2863  *  Description:
2864  *  -----------
2865  *  This procedure adjusts menu PushButton sensitivities and manage/unmanaged
2866  *  status for a toplevel menu.
2867  *
2868  *
2869  *  Inputs:
2870  *  ------
2871  *  menuSpec =    nonNULL toplevel menu specification with gadget
2872  *  pCD =         client data
2873  *  newContext =  context that the menu is to be posted under.
2874  *
2875  * 
2876  *  Outputs:
2877  *  -------
2878  *  menuSpec =      menu specification with modifications
2879  *  Return =        TRUE iff at least one menu item changed manage status.
2880  *
2881  *
2882  *  Comments:
2883  *  --------
2884  *  Adjusts PushButton sensitivity according to context and function type.
2885  *  Manages/Unmanages PushButtons according to clientFunction resource.
2886  * 
2887  *************************************<->***********************************/
2888 static Boolean AdjustPBs (MenuSpec *menuSpec, ClientData  *pCD,
2889                           Context newContext)
2890 {
2891     MenuButton    *menuButton;
2892     MenuItem      *menuItem;
2893     int            msgc;
2894     unsigned int   n;
2895     long          *pMsg;
2896     Boolean        fSupported;
2897     Boolean        fChangeManaged = FALSE;
2898
2899     /*
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.
2903      */
2904
2905     /* check for bad input value - shouldn't happen. */
2906     if (menuSpec == NULL) return (FALSE);
2907
2908     for (n = 0, menuButton = menuSpec->menuButtons;
2909          n < menuSpec->menuButtonCount;
2910          n++, menuButton++)
2911     {
2912         menuItem = menuButton->menuItem;
2913         if (menuItem->wmFunction == F_Send_Msg)
2914         /* f.send_msg button:  set according to context and message. */
2915         {
2916             if ((newContext & menuItem->greyedContext) ||
2917                 !(pCD && pCD->mwmMessagesCount && pCD->mwmMessages))
2918             /* insensitive context or empty client message list */
2919             {
2920                 XtSetSensitive (menuButton->buttonWidget, FALSE);
2921             }
2922             else
2923             /* 
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.
2927              */
2928             {
2929                 msgc = pCD->mwmMessagesCount;
2930                 pMsg = pCD->mwmMessages;
2931                 fSupported = FALSE;
2932                 while (msgc--)
2933                 /* scan nonempty message list */
2934                 {
2935                     if (*pMsg == (long) menuItem->wmFuncArgs)
2936                     /* found match */
2937                     {
2938                         fSupported = TRUE;
2939                         break;
2940                     }
2941                     pMsg++;  /* next message in list */
2942                 }
2943                 XtSetSensitive (menuButton->buttonWidget, fSupported);
2944             }
2945         }
2946         else
2947         /*
2948          * Non f.send_msg button:
2949          *  Adjust sensitivity according to context.
2950          *  Manage/Unmanage according to clientFunction.
2951          */
2952         {
2953             if (menuSpec->currentContext & menuItem->greyedContext)
2954             /* button is currently insensitive */
2955             {
2956                 if (!(newContext & menuItem->greyedContext))
2957                 /* insensitive -> sensitive */
2958                 {
2959                     XtSetSensitive (menuButton->buttonWidget, TRUE);
2960                 }
2961             }
2962             else
2963             /* button is currently sensitive */
2964             {
2965                 if (newContext & menuItem->greyedContext)
2966                 /* sensitive -> insensitive */
2967                 {
2968                     XtSetSensitive (menuButton->buttonWidget, FALSE);
2969                 }
2970             }
2971 #ifdef WSM
2972             if (menuItem->wmFunction == F_Remove)
2973             {
2974                 /*
2975                  * Only allow remove from workspace if the client
2976                  * is in more than one workspace
2977                  */
2978                 fSupported = (pCD && (pCD->numInhabited > 1));
2979                 XtSetSensitive (menuButton->buttonWidget, fSupported);
2980             }
2981 #endif /* WSM */
2982
2983             if ((menuItem->mgtMask) && pCD)
2984             /* PushButton might not apply */
2985             {
2986 #ifdef WSM
2987                 if ((pCD->clientFunctions & menuItem->mgtMask & MWM_MGT_MASK) ||
2988                     (pCD->dtwmFunctions & menuItem->mgtMask & DTWM_MGT_MASK))
2989 #else /* WSM */
2990                 if (pCD->clientFunctions & menuItem->mgtMask)
2991 #endif /* WSM */
2992                 /* function applies -- manage it */
2993                 {
2994                     if (!menuButton->managed)
2995                     /* unmanaged -> managed */
2996                     {
2997                         XtManageChild (menuButton->buttonWidget);
2998                         menuButton->managed = TRUE;
2999                         fChangeManaged = TRUE;
3000                         if (n == menuSpec->menuButtonCount - 1)
3001                         {
3002                             /* 
3003                              * last item, if it has a separator before
3004                              * it, manage the separator
3005                              */
3006                             
3007                             CheckTerminalSeparator(menuSpec, 
3008                                                    menuButton->buttonWidget, 
3009                                                    True);
3010                         }
3011                     }
3012                 }
3013                 else
3014                 /* function does not apply -- unmanage it */
3015                 {
3016                     if (menuButton->managed)
3017                     /* managed -> unmanaged */
3018                     {
3019                         XtUnmanageChild (menuButton->buttonWidget);
3020                         menuButton->managed = FALSE;
3021                         fChangeManaged = TRUE;
3022
3023                         if (n == menuSpec->menuButtonCount - 1)
3024                         {
3025                             /* 
3026                              * last item, if it has a separator before
3027                              * it, unmanage the separator
3028                              */
3029                             CheckTerminalSeparator(menuSpec, 
3030                                                    menuButton->buttonWidget, 
3031                                                    False);
3032                         }
3033
3034                     }
3035                 }
3036             }
3037             else if (!menuButton->managed)
3038             /* unmanaged PushButton applies */
3039             {
3040                 XtManageChild (menuButton->buttonWidget);
3041                 menuButton->managed = TRUE;
3042                 fChangeManaged = TRUE;
3043             }
3044         }
3045     }
3046
3047     return (fChangeManaged);
3048
3049 } /* END OF FUNCTION AdjustPBs */
3050
3051
3052 \f
3053 /*************************************<->*************************************
3054  *
3055  *  static Boolean
3056  *  SavePBInfo (topMenuSpec, menuItem, itemW)
3057  *
3058  *
3059  *  Description:
3060  *  -----------
3061  *  Fills a MenuButton structure for a PushButton.
3062  *  If necessary, mallocs or reallocs the menuButtons array in the toplevel
3063  *  MenuSpec.
3064  *
3065  *
3066  *  Inputs:
3067  *  ------
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
3074  *
3075  * 
3076  *  Outputs:
3077  *  -------
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
3083  *
3084  *
3085  *  Comments:
3086  *  --------
3087  *  The initial managed status of PushButtons is TRUE.
3088  * 
3089  *************************************<->***********************************/
3090 static Boolean SavePBInfo (MenuSpec *topMenuSpec, MenuItem *menuItem,
3091                              Widget itemW)
3092 {
3093     MenuButton *menuButton;
3094
3095
3096     /* check for bad input value - shouldn't happen. */
3097     if (topMenuSpec == NULL) return (FALSE);
3098
3099     if (topMenuSpec->menuButtonSize == 0)
3100     /* need to create array */
3101     {
3102         topMenuSpec->menuButtonSize = MENU_BUTTON_INC;
3103         topMenuSpec->menuButtons =
3104             (MenuButton *) XtMalloc (MENU_BUTTON_INC * sizeof(MenuButton));
3105     }
3106     else if (topMenuSpec->menuButtonCount == topMenuSpec->menuButtonSize)
3107     /* need larger array */
3108     {
3109         topMenuSpec->menuButtonSize += MENU_BUTTON_INC;
3110         topMenuSpec->menuButtons = (MenuButton *) 
3111             XtRealloc ((char*)topMenuSpec->menuButtons,
3112                      topMenuSpec->menuButtonSize * sizeof(MenuButton));
3113     }
3114
3115     if (topMenuSpec->menuButtons == NULL)
3116     /* insufficent memory */
3117     {
3118         topMenuSpec->menuButtonSize = 0;
3119         topMenuSpec->menuButtonCount = 0;
3120         return (FALSE);
3121     }
3122
3123     menuButton = &(topMenuSpec->menuButtons[topMenuSpec->menuButtonCount]);
3124     topMenuSpec->menuButtonCount++;
3125
3126     menuButton->menuItem = menuItem;
3127     menuButton->buttonWidget = itemW;
3128     menuButton->managed = TRUE;
3129     return (TRUE);
3130
3131 }
3132
3133
3134 \f
3135 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3136 /*************************************<->*************************************
3137  *
3138  * AdjustTearOffControl (cascade, closure, cbackdata) 
3139  *  
3140  * 
3141  *
3142  *
3143  *  Description:
3144  *  -----------
3145  *
3146  *  Inputs:
3147  *  ------
3148  *
3149  * 
3150  *  Outputs:
3151  *  -------
3152  *     returns true iff the tearoff control was enabled or diabled
3153  *     resulting in a change in height.
3154  *
3155  *  Comments:
3156  *  --------
3157  * 
3158  *************************************<->***********************************/
3159 static Boolean
3160 AdjustTearOffControl (Widget cascade,
3161                       XtPointer closure,
3162                       XtPointer cbackdata)
3163 {
3164     Widget submenu = (Widget) closure;
3165     int argn;
3166     Arg args[10];
3167     unsigned char tearoff_model;
3168     Boolean isMwmMenu;
3169
3170     argn = 0;
3171     XtSetArg(args[argn], XmNtearOffModel, &tearoff_model); ++argn;
3172     XtGetValues(submenu, args, argn);
3173
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))
3180       isMwmMenu = True;
3181     else
3182       isMwmMenu = False;
3183
3184     if ((tearoff_model == XmTEAR_OFF_ENABLED) && !isMwmMenu)
3185     {
3186         PRINT("Disabling the tear off\n");
3187         argn = 0;
3188         XtSetArg(args[argn], XmNtearOffModel, XmTEAR_OFF_DISABLED); ++argn;
3189         XtSetValues(submenu, args, argn);
3190
3191         return (True);
3192     }
3193
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)
3201     {
3202         MenuSpec *menuspec;
3203         Widget tearoff_widget = XtParent(cascade);
3204
3205         for (menuspec = wmGD.Screens[0].menuSpecs;
3206              menuspec != (MenuSpec *) NULL;
3207              menuspec = menuspec->nextMenuSpec)
3208         {
3209             if (tearoff_widget == menuspec->menuWidget)
3210             {
3211                 wmGD.menuActive = menuspec;
3212                 break;
3213             }
3214         }
3215
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)
3222         {
3223             wmGD.menuActive = ACTIVE_PSD->menuSpecs;
3224             PRINT("Couldn't find menu spec for tear off\n");
3225         }
3226
3227         /* Add a callback that will clear menuActive when this cascade
3228            is unmapped. */
3229 #if 0
3230         XtAddCallback (submenu, XmNunmapCallback,
3231 #else
3232         XtAddCallback (XtParent(submenu), XmNpopdownCallback,
3233 #endif
3234                        UnmapPulldownCallback,
3235                        (XtPointer) NULL);
3236     }
3237
3238   return (False);
3239 }
3240 \f
3241
3242 /*************************************<->*************************************
3243  *
3244  *  static Boolean
3245  *  CreateClientCommandSeparator (menuItem, child_position, last_child,
3246  *                                newMenuItem)
3247  *
3248  *
3249  *  Description:
3250  *  -----------
3251  *
3252  *  Inputs:
3253  *  ------
3254  *
3255  * 
3256  *  Outputs:
3257  *  -------
3258  *
3259  *  Comments:
3260  *  --------
3261  * 
3262  *************************************<->***********************************/
3263 static Boolean CreateClientCommandSeparator (MenuItem *menuItem,
3264                                              int       child_position,
3265                                              Widget    last_child,
3266                                              MenuItem **newMenuItem)
3267 {
3268     MenuItem *curMenuItem;
3269
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
3278      */
3279
3280     /* Check if a separator directly precedes this one. */
3281     if (child_position > 0 && last_child != (Widget) NULL &&
3282         XmIsSeparator(last_child))
3283       return(FALSE);
3284
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)
3289       return(FALSE);
3290
3291     /* Make sure this separator won't be the first item in the menu. */
3292     if (child_position == 0) return(FALSE);
3293
3294     /* Make sure this separator won't be the last item in the menu. */
3295     if (menuItem->nextMenuItem == (MenuItem *) NULL)
3296       return(FALSE);
3297
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
3303        separator. */
3304     if (menuItem->labelType == TOP_SEPARATOR)
3305     {
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)
3311         {
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)
3317             {
3318                 *newMenuItem = curMenuItem;
3319                 return(FALSE);
3320             }
3321             /* If we found a real menu item, then return TRUE. */
3322             if (curMenuItem->wmFunction != F_Separator &&
3323                 !IsClientCommand(curMenuItem->label))
3324             {
3325                 return(TRUE);
3326             }
3327         }
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);
3331     }
3332
3333     /* Well, nothing failed so let's create it. */
3334     return(TRUE);
3335 }
3336 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3337 \f
3338 /*************************************<->*************************************
3339  *
3340  *  CreateMenuWidget (pSD, menuName, parent, fTopLevelPane, topMenuSpec, 
3341  *                    moreMenuItems)
3342  *
3343  *
3344  *  Description:
3345  *  -----------
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.
3349  *
3350  *
3351  *  Inputs:
3352  *  ------
3353  *  pSD ---------- pointer to screen data
3354  *  menuName ----- the name of the menu specification to be used to create
3355  *                 the menu widget.
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.
3360  * 
3361  * 
3362  *  Outputs:
3363  *  -------
3364  *  Return = created PopupMenu or PulldownMenu widget, or NULL.
3365  *
3366  *
3367  *  Comments:
3368  *  --------
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 
3371  *  dispatcher.
3372  * 
3373  *************************************<->***********************************/
3374
3375 typedef struct _StrList
3376 {
3377    XmString         string;
3378    struct _StrList *next;
3379 } StrList;
3380
3381 Widget CreateMenuWidget (WmScreenData *pSD,
3382 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3383                          ClientData *pCD,
3384 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3385                          String menuName, Widget parent,
3386                          Boolean fTopLevelPane, MenuSpec *topMenuSpec,
3387                          MenuItem *moreMenuItems)
3388 {
3389     int         i, n;
3390     Arg         sepArgs[1];
3391     Arg         args[10];
3392     MenuSpec   *menuSpec = (MenuSpec *)NULL;
3393     MenuItem   *menuItem;
3394     Widget      menuShellW;
3395     Widget      menuW;
3396     Widget      subMenuW;
3397     Widget      children[CHILDREN_CACHE];
3398     Pixmap      labelPixmap;
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;
3406     XmString    tmpstr;
3407 #ifndef IBM_151913
3408     Screen     *scr;
3409 #endif
3410
3411
3412     /* check for bad input values. */
3413     if ((menuName == NULL) || (pSD == NULL))
3414     {
3415         return (NULL);
3416     }
3417
3418     /*
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.
3423      */
3424     if (fTopLevelPane)
3425     {
3426         menuSpec = topMenuSpec;
3427     }
3428     else
3429     {
3430         menuSpec = pSD->menuSpecs;
3431         while (menuSpec)
3432         {
3433             if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
3434             {
3435                 break;  /* found menuName's specification */
3436             }
3437             menuSpec = menuSpec->nextMenuSpec;  /* keep looking */
3438         }
3439     }
3440
3441     if (menuSpec == NULL)
3442     /* (submenu) specification not found */
3443     {
3444         MWarning(((char *)GETMESSAGE(48, 4, "Menu specification %s not found\n")), menuName);
3445         return (NULL);
3446     }
3447
3448     /*
3449      * If menuSpec is marked, we have menu recursion => fail.
3450      *  Otherwise, mark it.
3451      */
3452
3453     if (menuSpec->currentContext & CR_MENU_MARK)   /* marked? */
3454     /* menu recursion */
3455     {
3456         MWarning(((char *)GETMESSAGE(48, 5, "Menu recursion detected for %s\n")), menuName);
3457         return (NULL);
3458     }
3459     menuSpec->currentContext |= CR_MENU_MARK;   /* no, mark it */
3460
3461     /*
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.
3466      */
3467     i = 0;
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++;
3476
3477     if ((XtParent (parent) != NULL) && XmIsMenuShell (XtParent (parent)))
3478     {
3479         parent = XtParent (parent);
3480     }
3481
3482     menuShellW = XtCreatePopupShell (SHELL_NAME, xmMenuShellWidgetClass,
3483                                      parent, (ArgList) args, i);
3484
3485     /*
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.
3490      */
3491
3492     i = 0;
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++;
3496
3497     if (fTopLevelPane)
3498     {
3499         XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_POPUP); i++;
3500         XtSetArg (args[i], XmNpopupEnabled, (XtArgVal) TRUE); i++;
3501         menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
3502                                 (ArgList) args, i);
3503         XtAddCallback (menuW, XmNunmapCallback, UnmapCallback, 
3504                                 (XtPointer) menuSpec);
3505     }
3506     else
3507     {
3508         XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_PULLDOWN); i++;
3509         menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
3510                                 (ArgList) args, i);
3511     }
3512
3513     /*
3514      * Create the specified menu entries as children of the menupane.
3515      * Menus may contain the following widgets:
3516      *
3517      *   Label
3518      *   Separator 
3519      *   CascadeButton
3520      *   PushButton
3521      *
3522      * Add separator gadgets around menu titles.
3523      */
3524
3525     XtSetArg (sepArgs[0], XmNseparatorType, (XtArgVal) XmDOUBLE_LINE);
3526
3527     n = 0;
3528     menuItem = menuSpec->menuItems;
3529     if ((menuItem == NULL) && (moreMenuItems != NULL))
3530     /* handle custom menu with empty standard specification */
3531     {
3532         menuSpec->menuItems = menuItem = moreMenuItems;
3533         moreMenuItems = NULL;
3534     }
3535     while (menuItem)
3536     {
3537         i = 0;
3538
3539 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3540         labelIsClientCommand = IsClientCommand(menuItem->label);
3541 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3542
3543         if (menuItem->wmFunction == F_Separator)
3544         /* 
3545          * Add a Separator gadget for a menu separator.
3546          * An immediately following title will not have a top separator.
3547          */
3548         {
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
3556              */
3557             if (labelIsClientCommand)
3558             {
3559                 if (CreateClientCommandSeparator(menuItem, n,
3560                                                  (n > 0 ? children[n - 1] :
3561                                                   (Widget) NULL),
3562                                                  &menuItem))
3563                 {    
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) */
3567                     children[n++] =
3568                       XmCreateSeparatorGadget (menuW, SEPARATOR_NAME, 
3569                                                (ArgList)NULL, 0);
3570                     fUseTitleSep = FALSE;
3571                 }
3572             }
3573             else
3574 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3575             {
3576                 children[n] =
3577                   XmCreateSeparatorGadget (menuW, SEPARATOR_NAME, 
3578                                            (ArgList)NULL, 0);
3579                 fUseTitleSep = FALSE;
3580             }
3581         } /* F_Separator */
3582
3583         else
3584 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3585             if (!labelIsClientCommand)
3586 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3587         /*
3588          * We will use one of:
3589          *
3590          *   Label
3591          *   CascadeButton
3592          *   PushButton
3593          */
3594         {
3595             /*
3596              * Construct the label
3597              */
3598             if ((menuItem->labelType == XmPIXMAP) &&
3599                  (labelPixmap =
3600                       MakeCachedLabelPixmap (pSD, menuW,
3601                                              menuItem->labelBitmapIndex)))
3602             {
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++;
3607             }
3608             else
3609             {
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));
3614                 if (sPtr == NULL)
3615                   {
3616                      MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
3617                      return (NULL);
3618                   }
3619                 else
3620                   {
3621                      sPtr->string  = tmpstr;
3622                      sPtr->next    = stringsToFree;
3623                      stringsToFree = sPtr;
3624                   }
3625             }
3626
3627             if (menuItem->wmFunction == F_Title)
3628             /* 
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.
3633              */
3634             {
3635                 if (fUseTitleSep)
3636                 {
3637                     children[n] =
3638                         XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3639                                                  sepArgs, 1); n++;
3640                 }
3641
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,
3646                                           sepArgs, 1); 
3647
3648                 /*
3649                  * A following title will have both separators.
3650                  */
3651
3652                 fUseTitleSep = TRUE;
3653             }
3654
3655             else
3656             /*
3657              * We will use one of:
3658              *
3659              *   CascadeButton
3660              *   PushButton
3661              *
3662              * Both support mnemonics; only PushButtons support accelerators.
3663              */
3664             {
3665                 /*
3666                  * Align text on the left.
3667                  * Set any mnemonic text.
3668                  */
3669                 XtSetArg (args[i], XmNalignment, XmALIGNMENT_BEGINNING); i++;
3670
3671                 if (menuItem->mnemonic)
3672                 {
3673                     XtSetArg (args[i], XmNmnemonic, 
3674                                (XtArgVal) menuItem->mnemonic); i++;
3675                 }
3676
3677                 if (menuItem->wmFunction == F_Menu)
3678                 /* 
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 
3685                  *   created.
3686                  */
3687                 {
3688                     subMenuW = CREATE_MENU_WIDGET (pSD, pCD,
3689                                                  menuItem->wmFuncArgs, menuW,
3690                                                  FALSE, topMenuSpec, 
3691                                                  (MenuItem *)NULL);
3692                     if (subMenuW)
3693                     /*
3694                      * Attach submenu to cascade button. 
3695                      */
3696                     {
3697                         XtSetArg (args[i], XmNsubMenuId, (XtArgVal) subMenuW);
3698                             i++;
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) */
3706                     }
3707                     else
3708                     /*
3709                      * Unable to create submenupane: make the entry insensitive.
3710                      */
3711                     {
3712                         children[n] = XmCreateCascadeButtonGadget (menuW,
3713                                           CASCADE_BTN_NAME, (ArgList) args, i);
3714                         XtSetSensitive (children[n], FALSE);
3715                     }
3716
3717                     /*
3718                      * A following title will have both separators.
3719                      */
3720
3721                     fUseTitleSep = TRUE;
3722                 }
3723
3724                 else 
3725                 /* 
3726                  * Create a PushButton gadget.
3727                  */
3728                 {
3729                     /*
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.
3733                      */
3734                     if (menuItem->accelText)
3735                     {
3736                         XtSetArg (args[i], XmNacceleratorText, (XtArgVal)
3737                                   (tmpstr = XmStringCreateLocalized(menuItem->accelText))); i++;
3738                         sPtr = (StrList *) XtMalloc(sizeof(StrList));
3739                         if (sPtr == NULL)
3740                           {
3741                              MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
3742                              return (NULL);
3743                           }
3744                         else
3745                           {
3746                              sPtr->string = tmpstr;
3747                              sPtr->next   = stringsToFree;
3748                              stringsToFree = sPtr;
3749                           }
3750
3751                         if ((accelKeySpec = (KeySpec *)
3752                                  XtMalloc (sizeof (KeySpec ))) == NULL)
3753                         /* Handle insufficent memory */
3754                         {
3755                             MWarning (((char *)GETMESSAGE(48, 6, "Insufficient memory for menu %s\n")),
3756                                       menuName);
3757                             menuSpec->currentContext &= ~CR_MENU_MARK;
3758                             return (NULL);
3759                         }
3760
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;
3769                     }
3770
3771                     children[n] = XmCreatePushButtonGadget (menuW, 
3772                                       PUSH_BTN_NAME, (ArgList) args, i);
3773
3774                     /* 
3775                      * Set sensitivity.  Initially we only consider the context
3776                      * of the top level menupane.
3777                      */
3778
3779                     if (menuItem->greyedContext & topMenuSpec->currentContext)
3780                     /* insensitive button in this context*/
3781                     {
3782                         XtSetSensitive (children[n], FALSE);
3783                     }
3784                     else
3785                     /* sensitive button in this context*/
3786                     {
3787                         XtSetSensitive (children[n], TRUE);
3788                     }
3789
3790                     /*
3791                      * If necessary, fill a menuButtons element for this 
3792                      * PushButton.  Malloc or Realloc the array if necessary.
3793                      */
3794                     if ((menuItem->greyedContext) || (menuItem->mgtMask))
3795                     {
3796                         if (!SavePBInfo (topMenuSpec, menuItem, children[n]))
3797                         {
3798                             MWarning(((char *)GETMESSAGE(48, 7, "Insufficient memory for menu %s\n")),
3799                                        menuName);
3800                             menuSpec->currentContext &= ~CR_MENU_MARK;
3801                             return (NULL);
3802                         }
3803                     }
3804
3805                     /*
3806                      * Set up the function callback.
3807                      * A following title will have both separators.
3808                      */
3809
3810                     XtAddCallback (children[n], XmNactivateCallback,
3811                             (XtCallbackProc)ActivateCallback, 
3812                             (XtPointer) menuItem);
3813
3814                     fUseTitleSep = TRUE;
3815                 }
3816             }
3817         }
3818
3819         /*
3820          * Increment the children array count if we actually
3821          * created a new child.
3822          */
3823 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3824         if (!labelIsClientCommand)
3825 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3826             n++;
3827
3828         /*
3829          * Next menu item:  handle custom items and full children[]. 
3830          */
3831         menuItem = menuItem->nextMenuItem;
3832         if ((menuItem == NULL) && (moreMenuItems != NULL))
3833         {
3834             menuSpec->menuItems = menuItem = moreMenuItems;
3835             moreMenuItems = NULL;
3836         }
3837         if (n >= CHILDREN_CACHE - 2)  /* leave room for title separators */
3838         {
3839             XtManageChildren (children, n);
3840             n = 0;
3841         }
3842     }
3843
3844     if (n > 0)
3845     {
3846         XtManageChildren (children, n);
3847     }
3848
3849     /*
3850      * Get the initial height of the top level menu pane shell.
3851      * The actual height will change according to clientFunctions.
3852      */
3853     if (fTopLevelPane)
3854     {
3855         i = 0;
3856         XtSetArg (args[i], XtNheight, &menuHeight);  i++;
3857         XtGetValues (menuW, (ArgList)args, i);
3858         topMenuSpec->height = (unsigned int) menuHeight;
3859     }
3860
3861 #ifndef IBM_151913
3862     /*
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.
3865     */
3866
3867     i = 0;
3868     XtSetArg (args[i], XtNheight, &menuHeight);  i++;
3869     XtGetValues (menuW, (ArgList)args, i);
3870     scr = XtScreen (menuW);
3871     if (menuHeight > (Dimension)scr->height) {
3872         i = 0;
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);
3878     }
3879 #endif  /* IBM_151913 */
3880
3881     /* free the string that may have been created earlier. */
3882     for (sPtr = stringsToFree; sPtr != NULL; )
3883       {
3884          stringsToFree = stringsToFree->next;
3885          XmStringFree(sPtr->string);
3886          XtFree((char *)sPtr);
3887          sPtr = stringsToFree;
3888       }
3889
3890
3891     /* Unmark the menu specification and return. */
3892     menuSpec->currentContext &= ~CR_MENU_MARK;
3893     return (menuW);
3894
3895 } /* END OF FUNCTION CreateMenuWidget */
3896
3897
3898 \f
3899 /*************************************<->*************************************
3900  *
3901  *  PostMenu (menuSpec, pCD, x, y, button, newContext, flags, passedInEvent)
3902  *
3903  *
3904  *  Description:
3905  *  -----------
3906  *  This function is used to post a menu at a particular location.
3907  *
3908  *
3909  *  Inputs:
3910  *  ------
3911  *  menuSpec =      menu specification
3912  *  pCD =           client data
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
3915  *                  posted by a key
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
3919  * 
3920  *  Outputs:
3921  *  -------
3922  *  menuSpec =        menu specification with modifications
3923  *  wmGD.menuClient = pCD
3924  *  wmGD.menuActive = menuSpec
3925  *
3926  *
3927  *  Comments:
3928  *  --------
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.
3933  * 
3934  *************************************<->***********************************/
3935
3936 void PostMenu (MenuSpec *menuSpec, ClientData *pCD, int x, int y, unsigned int button, Context newContext, long flags, XEvent *passedInEvent)
3937 {
3938     int              i;
3939     Arg              args[3];
3940     unsigned int     whichButton;
3941     Dimension        menuHeight;
3942     XButtonPressedEvent event;
3943     Window           saveWindow;
3944     Display          *saveDisplay;
3945 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3946     Boolean          menuAdjusted;
3947 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3948     
3949     if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
3950     {
3951         return;
3952     }
3953
3954
3955     /* 
3956      * Don't post a menu from an icon in the iconbox if the
3957      * icon is not visible
3958      */
3959     if((newContext == F_SUBCONTEXT_IB_WICON ||
3960        newContext == F_SUBCONTEXT_IB_IICON) &&
3961        (!(IconVisible(pCD))))
3962     {
3963         return;
3964     }
3965
3966     /*
3967      * Set grabContext to be used in GrabWin when no event is passed
3968      * to GrabWin. 
3969      */
3970
3971     wmGD.grabContext = newContext;
3972
3973     /*
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.
3978      * 
3979      *  Also...
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)
3985      */
3986     
3987 #ifdef WSM
3988     if(pCD && pCD->clientFlags & ICON_BOX)
3989     {
3990         newContext |= F_CONTEXT_ICONBOX;
3991     }
3992
3993 #endif /* WSM */
3994
3995 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3996     menuAdjusted =
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))
4001         || menuAdjusted
4002 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
4003         )
4004     {
4005         i = 0;
4006         XtSetArg (args[i], XtNheight, &menuHeight);  i++;
4007         XtGetValues (menuSpec->menuWidget, (ArgList)args, i);
4008         menuSpec->height = (unsigned int) menuHeight;
4009     }
4010     menuSpec->currentContext = newContext;
4011
4012     /*
4013      *  Adjust the whichButton resource if necessary.
4014      *  Use SELECT_BUTTON for NoButton.
4015      */
4016
4017     whichButton = (button == NoButton) ? SELECT_BUTTON : button;
4018     if (whichButton != menuSpec->whichButton)
4019     {
4020         i = 0;
4021         XtSetArg (args[i], XmNwhichButton, (XtArgVal) whichButton); i++;
4022         XtSetValues (menuSpec->menuWidget, args, i);
4023         menuSpec->whichButton = whichButton;
4024     }
4025
4026     /*
4027      *  Determine the position of the popup menu.
4028      *  Compute position if necessary (system menu).
4029      */
4030
4031     if (!(flags & POST_AT_XY))
4032     /* compute the position */
4033     {
4034         GetSystemMenuPosition (pCD, &x, &y, menuSpec->height, newContext);
4035     }
4036
4037     event.x_root = x;
4038     event.y_root = y;
4039     XmMenuPosition (menuSpec->menuWidget, &event);
4040
4041     wmGD.menuClient = pCD;
4042     wmGD.menuActive = menuSpec;   /* set to NULL within UnmapCallback() */
4043
4044     /* 
4045      * Post the menu by managing its top-level RowColumn.
4046      *
4047      * First dispatch the event to set the time stamp in the toolkit
4048      */
4049
4050     if(passedInEvent)
4051     {
4052         saveWindow = passedInEvent->xany.window;
4053         saveDisplay = passedInEvent->xany.display;
4054         passedInEvent->xany.window = 0;
4055         passedInEvent->xany.display = XtDisplay(menuSpec->menuWidget);
4056
4057         XtDispatchEvent(passedInEvent);
4058         passedInEvent->xany.window = saveWindow;
4059         passedInEvent->xany.display = saveDisplay;
4060
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);
4066     }
4067     
4068 #ifndef ALTERNATE_POSTMENU
4069
4070     XtManageChild (menuSpec->menuWidget);
4071
4072 #else
4073     if (flags & POST_STICKY)
4074     {
4075         _XmSetPopupMenuClick(menuSpec->menuWidget, True);
4076     }
4077     else
4078     {
4079         _XmSetPopupMenuClick(menuSpec->menuWidget, False);
4080     }
4081
4082     /* 
4083      * Post the menu by calling the convenience routine that verifies
4084      * the button event, updates the Xt timestamp, and finally manages
4085      * the pane.
4086      */
4087
4088     _XmPostPopupMenu( menuSpec->menuWidget, passedInEvent);
4089 #endif
4090
4091
4092     /*
4093      *  set the traversal state.
4094      */
4095
4096     if ((button == NoButton) || (flags & POST_TRAVERSAL_ON))
4097     /* turn traversal on */
4098     {
4099         TraversalOn (menuSpec);
4100     }
4101     else
4102     /* turn traversal off */
4103     {
4104         TraversalOff (menuSpec);
4105     }
4106
4107 } /* END OF FUNCTION PostMenu */
4108
4109
4110 \f
4111 /*************************************<->*************************************
4112  *
4113  *  UnpostMenu (menuSpec)
4114  *
4115  *
4116  *  Description:
4117  *  -----------
4118  *  This function is used to unpost a menu.
4119  *
4120  *
4121  *  Inputs:
4122  *  ------
4123  *  menuSpec =      menu specification
4124  * 
4125  *  Outputs:
4126  *  -------
4127  *  None.
4128  *
4129  *
4130  *  Comments:
4131  *  --------
4132  *  wmGD.menuActive and wmGD.menuUnpostKey are set to NULL within 
4133  *  UnmapCallback.
4134  * 
4135  *************************************<->***********************************/
4136
4137 void UnpostMenu (MenuSpec *menuSpec)
4138 {
4139     if (menuSpec && (menuSpec->menuWidget))
4140     /* 
4141      * Unpost the menu by unmanaging its top-level RowColumn.
4142      */
4143     {
4144         XtUnmanageChild (menuSpec->menuWidget);
4145 #ifndef OLD_COLORMAP
4146         ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
4147 #endif
4148     }
4149
4150 } /* END OF FUNCTION UnpostMenu */
4151
4152
4153 \f
4154 /*************************************<->*************************************
4155  *
4156  *  ActivateCallback (w, client_data, call_data)
4157  *
4158  *
4159  *  Description:
4160  *  -----------
4161  *  This function is called whenever a menu item is selected.
4162  *
4163  *
4164  *  Inputs:
4165  *  ------
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
4170  *
4171  * 
4172  *  Outputs:
4173  *  -------
4174  *  None.
4175  *
4176  *
4177  *  Comments:
4178  *  --------
4179  *  None.
4180  * 
4181  *************************************<->***********************************/
4182
4183 void ActivateCallback (Widget w, caddr_t client_data, caddr_t call_data)
4184 {
4185     WmScreenData *pSD;
4186
4187     /* set active screen */
4188     pSD = GetScreenForWindow (XtWindow(w));
4189     if (pSD) SetActiveScreen (pSD);
4190
4191     ((MenuItem *)client_data)->wmFunction (
4192                 ((MenuItem *)client_data)->wmFuncArgs, wmGD.menuClient, NULL);
4193
4194 } /* END OF FUNCTION ActivateCallback */
4195
4196
4197 \f
4198 /*************************************<->*************************************
4199  *
4200  *  UnmapCallback (w, client_data, call_data)
4201  *
4202  *
4203  *  Description:
4204  *  -----------
4205  *  This function is called whenever a toplevel RowColumn is unmapped.
4206  *
4207  *
4208  *  Inputs:
4209  *  ------
4210  *  w =
4211  *  client_data =       not used
4212  *  call_data =         not used
4213  *  wmGD.gadgetClient = last client with depressed client
4214  *
4215  * 
4216  *  Outputs:
4217  *  -------
4218  *  wmGD.menuActive = NULL
4219  *  wmGD.menuUnpostKeySpec = NULL
4220  *  wmGD.checkHotspot = FALSE
4221  *
4222  *
4223  *  Comments:
4224  *  --------
4225  *  None.
4226  * 
4227  *************************************<->***********************************/
4228
4229 static void UnmapCallback (Widget w, XtPointer client_data,
4230                            XtPointer call_data)
4231 {
4232     wmGD.menuActive = NULL;
4233     wmGD.menuUnpostKeySpec = NULL;
4234     wmGD.checkHotspot = FALSE;
4235
4236     if (wmGD.gadgetClient) 
4237     {
4238         PopGadgetOut(wmGD.gadgetClient, FRAME_SYSTEM);
4239     }
4240
4241 #ifndef OLD_COLORMAP
4242     ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
4243 #endif
4244     PullExposureEvents();
4245
4246 } /* END OF FUNCTION UnmapCallback */
4247
4248 \f
4249 /*************************************<->*************************************
4250  *
4251  *  MWarning (message)
4252  *
4253  *
4254  *  Description:
4255  *  -----------
4256  *  This function lists a message to stderr.
4257  *
4258  *
4259  *  Inputs:
4260  *  ------
4261  *  format  = pointer to a format string
4262  *  message = pointer to a message string
4263  * 
4264  *************************************<->***********************************/
4265
4266 void MWarning (char *format, char *message)
4267 {
4268
4269     if (strlen(format) + strlen(message)  <  (size_t) MAXWMPATH)
4270       {
4271          char pch[MAXWMPATH+1];
4272
4273          sprintf (pch, format, message);
4274          Warning (pch);
4275       }
4276
4277 } /* END OF FUNCTION MWarning */
4278
4279
4280 \f
4281 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
4282 /*************************************<->*************************************
4283  *
4284  *  UnmapPulldownCallback (w, client_data, call_data)
4285  *
4286  *
4287  *  Description:
4288  *  -----------
4289  *
4290  *
4291  *  Inputs:
4292  *  ------
4293  * 
4294  *  Outputs:
4295  *  -------
4296  *
4297  *  Comments:
4298  *  --------
4299  * 
4300  *************************************<->***********************************/
4301
4302 static void UnmapPulldownCallback (Widget w, XtPointer client_data,
4303                                    XtPointer call_data)
4304 {
4305     wmGD.menuActive = (MenuSpec *) NULL;
4306 } /* END OF FUNCTION UnmapPulldownCallback */
4307 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
4308
4309 \f
4310 /*************************************<->*************************************
4311  *
4312  *  TraversalOff (menuSpec)
4313  *
4314  *
4315  *  Description:
4316  *  -----------
4317  *  This function turns menu traversal off.
4318  *
4319  *
4320  *  Inputs:
4321  *  ------
4322  *  menuSpec = menu specification
4323  *
4324  * 
4325  *  Outputs:
4326  *  -------
4327  *  None.
4328  *
4329  *
4330  *  Comments:
4331  *  --------
4332  *  None.
4333  * 
4334  *************************************<->***********************************/
4335
4336 void TraversalOff (MenuSpec *menuSpec)
4337 {
4338     if (menuSpec && (menuSpec->menuWidget))
4339     {
4340         /* function pointer */
4341         (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
4342                                        ->row_column_class.menuProcedures) 
4343                 /* argument list */
4344                (XmMENU_TRAVERSAL, menuSpec->menuWidget, False, NULL, NULL);
4345     }
4346
4347 } /* END OF FUNCTION TraversalOff */
4348
4349
4350 \f
4351 /*************************************<->*************************************
4352  *
4353  *  TraversalOn (menuSpec)
4354  *
4355  *
4356  *  Description:
4357  *  -----------
4358  *  This function turns menu traversal on.
4359  *
4360  *
4361  *  Inputs:
4362  *  ------
4363  *  menuSpec = menu specification
4364  *
4365  * 
4366  *  Outputs:
4367  *  -------
4368  *  None.
4369  *
4370  *
4371  *  Comments:
4372  *  --------
4373  *  None.
4374  * 
4375  *************************************<->***********************************/
4376
4377 void TraversalOn (MenuSpec *menuSpec)
4378 {
4379
4380     if (menuSpec && (menuSpec->menuWidget))
4381     {
4382         /* function pointer */
4383         (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
4384                                    ->row_column_class.menuProcedures) 
4385                 /*argument list */
4386                (XmMENU_TRAVERSAL, menuSpec->menuWidget, True, NULL, NULL);
4387     }
4388
4389 } /* END OF FUNCTION TraversalOn */
4390
4391
4392 \f
4393 /*************************************<->*************************************
4394  *
4395  *  FreeCustomMenuSpec (menuSpec)
4396  *
4397  *
4398  *  Description:
4399  *  -----------
4400  *  This procedure destroys a custom MenuSpec structure and its associated 
4401  *  menu widget, menuItems list, menuButtons array, and menu accelerator list.
4402  *
4403  *
4404  *  Inputs:
4405  *  ------
4406  *  menuSpec = MenuSpec structure
4407  *
4408  * 
4409  *  Outputs:
4410  *  -------
4411  *  None.
4412  *
4413  *
4414  *  Comments:
4415  *  --------
4416  *  Assumes that a MenuSpec is custom iff its name is NULL.
4417  *
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!
4421  * 
4422  *************************************<->***********************************/
4423
4424 void FreeCustomMenuSpec (MenuSpec *menuSpec)
4425 {
4426     MenuItem    *menuItem;
4427     MenuItem    *nextMenuItem;
4428     KeySpec     *accelKeySpec;
4429     KeySpec     *nextAccelKeySpec;
4430
4431     if ((menuSpec == NULL) || (menuSpec->name != NULL))
4432     /* we only destroy custom menus! */
4433     {
4434         return;
4435     }
4436   
4437     /*
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.
4442      */
4443      if (wmGD.menuActive == menuSpec)
4444        UnmapCallback((Widget)NULL, (caddr_t)NULL, (caddr_t)NULL);
4445     /*
4446      * End fix for CR 5450
4447      */
4448  
4449     menuItem = menuSpec->menuItems;
4450     while (menuItem)
4451     {
4452         nextMenuItem = menuItem->nextMenuItem;
4453         FreeMenuItem (menuItem);
4454         menuItem = nextMenuItem;
4455     }
4456
4457     if (menuSpec->menuButtons)
4458     {
4459         XtFree ((char *)menuSpec->menuButtons);
4460     }
4461
4462     accelKeySpec = menuSpec->accelKeySpecs;
4463     while (accelKeySpec)
4464     {
4465         nextAccelKeySpec = accelKeySpec->nextKeySpec;
4466         XtFree ((char *)accelKeySpec);
4467         accelKeySpec = nextAccelKeySpec;
4468     }
4469
4470     if (menuSpec->menuWidget)
4471     /* destroy all children of the menu's MenuShell parent */
4472     {
4473         XtDestroyWidget (XtParent(menuSpec->menuWidget));
4474     }
4475
4476     XtFree ((char *)menuSpec);
4477
4478 } /* END OF FUNCTION FreeCustomMenuSpec */
4479
4480 \f