1 /*
2  * Motif
3  *
4  * Copyright (c) 1987-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 #ifdef REV_INFO
24 #ifndef lint
25 static char rcsid[] = "$TOG: CascadeB.c /main/27 1999/08/11 14:26:35 mgreess $"
26 #endif
27 #endif
28 
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32 
33 
34 #include "XmI.h"		/* ShellP.h doesn't define externalref. */
35 #include <X11/ShellP.h>
36 #include <X11/keysymdef.h>
37 #include <Xm/BaseClassP.h>
38 #include <Xm/DisplayP.h>
39 #include <Xm/DrawP.h>
40 #include <Xm/LabelGP.h>
41 #include <Xm/LabelP.h>
42 #include <Xm/MenuShellP.h>
43 #include <Xm/MenuStateP.h>
44 #include <Xm/MenuT.h>
45 #include <Xm/RowColumnP.h>
46 #include <Xm/TearOffP.h>
47 #include <Xm/TraitP.h>
48 #include <Xm/TransltnsP.h>
49 #include <Xm/VaSimpleP.h>
50 #include "CascadeBI.h"
51 #include "CascadeBGI.h"
52 #include "LabelI.h"
53 #include "GadgetUtiI.h"
54 #include "MenuStateI.h"
55 #include "MenuProcI.h"
56 #include "MenuUtilI.h"
57 #include "MessagesI.h"
58 #include "PrimitiveI.h"
59 #include "RCMenuI.h"
60 #include "TearOffI.h"
61 #include "TraversalI.h"
62 #include "UniqueEvnI.h"
63 
64 #define FIX_1509
65 
66 #define CASCADE_PIX_SPACE    4	 /* pixels between label and bit map */
67 #define MAP_DELAY_DEFAULT   180
68 #define EVENTS              ((unsigned int) (ButtonPressMask | \
69 			     ButtonReleaseMask | EnterWindowMask | \
70 			     LeaveWindowMask))
71 
72 
73 #define WRONGPARENT	_XmMMsgCascadeB_0000
74 #define WRONGSUBMENU	_XmMMsgCascadeB_0001
75 #define WRONGMAPDELAY	_XmMMsgCascadeB_0002
76 
77 
78 /********    Static Function Declarations    ********/
79 
80 static void ClassInitialize( void ) ;
81 static void ClassPartInitialize(
82                         WidgetClass wc) ;
83 static void BorderHighlight(
84                         Widget wid) ;
85 static void BorderUnhighlight(
86                         Widget wid) ;
87 static void DrawShadow(
88                         register XmCascadeButtonWidget cb) ;
89 static void DrawCascade(
90                         register XmCascadeButtonWidget cb) ;
91 static void Redisplay(
92                         register Widget cb,
93                         XEvent *event,
94                         Region region) ;
95 static void Arm(
96                         register XmCascadeButtonWidget cb) ;
97 static void ArmAndPost(
98                         XmCascadeButtonWidget cb,
99                         XEvent *event) ;
100 static void ArmAndActivate(
101                         Widget wid,
102                         XEvent *event,
103                         String *params,
104                         Cardinal *num_params) ;
105 static void Disarm(
106                         register XmCascadeButtonWidget cb,
107 #if NeedWidePrototypes
108                         int unpost) ;
109 #else
110                         Boolean unpost) ;
111 #endif /* NeedWidePrototypes */
112 static void PostTimeout(
113                         XtPointer closure,
114                         XtIntervalId *id) ;
115 static void DelayedArm(
116                         Widget wid,
117                         XEvent *event,
118                         String *param,
119                         Cardinal *num_param) ;
120 static void CheckDisarm(
121                         Widget wid,
122                         XEvent *event,
123                         String *param,
124                         Cardinal *num_param) ;
125 static void StartDrag(
126                         Widget wid,
127                         XEvent *event,
128                         String *param,
129                         Cardinal *num_param) ;
130 static void Select(
131                         register XmCascadeButtonWidget cb,
132                         XEvent *event,
133 #if NeedWidePrototypes
134                         int doCascade) ;
135 #else
136                         Boolean doCascade) ;
137 #endif /* NeedWidePrototypes */
138 static void DoSelect(
139                         Widget wid,
140                         XEvent *event,
141                         String *param,
142                         Cardinal *num_param) ;
143 static void KeySelect(
144                         Widget wid,
145                         XEvent *event,
146                         String *param,
147                         Cardinal *num_param) ;
148 static void MenuBarSelect(
149                         Widget wid,
150                         XEvent *event,
151                         String *param,
152                         Cardinal *num_param) ;
153 static void MenuBarEnter(
154                         Widget wid,
155                         XEvent *event,
156                         String *param,
157                         Cardinal *num_param) ;
158 static void MenuBarLeave(
159                         Widget wid,
160                         XEvent *event,
161                         String *param,
162                         Cardinal *num_param) ;
163 static void CleanupMenuBar(
164                         Widget wid,
165                         XEvent *event,
166                         String *param,
167                         Cardinal *num_param) ;
168 static void PopdownGrandChildren(
169                         XmRowColumnWidget rowcol) ;
170 static void Cascading(
171                         Widget w,
172                         XEvent *event) ;
173 static void Popup(
174                         Widget cb,
175                         XEvent *event) ;
176 static void size_cascade(
177                         XmCascadeButtonWidget cascadebtn) ;
178 static void position_cascade(
179                         XmCascadeButtonWidget cascadebtn) ;
180 static void setup_cascade(
181                         XmCascadeButtonWidget cascadebtn,
182 #if NeedWidePrototypes
183                         int adjustWidth,
184                         int adjustHeight) ;
185 #else
186                         Boolean adjustWidth,
187                         Boolean adjustHeight) ;
188 #endif /* NeedWidePrototypes */
189 static void Destroy(
190                         Widget wid) ;
191 static void Resize(
192                         Widget cb) ;
193 static Boolean SetValuesPrehook(
194 			Widget cw,
195                         Widget rw,
196                         Widget nw,
197                         ArgList args,
198                         Cardinal *num_args) ;
199 static Boolean SetValues(
200                         Widget cw,
201                         Widget rw,
202                         Widget nw,
203                         ArgList args,
204                         Cardinal *num_args) ;
205 static void InitializePrehook(
206                         Widget req,
207                         Widget new_w,
208                         ArgList args,
209                         Cardinal *num_args) ;
210 static void InitializePosthook(
211                         Widget req,
212                         Widget new_w,
213                         ArgList args,
214                         Cardinal *num_args) ;
215 static void GetArmGC(
216                         XmCascadeButtonWidget cb) ;
217 static void GetBackgroundGC(
218                         XmCascadeButtonWidget cb) ;
219 static void Initialize(
220                         Widget w_req,
221                         Widget w_new,
222                         ArgList args,
223                         Cardinal *num_args) ;
224 
225 /********    End Static Function Declarations    ********/
226 
227 
228 /*
229  * event translation tables for cascadebutton.  There are different
230  * ones for the different menus a cascadebutton widget can appear in
231  */
232 
233 static XtTranslations menubar_events_parsed;
234 
235 #define menubar_events	_XmCascadeB_menubar_events
236 
237 static XtTranslations p_events_parsed;
238 
239 #define p_events 	_XmCascadeB_p_events
240 
241 static XtActionsRec action_table [] =
242 {
243     {"DelayedArm",	DelayedArm},
244     {"CheckDisarm",	CheckDisarm},
245     {"StartDrag",	StartDrag},
246     {"DoSelect", 	DoSelect},
247     {"KeySelect",	KeySelect},
248     {"MenuBarSelect",	MenuBarSelect},
249     {"MenuBarEnter",	MenuBarEnter},
250     {"MenuBarLeave",	MenuBarLeave},
251     {"MenuButtonTakeFocus", _XmMenuButtonTakeFocus },
252     {"MenuButtonTakeFocusUp", _XmMenuButtonTakeFocusUp },
253     {"CleanupMenuBar",	CleanupMenuBar},
254     {"Help",		_XmCBHelp},
255 };
256 
257 
258 static XtResource resources[] =
259 {
260     {	XmNactivateCallback,
261 	XmCCallback,
262 	XmRCallback,
263 	sizeof (XtCallbackList),
264 	XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.activate_callback),
265 	XmRCallback,
266 	NULL
267     },
268     {	XmNcascadingCallback,
269 	XmCCallback,
270 	XmRCallback,
271 	sizeof (XtCallbackList),
272 	XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.cascade_callback),
273 	XmRCallback,
274 	NULL
275     },
276     {	XmNsubMenuId,
277 	XmCMenuWidget,				/* submenu */
278 	XmRMenuWidget,
279 	sizeof (Widget),
280 	XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.submenu),
281 	XmRMenuWidget,
282 	(XtPointer) NULL
283     },
284     {	XmNcascadePixmap,
285 	XmCPixmap,
286 	XmRDynamicPixmap,
287 	sizeof(Pixmap),
288 	XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.cascade_pixmap),
289 	XmRImmediate,
290 	(XtPointer) XmUNSPECIFIED_PIXMAP
291     },
292     {   XmNmappingDelay,
293 	XmCMappingDelay,
294 	XmRInt,
295 	sizeof (int),
296 	XtOffsetOf( struct _XmCascadeButtonRec, cascade_button.map_delay),
297 	XmRImmediate,
298 	(XtPointer) MAP_DELAY_DEFAULT
299     },
300     {
301         XmNshadowThickness,
302         XmCShadowThickness,
303         XmRHorizontalDimension,
304         sizeof (Dimension),
305         XtOffsetOf( struct _XmCascadeButtonRec, primitive.shadow_thickness),
306         XmRCallProc,
307         (XtPointer) _XmSetThickness
308     },
309     {
310 	XmNtraversalOn,
311 	XmCTraversalOn,
312 	XmRBoolean,
313 	sizeof(Boolean),
314 	XtOffsetOf( struct _XmPrimitiveRec, primitive.traversal_on),
315 	XmRImmediate,
316 	(XtPointer) True
317     },
318     {
319         XmNhighlightThickness,
320         XmCHighlightThickness,
321         XmRHorizontalDimension,
322         sizeof (Dimension),
323         XtOffsetOf( struct _XmPrimitiveRec, primitive.highlight_thickness),
324         XmRCallProc,
325         (XtPointer) _XmSetThickness
326     },
327     {
328  	XmNmarginWidth,
329 	XmCMarginWidth,
330 	XmRHorizontalDimension,
331 	sizeof (Dimension),
332 	XtOffsetOf( struct _XmLabelRec, label.margin_width),
333 	XmRImmediate,
334 	(XtPointer) XmINVALID_DIMENSION
335     },
336 };
337 
338 /*
339  * static initialization of the cascade button widget class record,
340  * must do each field
341  */
342 static XmBaseClassExtRec       cascadeBBaseClassExtRec = {
343     NULL,                                     /* Next extension       */
344     NULLQUARK,                                /* record type XmQmotif */
345     XmBaseClassExtVersion,                    /* version              */
346     sizeof(XmBaseClassExtRec),                /* size                 */
347     InitializePrehook,                        /* initialize prehook   */
348     SetValuesPrehook,			      /* set_values prehook   */
349     InitializePosthook,                       /* initialize posthook  */
350     XmInheritSetValuesPosthook,               /* set_values posthook  */
351     XmInheritClass,                           /* secondary class      */
352     XmInheritSecObjectCreate,                 /* creation proc        */
353     XmInheritGetSecResData,                   /* getSecResData        */
354     {0},                                      /* fast subclass        */
355     XmInheritGetValuesPrehook,                /* get_values prehook   */
356     XmInheritGetValuesPosthook,               /* get_values posthook  */
357     NULL,                                     /* classPartInitPrehook */
358     NULL,                                     /* classPartInitPosthook*/
359     NULL,                                     /* ext_resources        */
360     NULL,                                     /* compiled_ext_resources*/
361     0,                                        /* num_ext_resources    */
362     FALSE,                                    /* use_sub_resources    */
363     XmInheritWidgetNavigable,                 /* widgetNavigable      */
364     XmInheritFocusChange,                     /* focusChange          */
365 };
366 
367 
368 externaldef(xmcascadebuttonclassrec) XmCascadeButtonClassRec xmCascadeButtonClassRec =
369 {
370     {			/* core class record */
371 	(WidgetClass) &xmLabelClassRec,		/* superclass ptr	*/
372 	"XmCascadeButton",			/* class_name	*/
373 	sizeof (XmCascadeButtonWidgetRec),	/* size of Pulldown widget */
374 	ClassInitialize,			/* class init proc */
375 	ClassPartInitialize,			/* chained class init */
376 	FALSE,					/* class is not init'ed */
377 	Initialize,				/* widget init proc */
378 	NULL,					/* init_hook proc */
379     	XtInheritRealize,       		/* widget realize proc */
380     	action_table,				/* class action table */
381     	XtNumber (action_table),
382 	resources,				/* this class's resource list*/
383 	XtNumber (resources),			/*  "	  " resource_count */
384     	NULLQUARK,				/* xrm_class	        */
385     	TRUE,					/* compress motion */
386     	XtExposeCompressMaximal,		/* compress exposure */
387 	TRUE,					/* compress enter-leave */
388    	FALSE,					/* no VisibilityNotify */
389 	Destroy,				/* class destroy proc */
390 	Resize,					/* class resize proc */
391 	Redisplay,				/* expose proc */
392 	SetValues,				/* set_value proc */
393 	NULL,					/* set_value_hook proc */
394 	XtInheritSetValuesAlmost,		/* set_value_almost proc */
395 	NULL,					/* get_values_hook */
396 	NULL,					/* class accept focus proc */
397 	XtVersion,				/* current version */
398     	NULL,					/* callback offset list */
399     	NULL,					/* default translation table */
400 						  /* this is manually set */
401 	XtInheritQueryGeometry,			/* query geo proc */
402 	NULL,				        /* display accelerator*/
403 	(XtPointer)&cascadeBBaseClassExtRec,	/* extension */
404     },
405 	{
406 			/* Primitive Class record */
407 	BorderHighlight,			/* border_highlight */
408 	BorderUnhighlight,			/* border_uhighlight */
409 	XtInheritTranslations,		        /* translations */
410 	ArmAndActivate,				/* arm & activate */
411 	NULL,					/* get resources */
412 	0,					/* num get_resources */
413 	NULL,	                                /* extension */
414 	},
415     {			/* Label Class record */
416 	XmInheritWidgetProc,		        /* set override callback */
417 	XmInheritMenuProc,		        /* menu procedures       */
418 	XtInheritTranslations,		        /* menu traversal xlation */
419 	NULL,					/* extension */
420     },
421     {			/* cascade_button class record */
422         NULL,					/* extension */
423     }
424 };
425 
426 /*
427  * now make a public symbol that points to this class record
428  */
429 
430 externaldef(xmcascadebuttonwidgetclass) WidgetClass xmCascadeButtonWidgetClass = (WidgetClass) &xmCascadeButtonClassRec;
431 
432 /* Menu Savvy trait record */
433 static XmMenuSavvyTraitRec MenuSavvyRecord = {
434     /* version: */
435     -1,
436     NULL,
437     NULL,
438     NULL,
439     _XmCBNameActivate,
440 };
441 
442 
443 /*
444  * parse the translation tables for the different menutypes
445  */
446 static void
ClassInitialize(void)447 ClassInitialize( void )
448 {
449     menubar_events_parsed  = XtParseTranslationTable (menubar_events);
450     p_events_parsed	   = XtParseTranslationTable (p_events);
451 
452    /* set up base class extension quark */
453    cascadeBBaseClassExtRec.record_type = XmQmotif;
454 }
455 
456 /*
457  * set up fast subclassing
458  */
459 
460 static void
ClassPartInitialize(WidgetClass wc)461 ClassPartInitialize(
462         WidgetClass wc )
463 {
464   _XmFastSubclassInit (wc, XmCASCADE_BUTTON_BIT);
465 
466   /* Install the menu savvy trait record,  copying fields from XmLabel */
467   _XmLabelCloneMenuSavvy (wc, &MenuSavvyRecord);
468 }
469 
470 
471 /*
472  * The button is armed (does not pop up submenus).
473  */
474 static void
BorderHighlight(Widget wid)475 BorderHighlight(
476         Widget wid )
477 {
478       Arm ((XmCascadeButtonWidget) wid);
479 }
480 
481 
482 /*
483  * The button is disarmed (does not pop down submenus).
484  */
485 static void
BorderUnhighlight(Widget wid)486 BorderUnhighlight(
487         Widget wid )
488 {
489     XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid;
490     Boolean popdown;
491 
492     if (Lab_IsMenupane(cb) &&
493 	(((XmManagerWidget)XtParent(cb))->manager.active_child == wid) &&
494 	CB_Submenu(cb))
495     {
496 	XmMenuShellWidget mshell =
497 	    (XmMenuShellWidget) XtParent(CB_Submenu(cb));
498 
499 	if ((mshell->composite.children[0] == CB_Submenu(cb)) &&
500 	    (XmIsMenuShell(mshell)) &&
501 	    (mshell->shell.popped_up))
502 	{
503 	    popdown = True;
504 	}
505 	else
506 	    popdown = False;
507     }
508     else
509 	popdown = False;
510 
511     Disarm ((XmCascadeButtonWidget) wid, popdown);
512 }
513 
514 
515 /*
516  * Draw the 3D shadow around the widget if its is armed.
517  */
518 static void
DrawShadow(register XmCascadeButtonWidget cb)519 DrawShadow(
520         register XmCascadeButtonWidget cb )
521 {
522   XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay((Widget) cb));
523   Boolean etched_in = dpy -> display.enable_etched_in_menu;
524 
525   if (CB_IsArmed(cb))
526     {
527       if (XtIsRealized((Widget)cb))
528       {
529 	 XmeDrawShadows (XtDisplay (cb), XtWindow (cb),
530 			cb->primitive.top_shadow_GC,
531 			cb->primitive.bottom_shadow_GC,
532 			cb->primitive.highlight_thickness,
533 			cb->primitive.highlight_thickness,
534 			cb->core.width - 2 *
535 			cb->primitive.highlight_thickness,
536 			cb->core.height - 2 *
537 			cb->primitive.highlight_thickness,
538 			cb->primitive.shadow_thickness,
539 			etched_in ? XmSHADOW_IN : XmSHADOW_OUT);
540       }
541    }
542 }
543 
544 
545 static void
DrawCascade(register XmCascadeButtonWidget cb)546 DrawCascade(
547         register XmCascadeButtonWidget cb )
548 {
549    if ((CB_HasCascade(cb)) && (CB_Cascade_width(cb) != 0))
550    {
551       Pixmap pixmap ;
552       int depth ;
553 
554       pixmap = CB_IsArmed(cb) &&
555 	  (CB_ArmedPixmap(cb) != XmUNSPECIFIED_PIXMAP) ?
556 	      CB_ArmedPixmap(cb) : CB_CascadePixmap(cb) ;
557 
558       XmeGetPixmapData(XtScreen(cb),
559 		       pixmap,
560 		       NULL,
561 		       &depth,
562 		       NULL, NULL,
563 		       NULL, NULL,
564 		       NULL, NULL);
565 
566       if (depth == cb->core.depth)
567 	  XCopyArea (XtDisplay(cb),
568 		     pixmap,
569 		     XtWindow(cb),
570 		     cb->label.normal_GC, 0, 0,
571 		     CB_Cascade_width(cb), CB_Cascade_height(cb),
572 		     CB_Cascade_x(cb), CB_Cascade_y(cb));
573       else
574       if (depth == 1)
575 	  XCopyPlane (XtDisplay(cb),
576 		      pixmap,
577 		      XtWindow(cb),
578 		      cb->label.normal_GC, 0, 0,
579 		      CB_Cascade_width(cb), CB_Cascade_height(cb),
580 		      CB_Cascade_x(cb), CB_Cascade_y(cb), 1);
581 
582   }
583 }
584 
585 /*
586  * redisplay the widget
587  */
588 static void
Redisplay(register Widget cb,XEvent * event,Region region)589 Redisplay(
590         register Widget cb,
591         XEvent *event,
592         Region region )
593 {
594 #ifdef FIX_1395
595     Pixel tmpc;
596 #endif
597     if (XtIsRealized (cb))
598     {
599 	XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(cb));
600 	Boolean etched_in = dpy->display.enable_etched_in_menu;
601 	GC tmpGC = NULL;
602 	XtExposeProc expose;
603 
604 	if (etched_in) {
605 #ifdef FIX_1509
606 		if (CB_IsArmed(cb))
607 		    XFillRectangle(XtDisplay(cb), XtWindow(cb), CB_ArmGC(cb),
608 				   0, 0, cb->core.width, cb->core.height);
609 		else
610 	        XClearArea(XtDisplay(cb), XtWindow(cb),
611 				0, 0, cb->core.width, cb->core.height, False);
612 #else
613 	    XFillRectangle(XtDisplay(cb), XtWindow(cb),
614 		           CB_IsArmed(cb) ? CB_ArmGC(cb) : CB_BackgroundGC(cb),
615 			   0, 0, cb->core.width, cb->core.height);
616 #endif
617 #ifdef USE_XFT
618 	} else if (Lab_MenuType(cb) != XmWORK_AREA) { /* adeed with XFT support */
619         XClearArea(XtDisplay(cb), XtWindow(cb),
620 			0, 0, cb->core.width, cb->core.height, False);
621 #endif
622 	}
623 	if (etched_in && CB_IsArmed(cb)) {
624 	    Pixel junk, select_pix;
625 	    Boolean replaceGC = False;
626 
627 	    XmGetColors(XtScreen(cb), cb->core.colormap,
628 			cb->core.background_pixel,
629 			&junk, &junk, &junk, &select_pix);
630 
631 	    if (select_pix ==
632 		((XmCascadeButtonWidget)cb)->primitive.foreground) {
633 		replaceGC = True;
634 		tmpGC = ((XmCascadeButtonWidget)cb)->label.normal_GC;
635 		((XmCascadeButtonWidget)cb)->label.normal_GC =
636 		    CB_BackgroundGC(cb);
637 	    }
638 #ifdef FIX_1395
639 	    /* 1395:
640 	     By default (if not etched and not armed) window (widget) have
641 	     background color that used to draw widget bg. When widget is
642 	     etched, background selected correctly (as selected color),
643 	     but label exposr method will use default background color
644 	     to fill area bellow lable text. Result is ugly menus.
645 	     We should replace colors before expose from label
646 	     and change it back after repainting.
647 	    */
648             tmpc = cb->core.background_pixel;
649             XSetWindowBackground(XtDisplay(cb), XtWindow(cb), select_pix);
650 #endif
651 
652 	    _XmProcessLock();
653 	    expose = xmLabelClassRec.core_class.expose;
654 	    _XmProcessUnlock();
655 	    (*expose)((Widget) cb, event, region);
656 #ifdef FIX_1395
657 	    /*
658 	     Set correct window background (label is repainted, role back)
659 	    */
660 	    XSetWindowBackground(XtDisplay(cb), XtWindow(cb), tmpc);
661 	    if (cb->core.background_pixmap != XmUNSPECIFIED_PIXMAP)
662 	    	XSetWindowBackgroundPixmap(XtDisplay(cb), XtWindow(cb),
663 	    			cb->core.background_pixmap);
664 #endif
665 
666 	    if (replaceGC)
667 		((XmCascadeButtonWidget)cb)->label.normal_GC = tmpGC;
668 	}
669 	else {
670 
671 	    /* Label class does most of the work */
672 
673 	    _XmProcessLock();
674 	    expose = xmLabelClassRec.core_class.expose;
675 	    _XmProcessUnlock();
676 	    (*expose)(cb, event, region);
677 	}
678 
679 	DrawCascade((XmCascadeButtonWidget) cb);
680 	DrawShadow ((XmCascadeButtonWidget) cb);
681     }
682 }
683 
684 
685 /*
686  * Arming the cascadebutton consists of setting the armed bit
687  * and drawing the 3D shadow.
688  */
689 static void
Arm(register XmCascadeButtonWidget cb)690 Arm(
691         register XmCascadeButtonWidget cb )
692 {
693   if (!CB_IsArmed(cb))
694     {
695       XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(cb));
696       Boolean etched_in = dpy->display.enable_etched_in_menu;
697 
698       CB_SetArmed(cb, TRUE);
699 
700       if (etched_in)
701 	  Redisplay((Widget) cb, NULL, NULL);
702       else {
703 	  DrawCascade(cb);
704 	  DrawShadow (cb);
705       }
706     }
707 
708   XmProcessTraversal((Widget) cb, XmTRAVERSE_CURRENT);
709 }
710 
711 
712 
713 /*
714  * Post the submenu and then arm the button.  The arming is done
715  * second so that the post can be quickly as possible.
716  */
717 static void
ArmAndPost(XmCascadeButtonWidget cb,XEvent * event)718 ArmAndPost(
719         XmCascadeButtonWidget cb,
720         XEvent *event )
721 {
722    if (!CB_IsArmed(cb))
723    {
724       _XmCascadingPopup ((Widget) cb, event, TRUE);
725       Arm (cb);
726    }
727 }
728 
729 
730 
731 
732 /*
733  * class function to cause the cascade button to be armed and selected
734  */
735 /*ARGSUSED*/
736 static void
ArmAndActivate(Widget wid,XEvent * event,String * params,Cardinal * num_params)737 ArmAndActivate(
738         Widget wid,
739         XEvent *event,
740         String *params,		/* unused */
741         Cardinal *num_params )	/* unused */
742 {
743     XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
744     XmRowColumnWidget parent = (XmRowColumnWidget) XtParent(cb);
745     XmMenuSystemTrait menuSTrait;
746     Time _time;
747 
748     /* check if event has been processed  - if it's NULL, override processing */
749     if (event && !_XmIsEventUnique(event))
750        return;
751 
752     _time = _XmGetDefaultTime(wid, event);
753 
754     menuSTrait = (XmMenuSystemTrait)
755       XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
756 
757     if (menuSTrait == NULL) {
758       /* We're in trouble.  This isn't a valid menu that we're
759 	 in and Arm and Activate has been called.  Abort ! */
760       return;
761     }
762 
763     switch (Lab_MenuType (cb))
764     {
765        case XmMENU_BAR:
766        {
767           ShellWidget myShell = NULL;
768 
769           /* Shared menupanes require some additional checks */
770           if (CB_Submenu(cb))
771               myShell = (ShellWidget)XtParent(CB_Submenu(cb));
772 
773           if (myShell &&
774 	      XmIsMenuShell(myShell) &&         /* not torn ?! */
775 	      (myShell->shell.popped_up) &&
776 	      (myShell->composite.children[0] == CB_Submenu(cb)) &&
777 	      (cb == (XmCascadeButtonWidget)RC_CascadeBtn(CB_Submenu(cb))))
778           {
779 	     menuSTrait -> popdown((Widget) parent, event);
780 	     Disarm (cb, FALSE);
781 	  }
782           else
783           {
784              /* call the cascading callbacks first thing */
785              Cascading ((Widget) cb, event);
786 
787 	     /*
788 	      * check if the traversing flag is set true.  This indicates
789 	      * that we are in a traverse and don't want to activate if
790 	      * there is no submenu attached.  Set during KDown in menubar.
791 	      */
792 	     if (CB_Traversing(cb) && !CB_Submenu(cb))
793 		 return;
794 
795              if (! RC_IsArmed (parent))
796 	     {
797 		_XmMenuFocus((Widget) parent, XmMENU_BEGIN, _time);
798 
799 		menuSTrait -> arm((Widget) cb);
800 	     }
801              else
802 	       menuSTrait -> menuBarCleanup((Widget) parent);
803 
804              /* do the select without calling the cascading callbacks again */
805              Select (cb, event, FALSE);
806 
807 	     /* To support menu replay, keep the pointer in sync mode */
808 	     XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
809 
810              if (CB_Submenu(cb))
811              {
812                 /*
813                  * if XmProcessTraversal() fails, it's possible that the pane
814                  * has no traversable children, so reset the focus to the pane.
815                  */
816                 if (!XmProcessTraversal(CB_Submenu(cb), XmTRAVERSE_CURRENT))
817 		{
818 		   /* Must clear focus path first for shared menushells.
819 		    * Otherwise, moving the focus back will have old stale
820 		    * (old) focus_item.
821 		    */
822 		   _XmClearFocusPath(CB_Submenu(cb));
823                    XtSetKeyboardFocus(XtParent(CB_Submenu(cb)), CB_Submenu(cb));
824 		}
825              }
826              else
827              {
828 	        menuSTrait -> disarm((Widget) parent);
829 		_XmMenuFocus(XtParent(cb), XmMENU_END, _time);
830                 XtUngrabPointer( (Widget) cb, _time);
831              }
832           }
833 
834           break;
835        }
836 
837        case XmMENU_PULLDOWN:
838        case XmMENU_POPUP:
839        {
840 	  /* In case the tear off is active but not armed or grabbed */
841 	  menuSTrait -> tearOffArm((Widget) parent);
842           Select (cb, event, TRUE);
843 	  if (CB_Submenu(cb))
844 	  {
845              /*
846               * if XmProcessTraversal() fails, it's possible that the pane
847               * has no traversable children, so reset the focus to the pane.
848               */
849              if (!XmProcessTraversal(CB_Submenu(cb), XmTRAVERSE_CURRENT))
850 	     {
851 		/* Must clear focus path first for shared menushells.
852 		 * Otherwise, moving the focus back will have old stale
853 		 * (old) focus_item.
854 		 */
855 		_XmClearFocusPath(CB_Submenu(cb));
856                 XtSetKeyboardFocus(XtParent(CB_Submenu(cb)), CB_Submenu(cb));
857 	     }
858 	  }
859           break;
860        }
861     }
862     /* Record so spring loaded DispatchEvent() doesn't recall this routine.  */
863     if (event)
864        _XmRecordEvent(event);
865 }
866 
867 
868 /*
869  * disarm the menu.  This may include popping down any submenu that is up or
870  * removing the timeout to post a submenu
871  */
872 static void
Disarm(register XmCascadeButtonWidget cb,int unpost)873 Disarm(
874         register XmCascadeButtonWidget cb,
875 #if NeedWidePrototypes
876         int unpost )
877 #else
878         Boolean unpost )
879 #endif /* NeedWidePrototypes */
880 {
881    Widget rowcol = XtParent (cb);
882 
883    if (CB_IsArmed(cb))
884    {
885       CB_SetArmed(cb, FALSE);
886 
887       /* popdown any posted submenus */
888       if (unpost && RC_PopupPosted(rowcol))
889       {
890 	  (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
891 	     menu_shell_class.popdownEveryone))(RC_PopupPosted(rowcol),NULL,
892 						NULL, NULL);
893       }
894 
895       /* if a delayed arm is pending, remove it */
896       if (cb->cascade_button.timer)
897       {
898          XtRemoveTimeOut (cb->cascade_button.timer);
899          cb->cascade_button.timer = 0;
900       }
901 
902       /* if the shadow is drawn and the menupane is not going down, erase it */
903       if ((! RC_PoppingDown(rowcol)) || RC_TornOff(rowcol))
904       {
905 	 if (XtIsRealized ((Widget)cb))
906 	 {
907 	     /* etched in menu button */
908 	     XmDisplay dpy = (XmDisplay) XmGetXmDisplay(XtDisplay(cb));
909 	     Boolean etched_in = dpy->display.enable_etched_in_menu;
910 
911 	     if (etched_in)
912 		 Redisplay((Widget) cb, NULL, NULL);
913 	     else
914 		 XmeClearBorder (XtDisplay (cb), XtWindow (cb),
915 				 cb->primitive.highlight_thickness,
916 				 cb->primitive.highlight_thickness,
917 				 cb->core.width - 2 *
918 				 cb->primitive.highlight_thickness,
919 				 cb->core.height - 2 *
920 				 cb->primitive.highlight_thickness,
921 				 cb->primitive.shadow_thickness);
922 	 }
923       }
924       DrawCascade(cb);
925    }
926 }
927 
928 
929 /*
930  * called when the post delay timeout occurs.
931  */
932 /*ARGSUSED*/
933 static void
PostTimeout(XtPointer closure,XtIntervalId * id)934 PostTimeout(
935         XtPointer closure,
936         XtIntervalId *id)	/* unused */
937 {
938         XmCascadeButtonWidget cb = (XmCascadeButtonWidget) closure ;
939 
940    if (cb->cascade_button.timer)
941    {
942       cb->cascade_button.timer = 0;
943 
944       _XmCascadingPopup ((Widget) cb, NULL, TRUE);
945    }
946 }
947 
948 
949 /*
950  * set the timer to post the submenu if a leave event does
951  * not occur first.
952  */
953 /*ARGSUSED*/
954 static void
DelayedArm(Widget wid,XEvent * event,String * param,Cardinal * num_param)955 DelayedArm(
956         Widget wid,
957         XEvent *event,
958         String *param,
959         Cardinal *num_param )
960 {
961         XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
962    if ((! CB_IsArmed(cb)) &&
963        (((XmMenuShellWidget) XtParent(XtParent(cb)))->shell.popped_up) &&
964        _XmGetInDragMode((Widget) cb))
965    {
966       if (cb->cascade_button.map_delay <= 0)
967 	 ArmAndPost (cb, event);
968 
969       else
970       {
971  	 /* To fix CR 8172,  the following two lines were reversed.
972 	    Because calling Arm seems to cause a focus change (temporary)
973 	    out of the widget,  the timer was incorrectly removed and
974 	    the menu wouldn't post. */
975          Arm(cb);
976          cb->cascade_button.timer =
977                XtAppAddTimeOut(XtWidgetToApplicationContext( (Widget) cb),
978 			       (unsigned long) cb->cascade_button.map_delay,
979                                                  PostTimeout, (XtPointer) cb) ;
980       }
981    }
982 }
983 
984 
985 /*
986  * if traversal is not on and the mouse
987  * has not entered its cascading submenu, disarm the
988  * cascadebutton.
989  */
990 /*ARGSUSED*/
991 static void
CheckDisarm(Widget wid,XEvent * event,String * param,Cardinal * num_param)992 CheckDisarm(
993         Widget wid,
994         XEvent *event,
995         String *param,
996         Cardinal *num_param )
997 {
998    XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
999    register XmMenuShellWidget submenushell;
1000    XEnterWindowEvent * entEvent = (XEnterWindowEvent *) event;
1001 
1002    if (_XmGetInDragMode((Widget) cb) &&
1003        (/* !ActiveTearOff || */ event->xcrossing.mode == NotifyNormal))
1004    {
1005       if ((CB_IsArmed(cb)) &&
1006           (CB_Submenu(cb)))
1007       {
1008          submenushell = (XmMenuShellWidget) XtParent (CB_Submenu(cb));
1009 
1010          if (submenushell->shell.popped_up)
1011          {
1012             if ((entEvent->x_root >= submenushell->core.x) &&
1013                 (entEvent->x_root <  submenushell->core.x +
1014                                      submenushell->core.width +
1015                                      (submenushell->core.border_width << 1)) &&
1016                 (entEvent->y_root >= submenushell->core.y) &&
1017                 (entEvent->y_root <  submenushell->core.y +
1018                                      submenushell->core.height +
1019 	   			     (submenushell->core.border_width << 1)))
1020 
1021   	        /* then we are in the cascading submenu, don't disarm */
1022  	        return;
1023 
1024              /*
1025               * When kick-starting a cascading menu from a tear off, we grab
1026               * the pointer to the parent rc when the cascade has the focus
1027               * (In StartDrag().  A leave window event is generated (with
1028               * mode = NotifyGrab) which we don't wish to recognize.
1029               */
1030              if ((entEvent->mode == NotifyGrab) &&
1031                  !XmIsMenuShell(XtParent(XtParent(cb))))
1032                 return;
1033          }
1034       }
1035       Disarm (cb, TRUE);
1036    }
1037 }
1038 
1039 
1040 /*
1041  * post submenu and disable menu's traversal.  The order of these
1042  * function calls is critical.
1043  */
1044 /*ARGSUSED*/
1045 static void
StartDrag(Widget wid,XEvent * event,String * param,Cardinal * num_param)1046 StartDrag(
1047         Widget wid,
1048         XEvent *event,
1049         String *param,
1050         Cardinal *num_param )
1051 {
1052    XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1053    Boolean validButton;
1054    XmRowColumnWidget parent = (XmRowColumnWidget)XtParent(cb);
1055    XmMenuSystemTrait menuSTrait;
1056 
1057    menuSTrait = (XmMenuSystemTrait)
1058      XmeTraitGet((XtPointer) XtClass((Widget) parent), XmQTmenuSystem);
1059 
1060    /* If no menu system trait then parent isn't a menu as it
1061       should be */
1062    if (menuSTrait == NULL) return;
1063 
1064    /* Start with posted submenu bit reset */
1065    CB_SetWasPosted(cb, FALSE);
1066 
1067    if (CB_Submenu(cb) &&
1068        RC_IsArmed ((XmRowColumnWidget) CB_Submenu(cb))) {
1069      CB_SetWasPosted(cb, TRUE);
1070    }
1071 
1072    /*
1073     * make sure the shell is popped up, this takes care of a corner case
1074     * that can occur with rapid pressing of the mouse button
1075     */
1076    if (Lab_IsMenupane(cb) &&
1077        (!((XmMenuShellWidget) XtParent(parent))->shell.popped_up))
1078    {
1079       /* To support menu replay, keep the pointer in sync mode */
1080       XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
1081 
1082       return;
1083    }
1084 
1085    validButton = menuSTrait -> verifyButton((Widget) parent, event);
1086 
1087    if (validButton)
1088    {
1089       /* In case the tear off is active but not armed or grabbed */
1090       menuSTrait -> tearOffArm((Widget) parent);
1091 
1092       _XmSetInDragMode((Widget) cb, True);
1093 
1094       _XmCascadingPopup ((Widget) cb, event, TRUE);
1095       Arm (cb);
1096 
1097       /* record event so MenuShell does not process it */
1098       _XmRecordEvent (event);
1099    }
1100 
1101    /* To support menu replay, keep the pointer in sync mode */
1102    XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
1103 }
1104 
1105 
1106 /*
1107  * do the popup (either w/ or w/o the cascade callbacks).
1108  * If there is not a submenu, bring down the menu system.
1109  */
1110 static void
Select(register XmCascadeButtonWidget cb,XEvent * event,int doCascade)1111 Select(
1112         register XmCascadeButtonWidget cb,
1113         XEvent *event,
1114 #if NeedWidePrototypes
1115         int doCascade )
1116 #else
1117         Boolean doCascade )
1118 #endif /* NeedWidePrototypes */
1119 {
1120    XmAnyCallbackStruct cback;
1121    XmMenuSystemTrait menuSTrait;
1122 
1123    menuSTrait = (XmMenuSystemTrait)
1124      XmeTraitGet((XtPointer) XtClass(XtParent(cb)), XmQTmenuSystem);
1125    if (menuSTrait == NULL) return;
1126 
1127    if (CB_WasPosted(cb)) {
1128      Disarm(cb, TRUE);
1129      if ((CB_Submenu(cb) != NULL) && (Lab_MenuType(cb) == XmMENU_BAR))
1130        _XmMenuPopDown(XtParent((Widget) cb), event, NULL);
1131      return;
1132    }
1133 
1134    _XmCascadingPopup ((Widget) cb, event, doCascade);
1135 
1136    /*
1137     * check if there is a submenu here in case this changed during
1138     * the cascading callbacks
1139     */
1140    if (CB_Submenu(cb) == NULL)
1141    {
1142       menuSTrait -> popdown(XtParent(cb), event);
1143 
1144       Disarm (cb, FALSE);
1145 
1146       menuSTrait -> disarm(XtParent(cb));
1147 
1148       cback.event = event;
1149       cback.reason = XmCR_ACTIVATE;
1150 
1151       if (menuSTrait != NULL)
1152       {
1153 	menuSTrait -> entryCallback(XtParent(cb), (Widget) cb, &cback);
1154       }
1155 
1156       if ((! cb->label.skipCallback) &&
1157 	  (cb->cascade_button.activate_callback))
1158       {
1159 
1160 	XtCallCallbackList ((Widget) cb, cb->cascade_button.activate_callback, &cback);
1161       }
1162     }
1163    else
1164      {
1165        Arm(cb);
1166      }
1167 }
1168 
1169 /*
1170  * if there is a submenu, enable traversal.
1171  * call select to do the work
1172  */
1173 /*ARGSUSED*/
1174 static void
DoSelect(Widget wid,XEvent * event,String * param,Cardinal * num_param)1175 DoSelect(
1176         Widget wid,
1177         XEvent *event,
1178         String *param,
1179         Cardinal *num_param )
1180 {
1181    register XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1182    Boolean validButton;
1183    XmMenuSystemTrait menuSTrait;
1184 
1185    menuSTrait = (XmMenuSystemTrait)
1186      XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
1187    if (menuSTrait == NULL) return;
1188 
1189    /* To support menu replay, keep the pointer in sync mode */
1190    XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
1191 
1192    if (event && event -> type == ButtonRelease &&
1193        event -> xbutton.button == 2)
1194      return;
1195 
1196    if (!CB_IsArmed(cb))
1197       return;
1198 
1199    if ((Lab_MenuType(cb) == XmMENU_BAR) && !RC_IsArmed (XtParent(cb)))
1200       return;
1201 
1202    /*
1203     * make sure the shell is popped up, this takes care of a corner case
1204     * that can occur with rapid pressing of the mouse button
1205     */
1206    if (Lab_IsMenupane(cb) &&
1207        (!((XmMenuShellWidget) XtParent(XtParent(cb)))->shell.popped_up))
1208    {
1209       return;
1210    }
1211 
1212    validButton = menuSTrait -> verifyButton(XtParent(cb), event);
1213 
1214    if (validButton)
1215    {
1216       Select (cb, event, (Boolean)(CB_Submenu(cb) != NULL));
1217 
1218       /* don't let the menu shell widget process this event */
1219       _XmRecordEvent (event);
1220 
1221       _XmSetInDragMode((Widget) cb, False);
1222 
1223       if (CB_Submenu(cb))
1224       {
1225          /*
1226           * if XmProcessTraversal() fails, it's possible that the pane
1227           * has no traversable children, so reset the focus to the pane.
1228           */
1229          if (!XmProcessTraversal(CB_Submenu(cb), XmTRAVERSE_CURRENT))
1230 	 {
1231 	    /* Must clear focus path first for shared menushells.
1232 	     * Otherwise, moving the focus back will have old stale
1233 	     * (old) focus_item.
1234 	     */
1235 	    _XmClearFocusPath(CB_Submenu(cb));
1236             XtSetKeyboardFocus(XtParent(CB_Submenu(cb)), CB_Submenu(cb));
1237 	 }
1238       }
1239       else
1240       {
1241 	 /* Move this call into Select().
1242 	  *
1243 	  * (* xmLabelClassRec.label_class.menuProcs) (XmMENU_DISARM,
1244 	  *					    XtParent(cb));
1245 	  */
1246 
1247          if (Lab_MenuType(cb) == XmMENU_BAR)
1248          {
1249 	    _XmMenuFocus(XtParent(cb), XmMENU_END, CurrentTime);
1250             XtUngrabPointer( (Widget) cb, CurrentTime);
1251          }
1252       }
1253    }
1254 }
1255 
1256 /*
1257  * if the menu system traversal is enabled, do a select
1258  */
1259 /*ARGSUSED*/
1260 static void
KeySelect(Widget wid,XEvent * event,String * param,Cardinal * num_param)1261 KeySelect(
1262         Widget wid,
1263         XEvent *event,
1264         String *param,
1265         Cardinal *num_param )
1266 {
1267    XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1268 
1269    if (!_XmGetInDragMode((Widget) cb) &&
1270        (RC_IsArmed(XtParent(cb)) ||
1271 	(RC_Type(XtParent(cb)) != XmMENU_BAR &&
1272 	 !XmIsMenuShell(XtParent(XtParent(cb))))))
1273       (* (((XmCascadeButtonClassRec *)(cb->core.widget_class))->
1274 		primitive_class.arm_and_activate))
1275 			((Widget) cb, event, NULL, NULL);
1276 }
1277 
1278 
1279 /*
1280  * If the menu system is not active, arm it and arm this cascadebutton
1281  * else start the drag mode
1282  */
1283 static void
MenuBarSelect(Widget wid,XEvent * event,String * param,Cardinal * num_param)1284 MenuBarSelect(
1285         Widget wid,
1286         XEvent *event,
1287         String *param,
1288         Cardinal *num_param )
1289 {
1290    XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1291    Boolean validButton;
1292    Time _time = _XmGetDefaultTime(wid, event);
1293    XmMenuSystemTrait menuSTrait;
1294 
1295    menuSTrait = (XmMenuSystemTrait)
1296      XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
1297    if (menuSTrait == NULL) return;
1298 
1299    CB_SetWasPosted(cb, FALSE);
1300 
1301    if (RC_IsArmed ((XmRowColumnWidget) XtParent(cb)))
1302    {
1303       /* Cleanup the PM menubar mode, if enabled */
1304       menuSTrait -> menuBarCleanup(XtParent(cb));
1305 
1306       if (!CB_Submenu(cb))
1307       {
1308 	 _XmMenuFocus(XtParent(cb), XmMENU_MIDDLE, _time);
1309       }
1310 
1311       StartDrag ((Widget) cb, event, param, num_param);
1312    }
1313 
1314    else
1315    {
1316       /* XAllowEvents() is called here because StartDrag also calls it */
1317       /* To support menu replay, keep the pointer in sync mode */
1318       XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
1319 
1320       validButton = menuSTrait -> verifyButton(XtParent(cb), event);
1321 
1322       if (validButton)
1323       {
1324          /*
1325          * Don't post the menu if the menu cannot control grabs!
1326          */
1327 
1328 	 if (_XmMenuGrabKeyboardAndPointer(XtParent(cb), _time) != GrabSuccess)
1329          {
1330 	     _XmRecordEvent (event);
1331 	     return;
1332 	 }
1333 
1334 	 _XmMenuFocus(XtParent(cb), XmMENU_BEGIN, _time);
1335 
1336 	 menuSTrait -> arm((Widget) cb);
1337 
1338          _XmSetInDragMode((Widget) cb, True);
1339 
1340          _XmCascadingPopup ((Widget) cb, event, TRUE);
1341 
1342 	 if (!CB_Submenu(cb))
1343 	 {
1344 	    /*
1345 	     * since no submenu is posted, check if the grab has occured
1346 	     * and if not, do the pointer grab now.
1347 	     */
1348 	    if (RC_BeingArmed(XtParent(cb)))
1349 	    {
1350 
1351                _XmGrabPointer(XtParent(cb), True, EVENTS,
1352                   GrabModeAsync, GrabModeAsync, None,
1353 		  XmGetMenuCursor(XtDisplay(cb)), _time);
1354 
1355 	       RC_SetBeingArmed(XtParent(cb), False);
1356 	    }
1357 	 }
1358 
1359           /* To support menu replay, keep the pointer in sync mode */
1360           XAllowEvents(XtDisplay(cb), SyncPointer, CurrentTime);
1361 
1362 	 /* record so that menuShell doesn't process this event */
1363 	 _XmRecordEvent (event);
1364       }
1365    }
1366 }
1367 
1368 
1369 /*
1370  * If the menu is active, post submenu and arm.
1371  */
1372 /*ARGSUSED*/
1373 static void
MenuBarEnter(Widget wid,XEvent * event,String * param,Cardinal * num_param)1374 MenuBarEnter(
1375         Widget wid,
1376         XEvent *event,
1377         String *param,
1378         Cardinal *num_param )
1379 {
1380         register XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1381    XmRowColumnWidget rc = (XmRowColumnWidget)XtParent(cb);
1382 
1383    if (RC_IsArmed(rc) && !CB_IsArmed(cb) && _XmGetInDragMode((Widget) cb))
1384    {
1385       if (!CB_Submenu(cb))
1386       {
1387 	 _XmMenuFocus((Widget) rc, XmMENU_MIDDLE,
1388 		      _XmGetDefaultTime(wid, event));
1389       }
1390 
1391       _XmCascadingPopup ((Widget) cb, event, TRUE);
1392       Arm(cb);
1393    }
1394 }
1395 
1396 
1397 /*
1398  * unless our submenu is posted or traversal is on, disarm
1399  */
1400 /*ARGSUSED*/
1401 static void
MenuBarLeave(Widget wid,XEvent * event,String * param,Cardinal * num_param)1402 MenuBarLeave(
1403         Widget wid,
1404         XEvent *event,
1405         String *param,
1406         Cardinal *num_param )
1407 {
1408    register XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1409    XmMenuShellWidget submenuShell;
1410 
1411    if (RC_IsArmed (XtParent (cb)))
1412    {
1413       /* Reset this bit so that we don't unpost if the user
1414 	 reenters the cascade button */
1415       CB_SetWasPosted(cb, FALSE);
1416 
1417       if (CB_Submenu(cb))
1418       {
1419          submenuShell = (XmMenuShellWidget) XtParent(CB_Submenu(cb));
1420 
1421          if (submenuShell->shell.popped_up)
1422             return;
1423       }
1424 
1425       if (_XmGetInDragMode((Widget) cb))
1426          Disarm (cb, TRUE);
1427    }
1428 }
1429 
1430 /*
1431  * Cleanup the menubar, if its in the PM traversal mode
1432  */
1433 /*ARGSUSED*/
1434 static void
CleanupMenuBar(Widget wid,XEvent * event,String * param,Cardinal * num_param)1435 CleanupMenuBar(
1436         Widget wid,
1437         XEvent *event,
1438         String *param,
1439         Cardinal *num_param )
1440 {
1441         XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1442     XmRowColumnWidget parent = (XmRowColumnWidget)XtParent(cb);
1443 
1444     if (RC_IsArmed(parent))
1445     {
1446         (* ((XmRowColumnWidgetClass) XtClass(parent))->
1447              row_column_class.armAndActivate) ( (Widget) parent,
1448                           (XEvent *) NULL, (String *) NULL, (Cardinal *) NULL);
1449 	_XmRecordEvent(event);
1450     }
1451 }
1452 
1453 
1454 /*
1455  * CascadeButton Widget and Gadget help routine - first bring down the
1456  * menu and then do the help callback.
1457  */
1458 void
_XmCBHelp(Widget w,XEvent * event,String * params,Cardinal * num_params)1459 _XmCBHelp(
1460         Widget w,
1461         XEvent *event,
1462         String *params,
1463         Cardinal *num_params )
1464 {
1465    XmRowColumnWidget parent = (XmRowColumnWidget) XtParent(w);
1466 
1467    if (RC_Type(parent) == XmMENU_BAR)
1468    {
1469       /* Cannot call CleanupMenubar() 'cause it calls _XmRecordEvent */
1470       if (RC_IsArmed(parent))
1471       {
1472 	 (* ((XmRowColumnWidgetClass) XtClass(parent))->
1473 	      row_column_class.armAndActivate) ( (Widget) parent,
1474 			   (XEvent *) NULL, (String *) NULL, (Cardinal *) NULL);
1475       }
1476    }
1477 
1478    else if ((RC_Type(parent) == XmMENU_PULLDOWN) ||
1479             (RC_Type(parent) == XmMENU_POPUP))
1480    {
1481       (*(((XmMenuShellClassRec *) xmMenuShellWidgetClass)->
1482           menu_shell_class.popdownDone)) (XtParent(parent), event,
1483                                                            params, num_params);
1484    }
1485 
1486    if (XmIsGadget(w))
1487       _XmSocorro(w, event, params, num_params);
1488    else
1489       _XmPrimitiveHelp( w, event, params, num_params) ;
1490 }
1491 
1492 
1493 /*
1494  * When moving between a shared menupane, we only want to unpost the
1495  * descendant panes, not the shared one.
1496  * We only need to check the first popup child, since the menushell
1497  * has always forced the popped up shell to be the first child.
1498  */
1499 static void
PopdownGrandChildren(XmRowColumnWidget rowcol)1500 PopdownGrandChildren(
1501         XmRowColumnWidget rowcol )
1502 {
1503    CompositeWidget menuShell;
1504 
1505    if ((menuShell = (CompositeWidget) RC_PopupPosted(rowcol)) == NULL)
1506        return;
1507 
1508    if ((menuShell = (CompositeWidget)
1509 	RC_PopupPosted (menuShell->composite.children[0])) != NULL)
1510    {
1511       (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
1512 	 menu_shell_class.popdownEveryone))( (Widget) menuShell, NULL,
1513                                                                    NULL, NULL);
1514    }
1515 }
1516 
1517 
1518 /*
1519  * call the cascading callbacks.  The cb parameter can be either a
1520  * cascadebutton widget or gadget.
1521  */
1522 static void
Cascading(Widget w,XEvent * event)1523 Cascading(
1524         Widget w,
1525         XEvent *event )
1526 {
1527     XmAnyCallbackStruct cback;
1528 
1529     cback.reason = XmCR_CASCADING;
1530     cback.event = event;
1531 
1532     if (XmIsCascadeButton(w))
1533     {
1534        XmCascadeButtonWidget cb = (XmCascadeButtonWidget)w;
1535        XmRowColumnWidget submenu = (XmRowColumnWidget) CB_Submenu(cb);
1536 
1537        /* if the submenu is already up, just return */
1538        /* In case of shared menupanes, check the cascade button attachment */
1539 
1540        if (submenu)
1541        {
1542            XmMenuShellWidget ms = (XmMenuShellWidget) XtParent(submenu);
1543 	   if (XmIsMenuShell(ms) &&
1544 	       ms->shell.popped_up &&
1545  	       ms->composite.children[0] == (Widget) submenu &&
1546  	       submenu->row_column.cascadeBtn == (Widget) cb)
1547            {
1548 	      return;
1549            }
1550        }
1551        XtCallCallbackList ((Widget) cb, cb->cascade_button.cascade_callback, &cback);
1552     }
1553     else
1554     {
1555        XmCascadeButtonGadget cb = (XmCascadeButtonGadget)w;
1556        XmRowColumnWidget submenu = (XmRowColumnWidget) CBG_Submenu(cb);
1557 
1558        /* if the submenu is already up, just return */
1559        if (submenu)
1560        {
1561            XmMenuShellWidget ms = (XmMenuShellWidget) XtParent(submenu);
1562 	   if (XmIsMenuShell(ms) &&
1563 	       ms->shell.popped_up &&
1564  	       ms->composite.children[0] == (Widget) submenu &&
1565  	       submenu->row_column.cascadeBtn == (Widget) cb)
1566            {
1567 	      return;
1568            }
1569        }
1570 
1571        XtCallCallbackList ((Widget) cb, cb->cascade_button.cascade_callback, &cback);
1572     }
1573 }
1574 
1575 
1576 /*
1577  * call the cascading callbacks and the popup any submenu.  This is called
1578  * by both the cascadebutton widget and gadget.
1579  */
1580 void
_XmCascadingPopup(Widget cb,XEvent * event,int doCascade)1581 _XmCascadingPopup(
1582         Widget cb,
1583         XEvent *event,
1584 #if NeedWidePrototypes
1585         int doCascade )
1586 #else
1587         Boolean doCascade )
1588 #endif /* NeedWidePrototypes */
1589 {
1590    /* We must make sure the tear off to menushell restoration/callbacks are
1591     * called before the cascading callback.  Exclude the pane in case Popup()
1592     * tries to restore it back to a transient if shared and already posted.
1593     */
1594    XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(cb));
1595    XmExcludedParentPaneRec *excPP =
1596 		&(((XmDisplayInfo *)(dd->display.displayInfo))->excParentPane);
1597 
1598    if (!excPP->pane)
1599    {
1600       excPP->pane_list_size = 4;
1601       excPP->pane = (Widget *)XtMalloc(sizeof(Widget) * excPP->pane_list_size);
1602    }
1603 
1604    if (XmIsCascadeButtonGadget(cb))
1605       *(excPP->pane) = CBG_Submenu(cb);
1606    else
1607       *(excPP->pane) = CB_Submenu(cb);
1608 
1609    if (*(excPP->pane))
1610    {
1611       excPP->num_panes = 1;
1612 
1613       if (RC_TornOff(*(excPP->pane)) &&
1614           !XmIsMenuShell(XtParent(*(excPP->pane))))
1615       {
1616  	 /* If a subpane is already posted and it is not the pane that
1617  	  * will be posted from cb.  Then it must be lowered so that
1618  	  * its tear off can be repainted.
1619  	  */
1620  	 if (RC_PopupPosted(XtParent(cb)))
1621  	 {
1622  	    XmRowColumnWidget postedPane = (XmRowColumnWidget)
1623  	       ((CompositeWidget)RC_PopupPosted(XtParent(cb)))->
1624  	          composite.children[0];
1625 
1626  	    if ((Widget)postedPane != *(excPP->pane))
1627  	    {
1628  	       _XmLowerTearOffObscuringPoppingDownPanes( (Widget)postedPane,
1629  		  *(excPP->pane));
1630  	    }
1631  	 }
1632 	 _XmRestoreTearOffToMenuShell(*(excPP->pane), event);
1633       }
1634    }
1635 
1636    if (doCascade)
1637        Cascading (cb, event);
1638    Popup (cb, event);
1639 }
1640 
1641 /*
1642  * pop up the pulldown menu associated with this cascadebutton
1643  */
1644 static void
Popup(Widget cb,XEvent * event)1645 Popup(
1646         Widget cb,
1647         XEvent *event )
1648 {
1649     Widget oldActiveChild;
1650     Boolean popped_up = False;
1651     register XmRowColumnWidget   submenu;
1652     XmMenuShellWidget shell = NULL;
1653     register XmRowColumnWidget	parent   = (XmRowColumnWidget) XtParent (cb);
1654     XmMenuSystemTrait menuSTrait;
1655     XmDisplay dd = (XmDisplay)XmGetXmDisplay(XtDisplay(cb));
1656     XmExcludedParentPaneRec *excPP =
1657 		&(((XmDisplayInfo *)(dd->display.displayInfo))->excParentPane);
1658 
1659     menuSTrait = (XmMenuSystemTrait)
1660       XmeTraitGet((XtPointer) XtClass((Widget) parent), XmQTmenuSystem);
1661     if (menuSTrait == NULL) return;
1662 
1663     if (XmIsCascadeButtonGadget(cb))
1664        submenu = (XmRowColumnWidget) CBG_Submenu(cb);
1665     else
1666        submenu = (XmRowColumnWidget) CB_Submenu(cb);
1667 
1668     /* if its already up, popdown submenus and then return */
1669     if (submenu &&
1670 	(shell = (XmMenuShellWidget)XtParent(submenu)) &&
1671 	XmIsMenuShell(shell) &&
1672 	(popped_up = shell->shell.popped_up))
1673     {
1674 
1675         /* Just in case the menu shell is being shared.
1676 	 * Shell's 0th child is currently posted submenu.  In case of shared
1677 	 * menupanes we must check to make sure that it is not posted from
1678 	 * the same cascade button before popping down.
1679 	 * Also this is as good a time as any to clear have_traversal field
1680 	 * of submenu's active child.  Updating this internal state allows
1681 	 * this gadget to highlight next time the submenu is posted.
1682 	 */
1683         if ((XmRowColumnWidget)shell->composite.children[0] == submenu)
1684 	{
1685 	   if (cb == RC_CascadeBtn(submenu))
1686 	   {
1687 	       if (RC_PopupPosted(submenu))
1688 		   (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
1689 		      menu_shell_class.popdownEveryone))
1690 		       (RC_PopupPosted(submenu),NULL,NULL, NULL);
1691 
1692               if (submenu->manager.active_child)
1693               {
1694                  /* update visible focus/highlighting */
1695                  if (XmIsPrimitive(submenu->manager.active_child))
1696                  {
1697                      (*(((XmPrimitiveClassRec *)XtClass(submenu->manager.
1698                      active_child))->primitive_class.border_unhighlight))
1699                      (submenu->manager.active_child);
1700                  }
1701                  else if (XmIsGadget(submenu->manager.active_child))
1702                  {
1703                      (*(((XmGadgetClassRec *)XtClass(submenu->manager.
1704                      active_child))->gadget_class.border_unhighlight))
1705                      (submenu->manager.active_child);
1706                  }
1707                  /* update internal focus state */
1708                  _XmClearFocusPath((Widget) submenu);
1709               }
1710 	      *(excPP->pane) = NULL;
1711 	      excPP->num_panes = 0;
1712 	      return;
1713 	   }
1714 	   else
1715 	   {
1716 	      oldActiveChild = submenu->manager.active_child;
1717 	      if (oldActiveChild && XmIsGadget(oldActiveChild))
1718 		 ((XmGadget)oldActiveChild)->gadget.have_traversal = False;
1719 	   }
1720 	}
1721     }
1722 
1723     if (XtIsManaged ((Widget)parent))
1724     {
1725         if ((RC_Type(parent) == XmMENU_BAR) && !RC_IsArmed (parent))
1726 	   return;
1727 
1728         /*
1729          * If the old active child for the menupane was a cascadeB gadget,
1730          * and it did not have its submenu posted, then
1731          * we need to manually send it FocusOut notification, since
1732          * when we managed our submenu, the active_child field for
1733          * our parent was set to us, and the parent now no longer knows
1734          * who previously had the focus.
1735          */
1736         oldActiveChild = parent->manager.active_child;
1737         if (oldActiveChild &&
1738             (oldActiveChild != (Widget)cb) &&
1739             XmIsCascadeButtonGadget(oldActiveChild) &&
1740             CBG_Submenu(oldActiveChild) &&
1741             (((XmMenuShellWidget)XtParent(CBG_Submenu(oldActiveChild)))->
1742                shell.popped_up == False))
1743         {
1744             parent->manager.active_child = NULL;
1745             _XmDispatchGadgetInput((Widget) oldActiveChild, NULL,
1746                                                             XmFOCUS_OUT_EVENT);
1747             ((XmGadget)oldActiveChild)->gadget.have_traversal = False;
1748         }
1749         else
1750 	  /*
1751 	   * Fix for CR 5683 - If the RC_CascadeBtn == cb, then the menupane
1752 	   *		       should not be popped down (it probably already
1753 	   *		       is popped down), so do not pop down the
1754 	   *		       menupane (it messes up the traversal)
1755 	   */
1756 	if (!submenu ||
1757 	    !popped_up ||
1758 	    (RC_PopupPosted(parent) != (Widget)shell) ||
1759 	    (submenu && RC_CascadeBtn(submenu) &&
1760 	     (RC_CascadeBtn(submenu) != cb) &&
1761 	     ((Widget)parent == XtParent(RC_CascadeBtn(submenu)))) )
1762         {
1763 	   /* popdown all visible subpanes of this parent when:
1764 	    * - moving to a button without a submenu
1765 	    * - between non-shared menushells
1766 	    *   = then menushell will not be popped_up
1767 	    *   = the old shell is different than the new shell
1768 	    *     (old shell nonshared, new shell shared)
1769 	    * - special case when same pane attached to > 1 cb in same parent
1770 	    */
1771 	    if (RC_PopupPosted(parent))
1772 	    {
1773 		(*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
1774 		   menu_shell_class.popdownEveryone))
1775 		    (RC_PopupPosted(parent),NULL,NULL, NULL);
1776 	    }
1777 
1778 	   /* Focus is not being handled perfectly for tear offs whose parent
1779 	    * is a top level shell.  So force cascade unhighlighting here.
1780 	    */
1781 	   if (NULL != oldActiveChild &&
1782 	       oldActiveChild != cb &&
1783 	       ((parent->row_column.type == XmMENU_PULLDOWN) ||
1784 		(parent->row_column.type == XmMENU_POPUP)) &&
1785 	       !XmIsMenuShell(XtParent(parent)))
1786 	       XmCascadeButtonHighlight(oldActiveChild, FALSE);
1787         }
1788         else
1789         {
1790            /*
1791             * Handle shared menupanes */
1792            PopdownGrandChildren (parent);
1793         }
1794 
1795 	/* We don't allow the possibility of the submenu to be restored
1796 	 * from the menushell back to the transient shell during the
1797 	 * previous popdown code.  This occurs when the tear off is shared
1798 	 * and previously posted.
1799 	 */
1800 	*(excPP->pane) = NULL;
1801 	excPP->num_panes = 0;
1802 
1803 	if (submenu)
1804 	{
1805            if (((ShellWidget)XtParent(submenu))->composite.num_children == 1)
1806 	   {
1807 	      menuSTrait -> cascade((Widget) submenu, cb, event);
1808 
1809 	      /* Map the window first to sync up the server in case the
1810 	       * menushell was previously shared
1811 	       */
1812 	      XMapWindow(XtDisplay(submenu), XtWindow(submenu));
1813 	      XtManageChild( (Widget) submenu);
1814 	   }
1815            else
1816            {
1817 	      /* We will call menuprocs XmMENU_CASCADING from
1818 	       * popupSharedMenuShell routine so that it occurs between
1819 	       * shared menupane window configurations.
1820 	       */
1821               (*(((XmMenuShellClassRec *)xmMenuShellWidgetClass)->
1822                    menu_shell_class.popupSharedMenupane))(cb,
1823                                                       (Widget) submenu, event);
1824            }
1825 	   /* So help is delivered correctly when in drag mode */
1826 	   if (_XmGetInDragMode((Widget)cb))
1827 	      XtSetKeyboardFocus((Widget)submenu, None);
1828 	}
1829     }
1830 }
1831 
1832 
1833 /*
1834  * get the cascade size set up
1835  */
1836 static void
size_cascade(XmCascadeButtonWidget cascadebtn)1837 size_cascade(
1838         XmCascadeButtonWidget cascadebtn )
1839 {
1840     Window rootwin;
1841     int x,y;					       /* must be int */
1842     unsigned int width, height, border, depth;	       /* must be int */
1843 
1844     if (CB_CascadePixmap(cascadebtn) != XmUNSPECIFIED_PIXMAP)
1845     {
1846        XGetGeometry(XtDisplay(cascadebtn), CB_CascadePixmap(cascadebtn),
1847 		    &rootwin, &x, &y, &width, &height,
1848 		    &border, &depth);
1849 
1850        CB_Cascade_width(cascadebtn) = (Dimension) width;
1851        CB_Cascade_height(cascadebtn) = (Dimension) height;
1852     }
1853     else
1854     {
1855        CB_Cascade_width(cascadebtn) = 0;
1856        CB_Cascade_height(cascadebtn) = 0;
1857     }
1858 }
1859 
1860 
1861 /*
1862  * set up the cascade position.
1863  */
1864 static void
position_cascade(XmCascadeButtonWidget cascadebtn)1865 position_cascade(
1866         XmCascadeButtonWidget cascadebtn )
1867 {
1868    Dimension buffer;
1869 
1870    if (CB_HasCascade(cascadebtn))
1871    {
1872       if (LayoutIsRtoLP(cascadebtn))
1873          CB_Cascade_x(cascadebtn) = cascadebtn->primitive.highlight_thickness +
1874                                     cascadebtn->primitive.shadow_thickness +
1875                                     Lab_MarginWidth(cascadebtn);
1876       else
1877 	CB_Cascade_x(cascadebtn) = XtWidth (cascadebtn) -
1878                                cascadebtn->primitive.highlight_thickness -
1879                                cascadebtn->primitive.shadow_thickness -
1880 			       Lab_MarginWidth(cascadebtn) -
1881                                CB_Cascade_width(cascadebtn);
1882 
1883       buffer = cascadebtn->primitive.highlight_thickness +
1884              cascadebtn->primitive.shadow_thickness +
1885              Lab_MarginHeight(cascadebtn);
1886 
1887       CB_Cascade_y(cascadebtn) = buffer +
1888                                ((XtHeight(cascadebtn) -  2*buffer) -
1889                                 CB_Cascade_height(cascadebtn)) / 2;
1890    }
1891    else
1892    {
1893       CB_Cascade_y(cascadebtn) = 0;
1894       CB_Cascade_x(cascadebtn) = 0;
1895    }
1896 }
1897 
1898 
1899 /*
1900  * set up the cascade size and location
1901  */
1902 static void
setup_cascade(XmCascadeButtonWidget cascadebtn,int adjustWidth,int adjustHeight)1903 setup_cascade(
1904         XmCascadeButtonWidget cascadebtn,
1905 #if NeedWidePrototypes
1906         int adjustWidth,
1907         int adjustHeight )
1908 #else
1909         Boolean adjustWidth,
1910         Boolean adjustHeight )
1911 #endif /* NeedWidePrototypes */
1912 {
1913    Dimension delta;
1914 
1915    if (CB_HasCascade(cascadebtn))
1916    {
1917       /*
1918        *  modify the size of the cascadebutton to acommadate the cascade, if
1919        *  needed.  The cascade should fit inside MarginRight.
1920        */
1921       if (LayoutIsRtoLP(cascadebtn))
1922       {
1923         if ((CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE) >
1924              Lab_MarginLeft(cascadebtn))
1925         {
1926             delta = CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE -
1927                 Lab_MarginLeft(cascadebtn);
1928             Lab_MarginLeft(cascadebtn) += delta;
1929 
1930             if (adjustWidth)
1931                 XtWidth(cascadebtn) += delta;
1932 
1933             else
1934             {
1935                if (cascadebtn->label.alignment == XmALIGNMENT_BEGINNING)
1936                    Lab_TextRect_x(cascadebtn) += delta;
1937                else if (cascadebtn->label.alignment == XmALIGNMENT_CENTER)
1938                    Lab_TextRect_x(cascadebtn) += delta/2;
1939             }
1940         }
1941 
1942       }
1943       else
1944       {
1945 	if ((CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE) >
1946 	    Lab_MarginRight(cascadebtn))
1947 	  {
1948 	    delta = CB_Cascade_width(cascadebtn) + CASCADE_PIX_SPACE -
1949 	      Lab_MarginRight(cascadebtn);
1950 	    Lab_MarginRight(cascadebtn) += delta;
1951 
1952 	    if (adjustWidth)
1953 	      XtWidth(cascadebtn) += delta;
1954 
1955 	    else
1956 	      {
1957 		if (cascadebtn->label.alignment == XmALIGNMENT_END)
1958 		  Lab_TextRect_x(cascadebtn) -= delta;
1959 		else if (cascadebtn->label.alignment == XmALIGNMENT_CENTER)
1960 		  Lab_TextRect_x(cascadebtn) -= delta/2;
1961 	      }
1962 	  }
1963       }
1964 
1965       /*
1966        * the cascade height should fit inside of
1967        * TextRect + marginTop + marginBottom
1968        */
1969       delta = CB_Cascade_height(cascadebtn) +
1970 	  2 * (Lab_MarginHeight(cascadebtn) +
1971 	       cascadebtn->primitive.shadow_thickness +
1972 	       cascadebtn->primitive.highlight_thickness);
1973 
1974       if (delta > XtHeight(cascadebtn))
1975       {
1976 	 delta -= XtHeight(cascadebtn);
1977 	 Lab_MarginTop(cascadebtn) += delta/2;
1978 	 Lab_TextRect_y(cascadebtn) += delta/2;
1979 	 Lab_MarginBottom(cascadebtn) += delta - (delta/2);
1980 
1981 	 if (adjustHeight)
1982 	     XtHeight(cascadebtn) += delta;
1983       }
1984    }
1985 
1986    position_cascade(cascadebtn);
1987 }
1988 
1989 
1990 /*
1991  * Destroy the widget
1992  */
1993 static void
Destroy(Widget wid)1994 Destroy(
1995         Widget wid )
1996 {
1997   XmCascadeButtonWidget cb = (XmCascadeButtonWidget) wid ;
1998   XmRowColumnWidget submenu = (XmRowColumnWidget) CB_Submenu(cb);
1999   XmMenuSystemTrait menuSTrait;
2000 
2001   menuSTrait = (XmMenuSystemTrait)
2002     XmeTraitGet((XtPointer) XtClass(XtParent(wid)), XmQTmenuSystem);
2003 
2004 
2005   /*
2006    * If the armed pixmap exists, both pixmaps must be cached arrows
2007    */
2008   if (CB_ArmedPixmap(cb))
2009     {
2010 	_XmProcessLock();
2011 	_XmArrowPixmapCacheDelete((XtPointer) CB_ArmedPixmap(cb));
2012 	_XmArrowPixmapCacheDelete((XtPointer) CB_CascadePixmap(cb));
2013 	_XmProcessUnlock();
2014     }
2015 
2016   /*
2017    * break the submenu link
2018    */
2019   if (submenu != NULL && menuSTrait != NULL)
2020     menuSTrait -> recordPostFromWidget((Widget) submenu, (Widget) cb, FALSE);
2021 
2022   if (cb->cascade_button.timer)
2023     XtRemoveTimeOut (cb->cascade_button.timer);
2024 
2025   /* Release the GCs */
2026 
2027   XtReleaseGC (wid, CB_ArmGC(wid));
2028   XtReleaseGC (wid, CB_BackgroundGC(wid));
2029 
2030 }
2031 
2032 
2033 /*
2034  * routine to resize a cascade button, called by the parent
2035  * geometery manager
2036  */
2037 static void
Resize(Widget cb)2038 Resize(
2039         Widget cb )
2040 {
2041     /*
2042      */
2043      if (cb)
2044      {
2045 	XtWidgetProc resize;
2046 
2047 	/* Label class does it's work */
2048 
2049 	_XmProcessLock();
2050         resize = xmLabelClassRec.core_class.resize;
2051 	_XmProcessUnlock();
2052 	(* resize) (cb);
2053 
2054 	/* move the cascade too */
2055 	position_cascade ((XmCascadeButtonWidget) cb);
2056      }
2057 }
2058 
2059 /************************************************************************
2060  *
2061  *  SetValuesPrehook
2062  *
2063  ************************************************************************/
2064 
2065 /*ARGSUSED*/
2066 static Boolean
SetValuesPrehook(Widget cw,Widget rw,Widget nw,ArgList args,Cardinal * num_args)2067 SetValuesPrehook(
2068         Widget cw,		/* unused */
2069         Widget rw,		/* unused */
2070         Widget nw,
2071         ArgList args,		/* unused */
2072         Cardinal *num_args )	/* unused */
2073 {
2074   XmCascadeButtonWidget new_w = (XmCascadeButtonWidget) nw ;
2075 
2076   /* CR 2990: Use XmNbuttonFontList as the default font. */
2077   if (new_w->label.font == NULL)
2078     new_w->label.font = XmeGetDefaultRenderTable (nw, XmBUTTON_FONTLIST);
2079 
2080   return False;
2081 }
2082 
2083 /*
2084  * Set Values
2085  */
2086 /*ARGSUSED*/
2087 static Boolean
SetValues(Widget cw,Widget rw,Widget nw,ArgList args,Cardinal * num_args)2088 SetValues(
2089         Widget cw,
2090         Widget rw,
2091         Widget nw,
2092         ArgList args,		/* unused */
2093         Cardinal *num_args )	/* unused */
2094 {
2095   XmCascadeButtonWidget old = (XmCascadeButtonWidget) cw ;
2096   XmCascadeButtonWidget requested = (XmCascadeButtonWidget) rw ;
2097   XmCascadeButtonWidget new_w = (XmCascadeButtonWidget) nw ;
2098   Boolean flag = FALSE;
2099   Boolean adjustWidth = FALSE;
2100   Boolean adjustHeight = FALSE;
2101   XmMenuSystemTrait menuSTrait;
2102 
2103   menuSTrait = (XmMenuSystemTrait)
2104     XmeTraitGet((XtPointer) XtClass(XtParent(cw)), XmQTmenuSystem);
2105 
2106   if (old->primitive.foreground != new_w->primitive.foreground
2107     || old->core.background_pixel != new_w->core.background_pixel) {
2108       GetBackgroundGC(new_w);
2109   }
2110 
2111   if ((CB_Submenu(new_w)) &&
2112       ((! XmIsRowColumn(CB_Submenu(new_w))) ||
2113        (RC_Type(CB_Submenu(new_w)) != XmMENU_PULLDOWN)))
2114     {
2115       CB_Submenu(new_w) = NULL;
2116       XmeWarning( (Widget)new_w, WRONGSUBMENU);
2117     }
2118 
2119     if (new_w->cascade_button.map_delay < 0)
2120     {
2121        new_w->cascade_button.map_delay = old->cascade_button.map_delay;
2122        XmeWarning( (Widget)new_w, WRONGMAPDELAY);
2123     }
2124 
2125     /* if there is a change to submenu, notify menu system */
2126     if (CB_Submenu(old) != CB_Submenu(new_w))
2127     {
2128       /* We must pass nw as the parameter to recordPostFromWidget
2129        * because old is a copy!  The call to recordPostFromWidget() does
2130        * a widget ID comparison and we must pass the real widget (nw).
2131        */
2132 
2133       if (CB_Submenu(old) && menuSTrait)
2134 	menuSTrait -> recordPostFromWidget(CB_Submenu(old), nw, FALSE);
2135 
2136       if (CB_Submenu(new_w) && menuSTrait)
2137 	menuSTrait -> recordPostFromWidget(CB_Submenu(new_w), nw, TRUE);
2138     }
2139 
2140     /* don't let traversal be changed */
2141     if (Lab_MenuType(new_w) == XmMENU_BAR)
2142 	new_w->primitive.traversal_on = TRUE;
2143 
2144     /* handle the cascade pixmap indicator */
2145     else if (Lab_IsMenupane(new_w))
2146     {
2147        /* don't let traversal be changed */
2148        new_w->primitive.traversal_on = TRUE;
2149 
2150        if ((new_w->label.recompute_size)  || (requested->core.width <= 0))
2151 	  adjustWidth = TRUE;
2152 
2153        if ((new_w->label.recompute_size)  || (requested->core.height <= 0))
2154 	  adjustHeight = TRUE;
2155 
2156        /* get new pixmap size */
2157        if (CB_CascadePixmap(old) != CB_CascadePixmap (new_w))
2158        {
2159 	  if (CB_ArmedPixmap(old) != XmUNSPECIFIED_PIXMAP)
2160 	  {
2161 	     _XmProcessLock();
2162 	     _XmArrowPixmapCacheDelete((XtPointer) CB_ArmedPixmap(old));
2163 	     _XmArrowPixmapCacheDelete((XtPointer) CB_CascadePixmap(old));
2164 	     _XmProcessUnlock();
2165 	  }
2166 	  CB_ArmedPixmap(new_w) = XmUNSPECIFIED_PIXMAP;
2167 	  size_cascade (new_w);
2168        } else
2169           if ( ((CB_CascadePixmap(new_w) ==  XmUNSPECIFIED_PIXMAP) &&
2170                   (!CB_Submenu(old) && CB_Submenu(new_w))) ||
2171                ((CB_ArmedPixmap(old) != XmUNSPECIFIED_PIXMAP) &&
2172                   ((Lab_TextRect_height(old) != Lab_TextRect_height(new_w)) ||
2173 		   (old->primitive.foreground != new_w->primitive.foreground) ||
2174 		   (old->core.background_pixel !=
2175 		      new_w->core.background_pixel))))
2176 	  {
2177 	     _XmProcessLock();
2178 	     _XmArrowPixmapCacheDelete((XtPointer) CB_ArmedPixmap(old));
2179 	     _XmArrowPixmapCacheDelete((XtPointer) CB_CascadePixmap(old));
2180 	     CB_ArmedPixmap(new_w) = XmUNSPECIFIED_PIXMAP;
2181 	     CB_CascadePixmap(new_w) = XmUNSPECIFIED_PIXMAP;
2182 	     _XmCreateArrowPixmaps((Widget) new_w);
2183 	     _XmProcessUnlock();
2184 	     size_cascade (new_w);
2185 	  }
2186 
2187        /*
2188         * resize widget if cascade appeared or disappeared, or if the
2189 	* cascade pixmap changed size.
2190 	*/
2191        if ((CB_CascadePixmap (old) != CB_CascadePixmap (new_w))  ||
2192 	   (old->label.label_type != new_w->label.label_type) ||
2193 	   (old->cascade_button.submenu != new_w->cascade_button.submenu))
2194        {
2195 	  setup_cascade (new_w, adjustWidth, adjustHeight);
2196 
2197 	  /* if there wasn't a cascade, and still isn't, don't redraw */
2198 	  if (old->cascade_button.submenu || new_w->cascade_button.submenu)
2199 	      flag = TRUE;
2200        }
2201 
2202        /* make sure that other changes did not scrunch our pixmap */
2203        else if (new_w->cascade_button.submenu)
2204        {
2205 	  if ((new_w->primitive.highlight_thickness !=
2206 	       old->primitive.highlight_thickness)               ||
2207 	      (new_w->primitive.shadow_thickness !=
2208 	       old->primitive.shadow_thickness)                  ||
2209 	      (Lab_MarginRight (new_w) != Lab_MarginRight (old))   ||
2210 	      (Lab_MarginHeight (new_w) != Lab_MarginHeight (old)) ||
2211 	      (Lab_MarginTop (new_w) != Lab_MarginTop (old))	 ||
2212 	      (Lab_MarginBottom (new_w) != Lab_MarginBottom (old)))
2213 	  {
2214 	     setup_cascade (new_w,adjustWidth, adjustHeight);
2215 	     flag = TRUE;
2216 	  }
2217 
2218 	  else if ((Lab_MarginWidth(new_w) != Lab_MarginWidth(old)) ||
2219 		   (new_w->core.width != old->core.width)           ||
2220 		   (new_w->core.height != old->core.height))
2221 
2222 	  {
2223 	     position_cascade (new_w);
2224 	     flag = TRUE;
2225 	  }
2226        }
2227     }
2228 
2229     return (flag);
2230 }
2231 
2232 /************************************************************
2233  *
2234  * InitializePrehook
2235  *
2236  * Put the proper translations in core_class tm_table so that
2237  * the data is massaged correctly
2238  *
2239  ************************************************************/
2240 /*ARGSUSED*/
2241 static void
InitializePrehook(Widget req,Widget new_w,ArgList args,Cardinal * num_args)2242 InitializePrehook(
2243         Widget req,		/* unused */
2244         Widget new_w,
2245         ArgList args,		/* unused */
2246         Cardinal *num_args )	/* unused */
2247 {
2248   unsigned char type;
2249   XmMenuSystemTrait menuSTrait;
2250   XmCascadeButtonWidget bw = (XmCascadeButtonWidget) new_w ;
2251 
2252   _XmSaveCoreClassTranslations (new_w);
2253 
2254   menuSTrait = (XmMenuSystemTrait)
2255     XmeTraitGet((XtPointer) XtClass((Widget) XtParent(new_w)), XmQTmenuSystem);
2256 
2257   if (menuSTrait != NULL)
2258     type = menuSTrait->type(XtParent(new_w));
2259   else
2260     type = XmWORK_AREA;
2261 
2262   _XmProcessLock();
2263   if (type == XmMENU_PULLDOWN ||
2264       type == XmMENU_POPUP)
2265     new_w->core.widget_class->core_class.tm_table = (String) p_events_parsed;
2266   else
2267     new_w->core.widget_class->core_class.tm_table =(String)menubar_events_parsed;
2268   _XmProcessUnlock();
2269 
2270   /* CR 2990: Use XmNbuttonFontList as the default font. */
2271   if (bw->label.font == NULL)
2272     bw->label.font = XmeGetDefaultRenderTable (new_w, XmBUTTON_FONTLIST);
2273 }
2274 
2275 /************************************************************
2276  *
2277  * InitializePosthook
2278  *
2279  * restore core class translations
2280  *
2281  ************************************************************/
2282 /*ARGSUSED*/
2283 static void
InitializePosthook(Widget req,Widget new_w,ArgList args,Cardinal * num_args)2284 InitializePosthook(
2285         Widget req,		/* unused */
2286         Widget new_w,
2287         ArgList args,		/* unused */
2288         Cardinal *num_args )	/* unused */
2289 {
2290   _XmRestoreCoreClassTranslations (new_w);
2291 }
2292 
2293 /************************************************************************
2294  *
2295  *  GetArmGC
2296  *     Get the graphics context used for filling in background of the
2297  *     cascade button when armed.
2298  *
2299  ************************************************************************/
2300 
2301 static void
GetArmGC(XmCascadeButtonWidget cb)2302 GetArmGC(
2303         XmCascadeButtonWidget cb )
2304 {
2305   XGCValues values;
2306   XtGCMask  valueMask;
2307   Pixel     junk, select_pixel;
2308 
2309   XmGetColors(XtScreen(cb), cb->core.colormap, cb->core.background_pixel,
2310 	      &junk, &junk, &junk, &select_pixel);
2311 
2312   valueMask = GCForeground | GCBackground | GCGraphicsExposures;
2313 
2314   values.foreground = select_pixel;
2315   values.background = cb->primitive.foreground;
2316   values.graphics_exposures = False;
2317 
2318   CB_ArmGC(cb) = XtGetGC ((Widget) cb, valueMask, &values);
2319 }
2320 
2321 /************************************************************************
2322  *
2323  *  GetBackgroundGC
2324  *     Get the graphics context used for filling in background of
2325  *     the cascade button when not armed.
2326  *
2327  ************************************************************************/
2328 static void
GetBackgroundGC(XmCascadeButtonWidget cb)2329 GetBackgroundGC(
2330         XmCascadeButtonWidget cb )
2331 {
2332   XGCValues       values;
2333   XtGCMask        valueMask;
2334   XFontStruct     *fs;
2335 
2336   valueMask = GCForeground | GCBackground | GCFont | GCGraphicsExposures;
2337 
2338   values.foreground = cb->core.background_pixel;
2339   values.background = cb->primitive.foreground;
2340   values.graphics_exposures = False;
2341 
2342   if (XmeRenderTableGetDefaultFont(cb->label.font, &fs))
2343     values.font = fs->fid;
2344   else
2345     valueMask &= ~GCFont;
2346 
2347   CB_BackgroundGC(cb) = XtGetGC ((Widget) cb, valueMask, &values);
2348 }
2349 
2350 /*
2351  * Initialize
2352  */
2353 /*ARGSUSED*/
2354 static void
Initialize(Widget w_req,Widget w_new,ArgList args,Cardinal * num_args)2355 Initialize(
2356         Widget w_req,
2357         Widget w_new,
2358         ArgList args,		/* unused */
2359         Cardinal *num_args )	/* unused */
2360 {
2361     XmCascadeButtonWidget  req = (XmCascadeButtonWidget) w_req ;
2362     XmCascadeButtonWidget  new_w = (XmCascadeButtonWidget) w_new ;
2363     Boolean adjustWidth = FALSE;
2364     Boolean adjustHeight = FALSE;
2365     XmMenuSystemTrait menuSTrait;
2366 
2367     XmRowColumnWidget    submenu = (XmRowColumnWidget) CB_Submenu(new_w);
2368     XmRowColumnWidget    parent = (XmRowColumnWidget) XtParent(new_w);
2369 
2370     menuSTrait = (XmMenuSystemTrait)
2371       XmeTraitGet((XtPointer) XtClass((Widget) parent), XmQTmenuSystem);
2372 
2373     if ((! XmIsRowColumn (parent)) &&
2374 	((Lab_MenuType(new_w) == XmMENU_PULLDOWN) ||
2375 	 (Lab_MenuType(new_w) == XmMENU_POPUP)    ||
2376 	 (Lab_MenuType(new_w) == XmMENU_BAR)))
2377     {
2378        XmeWarning( (Widget)new_w, WRONGPARENT);
2379     }
2380 
2381     /* if menuProcs is not set up yet, try again */
2382     _XmProcessLock();
2383     if (xmLabelClassRec.label_class.menuProcs == NULL)
2384 	xmLabelClassRec.label_class.menuProcs =
2385 	    (XmMenuProc) _XmGetMenuProcContext();
2386     _XmProcessUnlock();
2387 
2388     /* CR 7651: Clear before setting. */
2389     new_w->cascade_button.armed = 0;
2390     CB_SetArmed(new_w, FALSE);
2391     new_w->cascade_button.timer = 0;
2392     CB_SetTraverse (new_w, FALSE);
2393     CB_SetWasPosted (new_w, FALSE);
2394     CB_ArmedPixmap(new_w) = XmUNSPECIFIED_PIXMAP;
2395 
2396     /*
2397      * if the user did not specify a margin width, set the default.
2398      * The menubar cbs have a larger margin.
2399      */
2400     if (Lab_MarginWidth(req) == XmINVALID_DIMENSION)
2401     {
2402        if (Lab_MenuType(new_w) == XmMENU_BAR)
2403           Lab_MarginWidth(new_w) = 6;
2404        else
2405           Lab_MarginWidth(new_w) = 2;
2406     }
2407 
2408 
2409 
2410     if (submenu &&
2411 	(! XmIsRowColumn(submenu) ||
2412 	 (RC_Type(submenu) != XmMENU_PULLDOWN)))
2413     {
2414        submenu = NULL;
2415        XmeWarning( (Widget)new_w, WRONGSUBMENU);
2416     }
2417 
2418     if (new_w->cascade_button.map_delay < 0)
2419     {
2420        new_w->cascade_button.map_delay = MAP_DELAY_DEFAULT;
2421        XmeWarning( (Widget)new_w, WRONGMAPDELAY);
2422     }
2423 
2424     /* call submenu's class function to set the link */
2425     if (submenu != NULL && menuSTrait != NULL)
2426       menuSTrait -> recordPostFromWidget((Widget) submenu, (Widget) new_w, TRUE);
2427 
2428    if (submenu && (CB_CascadePixmap(new_w) == XmUNSPECIFIED_PIXMAP)) {
2429       _XmProcessLock();
2430       _XmCreateArrowPixmaps((Widget) new_w);
2431       _XmProcessUnlock();
2432    }
2433 
2434     if (Lab_IsMenupane(new_w))
2435     {
2436       if (req->core.width <= 0)
2437 	adjustWidth = TRUE;
2438 
2439       if (req->core.height <= 0)
2440 	adjustHeight = TRUE;
2441 
2442       /* get pixmap size and set up widget to allow room for it */
2443       size_cascade (new_w);
2444       setup_cascade (new_w, adjustWidth, adjustHeight);
2445     }
2446 
2447     new_w->primitive.traversal_on = TRUE;
2448 
2449     /* Initialize GCs for armed button select and background only */
2450     GetArmGC (new_w);
2451     GetBackgroundGC (new_w);
2452 
2453 }
2454 
2455 
2456 /*
2457  *************************************************************************
2458  *
2459  * Public Routines
2460  *
2461  *************************************************************************
2462  */
2463 Widget
XmCreateCascadeButton(Widget parent,char * name,ArgList al,Cardinal ac)2464 XmCreateCascadeButton(
2465         Widget parent,
2466         char *name,
2467         ArgList al,
2468         Cardinal ac )
2469 {
2470     Widget cb;
2471 
2472     cb = XtCreateWidget(name, xmCascadeButtonWidgetClass, parent, al, ac);
2473 
2474     return (cb);
2475 }
2476 
2477 Widget
XmVaCreateCascadeButton(Widget parent,char * name,...)2478 XmVaCreateCascadeButton(
2479         Widget parent,
2480         char *name,
2481         ...)
2482 {
2483     register Widget w;
2484     va_list var;
2485     int count;
2486 
2487     Va_start(var,name);
2488     count = XmeCountVaListSimple(var);
2489     va_end(var);
2490 
2491 
2492     Va_start(var, name);
2493     w = XmeVLCreateWidget(name,
2494                          xmCascadeButtonWidgetClass,
2495                          parent, False,
2496                          var, count);
2497     va_end(var);
2498     return w;
2499 }
2500 
2501 Widget
XmVaCreateManagedCascadeButton(Widget parent,char * name,...)2502 XmVaCreateManagedCascadeButton(
2503         Widget parent,
2504         char *name,
2505         ...)
2506 {
2507     Widget w = NULL;
2508     va_list var;
2509     int count;
2510 
2511     Va_start(var, name);
2512     count = XmeCountVaListSimple(var);
2513     va_end(var);
2514 
2515     Va_start(var, name);
2516     w = XmeVLCreateWidget(name,
2517                          xmCascadeButtonWidgetClass,
2518                          parent, True,
2519                          var, count);
2520     va_end(var);
2521     return w;
2522 }
2523 
2524 /*
2525  * This routine is called for both cascadebutton gadgets and widgets.
2526  * The button is armed or disarmed but it does not pop up or down submenus.
2527  */
2528 void
XmCascadeButtonHighlight(Widget cb,int highlight)2529 XmCascadeButtonHighlight(
2530         Widget cb,
2531 #if NeedWidePrototypes
2532         int highlight )
2533 #else
2534         Boolean highlight )
2535 #endif /* NeedWidePrototypes */
2536 {
2537   XtAppContext app;
2538 
2539   if (NULL == cb) return;
2540   app = XtWidgetToApplicationContext(cb);
2541   _XmAppLock(app);
2542    if ((cb) && XmIsCascadeButton(cb))
2543    {
2544       if (highlight)
2545          Arm ((XmCascadeButtonWidget) cb);
2546 
2547       else
2548          Disarm ((XmCascadeButtonWidget) cb, FALSE);
2549    }
2550 
2551    else if ((cb) && XmIsCascadeButtonGadget(cb))
2552       XmCascadeButtonGadgetHighlight ((Widget) cb, highlight);
2553   _XmAppUnlock(app);
2554 }
2555