Fix some minor issues and re-enable building of DE, ES, FR, and IT locale data (help...
[oweals/cde.git] / cde / programs / dtwm / WmMenu.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* 
24  * (c) Copyright 1989, 1990, 1991, 1992, 1993 OPEN SOFTWARE FOUNDATION, INC. 
25  * ALL RIGHTS RESERVED 
26 */ 
27 /* 
28  * Motif Release 1.2.3
29 */ 
30 #ifdef REV_INFO
31 #ifndef lint
32 static char rcsid[] = "$XConsortium: WmMenu.c /main/15 1996/11/20 15:20:17 rswiston $"
33 #endif
34 #endif
35 /*
36  * (c) Copyright 1987, 1988, 1989, 1990 HEWLETT-PACKARD COMPANY */
37 /*
38  * (c) Copyright 1987, 1988 DIGITAL EQUIPMENT CORPORATION */
39 /*
40  * (c) Copyright 1988 MASSACHUSETTS INSTITUTE OF TECHNOLOGY */
41
42 /*
43  * Included Files:
44  */
45
46 #include "WmGlobal.h"
47 #include "WmCEvent.h"
48 #include "WmResource.h"
49 #include "WmResParse.h"
50 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
51 # include "WmDebug.h"
52 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
53 #include <stdio.h>
54
55 #include <X11/Shell.h>
56
57 #include <Xm/Xm.h>
58 #include <Xm/XmP.h>
59 #include <Xm/CascadeB.h>
60 #include <Xm/CascadeBG.h>
61 #include <Xm/Label.h>
62 #include <Xm/LabelG.h>
63 #include <Xm/MenuShell.h>
64 #include <Xm/PushB.h>
65 #include <Xm/PushBG.h>
66 #include <Xm/RowColumn.h>
67 #include <Xm/RowColumnP.h>
68 #include <Xm/Separator.h>
69 #include <Xm/SeparatoG.h>
70
71 #define SHELL_NAME "menu"
72 #define SEPARATOR_NAME "separator"
73 #define TITLE_NAME "title_name"
74 #define CASCADE_BTN_NAME "cascadebutton"
75 #define PUSH_BTN_NAME "pushbutton"
76
77 #define CHILDREN_CACHE  22
78 #define MENU_BUTTON_INC 5
79
80 /*
81  * include extern functions
82  */
83 #include "WmMenu.h"
84 #include "WmCDecor.h"
85 #include "WmColormap.h"
86 #include "WmEvent.h"
87 #include "WmFunction.h"
88 #include "WmIconBox.h"
89 #include "WmImage.h"
90 #include "WmError.h"
91 #ifdef WSM
92 #include "WmWrkspace.h"
93 #endif /* WSM */
94
95
96 static void UnmapCallback (Widget w, XtPointer client_data,
97                            XtPointer call_data);
98 static MenuItem *DuplicateMenuItems (MenuItem *menuItems);
99
100 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
101 static MenuExclusion *DuplicateMenuExclusions(MenuExclusion *exclusions);
102 static Boolean FindClientCommandMatch (MenuSpec *menuSpec,
103                                        String clientCommand,
104                                        MenuItem **menuItem);
105 static void InsertTreeOnClient (WmScreenData *pSD, ClientData *pCD,
106                                 CmdTree *tree,
107                                 MatchList **client_match_list,
108                                 MatchList **global_match_list,
109                                 MenuSpec *menuSpec, MenuItem *template,
110                                 String command_so_far,
111                                 Boolean duplicate_globals, Atom selection,
112                                 Context greyed_context, Boolean inLine);
113 static MenuSpec *MakeMenuSpec (String menuName, CARD32 commandID);
114 static void UnmapPulldownCallback (Widget w, XtPointer client_data,
115                                    XtPointer call_data);
116 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
117
118
119 \f
120 /*************************************<->*************************************
121  *
122  *  MakeMenu (menuName, initialContext, accelContext, moreMenuItems,
123  *            fSystemMenu)
124  *
125  *
126  *  Description:
127  *  -----------
128  *  This function makes a menu widget.
129  *
130  *
131  *  Inputs:
132  *  ------
133  *  menuName       = name of the top-level menu pane for the menu
134  *  initialContext = initial context for menuitem sensitivity
135  *  accelContext   = accelerator context
136  *  moreMenuItems  = additional menuitems for custom menu.
137  *  fSystemMenu    = TRUE iff the menu is a client system menu.
138  *
139  * 
140  *  Outputs:
141  *  -------
142  *  Return = pointer to a MenuSpec structure with updated currentContext,
143  *           menuWidget, and menuButtons members.
144  *
145  *
146  *  Comments:
147  *  --------
148  *  If moreMenuItems is nonNULL, a custom MenuSpec will be created, with
149  *  menuItem member pointing to moreMenuItems.  The menuItems for the
150  *  standard MenuSpec of the same name and the moreMenuItems list will be 
151  *  used to create menubuttons, and the menu widget will be separate from 
152  *  any existing standard menu widget.
153  *
154  *  When the client is destroyed, this custom MenuSpec, its menuItem and
155  *  menuButton lists, and its menu widget should be freed.
156  * 
157  *************************************<->***********************************/
158 MenuSpec *MakeMenu (WmScreenData *pSD,
159 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
160                     ClientData *pCD,
161 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
162                     String menuName, Context initialContext,
163                     Context accelContext, MenuItem *moreMenuItems,
164                     Boolean fSystemMenu)
165 {
166     unsigned int n;
167     MenuSpec     *menuSpec;
168     MenuSpec     *newMenuSpec;
169     MenuItem     *menuItem;
170     KeySpec      *accelKeySpec;
171     
172     if ((menuName == NULL) || (pSD == NULL))
173     {
174         return (NULL);
175     }
176
177     /*
178      * Look for the menu specification:
179      */
180
181     menuSpec = pSD->menuSpecs;
182     while (menuSpec)
183     {
184         if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
185         /* Found the menu pane. */
186         {
187             break;
188         }
189         menuSpec = menuSpec->nextMenuSpec;
190     }
191     
192     if (menuSpec == NULL)
193     /* the menuSpecs list is exhausted */
194     {
195         MWarning(((char *)GETMESSAGE(48, 1, "Menu specification %s not found\n")), menuName);
196         return (NULL);
197     }
198
199     /*
200      * The top-level menu pane specification was found.
201      * Adjust the menu accelerator context?
202      */
203
204     if (fSystemMenu)
205     {
206         accelContext = 0;
207     }
208     else if (accelContext & F_CONTEXT_ROOT)
209     /* root context accelerators apply everywhere */
210     {
211         accelContext = F_CONTEXT_ALL;
212     }
213
214     /*
215      * If making a custom menu, create a custom copy of the specification with 
216      *   which to build the custom menu.
217      * Otherwise, if the menu widget exists, possibly modify the accelerator
218      *   contexts and return the specification.
219      */
220
221     if (moreMenuItems != NULL)
222     {
223 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
224         if ((newMenuSpec = DuplicateMenuSpec(menuSpec)) == (MenuSpec *)NULL)
225             return NULL;
226 #else
227         if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
228         /* Handle insufficent memory */
229         {
230             MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
231             return (NULL);
232         }
233         newMenuSpec->name = NULL;  /* distinguishes this as custom */
234         newMenuSpec->whichButton = SELECT_BUTTON;
235         newMenuSpec->height = 0;
236         newMenuSpec->menuItems = menuSpec->menuItems; /* temporary */
237         newMenuSpec->accelContext = menuSpec->accelContext;
238         newMenuSpec->accelKeySpecs = NULL;
239         newMenuSpec->nextMenuSpec = NULL;
240 #endif
241
242         menuSpec = newMenuSpec;
243     }
244     else if (menuSpec->menuWidget)
245     {
246         /* 
247          * OR the accelContext into the accelerators, if necessary.
248          */
249         if (accelContext != (menuSpec->accelContext & accelContext))
250         {
251             menuSpec->accelContext |= accelContext;
252             accelKeySpec = menuSpec->accelKeySpecs;
253             while (accelKeySpec)
254             {
255                 accelKeySpec->context |= accelContext;
256                 accelKeySpec = accelKeySpec->nextKeySpec;
257             }
258         }
259         return (menuSpec);
260     }
261
262     /*
263      * We have a menu specification with which to build the menu.
264      * Set the initial and accelerator contexts -- they are needed within 
265      *   CreateMenuWidget.
266      */
267
268     menuSpec->currentContext = initialContext;
269     menuSpec->accelContext = accelContext;
270
271     /*
272      * Scan the toplevel MenuSpec and create its initial menuButtons array
273      * if any of its items will need to be included.  This array will be
274      * created or enlarged within CreateMenuWidget below if necessary.
275      */
276
277     n = 0;
278     menuItem = menuSpec->menuItems;
279     while (menuItem)
280     {
281         if ((menuItem->greyedContext) || (menuItem->mgtMask))
282         {
283             n++;
284         }
285         menuItem = menuItem->nextMenuItem;
286     }
287     menuItem = moreMenuItems;
288     while (menuItem)
289     {
290         if ((menuItem->greyedContext) || (menuItem->mgtMask))
291         {
292             n++;
293         }
294         menuItem = menuItem->nextMenuItem;
295     }
296     if (n)
297     {
298         if ((menuSpec->menuButtons =
299                (MenuButton *) XtMalloc (n * sizeof(MenuButton))) == NULL)
300         /* insufficent memory */
301         {
302             MWarning(((char *)GETMESSAGE(48, 3, "Insufficient memory for menu %s\n")), menuName);
303             return (NULL);
304         }
305         menuSpec->menuButtonSize = n;
306     }
307     else
308     {
309         menuSpec->menuButtons = NULL;
310         menuSpec->menuButtonSize = 0;
311     }
312     menuSpec->menuButtonCount = 0;
313
314     /*
315      * Create a PopupShell widget as a child of the workspace manager widget
316      *   and a PopupMenu as a child of the shell.
317      * Fill the PopupMenu with the menu items.
318      */
319
320     menuSpec->menuWidget = CREATE_MENU_WIDGET (pSD, pCD, menuName, 
321                                              pSD->screenTopLevelW,
322                                              TRUE, menuSpec, moreMenuItems);
323     if (menuSpec->menuWidget == NULL)
324     {
325         /*
326          *  Could not make the top-level menu pane.
327          */
328         return (NULL);
329     }
330 /*
331     _XmSetPopupMenuClick(menuSpec->menuWidget, False); 
332 */
333     /* Return the top MenuSpec */
334
335     return (menuSpec);
336
337 } /* END OF FUNCTION MakeMenu */
338
339
340 \f
341 /*************************************<->***********************************/
342 void CheckTerminalSeparator(menuSpec, buttonWidget, manage)
343      MenuSpec *menuSpec;
344      Widget buttonWidget;
345      Boolean manage;
346 {
347     CompositeWidget cw;
348     WidgetList      children;
349     Cardinal        wPos;
350
351
352     if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
353       {
354          return;
355       }
356
357     cw = (CompositeWidget)menuSpec->menuWidget;
358     children = cw->composite.children;
359
360     for (wPos = 0;  wPos < cw->composite.num_children; wPos++)
361     {
362         if((Widget)children[wPos] == buttonWidget)
363         {
364             break;
365         }
366     }
367     
368     
369     if(wPos > 0 &&
370        XtClass((Widget) children[wPos -1]) == xmSeparatorGadgetClass)
371     {
372         if(manage)
373         {
374             if (!(XtIsManaged((Widget)children[wPos -1])))
375             {
376                 XtManageChild((Widget)children[wPos -1]);
377             }
378         }
379         else
380         {
381             if (XtIsManaged((Widget)children[wPos -1]))
382             {
383                 XtUnmanageChild((Widget)children[wPos -1]);
384             }
385         }
386     }
387
388 } /* END OF FUNCTION CheckTerminalSeparator */
389
390
391 \f
392 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
393 /*************************************<->*************************************
394  *
395  *  MakeMenuSpec (menuName, commandID)
396  *  
397  *
398  *
399  *  Description:
400  *  -----------
401  *  This function creates and returns a MenuSpec structure.
402  *
403  *
404  *  Inputs:
405  *  ------
406  *  menuName       = name of the menu specification
407  *  commandID      = client command id of the menu item to build.
408  *                   0 if not for a client command.
409  *
410  * 
411  *  Outputs:
412  *  -------
413  *  Return = pointer to a MenuSpec structure with zero'ed fields.
414  *
415  *
416  *  Comments:
417  *  --------
418  *  A new MenuSpec structure is allocated. The name is set to the
419  *  menuName argument. The menuItems list, menuButtons list and 
420  *  accelerator related fields are zero'ed out to NULL values. 
421  * 
422  *************************************<->***********************************/
423 static MenuSpec *
424 MakeMenuSpec (String menuName, CARD32 commandID)
425 {
426     MenuSpec *menuSpec;
427     
428     if ((menuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
429       /* Handle insufficent memory */
430     {
431         MWarning(((char *)GETMESSAGE(48, 2,
432                  "Insufficient memory for menu %s\n")), menuName);
433         return (NULL);
434     }
435     
436     menuSpec->name = XtNewString(menuName);
437     menuSpec->currentContext = F_CONTEXT_ALL;
438     menuSpec->menuWidget = (Widget) NULL;
439     menuSpec->whichButton = SELECT_BUTTON;
440     menuSpec->height = 0;
441     menuSpec->menuItems = (MenuItem *) NULL;
442     menuSpec->menuButtons = (MenuButton *) NULL;
443     menuSpec->menuButtonSize = 0;
444     menuSpec->menuButtonCount = 0;
445     menuSpec->accelContext = F_CONTEXT_ALL;
446     menuSpec->accelKeySpecs = (KeySpec *) NULL;
447     menuSpec->exclusions = (MenuExclusion *) NULL;
448     menuSpec->clientLocal = FALSE;
449     menuSpec->commandID = commandID;
450     menuSpec->nextMenuSpec = (MenuSpec *) NULL;
451     
452     return(menuSpec);
453 }
454 #endif
455 \f
456 /*************************************<->*************************************
457  *
458  *  DuplicateMenuItems (menuItems)
459  *  
460  *
461  *
462  *  Description:
463  *  -----------
464  *  This function creates an indentical duplicate of the given menuItems
465  *  list.
466  *
467  *
468  *  Inputs:
469  *  ------
470  *  menuItems = the linked list of menuItems to duplicate
471  *
472  * 
473  *  Outputs:
474  *  -------
475  *  Return = pointer to a new MenuItems list, identical to the original
476  *
477  *
478  *  Comments:
479  *  --------
480  * 
481  *************************************<->***********************************/
482 static MenuItem *
483 DuplicateMenuItems (MenuItem *menuItems)
484 {
485     MenuItem *newMenuItem = (MenuItem *) NULL, *returnMenuItem, *curMenuItem;
486     
487     for (curMenuItem = menuItems;
488          curMenuItem != (MenuItem *) NULL;
489          curMenuItem = curMenuItem->nextMenuItem)
490     {
491         /* If its the first one ... */
492         if (newMenuItem == (MenuItem *) NULL)
493         {
494             newMenuItem = (MenuItem *)XtMalloc(sizeof(MenuItem));
495             returnMenuItem = newMenuItem;
496         }
497         else /* ... otherwise, get the next menuItem. */
498         {
499             newMenuItem->nextMenuItem =
500               (MenuItem *)XtMalloc(sizeof(MenuItem));
501             newMenuItem = newMenuItem->nextMenuItem;
502         }
503         
504         newMenuItem->labelType = curMenuItem->labelType;
505         if (curMenuItem->label != (String) NULL)
506           newMenuItem->label = XtNewString(curMenuItem->label);
507         else
508           newMenuItem->label = NULL;
509         newMenuItem->labelBitmapIndex = curMenuItem->labelBitmapIndex;
510         newMenuItem->mnemonic = curMenuItem->mnemonic;
511         newMenuItem->accelState = curMenuItem->accelState;
512         newMenuItem->accelKeyCode = curMenuItem->accelKeyCode;
513         if (curMenuItem->accelText != (String) NULL)
514           newMenuItem->accelText = XtNewString(curMenuItem->accelText);
515         else
516           newMenuItem->accelText = NULL;
517         newMenuItem->wmFunction = curMenuItem->wmFunction;
518
519         if ((curMenuItem->wmFunction == F_Send_Msg)
520 #ifdef WSM
521             || (curMenuItem->wmFunction == F_Set_Context)
522 # ifdef PANELIST
523             /*
524              * NOTE: For now, in dtwm this function is used only
525              * to copy the FrontPanel menu.  So, we know that
526              * curMenuItem->wmFuncArgs isn't going anywhere,
527              * so it's safe to simply point at it.  If at some
528              * point it becomes possible that curMenuItem->wmFuncArgs
529              * can go away, we'll need to make a (deep) copy of
530              * the WmActionArg.  11/20/96
531              */
532             || (curMenuItem->wmFunction == F_Action)
533 # endif /* PANELIST */
534 #endif /* WSM */
535             )
536           newMenuItem->wmFuncArgs = curMenuItem->wmFuncArgs;
537         else if (curMenuItem->wmFuncArgs != (String) NULL)
538           newMenuItem->wmFuncArgs = XtNewString(curMenuItem->wmFuncArgs);
539         else
540           newMenuItem->wmFuncArgs = NULL;
541
542         newMenuItem->greyedContext = curMenuItem->greyedContext;
543         newMenuItem->mgtMask = curMenuItem->mgtMask;
544 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
545         newMenuItem->clientCommandName = 
546           XtNewString(curMenuItem->clientCommandName);
547         newMenuItem->clientCommandID = curMenuItem->clientCommandID;
548 #endif
549         newMenuItem->nextMenuItem = (MenuItem *) NULL;
550     }
551     
552     return(returnMenuItem);
553 }
554
555 \f
556 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
557 /*************************************<->*************************************
558  *
559  *  DuplicateMenuExclusions (exclusions)
560  *  
561  *
562  *
563  *  Description:
564  *  -----------
565  *  This function creates an indentical duplicate of the given menu exclusions
566  *  list.
567  *
568  *
569  *  Inputs:
570  *  ------
571  *  exclusions = the linked list of menu exclusions to duplicate
572  *
573  * 
574  *  Outputs:
575  *  -------
576  *  Return = pointer to a new MenuExclusion list, identical to the original
577  *
578  *
579  *  Comments:
580  *  --------
581  * 
582  *************************************<->***********************************/
583 static MenuExclusion *
584 DuplicateMenuExclusions (MenuExclusion *exclusions)
585 {
586     MenuExclusion *newMenuExclusion = (MenuExclusion *) NULL;
587     MenuExclusion *returnMenuExclusion = (MenuExclusion *) NULL;
588     MenuExclusion *curMenuExclusion = (MenuExclusion *) NULL;
589     
590     for (curMenuExclusion = exclusions;
591          curMenuExclusion != (MenuExclusion *) NULL;
592          curMenuExclusion = curMenuExclusion->nextExclusion)
593     {
594         /* If its the first one ... */
595         if (newMenuExclusion == (MenuExclusion *) NULL)
596         {
597             newMenuExclusion =
598               (MenuExclusion *)XtMalloc(sizeof(MenuExclusion));
599             returnMenuExclusion = newMenuExclusion;
600         }
601         else /* ... otherwise, get the next menuExclusion. */
602         {
603             newMenuExclusion->nextExclusion =
604               (MenuExclusion *)XtMalloc(sizeof(MenuExclusion));
605             newMenuExclusion = newMenuExclusion->nextExclusion;
606         }
607
608         newMenuExclusion->command_string =
609           XtNewString(curMenuExclusion->command_string);
610     }
611
612     /* Make sure we properly NULL terminate the list. */
613     if (newMenuExclusion != (MenuExclusion *) NULL)
614       newMenuExclusion->nextExclusion = (MenuExclusion *) NULL;
615     
616     return(returnMenuExclusion);
617 }
618 #endif
619 \f
620 /*************************************<->*************************************
621  *
622  *  DuplicateMenuSpec (menuSpec)
623  *  
624  *
625  *
626  *  Description:
627  *  -----------
628  *  This function creates an indentical duplicate of the given menuSpec.
629  *  The menuItems list in the menuSpec is also duplicated. 
630  *
631  *
632  *  Inputs:
633  *  ------
634  *  menuSpec = the menuSpec to duplicate
635  *
636  * 
637  *  Outputs:
638  *  -------
639  *  Return = pointer to a new MenuSpec structure with the same field
640  *           values as the original
641  *
642  *
643  *  Comments:
644  *  --------
645  *  A new MenuSpec structure is allocated. Most of he fields of the new
646  *  structure are set to the same values as the passed in menuSpec.
647  *  There are some differences between the two final structures.
648  *  One difference: any fields related to push buttons and other
649  *  widgets are left blank in the new MenuSpec to be filled in later.
650  * 
651  *************************************<->***********************************/
652 MenuSpec *
653 DuplicateMenuSpec (MenuSpec *menuSpec)
654 {
655     MenuSpec *newMenuSpec;
656     
657     if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
658       /* Handle insufficent memory */
659     {
660         Warning((char *)GETMESSAGE(48, 9,
661                  "Insufficient memory for menu specification\n"));
662         return (NULL);
663     }
664     newMenuSpec->name = XtNewString(menuSpec->name);
665     newMenuSpec->currentContext = menuSpec->currentContext;
666     newMenuSpec->menuWidget = (Widget) NULL;
667     newMenuSpec->whichButton = menuSpec->whichButton;
668     newMenuSpec->height = menuSpec->height;
669     newMenuSpec->menuItems = DuplicateMenuItems(menuSpec->menuItems);
670     newMenuSpec->menuButtons = (MenuButton *) NULL;
671     newMenuSpec->menuButtonSize = 0;
672     newMenuSpec->menuButtonCount = 0;
673     newMenuSpec->accelContext = menuSpec->accelContext;
674     newMenuSpec->accelKeySpecs = (KeySpec *) NULL;
675 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
676     newMenuSpec->exclusions = DuplicateMenuExclusions(menuSpec->exclusions);
677     newMenuSpec->clientLocal = TRUE;
678     newMenuSpec->commandID = menuSpec->commandID;
679 #endif
680     newMenuSpec->nextMenuSpec = (MenuSpec *) NULL;
681     
682     return(newMenuSpec);
683 }
684
685 \f
686 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
687 /*************************************<->*************************************
688  *
689  *  MakeMenuItem (label, wmFunction, funcArgs, mnemonic, accelText)
690  * 
691  *
692  *  Description:
693  *  -----------
694  *  This function creates and returns a MenuItem structure. 
695  *
696  *
697  *  Inputs:
698  *  ------
699  *  label      = the display name of the menu item
700  *  wmFunction = the wm function to invoke
701  *  funcArgs   = the function arguments to pass to the wm function
702  *  mnemonic   = the mnemonic keysym
703  *  accelText  = the accelerator text
704  *
705  *  Outputs:
706  *  -------
707  *  Return = pointer to a new MenuItem structure with fields filled
708  *           in as per passed arguments
709  *
710  *
711  *  Comments:
712  *  --------
713  *  This function is actually used as the underlying mechanism for 
714  *  MenuItem creation by MakeMenuItemFromTemplate and 
715  *  MakeClientCommandMenuItem.
716  *
717  *  Assumptions:
718  *  -----------
719  *  This function assumes that ParseWmAccelerator simply takes a pointer
720  *  to a string and parses the accelerator found in it. If ParseWmAccelerator
721  *  is ever modified to call GetString to get more text from the parse
722  *  stream (as other parse functions do) then this code will break.
723  * 
724  *************************************<->***********************************/
725 static MenuItem *
726 MakeMenuItem (String label, WmFunction wmFunction, String funcArgs,
727               KeySym mnemonic, unsigned int accelState,
728               KeyCode accelKeyCode, String accelText)
729 {
730     MenuItem *menuItem;
731 /*
732     unsigned char *copy_of_accelText;
733 */
734     
735     if ((menuItem = (MenuItem *) XtMalloc (sizeof (MenuItem))) == NULL)
736       /* Handle insufficent memory */
737     {
738         MWarning(((char *)GETMESSAGE(48, 10,
739                   "Insufficient memory for menu item %s\n")), label);
740         return (NULL);
741     }
742     
743     menuItem->labelType = XmSTRING;
744     menuItem->label = XtNewString(label);
745     menuItem->labelBitmapIndex = -1;
746     menuItem->mnemonic = mnemonic;
747     menuItem->clientCommandName = NULL;
748     menuItem->clientCommandID = 0;
749
750 /*     
751     copy_of_accelText = (unsigned char *)XtNewString(accelText);
752     ParseWmAccelerator(&copy_of_accelText, menuItem);
753 */
754
755     menuItem->accelState = accelState;
756     menuItem->accelKeyCode = accelKeyCode;
757     menuItem->accelText = XtNewString(accelText);
758
759     menuItem->wmFunction = wmFunction;
760     menuItem->wmFuncArgs = XtNewString(funcArgs);
761     SetGreyedContextAndMgtMask(menuItem, wmFunction);
762     menuItem->nextMenuItem = (MenuItem *) NULL;
763     
764     return(menuItem);
765 }
766
767 \f
768 /*************************************<->*************************************
769  *
770  *  MakeMenuItemFromTemplate (template, name, funcArgs)
771  * 
772  *
773  *  Description:
774  *  -----------
775  *  This function creates and returns a MenuItem structure. 
776  *
777  *
778  *  Inputs:
779  *  ------
780  *  template   = a template menuItem used to fill in fields of the
781  *               new menu item
782  *  name       = the display name this item should have
783  *  funcArgs   = the function arguments to pass to the wm function
784  *
785  *  Outputs:
786  *  -------
787  *  Return = pointer to a new MenuItem structure with fields filled
788  *           in as per template MenuItem and funcargs
789  *
790  *
791  *  Comments:
792  *  --------
793  *  This function uses the values in the template MenuItem to create
794  *  a new copy of the template with the given funcArgs.
795  * 
796  *************************************<->***********************************/
797
798 static MenuItem *MakeMenuItemFromTemplate (MenuItem *template, String name,
799                                            String funcArgs)
800 {
801     if (template->clientCommandName == (String) NULL)
802       return(MakeMenuItem(name, template->wmFunction, funcArgs,
803                           template->mnemonic, template->accelState,
804                           template->accelKeyCode, template->accelText));
805
806     return(MakeMenuItem(template->clientCommandName, template->wmFunction,
807                         funcArgs, template->mnemonic, template->accelState,
808                         template->accelKeyCode, template->accelText));
809 }
810
811 \f
812 /*************************************<->*************************************
813  *
814  *  MakeClientCommandMenuItem (label, funcArgs)
815  * 
816  *
817  *  Description:
818  *  -----------
819  *  This function creates and returns a MenuItem structure filled as
820  *  appropriate for client command menu items using the given label
821  *  and funcArgs. 
822  *
823  *
824  *  Inputs:
825  *  ------
826  *  label      = the display label for this menu item
827  *  funcArgs   = the function arguments to pass to the wm function
828  *
829  *  Outputs:
830  *  -------
831  *  Return = pointer to a new MenuItem structure with fields filled
832  *           in as per arguments and client command specs
833  *
834  *
835  *  Comments:
836  *  --------
837  *  This function will fill in a new MenuItem structure as appropriate for
838  *  client commands. This function is used when you want to insert a client
839  *  command into a menu without using a MenuItem template constructed from
840  *  mwmrc.
841  * 
842  *************************************<->***********************************/
843
844 static MenuItem *MakeClientCommandMenuItem (String label, String funcArgs)
845 {
846     return(MakeMenuItem(label, F_InvokeCommand, funcArgs,
847                         (KeySym) NULL, (unsigned int)0,
848                         (KeyCode) NULL, (String)NULL));
849 }
850
851 \f
852 /*************************************<->*************************************
853  *
854  *  PerformClientCommandMatch (clientCommand, menuItem, bestMatchSoFar)
855  * 
856  *
857  *  Description:
858  *  -----------
859  *  This function determines whether the menuItemCommand specification 
860  *  matches the clientCommand.
861  *
862  *  Inputs:
863  *  ------
864  *  clientCommand  = the clientCommand that we want to find a specification for
865  *  menuItem       = the menu item we will look in for a specification
866  *  bestMatchSoFar = the menu item we will return if the given menu item is
867  *                   not a match or not a better match
868  *
869  *  Outputs:
870  *  -------
871  *  Return = pointer to the given menuItem if it contains a client command
872  *           specification and if that specification matches the given
873  *           clientCommand *and* if the match is a better match than
874  *           the bestMatchSoFar. Otherwise, the bestMatchSoFar is returned,
875  *           which could possibly be NULL.
876  *
877  *  Comments:
878  *  --------
879  *  If the menuItem does match, it also determines whether it is
880  *  a better match than the bestMatchSoFar. If so, the menuItemCommand is
881  *  returned. Otherwise, bestMatchSoFar is returned.
882  *
883  *  Best matching is defined as follows:
884  *      1. A specification with fewer wildcards is considered a better
885  *         match than one with more wildcards.
886  *      2. Given two specifications with the same number of wildcards,
887  *         the specification with its wildcards more towards the right
888  *         than the left is considered a better match.
889  * 
890  *************************************<->***********************************/
891
892 /* @RGC: This is kind of arbitrary, but I can't imagine there being more
893    than a small number of segments in any given command specification. */
894 #define MAXSEGMENTS     100
895
896 static MenuItem *PerformClientCommandMatch (String clientCommand,
897                                      MenuItem *menuItem,
898                                      MenuItem *bestMatchSoFar)
899 {
900     String menuItemCommand, bestMatchStr;
901     int seglength, i;
902     int segments = 0, wildcards = 0, wildcardPositions[MAXSEGMENTS];
903     int bestSegments = 0, bestWildcards = 0;
904     int bestWildcardPositions[MAXSEGMENTS];
905     Boolean foundWildcard = FALSE;
906
907     if (menuItem == (MenuItem *) NULL)
908       return(bestMatchSoFar);
909     menuItemCommand = menuItem->label;
910     
911     /* Skip any modifier characters at the beginning of the
912        menu items client command. */
913     /* @RGC: This is kind of kludgy. We shouldn't have to know
914        the specifics of command parsing here. */
915     if (menuItemCommand[0] == '~')
916       ++menuItemCommand;
917     else if (menuItemCommand[0] == '=' && menuItemCommand[1] == '>')
918       menuItemCommand += 2;
919     else if (menuItemCommand[0] == '=')
920       ++menuItemCommand;
921     else if (menuItemCommand[0] == '-' && menuItemCommand[1] == '>')
922       menuItemCommand += 2;
923     
924     /* If the menu item doesn't even contain a client command spec,
925        then just return the existing best match. */
926     if (*menuItemCommand != '<') return(bestMatchSoFar);
927     
928     /* Run down the clientCommand and the menuItemCommand together,
929        matching along the way. If matching fails at any point, then
930        return the bestMatchSoFar.  */
931     for (segments = 0;
932          *menuItemCommand != '\0' && *clientCommand != '\0';
933          ++segments)
934     {
935         /* Skip past the '<' at the beginning of the next segment and
936            any whitespace. */
937         ++menuItemCommand; ++clientCommand;
938         while (isspace(*menuItemCommand)) ++menuItemCommand;
939         while (isspace(*clientCommand)) ++clientCommand;
940         
941         /* First check whether the current menuItemCommand segment is
942            a wildcard. */
943         if (*menuItemCommand == '*')
944         {
945             /* Since the menuItemCommand segment is a wildcard, skip
946                it and the current segment of the client command since
947                the wildcard has to match at least one segment in
948                the client command. */
949             wildcardPositions[wildcards++] = segments;
950             ++menuItemCommand;
951             while (isspace(*menuItemCommand)) ++menuItemCommand;
952             while (*clientCommand != '>' && *clientCommand != '\0')
953               ++clientCommand;
954             foundWildcard = TRUE;
955         }
956         else
957         {
958             /* Calculate how long the current segment of the
959                menuItemCommand is */
960             for (seglength = 0;
961                  menuItemCommand[seglength] != '>' &&
962                  menuItemCommand[seglength] != '\0';
963                  ++seglength)
964               /*EMPTY*/;
965             
966             /* If we are pointing at '\0', then this isn't a match */
967             if (menuItemCommand[seglength] == '\0') return(bestMatchSoFar);
968             
969             /* Get rid of trailing white space on the segment. */
970             for (; seglength > 0; --seglength)
971             {
972                 if (!isspace(menuItemCommand[seglength - 1]))
973                   break;
974             }
975             
976             /* Now string compare this segment with the clientCommand
977                segment, up to the number of characters in the menu
978                item segment. */
979             if (strncmp(menuItemCommand, clientCommand, seglength) == 0)
980             {
981                 /* So far so good. Just make sure clientCommand doesn't
982                    have anything but whitespace after its seglength
983                    character. */
984                 clientCommand += seglength;
985                 while (isspace(*clientCommand)) ++clientCommand;
986                 if (*clientCommand != '>') return(bestMatchSoFar);
987                 
988                 /* We have a match. Clear the foundWildcard since we
989                    have sync'ed up and keep trying to match. */
990                 foundWildcard = FALSE;
991                 menuItemCommand += seglength;
992                 while (isspace(*menuItemCommand)) ++menuItemCommand;
993             }
994             else if (foundWildcard == FALSE)
995             {
996                 /* We didn't match and there wasn't wildcard to 
997                    swallow the discrepancy. Therefore, this is not
998                    a match. */
999                 return(bestMatchSoFar);
1000             }
1001         }
1002         
1003         /* We finished the current segments, we should be looking at
1004            a close bracket and a following period or a close bracket and
1005            a following NULL. Skip past the close brackets and optional
1006            period. If we don't see those, then this isn't a match. */
1007         if (menuItemCommand[0] == '>' && menuItemCommand[1] == '\0' &&
1008             clientCommand[0] == '>'   && clientCommand[1] == '\0')
1009         {
1010             ++menuItemCommand; ++clientCommand;
1011         }
1012         else if (menuItemCommand[0] == '>' && menuItemCommand[1] == '.' &&
1013                  clientCommand[0] == '>'   && clientCommand[1] == '.')
1014         {       
1015             menuItemCommand += 2;
1016             clientCommand += 2;
1017         }
1018         else return(bestMatchSoFar);
1019     }
1020
1021     /* If we terminated the loop because only one of the two commands being
1022        compared was empty, then we don't have a complete match. Return the
1023        best match so far. */
1024     if (*menuItemCommand != '\0' || *clientCommand != '\0')
1025       return(bestMatchSoFar);
1026     
1027     /* So the menuItemCommand must have matched. If the current best
1028        match is NULL, then just return the menuItem. Otherwise calculate some
1029        matching quality metrics for the bestMatchSoFar and compare them
1030        to the menuItemCommand metrics to decide which of the two to
1031        return. */
1032     if (bestMatchSoFar == (MenuItem *) NULL)
1033       return(menuItem);
1034     
1035     bestMatchStr = bestMatchSoFar->label;
1036     
1037     /* Skip any modifier characters at the beginning of the
1038        best match client command. */
1039     /* @RGC: This is kind of kludgy. We shouldn't have to know
1040        the specifics of command parsing here. */
1041     if (bestMatchStr[0] == '~')
1042       ++bestMatchStr;
1043     else if (bestMatchStr[0] == '=' && bestMatchStr[1] == '>')
1044       bestMatchStr += 2;
1045     else if (bestMatchStr[0] == '=')
1046       ++bestMatchStr;
1047     else if (bestMatchStr[0] == '-' && bestMatchStr[1] == '>')
1048       bestMatchStr += 2;
1049     
1050     /* If the best match  doesn't even contain a client command spec,
1051        then just return the new match as the best match. */
1052     if (*bestMatchStr != '<') return(menuItem);
1053     
1054     for (bestSegments = 0;
1055          *bestMatchStr != '\0';
1056          ++bestSegments)
1057     {
1058         /* Skip past the '<' at the beginning of the next segment and
1059            any whitespace. */
1060         ++bestMatchStr;
1061         while (isspace(*bestMatchStr)) ++bestMatchStr;
1062         
1063         /* First check whether the current bestMatchStr segment is
1064            a wildcard. @RGC: We are assuming that there is nothing
1065            but possible whitespace after the *. */
1066         if (*bestMatchStr == '*')
1067           bestWildcardPositions[bestWildcards++] = bestSegments;
1068         while (*bestMatchStr != '>' && *bestMatchStr != '\0')
1069           ++bestMatchStr;
1070         
1071         /* Check for the closing > and .  or close > and NULL. If they
1072            do not both appear then the bestMatch is bad and we should
1073            return the menuItem. */
1074         if (bestMatchStr[0] == '>' && bestMatchStr[1] == '\0')
1075           ++bestMatchStr;
1076         else if (bestMatchStr[0] == '>' && bestMatchStr[1] == '.')
1077           bestMatchStr += 2;
1078         else return(menuItem);
1079     }
1080     
1081     /* Now compare the best match metrics with the menu item metrics to
1082        determine who should be returned. */
1083     if (bestWildcards != wildcards)
1084     {
1085         /* Return the menuItem with the fewest wildcards. */
1086         return(bestWildcards < wildcards ? bestMatchSoFar : menuItem);
1087     }
1088     else
1089     {
1090         /* Find which menu item has the earliest wild card and return
1091            the other. */
1092         for (i = 0; i < wildcards; ++i)
1093           if (wildcardPositions[i] != bestWildcardPositions[i])
1094           {
1095               return(bestWildcardPositions[i] < wildcardPositions[i] ?
1096                      bestMatchSoFar : menuItem);
1097           }
1098         
1099         /* If we got this far, then the two specifications are too
1100            close to call. Return bestMatchSoFar. */
1101         return(bestMatchSoFar);
1102     }
1103 }
1104
1105 \f
1106 /*************************************<->*************************************
1107  *
1108  *  ExcludeClientCommand (menuSpec, clientCommand)
1109  * 
1110  *
1111  *  Description:
1112  *  -----------
1113  *
1114  *  Inputs:
1115  *  ------
1116  *  menuSpec      = the menuSpec whose menuItems we want to search through
1117  *  clientCommand = the clientCommand that we want to find an exclusion for
1118  *
1119  *  Outputs:
1120  *  -------
1121  *  Return = TRUE if the command must be excluded from the menuSpec.
1122  *           FALSE if there is no exclusion preventing the insertion.
1123  *
1124  *  Comments:
1125  *  --------
1126  * 
1127  *************************************<->***********************************/
1128
1129 static Boolean ExcludeClientCommand (MenuSpec *menuSpec, String clientCommand)
1130 {
1131     MenuItem placeholder;
1132     MenuExclusion *curExclusion;
1133
1134     /* Search for an exclusion that would cause this command to be
1135        excluded, if any such exclusion exists. */
1136     for (curExclusion = menuSpec->exclusions;
1137          curExclusion != (MenuExclusion *) NULL;
1138          curExclusion = curExclusion->nextExclusion)
1139     {
1140         /* We don't have menu items for exclusions so just use a bogus
1141            placeholder menu item with the label field set to the string
1142            found in the exclusion. */
1143         placeholder.label = curExclusion->command_string;
1144         
1145         /* If we don't get NULL back, then this exclusion matches. */
1146         if (PerformClientCommandMatch(clientCommand,
1147                                       &placeholder, NULL) != (MenuItem *) NULL)
1148         {
1149             return(TRUE);
1150         }
1151     }
1152     return(FALSE);
1153 }
1154
1155 \f
1156 /*************************************<->*************************************
1157  *
1158  *  ForceInLineToCascade (menuSpec, clientCommand, bestMatch)
1159  * 
1160  *
1161  *  Description:
1162  *  -----------
1163  *
1164  *  Inputs:
1165  *  ------
1166  *  menuSpec      = the menuSpec whose menuItems we want to search through
1167  *  clientCommand = the clientCommand that we want to find an force for
1168  *  bestMatch     = the best matching menu item that was found
1169  *
1170  *  Outputs:
1171  *  -------
1172  *  Return = TRUE if the command set must be cascaded
1173  *           FALSE if there is no forced cascade 
1174  *
1175  *  Comments:
1176  *  --------
1177  * 
1178  *************************************<->***********************************/
1179
1180 static Boolean ForceInLineToCascade (MenuSpec *menuSpec,
1181                                      String clientCommand,
1182                                      MenuItem **bestMatch)
1183 {
1184     /* First find the best match in the menu spec. */
1185     FindClientCommandMatch(menuSpec, clientCommand, bestMatch);
1186
1187     /* If the best match is not NULL, then check whether it forces
1188        the client command to cascade. */
1189     if (*bestMatch != (MenuItem *) NULL)
1190     {
1191         /* If there is a force cascade modifier, then return TRUE. */
1192         if ((strncmp((*bestMatch)->label, "->", 2) == 0) ||
1193             (strncmp((*bestMatch)->label, "=>", 2) == 0))
1194           return(TRUE);
1195     }
1196
1197     /* If the best match is NULL, then return FALSE. We have been
1198        given no indication that the inLine command should be forced
1199        to cascade. */
1200     return(FALSE);
1201 }
1202
1203 \f
1204 /*************************************<->*************************************
1205  *
1206  *  FindClientCommandMatch (menuSpec, clientCommand, menuItem)
1207  * 
1208  *
1209  *  Description:
1210  *  -----------
1211  *  This function searches through the list of menuItems in the given
1212  *  menuSpec, searching for ones which have a client command specification
1213  *  and, for each one that does, whether that specification matches the
1214  *  given client. The best matching menuItem out of all that matched
1215  *  is returned.
1216  *
1217  *  Inputs:
1218  *  ------
1219  *  menuSpec      = the menuSpec whose menuItems we want to search through
1220  *  clientCommand = the clientCommand that we want to find a specification for
1221  *  menuItem      = the best matching menu item
1222  *
1223  *  Outputs:
1224  *  -------
1225  *  Return = TRUE if the command may be inserted into the menuSpec.
1226  *           FALSE if there is an exclusion preventing the insertion.
1227  *           Also return the best matching menu item in the menuItem
1228  *           buffer argument. NULL is returned if no matching MenuItem
1229  *           can be found.
1230  *
1231  *  Comments:
1232  *  --------
1233  *  Best matching is defined as follows:
1234  *      1. A specification with fewer wildcards is considered a better
1235  *         match than one with more wildcards.
1236  *      2. Given two specifications with the same number of wildcards,
1237  *         the specification with its wildcards more towards the right
1238  *         than the left is considered a better match.
1239  * 
1240  *************************************<->***********************************/
1241
1242 static Boolean FindClientCommandMatch (MenuSpec *menuSpec,
1243                                        String clientCommand,
1244                                        MenuItem **menuItem)
1245 {
1246     MenuItem *bestMatch = (MenuItem *) NULL, *curMenuItem, placeholder;
1247     MenuItem *bestExclusionItem = (MenuItem *) NULL;
1248     MenuExclusion *curExclusion;
1249     String    bestExclusionStr = (String) NULL;
1250     
1251     /* First search for a match in the menu items of the menu spec. */
1252     for (curMenuItem = menuSpec->menuItems;
1253          curMenuItem != (MenuItem *) NULL;
1254          curMenuItem = curMenuItem->nextMenuItem)
1255     {
1256         bestMatch =
1257           PerformClientCommandMatch(clientCommand, curMenuItem, bestMatch);
1258     }
1259
1260     /* Now search for the best exclusion that would cause this match to be
1261        excluded, if any such exclusion exists. */
1262     for (curExclusion = menuSpec->exclusions;
1263          curExclusion != (MenuExclusion *) NULL;
1264          curExclusion = curExclusion->nextExclusion)
1265     {
1266         /* We don't have menu items for exclusions so just use a bogus
1267            placeholder menu item with the label field set to the string
1268            found in the exclusion. */
1269         placeholder.label = curExclusion->command_string;
1270
1271         /* Find the best exclusion string in the bunch. */
1272         bestExclusionItem =
1273           PerformClientCommandMatch(clientCommand, &placeholder,
1274                                     bestExclusionItem);
1275
1276         /* Save the best exclusion string since we are going to reuse
1277            the placeholder menu item. */
1278         if (bestExclusionItem != (MenuItem *) NULL)
1279           bestExclusionStr = bestExclusionItem->label;
1280     }
1281
1282     /* Okay, now if we found an exclusion, we need to determine if the
1283        exclusion was a better match than the best match that we found.
1284        If so, the item is *really* exclude. Otherwise, we return the
1285        best match and let the item be included. */
1286     placeholder.label = bestExclusionStr;
1287     if (bestExclusionStr == (String) NULL ||
1288         PerformClientCommandMatch(clientCommand, bestMatch, &placeholder) ==
1289         bestMatch)
1290     {
1291         *menuItem = bestMatch;
1292         return(TRUE);
1293     }
1294     else
1295     {
1296         *menuItem = NULL;
1297         return(FALSE);
1298     }
1299 }
1300
1301
1302 \f
1303 /*************************************<->*************************************
1304  *
1305  *  PerformInsertionsOnMatchList (matchlist)
1306  *
1307  *
1308  *  Description:
1309  *  -----------
1310  *
1311  *  Inputs:
1312  *  ------
1313  * 
1314  *  Outputs:
1315  *  -------
1316  *  Return = 
1317  *
1318  *
1319  *  Comments:
1320  *  --------
1321  * 
1322  *************************************<->***********************************/
1323
1324 static void PerformInsertionsOnMatchList (MatchList **matchlist)
1325 {
1326     MatchList *curmatch;
1327     MenuItem *newMenuItem, *curitem;
1328     
1329     if (*matchlist == (MatchList *) NULL)
1330       return;
1331
1332     if ((*matchlist)->menuspec == (MenuSpec *) NULL)
1333       {
1334         /* should never get here, but if we do, then we can't
1335            continue in this routine since mwm will dump.  This
1336            may be caused by the cci code duplicating a global
1337            menu for a client when it shouldn't be duplicated.
1338            If we skip this routine, the cci command will not
1339            be added which is far less disturbing than a dump. */
1340         return;
1341       }
1342        
1343     
1344     for (curmatch = *matchlist;
1345          curmatch != (MatchList *) NULL;
1346          curmatch = curmatch->next)
1347     {
1348       if (curmatch->menuitem != (MenuItem *) NULL)
1349         {
1350           /* Find this menu item within the menuspec. */
1351           for (curitem = curmatch->menuspec->menuItems;
1352                curitem != curmatch->menuitem &&
1353                curitem != (MenuItem *) NULL;
1354                curitem = curitem->nextMenuItem)
1355             /*EMPTY*/;
1356           
1357           /* If we didn't find the menuitem in the menuspec, then
1358              don't do this match. */
1359           if (curitem == (MenuItem *) NULL) continue;
1360           
1361           newMenuItem =
1362             MakeMenuItemFromTemplate(curmatch->menuitem,
1363                                      curmatch->treenode->defaultName,
1364                                      curmatch->funcargs);
1365           newMenuItem->wmFunction = curmatch->function;
1366           newMenuItem->greyedContext = curmatch->greyed_context;
1367           newMenuItem->nextMenuItem = curitem->nextMenuItem;
1368           newMenuItem->clientCommandID = curmatch->treenode->commandID;
1369           curitem->nextMenuItem = newMenuItem;
1370         }
1371       else
1372         {
1373           MenuItem *last = (MenuItem *) NULL;
1374           
1375           if (curmatch->menuspec != NULL)
1376             {
1377               /* Find the last menu item in the menuspec */
1378               for (last = curmatch->menuspec->menuItems;
1379                    last != (MenuItem *) NULL &&
1380                    last->nextMenuItem != (MenuItem *) NULL;
1381                    last = last->nextMenuItem)
1382                 {
1383                   /* If the next item is f.quit and it is the last
1384                      item, then stop searching now. We don't want
1385                      to insert after a trailing f.kill (i.e. Close). */
1386                   if ((last->nextMenuItem->wmFunction == F_Kill) &&
1387                       (last->nextMenuItem->nextMenuItem == (MenuItem *) NULL))
1388                     break;
1389                 }
1390             }
1391           
1392           /* Create a new client command menu item */
1393           newMenuItem =
1394             MakeClientCommandMenuItem
1395               (XtNewString(curmatch->treenode->defaultName),
1396                XtNewString(curmatch->funcargs));
1397           newMenuItem->wmFunction = curmatch->function;
1398           newMenuItem->greyedContext = curmatch->greyed_context;
1399           newMenuItem->clientCommandID = curmatch->treenode->commandID;
1400           
1401           /* Insert the new menu item at the end of the list */
1402           if (last == (MenuItem *) NULL)
1403             {
1404               newMenuItem->nextMenuItem = (MenuItem *) NULL;
1405               if (curmatch->menuspec != NULL)
1406                 curmatch->menuspec->menuItems = newMenuItem;
1407               else
1408                 {
1409                   /* again, should never get here... */
1410                   return;
1411                 }
1412             }
1413           else
1414             {
1415               newMenuItem->nextMenuItem = last->nextMenuItem;
1416               last->nextMenuItem = newMenuItem;
1417             }
1418         }
1419     }
1420 }
1421
1422 \f
1423 /*************************************<->*************************************
1424  *
1425  *  void
1426  *  DestroyMenuSpecWidgets (menuSpec)
1427  *
1428  *
1429  *  Description:
1430  *  -----------
1431  *
1432  *
1433  *  Inputs:
1434  *  ------
1435  *  menuSpec = pointer to MenuSpec structure
1436  *
1437  * 
1438  *  Outputs:
1439  *  -------
1440  *
1441  *  Comments:
1442  *  --------
1443  *  Destroys all the menuspec widgets so that we can rebuild the menu from
1444  *  scratch.
1445  * 
1446  *************************************<->***********************************/
1447
1448 void DestroyMenuSpecWidgets (MenuSpec *menuSpec)
1449 {
1450     /* check for bad input value - shouldn't happen. */
1451     if (menuSpec == (MenuSpec *) NULL) return;
1452
1453     /* Destroy the menu widget */
1454     if (menuSpec->menuWidget != (Widget) NULL)
1455     {
1456       XtDestroyWidget(XtParent(menuSpec->menuWidget));
1457       menuSpec->menuWidget = (Widget) NULL;
1458     }
1459
1460     /* Destroy the menu buttons array */
1461     if (menuSpec->menuButtonSize != 0)
1462     {  
1463       XtFree((char *)menuSpec->menuButtons);
1464       menuSpec->menuButtons = (MenuButton *) NULL;
1465     }
1466
1467     /* Reset the counters */
1468     menuSpec->menuButtonSize = 0;
1469     menuSpec->menuButtonCount = 0;
1470
1471     /* Clear the flag that says we have processed this menu spec for
1472        widget creation. (We probably don't need to do this after all
1473        since CreateMenuWidgets clears it when done.) */
1474     if (menuSpec->currentContext & CR_MENU_MARK)
1475       menuSpec->currentContext &= ~(CR_MENU_MARK);
1476
1477     return;
1478 }
1479
1480 \f
1481 /*************************************<->*************************************
1482  *
1483  *  void
1484  *  DestroyMenuSpec (pSD, commandID)
1485  *
1486  *
1487  *  Description:
1488  *  -----------
1489  *
1490  *
1491  *  Inputs:
1492  *  ------
1493  *  pSD        = screen data pointer of screen with command to remove
1494  *  commandID  = command id of the menuspec to be removed.
1495  *               if no match is found, then no removal is done.
1496  * 
1497  *  Outputs:
1498  *  -------
1499  *
1500  *  Comments:
1501  *  --------
1502  *  Destroy the specified menuSpec from the list of menuspecs on the
1503  *  specified screen. Note, there may be more than one copy of the
1504  *  spec floating around since duplications may have been done for
1505  *  some clients.
1506  * 
1507  *************************************<->***********************************/
1508
1509 void DestroyMenuSpec (WmScreenData *pSD, CARD32 commandID)
1510 {
1511     MenuSpec *msToKill = NULL, *pMS;
1512     ClientListEntry *curClient;
1513
1514     /* Scan through global menu specs. */
1515     if (pSD != NULL  &&  pSD->menuSpecs != NULL  &&  commandID != 0)
1516       {
1517         /* Scan through the list of menuSpecs and pull the mathing one
1518          * out of the list.
1519          */
1520         if (commandID == pSD->menuSpecs->commandID)
1521           {
1522             /* match at head of menuSpec list. */
1523             msToKill = pSD->menuSpecs;
1524             pSD->menuSpecs = pSD->menuSpecs->nextMenuSpec;
1525             msToKill->nextMenuSpec = NULL;
1526           }
1527         else
1528           {
1529             for (pMS = pSD->menuSpecs;
1530                  (pMS->nextMenuSpec != NULL &&
1531                   pMS->nextMenuSpec->commandID != commandID);
1532                  pMS = pMS->nextMenuSpec)
1533               ;
1534
1535             if (pMS->nextMenuSpec != NULL)
1536               {
1537                 msToKill = pMS->nextMenuSpec;
1538                 pMS->nextMenuSpec = msToKill->nextMenuSpec;
1539                 msToKill->nextMenuSpec = NULL;
1540               }
1541           }
1542
1543         /* found it - now remove the menuSpec. */
1544         if (msToKill != NULL)
1545           FreeCustomMenuSpec(msToKill);
1546       }
1547
1548
1549     /* Check each client's menu spec list.  Stop searching if global. */
1550     for (curClient = pSD->clientList;
1551          curClient != (ClientListEntry *)NULL;
1552          curClient = curClient->nextSibling)
1553       {
1554         /*
1555          * Check the first position.
1556          * If matched, then we're done with this client.
1557          */
1558         if (commandID == pSD->menuSpecs->commandID)
1559           {
1560             msToKill = curClient->pCD->systemMenuSpec;
1561             curClient->pCD->systemMenuSpec = msToKill->nextMenuSpec;
1562             msToKill->nextMenuSpec = NULL;
1563           }
1564
1565         /* Check the rest of the list. */
1566         else
1567           {
1568             for (pMS = curClient->pCD->systemMenuSpec;
1569                  (pMS->nextMenuSpec != (MenuSpec *)NULL)     &&
1570                  (pMS->nextMenuSpec->commandID != commandID) &&
1571                  pMS->clientLocal;
1572                  pMS = pMS->nextMenuSpec)
1573               ;
1574
1575             if ((pMS->nextMenuSpec != (MenuSpec *)NULL)      &&
1576                 (pMS->nextMenuSpec->commandID != commandID))
1577               {
1578                 msToKill = pMS->nextMenuSpec;
1579                 pMS->nextMenuSpec = msToKill->nextMenuSpec;
1580                 msToKill->nextMenuSpec = NULL;
1581               }
1582             else
1583               msToKill = NULL;
1584           }
1585
1586         if (msToKill != NULL)
1587           FreeCustomMenuSpec(msToKill);
1588       }
1589
1590     return;
1591 }
1592
1593 \f
1594 /*************************************<->*************************************
1595  *
1596  *  ReplaceMenuSpecForClient (menuspec, pCD)
1597  *
1598  *
1599  *  Description:
1600  *  -----------
1601  *  Duplicates the given menuspec and replaces the given menuspec if
1602  *  found in the clients menuspec list with the duplicate.
1603  *
1604  *  Inputs:
1605  *  ------
1606  * 
1607  *  Outputs:
1608  *  -------
1609  *  Return = the duplicate menuspec
1610  *
1611  *
1612  *  Comments:
1613  *  --------
1614  * 
1615  *************************************<->***********************************/
1616
1617 static MenuSpec *ReplaceMenuSpecForClient (MenuSpec *menuSpec, ClientData *pCD)
1618 {
1619     MenuSpec *newMenuSpec, *curMenuSpec;
1620
1621     /* Duplicate the menu spec */
1622     newMenuSpec = DuplicateMenuSpec(menuSpec);
1623     
1624     /* Try to find this menuspec in the list of client
1625        menuspecs. If we find it then we want to replace it with
1626        the new one. */
1627     if (pCD->systemMenuSpec == menuSpec)
1628     {
1629         /* It was the head of the list. We need to handle that
1630            a little special */
1631         newMenuSpec->nextMenuSpec = pCD->systemMenuSpec->nextMenuSpec;
1632         pCD->systemMenuSpec = newMenuSpec;
1633     }
1634     else
1635     {
1636         /* Search through the list until we find the menuspec or
1637            the end of the list. */
1638         for (curMenuSpec = pCD->systemMenuSpec;
1639              curMenuSpec->nextMenuSpec != (MenuSpec *) NULL;
1640              curMenuSpec = curMenuSpec->nextMenuSpec)
1641         {
1642             if (curMenuSpec->nextMenuSpec == menuSpec)
1643             {
1644                 newMenuSpec->nextMenuSpec =
1645                   curMenuSpec->nextMenuSpec->nextMenuSpec;
1646                 curMenuSpec->nextMenuSpec = newMenuSpec;
1647                 /* We found it and replaced it. Now get out of
1648                    the loop. */
1649                 break;
1650             }
1651         }
1652         if (curMenuSpec->nextMenuSpec == (MenuSpec *) NULL)
1653         {
1654             /* We didn't find it. Just stick it at the end? We
1655                should have found it. I'm not sure how to handle
1656                this. */
1657             curMenuSpec->nextMenuSpec = newMenuSpec;
1658             newMenuSpec = (MenuSpec *) NULL;
1659         }
1660     }
1661
1662     return(newMenuSpec);
1663 }
1664
1665 \f
1666 /*************************************<->*************************************
1667  *
1668  *  FindLastMenuSpecToModify (menuspec, command_id)
1669  *
1670  *
1671  *  Description:
1672  *  -----------
1673  *
1674  *  Inputs:
1675  *  ------
1676  * 
1677  *  Outputs:
1678  *  -------
1679  *  Return = the last menu spec that would be affected by modifications
1680  *           to the given command id
1681  *
1682  *
1683  *  Comments:
1684  *  --------
1685  * 
1686  *************************************<->***********************************/
1687
1688 static MenuSpec * FindLastMenuSpecToModify(MenuSpec *menuSpec,
1689                                            CARD32 command_id)
1690 {
1691     MenuSpec *curMenuSpec, *lastToModify = (MenuSpec *) NULL;
1692     MenuItem *curItem;
1693
1694     /* Search through all the menu specs in the list starting with
1695        the passed in menuSpec */
1696     for (curMenuSpec = menuSpec;
1697          curMenuSpec != (MenuSpec *) NULL;
1698          curMenuSpec = curMenuSpec->nextMenuSpec)
1699     {
1700         /* Try to find a menu item in this menu spec with the
1701            command_id that will require modification */
1702         for (curItem = curMenuSpec->menuItems;
1703              curItem != (MenuItem *) NULL;
1704              curItem = curItem->nextMenuItem)
1705         {
1706             if (curItem->clientCommandID == command_id)
1707               break;
1708         }
1709
1710         /* If we found a menu item that needs changing, then this
1711            menu spec will need changing. Set the lastToModify to
1712            point to this menu spec. If we find no other menu spec
1713            that needs changing, then this will be the last one
1714            in the list that needs changing. */
1715         if (curItem != (MenuItem *) NULL)
1716           lastToModify = curMenuSpec;
1717     }
1718
1719     /* We've looked through all the menu specs starting with menuSpec
1720        and we've looked at all the menu items in all of those menu
1721        specs. The lastToModify variable should be set to the last
1722        menu spec we saw that needed modification. Return it. */
1723     return(lastToModify);
1724 }
1725
1726 \f
1727 /*************************************<->*************************************
1728  *
1729  *  RecreateMenuWidgets (matchlist)
1730  *
1731  *
1732  *  Description:
1733  *  -----------
1734  *
1735  *  Inputs:
1736  *  ------
1737  * 
1738  *  Outputs:
1739  *  -------
1740  *  Return = 
1741  *
1742  *
1743  *  Comments:
1744  *  --------
1745  * 
1746  *************************************<->***********************************/
1747
1748 static void RecreateMenuWidgets (WmScreenData *pSD, ClientData *pCD,
1749                                  MatchList **matchlist)
1750 {
1751     MatchList *current;
1752     int count = 0, i;
1753     MenuSpec **to_change;
1754
1755     /* First count how many menu specs we need to recreate widgets for */
1756     for (current = *matchlist;
1757          current != (MatchList *) NULL;
1758          current = current->next)
1759       ++count;
1760
1761     /* If there are no affected menuspecs, then just return. */
1762     if (count == 0) return;
1763
1764     /* Allocate an array of menuspec pointers that is the size of the
1765        number of menu specs we need to recreate widgets for */
1766     to_change = (MenuSpec **)XtMalloc(sizeof(MenuSpec *) * count);
1767     for (i = 0; i < count; ++i)
1768       to_change[i] = (MenuSpec *) NULL;
1769
1770     /* Now run through all the matchlist items, storing menuspecs in
1771        that array. If the menuspec is already there, then don't store
1772        it again. */
1773     for (current = *matchlist;
1774          current != (MatchList *) NULL;
1775          current = current->next)
1776     {
1777       for (i = 0; i < count; ++i)
1778       {
1779         if (to_change[i] == current->menuspec) break;
1780         else if (to_change[i] == (MenuSpec *) NULL)
1781         {
1782           to_change[i] = current->menuspec;
1783           break;
1784         }
1785       }
1786     }
1787
1788     /* Run through the array, destroy all existing widgets for each
1789        menuspec */
1790     for (i = 0; i < count && to_change[i] != (MenuSpec *) NULL ; ++i)
1791     {
1792         DestroyMenuSpecWidgets(to_change[i]);
1793     }
1794
1795     /* Run through the array again creating widgets for all the menuspecs */
1796     for (i = 0; i < count && to_change[i] != (MenuSpec *) NULL; ++i)
1797     {
1798         to_change[i]->menuWidget =
1799           CreateMenuWidget (pSD, pCD, to_change[i]->name, pSD->screenTopLevelW,
1800                             TRUE, to_change[i], NULL);
1801     }
1802
1803     /* Free the array. We're done. */
1804     XtFree((char *) to_change);
1805 }
1806
1807 \f
1808 /*************************************<->*************************************
1809  *
1810  *  FreeMatchList (matchlist)
1811  *
1812  *
1813  *  Description:
1814  *  -----------
1815  *
1816  *  Inputs:
1817  *  ------
1818  * 
1819  *  Outputs:
1820  *  -------
1821  *  Return = 
1822  *
1823  *
1824  *  Comments:
1825  *  --------
1826  * 
1827  *************************************<->***********************************/
1828
1829 static void FreeMatchList (MatchList **matchlist)
1830 {
1831     MatchList *current, *next;
1832
1833     current = *matchlist;
1834         
1835     while (current != (MatchList *) NULL)
1836     {
1837         next = current->next;
1838         XtFree(current->command_string);
1839         XtFree(current->funcargs);
1840         XtFree((char *)current);
1841         current = next;
1842     }
1843
1844     *matchlist = (MatchList *) NULL;
1845 }
1846
1847 \f
1848 /*************************************<->*************************************
1849  *
1850  *  StoreMatchedCommand (matchlist, menuSpec, menuItem, command_string,
1851  *                       treenode, function, funcargs)
1852  *
1853  *
1854  *  Description:
1855  *  -----------
1856  *
1857  *  Inputs:
1858  *  ------
1859  * 
1860  *  Outputs:
1861  *  -------
1862  *  Return = 
1863  *
1864  *
1865  *  Comments:
1866  *  --------
1867  *  If the existing match has NULL for the menuitem, then get rid of
1868  *  it and replace with proposed match.
1869  * 
1870  *************************************<->***********************************/
1871
1872 static void StoreMatchedCommand (MatchList **matchlist, MenuSpec *menuSpec,
1873                                  MenuItem *menuItem, String command_string,
1874                                  CmdTree *treenode, WmFunction function,
1875                                  String funcargs, Context greyed_context)
1876 {
1877     MatchList *current, *new;
1878     
1879     /* If this entry does not already exist in the match list, then insert
1880        it. This implies that we first have to perform a search of the list.
1881        The search is very easy. We can simply compare the tuple of
1882        <menuSpec,command_string> with each entry in the matchlist
1883        to see if we already have that tuple stored. We can do straight
1884        pointer value matching for the menuSpec and strcmp for the
1885        command_string */
1886     for (current = *matchlist;
1887          current != (MatchList *) NULL;
1888          current = current->next)
1889     {
1890         if (current->menuspec == menuSpec &&
1891             strcmp(current->command_string, command_string) == 0)
1892         {
1893             /* If the currently stored menu item is NULL,
1894                then replace with the new menuitem and return. */
1895             if (current->menuitem == (MenuItem *) NULL)
1896             {
1897                 current->menuitem = menuItem;
1898                 return;
1899             }
1900             /* Otherwise, we have alreay inserted this
1901                command into this menuspec so don't allow
1902                another insertion. */
1903             else return;
1904         }
1905     }
1906     
1907     /* Well, we didn't find a match, so store the entry */
1908     new = (MatchList *)XtMalloc(sizeof(MatchList));
1909     new->menuspec = menuSpec;
1910     new->menuitem = menuItem;
1911     new->command_string = XtNewString(command_string);
1912     new->treenode = treenode;
1913     new->function = function;
1914     new->funcargs = XtNewString(funcargs);
1915     new->greyed_context = greyed_context;
1916     new->next = (MatchList *) NULL;
1917     
1918     /* Stick it at the head of the list. It's easier. */
1919     new->next = *matchlist;
1920     *matchlist = new;
1921 }
1922
1923 \f
1924 /*************************************<->*************************************
1925  *
1926  *  SearchForOtherMatches (pSD, pCD, treenode,
1927  *                         client_match_list, global_match_list,
1928  *                         menuSpec, command_string, 
1929  *                         function, funcargs, duplicate_globals, selection,
1930  *                         greyed_context)
1931  *
1932  *
1933  *  Description:
1934  *  -----------
1935  *  menuSpec = menu spec to exclude from search
1936  *
1937  *  Inputs:
1938  *  ------
1939  * 
1940  *  Outputs:
1941  *  -------
1942  *  Return = 
1943  *
1944  *
1945  *  Comments:
1946  *  --------
1947  * 
1948  *************************************<->***********************************/
1949
1950 static void SearchForOtherMatches (WmScreenData *pSD, ClientData *pCD,
1951                                    CmdTree *treenode,
1952                                    MatchList **client_match_list,
1953                                    MatchList **global_match_list,
1954                                    MenuSpec *menuSpec, String command_string,
1955                                    WmFunction function, String funcargs,
1956                                    Boolean duplicate_globals, Atom selection,
1957                                    Context greyed_context, Boolean inLine)
1958 {
1959     MenuSpec *current, *newMenuSpec;
1960     MenuItem *match;
1961
1962     /* Search through all of the clients menuspecs first */
1963     for (current = (pCD == NULL ? NULL : pCD->systemMenuSpec);
1964          current != (MenuSpec *) NULL;
1965          current = current->nextMenuSpec)
1966     {
1967         /* If the current menu spec is a global, then just quit
1968            this loop. Any menu specs from this point on will
1969            have a next pointer that is still in the global list. */
1970         if (menuSpec->clientLocal != TRUE)  break;
1971         FindClientCommandMatch(current, command_string, &match);
1972         if (match != (MenuItem *) NULL)
1973         {
1974             if (treenode->subTrees != (CmdTree *) NULL && inLine &&
1975                 (strncmp(match->label, "->", 2) == 0 ||
1976                  strncmp(match->label, "=>", 2) == 0))
1977             {
1978                 CmdTree *tree;
1979                 for (tree = treenode->subTrees;
1980                      tree != (CmdTree *) NULL;
1981                      tree = tree->next)
1982                 {
1983                     char new_command_str[1024];
1984                     char new_funcargs[1024];
1985                     WmFunction inLine_function;
1986
1987                     if (command_string == NULL)
1988                       sprintf(new_command_str, "<%s>", tree->name);
1989                     else
1990                       sprintf(new_command_str, "%s.<%s>", command_string,
1991                               tree->name);
1992                     if (tree->subTrees != (CmdTree *) NULL)
1993                     {
1994                         /* menu to cascade to */
1995                         sprintf(new_funcargs, "<%s>", tree->name);
1996                         inLine_function = F_Menu;
1997                     }
1998                     else
1999                     {
2000                         sprintf(new_funcargs, "%d %ld %ld", tree->commandID,
2001                                 pCD->client, selection);
2002                         inLine_function = F_InvokeCommand;
2003                     }
2004                     StoreMatchedCommand(client_match_list, current, match,
2005                                         new_command_str, tree,
2006                                         inLine_function, new_funcargs,
2007                                         greyed_context);
2008                 }
2009             }
2010             else
2011             {
2012                 StoreMatchedCommand(client_match_list, current, match,
2013                                     command_string, treenode, function,
2014                                     funcargs, greyed_context);
2015             }
2016         }
2017     }
2018
2019     /* Search through all of the global menuspecs also. */
2020     for (current = pSD->menuSpecs;
2021          current != (MenuSpec *) NULL;
2022          current = current->nextMenuSpec)
2023     {
2024         FindClientCommandMatch(current, command_string, &match);
2025         if (match != (MenuItem *) NULL)
2026         {
2027             if (duplicate_globals == TRUE)
2028             {
2029                 /* Create a duplicate of the current menuspec and
2030                    store that away in the client instead of the current
2031                    menuspec. */
2032                 newMenuSpec = ReplaceMenuSpecForClient(current, pCD);
2033
2034                 /* Now store enough information so that we can actually
2035                    create the insertion later. */
2036                 StoreMatchedCommand(client_match_list, newMenuSpec, NULL,
2037                                     command_string, treenode, function,
2038                                     funcargs, greyed_context);
2039             }
2040             else /* Change global menu */
2041             {
2042                 StoreMatchedCommand(global_match_list, current, match,
2043                                     command_string, treenode, function,
2044                                     funcargs, greyed_context);
2045             }
2046         }
2047     }
2048 }
2049
2050 \f
2051 /*************************************<->*************************************
2052  *
2053  *  InsertTreeOnClient (pSD, pCD, tree, client_match_list, global_match_list,
2054  *                      menuSpec, templateMenuItem, command_so_far,
2055  *                      duplicate_globals, selection, greyed_context, inLine)
2056  *
2057  *
2058  *  Description:
2059  *  -----------
2060  *
2061  *  Inputs:
2062  *  ------
2063  * 
2064  *  Outputs:
2065  *  -------
2066  *  Return = 
2067  *
2068  *
2069  *  Comments:
2070  *  --------
2071  *  If duplicate_globals is TRUE, then pCD cannot be NULL.
2072  * 
2073  *************************************<->***********************************/
2074
2075 static void InsertTreeOnClient (WmScreenData *pSD, ClientData *pCD,
2076                                 CmdTree *tree,
2077                                 MatchList **client_match_list,
2078                                 MatchList **global_match_list,
2079                                 MenuSpec *menuSpec, MenuItem *template,
2080                                 String command_so_far,
2081                                 Boolean duplicate_globals, Atom selection,
2082                                 Context greyed_context, Boolean inLine)
2083 {
2084     String new_command_str;
2085     int length;
2086     char funcarg_buf[256];
2087     MenuSpec *newMenuSpec, *last, *dupMenuSpec;
2088     CmdTree *subtree;
2089     MenuItem *bestMatch = (MenuItem *) NULL;
2090
2091     /* If the menuSpec we were given is NULL, then just return. We need to
2092        at least have a starting menuSpec. */
2093     if (menuSpec == (MenuSpec *) NULL)
2094       return;
2095
2096     /* If we want global menus duplicated for a client, then the pCD
2097        had better not be NULL. */
2098     if (duplicate_globals && pCD == (ClientData *) NULL)
2099       return;
2100
2101     while (tree != (CmdTree *) NULL)
2102     {
2103         /* The "4" below is to allow for brackets to surround the
2104            tree->name, the period to separate it from the command
2105            so far and a NULL. */
2106         length = (command_so_far != NULL ? strlen(command_so_far) : 0) + 
2107                  (tree->name != NULL ? strlen(tree->name) : 0) + 4;
2108         new_command_str = XtMalloc(sizeof(unsigned char) * length);
2109         if (command_so_far != (String) NULL)
2110           sprintf(new_command_str, "%s.<%s>", command_so_far, tree->name);
2111         else
2112           sprintf(new_command_str, "<%s>", tree->name);
2113
2114         /* If there is an exclusion preventing this command from being 
2115            inserted, then just continue the loop. @RGC: This is wrong.
2116            We still want to search for other matches if there is an
2117            exclusion. We just don't want to allow one of those other
2118            matches to be this menuSpec. */
2119         if (ExcludeClientCommand(menuSpec, new_command_str))
2120         {
2121             tree = tree->next;
2122             XtFree(new_command_str);
2123             continue;
2124         }
2125
2126         /* If tree is a command set and the inLine flag is TRUE then
2127          * we need to insert the command sets commands in the current
2128          * menu spec instead of creating a cascade.
2129          */
2130         if (tree->subTrees != (CmdTree *) NULL && inLine == TRUE &&
2131             ForceInLineToCascade(menuSpec, new_command_str,
2132                                  &bestMatch) == FALSE)
2133         {
2134             /* Recursively process subtrees */
2135             for (subtree = tree->subTrees;
2136                  subtree != (CmdTree *) NULL;
2137                  subtree = subtree->next)
2138             {
2139                 /* Pass along the bestMatch. If it is a valid menu item
2140                    then we want the insertion to occur at that menuitem
2141                    instead of at the end of the menu. */
2142                 InsertTreeOnClient(pSD, pCD, subtree, client_match_list,
2143                                    global_match_list, menuSpec, bestMatch,
2144                                    new_command_str, duplicate_globals,
2145                                    selection, greyed_context, inLine);
2146             }
2147             /* We don't want to search for other matches because we
2148                want the items to be inserted inLine. Any other matches
2149                will be found in the recursive calls. (??? or am I wrong?) */
2150         }
2151         /* If tree is a command set then we need to create a new
2152            menuSpec. */
2153         else if (tree->subTrees != (CmdTree *) NULL)
2154         {
2155             /* Create the name of the menu for the f.menu command. */
2156             sprintf(funcarg_buf, "<%s>", tree->name);
2157
2158             /* Store the cascade button information so it can be
2159                created later. */
2160             StoreMatchedCommand(
2161                (menuSpec->clientLocal ? client_match_list : global_match_list),
2162                menuSpec, template, new_command_str, tree, F_Menu, funcarg_buf,
2163                greyed_context);
2164
2165             /* We need to create a menu spec for the menu that this cascade
2166                button will cascade to. Try to find one in the clients menu
2167                spec list, stopping the first time we hit a global menu. If we
2168                can't find one there and if we are *not* supposed to duplicate
2169                globals, then try to find it in the global list. In all other
2170                cases, create a new one using the funcarg_buf that we created
2171                above as the name of the menuspec. */
2172             for (newMenuSpec = (pCD == NULL ? NULL : pCD->systemMenuSpec);
2173                  newMenuSpec != (MenuSpec *) NULL;
2174                  newMenuSpec = newMenuSpec->nextMenuSpec)
2175             {
2176                 if (newMenuSpec->clientLocal == FALSE)
2177                 {
2178                     newMenuSpec = (MenuSpec *) NULL;
2179                     break;
2180                 }
2181                 if (strcmp(newMenuSpec->name, funcarg_buf) == 0)
2182                   break;
2183             }
2184
2185             /* If we didn't find it in the client list, maybe we should
2186                look in the global list. */
2187             if (newMenuSpec == (MenuSpec *) NULL && duplicate_globals == FALSE)
2188             {
2189                 for (newMenuSpec = pSD->menuSpecs;
2190                      newMenuSpec != (MenuSpec *) NULL;
2191                      newMenuSpec = newMenuSpec->nextMenuSpec)
2192                 {
2193                     if (strcmp(newMenuSpec->name, funcarg_buf) == 0)
2194                       break;
2195                 }
2196             }
2197
2198             /* If we still don't have a menu spec, then create a new one. */
2199             if (newMenuSpec == (MenuSpec *) NULL)
2200             {
2201                 newMenuSpec = MakeMenuSpec(funcarg_buf,
2202                                            tree == NULL ? (CARD32)NULL
2203                                                         : tree->commandID);
2204                 if (duplicate_globals) newMenuSpec->clientLocal = TRUE;
2205                 else                   newMenuSpec->clientLocal = FALSE;
2206
2207                 /* If we are duplicating globals, then add the new menu spec
2208                    to the client list. Otherwise add it to the global list. */
2209                 if (duplicate_globals)
2210                   last = pCD->systemMenuSpec;
2211                 else
2212                   last = pSD->menuSpecs;
2213
2214                 /* Find the last menu spec in the list. */
2215                 while (last != (MenuSpec *) NULL &&
2216                        last->nextMenuSpec != (MenuSpec *) NULL)
2217                   last = last->nextMenuSpec;
2218
2219                 /* Put the new menu spec at the end of the list. */
2220                 if (last == (MenuSpec *) NULL)
2221                 {
2222                     if (duplicate_globals)
2223                       pCD->systemMenuSpec = newMenuSpec;
2224                     else
2225                       pSD->menuSpecs = newMenuSpec;
2226                 }
2227                 else last->nextMenuSpec = newMenuSpec;
2228             }
2229
2230             /* Recursively process subtrees */
2231             for (subtree = tree->subTrees;
2232                  subtree != (CmdTree *) NULL;
2233                  subtree = subtree->next)
2234             {
2235                 InsertTreeOnClient(pSD, pCD, subtree, client_match_list,
2236                                    global_match_list, newMenuSpec, NULL,
2237                                    new_command_str, duplicate_globals,
2238                                    selection, greyed_context, inLine);
2239             }
2240
2241             /* Search for any other matches in the existing menu specs
2242                for this command, excluding newMenuSpec. */
2243             SearchForOtherMatches(pSD, pCD, tree,
2244                                   client_match_list, global_match_list,
2245                                   newMenuSpec, new_command_str,
2246                                   F_Menu, funcarg_buf,
2247                                   duplicate_globals, selection,
2248                                   greyed_context, inLine);
2249             
2250         }
2251         else /* the tree is a simple command */
2252         {
2253             /* Store away the push button information so it can be 
2254                created later. */
2255             sprintf(funcarg_buf, "%d %ld %ld", tree->commandID, 
2256                     (pCD == NULL ? None : pCD->client), selection);
2257
2258             /* If the menuSpec is global and we are supposed to be
2259                duplicating global menu specs, then create a duplicate
2260                and replace the menuspec with the duplicate for this
2261                client. */
2262             if (duplicate_globals)
2263               dupMenuSpec = ReplaceMenuSpecForClient(menuSpec, pCD);
2264             else
2265               dupMenuSpec = menuSpec;
2266
2267             /* Now store the match away in the appropriate match list */
2268             StoreMatchedCommand((dupMenuSpec->clientLocal ?
2269                                  client_match_list : global_match_list),
2270                                 dupMenuSpec, template, new_command_str, tree,
2271                                 F_InvokeCommand, funcarg_buf, greyed_context);
2272
2273             /* Search for any other matches in the existing menu specs
2274                for this command, excluding newMenuSpec. */
2275             
2276             SearchForOtherMatches(pSD, pCD, tree,
2277                                   client_match_list, global_match_list,
2278                                   dupMenuSpec, new_command_str,
2279                                   F_InvokeCommand, funcarg_buf,
2280                                   FALSE, /* Don't duplicate globals not associated
2281                                             with this pCD. CR 9623 */
2282                                   selection,
2283                                   greyed_context, inLine);
2284         }
2285
2286         /* Move on to the next tree item at this level */
2287         tree = tree->next;
2288         XtFree(new_command_str);
2289     }
2290 }
2291
2292 \f
2293 /*************************************<->*************************************
2294  *
2295  *  InsertTreeOnAllClients (pSD, tree, selection, active_context, inLine)
2296  *
2297  *
2298  *  Description:
2299  *  -----------
2300  *
2301  *  Inputs:
2302  *  ------
2303  *  pSD       = per screen data
2304  *  tree      = command tree
2305  *  selection = owned by inserting client
2306  * 
2307  *  Outputs:
2308  *  -------
2309  *  Return = 
2310  *
2311  *
2312  *  Comments:
2313  *  --------
2314  * 
2315  *************************************<->***********************************/
2316 void InsertTreeOnAllClients (WmScreenData *pSD, CmdTree *tree, Atom selection,
2317                              Context active_context, Boolean inLine)
2318 {
2319     ClientListEntry *current;
2320     MatchList *global_matchlist = (MatchList *) NULL;
2321     MatchList *client_matchlist = (MatchList *) NULL;
2322     Context greyed_context = F_CONTEXT_ALL;
2323
2324     /* If there aren't any clients, then there's nothing to do. */
2325     if (pSD->clientList == (ClientListEntry *) NULL)
2326       return;
2327
2328     /* Setup the greyed context based on the active context */
2329     if (active_context & F_CONTEXT_WINDOW)
2330       greyed_context &= ~(F_CONTEXT_WINDOW);
2331     if (active_context & F_CONTEXT_ICON)
2332       greyed_context &= ~(F_CONTEXT_ICON);
2333
2334     for (current = pSD->clientList;
2335          current != (ClientListEntry *) NULL;
2336          current = current->nextSibling)
2337     {
2338         /* Ignore client list entries for icons. */
2339         if (current->type == MINIMIZED_STATE)
2340           continue;
2341         InsertTreeOnClient(pSD, current->pCD, tree, &client_matchlist,
2342                            &global_matchlist, current->pCD->systemMenuSpec,
2343                            NULL, NULL, FALSE, 
2344                            selection, greyed_context, inLine);
2345         PerformInsertionsOnMatchList(&client_matchlist);
2346         RecreateMenuWidgets(pSD, current->pCD, &client_matchlist);
2347         FreeMatchList(&client_matchlist);
2348     }
2349     PerformInsertionsOnMatchList(&global_matchlist);
2350     RecreateMenuWidgets(pSD, NULL /* no pcd */, &global_matchlist);
2351     FreeMatchList(&global_matchlist);
2352 }
2353
2354 \f
2355 /*************************************<->*************************************
2356  *
2357  *  InsertTreeOnSingleClient (pSD, pCD, tree, selection, inLine)
2358  *
2359  *
2360  *  Description:
2361  *  -----------
2362  *
2363  *  Inputs:
2364  *  ------
2365  *  pSD       = per screen data
2366  *  tree      = command tree
2367  *  selection = owned by inserting client
2368  *
2369  * 
2370  *  Outputs:
2371  *  -------
2372  *  Return = 
2373  *
2374  *
2375  *  Comments:
2376  *  --------
2377  * 
2378  *************************************<->***********************************/
2379 void InsertTreeOnSingleClient (WmScreenData *pSD, ClientData *pCD,
2380                                CmdTree *tree, Atom selection,
2381                                Context active_context, Boolean inLine)
2382 {
2383     MatchList *global_matchlist = (MatchList *) NULL;
2384     MatchList *client_matchlist = (MatchList *) NULL;
2385     Context greyed_context = F_CONTEXT_ALL;
2386
2387     /* A quick sanity check */
2388     if (pCD == (ClientData *) NULL)
2389       return;
2390
2391     /* Setup the greyed context based on the active context */
2392     if (active_context & F_CONTEXT_WINDOW)
2393       greyed_context &= ~(F_CONTEXT_WINDOW);
2394     if (active_context & F_CONTEXT_ICON)
2395       greyed_context &= ~(F_CONTEXT_ICON);
2396
2397     InsertTreeOnClient(pSD, pCD, tree, &client_matchlist,
2398                        &global_matchlist, pCD->systemMenuSpec,
2399                        NULL, NULL, TRUE, selection, greyed_context, inLine);
2400     PerformInsertionsOnMatchList(&client_matchlist);
2401     RecreateMenuWidgets(pSD, pCD, &client_matchlist);
2402     FreeMatchList(&client_matchlist);
2403 }
2404
2405 \f
2406 /*************************************<->*************************************
2407  *
2408  *  InsertTreeOnRootMenu (pSD, tree, selection, active_context, inLine)
2409  *
2410  *
2411  *  Description:
2412  *  -----------
2413  *
2414  *  Inputs:
2415  *  ------
2416  *  pSD       = per screen data
2417  *  tree      = command tree
2418  *  selection = owned by inserting client
2419  *
2420  * 
2421  *  Outputs:
2422  *  -------
2423  *  Return = 
2424  *
2425  *
2426  *  Comments:
2427  *  --------
2428  * 
2429  *************************************<->***********************************/
2430 void InsertTreeOnRootMenu (WmScreenData *pSD, CmdTree *tree, Atom selection,
2431                            Boolean inLine)
2432 {
2433     MatchList *global_matchlist = (MatchList *) NULL;
2434     MatchList *client_matchlist = (MatchList *) NULL;
2435     Context greyed_context = F_CONTEXT_WINDOW | F_CONTEXT_ICON;
2436     MenuSpec *rootMenu;
2437
2438     /* Find the root menu spec */
2439     for (rootMenu = pSD->menuSpecs;
2440          rootMenu != (MenuSpec *) NULL;
2441          rootMenu = rootMenu->nextMenuSpec)
2442     {
2443         if (strcmp(rootMenu->name, pSD->rootMenu) == 0)
2444           break;
2445     }
2446     
2447     /* If we couldn't find the root menu, then do nothing. */
2448     if (rootMenu == (MenuSpec *) NULL) return;
2449     
2450     InsertTreeOnClient(pSD, NULL, tree, &client_matchlist,
2451                        &global_matchlist, rootMenu,
2452                        NULL, NULL, FALSE, selection, greyed_context, inLine);
2453     PerformInsertionsOnMatchList(&client_matchlist);
2454     RecreateMenuWidgets(pSD, NULL, &client_matchlist);
2455     FreeMatchList(&client_matchlist);
2456     PerformInsertionsOnMatchList(&global_matchlist);
2457     RecreateMenuWidgets(pSD, NULL, &global_matchlist);
2458     FreeMatchList(&global_matchlist);
2459 }
2460
2461 \f
2462 /*************************************<->*************************************
2463  *
2464  *  RemoveClientCommandFromMenuSpec (menuSpec, id)
2465  *
2466  *
2467  *  Description:
2468  *  -----------
2469  *
2470  *  Inputs:
2471  *  ------
2472  * 
2473  *  Outputs:
2474  *  -------
2475  *
2476  *  Comments:
2477  *  --------
2478  * 
2479  *************************************<->***********************************/
2480 static Boolean RemoveClientCommandFromMenuSpec (MenuSpec *menuSpec,
2481                                                 CARD32 id)
2482 {
2483     MenuItem *curMenuItem, *prevMenuItem = (MenuItem *) NULL;
2484     MenuItem *tmpMenuItem;
2485     Boolean was_changed = FALSE;
2486
2487     curMenuItem = menuSpec->menuItems;
2488     while (curMenuItem != (MenuItem *) NULL)
2489     {
2490         if (curMenuItem->clientCommandID == id)
2491         {
2492             tmpMenuItem = curMenuItem;
2493             curMenuItem = curMenuItem->nextMenuItem;
2494             if (prevMenuItem == (MenuItem *) NULL)
2495                 menuSpec->menuItems = tmpMenuItem->nextMenuItem;
2496             else
2497                 prevMenuItem->nextMenuItem = tmpMenuItem->nextMenuItem;
2498             FreeMenuItem(tmpMenuItem);
2499             was_changed = TRUE;
2500         }
2501         else
2502         {
2503             prevMenuItem = curMenuItem;
2504             curMenuItem = curMenuItem->nextMenuItem;
2505         }
2506     }
2507     return(was_changed);
2508 }
2509
2510 \f
2511 /*************************************<->*************************************
2512  *
2513  *  ModifyClientCommandForMenuSpec (menuSpec, id, modifier, context, newname)
2514  *
2515  *
2516  *  Description:
2517  *  -----------
2518  *
2519  *  Inputs:
2520  *  ------
2521  * 
2522  *  Outputs:
2523  *  -------
2524  *
2525  *  Comments:
2526  *  --------
2527  * 
2528  *************************************<->***********************************/
2529 static Boolean ModifyClientCommandForMenuSpec (MenuSpec *menuSpec,
2530                                                CARD32 id,
2531                                                CmdModifier modifier,
2532                                                Context context,
2533                                                String newname)
2534 {
2535     MenuItem *curMenuItem;
2536     Boolean was_changed = FALSE;
2537     int i, freebutton, buttoncount;
2538
2539     /* If the menuspec doesn't have any buttons or a valid menu widget
2540        then we don't want to search it. */
2541     if (menuSpec->menuWidget == (Widget) NULL ||
2542         menuSpec->menuButtons == (MenuButton *) NULL ||
2543         menuSpec->menuButtonCount == 0)
2544       return(FALSE);
2545
2546     /* Search through all the menu buttons of the menuspec for buttons
2547        which match the command ID to be removed. */
2548     for (i = 0; i < menuSpec->menuButtonCount; ++i)
2549     {
2550         curMenuItem = menuSpec->menuButtons[i].menuItem;
2551
2552         if ((curMenuItem->clientCommandID == id) &&
2553             (curMenuItem->wmFunction == F_InvokeCommand))
2554         {
2555             switch(modifier)
2556             {
2557               case ENABLE:
2558                 /* "context" is an active context */
2559                 curMenuItem->greyedContext &= ~(context);
2560                 /* Adjust the pushbutton to the state it would have had
2561                    given the last posting context. */
2562                 if (menuSpec->currentContext & curMenuItem->greyedContext)
2563                   XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2564                                  FALSE);
2565                 else
2566                   XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2567                                  TRUE);
2568                 break;
2569               case DISABLE:
2570                 /* "context" is a greyed context */
2571                 curMenuItem->greyedContext |= context;
2572                 /* Adjust the pushbutton to the state it would have had
2573                    given the last posting context. */
2574                 if (menuSpec->currentContext & curMenuItem->greyedContext)
2575                   XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2576                                  FALSE);
2577                 else
2578                   XtSetSensitive(menuSpec->menuButtons[i].buttonWidget,
2579                                  TRUE);
2580                 break;
2581               case RENAME:
2582                 if (newname != NULL && *newname != '\0')
2583                 {
2584                   /* When renaming a command, we shouldn't cause the
2585                    * entire menu to be recreated.  Recreating the menu
2586                    * will cause problems with tearoffs since the menu
2587                    * will disappear when it is destroyed. CR 9719
2588                    */
2589                   XmString labelString;
2590
2591                   /* Change the label of the menu item */
2592                   XtFree(curMenuItem->label);
2593                   /* Give the menu item the new name */
2594                   curMenuItem->label = XtNewString(newname);
2595                   was_changed = False;  /*  all taken care of here. */
2596                   
2597                   /* This is needed when changing the label since
2598                    * mwm will wait for a geometry reply from itself which
2599                    * it can never service. CR 9719
2600                    */
2601                   XtVaSetValues(XtParent(XtParent(menuSpec->menuButtons[i].buttonWidget)),
2602                                 XmNuseAsyncGeometry, TRUE, NULL);
2603
2604                   labelString = XmStringGenerate(curMenuItem->label,
2605                                                  XmFONTLIST_DEFAULT_TAG, 
2606                                                  XmCHARSET_TEXT, NULL);
2607                   XtVaSetValues(menuSpec->menuButtons[i].buttonWidget,
2608                                 XmNlabelString, labelString,
2609                                 NULL);
2610                   XmStringFree(labelString);
2611                 }
2612                 break;
2613               case REMOVE:
2614                 XtDestroyWidget(menuSpec->menuButtons[i].buttonWidget);
2615                 menuSpec->menuButtons[i].managed = FALSE;
2616                 menuSpec->menuButtons[i].menuItem = (MenuItem *) NULL;
2617                 menuSpec->menuButtons[i].buttonWidget = (Widget) NULL;
2618                 break;
2619             }
2620         }
2621     }
2622
2623     /* If we are being asked to remove a client command, then we need
2624      * to search through all the menu items as well as the buttons.
2625      * Do the menu items here.
2626      */
2627     if (modifier == REMOVE)
2628         was_changed = RemoveClientCommandFromMenuSpec(menuSpec, id);
2629
2630     /* Compact the menu buttons array. */
2631     buttoncount = menuSpec->menuButtonCount;
2632     freebutton = 0;
2633     for (i = 0; i < buttoncount; ++i)
2634     {
2635         if (menuSpec->menuButtons[i].buttonWidget == (Widget) NULL)
2636             --menuSpec->menuButtonCount;
2637         else
2638         {
2639             menuSpec->menuButtons[freebutton].menuItem = 
2640               menuSpec->menuButtons[i].menuItem;
2641             menuSpec->menuButtons[freebutton].buttonWidget = 
2642               menuSpec->menuButtons[i].buttonWidget;
2643             menuSpec->menuButtons[freebutton].managed =
2644               menuSpec->menuButtons[i].managed;
2645             ++freebutton;
2646         }
2647     }
2648     return(was_changed);
2649 }
2650
2651 \f
2652 /*************************************<->*************************************
2653  *
2654  *  ModifyClientCommandID (pSD, pCD, range, id, modifier, context, newname)
2655  *
2656  *
2657  *  Description:
2658  *  -----------
2659  *
2660  *  Inputs:
2661  *  ------
2662  * 
2663  *  Outputs:
2664  *  -------
2665  *
2666  *  Comments:
2667  *  --------
2668  * 
2669  *************************************<->***********************************/
2670 static void ModifyClientCommandID (WmScreenData *pSD,
2671                                    ClientData *pCD,
2672                                    OpRange range,
2673                                    CARD32 id,
2674                                    CmdModifier modifier,
2675                                    Context context,
2676                                    String newname)
2677 {
2678     MenuSpec *curMenuSpec;
2679     ClientListEntry *curClient;
2680
2681     switch(range)
2682     {
2683       case ALL:
2684         /* Search through all the menu specs of all the clients. */
2685         for (curClient = pSD->clientList;
2686              curClient != (ClientListEntry *) NULL;
2687              curClient = curClient->nextSibling)
2688         {
2689             for (curMenuSpec = curClient->pCD->systemMenuSpec;
2690                  curMenuSpec != (MenuSpec *) NULL;
2691                  curMenuSpec = curMenuSpec->nextMenuSpec)
2692             {
2693                 /* If the menu spec is global then stop searching
2694                    for this client. */
2695                 if (curMenuSpec->clientLocal == FALSE)
2696                   break;
2697                 if (ModifyClientCommandForMenuSpec(curMenuSpec, id,
2698                                                    modifier, context,
2699                                                    newname) == TRUE)
2700                 {
2701                     DestroyMenuSpecWidgets(curMenuSpec);
2702                     curMenuSpec->menuWidget =
2703                       CreateMenuWidget (pSD, curClient->pCD, curMenuSpec->name,
2704                                         pSD->screenTopLevelW, TRUE,
2705                                         curMenuSpec, NULL);
2706                 }
2707             }
2708         }
2709         /* Search through all the global menu specs. */
2710         for (curMenuSpec = pSD->menuSpecs;
2711              curMenuSpec != (MenuSpec *) NULL;
2712              curMenuSpec = curMenuSpec->nextMenuSpec)
2713         {
2714             if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2715                                                context, newname) == TRUE)
2716             {
2717                 DestroyMenuSpecWidgets(curMenuSpec);
2718                 curMenuSpec->menuWidget =
2719                   CreateMenuWidget (pSD, NULL, curMenuSpec->name,
2720                                     pSD->screenTopLevelW, TRUE,
2721                                     curMenuSpec, NULL);
2722             }
2723         }
2724         break;
2725       case ROOT:
2726         {
2727             /*
2728              * This section was changed to search the entire global menu list.
2729              * This was done to allow access to menu entries included using the
2730              * cci/.mwmrc interface.  Before, only the actual root menu could
2731              * be modified; however, the user could still include commands in
2732              * other menus specified in the .mwmrc file using the f.cci command.
2733              */
2734
2735
2736             MenuSpec *curMenuSpec;
2737
2738             /* Search through all the global menu specs. */
2739             for (curMenuSpec = pSD->menuSpecs;
2740                  curMenuSpec != (MenuSpec *) NULL;
2741                  curMenuSpec = curMenuSpec->nextMenuSpec)
2742             {
2743               if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2744                                                  context, newname) == TRUE)
2745                 {
2746                   DestroyMenuSpecWidgets(curMenuSpec);
2747                   curMenuSpec->menuWidget =
2748                     CreateMenuWidget (pSD, NULL, curMenuSpec->name,
2749                                       pSD->screenTopLevelW, TRUE,
2750                                       curMenuSpec, NULL);
2751                 }
2752             }
2753         }
2754         break;
2755       case SINGLE:
2756         /* If we weren't passed a valid pCD, then just return. */
2757         if (pCD == (ClientData *) NULL) return;
2758
2759         /* Search through the clients menu specs. If we find one that 
2760            is global then stop search if we are ENABLING or DISABLING.
2761            If we are REMOVING and we find a global, we may need to 
2762            perform some menu spec replacing to make the menu spec that
2763            needs modification local to the client. */
2764         for (curMenuSpec = pCD->systemMenuSpec;
2765              curMenuSpec != (MenuSpec *) NULL;
2766              curMenuSpec = curMenuSpec->nextMenuSpec)
2767         {
2768             if (curMenuSpec->clientLocal == FALSE)
2769             {
2770                 MenuSpec *last, *cur;
2771
2772                 /* Find the last global menuspec in the clients list
2773                    that needs to be changed and return it. Replace
2774                    all menuspecs between the current one and the
2775                    "last" one that needs changing. All the replaced
2776                    menuspecs will be marked as local, so that next
2777                    time clientLocal is FALSE in the enclosing for
2778                    loop above, there will be no global menu specs
2779                    needing changes. In other words, all the required
2780                    menu spec replacing will occur the first time we
2781                    find a global menu spec. */
2782                 last = FindLastMenuSpecToModify(curMenuSpec, id);
2783                 if (last != (MenuSpec *) NULL)
2784                 {
2785                     MenuSpec *newMenuSpec = (MenuSpec *) NULL;
2786                     MenuSpec *firstMenuSpec = (MenuSpec *) NULL;
2787                     MenuSpec *lastMenuSpec = (MenuSpec *) NULL;
2788
2789                     /* Replace all the global menu specs with local 
2790                        ones. */
2791                     for (cur = curMenuSpec;
2792                          cur != (MenuSpec *) NULL && cur != last->nextMenuSpec;
2793                          cur = cur->nextMenuSpec)
2794                     {
2795                         newMenuSpec = ReplaceMenuSpecForClient(cur, pCD);
2796                         if (cur == curMenuSpec)
2797                           curMenuSpec = firstMenuSpec = newMenuSpec;
2798                         /* If there is only one menu spec to change,
2799                            the first will also be the last. */
2800                         if (cur == last)
2801                           lastMenuSpec = newMenuSpec;
2802                     }
2803
2804                     /* Now that we have replaced all the menu specs, 
2805                        recreate all the widgets for the new menu specs. */
2806                     for (cur = firstMenuSpec;
2807                          cur != (MenuSpec *) NULL &&
2808                          cur != lastMenuSpec->nextMenuSpec;
2809                          cur = cur->nextMenuSpec)
2810                     {
2811                         DestroyMenuSpecWidgets(newMenuSpec);
2812                         newMenuSpec->menuWidget = 
2813                           CreateMenuWidget(pSD, pCD, newMenuSpec->name,
2814                                            pSD->screenTopLevelW,
2815                                            TRUE, newMenuSpec, NULL);
2816                     }
2817
2818                 }
2819                 /* None of the globals need changing. */
2820                 else break;
2821             }
2822             if (ModifyClientCommandForMenuSpec(curMenuSpec, id, modifier,
2823                                                context, newname) == TRUE)
2824             {
2825                 DestroyMenuSpecWidgets(curMenuSpec);
2826                 curMenuSpec->menuWidget =
2827                   CreateMenuWidget (pSD, pCD, curMenuSpec->name,
2828                                     pSD->screenTopLevelW, TRUE,
2829                                     curMenuSpec, NULL);
2830             }
2831         }
2832         break;
2833     }
2834 }
2835
2836 \f
2837 /*************************************<->*************************************
2838  *
2839  *  ModifyClientCommandTree (pSD, pCD, range, tree, modifier, context, newname)
2840  *
2841  *
2842  *  Description:
2843  *  -----------
2844  *
2845  *  Inputs:
2846  *  ------
2847  * 
2848  *  Outputs:
2849  *  -------
2850  *
2851  *  Comments:
2852  *  --------
2853  * 
2854  *************************************<->***********************************/
2855 void ModifyClientCommandTree (WmScreenData *pSD,
2856                               ClientData *pCD,
2857                               OpRange range,
2858                               CmdTree *tree,
2859                               CmdModifier modifier,
2860                               Context context,
2861                               String newname)
2862 {
2863     CmdTree *curTree;
2864     CARD32 cmdID;
2865
2866     /* Run through the top level of the tree. */
2867     for (curTree = tree; curTree != (CmdTree *) NULL; curTree = curTree->next)
2868     {
2869         cmdID = curTree->commandID;
2870         ModifyClientCommandID(pSD, pCD, range, cmdID, modifier,
2871                               context, newname);
2872         if (curTree->subTrees != (CmdTree *) NULL)
2873           ModifyClientCommandTree(pSD, pCD, range, curTree->subTrees,
2874                                   modifier, context, newname);
2875     }
2876 }
2877 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
2878 \f
2879 /*************************************<->*************************************
2880  *
2881  *  static Boolean
2882  *  AdjustPBs (menuSpec, pCD, newContext)
2883  *
2884  *
2885  *  Description:
2886  *  -----------
2887  *  This procedure adjusts menu PushButton sensitivities and manage/unmanaged
2888  *  status for a toplevel menu.
2889  *
2890  *
2891  *  Inputs:
2892  *  ------
2893  *  menuSpec =    nonNULL toplevel menu specification with gadget
2894  *  pCD =         client data
2895  *  newContext =  context that the menu is to be posted under.
2896  *
2897  * 
2898  *  Outputs:
2899  *  -------
2900  *  menuSpec =      menu specification with modifications
2901  *  Return =        TRUE iff at least one menu item changed manage status.
2902  *
2903  *
2904  *  Comments:
2905  *  --------
2906  *  Adjusts PushButton sensitivity according to context and function type.
2907  *  Manages/Unmanages PushButtons according to clientFunction resource.
2908  * 
2909  *************************************<->***********************************/
2910 static Boolean AdjustPBs (MenuSpec *menuSpec, ClientData  *pCD,
2911                           Context newContext)
2912 {
2913     MenuButton    *menuButton;
2914     MenuItem      *menuItem;
2915     int            msgc;
2916     unsigned int   n;
2917     long          *pMsg;
2918     Boolean        fSupported;
2919     Boolean        fChangeManaged = FALSE;
2920
2921     /*
2922      *  Set PushButton sensitivity.
2923      *  Set f.send_msg button sensitivity according to context and client 
2924      *  message list.  Adjust other button sensitivities only for context.
2925      */
2926
2927     /* check for bad input value - shouldn't happen. */
2928     if (menuSpec == NULL) return (FALSE);
2929
2930     for (n = 0, menuButton = menuSpec->menuButtons;
2931          n < menuSpec->menuButtonCount;
2932          n++, menuButton++)
2933     {
2934         menuItem = menuButton->menuItem;
2935         if (menuItem->wmFunction == F_Send_Msg)
2936         /* f.send_msg button:  set according to context and message. */
2937         {
2938             if ((newContext & menuItem->greyedContext) ||
2939                 !(pCD && pCD->mwmMessagesCount && pCD->mwmMessages))
2940             /* insensitive context or empty client message list */
2941             {
2942                 XtSetSensitive (menuButton->buttonWidget, FALSE);
2943             }
2944             else
2945             /* 
2946              * Have a context sensitive f.send_msg item and a client with a 
2947              * nonempty message list.  Set sensitive only if the message is 
2948              * supported by this client.  Otherwise set insensitive.
2949              */
2950             {
2951                 msgc = pCD->mwmMessagesCount;
2952                 pMsg = pCD->mwmMessages;
2953                 fSupported = FALSE;
2954                 while (msgc--)
2955                 /* scan nonempty message list */
2956                 {
2957                     if (*pMsg == (long) menuItem->wmFuncArgs)
2958                     /* found match */
2959                     {
2960                         fSupported = TRUE;
2961                         break;
2962                     }
2963                     pMsg++;  /* next message in list */
2964                 }
2965                 XtSetSensitive (menuButton->buttonWidget, fSupported);
2966             }
2967         }
2968         else
2969         /*
2970          * Non f.send_msg button:
2971          *  Adjust sensitivity according to context.
2972          *  Manage/Unmanage according to clientFunction.
2973          */
2974         {
2975             if (menuSpec->currentContext & menuItem->greyedContext)
2976             /* button is currently insensitive */
2977             {
2978                 if (!(newContext & menuItem->greyedContext))
2979                 /* insensitive -> sensitive */
2980                 {
2981                     XtSetSensitive (menuButton->buttonWidget, TRUE);
2982                 }
2983             }
2984             else
2985             /* button is currently sensitive */
2986             {
2987                 if (newContext & menuItem->greyedContext)
2988                 /* sensitive -> insensitive */
2989                 {
2990                     XtSetSensitive (menuButton->buttonWidget, FALSE);
2991                 }
2992             }
2993 #ifdef WSM
2994             if (menuItem->wmFunction == F_Remove)
2995             {
2996                 /*
2997                  * Only allow remove from workspace if the client
2998                  * is in more than one workspace
2999                  */
3000                 fSupported = (pCD && (pCD->numInhabited > 1));
3001                 XtSetSensitive (menuButton->buttonWidget, fSupported);
3002             }
3003 #endif /* WSM */
3004
3005             if ((menuItem->mgtMask) && pCD)
3006             /* PushButton might not apply */
3007             {
3008 #ifdef WSM
3009                 if ((pCD->clientFunctions & menuItem->mgtMask & MWM_MGT_MASK) ||
3010                     (pCD->dtwmFunctions & menuItem->mgtMask & DTWM_MGT_MASK))
3011 #else /* WSM */
3012                 if (pCD->clientFunctions & menuItem->mgtMask)
3013 #endif /* WSM */
3014                 /* function applies -- manage it */
3015                 {
3016                     if (!menuButton->managed)
3017                     /* unmanaged -> managed */
3018                     {
3019                         XtManageChild (menuButton->buttonWidget);
3020                         menuButton->managed = TRUE;
3021                         fChangeManaged = TRUE;
3022                         if (n == menuSpec->menuButtonCount - 1)
3023                         {
3024                             /* 
3025                              * last item, if it has a separator before
3026                              * it, manage the separator
3027                              */
3028                             
3029                             CheckTerminalSeparator(menuSpec, 
3030                                                    menuButton->buttonWidget, 
3031                                                    True);
3032                         }
3033                     }
3034                 }
3035                 else
3036                 /* function does not apply -- unmanage it */
3037                 {
3038                     if (menuButton->managed)
3039                     /* managed -> unmanaged */
3040                     {
3041                         XtUnmanageChild (menuButton->buttonWidget);
3042                         menuButton->managed = FALSE;
3043                         fChangeManaged = TRUE;
3044
3045                         if (n == menuSpec->menuButtonCount - 1)
3046                         {
3047                             /* 
3048                              * last item, if it has a separator before
3049                              * it, unmanage the separator
3050                              */
3051                             CheckTerminalSeparator(menuSpec, 
3052                                                    menuButton->buttonWidget, 
3053                                                    False);
3054                         }
3055
3056                     }
3057                 }
3058             }
3059             else if (!menuButton->managed)
3060             /* unmanaged PushButton applies */
3061             {
3062                 XtManageChild (menuButton->buttonWidget);
3063                 menuButton->managed = TRUE;
3064                 fChangeManaged = TRUE;
3065             }
3066         }
3067     }
3068
3069     return (fChangeManaged);
3070
3071 } /* END OF FUNCTION AdjustPBs */
3072
3073
3074 \f
3075 /*************************************<->*************************************
3076  *
3077  *  static Boolean
3078  *  SavePBInfo (topMenuSpec, menuItem, itemW)
3079  *
3080  *
3081  *  Description:
3082  *  -----------
3083  *  Fills a MenuButton structure for a PushButton.
3084  *  If necessary, mallocs or reallocs the menuButtons array in the toplevel
3085  *  MenuSpec.
3086  *
3087  *
3088  *  Inputs:
3089  *  ------
3090  *  topMenuSpec = pointer to toplevel MenuSpec structure
3091  *  menuItem    = pointer to PushButton MenuItem structure
3092  *  itemW       = PushButton gadget
3093  *  topMenuSpec->menuButtons[]
3094  *  topMenuSpec->menuButtonSize
3095  *  topMenuSpec->menuButtonCount
3096  *
3097  * 
3098  *  Outputs:
3099  *  -------
3100  *  Return = FALSE iff insufficient memory for malloc or realloc
3101  *           or bad input value forces exit.
3102  *  topMenuSpec->menuButtons[]
3103  *  topMenuSpec->menuButtonSize
3104  *  topMenuSpec->menuButtonCount
3105  *
3106  *
3107  *  Comments:
3108  *  --------
3109  *  The initial managed status of PushButtons is TRUE.
3110  * 
3111  *************************************<->***********************************/
3112 static Boolean SavePBInfo (MenuSpec *topMenuSpec, MenuItem *menuItem,
3113                              Widget itemW)
3114 {
3115     MenuButton *menuButton;
3116
3117
3118     /* check for bad input value - shouldn't happen. */
3119     if (topMenuSpec == NULL) return (FALSE);
3120
3121     if (topMenuSpec->menuButtonSize == 0)
3122     /* need to create array */
3123     {
3124         topMenuSpec->menuButtonSize = MENU_BUTTON_INC;
3125         topMenuSpec->menuButtons =
3126             (MenuButton *) XtMalloc (MENU_BUTTON_INC * sizeof(MenuButton));
3127     }
3128     else if (topMenuSpec->menuButtonCount == topMenuSpec->menuButtonSize)
3129     /* need larger array */
3130     {
3131         topMenuSpec->menuButtonSize += MENU_BUTTON_INC;
3132         topMenuSpec->menuButtons = (MenuButton *) 
3133             XtRealloc ((char*)topMenuSpec->menuButtons,
3134                      topMenuSpec->menuButtonSize * sizeof(MenuButton));
3135     }
3136
3137     if (topMenuSpec->menuButtons == NULL)
3138     /* insufficent memory */
3139     {
3140         topMenuSpec->menuButtonSize = 0;
3141         topMenuSpec->menuButtonCount = 0;
3142         return (FALSE);
3143     }
3144
3145     menuButton = &(topMenuSpec->menuButtons[topMenuSpec->menuButtonCount]);
3146     topMenuSpec->menuButtonCount++;
3147
3148     menuButton->menuItem = menuItem;
3149     menuButton->buttonWidget = itemW;
3150     menuButton->managed = TRUE;
3151     return (TRUE);
3152
3153 }
3154
3155
3156 \f
3157 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3158 /*************************************<->*************************************
3159  *
3160  * AdjustTearOffControl (cascade, closure, cbackdata) 
3161  *  
3162  * 
3163  *
3164  *
3165  *  Description:
3166  *  -----------
3167  *
3168  *  Inputs:
3169  *  ------
3170  *
3171  * 
3172  *  Outputs:
3173  *  -------
3174  *     returns true iff the tearoff control was enabled or diabled
3175  *     resulting in a change in height.
3176  *
3177  *  Comments:
3178  *  --------
3179  * 
3180  *************************************<->***********************************/
3181 static Boolean
3182 AdjustTearOffControl (Widget cascade,
3183                       XtPointer closure,
3184                       XtPointer cbackdata)
3185 {
3186     Widget submenu = (Widget) closure;
3187     int argn;
3188     Arg args[10];
3189     unsigned char tearoff_model;
3190     Boolean isMwmMenu;
3191
3192     argn = 0;
3193     XtSetArg(args[argn], XmNtearOffModel, &tearoff_model); ++argn;
3194     XtGetValues(submenu, args, argn);
3195
3196     /* Is this a root menu or a cascade of a root menu?         */
3197     /* If cbackdata is not null, then we got here by cascading. */
3198     /* Cascade menus from a tearoff menu-pane are not allowed.  */
3199     /* there is no way to know if the cascade is from a tearoff */
3200     /* or from a cascade on a system menu.                      */
3201     if ((wmGD.menuClient == NULL) && (cbackdata == NULL))
3202       isMwmMenu = True;
3203     else
3204       isMwmMenu = False;
3205
3206     if ((tearoff_model == XmTEAR_OFF_ENABLED) && !isMwmMenu)
3207     {
3208         PRINT("Disabling the tear off\n");
3209         argn = 0;
3210         XtSetArg(args[argn], XmNtearOffModel, XmTEAR_OFF_DISABLED); ++argn;
3211         XtSetValues(submenu, args, argn);
3212
3213         return (True);
3214     }
3215
3216     /* If this was invoked as a cascadingCallback and not by hand and if
3217        the menuActive field of the global data has not yet been set, then
3218        we can safely assume that we have just cascaded off of a torn off
3219        menu. In that case, set the menuActive field to be the menu spec of
3220        the torn off menu and register an unmap callback on the cascaded
3221        menu that will clear the menuActive field. */
3222     if (cbackdata != (XtPointer) NULL && wmGD.menuActive == (MenuSpec *) NULL)
3223     {
3224         MenuSpec *menuspec;
3225         Widget tearoff_widget = XtParent(cascade);
3226
3227         for (menuspec = wmGD.Screens[0].menuSpecs;
3228              menuspec != (MenuSpec *) NULL;
3229              menuspec = menuspec->nextMenuSpec)
3230         {
3231             if (tearoff_widget == menuspec->menuWidget)
3232             {
3233                 wmGD.menuActive = menuspec;
3234                 break;
3235             }
3236         }
3237
3238         /* If we can't find a menuspec for the torn off menu, then just
3239            take the first menu spec in the list of menuSpecs for the
3240            active pSD. NOTE: THIS SHOULD NEVER HAPPEN. In fact if it
3241            does, I'm not sure how mwm will behave having been given
3242            the wrong menu spec as the active menu. */
3243         if (wmGD.menuActive == (MenuSpec *) NULL)
3244         {
3245             wmGD.menuActive = ACTIVE_PSD->menuSpecs;
3246             PRINT("Couldn't find menu spec for tear off\n");
3247         }
3248
3249         /* Add a callback that will clear menuActive when this cascade
3250            is unmapped. */
3251 #if 0
3252         XtAddCallback (submenu, XmNunmapCallback,
3253 #else
3254         XtAddCallback (XtParent(submenu), XmNpopdownCallback,
3255 #endif
3256                        UnmapPulldownCallback,
3257                        (XtPointer) NULL);
3258     }
3259
3260   return (False);
3261 }
3262 \f
3263
3264 /*************************************<->*************************************
3265  *
3266  *  static Boolean
3267  *  CreateClientCommandSeparator (menuItem, child_position, last_child,
3268  *                                newMenuItem)
3269  *
3270  *
3271  *  Description:
3272  *  -----------
3273  *
3274  *  Inputs:
3275  *  ------
3276  *
3277  * 
3278  *  Outputs:
3279  *  -------
3280  *
3281  *  Comments:
3282  *  --------
3283  * 
3284  *************************************<->***********************************/
3285 static Boolean CreateClientCommandSeparator (MenuItem *menuItem,
3286                                              int       child_position,
3287                                              Widget    last_child,
3288                                              MenuItem **newMenuItem)
3289 {
3290     MenuItem *curMenuItem;
3291
3292     /* If it is a client command, then we only want to create the
3293      * separator under particular circumstances. Specifically, we
3294      * want to make sure that:
3295      *   1. a separator doesn't directly precede this one
3296      *   2. a separator doesn't directly follow this one
3297      *   3. this separator won't be the first or last item in the menu
3298      *   4. the client command that this separator surrounds actually
3299      *      matched something and is not an unmatched template
3300      */
3301
3302     /* Check if a separator directly precedes this one. */
3303     if (child_position > 0 && last_child != (Widget) NULL &&
3304         XmIsSeparator(last_child))
3305       return(FALSE);
3306
3307     /* Check if a separator directly follows this one. */
3308     if (menuItem->nextMenuItem != (MenuItem *) NULL &&
3309         menuItem->nextMenuItem->wmFunction == F_Separator &&
3310         IsClientCommand(menuItem->nextMenuItem->label) == FALSE)
3311       return(FALSE);
3312
3313     /* Make sure this separator won't be the first item in the menu. */
3314     if (child_position == 0) return(FALSE);
3315
3316     /* Make sure this separator won't be the last item in the menu. */
3317     if (menuItem->nextMenuItem == (MenuItem *) NULL)
3318       return(FALSE);
3319
3320     /* Make sure that the client command this separator surrounds actually
3321        matches something. We only do this check if the separator is the 
3322        TOP separator in the separator pair. If we are looking at a bottom
3323        separator then we can safely assume something matched, otherwise
3324        we would have passed over it when we look at the corresponding top
3325        separator. */
3326     if (menuItem->labelType == TOP_SEPARATOR)
3327     {
3328         /* If we find a real menu item (not just a template) before we find
3329            a bottom separator, then create the separator. */
3330         for (curMenuItem = menuItem;
3331              curMenuItem != (MenuItem *) NULL;
3332              curMenuItem = curMenuItem->nextMenuItem)
3333         {
3334             /* If we found the closing separator, then return FALSE and
3335                our new menu item position. */
3336             if (curMenuItem->wmFunction == F_Separator &&
3337                 IsClientCommand(curMenuItem->label) &&
3338                 curMenuItem->labelType == BOTTOM_SEPARATOR)
3339             {
3340                 *newMenuItem = curMenuItem;
3341                 return(FALSE);
3342             }
3343             /* If we found a real menu item, then return TRUE. */
3344             if (curMenuItem->wmFunction != F_Separator &&
3345                 !IsClientCommand(curMenuItem->label))
3346             {
3347                 return(TRUE);
3348             }
3349         }
3350         /* If by some bizarre chance we get to the end of the list
3351            without finding either, then return FALSE. Something is wrong. */
3352         if (curMenuItem == (MenuItem *) NULL) return(FALSE);
3353     }
3354
3355     /* Well, nothing failed so let's create it. */
3356     return(TRUE);
3357 }
3358 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3359 \f
3360 /*************************************<->*************************************
3361  *
3362  *  CreateMenuWidget (pSD, menuName, parent, fTopLevelPane, topMenuSpec, 
3363  *                    moreMenuItems)
3364  *
3365  *
3366  *  Description:
3367  *  -----------
3368  *  Creates a MenuShell as a child of the specified parent widget, and a 
3369  *  PopupMenu or PulldownMenu as a child of the shell.  Fill the menu with
3370  *  the named menupane items.
3371  *
3372  *
3373  *  Inputs:
3374  *  ------
3375  *  pSD ---------- pointer to screen data
3376  *  menuName ----- the name of the menu specification to be used to create
3377  *                 the menu widget.
3378  *  parent -------- parent of popup shell
3379  *  fTopLevelPane - TRUE iff the menupane is a top level one
3380  *  topMenuSpec --- pointer to the top menu specification.
3381  *  moreMenuItems - pointer to additional menu items for custom menu.
3382  * 
3383  * 
3384  *  Outputs:
3385  *  -------
3386  *  Return = created PopupMenu or PulldownMenu widget, or NULL.
3387  *
3388  *
3389  *  Comments:
3390  *  --------
3391  *  We attach a popdowncallback to the menu to set wmGD.menuActive to NULL,
3392  *  allowing us to not dispatch key events separately from the toolkit 
3393  *  dispatcher.
3394  * 
3395  *************************************<->***********************************/
3396
3397 typedef struct _StrList
3398 {
3399    XmString         string;
3400    struct _StrList *next;
3401 } StrList;
3402
3403 Widget CreateMenuWidget (WmScreenData *pSD,
3404 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3405                          ClientData *pCD,
3406 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3407                          String menuName, Widget parent,
3408                          Boolean fTopLevelPane, MenuSpec *topMenuSpec,
3409                          MenuItem *moreMenuItems)
3410 {
3411     int         i, n;
3412     Arg         sepArgs[1];
3413     Arg         args[10];
3414     MenuSpec   *menuSpec = (MenuSpec *)NULL;
3415     MenuItem   *menuItem;
3416     Widget      menuShellW;
3417     Widget      menuW;
3418     Widget      subMenuW;
3419     Widget      children[CHILDREN_CACHE];
3420     Pixmap      labelPixmap;
3421     KeySpec    *accelKeySpec;
3422     Dimension   menuHeight;
3423     Boolean     fUseTitleSep = False;
3424 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3425     Boolean     labelIsClientCommand = False;
3426 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3427     StrList    *stringsToFree = NULL, *sPtr;
3428     XmString    tmpstr;
3429 #ifndef IBM_151913
3430     Screen     *scr;
3431 #endif
3432
3433
3434     /* check for bad input values. */
3435     if ((menuName == NULL) || (pSD == NULL))
3436     {
3437         return (NULL);
3438     }
3439
3440     /*
3441      *  Find the menu pane specifications for menuName.
3442      *  The top-level menu specification is passed as an argument (it may
3443      *  be custom).  A submenu specification must be found and might not exist.
3444      *  Return NULL if a submenu specification is not found.
3445      */
3446     if (fTopLevelPane)
3447     {
3448         menuSpec = topMenuSpec;
3449     }
3450     else
3451     {
3452         menuSpec = pSD->menuSpecs;
3453         while (menuSpec)
3454         {
3455             if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
3456             {
3457                 break;  /* found menuName's specification */
3458             }
3459             menuSpec = menuSpec->nextMenuSpec;  /* keep looking */
3460         }
3461     }
3462
3463     if (menuSpec == NULL)
3464     /* (submenu) specification not found */
3465     {
3466         MWarning(((char *)GETMESSAGE(48, 4, "Menu specification %s not found\n")), menuName);
3467         return (NULL);
3468     }
3469
3470     /*
3471      * If menuSpec is marked, we have menu recursion => fail.
3472      *  Otherwise, mark it.
3473      */
3474
3475     if (menuSpec->currentContext & CR_MENU_MARK)   /* marked? */
3476     /* menu recursion */
3477     {
3478         MWarning(((char *)GETMESSAGE(48, 5, "Menu recursion detected for %s\n")), menuName);
3479         return (NULL);
3480     }
3481     menuSpec->currentContext |= CR_MENU_MARK;   /* no, mark it */
3482
3483     /*
3484      * Create a PopupShell widget.
3485      * If the parent of the specified parent ("grandparent") is a MenuShell 
3486      * widget, then use the grandparent as the parent of the PopupShell.
3487      * Otherwise, use the specified parent.
3488      */
3489     i = 0;
3490     XtSetArg (args[i], XmNwidth, (XtArgVal) 5); i++;
3491     XtSetArg (args[i], XmNheight, (XtArgVal) 5); i++;
3492     XtSetArg (args[i], XmNallowShellResize, (XtArgVal) TRUE); i++;
3493     XtSetArg (args[i], XtNoverrideRedirect, (XtArgVal) TRUE); i++;
3494     XtSetArg (args[i], XtNdepth, 
3495               (XtArgVal) DefaultDepth(XtDisplay(parent), pSD->screen)); i++;
3496     XtSetArg (args[i], XtNscreen, 
3497               (XtArgVal) ScreenOfDisplay(XtDisplay(parent), pSD->screen)); i++;
3498
3499     if ((XtParent (parent) != NULL) && XmIsMenuShell (XtParent (parent)))
3500     {
3501         parent = XtParent (parent);
3502     }
3503
3504     menuShellW = XtCreatePopupShell (SHELL_NAME, xmMenuShellWidgetClass,
3505                                      parent, (ArgList) args, i);
3506
3507     /*
3508      * Create a RowColumn widget as a child of the shell for the menu pane.
3509      * If the menu pane is top-level, create a popup menu for it and attach 
3510      *   the unmap callback to it.
3511      * Otherwise, create a pulldown menu for it.
3512      */
3513
3514     i = 0;
3515     XtSetArg (args[i], XmNborderWidth, (XtArgVal) 0); i++;
3516     XtSetArg (args[i], XmNwhichButton, (XtArgVal) SELECT_BUTTON); i++;
3517     XtSetArg (args[i], XmNadjustMargin, (XtArgVal) TRUE); i++;
3518
3519     if (fTopLevelPane)
3520     {
3521         XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_POPUP); i++;
3522         XtSetArg (args[i], XmNpopupEnabled, (XtArgVal) TRUE); i++;
3523         menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
3524                                 (ArgList) args, i);
3525         XtAddCallback (menuW, XmNunmapCallback, UnmapCallback, 
3526                                 (XtPointer) menuSpec);
3527     }
3528     else
3529     {
3530         XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_PULLDOWN); i++;
3531         menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
3532                                 (ArgList) args, i);
3533     }
3534
3535     /*
3536      * Create the specified menu entries as children of the menupane.
3537      * Menus may contain the following widgets:
3538      *
3539      *   Label
3540      *   Separator 
3541      *   CascadeButton
3542      *   PushButton
3543      *
3544      * Add separator gadgets around menu titles.
3545      */
3546
3547     XtSetArg (sepArgs[0], XmNseparatorType, (XtArgVal) XmDOUBLE_LINE);
3548
3549     n = 0;
3550     menuItem = menuSpec->menuItems;
3551     if ((menuItem == NULL) && (moreMenuItems != NULL))
3552     /* handle custom menu with empty standard specification */
3553     {
3554         menuSpec->menuItems = menuItem = moreMenuItems;
3555         moreMenuItems = NULL;
3556     }
3557     while (menuItem)
3558     {
3559         i = 0;
3560
3561 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3562         labelIsClientCommand = IsClientCommand(menuItem->label);
3563 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3564
3565         if (menuItem->wmFunction == F_Separator)
3566         /* 
3567          * Add a Separator gadget for a menu separator.
3568          * An immediately following title will not have a top separator.
3569          */
3570         {
3571 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3572             /* If it is a client command, then we only want to create the
3573              * separator under particular circumstances. Specifically, we
3574              * want to make sure that:
3575              *   1. a separator doesn't directly precede this one
3576              *   2. a separator doesn't directly follow this one
3577              *   3. this separator won't be the first or last item in the menu
3578              */
3579             if (labelIsClientCommand)
3580             {
3581                 if (CreateClientCommandSeparator(menuItem, n,
3582                                                  (n > 0 ? children[n - 1] :
3583                                                   (Widget) NULL),
3584                                                  &menuItem))
3585                 {    
3586                     /* Increment the counter here because we only increment
3587                        at the end of the loop if the item is not a client
3588                        command item (i.e. labelIsClientCommand == FALSE) */
3589                     children[n++] =
3590                       XmCreateSeparatorGadget (menuW, SEPARATOR_NAME, 
3591                                                (ArgList)NULL, 0);
3592                     fUseTitleSep = FALSE;
3593                 }
3594             }
3595             else
3596 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3597             {
3598                 children[n] =
3599                   XmCreateSeparatorGadget (menuW, SEPARATOR_NAME, 
3600                                            (ArgList)NULL, 0);
3601                 fUseTitleSep = FALSE;
3602             }
3603         } /* F_Separator */
3604
3605         else
3606 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3607             if (!labelIsClientCommand)
3608 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3609         /*
3610          * We will use one of:
3611          *
3612          *   Label
3613          *   CascadeButton
3614          *   PushButton
3615          */
3616         {
3617             /*
3618              * Construct the label
3619              */
3620             if ((menuItem->labelType == XmPIXMAP) &&
3621                  (labelPixmap =
3622                       MakeCachedLabelPixmap (pSD, menuW,
3623                                              menuItem->labelBitmapIndex)))
3624             {
3625                 XtSetArg (args[i], XmNlabelType, (XtArgVal) XmPIXMAP); i++;
3626                 XtSetArg (args[i], XmNlabelPixmap, (XtArgVal) labelPixmap); i++;
3627                 XtSetArg (args[i], XmNlabelInsensitivePixmap,
3628                           (XtArgVal) labelPixmap); i++;
3629             }
3630             else
3631             {
3632                 XtSetArg (args[i], XmNlabelType, (XtArgVal) XmSTRING); i++;
3633                 XtSetArg (args[i], XmNlabelString, (XtArgVal)
3634                           (tmpstr = XmStringCreateLocalized(menuItem->label))); i++;
3635                 sPtr = (StrList *) XtMalloc(sizeof(StrList));
3636                 if (sPtr == NULL)
3637                   {
3638                      MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
3639                      return (NULL);
3640                   }
3641                 else
3642                   {
3643                      sPtr->string  = tmpstr;
3644                      sPtr->next    = stringsToFree;
3645                      stringsToFree = sPtr;
3646                   }
3647             }
3648
3649             if (menuItem->wmFunction == F_Title)
3650             /* 
3651              * Add a centered Label gadget for a menu title.
3652              * Include separators above and below the title.
3653              * Don't include the top one if the title is the first pane item
3654              *   or immediately follows a user-supplied separator.
3655              */
3656             {
3657                 if (fUseTitleSep)
3658                 {
3659                     children[n] =
3660                         XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3661                                                  sepArgs, 1); n++;
3662                 }
3663
3664                 XtSetArg (args[i], XmNalignment, XmALIGNMENT_CENTER); i++; 
3665                 children[n] = XmCreateLabelGadget (menuW, TITLE_NAME,
3666                                                    (ArgList) args, i); n++;
3667                 children[n] = XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
3668                                           sepArgs, 1); 
3669
3670                 /*
3671                  * A following title will have both separators.
3672                  */
3673
3674                 fUseTitleSep = TRUE;
3675             }
3676
3677             else
3678             /*
3679              * We will use one of:
3680              *
3681              *   CascadeButton
3682              *   PushButton
3683              *
3684              * Both support mnemonics; only PushButtons support accelerators.
3685              */
3686             {
3687                 /*
3688                  * Align text on the left.
3689                  * Set any mnemonic text.
3690                  */
3691                 XtSetArg (args[i], XmNalignment, XmALIGNMENT_BEGINNING); i++;
3692
3693                 if (menuItem->mnemonic)
3694                 {
3695                     XtSetArg (args[i], XmNmnemonic, 
3696                                (XtArgVal) menuItem->mnemonic); i++;
3697                 }
3698
3699                 if (menuItem->wmFunction == F_Menu)
3700                 /* 
3701                  * Create a PopupShell and PulldownMenu for a submenu (the 
3702                  *   menushells are linked together).
3703                  * Create a CascadeButton Widget 
3704                  * The submenu widget is attached to the CascadeButton gadget
3705                  *   using the subMenuId resource.
3706                  * Make the CascadeButton insensitive if the submenu cannot be 
3707                  *   created.
3708                  */
3709                 {
3710                     subMenuW = CREATE_MENU_WIDGET (pSD, pCD,
3711                                                  menuItem->wmFuncArgs, menuW,
3712                                                  FALSE, topMenuSpec, 
3713                                                  (MenuItem *)NULL);
3714                     if (subMenuW)
3715                     /*
3716                      * Attach submenu to cascade button. 
3717                      */
3718                     {
3719                         XtSetArg (args[i], XmNsubMenuId, (XtArgVal) subMenuW);
3720                             i++;
3721                         children[n] = XmCreateCascadeButtonGadget (menuW,
3722                                           CASCADE_BTN_NAME, (ArgList) args, i);
3723 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3724                         XtAddCallback(children[n], XmNcascadingCallback,
3725                                       (XtCallbackProc)AdjustTearOffControl,
3726                                       (XtPointer)subMenuW);
3727 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3728                     }
3729                     else
3730                     /*
3731                      * Unable to create submenupane: make the entry insensitive.
3732                      */
3733                     {
3734                         children[n] = XmCreateCascadeButtonGadget (menuW,
3735                                           CASCADE_BTN_NAME, (ArgList) args, i);
3736                         XtSetSensitive (children[n], FALSE);
3737                     }
3738
3739                     /*
3740                      * A following title will have both separators.
3741                      */
3742
3743                     fUseTitleSep = TRUE;
3744                 }
3745
3746                 else 
3747                 /* 
3748                  * Create a PushButton gadget.
3749                  */
3750                 {
3751                     /*
3752                      * If an accelerator is specified, set acceleratorText,
3753                      * then create an accelerator KeySpec and insert it at the
3754                      * head of the toplevel MenuSpec's list.
3755                      */
3756                     if (menuItem->accelText)
3757                     {
3758                         XtSetArg (args[i], XmNacceleratorText, (XtArgVal)
3759                                   (tmpstr = XmStringCreateLocalized(menuItem->accelText))); i++;
3760                         sPtr = (StrList *) XtMalloc(sizeof(StrList));
3761                         if (sPtr == NULL)
3762                           {
3763                              MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
3764                              return (NULL);
3765                           }
3766                         else
3767                           {
3768                              sPtr->string = tmpstr;
3769                              sPtr->next   = stringsToFree;
3770                              stringsToFree = sPtr;
3771                           }
3772
3773                         if ((accelKeySpec = (KeySpec *)
3774                                  XtMalloc (sizeof (KeySpec ))) == NULL)
3775                         /* Handle insufficent memory */
3776                         {
3777                             MWarning (((char *)GETMESSAGE(48, 6, "Insufficient memory for menu %s\n")),
3778                                       menuName);
3779                             menuSpec->currentContext &= ~CR_MENU_MARK;
3780                             return (NULL);
3781                         }
3782
3783                         accelKeySpec->state = menuItem->accelState;
3784                         accelKeySpec->keycode = menuItem->accelKeyCode;
3785                         accelKeySpec->context = topMenuSpec->accelContext;
3786                         accelKeySpec->subContext = 0;
3787                         accelKeySpec->wmFunction = menuItem->wmFunction;
3788                         accelKeySpec->wmFuncArgs = menuItem->wmFuncArgs;
3789                         accelKeySpec->nextKeySpec = topMenuSpec->accelKeySpecs;
3790                         topMenuSpec->accelKeySpecs = accelKeySpec;
3791                     }
3792
3793                     children[n] = XmCreatePushButtonGadget (menuW, 
3794                                       PUSH_BTN_NAME, (ArgList) args, i);
3795
3796                     /* 
3797                      * Set sensitivity.  Initially we only consider the context
3798                      * of the top level menupane.
3799                      */
3800
3801                     if (menuItem->greyedContext & topMenuSpec->currentContext)
3802                     /* insensitive button in this context*/
3803                     {
3804                         XtSetSensitive (children[n], FALSE);
3805                     }
3806                     else
3807                     /* sensitive button in this context*/
3808                     {
3809                         XtSetSensitive (children[n], TRUE);
3810                     }
3811
3812                     /*
3813                      * If necessary, fill a menuButtons element for this 
3814                      * PushButton.  Malloc or Realloc the array if necessary.
3815                      */
3816                     if ((menuItem->greyedContext) || (menuItem->mgtMask))
3817                     {
3818                         if (!SavePBInfo (topMenuSpec, menuItem, children[n]))
3819                         {
3820                             MWarning(((char *)GETMESSAGE(48, 7, "Insufficient memory for menu %s\n")),
3821                                        menuName);
3822                             menuSpec->currentContext &= ~CR_MENU_MARK;
3823                             return (NULL);
3824                         }
3825                     }
3826
3827                     /*
3828                      * Set up the function callback.
3829                      * A following title will have both separators.
3830                      */
3831
3832                     XtAddCallback (children[n], XmNactivateCallback,
3833                             (XtCallbackProc)ActivateCallback, 
3834                             (XtPointer) menuItem);
3835
3836                     fUseTitleSep = TRUE;
3837                 }
3838             }
3839         }
3840
3841         /*
3842          * Increment the children array count if we actually
3843          * created a new child.
3844          */
3845 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3846         if (!labelIsClientCommand)
3847 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3848             n++;
3849
3850         /*
3851          * Next menu item:  handle custom items and full children[]. 
3852          */
3853         menuItem = menuItem->nextMenuItem;
3854         if ((menuItem == NULL) && (moreMenuItems != NULL))
3855         {
3856             menuSpec->menuItems = menuItem = moreMenuItems;
3857             moreMenuItems = NULL;
3858         }
3859         if (n >= CHILDREN_CACHE - 2)  /* leave room for title separators */
3860         {
3861             XtManageChildren (children, n);
3862             n = 0;
3863         }
3864     }
3865
3866     if (n > 0)
3867     {
3868         XtManageChildren (children, n);
3869     }
3870
3871     /*
3872      * Get the initial height of the top level menu pane shell.
3873      * The actual height will change according to clientFunctions.
3874      */
3875     if (fTopLevelPane)
3876     {
3877         i = 0;
3878         XtSetArg (args[i], XtNheight, &menuHeight);  i++;
3879         XtGetValues (menuW, (ArgList)args, i);
3880         topMenuSpec->height = (unsigned int) menuHeight;
3881     }
3882
3883 #ifndef IBM_151913
3884     /*
3885      * Check if the menu that's been created is higher than the screen.
3886      * If it is, force it to wrap.  Taken straight from the 1.1 fix.
3887     */
3888
3889     i = 0;
3890     XtSetArg (args[i], XtNheight, &menuHeight);  i++;
3891     XtGetValues (menuW, (ArgList)args, i);
3892     scr = XtScreen (menuW);
3893     if (menuHeight > (Dimension)scr->height) {
3894         i = 0;
3895         XtSetArg (args[i], XmNresizeHeight, (XtArgVal) FALSE); i++;
3896         XtSetArg (args[i], XmNpacking, (XtArgVal) XmPACK_TIGHT); i++;
3897         XtSetArg (args[i], XmNorientation, (XtArgVal) XmVERTICAL); i++;
3898         XtSetArg (args[i], XmNheight, scr->height); i++;
3899         XtSetValues (menuW, (ArgList)args, i);
3900     }
3901 #endif  /* IBM_151913 */
3902
3903     /* free the string that may have been created earlier. */
3904     for (sPtr = stringsToFree; sPtr != NULL; )
3905       {
3906          stringsToFree = stringsToFree->next;
3907          XmStringFree(sPtr->string);
3908          XtFree((char *)sPtr);
3909          sPtr = stringsToFree;
3910       }
3911
3912
3913     /* Unmark the menu specification and return. */
3914     menuSpec->currentContext &= ~CR_MENU_MARK;
3915     return (menuW);
3916
3917 } /* END OF FUNCTION CreateMenuWidget */
3918
3919
3920 \f
3921 /*************************************<->*************************************
3922  *
3923  *  PostMenu (menuSpec, pCD, x, y, button, newContext, flags, passedInEvent)
3924  *
3925  *
3926  *  Description:
3927  *  -----------
3928  *  This function is used to post a menu at a particular location.
3929  *
3930  *
3931  *  Inputs:
3932  *  ------
3933  *  menuSpec =      menu specification
3934  *  pCD =           client data
3935  *  x,y =           position to post the menu if (flags & POST_AT_XY) set
3936  *  button =        button number posting the menu or NoButton (WmGlobal.h) if
3937  *                  posted by a key
3938  *  newContext =    context that the menu is to be posted under.
3939  *  flags =         POST_AT_XY bit set iff x,y are valid, else compute from pCD
3940  *                  POST_TRAVERSAL_ON bit set if set traversal on
3941  * 
3942  *  Outputs:
3943  *  -------
3944  *  menuSpec =        menu specification with modifications
3945  *  wmGD.menuClient = pCD
3946  *  wmGD.menuActive = menuSpec
3947  *
3948  *
3949  *  Comments:
3950  *  --------
3951  *  Accepts x,y only if POST_AT_XY flag bit set.  Otherwise, computes from pCD.
3952  *  Adjusts PushButton sensitivity according to context and function type.
3953  *  Manages/Unmanages PushButtons according to clientFunction resource.
3954  *  Sets traversal on if button==NoButton or POST_TRAVERSAL_ON flag bit set.
3955  * 
3956  *************************************<->***********************************/
3957
3958 void PostMenu (MenuSpec *menuSpec, ClientData *pCD, int x, int y, unsigned int button, Context newContext, long flags, XEvent *passedInEvent)
3959 {
3960     int              i;
3961     Arg              args[3];
3962     unsigned int     whichButton;
3963     Dimension        menuHeight;
3964     XButtonPressedEvent event;
3965     Window           saveWindow;
3966     Display          *saveDisplay;
3967 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
3968     Boolean          menuAdjusted;
3969 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
3970     
3971     if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
3972     {
3973         return;
3974     }
3975
3976
3977     /* 
3978      * Don't post a menu from an icon in the iconbox if the
3979      * icon is not visible
3980      */
3981     if((newContext == F_SUBCONTEXT_IB_WICON ||
3982        newContext == F_SUBCONTEXT_IB_IICON) &&
3983        (!(IconVisible(pCD))))
3984     {
3985         return;
3986     }
3987
3988     /*
3989      * Set grabContext to be used in GrabWin when no event is passed
3990      * to GrabWin. 
3991      */
3992
3993     wmGD.grabContext = newContext;
3994
3995     /*
3996      *  Adjust PushButton sensitivity and manage/unmanage status.
3997      *  If the manage status of the system menu has changed, 
3998      *  then get the height of the top level menu pane shell and
3999      *  cache it in its MenuSpec.
4000      * 
4001      *  Also...
4002      *  Adjust the tear off control. If we are posting this menu from
4003      *  a client then force the tear off to be disabled. NOTE: This must
4004      *  be done after wmGD.menuClient has been set.
4005      *  Since turning off the tear-off control could result in a height
4006      *  change, we may need to remeasure things. (CR 9316)
4007      */
4008     
4009 #ifdef WSM
4010     if(pCD && pCD->clientFlags & ICON_BOX)
4011     {
4012         newContext |= F_CONTEXT_ICONBOX;
4013     }
4014
4015 #endif /* WSM */
4016
4017 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
4018     menuAdjusted =
4019       AdjustTearOffControl(NULL, (XtPointer) (menuSpec->menuWidget), NULL);
4020 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
4021     if (AdjustPBs (menuSpec, pCD, newContext)
4022 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
4023         || menuAdjusted
4024 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
4025         )
4026     {
4027         i = 0;
4028         XtSetArg (args[i], XtNheight, &menuHeight);  i++;
4029         XtGetValues (menuSpec->menuWidget, (ArgList)args, i);
4030         menuSpec->height = (unsigned int) menuHeight;
4031     }
4032     menuSpec->currentContext = newContext;
4033
4034     /*
4035      *  Adjust the whichButton resource if necessary.
4036      *  Use SELECT_BUTTON for NoButton.
4037      */
4038
4039     whichButton = (button == NoButton) ? SELECT_BUTTON : button;
4040     if (whichButton != menuSpec->whichButton)
4041     {
4042         i = 0;
4043         XtSetArg (args[i], XmNwhichButton, (XtArgVal) whichButton); i++;
4044         XtSetValues (menuSpec->menuWidget, args, i);
4045         menuSpec->whichButton = whichButton;
4046     }
4047
4048     /*
4049      *  Determine the position of the popup menu.
4050      *  Compute position if necessary (system menu).
4051      */
4052
4053     if (!(flags & POST_AT_XY))
4054     /* compute the position */
4055     {
4056         GetSystemMenuPosition (pCD, &x, &y, menuSpec->height, newContext);
4057     }
4058
4059     event.x_root = x;
4060     event.y_root = y;
4061     XmMenuPosition (menuSpec->menuWidget, &event);
4062
4063     wmGD.menuClient = pCD;
4064     wmGD.menuActive = menuSpec;   /* set to NULL within UnmapCallback() */
4065
4066     /* 
4067      * Post the menu by managing its top-level RowColumn.
4068      *
4069      * First dispatch the event to set the time stamp in the toolkit
4070      */
4071
4072     if(passedInEvent)
4073     {
4074         saveWindow = passedInEvent->xany.window;
4075         saveDisplay = passedInEvent->xany.display;
4076         passedInEvent->xany.window = 0;
4077         passedInEvent->xany.display = XtDisplay(menuSpec->menuWidget);
4078
4079         XtDispatchEvent(passedInEvent);
4080         passedInEvent->xany.window = saveWindow;
4081         passedInEvent->xany.display = saveDisplay;
4082
4083         /* If menu posted by ButtonPress/ButtonRelease, release grabs. */
4084         if ((passedInEvent->type == ButtonPress) ||
4085             (passedInEvent->type == ButtonRelease))
4086             XUngrabPointer(passedInEvent->xany.display,
4087                            passedInEvent->xbutton.time);
4088     }
4089     
4090 #ifndef ALTERNATE_POSTMENU
4091
4092     XtManageChild (menuSpec->menuWidget);
4093
4094 #else
4095     if (flags & POST_STICKY)
4096     {
4097         _XmSetPopupMenuClick(menuSpec->menuWidget, True);
4098     }
4099     else
4100     {
4101         _XmSetPopupMenuClick(menuSpec->menuWidget, False);
4102     }
4103
4104     /* 
4105      * Post the menu by calling the convenience routine that verifies
4106      * the button event, updates the Xt timestamp, and finally manages
4107      * the pane.
4108      */
4109
4110     _XmPostPopupMenu( menuSpec->menuWidget, passedInEvent);
4111 #endif
4112
4113
4114     /*
4115      *  set the traversal state.
4116      */
4117
4118     if ((button == NoButton) || (flags & POST_TRAVERSAL_ON))
4119     /* turn traversal on */
4120     {
4121         TraversalOn (menuSpec);
4122     }
4123     else
4124     /* turn traversal off */
4125     {
4126         TraversalOff (menuSpec);
4127     }
4128
4129 } /* END OF FUNCTION PostMenu */
4130
4131
4132 \f
4133 /*************************************<->*************************************
4134  *
4135  *  UnpostMenu (menuSpec)
4136  *
4137  *
4138  *  Description:
4139  *  -----------
4140  *  This function is used to unpost a menu.
4141  *
4142  *
4143  *  Inputs:
4144  *  ------
4145  *  menuSpec =      menu specification
4146  * 
4147  *  Outputs:
4148  *  -------
4149  *  None.
4150  *
4151  *
4152  *  Comments:
4153  *  --------
4154  *  wmGD.menuActive and wmGD.menuUnpostKey are set to NULL within 
4155  *  UnmapCallback.
4156  * 
4157  *************************************<->***********************************/
4158
4159 void UnpostMenu (MenuSpec *menuSpec)
4160 {
4161     if (menuSpec && (menuSpec->menuWidget))
4162     /* 
4163      * Unpost the menu by unmanaging its top-level RowColumn.
4164      */
4165     {
4166         XtUnmanageChild (menuSpec->menuWidget);
4167 #ifndef OLD_COLORMAP
4168         ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
4169 #endif
4170     }
4171
4172 } /* END OF FUNCTION UnpostMenu */
4173
4174
4175 \f
4176 /*************************************<->*************************************
4177  *
4178  *  ActivateCallback (w, client_data, call_data)
4179  *
4180  *
4181  *  Description:
4182  *  -----------
4183  *  This function is called whenever a menu item is selected.
4184  *
4185  *
4186  *  Inputs:
4187  *  ------
4188  *  w =               menubuttonWidget
4189  *  client_data =     pointer to menu button's MenuItem structure
4190  *  call_data =       not used
4191  *  wmGD.menuClient = pointer to client's ClientData structure
4192  *
4193  * 
4194  *  Outputs:
4195  *  -------
4196  *  None.
4197  *
4198  *
4199  *  Comments:
4200  *  --------
4201  *  None.
4202  * 
4203  *************************************<->***********************************/
4204
4205 void ActivateCallback (Widget w, caddr_t client_data, caddr_t call_data)
4206 {
4207     WmScreenData *pSD;
4208
4209     /* set active screen */
4210     pSD = GetScreenForWindow (XtWindow(w));
4211     if (pSD) SetActiveScreen (pSD);
4212
4213     ((MenuItem *)client_data)->wmFunction (
4214                 ((MenuItem *)client_data)->wmFuncArgs, wmGD.menuClient, NULL);
4215
4216 } /* END OF FUNCTION ActivateCallback */
4217
4218
4219 \f
4220 /*************************************<->*************************************
4221  *
4222  *  UnmapCallback (w, client_data, call_data)
4223  *
4224  *
4225  *  Description:
4226  *  -----------
4227  *  This function is called whenever a toplevel RowColumn is unmapped.
4228  *
4229  *
4230  *  Inputs:
4231  *  ------
4232  *  w =
4233  *  client_data =       not used
4234  *  call_data =         not used
4235  *  wmGD.gadgetClient = last client with depressed client
4236  *
4237  * 
4238  *  Outputs:
4239  *  -------
4240  *  wmGD.menuActive = NULL
4241  *  wmGD.menuUnpostKeySpec = NULL
4242  *  wmGD.checkHotspot = FALSE
4243  *
4244  *
4245  *  Comments:
4246  *  --------
4247  *  None.
4248  * 
4249  *************************************<->***********************************/
4250
4251 static void UnmapCallback (Widget w, XtPointer client_data,
4252                            XtPointer call_data)
4253 {
4254     wmGD.menuActive = NULL;
4255     wmGD.menuUnpostKeySpec = NULL;
4256     wmGD.checkHotspot = FALSE;
4257
4258     if (wmGD.gadgetClient) 
4259     {
4260         PopGadgetOut(wmGD.gadgetClient, FRAME_SYSTEM);
4261     }
4262
4263 #ifndef OLD_COLORMAP
4264     ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
4265 #endif
4266     PullExposureEvents();
4267
4268 } /* END OF FUNCTION UnmapCallback */
4269
4270 \f
4271 /*************************************<->*************************************
4272  *
4273  *  MWarning (message)
4274  *
4275  *
4276  *  Description:
4277  *  -----------
4278  *  This function lists a message to stderr.
4279  *
4280  *
4281  *  Inputs:
4282  *  ------
4283  *  format  = pointer to a format string
4284  *  message = pointer to a message string
4285  * 
4286  *************************************<->***********************************/
4287
4288 void MWarning (char *format, char *message)
4289 {
4290
4291     if (strlen(format) + strlen(message)  <  (size_t) MAXWMPATH)
4292       {
4293          char pch[MAXWMPATH+1];
4294
4295          sprintf (pch, format, message);
4296          Warning (pch);
4297       }
4298
4299 } /* END OF FUNCTION MWarning */
4300
4301
4302 \f
4303 #if ((!defined(WSM)) || defined(MWM_QATS_PROTOCOL))
4304 /*************************************<->*************************************
4305  *
4306  *  UnmapPulldownCallback (w, client_data, call_data)
4307  *
4308  *
4309  *  Description:
4310  *  -----------
4311  *
4312  *
4313  *  Inputs:
4314  *  ------
4315  * 
4316  *  Outputs:
4317  *  -------
4318  *
4319  *  Comments:
4320  *  --------
4321  * 
4322  *************************************<->***********************************/
4323
4324 static void UnmapPulldownCallback (Widget w, XtPointer client_data,
4325                                    XtPointer call_data)
4326 {
4327     wmGD.menuActive = (MenuSpec *) NULL;
4328 } /* END OF FUNCTION UnmapPulldownCallback */
4329 #endif /* !defined(WSM) || defined(MWM_QATS_PROTOCOL) */
4330
4331 \f
4332 /*************************************<->*************************************
4333  *
4334  *  TraversalOff (menuSpec)
4335  *
4336  *
4337  *  Description:
4338  *  -----------
4339  *  This function turns menu traversal off.
4340  *
4341  *
4342  *  Inputs:
4343  *  ------
4344  *  menuSpec = menu specification
4345  *
4346  * 
4347  *  Outputs:
4348  *  -------
4349  *  None.
4350  *
4351  *
4352  *  Comments:
4353  *  --------
4354  *  None.
4355  * 
4356  *************************************<->***********************************/
4357
4358 void TraversalOff (MenuSpec *menuSpec)
4359 {
4360     if (menuSpec && (menuSpec->menuWidget))
4361     {
4362         /* function pointer */
4363         (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
4364                                        ->row_column_class.menuProcedures) 
4365                 /* argument list */
4366                (XmMENU_TRAVERSAL, menuSpec->menuWidget, False, NULL, NULL);
4367     }
4368
4369 } /* END OF FUNCTION TraversalOff */
4370
4371
4372 \f
4373 /*************************************<->*************************************
4374  *
4375  *  TraversalOn (menuSpec)
4376  *
4377  *
4378  *  Description:
4379  *  -----------
4380  *  This function turns menu traversal on.
4381  *
4382  *
4383  *  Inputs:
4384  *  ------
4385  *  menuSpec = menu specification
4386  *
4387  * 
4388  *  Outputs:
4389  *  -------
4390  *  None.
4391  *
4392  *
4393  *  Comments:
4394  *  --------
4395  *  None.
4396  * 
4397  *************************************<->***********************************/
4398
4399 void TraversalOn (MenuSpec *menuSpec)
4400 {
4401
4402     if (menuSpec && (menuSpec->menuWidget))
4403     {
4404         /* function pointer */
4405         (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
4406                                    ->row_column_class.menuProcedures) 
4407                 /*argument list */
4408                (XmMENU_TRAVERSAL, menuSpec->menuWidget, True, NULL, NULL);
4409     }
4410
4411 } /* END OF FUNCTION TraversalOn */
4412
4413
4414 \f
4415 /*************************************<->*************************************
4416  *
4417  *  FreeCustomMenuSpec (menuSpec)
4418  *
4419  *
4420  *  Description:
4421  *  -----------
4422  *  This procedure destroys a custom MenuSpec structure and its associated 
4423  *  menu widget, menuItems list, menuButtons array, and menu accelerator list.
4424  *
4425  *
4426  *  Inputs:
4427  *  ------
4428  *  menuSpec = MenuSpec structure
4429  *
4430  * 
4431  *  Outputs:
4432  *  -------
4433  *  None.
4434  *
4435  *
4436  *  Comments:
4437  *  --------
4438  *  Assumes that a MenuSpec is custom iff its name is NULL.
4439  *
4440  *  Assumes that ParseWmFuncStr() has parsed a menu item's function
4441  *  argument only for F_Exec and F_Menu.  If it is used for other functions,
4442  *  be sure to include them here!
4443  * 
4444  *************************************<->***********************************/
4445
4446 void FreeCustomMenuSpec (MenuSpec *menuSpec)
4447 {
4448     MenuItem    *menuItem;
4449     MenuItem    *nextMenuItem;
4450     KeySpec     *accelKeySpec;
4451     KeySpec     *nextAccelKeySpec;
4452
4453     if ((menuSpec == NULL) || (menuSpec->name != NULL))
4454     /* we only destroy custom menus! */
4455     {
4456         return;
4457     }
4458   
4459     /*
4460      * Fix for CR 5450 - If the custom menu is the same as wmGD.menuActive, call
4461      *                   the UnmapCallback directly to clean things up.  Since
4462      *                   the menu is going to be destroyed, this callback will
4463      *                   not get called, leaving MWM in a failure state.
4464      */
4465      if (wmGD.menuActive == menuSpec)
4466        UnmapCallback((Widget)NULL, (caddr_t)NULL, (caddr_t)NULL);
4467     /*
4468      * End fix for CR 5450
4469      */
4470  
4471     menuItem = menuSpec->menuItems;
4472     while (menuItem)
4473     {
4474         nextMenuItem = menuItem->nextMenuItem;
4475         FreeMenuItem (menuItem);
4476         menuItem = nextMenuItem;
4477     }
4478
4479     if (menuSpec->menuButtons)
4480     {
4481         XtFree ((char *)menuSpec->menuButtons);
4482     }
4483
4484     accelKeySpec = menuSpec->accelKeySpecs;
4485     while (accelKeySpec)
4486     {
4487         nextAccelKeySpec = accelKeySpec->nextKeySpec;
4488         XtFree ((char *)accelKeySpec);
4489         accelKeySpec = nextAccelKeySpec;
4490     }
4491
4492     if (menuSpec->menuWidget)
4493     /* destroy all children of the menu's MenuShell parent */
4494     {
4495         XtDestroyWidget (XtParent(menuSpec->menuWidget));
4496     }
4497
4498     XtFree ((char *)menuSpec);
4499
4500 } /* END OF FUNCTION FreeCustomMenuSpec */
4501
4502 \f