dtcm: Coverity 88353
[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 libraries 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 /*
31  * (c) Copyright 1987, 1988, 1989, 1990 HEWLETT-PACKARD COMPANY */
32 /*
33  * (c) Copyright 1987, 1988 DIGITAL EQUIPMENT CORPORATION */
34 /*
35  * (c) Copyright 1988 MASSACHUSETTS INSTITUTE OF TECHNOLOGY */
36
37 /*
38  * Included Files:
39  */
40
41 #include "WmGlobal.h"
42 #include "WmCEvent.h"
43 #include "WmResource.h"
44 #include "WmResParse.h"
45 #include <stdio.h>
46
47 #include <X11/Shell.h>
48
49 #include <Xm/Xm.h>
50 #include <Xm/XmP.h>
51 #include <Xm/CascadeB.h>
52 #include <Xm/CascadeBG.h>
53 #include <Xm/Label.h>
54 #include <Xm/LabelG.h>
55 #include <Xm/MenuShell.h>
56 #include <Xm/PushB.h>
57 #include <Xm/PushBG.h>
58 #include <Xm/RowColumn.h>
59 #include <Xm/RowColumnP.h>
60 #include <Xm/Separator.h>
61 #include <Xm/SeparatoG.h>
62
63 #define SHELL_NAME "menu"
64 #define SEPARATOR_NAME "separator"
65 #define TITLE_NAME "title_name"
66 #define CASCADE_BTN_NAME "cascadebutton"
67 #define PUSH_BTN_NAME "pushbutton"
68
69 #define CHILDREN_CACHE  22
70 #define MENU_BUTTON_INC 5
71
72 /*
73  * include extern functions
74  */
75 #include "WmMenu.h"
76 #include "WmCDecor.h"
77 #include "WmColormap.h"
78 #include "WmEvent.h"
79 #include "WmFunction.h"
80 #include "WmIconBox.h"
81 #include "WmImage.h"
82 #include "WmError.h"
83 #include "WmWrkspace.h"
84
85
86 static void UnmapCallback (Widget w, XtPointer client_data,
87                            XtPointer call_data);
88 static MenuItem *DuplicateMenuItems (MenuItem *menuItems);
89
90
91 /*************************************<->*************************************
92  *
93  *  MakeMenu (menuName, initialContext, accelContext, moreMenuItems,
94  *            fSystemMenu)
95  *
96  *
97  *  Description:
98  *  -----------
99  *  This function makes a menu widget.
100  *
101  *
102  *  Inputs:
103  *  ------
104  *  menuName       = name of the top-level menu pane for the menu
105  *  initialContext = initial context for menuitem sensitivity
106  *  accelContext   = accelerator context
107  *  moreMenuItems  = additional menuitems for custom menu.
108  *  fSystemMenu    = TRUE iff the menu is a client system menu.
109  *
110  * 
111  *  Outputs:
112  *  -------
113  *  Return = pointer to a MenuSpec structure with updated currentContext,
114  *           menuWidget, and menuButtons members.
115  *
116  *
117  *  Comments:
118  *  --------
119  *  If moreMenuItems is nonNULL, a custom MenuSpec will be created, with
120  *  menuItem member pointing to moreMenuItems.  The menuItems for the
121  *  standard MenuSpec of the same name and the moreMenuItems list will be 
122  *  used to create menubuttons, and the menu widget will be separate from 
123  *  any existing standard menu widget.
124  *
125  *  When the client is destroyed, this custom MenuSpec, its menuItem and
126  *  menuButton lists, and its menu widget should be freed.
127  * 
128  *************************************<->***********************************/
129 MenuSpec *MakeMenu (WmScreenData *pSD,
130                     String menuName, Context initialContext,
131                     Context accelContext, MenuItem *moreMenuItems,
132                     Boolean fSystemMenu)
133 {
134     unsigned int n;
135     MenuSpec     *menuSpec;
136     MenuSpec     *newMenuSpec;
137     MenuItem     *menuItem;
138     KeySpec      *accelKeySpec;
139     
140     if ((menuName == NULL) || (pSD == NULL))
141     {
142         return (NULL);
143     }
144
145     /*
146      * Look for the menu specification:
147      */
148
149     menuSpec = pSD->menuSpecs;
150     while (menuSpec)
151     {
152         if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
153         /* Found the menu pane. */
154         {
155             break;
156         }
157         menuSpec = menuSpec->nextMenuSpec;
158     }
159     
160     if (menuSpec == NULL)
161     /* the menuSpecs list is exhausted */
162     {
163         MWarning(((char *)GETMESSAGE(48, 1, "Menu specification %s not found\n")), menuName);
164         return (NULL);
165     }
166
167     /*
168      * The top-level menu pane specification was found.
169      * Adjust the menu accelerator context?
170      */
171
172     if (fSystemMenu)
173     {
174         accelContext = 0;
175     }
176     else if (accelContext & F_CONTEXT_ROOT)
177     /* root context accelerators apply everywhere */
178     {
179         accelContext = F_CONTEXT_ALL;
180     }
181
182     /*
183      * If making a custom menu, create a custom copy of the specification with 
184      *   which to build the custom menu.
185      * Otherwise, if the menu widget exists, possibly modify the accelerator
186      *   contexts and return the specification.
187      */
188
189     if (moreMenuItems != NULL)
190     {
191         if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
192         /* Handle insufficent memory */
193         {
194             MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
195             return (NULL);
196         }
197         newMenuSpec->name = NULL;  /* distinguishes this as custom */
198         newMenuSpec->whichButton = SELECT_BUTTON;
199         newMenuSpec->height = 0;
200         newMenuSpec->menuItems = menuSpec->menuItems; /* temporary */
201         newMenuSpec->accelContext = menuSpec->accelContext;
202         newMenuSpec->accelKeySpecs = NULL;
203         newMenuSpec->nextMenuSpec = NULL;
204
205         menuSpec = newMenuSpec;
206     }
207     else if (menuSpec->menuWidget)
208     {
209         /* 
210          * OR the accelContext into the accelerators, if necessary.
211          */
212         if (accelContext != (menuSpec->accelContext & accelContext))
213         {
214             menuSpec->accelContext |= accelContext;
215             accelKeySpec = menuSpec->accelKeySpecs;
216             while (accelKeySpec)
217             {
218                 accelKeySpec->context |= accelContext;
219                 accelKeySpec = accelKeySpec->nextKeySpec;
220             }
221         }
222         return (menuSpec);
223     }
224
225     /*
226      * We have a menu specification with which to build the menu.
227      * Set the initial and accelerator contexts -- they are needed within 
228      *   CreateMenuWidget.
229      */
230
231     menuSpec->currentContext = initialContext;
232     menuSpec->accelContext = accelContext;
233
234     /*
235      * Scan the toplevel MenuSpec and create its initial menuButtons array
236      * if any of its items will need to be included.  This array will be
237      * created or enlarged within CreateMenuWidget below if necessary.
238      */
239
240     n = 0;
241     menuItem = menuSpec->menuItems;
242     while (menuItem)
243     {
244         if ((menuItem->greyedContext) || (menuItem->mgtMask))
245         {
246             n++;
247         }
248         menuItem = menuItem->nextMenuItem;
249     }
250     menuItem = moreMenuItems;
251     while (menuItem)
252     {
253         if ((menuItem->greyedContext) || (menuItem->mgtMask))
254         {
255             n++;
256         }
257         menuItem = menuItem->nextMenuItem;
258     }
259     if (n)
260     {
261         if ((menuSpec->menuButtons =
262                (MenuButton *) XtMalloc (n * sizeof(MenuButton))) == NULL)
263         /* insufficent memory */
264         {
265             MWarning(((char *)GETMESSAGE(48, 3, "Insufficient memory for menu %s\n")), menuName);
266             return (NULL);
267         }
268         menuSpec->menuButtonSize = n;
269     }
270     else
271     {
272         menuSpec->menuButtons = NULL;
273         menuSpec->menuButtonSize = 0;
274     }
275     menuSpec->menuButtonCount = 0;
276
277     /*
278      * Create a PopupShell widget as a child of the workspace manager widget
279      *   and a PopupMenu as a child of the shell.
280      * Fill the PopupMenu with the menu items.
281      */
282
283     menuSpec->menuWidget = CREATE_MENU_WIDGET (pSD, pCD, menuName, 
284                                              pSD->screenTopLevelW,
285                                              TRUE, menuSpec, moreMenuItems);
286     if (menuSpec->menuWidget == NULL)
287     {
288         /*
289          *  Could not make the top-level menu pane.
290          */
291         return (NULL);
292     }
293 /*
294     _XmSetPopupMenuClick(menuSpec->menuWidget, False); 
295 */
296     /* Return the top MenuSpec */
297
298     return (menuSpec);
299
300 } /* END OF FUNCTION MakeMenu */
301
302
303 \f
304 /*************************************<->***********************************/
305 void CheckTerminalSeparator(MenuSpec *menuSpec, Widget buttonWidget, Boolean manage)
306 {
307     CompositeWidget cw;
308     WidgetList      children;
309     Cardinal        wPos;
310
311
312     if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
313       {
314          return;
315       }
316
317     cw = (CompositeWidget)menuSpec->menuWidget;
318     children = cw->composite.children;
319
320     for (wPos = 0;  wPos < cw->composite.num_children; wPos++)
321     {
322         if((Widget)children[wPos] == buttonWidget)
323         {
324             break;
325         }
326     }
327     
328     
329     if(wPos > 0 &&
330        XtClass((Widget) children[wPos -1]) == xmSeparatorGadgetClass)
331     {
332         if(manage)
333         {
334             if (!(XtIsManaged((Widget)children[wPos -1])))
335             {
336                 XtManageChild((Widget)children[wPos -1]);
337             }
338         }
339         else
340         {
341             if (XtIsManaged((Widget)children[wPos -1]))
342             {
343                 XtUnmanageChild((Widget)children[wPos -1]);
344             }
345         }
346     }
347
348 } /* END OF FUNCTION CheckTerminalSeparator */
349
350
351 /*************************************<->*************************************
352  *
353  *  DuplicateMenuItems (menuItems)
354  *  
355  *
356  *
357  *  Description:
358  *  -----------
359  *  This function creates an indentical duplicate of the given menuItems
360  *  list.
361  *
362  *
363  *  Inputs:
364  *  ------
365  *  menuItems = the linked list of menuItems to duplicate
366  *
367  * 
368  *  Outputs:
369  *  -------
370  *  Return = pointer to a new MenuItems list, identical to the original
371  *
372  *
373  *  Comments:
374  *  --------
375  * 
376  *************************************<->***********************************/
377 static MenuItem *
378 DuplicateMenuItems (MenuItem *menuItems)
379 {
380     MenuItem *newMenuItem = (MenuItem *) NULL, *returnMenuItem, *curMenuItem;
381     
382     for (curMenuItem = menuItems;
383          curMenuItem != (MenuItem *) NULL;
384          curMenuItem = curMenuItem->nextMenuItem)
385     {
386         /* If its the first one ... */
387         if (newMenuItem == (MenuItem *) NULL)
388         {
389             newMenuItem = (MenuItem *)XtMalloc(sizeof(MenuItem));
390             returnMenuItem = newMenuItem;
391         }
392         else /* ... otherwise, get the next menuItem. */
393         {
394             newMenuItem->nextMenuItem =
395               (MenuItem *)XtMalloc(sizeof(MenuItem));
396             newMenuItem = newMenuItem->nextMenuItem;
397         }
398         
399         newMenuItem->labelType = curMenuItem->labelType;
400         if (curMenuItem->label != (String) NULL)
401           newMenuItem->label = XtNewString(curMenuItem->label);
402         else
403           newMenuItem->label = NULL;
404         newMenuItem->labelBitmapIndex = curMenuItem->labelBitmapIndex;
405         newMenuItem->mnemonic = curMenuItem->mnemonic;
406         newMenuItem->accelState = curMenuItem->accelState;
407         newMenuItem->accelKeyCode = curMenuItem->accelKeyCode;
408         if (curMenuItem->accelText != (String) NULL)
409           newMenuItem->accelText = XtNewString(curMenuItem->accelText);
410         else
411           newMenuItem->accelText = NULL;
412         newMenuItem->wmFunction = curMenuItem->wmFunction;
413
414         if ((curMenuItem->wmFunction == F_Send_Msg)
415             || (curMenuItem->wmFunction == F_Set_Context)
416             /*
417              * NOTE: For now, in dtwm this function is used only
418              * to copy the FrontPanel menu.  So, we know that
419              * curMenuItem->wmFuncArgs isn't going anywhere,
420              * so it's safe to simply point at it.  If at some
421              * point it becomes possible that curMenuItem->wmFuncArgs
422              * can go away, we'll need to make a (deep) copy of
423              * the WmActionArg.  11/20/96
424              */
425             || (curMenuItem->wmFunction == F_Action)
426             )
427           newMenuItem->wmFuncArgs = curMenuItem->wmFuncArgs;
428         else if (curMenuItem->wmFuncArgs != (String) NULL)
429           newMenuItem->wmFuncArgs = XtNewString(curMenuItem->wmFuncArgs);
430         else
431           newMenuItem->wmFuncArgs = NULL;
432
433         newMenuItem->greyedContext = curMenuItem->greyedContext;
434         newMenuItem->mgtMask = curMenuItem->mgtMask;
435         newMenuItem->nextMenuItem = (MenuItem *) NULL;
436     }
437     
438     return(returnMenuItem);
439 }
440
441 /*************************************<->*************************************
442  *
443  *  DuplicateMenuSpec (menuSpec)
444  *  
445  *
446  *
447  *  Description:
448  *  -----------
449  *  This function creates an indentical duplicate of the given menuSpec.
450  *  The menuItems list in the menuSpec is also duplicated. 
451  *
452  *
453  *  Inputs:
454  *  ------
455  *  menuSpec = the menuSpec to duplicate
456  *
457  * 
458  *  Outputs:
459  *  -------
460  *  Return = pointer to a new MenuSpec structure with the same field
461  *           values as the original
462  *
463  *
464  *  Comments:
465  *  --------
466  *  A new MenuSpec structure is allocated. Most of he fields of the new
467  *  structure are set to the same values as the passed in menuSpec.
468  *  There are some differences between the two final structures.
469  *  One difference: any fields related to push buttons and other
470  *  widgets are left blank in the new MenuSpec to be filled in later.
471  * 
472  *************************************<->***********************************/
473 MenuSpec *
474 DuplicateMenuSpec (MenuSpec *menuSpec)
475 {
476     MenuSpec *newMenuSpec;
477     
478     if ((newMenuSpec = (MenuSpec *) XtMalloc (sizeof (MenuSpec))) == NULL)
479       /* Handle insufficent memory */
480     {
481         Warning((char *)GETMESSAGE(48, 9,
482                  "Insufficient memory for menu specification\n"));
483         return (NULL);
484     }
485     newMenuSpec->name = XtNewString(menuSpec->name);
486     newMenuSpec->currentContext = menuSpec->currentContext;
487     newMenuSpec->menuWidget = (Widget) NULL;
488     newMenuSpec->whichButton = menuSpec->whichButton;
489     newMenuSpec->height = menuSpec->height;
490     newMenuSpec->menuItems = DuplicateMenuItems(menuSpec->menuItems);
491     newMenuSpec->menuButtons = (MenuButton *) NULL;
492     newMenuSpec->menuButtonSize = 0;
493     newMenuSpec->menuButtonCount = 0;
494     newMenuSpec->accelContext = menuSpec->accelContext;
495     newMenuSpec->accelKeySpecs = (KeySpec *) NULL;
496     newMenuSpec->nextMenuSpec = (MenuSpec *) NULL;
497     
498     return(newMenuSpec);
499 }
500
501 /*************************************<->*************************************
502  *
503  *  static Boolean
504  *  AdjustPBs (menuSpec, pCD, newContext)
505  *
506  *
507  *  Description:
508  *  -----------
509  *  This procedure adjusts menu PushButton sensitivities and manage/unmanaged
510  *  status for a toplevel menu.
511  *
512  *
513  *  Inputs:
514  *  ------
515  *  menuSpec =    nonNULL toplevel menu specification with gadget
516  *  pCD =         client data
517  *  newContext =  context that the menu is to be posted under.
518  *
519  * 
520  *  Outputs:
521  *  -------
522  *  menuSpec =      menu specification with modifications
523  *  Return =        TRUE iff at least one menu item changed manage status.
524  *
525  *
526  *  Comments:
527  *  --------
528  *  Adjusts PushButton sensitivity according to context and function type.
529  *  Manages/Unmanages PushButtons according to clientFunction resource.
530  * 
531  *************************************<->***********************************/
532 static Boolean AdjustPBs (MenuSpec *menuSpec, ClientData  *pCD,
533                           Context newContext)
534 {
535     MenuButton    *menuButton;
536     MenuItem      *menuItem;
537     int            msgc;
538     unsigned int   n;
539     long          *pMsg;
540     Boolean        fSupported;
541     Boolean        fChangeManaged = FALSE;
542
543     /*
544      *  Set PushButton sensitivity.
545      *  Set f.send_msg button sensitivity according to context and client 
546      *  message list.  Adjust other button sensitivities only for context.
547      */
548
549     /* check for bad input value - shouldn't happen. */
550     if (menuSpec == NULL) return (FALSE);
551
552     for (n = 0, menuButton = menuSpec->menuButtons;
553          n < menuSpec->menuButtonCount;
554          n++, menuButton++)
555     {
556         menuItem = menuButton->menuItem;
557         if (menuItem->wmFunction == F_Send_Msg)
558         /* f.send_msg button:  set according to context and message. */
559         {
560             if ((newContext & menuItem->greyedContext) ||
561                 !(pCD && pCD->mwmMessagesCount && pCD->mwmMessages))
562             /* insensitive context or empty client message list */
563             {
564                 XtSetSensitive (menuButton->buttonWidget, FALSE);
565             }
566             else
567             /* 
568              * Have a context sensitive f.send_msg item and a client with a 
569              * nonempty message list.  Set sensitive only if the message is 
570              * supported by this client.  Otherwise set insensitive.
571              */
572             {
573                 msgc = pCD->mwmMessagesCount;
574                 pMsg = pCD->mwmMessages;
575                 fSupported = FALSE;
576                 while (msgc--)
577                 /* scan nonempty message list */
578                 {
579                     if (*pMsg == (long) menuItem->wmFuncArgs)
580                     /* found match */
581                     {
582                         fSupported = TRUE;
583                         break;
584                     }
585                     pMsg++;  /* next message in list */
586                 }
587                 XtSetSensitive (menuButton->buttonWidget, fSupported);
588             }
589         }
590         else
591         /*
592          * Non f.send_msg button:
593          *  Adjust sensitivity according to context.
594          *  Manage/Unmanage according to clientFunction.
595          */
596         {
597             if (menuSpec->currentContext & menuItem->greyedContext)
598             /* button is currently insensitive */
599             {
600                 if (!(newContext & menuItem->greyedContext))
601                 /* insensitive -> sensitive */
602                 {
603                     XtSetSensitive (menuButton->buttonWidget, TRUE);
604                 }
605             }
606             else
607             /* button is currently sensitive */
608             {
609                 if (newContext & menuItem->greyedContext)
610                 /* sensitive -> insensitive */
611                 {
612                     XtSetSensitive (menuButton->buttonWidget, FALSE);
613                 }
614             }
615
616             if (menuItem->wmFunction == F_Remove)
617             {
618                 /*
619                  * Only allow remove from workspace if the client
620                  * is in more than one workspace
621                  */
622                 fSupported = (pCD && (pCD->numInhabited > 1));
623                 XtSetSensitive (menuButton->buttonWidget, fSupported);
624             }
625
626             if ((menuItem->mgtMask) && pCD)
627             /* PushButton might not apply */
628             {
629                 if ((pCD->clientFunctions & menuItem->mgtMask & MWM_MGT_MASK) ||
630                     (pCD->dtwmFunctions & menuItem->mgtMask & DTWM_MGT_MASK))
631                 /* function applies -- manage it */
632                 {
633                     if (!menuButton->managed)
634                     /* unmanaged -> managed */
635                     {
636                         XtManageChild (menuButton->buttonWidget);
637                         menuButton->managed = TRUE;
638                         fChangeManaged = TRUE;
639                         if (n == menuSpec->menuButtonCount - 1)
640                         {
641                             /* 
642                              * last item, if it has a separator before
643                              * it, manage the separator
644                              */
645                             
646                             CheckTerminalSeparator(menuSpec, 
647                                                    menuButton->buttonWidget, 
648                                                    True);
649                         }
650                     }
651                 }
652                 else
653                 /* function does not apply -- unmanage it */
654                 {
655                     if (menuButton->managed)
656                     /* managed -> unmanaged */
657                     {
658                         XtUnmanageChild (menuButton->buttonWidget);
659                         menuButton->managed = FALSE;
660                         fChangeManaged = TRUE;
661
662                         if (n == menuSpec->menuButtonCount - 1)
663                         {
664                             /* 
665                              * last item, if it has a separator before
666                              * it, unmanage the separator
667                              */
668                             CheckTerminalSeparator(menuSpec, 
669                                                    menuButton->buttonWidget, 
670                                                    False);
671                         }
672
673                     }
674                 }
675             }
676             else if (!menuButton->managed)
677             /* unmanaged PushButton applies */
678             {
679                 XtManageChild (menuButton->buttonWidget);
680                 menuButton->managed = TRUE;
681                 fChangeManaged = TRUE;
682             }
683         }
684     }
685
686     return (fChangeManaged);
687
688 } /* END OF FUNCTION AdjustPBs */
689
690
691 \f
692 /*************************************<->*************************************
693  *
694  *  static Boolean
695  *  SavePBInfo (topMenuSpec, menuItem, itemW)
696  *
697  *
698  *  Description:
699  *  -----------
700  *  Fills a MenuButton structure for a PushButton.
701  *  If necessary, mallocs or reallocs the menuButtons array in the toplevel
702  *  MenuSpec.
703  *
704  *
705  *  Inputs:
706  *  ------
707  *  topMenuSpec = pointer to toplevel MenuSpec structure
708  *  menuItem    = pointer to PushButton MenuItem structure
709  *  itemW       = PushButton gadget
710  *  topMenuSpec->menuButtons[]
711  *  topMenuSpec->menuButtonSize
712  *  topMenuSpec->menuButtonCount
713  *
714  * 
715  *  Outputs:
716  *  -------
717  *  Return = FALSE iff insufficient memory for malloc or realloc
718  *           or bad input value forces exit.
719  *  topMenuSpec->menuButtons[]
720  *  topMenuSpec->menuButtonSize
721  *  topMenuSpec->menuButtonCount
722  *
723  *
724  *  Comments:
725  *  --------
726  *  The initial managed status of PushButtons is TRUE.
727  * 
728  *************************************<->***********************************/
729 static Boolean SavePBInfo (MenuSpec *topMenuSpec, MenuItem *menuItem,
730                              Widget itemW)
731 {
732     MenuButton *menuButton;
733
734
735     /* check for bad input value - shouldn't happen. */
736     if (topMenuSpec == NULL) return (FALSE);
737
738     if (topMenuSpec->menuButtonSize == 0)
739     /* need to create array */
740     {
741         topMenuSpec->menuButtonSize = MENU_BUTTON_INC;
742         topMenuSpec->menuButtons =
743             (MenuButton *) XtMalloc (MENU_BUTTON_INC * sizeof(MenuButton));
744     }
745     else if (topMenuSpec->menuButtonCount == topMenuSpec->menuButtonSize)
746     /* need larger array */
747     {
748         topMenuSpec->menuButtonSize += MENU_BUTTON_INC;
749         topMenuSpec->menuButtons = (MenuButton *) 
750             XtRealloc ((char*)topMenuSpec->menuButtons,
751                      topMenuSpec->menuButtonSize * sizeof(MenuButton));
752     }
753
754     if (topMenuSpec->menuButtons == NULL)
755     /* insufficent memory */
756     {
757         topMenuSpec->menuButtonSize = 0;
758         topMenuSpec->menuButtonCount = 0;
759         return (FALSE);
760     }
761
762     menuButton = &(topMenuSpec->menuButtons[topMenuSpec->menuButtonCount]);
763     topMenuSpec->menuButtonCount++;
764
765     menuButton->menuItem = menuItem;
766     menuButton->buttonWidget = itemW;
767     menuButton->managed = TRUE;
768     return (TRUE);
769
770 }
771
772 /*************************************<->*************************************
773  *
774  *  CreateMenuWidget (pSD, menuName, parent, fTopLevelPane, topMenuSpec, 
775  *                    moreMenuItems)
776  *
777  *
778  *  Description:
779  *  -----------
780  *  Creates a MenuShell as a child of the specified parent widget, and a 
781  *  PopupMenu or PulldownMenu as a child of the shell.  Fill the menu with
782  *  the named menupane items.
783  *
784  *
785  *  Inputs:
786  *  ------
787  *  pSD ---------- pointer to screen data
788  *  menuName ----- the name of the menu specification to be used to create
789  *                 the menu widget.
790  *  parent -------- parent of popup shell
791  *  fTopLevelPane - TRUE iff the menupane is a top level one
792  *  topMenuSpec --- pointer to the top menu specification.
793  *  moreMenuItems - pointer to additional menu items for custom menu.
794  * 
795  * 
796  *  Outputs:
797  *  -------
798  *  Return = created PopupMenu or PulldownMenu widget, or NULL.
799  *
800  *
801  *  Comments:
802  *  --------
803  *  We attach a popdowncallback to the menu to set wmGD.menuActive to NULL,
804  *  allowing us to not dispatch key events separately from the toolkit 
805  *  dispatcher.
806  * 
807  *************************************<->***********************************/
808
809 typedef struct _StrList
810 {
811    XmString         string;
812    struct _StrList *next;
813 } StrList;
814
815 Widget CreateMenuWidget (WmScreenData *pSD,
816                          String menuName, Widget parent,
817                          Boolean fTopLevelPane, MenuSpec *topMenuSpec,
818                          MenuItem *moreMenuItems)
819 {
820     int         i, n;
821     Arg         sepArgs[1];
822     Arg         args[10];
823     MenuSpec   *menuSpec = (MenuSpec *)NULL;
824     MenuItem   *menuItem;
825     Widget      menuShellW;
826     Widget      menuW;
827     Widget      subMenuW;
828     Widget      children[CHILDREN_CACHE];
829     Pixmap      labelPixmap;
830     KeySpec    *accelKeySpec;
831     Dimension   menuHeight;
832     Boolean     fUseTitleSep = False;
833     StrList    *stringsToFree = NULL, *sPtr;
834     XmString    tmpstr;
835 #ifndef IBM_151913
836     Screen     *scr;
837 #endif
838
839
840     /* check for bad input values. */
841     if ((menuName == NULL) || (pSD == NULL))
842     {
843         return (NULL);
844     }
845
846     /*
847      *  Find the menu pane specifications for menuName.
848      *  The top-level menu specification is passed as an argument (it may
849      *  be custom).  A submenu specification must be found and might not exist.
850      *  Return NULL if a submenu specification is not found.
851      */
852     if (fTopLevelPane)
853     {
854         menuSpec = topMenuSpec;
855     }
856     else
857     {
858         menuSpec = pSD->menuSpecs;
859         while (menuSpec)
860         {
861             if ((menuSpec->name != NULL) && !strcmp (menuSpec->name, menuName))
862             {
863                 break;  /* found menuName's specification */
864             }
865             menuSpec = menuSpec->nextMenuSpec;  /* keep looking */
866         }
867     }
868
869     if (menuSpec == NULL)
870     /* (submenu) specification not found */
871     {
872         MWarning(((char *)GETMESSAGE(48, 4, "Menu specification %s not found\n")), menuName);
873         return (NULL);
874     }
875
876     /*
877      * If menuSpec is marked, we have menu recursion => fail.
878      *  Otherwise, mark it.
879      */
880
881     if (menuSpec->currentContext & CR_MENU_MARK)   /* marked? */
882     /* menu recursion */
883     {
884         MWarning(((char *)GETMESSAGE(48, 5, "Menu recursion detected for %s\n")), menuName);
885         return (NULL);
886     }
887     menuSpec->currentContext |= CR_MENU_MARK;   /* no, mark it */
888
889     /*
890      * Create a PopupShell widget.
891      * If the parent of the specified parent ("grandparent") is a MenuShell 
892      * widget, then use the grandparent as the parent of the PopupShell.
893      * Otherwise, use the specified parent.
894      */
895     i = 0;
896     XtSetArg (args[i], XmNwidth, (XtArgVal) 5); i++;
897     XtSetArg (args[i], XmNheight, (XtArgVal) 5); i++;
898     XtSetArg (args[i], XmNallowShellResize, (XtArgVal) TRUE); i++;
899     XtSetArg (args[i], XtNoverrideRedirect, (XtArgVal) TRUE); i++;
900     XtSetArg (args[i], XtNdepth, 
901               (XtArgVal) DefaultDepth(XtDisplay(parent), pSD->screen)); i++;
902     XtSetArg (args[i], XtNscreen, 
903               (XtArgVal) ScreenOfDisplay(XtDisplay(parent), pSD->screen)); i++;
904
905     if ((XtParent (parent) != NULL) && XmIsMenuShell (XtParent (parent)))
906     {
907         parent = XtParent (parent);
908     }
909
910     menuShellW = XtCreatePopupShell (SHELL_NAME, xmMenuShellWidgetClass,
911                                      parent, (ArgList) args, i);
912
913     /*
914      * Create a RowColumn widget as a child of the shell for the menu pane.
915      * If the menu pane is top-level, create a popup menu for it and attach 
916      *   the unmap callback to it.
917      * Otherwise, create a pulldown menu for it.
918      */
919
920     i = 0;
921     XtSetArg (args[i], XmNborderWidth, (XtArgVal) 0); i++;
922     XtSetArg (args[i], XmNwhichButton, (XtArgVal) SELECT_BUTTON); i++;
923     XtSetArg (args[i], XmNadjustMargin, (XtArgVal) TRUE); i++;
924
925     if (fTopLevelPane)
926     {
927         XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_POPUP); i++;
928         XtSetArg (args[i], XmNpopupEnabled, (XtArgVal) TRUE); i++;
929         menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
930                                 (ArgList) args, i);
931         XtAddCallback (menuW, XmNunmapCallback, UnmapCallback, 
932                                 (XtPointer) menuSpec);
933     }
934     else
935     {
936         XtSetArg (args[i], XmNrowColumnType, (XtArgVal) XmMENU_PULLDOWN); i++;
937         menuW = XtCreateWidget (menuName, xmRowColumnWidgetClass, menuShellW,
938                                 (ArgList) args, i);
939     }
940
941     /*
942      * Create the specified menu entries as children of the menupane.
943      * Menus may contain the following widgets:
944      *
945      *   Label
946      *   Separator 
947      *   CascadeButton
948      *   PushButton
949      *
950      * Add separator gadgets around menu titles.
951      */
952
953     XtSetArg (sepArgs[0], XmNseparatorType, (XtArgVal) XmDOUBLE_LINE);
954
955     n = 0;
956     menuItem = menuSpec->menuItems;
957     if ((menuItem == NULL) && (moreMenuItems != NULL))
958     /* handle custom menu with empty standard specification */
959     {
960         menuSpec->menuItems = menuItem = moreMenuItems;
961         moreMenuItems = NULL;
962     }
963     while (menuItem)
964     {
965         i = 0;
966
967         if (menuItem->wmFunction == F_Separator)
968         /* 
969          * Add a Separator gadget for a menu separator.
970          * An immediately following title will not have a top separator.
971          */
972         {
973             {
974                 children[n] =
975                   XmCreateSeparatorGadget (menuW, SEPARATOR_NAME, 
976                                            (ArgList)NULL, 0);
977                 fUseTitleSep = FALSE;
978             }
979         } /* F_Separator */
980
981         else
982         /*
983          * We will use one of:
984          *
985          *   Label
986          *   CascadeButton
987          *   PushButton
988          */
989         {
990             /*
991              * Construct the label
992              */
993             if ((menuItem->labelType == XmPIXMAP) &&
994                  (labelPixmap =
995                       MakeCachedLabelPixmap (pSD, menuW,
996                                              menuItem->labelBitmapIndex)))
997             {
998                 XtSetArg (args[i], XmNlabelType, (XtArgVal) XmPIXMAP); i++;
999                 XtSetArg (args[i], XmNlabelPixmap, (XtArgVal) labelPixmap); i++;
1000                 XtSetArg (args[i], XmNlabelInsensitivePixmap,
1001                           (XtArgVal) labelPixmap); i++;
1002             }
1003             else
1004             {
1005                 XtSetArg (args[i], XmNlabelType, (XtArgVal) XmSTRING); i++;
1006                 XtSetArg (args[i], XmNlabelString, (XtArgVal)
1007                           (tmpstr = XmStringCreateLocalized(menuItem->label))); i++;
1008                 sPtr = (StrList *) XtMalloc(sizeof(StrList));
1009                 if (sPtr == NULL)
1010                   {
1011                      MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
1012                      return (NULL);
1013                   }
1014                 else
1015                   {
1016                      sPtr->string  = tmpstr;
1017                      sPtr->next    = stringsToFree;
1018                      stringsToFree = sPtr;
1019                   }
1020             }
1021
1022             if (menuItem->wmFunction == F_Title)
1023             /* 
1024              * Add a centered Label gadget for a menu title.
1025              * Include separators above and below the title.
1026              * Don't include the top one if the title is the first pane item
1027              *   or immediately follows a user-supplied separator.
1028              */
1029             {
1030                 if (fUseTitleSep)
1031                 {
1032                     children[n] =
1033                         XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
1034                                                  sepArgs, 1); n++;
1035                 }
1036
1037                 XtSetArg (args[i], XmNalignment, XmALIGNMENT_CENTER); i++; 
1038                 children[n] = XmCreateLabelGadget (menuW, TITLE_NAME,
1039                                                    (ArgList) args, i); n++;
1040                 children[n] = XmCreateSeparatorGadget (menuW, SEPARATOR_NAME,
1041                                           sepArgs, 1); 
1042
1043                 /*
1044                  * A following title will have both separators.
1045                  */
1046
1047                 fUseTitleSep = TRUE;
1048             }
1049
1050             else
1051             /*
1052              * We will use one of:
1053              *
1054              *   CascadeButton
1055              *   PushButton
1056              *
1057              * Both support mnemonics; only PushButtons support accelerators.
1058              */
1059             {
1060                 /*
1061                  * Align text on the left.
1062                  * Set any mnemonic text.
1063                  */
1064                 XtSetArg (args[i], XmNalignment, XmALIGNMENT_BEGINNING); i++;
1065
1066                 if (menuItem->mnemonic)
1067                 {
1068                     XtSetArg (args[i], XmNmnemonic, 
1069                                (XtArgVal) menuItem->mnemonic); i++;
1070                 }
1071
1072                 if (menuItem->wmFunction == F_Menu)
1073                 /* 
1074                  * Create a PopupShell and PulldownMenu for a submenu (the 
1075                  *   menushells are linked together).
1076                  * Create a CascadeButton Widget 
1077                  * The submenu widget is attached to the CascadeButton gadget
1078                  *   using the subMenuId resource.
1079                  * Make the CascadeButton insensitive if the submenu cannot be 
1080                  *   created.
1081                  */
1082                 {
1083                     subMenuW = CREATE_MENU_WIDGET (pSD, pCD,
1084                                                  menuItem->wmFuncArgs, menuW,
1085                                                  FALSE, topMenuSpec, 
1086                                                  (MenuItem *)NULL);
1087                     if (subMenuW)
1088                     /*
1089                      * Attach submenu to cascade button. 
1090                      */
1091                     {
1092                         XtSetArg (args[i], XmNsubMenuId, (XtArgVal) subMenuW);
1093                             i++;
1094                         children[n] = XmCreateCascadeButtonGadget (menuW,
1095                                           CASCADE_BTN_NAME, (ArgList) args, i);
1096                     }
1097                     else
1098                     /*
1099                      * Unable to create submenupane: make the entry insensitive.
1100                      */
1101                     {
1102                         children[n] = XmCreateCascadeButtonGadget (menuW,
1103                                           CASCADE_BTN_NAME, (ArgList) args, i);
1104                         XtSetSensitive (children[n], FALSE);
1105                     }
1106
1107                     /*
1108                      * A following title will have both separators.
1109                      */
1110
1111                     fUseTitleSep = TRUE;
1112                 }
1113
1114                 else 
1115                 /* 
1116                  * Create a PushButton gadget.
1117                  */
1118                 {
1119                     /*
1120                      * If an accelerator is specified, set acceleratorText,
1121                      * then create an accelerator KeySpec and insert it at the
1122                      * head of the toplevel MenuSpec's list.
1123                      */
1124                     if (menuItem->accelText)
1125                     {
1126                         XtSetArg (args[i], XmNacceleratorText, (XtArgVal)
1127                                   (tmpstr = XmStringCreateLocalized(menuItem->accelText))); i++;
1128                         sPtr = (StrList *) XtMalloc(sizeof(StrList));
1129                         if (sPtr == NULL)
1130                           {
1131                              MWarning(((char *)GETMESSAGE(48, 2, "Insufficient memory for menu %s\n")), menuName);
1132                              return (NULL);
1133                           }
1134                         else
1135                           {
1136                              sPtr->string = tmpstr;
1137                              sPtr->next   = stringsToFree;
1138                              stringsToFree = sPtr;
1139                           }
1140
1141                         if ((accelKeySpec = (KeySpec *)
1142                                  XtMalloc (sizeof (KeySpec ))) == NULL)
1143                         /* Handle insufficent memory */
1144                         {
1145                             MWarning (((char *)GETMESSAGE(48, 6, "Insufficient memory for menu %s\n")),
1146                                       menuName);
1147                             menuSpec->currentContext &= ~CR_MENU_MARK;
1148                             return (NULL);
1149                         }
1150
1151                         accelKeySpec->state = menuItem->accelState;
1152                         accelKeySpec->keycode = menuItem->accelKeyCode;
1153                         accelKeySpec->context = topMenuSpec->accelContext;
1154                         accelKeySpec->subContext = 0;
1155                         accelKeySpec->wmFunction = menuItem->wmFunction;
1156                         accelKeySpec->wmFuncArgs = menuItem->wmFuncArgs;
1157                         accelKeySpec->nextKeySpec = topMenuSpec->accelKeySpecs;
1158                         topMenuSpec->accelKeySpecs = accelKeySpec;
1159                     }
1160
1161                     children[n] = XmCreatePushButtonGadget (menuW, 
1162                                       PUSH_BTN_NAME, (ArgList) args, i);
1163
1164                     /* 
1165                      * Set sensitivity.  Initially we only consider the context
1166                      * of the top level menupane.
1167                      */
1168
1169                     if (menuItem->greyedContext & topMenuSpec->currentContext)
1170                     /* insensitive button in this context*/
1171                     {
1172                         XtSetSensitive (children[n], FALSE);
1173                     }
1174                     else
1175                     /* sensitive button in this context*/
1176                     {
1177                         XtSetSensitive (children[n], TRUE);
1178                     }
1179
1180                     /*
1181                      * If necessary, fill a menuButtons element for this 
1182                      * PushButton.  Malloc or Realloc the array if necessary.
1183                      */
1184                     if ((menuItem->greyedContext) || (menuItem->mgtMask))
1185                     {
1186                         if (!SavePBInfo (topMenuSpec, menuItem, children[n]))
1187                         {
1188                             MWarning(((char *)GETMESSAGE(48, 7, "Insufficient memory for menu %s\n")),
1189                                        menuName);
1190                             menuSpec->currentContext &= ~CR_MENU_MARK;
1191                             return (NULL);
1192                         }
1193                     }
1194
1195                     /*
1196                      * Set up the function callback.
1197                      * A following title will have both separators.
1198                      */
1199
1200                     XtAddCallback (children[n], XmNactivateCallback,
1201                             (XtCallbackProc)ActivateCallback, 
1202                             (XtPointer) menuItem);
1203
1204                     fUseTitleSep = TRUE;
1205                 }
1206             }
1207         }
1208
1209         /*
1210          * Increment the children array count if we actually
1211          * created a new child.
1212          */
1213         n++;
1214
1215         /*
1216          * Next menu item:  handle custom items and full children[]. 
1217          */
1218         menuItem = menuItem->nextMenuItem;
1219         if ((menuItem == NULL) && (moreMenuItems != NULL))
1220         {
1221             menuSpec->menuItems = menuItem = moreMenuItems;
1222             moreMenuItems = NULL;
1223         }
1224         if (n >= CHILDREN_CACHE - 2)  /* leave room for title separators */
1225         {
1226             XtManageChildren (children, n);
1227             n = 0;
1228         }
1229     }
1230
1231     if (n > 0)
1232     {
1233         XtManageChildren (children, n);
1234     }
1235
1236     /*
1237      * Get the initial height of the top level menu pane shell.
1238      * The actual height will change according to clientFunctions.
1239      */
1240     if (fTopLevelPane)
1241     {
1242         i = 0;
1243         XtSetArg (args[i], XtNheight, &menuHeight);  i++;
1244         XtGetValues (menuW, (ArgList)args, i);
1245         topMenuSpec->height = (unsigned int) menuHeight;
1246     }
1247
1248 #ifndef IBM_151913
1249     /*
1250      * Check if the menu that's been created is higher than the screen.
1251      * If it is, force it to wrap.  Taken straight from the 1.1 fix.
1252     */
1253
1254     i = 0;
1255     XtSetArg (args[i], XtNheight, &menuHeight);  i++;
1256     XtGetValues (menuW, (ArgList)args, i);
1257     scr = XtScreen (menuW);
1258     if (menuHeight > (Dimension)scr->height) {
1259         i = 0;
1260         XtSetArg (args[i], XmNresizeHeight, (XtArgVal) FALSE); i++;
1261         XtSetArg (args[i], XmNpacking, (XtArgVal) XmPACK_TIGHT); i++;
1262         XtSetArg (args[i], XmNorientation, (XtArgVal) XmVERTICAL); i++;
1263         XtSetArg (args[i], XmNheight, scr->height); i++;
1264         XtSetValues (menuW, (ArgList)args, i);
1265     }
1266 #endif  /* IBM_151913 */
1267
1268     /* free the string that may have been created earlier. */
1269     for (sPtr = stringsToFree; sPtr != NULL; )
1270       {
1271          stringsToFree = stringsToFree->next;
1272          XmStringFree(sPtr->string);
1273          XtFree((char *)sPtr);
1274          sPtr = stringsToFree;
1275       }
1276
1277
1278     /* Unmark the menu specification and return. */
1279     menuSpec->currentContext &= ~CR_MENU_MARK;
1280     return (menuW);
1281
1282 } /* END OF FUNCTION CreateMenuWidget */
1283
1284
1285 \f
1286 /*************************************<->*************************************
1287  *
1288  *  PostMenu (menuSpec, pCD, x, y, button, newContext, flags, passedInEvent)
1289  *
1290  *
1291  *  Description:
1292  *  -----------
1293  *  This function is used to post a menu at a particular location.
1294  *
1295  *
1296  *  Inputs:
1297  *  ------
1298  *  menuSpec =      menu specification
1299  *  pCD =           client data
1300  *  x,y =           position to post the menu if (flags & POST_AT_XY) set
1301  *  button =        button number posting the menu or NoButton (WmGlobal.h) if
1302  *                  posted by a key
1303  *  newContext =    context that the menu is to be posted under.
1304  *  flags =         POST_AT_XY bit set iff x,y are valid, else compute from pCD
1305  *                  POST_TRAVERSAL_ON bit set if set traversal on
1306  * 
1307  *  Outputs:
1308  *  -------
1309  *  menuSpec =        menu specification with modifications
1310  *  wmGD.menuClient = pCD
1311  *  wmGD.menuActive = menuSpec
1312  *
1313  *
1314  *  Comments:
1315  *  --------
1316  *  Accepts x,y only if POST_AT_XY flag bit set.  Otherwise, computes from pCD.
1317  *  Adjusts PushButton sensitivity according to context and function type.
1318  *  Manages/Unmanages PushButtons according to clientFunction resource.
1319  *  Sets traversal on if button==NoButton or POST_TRAVERSAL_ON flag bit set.
1320  * 
1321  *************************************<->***********************************/
1322
1323 void PostMenu (MenuSpec *menuSpec, ClientData *pCD, int x, int y, unsigned int button, Context newContext, long flags, XEvent *passedInEvent)
1324 {
1325     int              i;
1326     Arg              args[3];
1327     unsigned int     whichButton;
1328     Dimension        menuHeight;
1329     XButtonPressedEvent event;
1330     Window           saveWindow;
1331     Display          *saveDisplay;
1332     
1333     if ((menuSpec == NULL) || (menuSpec->menuWidget == NULL))
1334     {
1335         return;
1336     }
1337
1338
1339     /* 
1340      * Don't post a menu from an icon in the iconbox if the
1341      * icon is not visible
1342      */
1343     if((newContext == F_SUBCONTEXT_IB_WICON ||
1344        newContext == F_SUBCONTEXT_IB_IICON) &&
1345        (!(IconVisible(pCD))))
1346     {
1347         return;
1348     }
1349
1350     /*
1351      * Set grabContext to be used in GrabWin when no event is passed
1352      * to GrabWin. 
1353      */
1354
1355     wmGD.grabContext = newContext;
1356
1357     /*
1358      *  Adjust PushButton sensitivity and manage/unmanage status.
1359      *  If the manage status of the system menu has changed, 
1360      *  then get the height of the top level menu pane shell and
1361      *  cache it in its MenuSpec.
1362      * 
1363      *  Also...
1364      *  Adjust the tear off control. If we are posting this menu from
1365      *  a client then force the tear off to be disabled. NOTE: This must
1366      *  be done after wmGD.menuClient has been set.
1367      *  Since turning off the tear-off control could result in a height
1368      *  change, we may need to remeasure things. (CR 9316)
1369      */
1370     
1371     if(pCD && pCD->clientFlags & ICON_BOX)
1372     {
1373         newContext |= F_CONTEXT_ICONBOX;
1374     }
1375
1376
1377     if (AdjustPBs (menuSpec, pCD, newContext))
1378     {
1379         i = 0;
1380         XtSetArg (args[i], XtNheight, &menuHeight);  i++;
1381         XtGetValues (menuSpec->menuWidget, (ArgList)args, i);
1382         menuSpec->height = (unsigned int) menuHeight;
1383     }
1384     menuSpec->currentContext = newContext;
1385
1386     /*
1387      *  Adjust the whichButton resource if necessary.
1388      *  Use SELECT_BUTTON for NoButton.
1389      */
1390
1391     whichButton = (button == NoButton) ? SELECT_BUTTON : button;
1392     if (whichButton != menuSpec->whichButton)
1393     {
1394         i = 0;
1395         XtSetArg (args[i], XmNwhichButton, (XtArgVal) whichButton); i++;
1396         XtSetValues (menuSpec->menuWidget, args, i);
1397         menuSpec->whichButton = whichButton;
1398     }
1399
1400     /*
1401      *  Determine the position of the popup menu.
1402      *  Compute position if necessary (system menu).
1403      */
1404
1405     if (pCD && !(flags & POST_AT_XY))
1406     /* compute the position */
1407     {
1408         GetSystemMenuPosition (pCD, &x, &y, menuSpec->height, newContext);
1409     }
1410
1411     event.x_root = x;
1412     event.y_root = y;
1413     XmMenuPosition (menuSpec->menuWidget, &event);
1414
1415     wmGD.menuClient = pCD;
1416     wmGD.menuActive = menuSpec;   /* set to NULL within UnmapCallback() */
1417
1418     /* 
1419      * Post the menu by managing its top-level RowColumn.
1420      *
1421      * First dispatch the event to set the time stamp in the toolkit
1422      */
1423
1424     if(passedInEvent)
1425     {
1426         saveWindow = passedInEvent->xany.window;
1427         saveDisplay = passedInEvent->xany.display;
1428         passedInEvent->xany.window = 0;
1429         passedInEvent->xany.display = XtDisplay(menuSpec->menuWidget);
1430
1431         XtDispatchEvent(passedInEvent);
1432         passedInEvent->xany.window = saveWindow;
1433         passedInEvent->xany.display = saveDisplay;
1434
1435         /* If menu posted by ButtonPress/ButtonRelease, release grabs. */
1436         if ((passedInEvent->type == ButtonPress) ||
1437             (passedInEvent->type == ButtonRelease))
1438             XUngrabPointer(passedInEvent->xany.display,
1439                            passedInEvent->xbutton.time);
1440     }
1441     
1442 #ifndef ALTERNATE_POSTMENU
1443
1444     XtManageChild (menuSpec->menuWidget);
1445
1446 #else
1447     if (flags & POST_STICKY)
1448     {
1449         _XmSetPopupMenuClick(menuSpec->menuWidget, True);
1450     }
1451     else
1452     {
1453         _XmSetPopupMenuClick(menuSpec->menuWidget, False);
1454     }
1455
1456     /* 
1457      * Post the menu by calling the convenience routine that verifies
1458      * the button event, updates the Xt timestamp, and finally manages
1459      * the pane.
1460      */
1461
1462     _XmPostPopupMenu( menuSpec->menuWidget, passedInEvent);
1463 #endif
1464
1465
1466     /*
1467      *  set the traversal state.
1468      */
1469
1470     if ((button == NoButton) || (flags & POST_TRAVERSAL_ON))
1471     /* turn traversal on */
1472     {
1473         TraversalOn (menuSpec);
1474     }
1475     else
1476     /* turn traversal off */
1477     {
1478         TraversalOff (menuSpec);
1479     }
1480
1481 } /* END OF FUNCTION PostMenu */
1482
1483
1484 \f
1485 /*************************************<->*************************************
1486  *
1487  *  UnpostMenu (menuSpec)
1488  *
1489  *
1490  *  Description:
1491  *  -----------
1492  *  This function is used to unpost a menu.
1493  *
1494  *
1495  *  Inputs:
1496  *  ------
1497  *  menuSpec =      menu specification
1498  * 
1499  *  Outputs:
1500  *  -------
1501  *  None.
1502  *
1503  *
1504  *  Comments:
1505  *  --------
1506  *  wmGD.menuActive and wmGD.menuUnpostKey are set to NULL within 
1507  *  UnmapCallback.
1508  * 
1509  *************************************<->***********************************/
1510
1511 void UnpostMenu (MenuSpec *menuSpec)
1512 {
1513     if (menuSpec && (menuSpec->menuWidget))
1514     /* 
1515      * Unpost the menu by unmanaging its top-level RowColumn.
1516      */
1517     {
1518         XtUnmanageChild (menuSpec->menuWidget);
1519 #ifndef OLD_COLORMAP
1520         ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
1521 #endif
1522     }
1523
1524 } /* END OF FUNCTION UnpostMenu */
1525
1526
1527 \f
1528 /*************************************<->*************************************
1529  *
1530  *  ActivateCallback (w, client_data, call_data)
1531  *
1532  *
1533  *  Description:
1534  *  -----------
1535  *  This function is called whenever a menu item is selected.
1536  *
1537  *
1538  *  Inputs:
1539  *  ------
1540  *  w =               menubuttonWidget
1541  *  client_data =     pointer to menu button's MenuItem structure
1542  *  call_data =       not used
1543  *  wmGD.menuClient = pointer to client's ClientData structure
1544  *
1545  * 
1546  *  Outputs:
1547  *  -------
1548  *  None.
1549  *
1550  *
1551  *  Comments:
1552  *  --------
1553  *  None.
1554  * 
1555  *************************************<->***********************************/
1556
1557 void ActivateCallback (Widget w, caddr_t client_data, caddr_t call_data)
1558 {
1559     WmScreenData *pSD;
1560
1561     /* set active screen */
1562     pSD = GetScreenForWindow (XtWindow(w));
1563     if (pSD) SetActiveScreen (pSD);
1564
1565     ((MenuItem *)client_data)->wmFunction (
1566                 ((MenuItem *)client_data)->wmFuncArgs, wmGD.menuClient, NULL);
1567
1568 } /* END OF FUNCTION ActivateCallback */
1569
1570
1571 \f
1572 /*************************************<->*************************************
1573  *
1574  *  UnmapCallback (w, client_data, call_data)
1575  *
1576  *
1577  *  Description:
1578  *  -----------
1579  *  This function is called whenever a toplevel RowColumn is unmapped.
1580  *
1581  *
1582  *  Inputs:
1583  *  ------
1584  *  w =
1585  *  client_data =       not used
1586  *  call_data =         not used
1587  *  wmGD.gadgetClient = last client with depressed client
1588  *
1589  * 
1590  *  Outputs:
1591  *  -------
1592  *  wmGD.menuActive = NULL
1593  *  wmGD.menuUnpostKeySpec = NULL
1594  *  wmGD.checkHotspot = FALSE
1595  *
1596  *
1597  *  Comments:
1598  *  --------
1599  *  None.
1600  * 
1601  *************************************<->***********************************/
1602
1603 static void UnmapCallback (Widget w, XtPointer client_data,
1604                            XtPointer call_data)
1605 {
1606     wmGD.menuActive = NULL;
1607     wmGD.menuUnpostKeySpec = NULL;
1608     wmGD.checkHotspot = FALSE;
1609
1610     if (wmGD.gadgetClient) 
1611     {
1612         PopGadgetOut(wmGD.gadgetClient, FRAME_SYSTEM);
1613     }
1614
1615 #ifndef OLD_COLORMAP
1616     ForceColormapFocus (ACTIVE_PSD, ACTIVE_PSD->colormapFocus);
1617 #endif
1618     PullExposureEvents();
1619
1620 } /* END OF FUNCTION UnmapCallback */
1621
1622 \f
1623 /*************************************<->*************************************
1624  *
1625  *  MWarning (message)
1626  *
1627  *
1628  *  Description:
1629  *  -----------
1630  *  This function lists a message to stderr.
1631  *
1632  *
1633  *  Inputs:
1634  *  ------
1635  *  format  = pointer to a format string
1636  *  message = pointer to a message string
1637  * 
1638  *************************************<->***********************************/
1639
1640 void MWarning (char *format, char *message)
1641 {
1642
1643     if (strlen(format) + strlen(message)  <  (size_t) MAXWMPATH)
1644       {
1645          char pch[MAXWMPATH+1];
1646
1647          sprintf (pch, format, message);
1648          Warning (pch);
1649       }
1650
1651 } /* END OF FUNCTION MWarning */
1652
1653
1654
1655 /*************************************<->*************************************
1656  *
1657  *  TraversalOff (menuSpec)
1658  *
1659  *
1660  *  Description:
1661  *  -----------
1662  *  This function turns menu traversal off.
1663  *
1664  *
1665  *  Inputs:
1666  *  ------
1667  *  menuSpec = menu specification
1668  *
1669  * 
1670  *  Outputs:
1671  *  -------
1672  *  None.
1673  *
1674  *
1675  *  Comments:
1676  *  --------
1677  *  None.
1678  * 
1679  *************************************<->***********************************/
1680
1681 void TraversalOff (MenuSpec *menuSpec)
1682 {
1683     if (menuSpec && (menuSpec->menuWidget))
1684     {
1685         /* function pointer */
1686         (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
1687                                        ->row_column_class.menuProcedures) 
1688                 /* argument list */
1689                (XmMENU_TRAVERSAL, menuSpec->menuWidget, False, NULL, NULL);
1690     }
1691
1692 } /* END OF FUNCTION TraversalOff */
1693
1694
1695 \f
1696 /*************************************<->*************************************
1697  *
1698  *  TraversalOn (menuSpec)
1699  *
1700  *
1701  *  Description:
1702  *  -----------
1703  *  This function turns menu traversal on.
1704  *
1705  *
1706  *  Inputs:
1707  *  ------
1708  *  menuSpec = menu specification
1709  *
1710  * 
1711  *  Outputs:
1712  *  -------
1713  *  None.
1714  *
1715  *
1716  *  Comments:
1717  *  --------
1718  *  None.
1719  * 
1720  *************************************<->***********************************/
1721
1722 void TraversalOn (MenuSpec *menuSpec)
1723 {
1724
1725     if (menuSpec && (menuSpec->menuWidget))
1726     {
1727         /* function pointer */
1728         (*((XmRowColumnWidgetClass) XtClass (menuSpec->menuWidget))
1729                                    ->row_column_class.menuProcedures) 
1730                 /*argument list */
1731                (XmMENU_TRAVERSAL, menuSpec->menuWidget, True, NULL, NULL);
1732     }
1733
1734 } /* END OF FUNCTION TraversalOn */
1735
1736
1737 \f
1738 /*************************************<->*************************************
1739  *
1740  *  FreeCustomMenuSpec (menuSpec)
1741  *
1742  *
1743  *  Description:
1744  *  -----------
1745  *  This procedure destroys a custom MenuSpec structure and its associated 
1746  *  menu widget, menuItems list, menuButtons array, and menu accelerator list.
1747  *
1748  *
1749  *  Inputs:
1750  *  ------
1751  *  menuSpec = MenuSpec structure
1752  *
1753  * 
1754  *  Outputs:
1755  *  -------
1756  *  None.
1757  *
1758  *
1759  *  Comments:
1760  *  --------
1761  *  Assumes that a MenuSpec is custom iff its name is NULL.
1762  *
1763  *  Assumes that ParseWmFuncStr() has parsed a menu item's function
1764  *  argument only for F_Exec and F_Menu.  If it is used for other functions,
1765  *  be sure to include them here!
1766  * 
1767  *************************************<->***********************************/
1768
1769 void FreeCustomMenuSpec (MenuSpec *menuSpec)
1770 {
1771     MenuItem    *menuItem;
1772     MenuItem    *nextMenuItem;
1773     KeySpec     *accelKeySpec;
1774     KeySpec     *nextAccelKeySpec;
1775
1776     if ((menuSpec == NULL) || (menuSpec->name != NULL))
1777     /* we only destroy custom menus! */
1778     {
1779         return;
1780     }
1781   
1782     /*
1783      * Fix for CR 5450 - If the custom menu is the same as wmGD.menuActive, call
1784      *                   the UnmapCallback directly to clean things up.  Since
1785      *                   the menu is going to be destroyed, this callback will
1786      *                   not get called, leaving MWM in a failure state.
1787      */
1788      if (wmGD.menuActive == menuSpec)
1789        UnmapCallback((Widget)NULL, (caddr_t)NULL, (caddr_t)NULL);
1790     /*
1791      * End fix for CR 5450
1792      */
1793  
1794     menuItem = menuSpec->menuItems;
1795     while (menuItem)
1796     {
1797         nextMenuItem = menuItem->nextMenuItem;
1798         FreeMenuItem (menuItem);
1799         menuItem = nextMenuItem;
1800     }
1801
1802     if (menuSpec->menuButtons)
1803     {
1804         XtFree ((char *)menuSpec->menuButtons);
1805     }
1806
1807     accelKeySpec = menuSpec->accelKeySpecs;
1808     while (accelKeySpec)
1809     {
1810         nextAccelKeySpec = accelKeySpec->nextKeySpec;
1811         XtFree ((char *)accelKeySpec);
1812         accelKeySpec = nextAccelKeySpec;
1813     }
1814
1815     if (menuSpec->menuWidget)
1816     /* destroy all children of the menu's MenuShell parent */
1817     {
1818         XtDestroyWidget (XtParent(menuSpec->menuWidget));
1819     }
1820
1821     XtFree ((char *)menuSpec);
1822
1823 } /* END OF FUNCTION FreeCustomMenuSpec */
1824
1825 \f