1 /**
2  *
3  * $Header: /cvsroot/lesstif/lesstif/lib/Xm-2.1/MenuShell.c,v 1.6 2005/03/19 10:02:25 dannybackx Exp $
4  *
5  * Copyright (C) 1996 Free Software Foundation, Inc.
6  * Copyright � 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005 LessTif Development Team
7  *
8  * This file is part of the GNU LessTif Library.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public
21  * License along with this library; if not, write to the Free
22  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  **/
25 
26 static const char rcsid[] = "$Header: /cvsroot/lesstif/lesstif/lib/Xm-2.1/MenuShell.c,v 1.6 2005/03/19 10:02:25 dannybackx Exp $";
27 
28 #include <LTconfig.h>
29 
30 #include <XmI/XmI.h>
31 #include <Xm/XmP.h>
32 #include <Xm/MenuShellP.h>
33 #include <Xm/MenuUtilP.h>
34 #include <Xm/RowColumnP.h>
35 #include <Xm/CascadeBP.h>
36 #include <Xm/CascadeBGP.h>
37 #include <Xm/TransltnsP.h>
38 
39 #include <Xm/SpecRenderT.h>
40 
41 #include <XmI/DebugUtil.h>
42 
43 /* ? */
44 #undef USE_FOCUS
45 
46 /* Forward Declarations */
47 static void class_initialize(void);
48 static void class_part_initialize(WidgetClass w_class);
49 static void initialize(Widget request, Widget new_w,
50 		       ArgList args, Cardinal *num_args);
51 static void destroy(Widget w);
52 static void realize(Widget w, XtValueMask *value_mask,
53 		    XSetWindowAttributes *attributes);
54 
55 #if 0
56 static void expose(Widget w, XEvent *event, Region region);
57 #endif
58 
59 static void resize(Widget w);
60 static Boolean set_values(Widget current, Widget request, Widget new_w,
61 			  ArgList args, Cardinal *num_args);
62 static XtGeometryResult geometry_manager(Widget w,
63 					 XtWidgetGeometry *request,
64 					 XtWidgetGeometry *reply);
65 static void change_managed(Widget w);
66 static void insert_child(Widget w);
67 
68 #define Offset(field) XtOffsetOf(XmMenuShellRec, menu_shell.field)
69 
70 /* Resources for the MenuShell class */
71 static XtResource resources[] =
72 {
73     {
74 	XmNdefaultFontList, XmCDefaultFontList, XmRFontList,
75 	sizeof(XmFontList), Offset(default_font_list),
76 	XmRString, (XtPointer)NULL
77     },
78     {
79 	XmNlabelFontList, XmCLabelFontList, XmRFontList,
80 	sizeof(XmFontList), Offset(label_font_list),
81 	XmRFontList, (XtPointer)NULL
82     },
83     {
84 	XmNbuttonFontList, XmCButtonFontList, XmRFontList,
85 	sizeof(XmFontList), Offset(button_font_list),
86 	XmRFontList, (XtPointer)NULL
87     }
88 };
89 
90 static void MenuShellPopdownDone(Widget w, XEvent *event, String *params, Cardinal *num_params);
91 static void MenuShellPopdownOne(Widget w, XEvent *event, String *params, Cardinal *num_params);
92 static void MenuShellPopdownEveryone(Widget w, XEvent *event, String *params, Cardinal *num_params);
93 static void _XmXtMenuPopup(Widget widget, XEvent *event, String *params, Cardinal *num_params);
94 static void _XmXtMenuPopdown(Widget widget, XEvent *event, String *params, Cardinal *num_params);
95 static void MenuShellPopupSharedMenuPane(Widget w, Widget w2, XEvent *event);
96 static XmRenderTable GetRenderTable(Widget w, XtEnum renderTableType);
97 
98 static XtActionsRec actions[] =
99 {
100     {"MenuShellPopdownOne", MenuShellPopdownOne},
101     {"MenuShellPopdownDone", MenuShellPopdownDone},
102     {"XtMenuPopup", _XmXtMenuPopup},
103     {"XtMenuPopdown", _XmXtMenuPopdown},
104     {"ClearTraversal", _XmClearTraversal},
105 };
106 
107 static XmBaseClassExtRec _XmMenuSCoreClassExtRec = {
108     /* next_extension            */ NULL,
109     /* record_type               */ NULLQUARK,
110     /* version                   */ XmBaseClassExtVersion,
111     /* size                      */ sizeof(XmBaseClassExtRec),
112     /* initialize_prehook        */ NULL,
113     /* set_values_prehook        */ NULL,
114     /* initialize_posthook       */ NULL,
115     /* set_values_posthook       */ NULL,
116     /* secondary_object_class    */ NULL,
117     /* secondary_object_create   */ NULL,
118     /* get_secondary_resources   */ NULL,
119     /* fast_subclass             */ { 0 },
120     /* get_values_prehook        */ NULL,
121     /* get_values_posthook       */ NULL,
122     /* class_part_init_prehook   */ NULL,
123     /* class_part_init_posthook  */ NULL,
124     /* ext_resources             */ NULL,
125     /* compiled_ext_resources    */ NULL,
126     /* num_ext_resources         */ 0,
127     /* use_sub_resources         */ False,
128     /* widget_navigable          */ NULL,
129     /* focus_change              */ NULL,
130     /* wrapper_data              */ NULL
131 };
132 
133 
134 XmMenuShellClassRec xmMenuShellClassRec = {
135     /* Core class part */
136     {
137 	/* superclass            */ (WidgetClass) &overrideShellClassRec,
138         /* class_name            */ "XmMenuShell",
139 	/* widget_size           */ sizeof(XmMenuShellRec),
140 	/* class_initialize      */ class_initialize,
141 	/* class_part_initialize */ class_part_initialize,
142 	/* class_inited          */ False,
143 	/* initialize            */ initialize,
144 	/* initialize_hook       */ NULL,
145 	/* realize               */ realize,
146 	/* actions               */ actions,
147 	/* num_actions           */ XtNumber(actions),
148 	/* resources             */ resources,
149 	/* num_resources         */ XtNumber(resources),
150 	/* xrm_class             */ NULLQUARK,
151 	/* compress_motion       */ True,
152 	/* compress_exposure     */ XtExposeCompressMaximal /*XtExposeCompressMultiple*/,
153 	/* compress_enterleave   */ True,
154 	/* visible_interest      */ False,
155 	/* destroy               */ destroy,
156 	/* resize                */ resize,
157 	/* expose                */ XtInheritExpose /*expose*/,
158 	/* set_values            */ set_values,
159 	/* set_values_hook       */ NULL,
160 	/* set_values_almost     */ XtInheritSetValuesAlmost,
161 	/* get_values_hook       */ NULL,
162 	/* accept_focus          */ NULL,
163 	/* version               */ XtVersion,
164 	/* callback offsets      */ NULL,
165 	/* tm_table              */ _XmMenuShell_translations,
166 	/* query_geometry        */ NULL,
167 	/* display_accelerator   */ NULL,
168 	/* extension             */ (XtPointer)&_XmMenuSCoreClassExtRec
169     },
170     /* Composite class part */
171     {
172 	/* geometry manager */ geometry_manager,
173         /* change_managed   */ change_managed,
174         /* insert_child     */ insert_child,
175         /* delete_child     */ XtInheritDeleteChild,
176         /* extension        */ NULL,
177     },
178     /* Shell class part */
179     {
180 	/* extension        */ NULL,
181     },
182     /* Override class part */
183     {
184 	/* extension        */ NULL,
185     },
186     /* XmMenuShell class part */
187     {
188         /* popdownOne          */ MenuShellPopdownOne,
189         /* popdownEveryone     */ MenuShellPopdownEveryone,
190         /* popdownDone         */ MenuShellPopdownDone,
191         /* popupSharedMenupane */ MenuShellPopupSharedMenuPane,
192 	/* extension           */ NULL,
193     },
194 };
195 
196 static XmSpecRenderTraitRec _XmMenuShellTraitRec = {
197 	/* version	*/	0,
198 	/* cb		*/	GetRenderTable
199 };
200 
201 WidgetClass xmMenuShellWidgetClass = (WidgetClass)&xmMenuShellClassRec;
202 
203 
204 static void
class_initialize(void)205 class_initialize(void)
206 {
207 	_XmInitializeExtensions();
208 	_XmMenuSCoreClassExtRec.record_type = XmQmotif;
209 
210 
211 	if (! XmeTraitSet((XtPointer)xmMenuShellWidgetClass, XmQTspecifyRenderTable,
212 				(XtPointer)&_XmMenuShellTraitRec)) {
213 		_XmWarning(NULL, "XmMenuShell ClassInitialize: XmeTraitSet failed\n");
214 	}
215 }
216 
217 
218 static void
class_part_initialize(WidgetClass widget_class)219 class_part_initialize(WidgetClass widget_class)
220 {
221     _XmFastSubclassInit(widget_class, XmMENU_SHELL_BIT);
222 }
223 
224 
225 static void
initialize(Widget request,Widget new_w,ArgList args,Cardinal * num_args)226 initialize(Widget request, Widget new_w,
227 	   ArgList args, Cardinal *num_args)
228 {
229     DEBUGOUT(_LtDebug(__FILE__, new_w,
230 		      "%s:initialize(%d) - %i args\n"
231 		      "\trequest X %5i Y %5i W %5i H %5i\n"
232 		      "\t  new_w X %5i Y %5i W %5i H %5i\n",
233 		      __FILE__, __LINE__,
234 		      *num_args,
235 		      XtX(request), XtY(request),
236 		      XtWidth(request), XtHeight(request),
237 		      XtX(new_w), XtY(new_w),
238 		      XtWidth(new_w), XtHeight(new_w)));
239     DEBUGOUT(_LtDebugPrintArgList(__FILE__, new_w, args, *num_args, False));
240 
241     if (MS_DefaultFontList(new_w) != NULL)
242     {
243 	MS_DefaultFontList(new_w) = XmFontListCopy(MS_DefaultFontList(new_w));
244     }
245     else
246     {
247 	MS_DefaultFontList(new_w) = _XmGetDefaultFontList(new_w, XmBUTTON_FONTLIST);
248     }
249     if (MS_ButtonFontList(new_w) == NULL)
250     {
251 	if (MS_DefaultFontList(new_w) != NULL) {
252 	    MS_ButtonFontList(new_w) = XmFontListCopy(MS_DefaultFontList(new_w));
253 	} else {
254 	    MS_ButtonFontList(new_w) = _XmGetDefaultFontList(new_w, XmBUTTON_FONTLIST);
255 	}
256     } else {
257 	MS_ButtonFontList(new_w) = XmFontListCopy(MS_ButtonFontList(new_w));
258     }
259 
260     if (MS_LabelFontList(new_w) == NULL) {
261 	if (MS_DefaultFontList(new_w) != NULL) {
262 	    MS_LabelFontList(new_w) = XmFontListCopy(MS_DefaultFontList(new_w));
263 	} else {
264 	    MS_LabelFontList(new_w) = _XmGetDefaultFontList(new_w, XmLABEL_FONTLIST);
265 	}
266     } else {
267 	MS_LabelFontList(new_w) = XmFontListCopy(MS_LabelFontList(new_w));
268     }
269 
270     XtBorderWidth(new_w) = 0;
271 
272     /* menu shells must be given non-zero width's and height's when
273      * they are initialized, as we create the window (realize the widget) here.
274      */
275 
276     if (XtWidth(new_w) == 0 || XtHeight(new_w) == 0)
277     {
278 	DEBUGOUT(_LtDebug(__FILE__, new_w,
279 			  "Initialize: dimensions %d %d changed to 1x1",
280 			  XtWidth(new_w), XtHeight(new_w)));
281 
282 	XtWidth(new_w) = XtHeight(new_w) = 1;
283     }
284 
285     MS_PrivateShell(new_w) = False;
286 
287     MS_FocusData(new_w) = _XmCreateFocusData();
288     MS_FocusPolicy(new_w) = XmEXPLICIT;
289 
290     XtRealizeWidget(new_w);
291 }
292 
293 static void
destroy(Widget w)294 destroy(Widget w)
295 {
296     DEBUGOUT(_LtDebug(__FILE__, w, "Destroy\n"));
297     _XmDestroyFocusData(MS_FocusData(w));
298 
299     XmFontListFree(MS_DefaultFontList(w));
300     XmFontListFree(MS_ButtonFontList(w));
301     XmFontListFree(MS_LabelFontList(w));
302 }
303 
304 static void
resize(Widget w)305 resize(Widget w)
306 {
307     DEBUGOUT(_LtDebug(__FILE__, w, "Resize -- (%d, %d)\n",
308 		      XtWidth(w), XtHeight(w)));
309 }
310 
311 static void
realize(Widget w,XtValueMask * value_mask,XSetWindowAttributes * attributes)312 realize(Widget w,
313 	XtValueMask *value_mask,
314 	XSetWindowAttributes *attributes)
315 {
316     /* Motif inherits this method */
317     *value_mask = CWBackPixmap | CWBorderPixel | CWBitGravity |
318 	CWOverrideRedirect | CWEventMask | CWSaveUnder | CWColormap;
319 
320     /* FIX ME: might it be that Xt passes these in attributes ? */
321     attributes->background_pixmap = None;
322     attributes->save_under = True;	/* FIX ME: hardwired? */
323     attributes->bit_gravity = NorthWestGravity;
324     attributes->override_redirect = True;
325     attributes->event_mask = ButtonPressMask | ButtonReleaseMask |
326 	StructureNotifyMask;
327 
328     if (XtWidth(w) == 0)
329     {
330 	XtWidth(w) = 1;
331     }
332     if (XtHeight(w) == 0)
333     {
334 	XtHeight(w) = 1;
335     }
336 
337 #define superclass (&overrideShellClassRec)
338     (*superclass->core_class.realize) (w, value_mask, attributes);
339 #undef superclass
340 
341     DEBUGOUT(_LtDebug(__FILE__, w, "Realize (size %dx%d)\n",
342 		      XtWidth(w), XtHeight(w)));
343 }
344 
345 #if 0
346 static void
347 expose(Widget w, XEvent *event, Region region)
348 {
349     DEBUGOUT(_LtDebug(__FILE__, w, "Expose\n"));
350 }
351 #endif
352 
353 static Boolean
set_values(Widget current,Widget request,Widget new_w,ArgList args,Cardinal * num_args)354 set_values(Widget current, Widget request, Widget new_w,
355 	   ArgList args, Cardinal *num_args)
356 {
357 	DEBUGOUT(_LtDebug(__FILE__, new_w, "SetValues\n"));
358 	DEBUGOUT(_LtDebugPrintArgList(__FILE__, new_w, args, *num_args, False));
359 
360 	if (MS_DefaultFontList(new_w) != MS_DefaultFontList(current)) {
361 		XmFontListFree(MS_DefaultFontList(current));
362 		MS_DefaultFontList(new_w) = XmFontListCopy(MS_DefaultFontList(new_w));
363 	}
364 
365 	if (MS_ButtonFontList(new_w) != MS_ButtonFontList(current)) {
366 		XmFontListFree(MS_ButtonFontList(current));
367 		MS_ButtonFontList(new_w) = XmFontListCopy(MS_ButtonFontList(new_w));
368 	}
369 
370 	if (MS_LabelFontList(new_w) != MS_LabelFontList(current)) {
371 		XmFontListFree(MS_LabelFontList(current));
372 		MS_LabelFontList(new_w) = XmFontListCopy(MS_LabelFontList(new_w));
373 	}
374 
375 	return True;
376 }
377 
378 static XtGeometryResult
geometry_manager(Widget w,XtWidgetGeometry * request,XtWidgetGeometry * reply)379 geometry_manager(Widget w,
380 		 XtWidgetGeometry *request,
381 		 XtWidgetGeometry *reply)
382 {
383     XtGeometryResult res;
384     XtWidgetGeometry wants;
385     Widget ms = XtParent(w);
386 
387     DEBUGOUT(_LtDebug2(__FILE__, ms, w,
388 		       "geometry_manager: request %s, allow_shell_resize %s\n",
389 		       _LtDebugWidgetGeometry2String(request),
390 		       ((XmMenuShellWidget)ms)->shell.allow_shell_resize
391 		       ? "True"
392 		       : "False"));
393 
394     if ((request->request_mode & (CWWidth | CWHeight)) == 0)
395     {
396 	return XtGeometryYes;
397     }
398 
399     DEBUGOUT(_LtDebug(__FILE__, ms,
400 		      "geometry_manager: %s\n",
401 		      _LtDebugWidgetGeometry2String(request)));
402 
403     wants = *request;
404 
405     res = _XmMakeGeometryRequest(ms, &wants);
406 
407     if (res == XtGeometryNo)
408     {
409 	DEBUGOUT(_LtDebug(__FILE__, w,
410 			  "XtGeometryNo returned... THIS SHOULD NOT HAPPEN\n"));
411     }
412     *reply = wants;
413 
414     if (wants.request_mode & CWWidth)
415     {
416 	XtWidth(w) = wants.width;
417     }
418     if (wants.request_mode & CWHeight)
419     {
420 	XtHeight(w) = wants.height;
421     }
422 
423     DEBUGOUT(_LtDebug(__FILE__, w,
424 		      "geometry_manager: size %dx%d => Yes\n",
425 		      reply->width, reply->height));
426 
427     return XtGeometryYes;
428 }
429 
430 static void
change_managed(Widget w)431 change_managed(Widget w)
432 {
433     XtWidgetGeometry geo;
434     Widget child;
435     Cardinal i;
436 
437     DEBUGOUT(_LtDebug(__FILE__, w,
438 		      "ChangeManaged: trying to find child to manage\n"));
439 
440     /* FIX ME: isn't having no managed children perfectly valid ? */
441     child = NULL;
442     for (i = 0; i < MGR_NumChildren(w); i++)
443     {
444 	DEBUGOUT(_LtDebug2(__FILE__, w,
445 			   MGR_Children(w)[i], "ChangeManaged [%d] %s\n",
446 			   i,
447 			   XtIsManaged(MGR_Children(w)[i])
448 			   ? "Managed"
449 			   : "Not Managed"));
450 	if (XtIsManaged(MGR_Children(w)[i]))
451 	{
452 
453 	    if (!MS_PrivateShell(w))
454 	    {
455 		child = MGR_Children(w)[i];
456 		break;
457 	    }
458 	    /* I don't think this is really necessary */
459 	    else
460 	    {
461 		Widget tmp = MGR_Children(w)[i];
462 
463 		if (RC_Type(tmp) != XmMENU_POPUP && RC_CascadeBtn(tmp))
464 		{
465 		    if (XmIsCascadeButton(RC_CascadeBtn(tmp)) &&
466 			CB_IsArmed(RC_CascadeBtn(tmp)))
467 		    {
468 			child = tmp;
469 			break;
470 		    }
471 		    else if (XmIsCascadeButtonGadget(RC_CascadeBtn(tmp)) &&
472 			     CBG_IsArmed(RC_CascadeBtn(tmp)))
473 		    {
474 			child = tmp;
475 			break;
476 		    }
477 		}
478 	    }
479 	}
480 #if 0
481 	/* rws 26 Sep 1999
482 	   This is for http://www.bartels.de/baedform.htm. It seems that it
483 	   un-manages the menus somewhwhere along the line. We manage them
484 	   in insert_child, and expect them to stay managed.
485 	 */
486 	 /* rws 29 Sep 1999
487 	    However this messes up Mozilla's Go menu, sigh. So instead just
488 	    make sure the menu is managed in MenuShellPopupSharedMenuPane
489 	  */
490 	else
491 	{
492 	    if (MS_PrivateShell(w))
493 	    {
494 		if (!XtIsManaged(MGR_Children(w)[i]))
495 		{
496 		    XtManageChild(MGR_Children(w)[i]);
497 		}
498 	    }
499 	}
500 #endif
501     }
502 
503     if (!child)
504     {
505 	DEBUGOUT(_LtDebug(__FILE__, w,
506 			  "change_managed: no managed children so we must"
507 			  " be popping down\n"));
508 
509  	if (Shell_PoppedUp(w))
510 	{
511 	    if (MS_PrivateShell(w))
512 	    {
513 		DEBUGOUT(_LtDebug(__FILE__, w, "  PrivateShell\n"));
514 
515 		_XmXtMenuPopdown(w, NULL, NULL, NULL);
516 	    }
517 	    else
518 	    {
519 		DEBUGOUT(_LtDebug(__FILE__, w, "  public shell\n"));
520 		_XmRemoveGrab(w);
521 
522 		XtUnmapWidget(w);
523 		XtCallCallbacks(w, XmNpopdownCallback, NULL);
524 		Shell_PoppedUp(w) = False;
525 
526 		if (_XmIsActiveTearOff(MGR_Children(w)[0]))
527 		{
528 		Widget rc = MGR_Children(w)[0];
529 
530 		    RCClass_MenuProcs(XtClass(rc))(XmMENU_RESTORE_TEAROFF_TO_TOPLEVEL_SHELL,
531 					     rc, NULL);
532 		}
533 	    }
534 	}
535 
536 	return;
537     }
538 
539     /* MenuShell's take their height/width from the child */
540     /* MLM: either of these work, I think, but for now... */
541     geo.width = XtWidth(child) == 0 ? 1 : XtWidth(child);
542     geo.height = XtHeight(child) == 0 ? 1 : XtHeight(child);
543 
544     /* FIX ME: should look at the flags */
545     _XmResizeObject(w, geo.width, geo.height, 0);
546 
547     DEBUGOUT(_LtDebug2(__FILE__, w, child,
548 		       "ChangeManaged width %d height %d\n",
549 		       XtWidth(w), XtHeight(w)));
550 
551     /* MenuShell children should be at 0,0 positions !
552      * Danny 17/11/96 */
553     /* Actually they are at -border_width (similiar to Vendor.c)
554      * rws 24 Feb 1197 */
555     _XmMoveObject(child, -XtBorderWidth(child), -XtBorderWidth(child));
556 
557     if (RC_Type(child) == XmMENU_POPUP)
558     {
559 	XmMenuState state = _XmGetMenuState(child);
560 
561 	DEBUGOUT(_LtDebug2(__FILE__, w, child, "Popping up\n"));
562 
563 	_XmPostPopupMenu(child,
564 			 (XEvent *)&state->RC_ButtonEventStatus.event);
565 
566 	state->RC_ButtonEventStatus.event.type = 0;
567     }
568     else
569     {
570 	/* FIX ME: skip if shared menu shell. Children of shared menu shells
571 	 * do get managed upon creation. More correctly: in insert_child.
572 	 */
573 	if (RC_Type(child) == XmMENU_PULLDOWN)
574 	{
575 	    DEBUGOUT(_LtDebug(__FILE__, child, "Pulldown posting\n"));
576 
577 	    if (RC_CascadeBtn(child))
578 	    {
579 	        DEBUGOUT(_LtDebug(__FILE__, child, "Child is cascade\n"));
580 		if (XtIsManaged(child))
581 		{
582 	            DEBUGOUT(_LtDebug(__FILE__, w, "Popping up\n"));
583 
584 		    if (MS_PrivateShell(w))
585 		    {
586 			DEBUGOUT(_LtDebug(__FILE__, w, "Popping up private shell\n"));
587 			MSClass_PopupSharedMenuPane(w)(w, child, NULL);
588 		    }
589 		    else
590 		    {
591 			DEBUGOUT(_LtDebug(__FILE__, w, "Popping up public shell\n"));
592 		    	XtManageChild(child);
593 			XMapRaised(XtDisplay(w), XtWindow(w));
594 			XtCallCallbacks(w, XmNpopupCallback, NULL);
595 #ifdef	NEW_MAPCALLBACK
596     /*
597     _XmCallRowColumnMapCallback(w2, event);
598     */
599 #endif
600 			Shell_PoppedUp(w) = True;
601 		    }
602 		}
603 		else
604 		{
605 	            DEBUGOUT(_LtDebug(__FILE__, w, "Popping down\n"));
606 
607 		    if (MS_PrivateShell(w))
608 		    {
609 			DEBUGOUT(_LtDebug(__FILE__, w, "  PrivateShell\n"));
610 			_XmXtMenuPopdown(w, NULL, NULL, NULL);
611 		    }
612 		    else
613 		    {
614 			DEBUGOUT(_LtDebug(__FILE__, w, "  public shell\n"));
615 			if (RC_Type(child) == XmMENU_POPUP || RC_Type(child) == XmMENU_OPTION)
616 			{
617 			    _XmRemoveGrab(w);
618 			}
619 
620 			XtUnmapWidget(w);
621 			XtCallCallbacks(w, XmNpopdownCallback, NULL);
622 			XtUnmanageChild(child);
623 		    }
624 		}
625 	    }
626 	    else
627 	    {
628 		DEBUGOUT(_LtDebug(__FILE__, w, "Not cascade\n"));
629 		/* Not attached to a cascade button. */
630 		if (XtIsManaged(child))
631 		{
632 		    /* Probably the user managed the pulldown after creation. */
633 		    /* FIX ME: Motif 2.0 does issue a warning here. */
634 		    DEBUGOUT(_LtDebug(__FILE__, child,
635 				      "Unmanaging in change_managed...\n"));
636 		    XtUnmanageChild(child);
637 		}
638 	    }
639 	}
640     }
641 }
642 
643 static void
insert_child(Widget w)644 insert_child(Widget w)
645 {
646     if (!XmIsRowColumn(w))
647     {
648 	_XmWarning(w,
649 		   "MenuShell widgets must have a xmRowColumnWidgetClass "
650 		   "child.");
651 	return;
652     }
653     else
654     {
655 	/* T. Straumann: M*TIF enforces 0 borderWidth on MenuShell's child */
656 	if ( 0 != XtBorderWidth(w) )
657 	{
658 		XtVaSetValues(w,XmNborderWidth,0,NULL);
659 	}
660 #define superclass (&overrideShellClassRec)
661 	(*superclass->composite_class.insert_child) (w);
662 #undef superclass
663 
664 	/* this might not need to get done.
665 	 * Does the composite class's insert child realize
666 	 * a widget if the composite is realized? */
667 	XtRealizeWidget(w);
668 
669 	if (MS_PrivateShell(XtParent(w)))
670 	{
671 	    XtManageChild(w);
672 	}
673     }
674 }
675 
676 
677 static void
MenuShellPopdownDone(Widget w,XEvent * event,String * params,Cardinal * num_params)678 MenuShellPopdownDone(Widget w, XEvent *event,
679 		     String *params, Cardinal *num_params)
680 {
681     Cardinal numparams = 0;
682     Widget rc, toplevelrc;
683     Widget menu_shell = NULL;
684     Cardinal i;
685     XmMenuState state = _XmGetMenuState(w);
686 
687     DEBUGOUT(_LtDebug(__FILE__, w, "MenuShellPopdownDone()\n"));
688 
689     /* make sure the trigger event here hasn't already been processed */
690     if (_XmMenuGetInPMMode(w) && event && event->type == ButtonRelease &&
691 	event->xbutton.time <= state->RC_ButtonEventStatus.time)
692     {
693       DEBUGOUT(_LtDebug(__FILE__, w, "MenuShellPopdownDone(): exiting b/c already processed\n"));
694 	return;
695     }
696 
697 #if 0
698     /*
699      * Don't use an event that's within the multi click time.
700      * Hopefully this is an event which we can discard :-)
701      * If we get bug reports about things not disappearing when
702      * the user is fast, this is the reason.
703      * Danny 22/04/1998
704      *
705      * I also have a bad feeling about the test above. Is it meant to do
706      * what I changed it to below ?
707      */
708     if (_XmMenuGetInPMMode(w) && event && event->type == ButtonRelease &&
709 	event->xbutton.time <= state->RC_ButtonEventStatus.time
710 				+ XtGetMultiClickTime(XtDisplay(w)))
711     {
712       DEBUGOUT(_LtDebug(__FILE__, w, "MenuShellPopdownDone(): exiting b/c user too fast\n"));
713 	return;
714     }
715 #endif
716 
717     /* must be a menu shell here:
718     assert(XmIsMenuShell(w)); */
719 
720     if (MGR_NumChildren(w) == 0) {
721       DEBUGOUT(_LtDebug(__FILE__, w, "MenuShellPopdownDone(): exiting b/c no children\n"));
722 	return;
723     }
724     /* Find a child that is managed */
725     rc = NULL;
726     for (i = 0; i < MGR_NumChildren(w); i++)
727     {
728 	rc = MGR_Children(w)[i];
729 	if (XmIsRowColumn(rc) && XtIsManaged(rc))
730 	{
731 	    break;
732 	}
733     }
734 
735     if (!rc)
736     {
737 	DEBUGOUT(_LtDebug(__FILE__, w, "MenuShellPopdownDone - NO RC\n"));
738 	return;
739     }
740 
741     DEBUGOUT(_LtDebug2(__FILE__, w, rc,
742 		       "MenuShellPopdownDone - found RC %s, posted: %s\n",
743 			XtName(rc), RC_PopupPosted(rc) ? XtName(RC_PopupPosted(rc)) : "NULL"));
744 
745 #if 1
746     /*
747      * Don't use an event that's within the multi click time.
748      * Hopefully this is an event which we can discard :-)
749      * If we get bug reports about things not disappearing when
750      * the user is fast, this is the reason.
751      * Danny 22/04/1998
752      *
753      * I also have a bad feeling about the test above. Is it meant to do
754      * what I changed it to below ?
755      */
756     /* Danny had this ifdef'd out, but it seems to be exactly what
757      * Motif does, so I put it back in.  In Motif, a quick click/release
758      * causes the menu to "stick" open.
759      * 	-dwilliss 28-Sep-04
760      */
761     if (_XmMenuGetInPMMode(w) && event && event->type == ButtonRelease &&
762 	event->xbutton.time <= state->RC_ButtonEventStatus.time
763 				+ XtGetMultiClickTime(XtDisplay(w)))
764     {
765         if (RC_Type(rc) != XmMENU_OPTION)
766         {
767 	   /* Not that this _shouldn't_ be done for option menus, but I'm
768 	    * not sure if this is the correct way to do it for option menus,
769 	    * so in that case, leave them alone for now
770 	    *  -- dwilliss 28-Sep-04
771 	    */
772 	    _XmMenuFocus(rc, XmMENU_FOCUS_SAVE, CurrentTime);
773 	    RCClass_MenuTraverse(rc, XmTRAVERSE_HOME);
774 	    XAllowEvents(XtDisplay(rc), SyncPointer, event->xbutton.time);
775         }
776       DEBUGOUT(_LtDebug(__FILE__, w, "MenuShellPopdownDone(): exiting b/c user too fast\n"));
777 	return;
778     }
779 #endif
780 
781 
782     _XmGetActiveTopLevelMenu(rc, &toplevelrc);
783 
784     if (!toplevelrc)
785     {
786 	DEBUGOUT(_LtDebug2(__FILE__, w, rc, "No toplevelrc\n"));
787 
788 #if 0
789 	/*
790 	 * This probably only happens for accelerators, and in that
791 	 * case there's nothing to unmanage.
792 	 * Removing this call fixes the problem in XmHTML's example_2.
793 	 * Danny 3/2/1998.
794 	 */
795 	XtUnmanageChild(rc);
796 #endif
797 	return;			/* FIX ME */
798     }
799 
800     if (XmIsOptionMenu(toplevelrc))
801     {
802 	if (RC_PopupPosted(toplevelrc))
803 	{
804 	    menu_shell = XtParent(RC_PopupPosted(toplevelrc));
805 	}
806 	else
807 	{
808 	    DEBUGOUT(_LtDebug2(__FILE__, w, rc,
809 			       "No posted menu\n"));
810 	    return;
811 	}
812     }
813     else
814     {
815 	menu_shell = XtParent(toplevelrc);
816     }
817 
818     if (XmIsMenuShell(menu_shell))
819     {
820 	/* assert(menu_shell && XmIsMenuShell(menu_shell)); */
821 
822 	if (Shell_PoppedUp(menu_shell))
823 	{
824 	    DEBUGOUT(_LtDebug(__FILE__, w,
825 		      "calling PopdownEveryone on toplevel menushell\n"));
826 
827 	    MSClass_PopdownEveryone(menu_shell)(menu_shell, event, params, &numparams);
828 	}
829 	else
830 	{
831 	    DEBUGOUT(_LtDebug(__FILE__, w,
832 		      "MenuShellPopdownDone - MenuShell is not popped up, must have been an accelerator\n"));
833 	    return;
834 	}
835     }
836     else if (XtIsTransientShell(menu_shell))
837     {
838 	MSClass_PopdownEveryone(w)(w, event, params, &numparams);
839     }
840 
841     /* is a popup? */
842     if (_XmMenuGetInPMMode(w))
843     {
844 	Widget msh = XtParent(rc);
845 
846 	if (msh && XmIsMenuShell(msh) && MS_PrivateShell(msh))
847 	{
848 	    _XmXtMenuPopdown(msh, NULL, NULL, NULL);
849 	}
850 	else
851 	{
852 	    DEBUGOUT(_LtDebug(__FILE__, rc, "Unmanaging...\n"));
853 	    {
854 	    Widget realpar;
855 
856 		if (XtIsShell(XtParent(rc)))
857 		{
858 		    realpar = XtParent(XtParent(rc));
859 		}
860 		else
861 		{
862 		    realpar = XtParent(rc);
863 		}
864 		DEBUGOUT(_LtDebug("EMACS", realpar, "%s:%s(%d) - UNGRAB %i %i\n", __FILE__, "MenuShellPopdownDone" , __LINE__,
865 			      RC_PostButton(rc), RC_PostModifiers(rc)));
866 		XtUngrabButton(realpar,
867 			       RC_PostButton(rc), RC_PostModifiers(rc));
868 		XtUngrabKeyboard(realpar, CurrentTime);	/* XXXX 761607 */
869 	    }
870 	    XtUnmanageChild(rc);
871 	    if (_XmIsActiveTearOff(rc))
872 	    {
873 		DEBUGOUT(_LtDebug(__FILE__, rc, "Unmanaging... ActiveTearOff\n"));
874 		XtManageChild(rc);
875 	    }
876 	}
877     }
878 
879     /* FIX ME: this is not really elegant. */
880     if (RC_LastSelectToplevel(rc) &&
881 	(XmIsMenuBar(RC_LastSelectToplevel(rc)) ||
882 	 XmIsOptionMenu(RC_LastSelectToplevel(rc))))
883     {
884 	Widget tmp = RC_LastSelectToplevel(rc);
885 
886 	DEBUGOUT(_LtDebug(__FILE__, tmp,
887 			  "  calling menuDisarm on lastSelectTopLevel\n"));
888 	RCClass_MenuProcs(XtClass(tmp))(XmMENU_DISARM, tmp, NULL);
889     }
890     else if (XtParent(menu_shell) &&
891 	     (XmIsMenuBar(XtParent(menu_shell)) ||
892 	      XmIsOptionMenu(XtParent(menu_shell))))
893     {
894 	Widget tmp = XtParent(menu_shell);
895 
896 	DEBUGOUT(_LtDebug(__FILE__, tmp,
897 			  "  calling menuDisarm on XtParent(menu_shell)\n"));
898 
899 	RCClass_MenuProcs(XtClass(tmp))(XmMENU_DISARM, tmp, NULL);
900     }
901 
902     _XmMenuSetInPMMode(w, False);
903 
904     _XmSetInDragMode(w, False);
905 }
906 
907 
908 static void
MenuShellPopdownOne(Widget w,XEvent * event,String * params,Cardinal * num_params)909 MenuShellPopdownOne(Widget w, XEvent *event,
910 		    String *params, Cardinal *num_params)
911 {
912     Widget rc = NULL;
913 
914     DEBUGOUT(_LtDebug(__FILE__, w, "MenuShellPopdownOne()\n"));
915 
916     /* must be a menu shell here:
917     assert(XmIsMenuShell(w)); */
918 
919     if (MGR_NumChildren(w) == 0)
920     {
921 	DEBUGOUT(_LtDebug(__FILE__, w, "MenuShellPopdownOne: no children\n"));
922 	return;
923     }
924 
925     if (MS_PrivateShell(w) && XmIsRowColumn(XtParent(w)) &&
926 	XmIsMenuBar(XtParent(w)))
927     {
928 	DEBUGOUT(_LtDebug(__FILE__, w, "MenuShellPopdownOne: parent %s\n",
929 			  XtName(XtParent(w))));
930 	rc = RC_PopupPosted(XtParent(w));
931     }
932     else
933     {
934 	rc = MGR_Children(w)[0];
935     }
936 
937     if (!rc || !XmIsRowColumn(rc))
938     {
939 	DEBUGOUT(_LtDebug(__FILE__, w, "MenuShellPopdownOne: no RC\n"));
940 	return;
941     }
942 
943     DEBUGOUT(_LtDebug(__FILE__, w, "  Child menu pane is %s\n", XtName(rc)));
944 
945     /* unhighlight the cascade button that popped us up. */
946     if (RC_CascadeBtn(rc))
947     {
948 	DEBUGOUT(_LtDebug(__FILE__, w, "  Removing myself from the cascade\n"));
949 
950 	/* disarm the cascade button that pulled us down. */
951 	if (XmIsPrimitive(RC_CascadeBtn(rc)))
952 	{
953 	    CB_SetArmed(RC_CascadeBtn(rc), False);
954 	}
955 	else
956 	{
957 	    CBG_SetArmed(RC_CascadeBtn(rc), False);
958 	}
959 	XmCascadeButtonHighlight(RC_CascadeBtn(rc), False);
960 
961 	/* clean up everything so noone thinks we're
962 	   popped up. */
963 	RC_PopupPosted(XtParent(RC_CascadeBtn(rc))) = NULL;
964 	RC_CascadeBtn(rc) = NULL;
965     }
966 
967     DEBUGOUT(_LtDebug(__FILE__, w, "  Popping down\n"));
968 
969     if (MS_PrivateShell(w))
970     {
971 	DEBUGOUT(_LtDebug(__FILE__, w, "  PrivateShell\n"));
972 
973 	_XmXtMenuPopdown(w, NULL, NULL, NULL);
974     }
975     else
976     {
977 	DEBUGOUT(_LtDebug(__FILE__, w, "  public shell\n"));
978 	if (RC_Type(rc) == XmMENU_POPUP || RC_Type(rc) == XmMENU_OPTION)
979 	{
980 	    _XmRemoveGrab(w);
981 	}
982 
983 	XtUnmapWidget(w);
984 	XtCallCallbacks(w, XmNpopdownCallback, NULL);
985 	XtUnmanageChild(rc);
986     }
987 	_XmCallRowColumnUnmapCallback(rc, event);
988 }
989 
990 
991 static void
MenuShellPopdownEveryone(Widget w,XEvent * event,String * params,Cardinal * num_params)992 MenuShellPopdownEveryone(Widget w, XEvent *event,
993 			 String *params, Cardinal *num_params)
994 {
995     Cardinal i;
996     Widget rc;
997 
998     /* must be a menu shell here:
999     assert(XmIsMenuShell(w)); */
1000 
1001     DEBUGOUT(_LtDebug(__FILE__, w, "Popping down everyone - %i kids\n",
1002     		MGR_NumChildren(w)));
1003 
1004     if (MGR_NumChildren(w) == 0)
1005     {
1006 	return;
1007     }
1008 
1009     for (i = 0; i < MGR_NumChildren(w); i++)
1010     {
1011 	rc = MGR_Children(w)[i];
1012 
1013 	DEBUGOUT(_LtDebug2(__FILE__, w, rc,"child %i\n", i));
1014 	if (!rc)
1015 	{
1016 	    continue;
1017 	}
1018 
1019 	if (RC_PopupPosted(rc))
1020 	{
1021 	    /* pop down our own sub tree, then ourselves. */
1022 	    Widget shell = XtParent(RC_PopupPosted(rc));
1023 
1024 	    if (!shell)
1025 	    {
1026 		continue;
1027 	    }
1028 
1029 	    if (XmIsMenuShell(shell))
1030 	    {
1031 		DEBUGOUT(_LtDebug(__FILE__, shell,
1032 			  "  recursing in PopdownEveryone.\n"));
1033 		MenuShellPopdownEveryone(shell, event, params, num_params);
1034 	    }
1035 	}
1036 	DEBUGOUT(_LtDebug(__FILE__, w, "  calling popdownOne.\n"));
1037 
1038 	MenuShellPopdownOne(XtParent(rc), event, params, num_params);
1039 #if 0
1040 	_XmCallRowColumnUnmapCallback(rc, event);
1041 #endif
1042 	DEBUGOUT(_LtDebug2(__FILE__, w, rc,"child %i parent %s\n",
1043 			i,
1044 			XtName(XtParent(rc))));
1045     }
1046     for (i = 0; i < MGR_NumChildren(w); i++)
1047     {
1048 	rc = MGR_Children(w)[i];
1049 	if (_XmIsActiveTearOff(rc))
1050 	{
1051 	    RCClass_MenuProcs(XtClass(rc))(XmMENU_RESTORE_TEAROFF_TO_TOPLEVEL_SHELL,
1052 					 rc, event);
1053 	}
1054     }
1055 }
1056 
1057 
1058 static void
MenuShellPopupSharedMenuPane(Widget w,Widget w2,XEvent * event)1059 MenuShellPopupSharedMenuPane(Widget w, Widget w2, XEvent *event)
1060 {
1061     DEBUGOUT(_LtDebug(__FILE__, w, "Popping up shared pane\n"));
1062 
1063     /* must be a menu shell here:
1064     assert(XmIsMenuShell(w)); */
1065 
1066     XtManageChild(w2);
1067     XRaiseWindow(XtDisplay(w2), XtWindow(w2));
1068     _XmResizeObject(w, XtWidth(w2) == 0 ? 1 : XtWidth(w2), XtHeight(w2) == 0 ? 1 : XtHeight(w2), 0);
1069 
1070     XtRealizeWidget(w);
1071     XMapRaised(XtDisplay(w), XtWindow(w));
1072     XtCallCallbacks(w, XmNpopupCallback, NULL);
1073 #ifdef	NEW_MAPCALLBACK
1074     /*
1075     _XmCallRowColumnMapCallback(w2, event);
1076     */
1077 #endif
1078     Shell_PoppedUp(w) = True;
1079 
1080 #ifdef USE_FOCUS
1081     DEBUGOUT(_LtDebug(__FILE__, w,
1082 		      "FOCUS SET: %s %s\n", XtName(w), XtName(w2)));
1083 
1084     _XmMenuFocus(w2, XmMENU_FOCUS_SET, CurrentTime);
1085 #endif
1086 
1087     XAllowEvents(XtDisplay(w2), SyncPointer/*ReplayPointer quick release problem */, CurrentTime);
1088 
1089     _XmAddGrab(w, False, False);
1090 }
1091 
1092 
1093 static void
_XmXtMenuPopup(Widget widget,XEvent * event,String * params,Cardinal * num_params)1094 _XmXtMenuPopup(Widget widget, XEvent *event,
1095 	       String *params, Cardinal *num_params)
1096 {
1097     /* for popup menus */
1098     XtRealizeWidget(widget);
1099     XMapRaised(XtDisplay(widget), XtWindow(widget));
1100     XtCallCallbacks(widget, XmNpopupCallback, NULL);
1101 #ifdef	NEW_MAPCALLBACK
1102     /*
1103     _XmCallRowColumnMapCallback(w2, event);
1104     */
1105 #endif
1106     Shell_PoppedUp(widget) = True;
1107 
1108     XAllowEvents(XtDisplay(widget), SyncBoth, event->xbutton.time);
1109 
1110     _XmGrabKeyboard(widget, True, GrabModeSync, GrabModeSync, CurrentTime);
1111 
1112     _XmGrabPointer(widget, True, (ButtonPressMask | ButtonReleaseMask |
1113                                   EnterWindowMask | LeaveWindowMask),
1114                    GrabModeSync, GrabModeSync, None,
1115                    _XmGetMenuCursorByScreen(XtScreen(widget)), CurrentTime);
1116 
1117     _XmAddGrab(widget, True, True);
1118 
1119     XAllowEvents(XtDisplay(widget), SyncPointer, CurrentTime);
1120 }
1121 
1122 
1123 static void
_XmXtMenuPopdown(Widget widget,XEvent * event,String * params,Cardinal * num_params)1124 _XmXtMenuPopdown(Widget widget, XEvent *event,
1125 		 String *params, Cardinal *num_params)
1126 {
1127     DEBUGOUT(_LtDebug(__FILE__, widget, "_XmXtMenuPopdown()\n"));
1128     if (MS_PrivateShell(widget))
1129     {
1130         XtUnmapWidget(widget);
1131 	XtCallCallbacks(widget, XmNpopdownCallback, NULL);
1132 	Shell_PoppedUp(widget) = False;
1133 
1134 #ifdef USE_FOCUS
1135 #if 0
1136 	DEBUGOUT(_LtDebug(__FILE__, widget,
1137 			  "RESTORE: %s\n", XtName(widget)));
1138 
1139         _XmMenuFocus(widget, XmMENU_FOCUS_RESTORE, CurrentTime);
1140 #endif
1141 #endif
1142 
1143         _XmRemoveGrab(widget);
1144     }
1145 }
1146 
1147 
1148 extern void
_XmEnterRowColumn(Widget widget,XtPointer closure,XEvent * event,Boolean * cont)1149 _XmEnterRowColumn(Widget widget, XtPointer closure,
1150 		  XEvent *event, Boolean *cont)
1151 {
1152     DEBUGOUT(_LtDebug(__FILE__, widget,
1153 		      "_XmEnterRowColumn(not implemented)\n"));
1154 }
1155 
1156 
1157 extern void
_XmClearTraversal(Widget wid,XEvent * event,String * params,Cardinal * num_params)1158 _XmClearTraversal(Widget wid, XEvent *event,
1159 		  String *params, Cardinal *num_params)
1160 {
1161     DEBUGOUT(_LtDebug(__FILE__, wid,
1162 		      "_XmClearTraversal(not implemented)\n"));
1163 
1164     XAllowEvents(XtDisplay(wid), SyncPointer, CurrentTime);
1165 }
1166 
1167 
1168 extern void
_XmSetLastManagedMenuTime(Widget wid,Time newTime)1169 _XmSetLastManagedMenuTime(Widget wid,
1170 			  Time newTime)
1171 {
1172     XmMenuState state = _XmGetMenuState(wid);
1173 
1174     state->MS_LastManagedMenuTime = newTime;
1175 }
1176 
1177 
1178 extern Widget
XmCreateMenuShell(Widget parent,char * name,ArgList arglist,Cardinal argcount)1179 XmCreateMenuShell(Widget parent, char *name, ArgList arglist, Cardinal argcount)
1180 {
1181     while (parent && !XtIsComposite(parent))
1182 	parent = XtParent(parent);
1183 
1184     return XtCreatePopupShell(name, xmMenuShellWidgetClass, parent,
1185 			      arglist, argcount);
1186 }
1187 
GetRenderTable(Widget w,XtEnum renderTableType)1188 static XmRenderTable GetRenderTable(Widget w, XtEnum renderTableType)
1189 {
1190 	XmMenuShellWidget bb = (XmMenuShellWidget)w;
1191 
1192 	switch(renderTableType) {
1193 	case XmLABEL_RENDER_TABLE:
1194 		return bb->menu_shell.label_font_list;
1195 	case XmBUTTON_RENDER_TABLE:
1196 		return bb->menu_shell.button_font_list;
1197 	case XmTEXT_RENDER_TABLE:
1198 		return NULL;	/* ?? FIX ME FIXME */
1199 #if 0
1200 		return bb->menu_shell.text_font_list;
1201 #endif
1202 	}
1203 	return NULL;
1204 }
1205 
1206