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