source: IOIPSL/trunk/tools/tkgen.c @ 451

Last change on this file since 451 was 386, checked in by bellier, 16 years ago

Added CeCILL License information

  • Property svn:keywords set to Id
File size: 32.8 KB
Line 
1/* Generate tk script based upon config.in
2 * $Id$
3 *
4 * This software is governed by the CeCILL license
5 * See IOIPSL/IOIPSL_License_CeCILL.txt
6 *
7 * Version 1.0
8 * Eric Youngdale
9 * 10/95
10 *
11 * 1996 01 04
12 * Avery Pennarun - Aesthetic improvements.
13 *
14 * 1996 01 24
15 * Avery Pennarun - Bugfixes and more aesthetics.
16 *
17 * 1996 03 08
18 * Avery Pennarun - The int and hex config.in commands work right.
19 *                - Choice buttons are more user-friendly.
20 *                - Disabling a text entry line greys it out properly.
21 *                - dep_tristate now works like in Configure. (not pretty)
22 *                - No warnings in gcc -Wall. (Fixed some "interesting" bugs.)
23 *                - Faster/prettier "Help" lookups.
24 *
25 * 1996 03 15
26 * Avery Pennarun - Added new sed script from Axel Boldt to make help even
27 *                  faster. (Actually awk is downright slow on some machines.)
28 *                - Fixed a bug I introduced into Choice dependencies.  Thanks
29 *                  to Robert Krawitz for pointing this out.
30 *
31 * 1996 03 16
32 * Avery Pennarun - basic "do_make" support added to let sound config work.
33 *
34 * 1996 03 25
35 *     Axel Boldt - Help now works on "choice" buttons.
36 *
37 * 1996 04 06
38 * Avery Pennarun - Improved sound config stuff. (I think it actually works
39 *                  now!)
40 *                - Window-resize-limits don't use ugly /usr/lib/tk4.0 hack.
41 *                - int/hex work with tk3 again. (The "cget" error.)
42 *                - Next/Prev buttons switch between menus.  I can't take
43 *                  much credit for this; the code was already there, but
44 *                  ifdef'd out for some reason.  It flickers a lot, but
45 *                  I suspect there's no "easy" fix for that.
46 *                - Labels no longer highlight as you move the mouse over
47 *                  them (although you can still press them... oh well.)
48 *                - Got rid of the last of the literal color settings, to
49 *                  help out people with mono X-Windows systems.
50 *                  (Apparently there still are some out there!)
51 *                - Tabstops seem sensible now.
52 *
53 * 1996 04 14
54 * Avery Pennarun - Reduced flicker when creating windows, even with "update
55 *                  idletasks" hack.
56 *
57 * TO DO:
58 *   - clean up - there are useless ifdef's everywhere.
59 *   - better comments throughout - C code generating tcl is really cryptic.
60 *   - eliminate silly "update idletasks" hack to improve display speed and
61 *     reduce flicker.  But how?
62 *   - make canvas contents resize with the window (good luck).
63 *   - some way to make submenus inside of submenus (ie. Main->Networking->IP)
64 *           (perhaps a button where the description would be)
65 *   - make the main menu use the same tcl code as the submenus.
66 *   - make choice and int/hex input types line up vertically with
67 *           bool/tristate.
68 *   - general speedups - how?  The canvas seems to slow it down a lot.
69 *   - choice buttons should default to the first menu option, rather than a
70 *           blank.  Also look up the right variable when the help button
71 *           is pressed.
72 *   - clean up +/- 16 confusion for enabling/disabling variables; causes
73 *           (theoretical, at the moment) problems with dependencies.
74 *   
75 */
76#include <stdio.h>
77#include <unistd.h>
78#include "tkparse.h"
79
80#ifndef TRUE
81#define TRUE (1)
82#endif
83
84#ifndef FALSE
85#define FALSE (0)
86#endif
87
88/*
89 * This is the total number of submenus that we have.
90 */
91static int tot_menu_num =0;
92
93/*
94 * Generate portion of wish script for the beginning of a submenu.
95 * The guts get filled in with the various options.
96 */
97static void start_proc(char * label, int menu_num, int flag)
98{
99  if( flag )
100    printf("menu_option menu%d %d \"%s\"\n", menu_num, menu_num, label);
101  printf("proc menu%d {w title} {\n", menu_num);
102  printf("\tcatch {destroy $w}\n");
103  printf("\ttoplevel $w -class Dialog\n");
104  printf("\twm withdraw $w\n");
105  printf("\tmessage $w.m -width 400 -aspect 300 -text \\\n");
106  printf("\t\t\"%s\"  -relief raised\n",label);
107  printf("\tpack $w.m -pady 10 -side top -padx 10\n");
108  printf("\twm title $w \"%s\" \n\n", label);
109 
110  /*
111   * Attach the "Prev", "Next" and "OK" buttons at the end of the window.
112   */
113  printf("\tset oldFocus [focus]\n");
114  printf("\tframe $w.f\n");
115  printf("\tbutton $w.f.back -text \"Main Menu\" \\\n"
116         "\t\t-width 15 -command \"destroy $w; focus $oldFocus; update_mainmenu $w\"\n");
117  printf("\tbutton $w.f.next -text \"Next\" \\\n"
118         "\t\t-width 15 -command \" destroy $w; focus $oldFocus;  menu%d .menu%d \\\"$title\\\"\"\n",
119                menu_num+1, menu_num+1);
120  if (menu_num == tot_menu_num)
121        printf("\t$w.f.next configure -state disabled\n");
122  printf("\tbutton $w.f.prev -text \"Prev\" \\\n"
123         "\t\t-width 15 -command \" destroy $w; focus $oldFocus; menu%d .menu%d \\\"$title\\\"\"\n",
124                menu_num-1, menu_num-1);
125  if (1 == menu_num)
126        printf("\t$w.f.prev configure -state disabled\n");
127  printf("\tpack $w.f.back $w.f.next $w.f.prev -side left -expand on\n");
128  printf("\tpack $w.f -pady 10 -side bottom -anchor w -fill x\n");
129
130  /*
131   * Lines between canvas and other areas of the window.
132   */
133  printf("\tframe $w.topline -relief ridge -borderwidth 2 -height 2\n");
134  printf("\tpack $w.topline -side top -fill x\n\n");
135  printf("\tframe $w.botline -relief ridge -borderwidth 2 -height 2\n");
136  printf("\tpack $w.botline -side bottom -fill x\n\n");
137 
138  /*
139   * The "config" frame contains the canvas and a scrollbar.
140   */
141  printf("\tframe $w.config\n");
142  printf("\tpack $w.config -fill y -expand on\n\n");
143  printf("\tscrollbar $w.config.vscroll -command \"$w.config.canvas yview\"\n");
144  printf("\tpack $w.config.vscroll -side right -fill y\n\n");
145 
146  /*
147   * The scrollable canvas itself, where the real work (and mess) gets done.
148   */
149  printf("\tcanvas $w.config.canvas -height 1\\\n"
150         "\t\t-relief flat -borderwidth 0 -yscrollcommand \"$w.config.vscroll set\" \\\n"
151         "\t\t-width [expr [winfo screenwidth .] * 1 / 2] \n");
152  printf("\tframe $w.config.f\n");
153  printf("\tpack $w.config.canvas -side right -fill y\n");
154 
155  printf("\n\n");
156}
157
158/*
159 * Each proc we create needs a global declaration for any global variables we
160 * use.  To minimize the size of the file, we set a flag each time we output
161 * a global declaration so we know whether we need to insert one for a
162 * given function or not.
163 */
164void clear_globalflags(struct kconfig * cfg)
165{
166  for(; cfg != NULL; cfg = cfg->next)
167  {
168    cfg->flags &= ~GLOBAL_WRITTEN;
169  }
170}
171
172/*
173 * Output a "global" line for a given variable.  Also include the
174 * call to "vfix".  (If vfix is not needed, then it's fine to just printf
175 * a "global" line).
176 */
177void global(char *var)
178{
179  printf("\tglobal %s; vfix %s\n", var, var);
180}
181
182/*
183 * This function walks the chain of conditions that we got from cond.c,
184 * and creates a wish conditional to enable/disable a given widget.
185 */
186void generate_if(struct kconfig * item,
187            struct condition * cond,
188            int menu_num,
189            int line_num)
190{
191  struct condition * ocond;
192
193  ocond = cond;
194
195  /*
196   * First write any global declarations we need for this conditional.
197   */
198  while(cond != NULL )
199    {
200      switch(cond->op){
201      case op_variable:
202        global(cond->variable.str);
203        break;
204      case op_kvariable:
205        if(cond->variable.cfg->flags & GLOBAL_WRITTEN) break;
206        cond->variable.cfg->flags |= GLOBAL_WRITTEN;
207        global(cond->variable.cfg->optionname);
208        break;
209      default:
210        break;
211      }
212      cond = cond->next;
213    }
214 
215  /*
216   * Now write this option.
217   */
218  if(   (item->flags & GLOBAL_WRITTEN) == 0
219     && (item->optionname != NULL) )
220    {
221      global(item->optionname);
222      item->flags |= GLOBAL_WRITTEN;
223    }
224  /*
225   * Now generate the body of the conditional.
226   */
227  printf("\tif {");
228  cond = ocond;
229  while(cond != NULL )
230    {
231      switch(cond->op){
232      case op_bang:
233        printf(" ! ");
234        break;
235      case op_eq:
236        printf(" == ");
237        break;
238      case op_neq:
239        printf(" != ");
240        break;
241      case op_and:
242      case op_and1:
243        printf(" && ");
244        break;
245      case op_or:
246        printf(" || ");
247        break;
248      case op_lparen:
249        printf("(");
250        break;
251      case op_rparen:
252        printf(")");
253        break;
254      case op_variable:
255        printf("$%s", cond->variable.str);
256        break;
257      case op_kvariable:
258        printf("$%s", cond->variable.cfg->optionname);
259        break;
260      case op_shellcmd:
261        printf("[exec %s]", cond->variable.str);
262        break;
263      case op_constant:
264        if( strcmp(cond->variable.str, "y") == 0 )
265          printf("1");
266        else if( strcmp(cond->variable.str, "n") == 0 )
267          printf("0");
268        else if( strcmp(cond->variable.str, "m") == 0 )
269          printf("2");
270        else
271          printf("\"%s\"", cond->variable.str);
272        break;
273      default:
274        break;
275      }
276      cond = cond->next;
277    }
278
279  /*
280   * Now we generate what we do depending upon the value of the conditional.
281   * Depending upon what the token type is, there are different things
282   * we must do to enable/disable the given widget - this code needs to
283   * be closely coordinated with the widget creation procedures in header.tk.
284   */
285  switch(item->tok)
286    {
287    case tok_define:
288      printf("} then { set %s %s } \n",  item->optionname, item->value);
289      break;
290    case tok_menuoption:
291      printf("} then { .f0.x%d configure -state normal } else { .f0.x%d configure -state disabled }\n",
292             menu_num, menu_num);
293      break;
294    case tok_int:
295    case tok_hex:
296      printf("} then { ");
297      printf(".menu%d.config.f.x%d.x configure -state normal -fore [ cget .ref -foreground ]; ", menu_num, line_num);
298      printf(".menu%d.config.f.x%d.l configure -state normal; ", menu_num, line_num);
299      printf("} else { ");
300      printf(".menu%d.config.f.x%d.x configure -state disabled -fore [ cget .ref -disabledforeground ];", menu_num, line_num );
301      printf(".menu%d.config.f.x%d.l configure -state disabled;", menu_num, line_num );
302      printf("}\n");
303      break;
304    case tok_bool:
305#ifdef BOOL_IS_BUTTON
306      /*
307       * If a bool is just a button, then use this definition.
308       */
309      printf("} then { .menu%d.config.f.x%d configure -state normal } else { .menu%d.config.f.x%d configure -state disabled }\n",
310             menu_num, line_num,
311             menu_num, line_num );
312#else
313      /*
314       * If a bool is a radiobutton, then use this instead.
315       */
316      printf("} then { ");
317      printf(".menu%d.config.f.x%d.y configure -state normal;",menu_num, line_num);
318      printf(".menu%d.config.f.x%d.n configure -state normal;",menu_num, line_num);
319      printf(".menu%d.config.f.x%d.l configure -state normal;",menu_num, line_num);
320      printf("set %s [expr $%s&15];", item->optionname, item->optionname);
321      printf("} else { ");
322      printf(".menu%d.config.f.x%d.y configure -state disabled;",menu_num, line_num);
323      printf(".menu%d.config.f.x%d.n configure -state disabled;",menu_num, line_num);
324      printf(".menu%d.config.f.x%d.l configure -state disabled;",menu_num, line_num);
325      printf("set %s [expr $%s|16];", item->optionname, item->optionname);
326      printf("}\n");
327#endif
328      break;
329    case tok_tristate:
330    case tok_dep_tristate:
331      printf("} then { ");
332      if( item->tok == tok_dep_tristate )
333        {
334          global(item->depend.str);
335          printf("if { $%s != 1 && $%s != 0 } then {", 
336                item->depend.str,item->depend.str);
337          printf(".menu%d.config.f.x%d.y configure -state disabled;",menu_num, line_num);
338          printf("} else {");
339          printf(".menu%d.config.f.x%d.y configure -state normal;",menu_num, line_num);
340          printf("}; ");
341        }
342      else
343        {
344          printf(".menu%d.config.f.x%d.y configure -state normal;",menu_num, line_num);
345        }
346     
347      printf(".menu%d.config.f.x%d.n configure -state normal;",menu_num, line_num);
348      printf(".menu%d.config.f.x%d.m configure -state normal;",menu_num, line_num);
349      printf(".menu%d.config.f.x%d.l configure -state normal;",menu_num, line_num);
350      /*
351       * Or in a bit to the variable - this causes all of the radiobuttons
352       * to be deselected (i.e. not be red).
353       */
354      printf("set %s [expr $%s&15];", item->optionname, item->optionname);
355      printf("} else { ");
356      printf(".menu%d.config.f.x%d.y configure -state disabled;",menu_num, line_num);
357      printf(".menu%d.config.f.x%d.n configure -state disabled;",menu_num, line_num);
358      printf(".menu%d.config.f.x%d.m configure -state disabled;",menu_num, line_num);
359      printf(".menu%d.config.f.x%d.l configure -state disabled;",menu_num, line_num);
360      /*
361       * Clear the disable bit - this causes the correct radiobutton
362       * to appear selected (i.e. turn red).
363       */
364      printf("set %s [expr $%s|16];", item->optionname, item->optionname);
365      printf("}\n");
366      break;
367    case tok_choose:
368    case tok_choice:
369      fprintf(stderr,"Fixme\n");
370      exit(0);
371    default:
372      break;
373    }
374}
375
376/*
377 * Similar to generate_if, except we come here when generating an
378 * output file.  Thus instead of enabling/disabling a widget, we
379 * need to decide whether to write out a given configuration variable
380 * to the output file.
381 */
382void generate_if_for_outfile(struct kconfig * item,
383            struct condition * cond)
384{
385  struct condition * ocond;
386
387  /*
388   * First write any global declarations we need for this conditional.
389   */
390  ocond = cond;
391  for(; cond != NULL; cond = cond->next )
392    {
393      switch(cond->op){
394      case op_variable:
395        global(cond->variable.str);
396        break;
397      case op_kvariable:
398        if(cond->variable.cfg->flags & GLOBAL_WRITTEN) break;
399        cond->variable.cfg->flags |= GLOBAL_WRITTEN;
400        global(cond->variable.cfg->optionname);
401        break;
402      default:
403        break;
404      }
405    }
406
407  /*
408   * Now generate the body of the conditional.
409   */
410  printf("\tif {");
411  cond = ocond;
412  while(cond != NULL )
413    {
414      switch(cond->op){
415      case op_bang:
416        printf(" ! ");
417        break;
418      case op_eq:
419        printf(" == ");
420        break;
421      case op_neq:
422        printf(" != ");
423        break;
424      case op_and:
425      case op_and1:
426        printf(" && ");
427        break;
428      case op_or:
429        printf(" || ");
430        break;
431      case op_lparen:
432        printf("(");
433        break;
434      case op_rparen:
435        printf(")");
436        break;
437      case op_variable:
438        printf("$%s", cond->variable.str);
439        break;
440      case op_shellcmd:
441        printf("[exec %s]", cond->variable.str);
442        break;
443      case op_kvariable:
444        printf("$%s", cond->variable.cfg->optionname);
445        break;
446      case op_constant:
447        if( strcmp(cond->variable.str, "y") == 0 )
448          printf("1");
449        else if( strcmp(cond->variable.str, "n") == 0 )
450          printf("0");
451        else if( strcmp(cond->variable.str, "m") == 0 )
452          printf("2");
453        else
454          printf("\"%s\"", cond->variable.str);
455        break;
456      default:
457        break;
458      }
459      cond = cond->next;
460    }
461
462  /*
463   * Now we generate what we do depending upon the value of the
464   * conditional.  Depending upon what the token type is, there are
465   * different things we must do write the value the given widget -
466   * this code needs to be closely coordinated with the widget
467   * creation procedures in header.tk. 
468   */
469  switch(item->tok)
470    {
471    case tok_define:
472      printf("} then {write_tristate $cfg $autocfg %s %s $notmod }\n", item->optionname, item->value);
473      break;
474    case tok_comment:
475      printf("} then {write_comment $cfg $autocfg \"%s\"}\n", item->label);
476      break;
477    case tok_dep_tristate:
478      printf("} then { write_tristate $cfg $autocfg %s $%s $%s } \n", 
479             item->optionname, item->optionname, item->depend.str);
480      break;
481    case tok_tristate:
482    case tok_bool:
483      printf("} then { write_tristate $cfg $autocfg %s $%s $notmod }\n", 
484             item->optionname, item->optionname);
485      break;
486    case tok_int:
487      printf("} then { write_int $cfg $autocfg %s $%s $notmod }\n",
488             item->optionname, item->optionname);
489      break;
490    case tok_hex:
491      printf("} then { write_hex $cfg $autocfg %s $%s $notmod }\n",
492             item->optionname, item->optionname);
493      break;
494    case tok_make:
495      printf("} then { do_make {%s} }\n",item->value);
496      break;
497    case tok_choose:
498    case tok_choice:
499      fprintf(stderr,"Fixme\n");
500      exit(0);
501    default:
502      break;
503    }
504}
505
506/*
507 * Generates a fragment of wish script that closes out a submenu procedure.
508 */
509static void end_proc(int menu_num)
510{
511  struct kconfig * cfg;
512
513  printf("\n\n\n");
514  printf("\tfocus $w\n");
515  printf("\tupdate_menu%d $w.config.f\n", menu_num);
516  printf("\tglobal winx; global winy\n");
517  printf("\tset winx [expr [winfo x .]+30]; set winy [expr [winfo y .]+30]\n");
518  printf("\twm geometry $w +$winx+$winy\n");
519 
520  /*
521   * Now that the whole window is in place, we need to wait for an "update"
522   * so we can tell the canvas what its virtual size should be.
523   *
524   * Unfortunately, this causes some ugly screen-flashing because the whole
525   * window is drawn, and then it is immediately resized.  It seems
526   * unavoidable, though, since "frame" objects won't tell us their size
527   * until after an update, and "canvas" objects can't automatically pack
528   * around frames.  Sigh.
529   */
530  printf("\tupdate idletasks\n");
531  printf("\t$w.config.canvas create window 0 0 -anchor nw -window $w.config.f\n\n");
532  printf("\t$w.config.canvas configure \\\n"
533         "\t\t-width [expr [winfo reqwidth $w.config.f] + 1]\\\n"
534         "\t\t-scrollregion \"-1 -1 [expr [winfo reqwidth $w.config.f] + 1] \\\n"
535         "\t\t\t [expr [winfo reqheight $w.config.f] + 1]\"\n\n");
536         
537  /*
538   * If the whole canvas will fit in 3/4 of the screen height, do it;
539   * otherwise, resize to around 1/2 the screen and let us scroll.
540   */
541  printf("\tset winy [expr [winfo reqh $w] - [winfo reqh $w.config.canvas]]\n");
542  printf("\tset scry [expr [winfo screenh $w] / 2]\n");
543  printf("\tset maxy [expr [winfo screenh $w] * 3 / 4]\n");
544  printf("\tset canvtotal [expr [winfo reqh $w.config.f] + 2]\n");
545  printf("\tif [expr $winy + $canvtotal < $maxy] {\n"
546         "\t\t$w.config.canvas configure -height $canvtotal\n"
547         "\t} else {\n"
548         "\t\t$w.config.canvas configure -height [expr $scry - $winy]\n"
549         "\t}\n");
550 
551  /*
552   * Limit the min/max window size.  Height can vary, but not width,
553   * because of the limitations of canvas and our laziness.
554   */
555  printf("\tupdate idletasks\n");
556  printf("\twm maxsize $w [winfo width $w] [winfo screenheight $w]\n");
557  printf("\twm minsize $w [winfo width $w] 100\n\n");
558  printf("\twm deiconify $w\n");
559   
560  printf("}\n\n\n");
561
562  /*
563   * Now we generate the companion procedure for the menu we just
564   * generated.  This procedure contains all of the code to
565   * disable/enable widgets based upon the settings of the other
566   * widgets, and will be called first when the window is mapped,
567   * and each time one of the buttons in the window are clicked.
568   */
569  printf("proc update_menu%d {w}  {\n", menu_num);
570
571  printf("\tupdate_define\n");
572  clear_globalflags(config);
573  for(cfg = config;cfg != NULL; cfg = cfg->next)
574    {
575      /*
576       * Skip items not for this menu, or ones having no conditions.
577       */
578      if (cfg->menu_number != menu_num ) continue;
579      if (cfg->tok != tok_define) continue;
580      /*
581       * Clear all of the booleans that are defined in this menu.
582       */
583      if(   (cfg->flags & GLOBAL_WRITTEN) == 0
584         && (cfg->optionname != NULL) )
585        {
586          printf("\tglobal %s\n", cfg->optionname);
587          cfg->flags |= GLOBAL_WRITTEN;
588          printf("\tset %s 0\n", cfg->optionname);
589        }
590
591    }
592  for(cfg = config;cfg != NULL; cfg = cfg->next)
593    {
594      /*
595       * Skip items not for this menu, or ones having no conditions.
596       */
597      if (cfg->menu_number != menu_num ) continue;
598      if (cfg->tok == tok_menuoption) continue;
599      if (cfg->cond != NULL ) 
600        generate_if(cfg, cfg->cond, menu_num, cfg->menu_line);
601      else
602        {
603          /*
604           * If this token has no conditionals, check to see whether
605           * it is a tristate - if so, then generate the conditional
606           * to enable/disable the "y" button based upon the setting
607           * of the option it depends upon.
608           */
609          if(cfg->tok == tok_dep_tristate)
610            {
611              global(cfg->depend.str);
612              printf("\tif {$%s != 1 && $%s != 0 } then { .menu%d.config.f.x%d.y configure -state disabled } else { .menu%d.config.f.x%d.y configure -state normal}\n",
613                     cfg->depend.str,cfg->depend.str,
614                     menu_num, cfg->menu_line,
615                     menu_num, cfg->menu_line);
616            }
617        }
618
619    }
620
621
622  printf("}\n\n\n");
623}
624
625/*
626 * This function goes through and counts up the number of items in
627 * each submenu. If there are too many options, we need to split it
628 * into submenus.  This function just calculates how many submenus,
629 * and how many items go in each submenu.
630 */
631static void find_menu_size(struct kconfig *cfg,
632                          int *menu_max, 
633                          int *menu_maxlines)
634
635{
636  struct kconfig * pnt;
637  int tot;
638 
639  /*
640   * First count up the number of options in this menu.
641   */
642  tot = 0;
643  for(pnt = cfg->next; pnt; pnt = pnt->next)
644  {
645    if( pnt->tok == tok_menuoption) break;
646    switch (pnt->tok)
647      {
648      case tok_bool:
649      case tok_tristate:
650      case tok_dep_tristate:
651      case tok_int:
652      case tok_hex:
653      case tok_choose:
654        tot++;
655        break;
656      case tok_choice:
657      default:
658        break;
659      }
660  }
661
662  *menu_max = cfg->menu_number;
663  *menu_maxlines = tot;
664}
665
666/*
667 * This is the top level function for generating the tk script.
668 */
669void dump_tk_script(struct kconfig *scfg)
670{
671  int menu_num =0;
672  int menu_max =0;
673  int menu_min =0;
674  int menu_line = 0;
675  int menu_maxlines = 0;
676  struct kconfig * cfg;
677  struct kconfig * cfg1 = NULL;
678  char * menulabel;
679
680  /*
681   * Start by assigning menu numbers, and submenu numbers.
682   */
683  for(cfg = scfg;cfg != NULL; cfg = cfg->next)
684    {
685      switch (cfg->tok)
686        {
687        case tok_menuname:
688          break;
689        case tok_menuoption:
690          /*
691           * At the start of a new menu, calculate the number of items
692           * we will put into each submenu so we know when to bump the
693           * menu number. The submenus are really no different from a
694           * normal menu, but the top level buttons only access the first
695           * of the chain of menus, and the prev/next buttons are used
696           * access the submenus.
697           */
698          cfg->menu_number = ++menu_num;
699          find_menu_size(cfg, &menu_max, &menu_maxlines);
700          cfg->submenu_start = menu_num;
701          cfg->submenu_end = menu_max;
702          menu_line = 0;
703          break;
704        case tok_bool:
705        case tok_tristate:
706        case tok_dep_tristate:
707        case tok_int:
708        case tok_hex:
709        case tok_choose:
710          /*
711           * If we have overfilled the menu, then go to the next one.
712           */
713          if( menu_line == menu_maxlines )
714            {
715              menu_line = 0;
716              menu_num++;
717            }
718          cfg->menu_number = menu_num;
719          cfg->submenu_start = menu_min;
720          cfg->submenu_end = menu_max;
721          cfg->menu_line = menu_line++;
722          break;
723        case tok_define:
724          cfg->menu_number = -1;
725        case tok_choice:
726        default:
727          break;
728        };
729    }
730
731  /*
732   * Record this so we can set up the prev/next buttons correctly.
733   */
734  tot_menu_num = menu_num;
735
736  /*
737   * Now start generating the actual wish script that we will use.
738   * We need to keep track of the menu numbers of the min/max menu
739   * for a range of submenus so that we can correctly limit the
740   * prev and next buttons so that they don't go over into some other
741   * category.
742   */
743  for(cfg = scfg; cfg != NULL; cfg = cfg->next)
744    {
745      switch (cfg->tok)
746        {
747        case tok_menuname:
748          printf("mainmenu_name \"%s\"\n", cfg->label);
749          break;
750        case tok_menuoption:
751          /*
752           * We are at the start of a new menu. If we had one that
753           * we were working on before, close it out, and then generate
754           * the script to start the new one.
755           */
756          if( cfg->menu_number > 1 )
757            {
758              end_proc(menu_num);
759            }
760          menulabel = cfg->label;
761          start_proc(cfg->label, cfg->menu_number, TRUE);
762          menu_num = cfg->menu_number;
763          menu_max = cfg->submenu_end;
764          menu_min = cfg->submenu_start;
765          break;
766        case tok_bool:
767          /*
768           * If we reached the point where we need to switch over
769           * to the next submenu, then bump the menu number and generate
770           * the code to close out the old menu and start the new one.
771           */
772          if( cfg->menu_number != menu_num )
773            {
774              end_proc(menu_num);
775              start_proc(menulabel, cfg->menu_number, FALSE);
776              menu_num = cfg->menu_number;
777            }
778          printf("\tbool $w.config.f %d %d \"%s\" %s\n",
779                 cfg->menu_number,
780                 cfg->menu_line,
781                 cfg->label,
782                 cfg->optionname);
783          break;
784
785        case tok_choice:
786          printf("\t$w.config.f.x%d.x.menu add radiobutton -label \"%s\" -variable %s -value \"%s\" -command \"update_menu%d .menu%d.config.f\"\n",
787                 cfg1->menu_line,
788                 cfg->label,
789                 cfg1->optionname,
790                 cfg->label,
791                 cfg1->menu_number, cfg1->menu_number);
792          break;
793        case tok_choose:
794          if( cfg->menu_number != menu_num )
795            {
796              end_proc(menu_num);
797              start_proc(menulabel, cfg->menu_number, FALSE);
798              menu_num = cfg->menu_number;
799            }
800          printf("\tglobal %s\n",cfg->optionname);
801          printf("\tminimenu $w.config.f %d %d \"%s\" %s %s\n",
802                cfg->menu_number,
803                cfg->menu_line,
804                cfg->label,
805                cfg->optionname,
806                /*
807                 * We rely on the fact that the first tok_choice corresponding
808                 * to the current tok_choose is cfg->next (compare parse() in
809                 * tkparse.c).  We need its name to pick out the right help
810                 * text from Configure.help.
811                 */
812                cfg->next->optionname);
813          printf("\tmenu $w.config.f.x%d.x.menu\n", cfg->menu_line);
814          cfg1 = cfg;
815          break;
816        case tok_tristate:
817          if( cfg->menu_number != menu_num )
818            {
819              end_proc(menu_num);
820              start_proc(menulabel, cfg->menu_number, FALSE);
821              menu_num = cfg->menu_number;
822            }
823          printf("\ttristate $w.config.f %d %d \"%s\" %s\n",
824                 cfg->menu_number,
825                 cfg->menu_line,
826                 cfg->label,
827                 cfg->optionname);
828          break;
829        case tok_dep_tristate:
830          if( cfg->menu_number != menu_num )
831            {
832              end_proc(menu_num);
833              start_proc(menulabel, cfg->menu_number, FALSE);
834              menu_num = cfg->menu_number;
835            }
836          printf("\tdep_tristate $w.config.f %d %d \"%s\" %s %s\n",
837                 cfg->menu_number,
838                 cfg->menu_line,
839                 cfg->label,
840                 cfg->optionname,
841                 cfg->depend.str);
842          break;
843        case tok_int:
844          if( cfg->menu_number != menu_num )
845            {
846              end_proc(menu_num);
847              start_proc(menulabel, cfg->menu_number, FALSE);
848              menu_num = cfg->menu_number;
849            }
850          printf("\tint $w.config.f %d %d \"%s\" %s\n",
851                 cfg->menu_number,
852                 cfg->menu_line,
853                 cfg->label,
854                 cfg->optionname);
855          break;
856        case tok_hex:
857          if( cfg->menu_number != menu_num )
858            {
859              end_proc(menu_num);
860              start_proc(menulabel, cfg->menu_number, FALSE);
861              menu_num = cfg->menu_number;
862            }
863          printf("\thex $w.config.f %d %d \"%s\" %s\n",
864                 cfg->menu_number,
865                 cfg->menu_line,
866                 cfg->label,
867                 cfg->optionname);
868          break;
869        default:
870          break;
871        }
872
873    }
874
875  /*
876   * Generate the code to close out the last menu.
877   */
878  end_proc(menu_num);
879
880#ifdef ERIC_DONT_DEF
881  /*
882   * Generate the code for configuring the sound driver.  Right now this
883   * cannot be done from the X script, but we insert the menu anyways.
884   */
885  start_proc("Configure sound driver", ++menu_num, TRUE);
886#if 0
887  printf("\tdo_make -C drivers/sound config\n");
888  printf("\techo check_sound_config %d\n",menu_num);
889#endif
890  printf("\tlabel $w.config.f.m0 -bitmap error\n");
891  printf("\tmessage $w.config.f.m1 -width 400 -aspect 300 -text \"The sound drivers cannot as of yet be configured via the X-based interface\" -relief raised\n");
892  printf("\tpack $w.config.f.m0 $w.config.f.m1 -side top -pady 10 -expand on\n");
893  /*
894   * Close out the last menu.
895   */
896  end_proc(menu_num);
897#endif
898
899  /*
900   * The top level menu also needs an update function.  When we exit a
901   * submenu, we may need to disable one or more of the submenus on
902   * the top level menu, and this procedure will ensure that things are
903   * correct.
904   */
905  printf("proc update_mainmenu {w}  {\n");
906  for(cfg = scfg; cfg != NULL; cfg = cfg->next)
907    {
908      switch (cfg->tok)
909        {
910        case tok_menuoption:
911          if (cfg->cond != NULL ) 
912            generate_if(cfg, cfg->cond, cfg->menu_number, cfg->menu_line);
913          break;
914        default:
915          break;
916        }
917    }
918
919  printf("}\n\n\n");
920
921#if 0
922  /*
923   * Generate some code to set the variables that are "defined".
924   */
925  for(cfg = config;cfg != NULL; cfg = cfg->next)
926    {
927      /*
928       * Skip items not for this menu, or ones having no conditions.
929       */
930      if( cfg->tok != tok_define) continue;
931      if (cfg->cond != NULL )
932        generate_if(cfg, cfg->cond, menu_num, cfg->menu_line);
933      else
934        {
935          printf("\twrite_define %s %s\n", cfg->optionname, cfg->value);
936        }
937
938    }
939#endif
940
941  /*
942   * Now generate code to load the default settings into the variables.
943   * Note that the script in tail.tk will attempt to load .config,
944   * which may override these settings, but that's OK.
945   */
946  for(cfg = scfg; cfg != NULL; cfg = cfg->next)
947    {
948      switch (cfg->tok)
949        {
950        case tok_bool:
951        case tok_tristate:
952        case tok_dep_tristate:
953        case tok_choice:
954          printf("set %s 0\n", cfg->optionname);
955          break;
956        case tok_int:
957        case tok_hex:
958          printf("set %s %s\n", cfg->optionname, cfg->value);
959          break;
960        case tok_choose:
961          printf("set %s \"(not set)\"\n",cfg->optionname);
962        default:
963          break;
964        }
965    }
966
967  /*
968   * Next generate a function that can be called from the main menu that will
969   * write all of the variables out.  This also serves double duty - we can
970   * save configuration to a file using this.
971   */
972  printf("proc writeconfig {file1 file2} {\n");
973  printf("\tset cfg [open $file1 w]\n");
974  printf("\tset autocfg [open $file2 w]\n");
975  printf("\tset notmod 1\n");
976  printf("\tset notset 0\n");
977  clear_globalflags(config);
978  printf("\tputs $cfg \"#\"\n");
979  printf("\tputs $cfg \"# Automatically generated make config: don't edit\"\n");
980  printf("\tputs $cfg \"#\"\n");
981
982  printf("\tputs $autocfg \"/*\"\n");
983  printf("\tputs $autocfg \" * Automatically generated C config: don't edit\"\n");
984  printf("\tputs $autocfg \" */\"\n");
985  for(cfg = scfg; cfg != NULL; cfg = cfg->next)
986    {
987      switch (cfg->tok)
988        {
989        case tok_int:
990        case tok_hex:
991        case tok_bool:
992        case tok_tristate:
993        case tok_dep_tristate:
994        case tok_define:
995        case tok_choose:
996          if(!(cfg->flags & GLOBAL_WRITTEN))
997            {
998              cfg->flags |= GLOBAL_WRITTEN;
999              printf("\tglobal %s\n", cfg->optionname);
1000            }
1001          /* fall through */
1002        case tok_make:
1003        case tok_comment:
1004          if (cfg->cond != NULL ) 
1005            generate_if_for_outfile(cfg, cfg->cond);
1006          else
1007            {
1008              if(cfg->tok == tok_dep_tristate)
1009                {
1010                  printf("\tif {$%s == 0 } then {\n"
1011                         "\t\twrite_tristate $cfg $autocfg %s $notset $notmod\n"
1012                         "\t} else {\n"
1013                         "\t\twrite_tristate $cfg $autocfg %s $%s $%s\n"
1014                         "\t}\n",
1015                         cfg->depend.str,
1016                         cfg->optionname,
1017                         cfg->optionname,
1018                         cfg->optionname,
1019                         cfg->depend.str);
1020                }
1021              else if(cfg->tok == tok_comment)
1022                {
1023                  printf("\twrite_comment $cfg $autocfg \"%s\"\n", cfg->label);
1024                }
1025#if 0
1026              else if(cfg->tok == tok_define)
1027                {
1028                  printf("\twrite_define %s %s\n", cfg->optionname,
1029                         cfg->value);
1030                }
1031#endif
1032              else if (cfg->tok == tok_choose )
1033                {
1034                  for(cfg1 = cfg->next; 
1035                      cfg1 != NULL && cfg1->tok == tok_choice;
1036                      cfg1 = cfg1->next)
1037                    {
1038                      printf("\tif { $%s == \"%s\" } then { write_tristate $cfg $autocfg %s 1 $notmod }\n",
1039                             cfg->optionname,
1040                             cfg1->label,
1041                             cfg1->optionname);
1042                    }
1043                }
1044              else if (cfg->tok == tok_int )
1045                {
1046                  printf("\twrite_int $cfg $autocfg %s $%s $notmod\n",
1047                         cfg->optionname,
1048                         cfg->optionname);
1049                }
1050              else if (cfg->tok == tok_hex )
1051                {
1052                  printf("\twrite_hex $cfg $autocfg %s $%s $notmod\n",
1053                         cfg->optionname,
1054                         cfg->optionname);
1055                }
1056              else if (cfg->tok == tok_make )
1057                {
1058                  printf("\tdo_make {%s}\n",cfg->value);
1059                }
1060              else
1061                {
1062                  printf("\twrite_tristate $cfg $autocfg %s $%s $notmod\n",
1063                         cfg->optionname,
1064                         cfg->optionname);
1065                }
1066            }
1067          break;
1068        default:
1069          break;
1070        }
1071    }
1072  printf("\tclose $cfg\n");
1073  printf("\tclose $autocfg\n");
1074  printf("}\n\n\n");
1075
1076  /*
1077   * Finally write a simple function that updates the master choice
1078   * variable depending upon what values were loaded from a .config
1079   * file. 
1080   */
1081  printf("proc clear_choices { } {\n");
1082  for(cfg = scfg; cfg != NULL; cfg = cfg->next)
1083    {
1084      if( cfg->tok != tok_choose ) continue;
1085      for(cfg1 = cfg->next; 
1086          cfg1 != NULL && cfg1->tok == tok_choice;
1087          cfg1 = cfg1->next)
1088        {
1089          printf("\tglobal %s; set %s 0\n",cfg1->optionname,cfg1->optionname);
1090        }
1091    }
1092  printf("}\n\n\n");
1093
1094  printf("proc update_choices { } {\n");
1095  for(cfg = scfg; cfg != NULL; cfg = cfg->next)
1096    {
1097      if( cfg->tok != tok_choose ) continue;
1098      printf("\tglobal %s\n", cfg->optionname);
1099      for(cfg1 = cfg->next; 
1100          cfg1 != NULL && cfg1->tok == tok_choice;
1101          cfg1 = cfg1->next)
1102        {
1103          printf("\tglobal %s\n", cfg1->optionname);
1104          printf("\tif { $%s == 1 } then { set %s \"%s\" }\n",
1105                 cfg1->optionname,
1106                 cfg->optionname,
1107                 cfg1->label);
1108        }
1109    }
1110  printf("}\n\n\n");
1111
1112  printf("proc update_define { } {\n");
1113  clear_globalflags(config);
1114  for(cfg = scfg; cfg != NULL; cfg = cfg->next)
1115    {
1116      if( cfg->tok != tok_define ) continue;
1117      printf("\tglobal %s; set %s 0\n",  cfg->optionname,  cfg->optionname);
1118      cfg->flags |= GLOBAL_WRITTEN;
1119    }
1120  for(cfg = scfg; cfg != NULL; cfg = cfg->next)
1121    {
1122      if( cfg->tok != tok_define ) continue;
1123      if (cfg->cond != NULL ) 
1124        generate_if(cfg, cfg->cond, -1, 0);
1125      else
1126        {
1127          printf("\tset %s %s\n",
1128                 cfg->optionname, cfg->value);
1129        }
1130    }
1131  printf("}\n\n\n");
1132  /*
1133   * That's it.  We are done.  The output of this file will have header.tk
1134   * prepended and tail.tk appended to create an executable wish script.
1135   */
1136}
Note: See TracBrowser for help on using the repository browser.