1 /********************************************************************\
2 ** _________________________________ **
3 ** A n t h o n y |________ __ __ _________| **
4 ** | |o_| |o_| | **
5 ** T h y s s e n __| __ __ |__ **
6 ** __| __| | | |__ |__ **
7 ** `` Dragon Computing! '' __| __| | | |__ |__ **
8 ** |_____| |__| |_____| **
9 ** **
10 \********************************************************************/
11 /*
12 ** user-menu.c
13 **
14 ** This module reads and handles a user configuration (RC) file defining
15 ** a number of user menus. The module is application indepentant only
16 ** requiring application specific declarations at the top of the file.
17 ** The following features are provided by this module.
18 **
19 ** * The "menu" keyword in the config file (see example below) is used
20 ** define the menu to which new menu items are to be added. Only the
21 ** menus named in the ``decl_menus'' structure below can be given by the
22 ** user. If the menu widget variable pointed to in this structure is
23 ** currently set to ``None'', then the module assumes it needs to create
24 ** the menu and does so, using the same menu name the user uses in the
25 ** config file.
26 **
27 ** The order in which menus are created in the config file is not
28 ** important, and it is even posible for the user to switch back and
29 ** forth between menus as desired. If the program did not pre-create the
30 ** menu and the user decided not to use that menu, that menu will not be
31 ** created.
32 **
33 ** If the programmer pre-created the menu widget pointed to, with some
34 ** items in it also pre-added, the user specified items will be appended
35 ** to these items.
36 **
37 ** It is up to the other parts of the application to popup and popdown
38 ** the created menus. The actual menu items added by the user however
39 ** will call an internal routine which calles the function sequence the
40 ** user wanted for this menu item. (see below)
41 **
42 ** * Each item read from the config file call a number of functions whose
43 ** names are declared in the ``decl_functs'' structure below. These
44 ** functions are to be defined in another module of the application. Each
45 ** function must be of type `void', and can have upto TWO string
46 ** arguments. These arguments are given by the user in the menu
47 ** configuraion file and certian string substitutions are pre-performed
48 ** before the function is called (see below).
49 **
50 ** If the function fails for some reason and wants to abort the current
51 ** it can abort the current function sequence on this menu item, it can
52 ** do so by calling menu_item_abort() before it returns. This abort
53 ** function will clear the current function sequence and free the menu
54 ** handler to accept new menu selections by the user.
55 **
56 ** A function that has the input flag set to TRUE is expected to popup a
57 ** dialog widget of some type before returning. The callbacks for this
58 ** input widget, after setting appropiate substition variables, is
59 ** expected to call either menu_item_abort() or menu_item_continue()
60 ** as appropiate, just before it returns back to the mail application
61 ** processing loop. This method will allow the application to ask the
62 ** user for some input or some confirmation before continuing the menu
63 ** function sequence, as set by the user in configuration file.
64 **
65 ** * The arguments are strings provided by the user in the config file.
66 ** Each is surrounded by either double quotes `"' or single quotes `''
67 ** and can contain any number of string substitution ( % ) escapes in a
68 ** simular fashion to the C library function printf(). These substition
69 ** escape characters are replaced by string variables, declared in the
70 ** ``decl_subchars'' structure below, before calling the appropiate
71 ** function. The substitution charcater will escape itself.
72 **
73 ** * The module also provides a `line' keyword as an menu item seperator
74 ** within the user menu config file.
75 **
76 ** * External Functions provided are :-
77 **
78 ** read_user_menu( parent_widget, file_ptr, file_name)
79 ** Parse the open file, createing the user menus as declared in the
80 ** top section of this module.
81 **
82 ** menu_item_abort()
83 ** Notify the item handler not to continue the current menu_item
84 ** when the current routine returns.
85 **
86 ** menu_item_continue()
87 ** Continue processing user functions after the user has returned
88 ** from a input dialog user function. The only action the caller
89 ** should do after calling this function is to return back to the
90 ** main application loop. Any other action performed may have
91 ** undefined results.
92 **
93 ** * FUTURE:-
94 ** * Figure out how to add `title' elements which do not highlight
95 ** * Addition of user defined Sub-Menus (what a lot of work).
96 **
97 ** # Example application user menu configuration `RC' file.
98 ** # This may not be valid for the current application.
99 ** #
100 ** # Any line starting with a `#' is a comment line and is ignored
101 ** # Any lines ending with a `\' as the very last character of a
102 ** # line is continued onto the next line.
103 ** #
104 ** menu "usermenu" "User Menu"
105 ** # Create the menu `usermenu' if required. This name must match one of
106 ** # declared menu names given below. Any other menu items following will
107 ** # be added to this menu. The second string is optional but if present
108 ** # will be added as a title for the menu.
109 ** #
110 ** line
111 ** # line adds extra line across the menus
112 ** #
113 ** item "Quit" quit()
114 ** # Add a user menu item with the given functions to be called if
115 ** # this menu item is selected.
116 ** #
117 ** item "Save" save('%f')
118 ** # function arguments are defined by the user but can have substitions
119 ** # In this example %f is the current filename within the application
120 ** #
121 ** item "Save As" input("Save As", '%f') save('%i')
122 ** # Multiple functions can be called, and are executed in the order given.
123 ** # Note that the arguments can use either double or single quotes.
124 ** #
125 ** # In this example input() is a external function which sets the %i to
126 ** # some user input (somehow), while this is happing the current function
127 ** # sequence is put on hold the input widget either continues or aborts
128 ** # the sequence.
129 ** #
130 ** item "Load" input("File to load", '') \
131 ** load('%i')
132 ** # a line can be continued on the next line by backslashing the EOL.
133 ** #
134 */
135 /* ====================================================================== */
136 /* ================= Application Specific Definitions ================= */
137 /* ====================================================================== */
138
139 #include "xbmbrowser.h"
140 #include <ctype.h>
141
142 #define QUIT_APP quit_browser /* The applications quit routine */
143 #define SET_WAIT set_busywait /* routine to set a wait cursor */
144 #define CLEAR_WAIT clear_busywait /* routine to clear a wait cursor */
145
146 #define MAX_STRING 1024 /* max length of a string argument */
147 #define SUBSTITION '%' /* the substitution character */
148 #define MAX_ARGS 2 /* this hardcoded in usr_item_handler() */
149
150 /* Declare the menus the users are allowed to define (though may not) */
151 /* WARNING: the first menu must be the main menu which must be defined */
152 static struct {
153 char *name; /* name of this menu the user can define */
154 Widget *menu; /* ptr to variable holding this user-menu */
155 } decl_menus[] = {
156 { "main", &menu_main }, /* main menu -- menu button at top */
157 { "global", &menu_global }, /* global menu -- button 2 */
158 { "bitmap", &menu_bitmap }, /* bitmap menu -- but 3 on xbm files */
159 { "pixmap", &menu_pixmap }, /* pixmap menu */
160 { "directory", &menu_directory }, /* directory menu */
161 { "other", &menu_other }, /* other files menu */
162 { NULL, NULL }
163 };
164
165 /* Declare functions the user menu items can call */
166 /* WARNING: the first function must be the quit function */
167 static struct {
168 char *name; /* the name of function in the RC file */
169 void (*funct)(); /* the function Pointer */
170 Cardinal numargs; /* number of arguments this function needs */
171 Boolean input; /* this function performs a input popup sequence */
172 } decl_functs[] = {
173 { "quit", quit_browser, 0, False }, /* cleanup and exit program */
174 { "scan", scan_images, 0, False }, /* full directory scan */
175 { "rescan", rescan_images, 0, False }, /* fast dir rescan */
176 { "chdir", change_dir, 1, False }, /* change directory */
177 { "exec", exec_string, 1, False }, /* external command */
178 { "confirm", user_confirm, 1, True }, /* user confirm dialog */
179 { "input", input_string, 2, True }, /* user input dialog */
180 { "selected", file_selected, 0, False }, /* abort on no selection */
181 { NULL, NULL, 0 }
182 };
183
184
185 /* Declare the % substitutions in function arguments
186 ** (NOTE %% is predefined) */
187 static struct {
188 char subchar; /* % escaped substition character for function args */
189 char *string; /* char ptr to substitution string */
190 } decl_subchars[] = {
191 { 'h', home_dir }, /* %h = the users home directory */
192 { 'd', dir_name }, /* %d = the current directory */
193 { 'f', file_name }, /* %f = the file selected in this directory */
194 { 'b', base_name }, /* %b = basename of the selected file */
195 { 's', suffix }, /* %s = suffix of the selected file (EG: ".xbm" ) */
196 { 'i', input }, /* %i = User input from input() function */
197 { 'D', init_dir }, /* %D = initial startup directory */
198 { '\0', NULL }
199 };
200 /* Full path of selected file = "%d%f". Also "%f" = "%b%s" */
201
202
203 /* ====================================================================== */
204 /* ============= End of Application Specific Definitions ============== */
205 /* ====================================================================== */
206
207 /* --------------- Menu Handler and Argument Substition ----------------- */
208
209 /* define the functions and argument structure each menu item is to call */
210 typedef struct _Functs {
211 void (*funct)(); /* the function to be called */
212 char *arg[MAX_ARGS]; /* the argument strings the user gave */
213 Boolean input; /* function popups up a input dialog */
214 struct _Functs *next; /* next function and args in sequence */
215 } Functs;
216
217 /* --------------------------- */
218
219 /* current function list in progress */
220 static Functs *next_function = NULL; /* next function to be called */
221 static char *item_in_progress = NULL; /* the menu item selected */
222
223 void
menu_item_abort()224 menu_item_abort()
225 /* Abort the current user-item-handler calling sequence */
226 {
227 next_function = NULL; /* no more functions need to be called */
228 }
229
230
231 void
menu_item_continue()232 menu_item_continue()
233 /* perferm the string substitions for function arguments and call the
234 ** functions asked for by the user in sequence. This routine is also
235 ** called by the return callbacks of any user input dialogs that
236 ** one of these functions may have popped up.
237 */
238 {
239 char arg[MAX_ARGS][MAX_STRING]; /* arguments to pass to user functs */
240 char *a, *d, *s; /* string pointers arg, dest, sub-string */
241 int i, j, left, len; /* temp variables */
242
243
244 /* while functions are present in the current function sequence */
245 SET_WAIT();
246 while( next_function != NULL ) {
247
248 /* prepare each argument */
249 for( i = 0; i < MAX_ARGS; i++ ) {
250 left = MAX_STRING; /* count of length of destinition string */
251 a = next_function->arg[i]; /* user supplied argument pointer */
252 d = arg[i]; /* set the destination pointer */
253
254 if( a == NULL ) { /* no argument given */
255 *d = '\0'; /* set empty string */
256 continue; /* goto next argument */
257 }
258
259 while( *a != '\0' ) {
260
261 /* while next char is the substitution char -- substitute */
262 while( *a == SUBSTITION ) {
263 a++; /* skip substitution char */
264 /* check is it escapes another substition char */
265 if ( *a == SUBSTITION ) {
266 *d++ = *a++; continue;
267 }
268 /* find the external variable to substute */
269 for( j = 0; decl_subchars[j].subchar; j++ )
270 if( *a == decl_subchars[j].subchar )
271 break;
272 if( decl_subchars[j].subchar == '\0' ) {
273 fprintf(stderr,
274 "Substitution %c%c in menu item \"%s\" is invalid\n",
275 SUBSTITION, *a, item_in_progress );
276 /* just abort current function sequence */
277 menu_item_abort();
278 goto menu_return;
279 }
280 a++; /* skip subitution type character */
281 len = strlen( decl_subchars[j].string );
282 if( (left -= len) <= 0 ) /* test if it will fit */
283 goto argument_overflow;
284 strcpy(d, decl_subchars[j].string);
285 d += len; /* increment string pointers */
286 }
287
288 /* copy up to the next substition char */
289 s = index( a, SUBSTITION ); /* scan for next substition char */
290 len = (s == NULL) ? strlen(a) /* not found -- just copy rest */
291 : s - a; /* copy upto the substition char */
292 if( (left -= len) <= 0 ) /* test if it will fit */
293 goto argument_overflow;
294 strncpy(d, a, len); /* copy from the user argument */
295 d += len; /* increment string pointers */
296 a += len;
297 }
298 *d = '\0'; /* finish of the string */
299 } /* for each argument */
300
301 /* FUNCTION CALL */
302 /* Make the actual function call requested by the user */
303 /* HARDCODED -- MAX_ARGS is limited by this bit of code */
304 (next_function->funct)(arg[0], arg[1]);
305
306 /* if the user called menu_item_abort() abort sequence now */
307 if( next_function == NULL )
308 goto menu_return;
309
310 /* do we have to wait for a popup before continuing? */
311 if( next_function->input ) {
312 /* Presumably the function just called popped up a input dialog of
313 ** some kind, and was successful in doing so. OK, increment the
314 ** next_function pointer and return to the main application loop,
315 ** to wait for the dialog to re-call this routine, menu_item_continue(),
316 ** when ready to continue the current function sequence.
317 */
318 next_function = next_function->next;
319 goto menu_return;
320 }
321
322 /* increment to the next function (if any) */
323 next_function = next_function->next;
324
325 } /* while next function is not an empty list */
326
327 /* finished */
328 goto menu_return;
329
330 argument_overflow: /* the argument after substitutions is too big! */
331 fprintf(stderr, "Argument for user menu function overflows argument\n");
332 fprintf(stderr, "buffer after all substutions are made. -- ABORTING\n" );
333 /* just abort this function sequence */
334 menu_item_abort();
335
336 menu_return:
337 CLEAR_WAIT();
338 return;
339 }
340
341
342 static void
menu_item_handler(widget,client_data,call_data)343 menu_item_handler(widget, client_data, call_data)
344 /* Callback for the menu items. This will prepare for the current
345 ** function sequence required by this menu item before calling the
346 ** menu_item_continue() routine above, which actually performs the
347 ** function sequence in order.
348 ** NOTE: the function sequence to be called is expected in the call_data.
349 */
350 Widget widget;
351 XtPointer client_data, call_data;
352 {
353 char *item_name;
354
355 XtVaGetValues(widget, XtNlabel, &item_name, NULL); /* get widgets name */
356
357 if( next_function != NULL ) {
358 fprintf(stderr, "User menu selection \"%s\" ignored.\n", item_name );
359 fprintf(stderr,
360 "\"%s\" is currently in progress, waiting for user input.\n",
361 item_in_progress );
362 return;
363 }
364 item_in_progress = item_name;
365 next_function = (Functs *)client_data;
366
367 /* start calling functions */
368 menu_item_continue();
369 }
370
371
372 /* ---------------------- Parsing and Setup Code ------------------------- */
373
374 #define IS_SPACE(x) ( (x)==' ' || (x)=='\t' || (x)=='\r' )
375 #define IS_ALPHA(x) ( 'a'<=(x)&&(x)<='z' || 'A'<=(x)&&(x)<='Z' )
376 #define IS_DIGIT(x) ( '0'<=(x)&&(x)<='9' )
377 #define IS_ALPHANUMERIC(x) ( IS_ALPHA(x) || IS_DIGIT(x) )
378
379 typedef enum {
380 KEYWORD, STRING, CHAR, EOL
381 } Token;
382
383 static FILE *file_ptr; /* given filename being read */
384 static char *configname; /* given filename being read */
385 static char token_string[MAX_STRING+1]; /* the actual token read */
386 static int line = 1; /* the current line number */
387 static Widget cur_menu = NULL; /* the current menu for items */
388 /* static Boolean firstitem = FALSE; /* item is the first in menu */
389
390 #ifdef PARSE
391 #define PARSE_OUTPUT(a) printf a
392 #else
393 #define PARSE_OUTPUT(a) /**/
394 #endif
395
396
397 /* ----------------------------- */
398
399 static char
get()400 get()
401 /* Read a character from the RC file and update the current
402 ** line number as appropiate. NOTE EOF is treated as an
403 ** infinite End-Of-Line. The main loop tests EOF specifically.
404 */
405 { int c;
406
407 c = fgetc(file_ptr);
408 if( c == EOF || c == '\n' ) {
409 c = '\n'; line++;
410 }
411 return c;
412 }
413
414
415 static void
unget(c)416 unget( c )
417 /* push the character back to be retrived again latter
418 ** Update the current line count as appropiately
419 */
420 char c;
421 {
422 if( c == '\n' ) line--;
423 ungetc( c, file_ptr );
424 }
425
426
427 /* Marco inline function for use within the get_token() function below */
428 #define read_token_string(test) \
429 { int i = 0; \
430 while( c = get(), (test) && i < MAX_STRING ) \
431 token_string[i++] = c; \
432 if( i >= MAX_STRING ) \
433 goto token_overflow; \
434 token_string[i] = '\0'; \
435 }
436
437 static Token
get_token()438 get_token()
439 /* Read the given file stream and return the first valid token found.
440 ** NOTE: This proceedure will just return EOL when the end of file is
441 ** reached
442 */
443 {
444 char c; /* character just read (not int) */
445
446 retry_get_token:
447 while( c = get(), IS_SPACE(c) ) ; /* skip leading space */
448
449 /* test for specific characters */
450 switch( c ) {
451
452 /* end-of-line and end-of-file conditions */
453 case '\n':
454 token_string[0] = '\n';
455 token_string[1] = '\0';
456 PARSE_OUTPUT(("EOL\n"));
457 return EOL;
458
459 /* comment skipping */
460 case '#': /* skip to next line */
461 while( c = get(), c != '\n' ) ; /* loop until EOL */
462 unget( c ); /* push the return back */
463 goto retry_get_token;
464
465 /* do special escape of the EOL */
466 case '\\': /* get and test EOL */
467 if( (c = get()) == '\n' )
468 goto retry_get_token;
469 /* not a EOL! -- return this char */
470 unget( c ); /* unget bad char */
471 c = '\\'; /* restore backslash */
472 break; /* exit to the CHAR return */
473
474 /* single quoted string */
475 case '\'':
476 /* skip the quote */
477 read_token_string( c != '\'' );
478 /* skip the quote */
479 PARSE_OUTPUT(("STRING \"%s\"\n", token_string));
480 return STRING;
481
482 /* double quoted string */
483 case '"':
484 /* skip the quote */
485 read_token_string( c != '"' );
486 /* skip the quote */
487 PARSE_OUTPUT(("STRING \"%s\"\n", token_string));
488 return STRING;
489 }
490
491 /* test if a keyword is found */
492 if( IS_ALPHA(c) ) {
493 unget( c ); /* this is the first char of token */
494 read_token_string( IS_ALPHANUMERIC(c) );
495 unget( c ); /* return this char to input stream */
496 PARSE_OUTPUT(("KEYWORD \"%s\"\n", token_string));
497 return KEYWORD;
498 }
499
500 /* At this point any unreconised character is just returned */
501 token_string[0] = c;
502 token_string[1] = '\0';
503 PARSE_OUTPUT(("CHAR '%s'\n", token_string));
504 return CHAR;
505
506 token_overflow: /* handle the event that the current token is too big */
507 token_string[40] = '\0'; /* limit the length of the long string */
508 fprintf(stderr, "Token found in \"%s\" on line %d is too long to handle!\n",
509 configname, line );
510 fprintf(stderr, "Token starts with -->%s...\n", token_string );
511 QUIT_APP();
512 }
513 /* this marco is no longer needed */
514 #undef read_token_string
515
516
517 static void
parse_line()518 parse_line()
519 {
520 if( cur_menu == NULL ) {
521 fprintf(stderr, "Missing menu declaration for line, \"%s\" at line %d.\n",
522 configname, line );
523 QUIT_APP();
524 }
525
526 XtVaCreateManagedWidget(
527 "line", smeLineObjectClass, cur_menu,
528 XtNheight, 2, /* allow multiple lines to be spaced */
529 NULL );
530 }
531
532
533 static void
parse_menu(parent)534 parse_menu(parent)
535 /* Parse a `menu' line and create the user menu pre-declared */
536 Widget parent; /* the parent of the menu */
537 {
538 Token token;
539 int i; /* junk looping variable */
540 Boolean title = FALSE; /* is a title present for this menu? */
541
542 token = get_token();
543 if( token != STRING ) {
544 fprintf(stderr, "Menu name not found in \"%s\" at line %d.\n",
545 configname, line );
546 QUIT_APP();
547 }
548
549 for( i = 0; decl_menus[i].name; i++ )
550 if( strcmp( token_string, decl_menus[i].name ) == 0 )
551 break;
552
553 if( decl_menus[i].name == NULL ) {
554 fprintf(stderr, "Unknown menu \"%s\", \"%s\" on line %d.\n",
555 token_string, configname, line );
556 QUIT_APP();
557 }
558
559 switch ( get_token() ) {
560 case STRING: title = TRUE;
561 break;
562 case EOL: break; /* this is valid too */
563 default:
564 fprintf(stderr,
565 "Menu \"%s\", \"%s\" is not a title or EOL, \"%s\" at line %d.\n",
566 decl_menus[i].name, token_string, configname, line );
567 QUIT_APP();
568 }
569
570 /* only create the actual menu if it is not already created */
571 if( *(decl_menus[i].menu) == NULL )
572 *(decl_menus[i].menu) = XtVaCreatePopupShell(
573 decl_menus[i].name, simpleMenuWidgetClass, parent,
574 XtNlabel, title ? token_string : NULL,
575 NULL );
576
577 /* add new items to this menu */
578 cur_menu = *(decl_menus[i].menu);
579
580 /* add an extra line after a title */
581 if( title ) parse_line();
582 }
583
584
585 static Functs *
parse_item_functs()586 parse_item_functs()
587 {
588 Token token;
589 Functs *functs = NULL;
590 Functs **lastlink = &functs;
591 int startline;
592 int i,j; /* junk looping variables */
593
594 /* while end of line is not reached */
595 while( token = get_token(), token != EOL ) {
596
597 if( token != KEYWORD ) {
598 fprintf(stderr, "Unknown function \"%s\", \"%s\" at line %d.\n",
599 token_string, configname, line );
600 QUIT_APP();
601 }
602 startline = line; /* save a copy of the line this function starts on */
603
604 for( i = 0; decl_functs[i].name; i++ )
605 if( strcmp( token_string, decl_functs[i].name ) == 0 )
606 break;
607
608 if( decl_functs[i].name == NULL ) {
609 fprintf(stderr, "Unknown function \"%s\", \"%s\" on line %d.\n",
610 token_string, configname, startline );
611 QUIT_APP();
612 }
613
614 if( decl_functs[i].numargs > MAX_ARGS ) {
615 fprintf(stderr,
616 "Programming Error, function \"%s\" has too many arguments\n",
617 token_string );
618 QUIT_APP();
619 }
620
621
622 /* Malloc, set defaults and link the Functs Structure */
623 *lastlink = (Functs *) XtMalloc( sizeof( Functs ) );
624 (*lastlink)->next = NULL;
625 (*lastlink)->funct = decl_functs[i].funct;
626 (*lastlink)->input = decl_functs[i].input;
627
628 /* read in the argument strings supplied by the user */
629 for( j = 0; j < MAX_ARGS; j++ )
630 (*lastlink)->arg[j] = NULL;
631
632 token = get_token();
633 if( token != CHAR || token_string[0] != '(' ) {
634 fprintf(stderr, "Function \"%s\" missing '(', \"%s\" at line %d.\n",
635 decl_functs[i].name, configname, startline );
636 QUIT_APP();
637 }
638
639 /* gather the arguments */
640 for( j = 0; j < decl_functs[i].numargs; j++ ) {
641
642 /* get intervening ',' */
643 if( j > 0 ) {
644 token = get_token();
645 if( token != CHAR || token_string[0] != ',' ) {
646 if( token == CHAR && token_string[0] == ')' )
647 fprintf(stderr,
648 "Function \"%s\" requires %d arguments, \"%s\" at line %d.\n",
649 decl_functs[i].name, decl_functs[i].numargs,
650 configname, startline );
651 else
652 fprintf(stderr,
653 "Missing ',' for function \"%s\", \"%s\" at line %d.\n",
654 decl_functs[i].name, configname, startline );
655 QUIT_APP();
656 }
657 }
658
659 /* get the argument string and save it in Functs call structure */
660 token = get_token();
661 if( token != STRING ) {
662 fprintf(stderr,
663 "Function \"%s\" requires %d arguments, \"%s\" at line %d.\n",
664 decl_functs[i].name, decl_functs[i].numargs,
665 configname, startline );
666 QUIT_APP();
667 }
668 (*lastlink)->arg[j] = XtMalloc( strlen( token_string ) + 1 );
669 strcpy( (*lastlink)->arg[j], token_string );
670 } /* for each arg */
671
672 token = get_token();
673 if( token != CHAR || token_string[0] != ')' ) {
674 if ( token == STRING || token == CHAR && token_string[0] != ',' )
675 fprintf(stderr,
676 "Function \"%s\" only needs %d arguments, \"%s\" at line %d.\n",
677 decl_functs[i].name, decl_functs[i].numargs,
678 configname, startline );
679 else
680 fprintf(stderr, "Function \"%s\" missing ')', \"%s\" at line %d.\n",
681 decl_functs[i].name, configname, startline );
682 QUIT_APP();
683 }
684
685 /* move the pointer to the lastlink of Functs list */
686 lastlink = &(*lastlink)->next;
687
688 } /* loop until EOL */
689
690 return functs;
691 }
692
693
694 static void
parse_item()695 parse_item()
696 {
697 Token token;
698 Widget item;
699
700 if( cur_menu == NULL ) {
701 fprintf(stderr, "Missing menu declaration for item, \"%s\" at line %d.\n",
702 configname, line );
703 QUIT_APP();
704 }
705
706 token = get_token();
707 if( token != STRING ) {
708 fprintf(stderr, "Item name not found in \"%s\" at line %d.\n",
709 configname, line );
710 QUIT_APP();
711 }
712
713 /* create item for current menu */
714 item = XtVaCreateManagedWidget(
715 token_string, smeBSBObjectClass, cur_menu,
716 XtNlabel, token_string,
717 XtNjustify, XtJustifyLeft,
718 NULL );
719 XtAddCallback(item, XtNcallback, menu_item_handler, parse_item_functs());
720
721 }
722
723
724 void
read_user_menus(parent,rc,name)725 read_user_menus(parent, rc, name)
726 /* Read the config file and setup all menus required
727 ** from the given file name and descriptor
728 */
729 Widget parent; /* parent widget for user menus */
730 FILE *rc; /* file to read config from */
731 char *name; /* config filename for errors */
732 {
733 Token token; /* the current token from config file */
734
735 file_ptr = rc; /* save these for use by other paseing routines */
736 configname = name;
737
738 while( !feof(rc) ) { /* while not end of file and no error */
739 token = get_token();
740
741 switch ( token ) {
742 case EOL:
743 continue;
744 case STRING:
745 fprintf(stderr, "Unexpected String \"%s\" in \"%s\" at line %d.\n",
746 token_string, configname, line );
747 QUIT_APP();
748 case CHAR:
749 fprintf(stderr, "Unexpected Character `%c' in \"%s\" at line %d.\n",
750 token_string[0], configname, line );
751 QUIT_APP();
752 }
753
754 /* handle KEYWORD */
755
756 if( strcmp( token_string, "menu" ) == 0 ) {
757 /* find and create this usermenu */
758 parse_menu(parent);
759 continue;
760 }
761 if( strcmp( token_string, "line" ) == 0 ) {
762 /* find and create this usermenu */
763 parse_line();
764 continue;
765 }
766 if( strcmp( token_string, "item" ) == 0 ) {
767 /* find and create this usermenu */
768 parse_item();
769 continue;
770 }
771
772 fprintf(stderr, "Unknown config line `%s' in \"%s\" at line %d.\n",
773 token_string, configname, line );
774 QUIT_APP();
775 }
776
777
778 /* If no user menu "main" was created -- create a plain quit menu */
779 if ( *(decl_menus[0].menu) == NULL ) {
780 Widget item;
781 Functs *quit;
782 int j;
783
784 fprintf(stderr, "WARNING: menu \"main\" not created in \"%s\"\n",
785 configname );
786 fprintf(stderr, " creating default quit menu\n");
787
788 /* create default menu for "main" with a title */
789 *(decl_menus[0].menu) = XtVaCreatePopupShell(
790 decl_menus[0].name, simpleMenuWidgetClass, parent,
791 XtNlabel, "No Main Menu Found",
792 NULL );
793 cur_menu = *(decl_menus[0].menu);
794 parse_line();
795
796 /* create a quit menu item */
797 item = XtVaCreateManagedWidget(
798 token_string, smeBSBObjectClass, cur_menu,
799 XtNlabel, "Quit Application..",
800 XtNjustify, XtJustifyLeft,
801 NULL );
802
803 /* which calls the quit function (and on others) */
804 quit = (Functs *) XtMalloc( sizeof( Functs ) );
805 quit->next = NULL;
806 quit->funct = decl_functs[0].funct;
807 quit->input = decl_functs[0].input;
808
809 /* and which contains no arguments */
810 for( j = 0; j < MAX_ARGS; j++ )
811 quit->arg[j] = NULL;
812
813 /* assign it to the item in the menu */
814 XtAddCallback(item, XtNcallback, menu_item_handler, quit);
815 }
816
817 }
818
819