1 /*
2  * Motif Tools Library, Version 3.1
3  * $Id: Menu.c,v 1.1.1.1 2001/02/10 13:48:49 motiftools Exp $
4  *
5  * Written by David Flanagan.
6  * Copyright (c) 1992-2001 by David Flanagan.
7  * All Rights Reserved.  See the file COPYRIGHT for details.
8  * This is open source software.  See the file LICENSE for details.
9  * There is no warranty for this software.  See NO_WARRANTY for details.
10  *
11  * $Log: Menu.c,v $
12  * Revision 1.1.1.1  2001/02/10 13:48:49  motiftools
13  * Initial import of Xmt310 to CVS
14  *
15  *
16  */
17 
18 #include <stdio.h>
19 #include <Xmt/XmtP.h>
20 #include <Xm/XmP.h>
21 #include <Xm/RowColumnP.h>
22 #include <Xm/MenuShellP.h>
23 #include <Xmt/MenuP.h>
24 #include <Xmt/Util.h>
25 #include <Xmt/ConvertersP.h>
26 #include <Xmt/Procedures.h>
27 #include <Xmt/Lexer.h>
28 #include <Xm/Separator.h>
29 #include <Xm/CascadeB.h>
30 #include <Xm/PushB.h>
31 #include <Xm/ToggleB.h>
32 #include <Xm/Label.h>
33 
34 #if XmVersion >= 2000
35 #include <Xm/MenuT.h>
36 #endif
37 #if XmVersion >= 2001
38 #include <Xm/TraitP.h>
39 #endif
40 
41 static XtResource resources[] = {
42 {XmtNitems, XmtCItems, XmtRXmtMenuItemList,
43      sizeof(XmtMenuItem *), XtOffsetOf(XmtMenuRec, menu.items),
44      XmtRXmtMenuItemList, NULL},
45 {XmtNnumItems, XmtCNumItems, XtRInt,
46      sizeof(Cardinal), XtOffsetOf(XmtMenuRec, menu.num_items),
47      XtRImmediate, (XtPointer) -1},
48 {XmtNacceleratorFontTag, XmtCAcceleratorFontTag, XtRString,
49      sizeof(String), XtOffsetOf(XmtMenuRec, menu.accelerator_font_tag),
50      XtRImmediate, NULL},
51 /* override RowCol default */
52 {XmNrowColumnType, XmCRowColumnType, XmRRowColumnType,
53      sizeof(unsigned char), XtOffsetOf(XmtMenuRec, row_column.type),
54      XtRImmediate, (XtPointer) XmMENU_BAR}
55 };
56 
57 #if NeedFunctionPrototypes
58 static void ClassPartInitialize(WidgetClass);
59 static void Initialize(Widget, Widget, ArgList, Cardinal *);
60 static void Destroy(Widget);
61 #else
62 static void ClassPartInitialize();
63 static void Initialize();
64 static void Destroy();
65 #endif
66 
67 #if XmVersion >= 1002
68 
69 #if NeedFunctionPrototype
70 static void ClassInitialize(void);
71 #else
72 static void ClassInitialize();
73 #endif
74 
75 /*
76  * we purposely inherit the initialize prehook here, but not the
77  * the initialize posthook.  The posthook scheme is buggy and does
78  * not allow recursive creations of submenus.  So instead, we undo
79  * what the prehook did explicitly in our Initialize() method.  It is
80  * okay that this is not done strictly at the "leaf class", because the
81  * prehook really only affects translations which are handled early on,
82  * in core class or somewhere.  See the comments in Initialize() for
83  * more info.
84  */
85 static XmBaseClassExtRec       baseClassExtRec = {
86     NULL,                                     /* Next extension       */
87     NULLQUARK,                                /* record type XmQmotif */
88     XmBaseClassExtVersion,                    /* version              */
89     sizeof(XmBaseClassExtRec),                /* size                 */
90     XmInheritInitializePrehook,               /* initialize prehook   */
91     XmInheritSetValuesPrehook,                /* set_values prehook   */
92     NULL,                                     /* initialize posthook  */
93     XmInheritSetValuesPosthook,               /* set_values posthook  */
94     XmInheritClass,                           /* secondary class      */
95     XmInheritSecObjectCreate,                 /* creation proc        */
96     XmInheritGetSecResData,                   /* getSecResData        */
97     {0},                                      /* fast subclass        */
98     XmInheritGetValuesPrehook,                /* get_values prehook   */
99     XmInheritGetValuesPosthook,               /* get_values posthook  */
100     NULL,                                     /* classPartInitPrehook */
101     NULL,                                     /* classPartInitPosthook*/
102     NULL,                                     /* ext_resources        */
103     NULL,                                     /* compiled_ext_resources*/
104     0,                                        /* num_ext_resources    */
105     FALSE,                                    /* use_sub_resources    */
106     XmInheritWidgetNavigable,                 /* widgetNavigable      */
107     XmInheritFocusChange,                     /* focusChange          */
108 };
109 
110 static XmManagerClassExtRec managerClassExtRec = {
111     NULL,
112     NULLQUARK,
113     XmManagerClassExtVersion,
114     sizeof(XmManagerClassExtRec),
115     XmInheritTraversalChildrenProc,
116 };
117 #endif
118 
119 externaldef(xmtmenuclassrec) XmtMenuClassRec xmtMenuClassRec = {
120   { /* core fields */
121     /* superclass		*/	(WidgetClass) &xmRowColumnClassRec,
122     /* class_name		*/	"XmtMenu",
123     /* widget_size		*/	sizeof(XmtMenuRec),
124 #if XmVersion >= 1002
125     /* class_initialize		*/	ClassInitialize,
126 #else
127     /* class_initialize		*/	NULL,
128 #endif
129     /* class_part_initialize	*/	ClassPartInitialize,
130     /* class_inited		*/	FALSE,
131     /* initialize		*/	Initialize,
132     /* initialize_hook		*/	NULL,
133     /* realize			*/	XtInheritRealize,
134     /* actions			*/	NULL,
135     /* num_actions		*/	0,
136     /* resources		*/	resources,
137     /* num_resources		*/	XtNumber(resources),
138     /* xrm_class		*/	NULLQUARK,
139     /* compress_motion		*/	TRUE,
140     /* compress_exposure	*/	XtExposeCompressMaximal,
141     /* compress_enterleave	*/	FALSE,
142     /* visible_interest		*/	FALSE,
143     /* destroy			*/	Destroy,
144     /* resize			*/	XtInheritResize,
145     /* expose			*/	XtInheritExpose,
146     /* set_values		*/	NULL,
147     /* set_values_hook		*/	NULL,
148     /* set_values_almost	*/	XtInheritSetValuesAlmost,
149     /* get_values_hook		*/	NULL,
150     /* accept_focus		*/	XtInheritAcceptFocus,
151     /* version			*/	XtVersion,
152     /* callback_private		*/	NULL,
153     /* tm_table			*/	XtInheritTranslations,
154     /* query_geometry		*/	XtInheritQueryGeometry,
155     /* display_accelerator	*/	XtInheritDisplayAccelerator,
156 #if XmVersion >= 1002
157     /* extension		*/	(XtPointer)&baseClassExtRec,
158 #else
159     /* extension		*/	NULL,
160 #endif
161   },
162   { /* composite_class fields */
163     /* geometry_manager   */    XtInheritGeometryManager,
164     /* change_managed     */    XtInheritChangeManaged,
165     /* insert_child       */    XtInheritInsertChild,
166     /* delete_child       */    XtInheritDeleteChild,
167     /* extension          */    NULL
168   },
169   { /* constraint_class fields */
170     /* resource list 	  */    NULL,
171     /* num resources	  */    0,
172     /* constraint size    */    sizeof(XmRowColumnConstraintRec),
173     /* init proc	  */    NULL,
174     /* destroy proc       */    NULL,
175     /* set values proc    */    NULL,
176     /* extension 	  */    NULL
177   },
178   { /* manager_class	  */
179     /* translations 	  */    XtInheritTranslations,
180     /* syn_resources	  */	NULL,
181     /* num_syn_resources  */	0,
182     /* syn_cont_resources */	NULL,
183     /* num_syn_cont_resources */0,
184     /* parent_process     */    XmInheritParentProcess,
185 #if XmVersion >= 1002
186     /* extension	  */	(XtPointer)&managerClassExtRec
187 #else
188     /* extension	  */	NULL
189 #endif
190   },
191   { /* row column class record        */
192     /* proc to interface with widgets */   XmInheritMenuProc,
193     /* proc to arm&activate menu      */   XmInheritArmAndActivate,
194     /* traversal handler              */   XmInheritMenuTraversalProc,
195     /* extension                      */   NULL,
196   },
197   { /* XmtMenuClassPart */
198     /* extension          */ NULL
199   }
200 };
201 
202 externaldef(xmtmenuwidgetclass)
203 WidgetClass xmtMenuWidgetClass = (WidgetClass)&xmtMenuClassRec;
204 
205 #if XmVersion == 2000
206 /* Traits Declarations */
207 extern XmMenuSystemTraitRec _XmRC_menuSystemRecord;
208 #endif
209 
210 
211 /*
212  * a macro for extracting the type bits from the type field of a menu item
213  */
214 #define GetType(item) ((item)->type & 0x07)
215 
216 
217 #if XmVersion >= 1002
218 
219 #if NeedFunctionPrototypes
ClassInitialize(void)220 static void ClassInitialize(void)
221 #else
222 static void ClassInitialize()
223 #endif
224 {
225     baseClassExtRec.record_type = XmQmotif;
226 
227 #if XmVersion == 2000
228     /* Trait records */
229     XmeTraitSet((XtPointer) xmtMenuWidgetClass, XmQTmenuSystem,
230                 (XtPointer) &_XmRC_menuSystemRecord);
231 #endif
232 #if XmVersion >= 2001
233     /* Trait records */
234     XmeTraitSet((XtPointer) xmtMenuWidgetClass, XmQTmenuSystem,
235                 XmeTraitGet ((XtPointer) xmRowColumnWidgetClass,
236 			     XmQTmenuSystem));
237 #endif
238 }
239 
240 #endif
241 
242 #if NeedFunctionPrototypes
ClassPartInitialize(WidgetClass subclass)243 static void ClassPartInitialize(WidgetClass subclass)
244 #else
245 static void ClassPartInitialize(subclass)
246 WidgetClass subclass;
247 #endif
248 {
249     XmtMenuWidgetClass sub = (XmtMenuWidgetClass)subclass;
250     XmtMenuWidgetClass super = (XmtMenuWidgetClass) sub->core_class.superclass;
251 
252     /*
253      * the XmtMenu widget doesn't have any class fields that need
254      * inheritance, but the XmRowColumn does, and that widget does not
255      * handle the inheritance itself.  So we do it here.
256      */
257 #define inherit(field) \
258     if ((XtProc)sub->field == _XtInherit) sub->field = super->field
259 
260      inherit(row_column_class.menuProcedures);
261      inherit(row_column_class.armAndActivate);
262      inherit(row_column_class.traversalHandler);
263 
264 #undef inherit
265 }
266 
267 
268 /* ARGSUSED */
269 #if NeedFunctionPrototypes
UpdateWidget(XmtSymbol s,XtPointer tag,XtArgVal value)270 static void UpdateWidget(XmtSymbol s, XtPointer tag, XtArgVal value)
271 #else
272 static void UpdateWidget(s, tag, value)
273 XmtSymbol s;
274 XtPointer tag;
275 XtArgVal value;
276 #endif
277 {
278     XmtMenuItem *item = (XmtMenuItem *)tag;
279 
280     /*
281      * When the user changes the widget, the callback below will change
282      * the symbol, and this callback will get called, and will go change
283      * the widget again.  But it changes the widget to the same value,
284      * and with notify False, it does not call the callbacks again, so
285      * it is okay.
286      */
287     XmToggleButtonSetState(item->w, value, False);
288     if (value)
289 	item->type |= XmtMenuItemOn;
290     else
291 	item->type &= ~XmtMenuItemOn;
292 }
293 
294 
295 /* ARGSUSED */
296 #if NeedFunctionPrototypes
ToggleCallback(Widget w,XtPointer tag,XtPointer data)297 static void ToggleCallback(Widget w, XtPointer tag, XtPointer data)
298 #else
299 static void ToggleCallback(w, tag, data)
300 Widget w;
301 XtPointer tag;
302 XtPointer data;
303 #endif
304 {
305     XmtMenuItem *item = (XmtMenuItem *)tag;
306     XmToggleButtonCallbackStruct *tbcs = (XmToggleButtonCallbackStruct *)data;
307 
308     /* remember the current state */
309     if (tbcs->set)
310 	item->type |= XmtMenuItemOn;
311     else
312 	item->type &= ~XmtMenuItemOn;
313 
314     /*
315      * if the toggle has a symbol, update the symbol.
316      */
317     if (item->symbol) {
318 	XmtSymbolSetValue(item->symbol, (XtArgVal)tbcs->set);
319     }
320 
321     /*
322      * if there is an alternate string (not a pixmap), add a callback
323      * to switch strings  when the state changes.
324      */
325     if (item->alt_label && !(item->type & XmtMenuItemPixmap))
326 	XtVaSetValues(item->w,
327 		     XmNlabelString, (tbcs->set)?item->label1:item->label0,
328 		     XmNmnemonic,(tbcs->set)?item->alt_mnemonic:item->mnemonic,
329 		     NULL);
330 }
331 
332 #if NeedFunctionPrototypes
CreateMenuItems(Widget w,XmtMenuItem * items,Cardinal num_items)333 static void CreateMenuItems(Widget w, XmtMenuItem *items, Cardinal num_items)
334 #else
335 static void CreateMenuItems(w, items, num_items)
336 Widget w;
337 XmtMenuItem *items;
338 Cardinal num_items;
339 #endif
340 {
341     XmtMenuWidget mw = (XmtMenuWidget) w;
342     XmtMenuItem *item;
343     Arg args[10], submenu_args[10];
344     int n, m;
345     XmString accel_label;
346     char namebuf[10], submenu_buf[20];
347     char *name, *submenu_name;
348     int numlabel, numpush, numtoggle, numsep, numsub;
349     int i;
350     static XrmQuark QBoolean = NULLQUARK;
351 
352     if (QBoolean == NULLQUARK) QBoolean = XrmPermStringToQuark(XtRBoolean);
353 
354     numlabel = numpush = numtoggle = numsep = numsub = 0;
355 
356     for(i=0; i < num_items; i++) {
357 	n = 0;
358 	item = &items[i];
359 
360 	/* XXX
361 	 * We should do some error checking here.  A cascade button
362 	 * shouldn't have accelerators, for example, and a non-cascade
363 	 * button shouldn't have the help flag set, and there are probably
364 	 * more.
365 	 */
366 
367 	switch(GetType(item)) {
368 	case XmtMenuItemLabel:
369 	    sprintf(namebuf, "label%d", numlabel);  numlabel++; break;
370 	case XmtMenuItemPushButton:
371 	    sprintf(namebuf, "button%d", numpush); numpush++; break;
372 	case XmtMenuItemToggleButton:
373 	    sprintf(namebuf, "toggle%d", numtoggle); numtoggle++; break;
374 	case XmtMenuItemCascadeButton:
375 	    sprintf(namebuf, "cascade%d", numsub); numsub++; break;
376 	case XmtMenuItemSeparator:
377 	case XmtMenuItemDoubleSeparator:
378 	    sprintf(namebuf, "separator%d", numsep); numsep++; break;
379 	}
380 
381 	if (item->name) name = item->name;
382 	else name = namebuf;
383 
384 	/* if there is a symbol name, bind it and get the initial state */
385 	if ((item->symbol_name) && (GetType(item) == XmtMenuItemToggleButton)){
386 	    Boolean state;
387 	    /*  XXX
388 	     * we should register a callback to free these symbols if
389 	     * the toggle buttons are destroyed.
390 	     */
391 	    item->symbol = XmtLookupSymbol(item->symbol_name);
392 	    if (!item->symbol ||
393 		(XmtSymbolTypeQuark(item->symbol) != QBoolean)) {
394 		XmtWarningMsg("XmtMenu", "symbolType",
395 			      "item '%s': symbol '%s' undefined or not of type Boolean",
396 			      item->label, item->symbol_name);
397 		item->symbol_name = NULL;
398 		item->symbol = NULL;
399 	    }
400 	    else {
401 		XmtSymbolGetValue(item->symbol, (XtArgVal *)&state);
402 		if (state) item->type |= XmtMenuItemOn;
403 		else item->type &= ~XmtMenuItemOn;
404 	    }
405 	}
406 
407 	/*
408 	 * if there is an alternate label, and this is a toggle button,
409 	 * create the alternate XmString or pixmap
410 	 */
411 	if (item->alt_label && (GetType(item) == XmtMenuItemToggleButton)) {
412 	    if (item->type & XmtMenuItemPixmap) {
413 		XrmValue from, to;
414 
415 		from.addr = item->alt_label;
416 		from.size = strlen(item->label) + 1;
417 		to.addr =(XPointer) &item->label1;
418 		to.size = sizeof(Pixmap);
419 		item->label1 = (XmString)XmUNSPECIFIED_PIXMAP;
420 		XtConvertAndStore(w, XtRString, &from, XtRPixmap, &to);
421 	    }
422 	    else item->label1 = XmtCreateXmString(item->alt_label);
423 	}
424 
425 	/* if there is a normal label, create the XmString or pixmap */
426 	if (item->label) {
427 	    if (item->type & XmtMenuItemPixmap) {
428 		XrmValue from, to;
429 
430 		from.addr = item->label;
431 		from.size = strlen(item->label) + 1;
432 		to.addr =(XPointer) &item->label0;
433 		to.size = sizeof(Pixmap);
434 		item->label0 = (XmString)XmUNSPECIFIED_PIXMAP;
435 		XtConvertAndStore(w, XtRString, &from, XtRPixmap, &to);
436 	    }
437 	    else item->label0 = XmtCreateXmString(item->label);
438 	}
439 
440 	/*
441 	 * if this is a toggle button w/ an alternate string and mnemonic,
442 	 * set whichever is appopriate for the initial state.  Otherwise,
443 	 * just set the normal one.
444 	 * Also, for toggle buttons with two labels, turn the indicator off.
445 	 */
446 	if (item->alt_label && (GetType(item) == XmtMenuItemToggleButton)) {
447 	    XtSetArg(args[n], XmNindicatorOn, False); n++;
448 	    if (item->type & XmtMenuItemPixmap) {
449 		XtSetArg(args[n], XmNlabelType, XmPIXMAP); n++;
450 		XtSetArg(args[n], XmNlabelPixmap, item->label0); n++;
451 		XtSetArg(args[n], XmNselectPixmap, item->label1); n++;
452 	    }
453 	    else {
454 		XtSetArg(args[n], XmNlabelString,
455 			 (item->type&XmtMenuItemOn)?item->label1:item->label0);
456 		n++;
457 		XtSetArg(args[n], XmNmnemonic, (item->type&XmtMenuItemOn)
458 			 ?item->alt_mnemonic
459 			 :item->mnemonic);
460 		n++;
461 	    }
462 	}
463 	else {
464 	    if (item->type & XmtMenuItemPixmap) {
465 		XtSetArg(args[n], XmNlabelType, XmPIXMAP); n++;
466 		XtSetArg(args[n], XmNlabelPixmap, item->label0); n++;
467 	    }
468 	    else {
469 		XtSetArg(args[n], XmNlabelString, item->label0); n++;
470 		XtSetArg(args[n], XmNmnemonic, item->mnemonic); n++;
471 	    }
472 	}
473 
474 	if (item->accelerator_label) {
475 	    if (mw->menu.accelerator_font_tag)
476 		accel_label = XmStringCreate(item->accelerator_label,
477 					     mw->menu.accelerator_font_tag);
478 	    else
479 		accel_label = XmStringCreateSimple(item->accelerator_label);
480 	    XtSetArg(args[n], XmNacceleratorText, accel_label); n++;
481 	}
482 
483 	if (item->accelerator) {
484 	    XtSetArg(args[n], XmNaccelerator, item->accelerator); n++;
485 	}
486 
487 	switch (GetType(item)) {
488 	case XmtMenuItemLabel:
489 	    item->w = XmCreateLabel(w, name, args, n);
490 	    break;
491 	case XmtMenuItemDoubleSeparator:
492 	    XtSetArg(args[n], XmNseparatorType, XmDOUBLE_LINE); n++;
493 	case XmtMenuItemSeparator:
494 	    item->w = XmCreateSeparator(w, name, args, n);
495 	    break;
496 	case XmtMenuItemPushButton:
497 	    item->w = XmCreatePushButton(w, name, args, n);
498 	    if (item->callback) {
499 		if (item->type & XmtMenuItemCallbackList)
500 		    XtAddCallbacks(item->w, XmNactivateCallback,
501 				   (XtCallbackList)item->callback);
502 		else
503 		    XtAddCallback(item->w, XmNactivateCallback,
504 				  item->callback, item->client_data);
505 	    }
506 	    break;
507 	case XmtMenuItemToggleButton:
508 	    XtSetArg(args[n], XmNset, (item->type&XmtMenuItemOn)?True:False);
509 	    n++;
510 	    item->w = XmCreateToggleButton(w, name, args, n);
511 
512 	    /*
513 	     * Add a callback to track the button state and do other
514 	     * housekeeping.
515 	     */
516 	    XtAddCallback(item->w, XmNvalueChangedCallback,
517 			  ToggleCallback, (XtPointer)item);
518 
519 	    /* Add any programmer specified callbacks */
520 	    if (item->callback) {
521 		if (item->type & XmtMenuItemCallbackList)
522 		    XtAddCallbacks(item->w, XmNvalueChangedCallback,
523 				   (XtCallbackList)item->callback);
524 		else
525 		    XtAddCallback(item->w, XmNvalueChangedCallback,
526 				  item->callback, item->client_data);
527 	    }
528 
529 	    /*
530 	     * if the item has a symbol, add a callback to be notifed
531 	     * when the symbol value changes under us.  Note that
532 	     * ToggleCallback handles updating the symbol when the
533 	     * toggle changes
534 	     */
535 	    if (item->symbol) {
536 		XmtSymbolAddCallback(item->symbol,
537 				     UpdateWidget, (XtPointer) item);
538 	    }
539 	    break;
540 	case XmtMenuItemCascadeButton:
541 	    m = 0;
542 	    if (item->submenu) {
543 		XtSetArg(submenu_args[m], XmtNitems, item->submenu); m++;
544 		sprintf(submenu_buf, "item%d_pane", i);
545 		submenu_name = submenu_buf;
546 	    }
547 	    else if (item->submenu_name) {
548 		submenu_name = item->submenu_name;
549 	    }
550 	    else {
551 		submenu_name = NULL;
552 		XmtWarningMsg("XmtMenu", "noSubmenu",
553 			      "no submenu specified for menu item %s",
554 			      item->label);
555 	    }
556 
557 	    if (submenu_name) {
558 		XtSetArg(submenu_args[m],XmNrowColumnType,XmMENU_PULLDOWN);m++;
559 		if (item->type & XmtMenuItemTearoff) {
560 #if XmVersion >= 1002
561 		    XtSetArg(submenu_args[m],
562 			     XmNtearOffModel, XmTEAR_OFF_ENABLED);
563 		    m++;
564 #else
565 		    XmtWarningMsg("XmtMenu", "tearoff",
566 		  "item '%s': tearoff menus not supported prior to Motif 1.2",
567 				  item->label);
568 #endif
569 		}
570 		item->submenu_pane = XmtCreateMenuPane(w, submenu_name,
571 						       submenu_args, m);
572 		XtSetArg(args[n], XmNsubMenuId, item->submenu_pane); n++;
573 		if (item->submenu == NULL)
574 		    item->submenu =
575 			((XmtMenuWidget)item->submenu_pane)->menu.items;
576 	    }
577 	    item->w = XmCreateCascadeButton(w, name, args, n);
578 	    break;
579 	default:  /* an error */
580 	    break;
581 	}
582 
583 	XtManageChild(item->w);
584 
585 	if ((GetType(item) == XmtMenuItemCascadeButton) &&
586 	    (item->type & XmtMenuItemHelp))
587 	    XtVaSetValues(w, XmNmenuHelpWidget, item->w, NULL);
588 
589 	/* free the XmStrings */
590 	if (item->label
591 	    && !(item->alt_label && (GetType(item) == XmtMenuItemToggleButton))
592 	    && !(item->type & XmtMenuItemPixmap))
593 	    XmStringFree(item->label0);
594 	if (item->accelerator_label)
595 	    XmStringFree(accel_label);
596     }
597 }
598 
599 
600 /*ARGSUSED*/
601 #if NeedFunctionPrototypes
Initialize(Widget request,Widget init,ArgList args,Cardinal * num_args)602 static void Initialize(Widget request, Widget init,
603 		       ArgList args, Cardinal *num_args)
604 #else
605 static void Initialize(request, init, args, num_args)
606 Widget request;
607 Widget init;
608 ArgList args;
609 Cardinal *num_args;
610 #endif
611 {
612     XmtMenuWidget w = (XmtMenuWidget)init;
613     int i;
614 
615 #if XmVersion >= 1002
616     /*
617      * In Motif 1.2, the first thing we do is restore our core_class
618      * translations field, which was munged by the initialize prehook
619      * method we inherited (in the base class extension record) from
620      * the XmRowColumn.  This restore would normally be done by a
621      * corresponding posthook method, but we purposely did not inherit
622      * that posthook, because if we have a posthook, then we cannot
623      * recursively create submenus (as we need to do) -- the post hook
624      * does not get restored soon enough, and the recursive creations
625      * are done without ever calling this initialize procedure.
626      */
627 #if NeedFunctionPrototypes
628     extern void _XmRestoreCoreClassTranslations(Widget);
629 #else
630     extern void _XmRestoreCoreClassTranslations();
631 #endif
632     _XmRestoreCoreClassTranslations(init);
633 #endif
634 
635     /*
636      * Copy the XmtNacceleratorFontTag string.  The only reason we have
637      * to do this is so that programmers can query that resource.
638      */
639     if (w->menu.accelerator_font_tag)
640 	w->menu.accelerator_font_tag =
641 	    XtNewString(w->menu.accelerator_font_tag);
642 
643     if (w->menu.items != NULL) {
644     /*
645      * The items resource may be NULL terminated at this point, or it may
646      * not be.  If num_items is -1 (ie. unspecified) then it had better be
647      * NULL-terminated.  The resource converter delivers a NULL-terminated
648      * array, but most programmers will probably use counted arrays.  It is
649      * also possible that items is NULL-terminated at less than num_items.
650      * So figure out num_items.  Note that NULL-terminated in this case
651      * means that the type field is XmtMenuItemEnd.
652      */
653 	if (w->menu.num_items == -1) {
654 	    /* if unspecified look for a NULL */
655 	    for(i=0; GetType(&w->menu.items[i]) != XmtMenuItemEnd; i++);
656 	    w->menu.num_items = i;
657 	}
658 	else {
659 	    /* if specified, look for a NULL anyway */
660 	    for(i=0;
661 		i<w->menu.num_items &&
662 		  GetType(&w->menu.items[i])!=XmtMenuItemEnd;
663 		i++);
664 	    w->menu.num_items = i;
665 	}
666     }
667     else {/* if no items are specified */
668 	w->menu.num_items = 0;
669 	XmtWarningMsg("XmtMenu", "noItems",
670 		      "no items specified for widget %s",
671 		      XtName(init));
672     }
673 
674     CreateMenuItems(init, w->menu.items, w->menu.num_items);
675 }
676 
677 #if NeedFunctionPrototypes
Destroy(Widget w)678 static void Destroy(Widget w)
679 #else
680 static void Destroy(w)
681 Widget w;
682 #endif
683 {
684     XmtMenuWidget mw = (XmtMenuWidget)w;
685 
686     XtFree(mw->menu.accelerator_font_tag);
687 }
688 
689 #if NeedFunctionPrototypes
XmtCreateMenubar(Widget w,String name,ArgList args,Cardinal num_args)690 Widget XmtCreateMenubar(Widget w, String name,
691 			ArgList args, Cardinal num_args)
692 #else
693 Widget XmtCreateMenubar(w, name, args, num_args)
694 Widget w;
695 String name;
696 ArgList args;
697 Cardinal num_args;
698 #endif
699 {
700     return XtCreateWidget(name, xmtMenuWidgetClass, w, args, num_args);
701 }
702 
703 #if NeedFunctionPrototypes
CreateMenuPane(Widget w,String name,ArgList args,Cardinal num_args,int type)704 static Widget CreateMenuPane(Widget w, String name,
705 		      ArgList args, Cardinal num_args, int type)
706 #else
707 static Widget CreateMenuPane(w, name, args, num_args, type)
708 Widget w;
709 String name;
710 ArgList args;
711 Cardinal num_args;
712 int type;
713 #endif
714 {
715     ArgList pane_args, shell_args;
716     Cardinal pane_num_args, shell_num_args;
717     Arg new_args[10];
718     String shell_name;
719     Widget parent, popup, shell = NULL;
720     Widget menu;
721     int i, n;
722 
723     /*
724      * if this menu pane is a submenu of a pulldown or a popup menu, then
725      * the programmer probably passed the rowcol as the parent widget.  What
726      * we really want is the menu shell that is the parent of that rowcol.
727      * This is kludgy, but it is what the Motif XmCreatePulldownMenu() does.
728      */
729     if (XtParent(w) && XmIsMenuShell(XtParent(w)))
730 	parent = XtParent(w);
731     else
732 	parent = w;
733 
734     /*
735      * Now we need a menu shell.  A menubar or menupane with multiple
736      * submenus need only have a single menu shell child, because only
737      * one submenu can be popped up at a time.  Therefore, before we
738      * go create a menu shell, we go see if one already exists as a popup
739      * child of our parent.  We only make this check if the supplied parent
740      * widget is a menubar or a menupane. Again, this is what Motif does.
741      */
742     if (XmIsRowColumn(w) &&
743 	((((XmRowColumnWidget)w)->row_column.type == XmMENU_BAR) ||
744 	 (((XmRowColumnWidget)w)->row_column.type == XmMENU_PULLDOWN) ||
745 	 (((XmRowColumnWidget)w)->row_column.type == XmMENU_POPUP))) {
746 	/* XXX
747 	 * Motif does the above test, which effectively means that popup
748 	 * menus never share shells.  I don't know if it is really needed.
749 	 */
750 	for (i = 0; i < parent->core.num_popups; i++) {
751 	    popup = parent->core.popup_list[i];
752 	    if (XmIsMenuShell(popup) && !popup->core.being_destroyed &&
753 		((XmMenuShellWidget)popup)->menu_shell.private_shell) {
754 		shell = popup;
755 		break;
756 	    }
757 	}
758     }
759 
760     /*
761      * if we can't share a shell, create one, and mark it as sharable.
762      * We use the same shell naming convention as Motif does.
763      */
764     if (!shell) {
765 	/* put together a new arg list */
766 	n = 0;
767 	XtSetArg(new_args[n], XmNwidth, 5); n++;
768 	XtSetArg(new_args[n], XmNheight, 5); n++;
769 	XtSetArg(new_args[n], XmNallowShellResize, True); n++;
770 	XtSetArg(new_args[n], XmNoverrideRedirect, True); n++;
771 	shell_args = XtMergeArgLists(args, num_args, new_args, n);
772 	shell_num_args = num_args + n;
773 
774 	/* put together the new name */
775 	shell_name = XtMalloc(strlen(name) + 7);
776 	sprintf(shell_name, "popup_%s", name);
777 
778 	/* create the shell */
779 	shell = XmCreateMenuShell(parent, shell_name,
780 				  shell_args, shell_num_args);
781 
782 	/* free the name and arglist */
783 	XtFree(shell_name);
784 	XtFree((char *)shell_args);
785 
786 	/* mark shell to allow sharing */
787 	((XmMenuShellWidget)shell)->menu_shell.private_shell = True;
788     }
789 
790     /*
791      * The default type of an XmtMenu widget is MENU_BAR.  We need to
792      * explictly specify that this one is a pulldown or popup type.
793      */
794     n = 0;
795     XtSetArg(new_args[n], XmNrowColumnType, type); n++;
796     pane_args = XtMergeArgLists(args, num_args, new_args, n);
797     pane_num_args = num_args + n;
798 
799     /* finally, create the menu */
800     menu = XtCreateWidget(name, xmtMenuWidgetClass, shell,
801 			  pane_args, pane_num_args);
802 
803     /* and free the allocated arglist */
804     XtFree((char *) pane_args);
805 
806     return menu;
807 }
808 
809 #if NeedFunctionPrototypes
XmtCreateMenuPane(Widget w,String name,ArgList args,Cardinal num_args)810 Widget XmtCreateMenuPane(Widget w, String name,
811 			 ArgList args, Cardinal num_args)
812 #else
813 Widget XmtCreateMenuPane(w, name, args, num_args)
814 Widget w;
815 String name;
816 ArgList args;
817 Cardinal num_args;
818 #endif
819 {
820     return CreateMenuPane(w, name, args, num_args, XmMENU_PULLDOWN);
821 }
822 
823 /* ARGSUSED */
824 #if NeedFunctionPrototypes
XmtMenuPopupHandler(Widget w,XtPointer data,XEvent * event,Boolean * cont)825 void XmtMenuPopupHandler(Widget w, XtPointer data, XEvent *event,Boolean *cont)
826 #else
827 void XmtMenuPopupHandler(w, data, event, cont)
828 Widget w;
829 XtPointer data;
830 XEvent *event;
831 Boolean *cont;
832 #endif
833 {
834     XmtMenuWidget menu = (XmtMenuWidget) data;
835     XButtonPressedEvent *e = (XButtonPressedEvent *)event;
836     int state;
837 
838     /* mask buttons out of modifiers/buttons state */
839     state = e->state & (ShiftMask | ControlMask | LockMask | Mod1Mask |
840 			Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask);
841 
842     if ((e->button == RC_PostButton(menu)) &&
843 	((state == RC_PostModifiers(menu)) ||
844 	 (RC_PostModifiers(menu) == AnyModifier))) {
845 	XmMenuPosition((Widget)menu, e);
846 	XtManageChild((Widget)menu);
847     }
848 }
849 
850 #if NeedFunctionPrototypes
XmtCreatePopupMenu(Widget w,String name,ArgList args,Cardinal num_args)851 Widget XmtCreatePopupMenu(Widget w, String name,
852 			  ArgList args, Cardinal num_args)
853 #else
854 Widget XmtCreatePopupMenu(w, name, args, num_args)
855 Widget w;
856 String name;
857 ArgList args;
858 Cardinal num_args;
859 #endif
860 {
861     Widget menu;
862 
863     menu = CreateMenuPane(w, name, args, num_args, XmMENU_POPUP);
864     XtAddEventHandler(w, ButtonPressMask,
865 		      False, XmtMenuPopupHandler, (XtPointer)menu);
866     return menu;
867 }
868 
869 #if NeedFunctionPrototypes
XmtCreateOptionMenu(Widget w,String name,ArgList args,Cardinal num_args)870 Widget XmtCreateOptionMenu(Widget w, String name,
871 			   ArgList args, Cardinal num_args)
872 #else
873 Widget XmtCreateOptionMenu(w, name, args, num_args)
874 Widget w;
875 String name;
876 ArgList args;
877 Cardinal num_args;
878 #endif
879 {
880     Widget pane;
881     Widget option;
882     String pane_name;
883 
884     /* XXX
885      * This is sort of a bad convenience routine.  It returns an XmRowColumn,
886      * rather than an XmMenu widget.  It passes the same arglist to both
887      * creation routines, and because they are similar widgets, there could
888      * be arguments that are only intended for one.
889      */
890 
891     pane_name = XtMalloc(strlen(name) + 6);
892     sprintf(pane_name, "%s_pane", name);
893     pane = XmtCreateMenuPane(w, pane_name, args, num_args);
894     XtFree(pane_name);
895 
896     option = XmCreateOptionMenu(w, (String)name, args, num_args);
897     XtVaSetValues(option, XmNsubMenuId, pane, NULL);
898     return option;
899 }
900 
901 
902 #if NeedFunctionPrototypes
XmtMenuItemGetSubmenu(XmtMenuItem * item)903 Widget XmtMenuItemGetSubmenu(XmtMenuItem *item)
904 #else
905 Widget XmtMenuItemGetSubmenu(item)
906 XmtMenuItem *item;
907 #endif
908 {
909     if (GetType(item) != XmtMenuItemCascadeButton) {
910 	XmtWarningMsg("XmtMenuItemGetSubmenu", "typeMismatch",
911 		      "specified item is not a cascade button.");
912 	return NULL;
913     }
914     else
915 	return item->submenu_pane;
916 }
917 
918 #if NeedFunctionPrototypes
XmtMenuItemGetWidget(XmtMenuItem * item)919 Widget XmtMenuItemGetWidget(XmtMenuItem *item)
920 #else
921 Widget XmtMenuItemGetWidget(item)
922 XmtMenuItem *item;
923 #endif
924 {
925     return item->w;
926 }
927 
928 #if NeedFunctionPrototypes
XmtMenuItemGetState(XmtMenuItem * item)929 Boolean XmtMenuItemGetState(XmtMenuItem *item)
930 #else
931 Boolean XmtMenuItemGetState(item)
932 XmtMenuItem *item;
933 #endif
934 {
935     if (GetType(item) != XmtMenuItemToggleButton) {
936 	XmtWarningMsg("XmtMenuItemGetState", "typeMismatch",
937 		      "specified item is not a toggle button.");
938 	return False;
939     }
940     else return ((item->type & XmtMenuItemOn) != 0);
941 }
942 
943 #if NeedFunctionPrototypes
XmtMenuItemSetState(XmtMenuItem * item,XmtWideBoolean state,XmtWideBoolean notify)944 void XmtMenuItemSetState(XmtMenuItem *item, XmtWideBoolean state,
945 			 XmtWideBoolean notify)
946 #else
947 void XmtMenuItemSetState(item, state, notify)
948 XmtMenuItem *item;
949 int state;
950 int notify;
951 #endif
952 {
953     if (GetType(item) != XmtMenuItemToggleButton) {
954 	XmtWarningMsg("XmtMenuItemSetState", "typeMismatch",
955 		      "specified item is not a toggle button.");
956     }
957     else {
958 	if (item->w)
959 	    XmToggleButtonSetState(item->w, state, notify);
960 	if (state) item->type |= XmtMenuItemOn;
961 	else item->type &= ~XmtMenuItemOn;
962     }
963 }
964 
965 #if NeedFunctionPrototypes
XmtMenuItemSetSensitivity(XmtMenuItem * item,XmtWideBoolean sensitive)966 void XmtMenuItemSetSensitivity(XmtMenuItem *item, XmtWideBoolean sensitive)
967 #else
968 void XmtMenuItemSetSensitivity(item, sensitive)
969 XmtMenuItem *item;
970 int sensitive;
971 #endif
972 {
973     if (sensitive) {
974 	if (item->sensitive == 0)  /* if already sensitive */
975 	    return;
976 	item->sensitive--;
977 	if ((item->sensitive == 0) && (item->w))  /* if newly sensitive */
978 	    XtSetSensitive(item->w, True);
979     }
980     else {
981 	if ((item->sensitive == 0) && item->w)  /* if currently sensitive */
982 	    XtSetSensitive(item->w, False);
983 	item->sensitive++;
984     }
985 }
986 
987 #if NeedFunctionPrototypes
GetMenuItem(XmtMenuWidget mw,XrmName quark)988 static XmtMenuItem *GetMenuItem(XmtMenuWidget mw, XrmName quark)
989 #else
990 static XmtMenuItem *GetMenuItem(mw, quark)
991 XmtMenuWidget mw;
992 XrmName quark;
993 #endif
994 {
995     int i;
996     XmtMenuItem *item, *subitem;
997 
998     /*
999      * check each of the items in the menu.
1000      * Note that we use the widget name rather than item->name, because
1001      * comparing quarks is faster.
1002      */
1003     for(i=0; i < mw->menu.num_items; i++) {
1004 	item = &mw->menu.items[i];
1005 	if (item->w && (item->w->core.xrm_name == quark))
1006 	    return item;
1007     }
1008 
1009     /* if no item matched, recursively check any submenus */
1010     for(i=0; i < mw->menu.num_items; i++) {
1011 	item = &mw->menu.items[i];
1012 	if (GetType(item) == XmtMenuItemCascadeButton) {
1013 	    subitem = GetMenuItem((XmtMenuWidget)item->submenu_pane, quark);
1014 	    if (subitem) return subitem;
1015 	}
1016     }
1017 
1018     /* if no subitem matched, give up */
1019     return NULL;
1020 }
1021 
1022 
1023 #if NeedFunctionPrototypes
XmtMenuGetMenuItem(Widget w,StringConst name)1024 XmtMenuItem *XmtMenuGetMenuItem(Widget w, StringConst name)
1025 #else
1026 XmtMenuItem *XmtMenuGetMenuItem(w, name)
1027 Widget w;
1028 StringConst name;
1029 #endif
1030 {
1031     XmtMenuWidget mw = (XmtMenuWidget) w;
1032     XmtMenuItem *item;
1033 
1034     XmtAssertWidgetClass(w, xmtMenuWidgetClass, "XmtMenuGetMenuItem");
1035     item = GetMenuItem(mw, XrmStringToQuark(name));
1036     return item;
1037 }
1038 
1039 #if NeedFunctionPrototypes
CheckSensitivity(Widget w,XtCallbackProc proc,Boolean sensitive)1040 static void CheckSensitivity(Widget w, XtCallbackProc proc, Boolean sensitive)
1041 #else
1042 static void CheckSensitivity(w, proc, sensitive)
1043 Widget w;
1044 XtCallbackProc proc;
1045 Boolean sensitive;
1046 #endif
1047 {
1048     XmtMenuWidget mw = (XmtMenuWidget) w;
1049     XmtMenuItem *item;
1050     int i;
1051 
1052     if (!mw) return;
1053 
1054     for(i=0; i < mw->menu.num_items; i++) {
1055 	item = &mw->menu.items[i];
1056 	if (GetType(item) == XmtMenuItemCascadeButton)
1057 	    CheckSensitivity(item->submenu_pane, proc, sensitive);
1058 	else {
1059 	    if (((item->type & XmtMenuItemCallbackList) &&
1060 		 (XmtCallbackCheckList((XtCallbackList) item->callback,
1061 				       (XmtProcedure) proc))) ||
1062 		(!(item->type & XmtMenuItemCallbackList) &&
1063 		 (item->callback == proc)))
1064 		XmtMenuItemSetSensitivity(item, sensitive);
1065 	}
1066     }
1067 }
1068 
1069 #if NeedFunctionPrototypes
XmtMenuInactivateProcedure(Widget w,XtCallbackProc proc)1070 void XmtMenuInactivateProcedure(Widget w, XtCallbackProc proc)
1071 #else
1072 void XmtMenuInactivateProcedure(w, proc)
1073 Widget w;
1074 XtCallbackProc proc;
1075 #endif
1076 {
1077     XmtAssertWidgetClass(w, xmtMenuWidgetClass, "XmtMenuInactivateProcedure");
1078     CheckSensitivity(w, proc, False);
1079 }
1080 
1081 #if NeedFunctionPrototypes
XmtMenuActivateProcedure(Widget w,XtCallbackProc proc)1082 void XmtMenuActivateProcedure(Widget w, XtCallbackProc proc)
1083 #else
1084 void XmtMenuActivateProcedure(w, proc)
1085 Widget w;
1086 XtCallbackProc proc;
1087 #endif
1088 {
1089     XmtAssertWidgetClass(w, xmtMenuWidgetClass, "XmtMenuActivateProcedure");
1090     CheckSensitivity(w, proc, True);
1091 }
1092 
1093