2
* Motif Tools Library, Version 3.1
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.
12
* Revision 1.1.1.1 2001/07/18 11:06:02 root
15
* Revision 1.2 2001/06/12 16:25:28 andre
16
* *** empty log message ***
24
#include <Xmt/MenuP.h>
25
#include <Xmt/ConvertersP.h>
26
#include <Xmt/Lexer.h>
29
* These are keywords and symbolic names for keywords, used by the lexer.
31
static String keywords[] = {
68
#if NeedFunctionPrototypes
69
static int ParseAccelerator(XmtLexer l, XmtMenuItem *item)
71
static int ParseAccelerator(l, item)
78
char accel_label[200];
81
accel_label[0] = '\0';
83
while((XmtLexerGetToken(l) == XmtLexerKeyword) &&
84
((XmtLexerKeyValue(l) == CTRL) ||
85
(XmtLexerKeyValue(l) == SHIFT) ||
86
(XmtLexerKeyValue(l) == META) ||
87
(XmtLexerKeyValue(l) == ALT) ||
88
(XmtLexerKeyValue(l) == LOCK))) {
89
strcat(accel, XmtLexerStrValue(l));
90
strcat(accel_label, XmtLexerStrValue(l));
91
XmtLexerConsumeToken(l);
92
tok = XmtLexerGetToken(l);
93
if (tok == XmtLexerPlus) {
95
strcat(accel_label, "+");
96
XmtLexerConsumeToken(l);
98
else if (tok == XmtLexerMinus) {
100
strcat(accel_label, "-");
101
XmtLexerConsumeToken(l);
104
XmtWarningMsg("XmtMenu", "missingPlus",
105
"item \"%s\": modifier name in accelerator must be followed by `+' or `-'",
111
strcat(accel, "<Key>");
112
tok = XmtLexerGetToken(l);
114
if ((tok == XmtLexerIdent) || (tok == XmtLexerString)) {
115
char *sym = XmtLexerStrValue(l);
121
sprintf(buf, "%d", sym[0]);
125
strcat(accel_label, sym);
127
XmtLexerConsumeToken(l);
129
else if (tok == XmtLexerInteger) {
131
sprintf(buf, "%d", XmtLexerIntValue(l));
133
strcat(accel_label, buf);
134
XmtLexerConsumeToken(l);
137
XmtWarningMsg("XmtMenu", "missingKeysym",
138
"item \"%s\": accelerator is missing keysym",
143
item->accelerator = XtNewString(accel);
144
item->accelerator_label = XtNewString(accel_label);
148
/* read forward until matching ']' */
150
tok = XmtLexerGetToken(l);
151
if ((tok == XmtLexerRBracket) || (tok == XmtLexerEndOfString)) break;
152
XmtLexerConsumeToken(l);
154
item->accelerator = item->accelerator_label = NULL;
158
#if NeedFunctionPrototypes
159
static void ParseLabelAndMnemonic(XmtLexer l, String *label,
160
int look_for_mnemonic, char *mnemonic)
162
static void ParseLabelAndMnemonic(l, label, look_for_mnemonic, mnemonic)
165
int look_for_mnemonic;
172
if (XmtLexerGetToken(l) != XmtLexerString) return;
174
if (look_for_mnemonic) {
175
*label = XtMalloc(XmtLexerStrLength(l) + 1);
178
for(i=0, c = XmtLexerStrValue(l); *c != '\0'; c++) {
179
if ((*c == '_') && (*mnemonic == '\0'))
186
XtFree(XmtLexerStrValue(l));
189
*label = XmtLexerStrValue(l);
193
XmtLexerConsumeToken(l);
199
* item:: [name] '-' '-'* ';' // a separator
200
* | [name] '=' '='* ';' // a double separator
201
* | [name] [type] flags* [label] [accelerator]
202
* [submenu] [symbol] callbacks
205
* type:: 'Title' | 'Button' | 'Toggle' |
206
* 'Line' | 'DoubleLine' | 'Submenu'
207
* flags:: 'On' | 'Off' | 'Help' | 'Tearoff' | 'Pixmap'
208
* label:: string-with-embedded-mnemonic [ '|' string ]
209
* accelerator:: '[' (modifier ('+'|'-'))* keysym ']'
210
* modifier:: 'Ctrl' | 'Shift' | 'Meta' | 'Alt' | 'Lock'
212
* submenu:: '-' '>' ident
214
* callbacks:: callback | '{' callback+ '}' | ';'
215
* callback:: ident '(' [args] ')' ';'
216
* args:: list-of-comma-separated-strings
219
#if NeedFunctionPrototypes
220
static int ParseItem(Widget w, XmtLexer l, XmtMenuItem *item)
222
static int ParseItem(w, l, item)
229
static XmtMenuItem null_item; /* static memory; all fields NULL */
231
/* initialize item structure */
232
null_item.type = XmtMenuItemEnd; /* undefined type, no flags */
235
/* parse item name */
236
if (XmtLexerGetToken(l) == XmtLexerIdent) {
237
item->name = XmtLexerStrValue(l);
238
XmtLexerConsumeToken(l);
239
if (XmtLexerGetToken(l) == XmtLexerColon)
240
XmtLexerConsumeToken(l);
242
XmtWarningMsg("XmtMenu", "colonExpected",
243
"colon expected after item name %s.",
247
/* parse separators as a special case */
248
if (((tok = XmtLexerGetToken(l)) == XmtLexerMinus) ||
249
(tok == XmtLexerEqual)) { /* a separator */
250
if (tok == XmtLexerMinus) item->type = XmtMenuItemSeparator;
251
else item->type = XmtMenuItemDoubleSeparator;
253
while (((tok = XmtLexerGetToken(l)) == XmtLexerMinus) ||
254
(tok == XmtLexerEqual))
255
XmtLexerConsumeToken(l);
257
if (tok == XmtLexerSemicolon) {
258
XmtLexerConsumeToken(l);
262
XmtWarningMsg("XmtMenu", "semicolonAfterSeparator",
263
"semicolon expeced after separator.");
269
* parse an optional type keyword.
270
* We can set the type directly, because we know that no flags are set yet.
272
if (XmtLexerGetToken(l) == XmtLexerKeyword) {
273
switch(XmtLexerKeyValue(l)) {
274
case TITLE: item->type = XmtMenuItemLabel; break;
275
case BUTTON: item->type = XmtMenuItemPushButton; break;
276
case TOGGLE: item->type = XmtMenuItemToggleButton; break;
277
case LINE: item->type = XmtMenuItemSeparator; break;
278
case DOUBLELINE: item->type = XmtMenuItemDoubleSeparator; break;
279
case SUBMENU: item->type = XmtMenuItemCascadeButton; break;
281
/* if we set a type eat the token */
282
if (item->type != XmtMenuItemEnd) XmtLexerConsumeToken(l);
286
* If the type is not set, assume it is a push button until
287
* we find a flag or something else that indicates otherwise.
289
if (item->type == XmtMenuItemEnd)
290
item->type = XmtMenuItemPushButton;
293
* now that we're going to set flags, we've got to be
294
* careful when setting the type. Use this macro:
296
#define SetType(item, value) ((item)->type = ((item)->type & ~0x7) | (value))
299
* parse any flags that modify the type.
301
while (XmtLexerGetToken(l) == XmtLexerKeyword) {
302
switch(XmtLexerKeyValue(l)) {
304
SetType(item, XmtMenuItemToggleButton);
305
item->type |= XmtMenuItemOn;
308
SetType(item, XmtMenuItemToggleButton);
309
item->type &= ~XmtMenuItemOn;
312
item->type |= XmtMenuItemHelp;
315
item->type |= XmtMenuItemTearoff;
318
item->type |= XmtMenuItemPixmap;
321
XmtWarningMsg("XmtMenu", "unexpectedKeyword",
322
"item \"%s\": unexpected keyword \"%s\"",
323
(item->label)?item->label:"",
324
XmtLexerStrValue(l));
327
XmtLexerConsumeToken(l);
331
/* parse item label */
332
if (XmtLexerGetToken(l) == XmtLexerString) {
333
/* the item label string; figure out label and mnemonic */
334
ParseLabelAndMnemonic(l, &item->label,
335
!(item->type & XmtMenuItemPixmap),
337
if (XmtLexerGetToken(l) == XmtLexerBar) { /* alternate label */
338
SetType(item, XmtMenuItemToggleButton);
339
if (XmtLexerNextToken(l) != XmtLexerString) {
340
XmtWarningMsg("XmtMenu", "missingLabel",
341
"item '%s': alternate label expected after '|'",
345
ParseLabelAndMnemonic(l, &item->alt_label,
346
!(item->type & XmtMenuItemPixmap),
347
&item->alt_mnemonic);
351
/* parse item accelerator */
352
if (XmtLexerGetToken(l) == XmtLexerLBracket) { /* an accelerator */
353
XmtLexerConsumeToken(l);
354
(void) ParseAccelerator(l, item);
355
if (XmtLexerGetToken(l) == XmtLexerRBracket)
356
XmtLexerConsumeToken(l);
358
XmtWarningMsg("XmtMenu", "missingRBracket",
359
"`]' expected at end of accelerator for item \"%s\"",
363
/* parse optional arrow and submenu name */
364
if (XmtLexerGetToken(l) == XmtLexerMinus) {
365
SetType(item, XmtMenuItemCascadeButton);
366
if (XmtLexerNextToken(l) == XmtLexerGreater)
367
XmtLexerConsumeToken(l);
368
if (XmtLexerGetToken(l) == XmtLexerIdent) {
369
item->submenu_name = XmtLexerStrValue(l);
370
XmtLexerConsumeToken(l);
373
XmtWarningMsg("XmtMenu", "missingSubmenuName",
374
"item \"%s\": submenu name expected after `->'.",
376
SetType(item, XmtMenuItemPushButton);
382
* Parse an optional symbol name, to be used by toggle buttons
384
if (XmtLexerGetToken(l) == XmtLexerDollar) {
385
if (XmtLexerNextToken(l) != XmtLexerIdent) {
386
XmtWarningMsg("XmtMenu", "missingSymbol",
387
"item '%s': symbol name expected after '$'.",
391
item->symbol_name = XmtLexerStrValue(l);
392
XmtLexerConsumeToken(l);
397
* Expect an identifier, a '{' or a ';'.
398
* To parse the callbacks, we extract the callback string from the
399
* private insides of the lexer and invoke whatever string-to-callback
400
* converter is registered.
402
tok = XmtLexerGetToken(l);
403
if ((tok == XmtLexerIdent) || (tok == XmtLexerLBrace)) { /* a callback */
404
char *callback_string;
407
Boolean stat = False;
409
if (tok == XmtLexerIdent) { /* a single callback */
410
/* back up by the length of the current identifier */
411
l->c -= XmtLexerStrLength(l);
412
XtFree(XmtLexerStrValue(l));
414
/* scan forward just past an unquoted semicolon */
415
tok = XmtLexerScan(l, ";", True);
416
if (tok == XmtLexerString) {
417
callback_string = XmtLexerStrValue(l);
418
len = XmtLexerStrLength(l);
419
XmtLexerConsumeToken(l);
421
else callback_string = NULL;
423
else { /* a compound callback */
424
XmtLexerConsumeToken(l); /* eat the open brace */
425
XmtLexerSkipWhite(l); /* skip whitespace */
426
/* scan forward to just before an unquoted close brace */
427
tok = XmtLexerScan(l, "}", False);
428
if (tok == XmtLexerString) {
429
callback_string = XmtLexerStrValue(l);
430
len = XmtLexerStrLength(l);
431
XmtLexerConsumeToken(l);
433
else callback_string = NULL;
435
/* Now get and eat the '}' token */
436
XmtLexerNextToken(l);
437
XmtLexerConsumeToken(l);
440
/* convert the string to an XtCallbackList */
441
if (!callback_string) {
442
XmtWarningMsg("XmtMenu", "badCallback",
443
"bad callback string for item '%s'",
449
* The ref counting on the callback converter somehow causes
450
* a core dump when the Menu widget is destroyed. So we don't
453
from.addr = callback_string;
455
to.addr = (XPointer) &item->callback;
456
to.size = sizeof(XtCallbackList);
457
stat = XtConvertAndStore(w, XtRString, &from, XtRCallback, &to);
459
if (_XmtCallbackConverter == NULL) {
460
XmtWarningMsg("XmtMenu", "noConverter",
461
"no String to XtCallbackList converter registered.\n\tCall XmtRegisterCallbackConverter().");
465
XtCacheRef ref_return;
467
* Here we just call the converter directly.
468
* We ignore the returned cache reference. This will only
469
* cause a memory leak if the menu is destroyed, and in
470
* that case, a memory leak is better than the core dump
473
from.addr = (XPointer) callback_string;
475
to.addr = (XPointer) &item->callback;
476
to.size = sizeof(XtCallbackList);
477
stat = XtCallConverter(XtDisplay(w), _XmtCallbackConverter,
479
&from, &to, &ref_return);
484
/* if conversion failed, a warning will already be issued */
485
if (!callback_string || !stat) item->callback = NULL;
487
/* set flag that says we have a XtCallbackList, not XtCallbackProc */
488
item->type |= XmtMenuItemCallbackList;
490
/* and free the callback string */
491
XtFree(callback_string);
493
else if (tok == XmtLexerSemicolon) { /* no callback at all */
494
XmtLexerConsumeToken(l);
497
XmtWarningMsg("XmtMenu", "semicolonExpected",
498
"item \"%s\": semicolon expected at end of item description.",
506
/* error recovery: free strings and read 'till `;' or end of string */
508
XtFree(item->accelerator);
509
XtFree(item->accelerator_label);
511
XtFree(item->submenu_name);
514
tok = XmtLexerGetToken(l);
515
if ((tok == XmtLexerSemicolon) || (tok == XmtLexerEndOfString)) break;
516
XmtLexerConsumeToken(l);
518
XmtLexerConsumeToken(l);
522
#if NeedFunctionPrototypes
523
Boolean XmtParseMenuString(Widget w, String str, XmtMenuItem **items_return)
525
Boolean XmtParseMenuString(w, str, items_return)
528
XmtMenuItem **items_return;
531
static XmtLexer l = NULL;
533
int num_items, max_items;
536
l = XmtLexerCreate(keywords, XtNumber(keywords));
538
XmtLexerInit(l, str);
542
items = (XmtMenuItem *) XtMalloc(sizeof(XmtMenuItem) * max_items);
544
while (XmtLexerGetToken(l) != XmtLexerEndOfString) {
545
if (num_items == max_items) {
547
items = (XmtMenuItem *) XtRealloc((char *) items,
548
sizeof(XmtMenuItem) * max_items);
550
if (ParseItem(w, l, &items[num_items]) == 0) /* if no error */
554
if (num_items == max_items) {
556
items = (XmtMenuItem *) XtRealloc((char *) items,
557
sizeof(XmtMenuItem) * max_items);
559
items[num_items].type = XmtMenuItemEnd; /* NULL-termination */
561
*items_return = items;
567
#if NeedFunctionPrototypes
568
Boolean XmtConvertStringToXmtMenuItems(Display *dpy,
569
XrmValuePtr args, Cardinal *num_args,
570
XrmValuePtr from, XrmValuePtr to,
571
XtPointer *closure_return)
573
Boolean XmtConvertStringToXmtMenuItems(dpy, args, num_args,
574
from, to, closure_return)
580
XtPointer *closure_return;
583
String str = (String)from->addr;
586
Widget w = *((Widget *)args[0].addr);
588
/* convert string to NULL-terminated array of XmtMenuItems */
589
status = XmtParseMenuString(w, str, &value);
591
if (status == False) return False;
593
done(XmtMenuItem *, value); /* a macro in ConvertersP.h */
597
#if NeedFunctionPrototypes
598
static void MenuItemsDestructor(XtAppContext app, XrmValue *to,
599
XtPointer converter_data,
600
XrmValue *args, Cardinal *num_args)
602
static void MenuItemsDestructor(app, to, converter_data, args, num_args)
605
XtPointer converter_data;
610
XmtMenuItem *items = *(XmtMenuItem **) to->addr;
613
for(i=0; items[i].type != XmtMenuItemEnd; i++) {
614
XtFree(items[i].label);
615
XtFree(items[i].accelerator);
616
XtFree(items[i].accelerator_label);
617
XtFree(items[i].name);
618
XtFree(items[i].submenu_name);
620
XtFree((char *)items);
623
static XtConvertArgRec convert_args[] = {
624
{XtBaseOffset, (XtPointer)XtOffsetOf(WidgetRec, core.self), sizeof(Widget)}
628
#if NeedFunctionPrototypes
629
void XmtRegisterMenuItemsConverter(void)
631
void XmtRegisterMenuItemsConverter()
634
static Boolean registered = False;
638
XtSetTypeConverter(XmRString, XmtRXmtMenuItemList,
639
XmtConvertStringToXmtMenuItems,
640
convert_args, XtNumber(convert_args),
641
XtCacheNone, MenuItemsDestructor);