1 //
2 // "$Id$"
3 //
4 // Fluid file routines for the Fast Light Tool Kit (FLTK).
5 //
6 // You may find the basic read_* and write_* routines to
7 // be useful for other programs. I have used them many times.
8 // They are somewhat similar to tcl, using matching { and }
9 // to quote strings.
10 //
11 // Copyright 1998-2016 by Bill Spitzak and others.
12 //
13 // This library is free software. Distribution and use rights are outlined in
14 // the file "COPYING" which should have been included with this file. If this
15 // file is missing or damaged, see the license at:
16 //
17 // http://www.fltk.org/COPYING.php
18 //
19 // Please report all bugs and problems on the following page:
20 //
21 // http://www.fltk.org/str.php
22 //
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include "../src/flstring.h"
27 #include <stdarg.h>
28 #include "alignment_panel.h"
29 #include <FL/Fl.H>
30 #include "Fl_Widget_Type.h"
31
32 ////////////////////////////////////////////////////////////////
33 // BASIC FILE WRITING:
34
35 static FILE *fout;
36
open_write(const char * s)37 int open_write(const char *s) {
38 if (!s) {fout = stdout; return 1;}
39 FILE *f = fl_fopen(s,"w");
40 if (!f) return 0;
41 fout = f;
42 return 1;
43 }
44
close_write()45 int close_write() {
46 if (fout != stdout) {
47 int x = fclose(fout);
48 fout = stdout;
49 return x >= 0;
50 }
51 return 1;
52 }
53
54 static int needspace;
55 int is_id(char); // in code.C
56
57 // write a string, quoting characters if necessary:
write_word(const char * w)58 void write_word(const char *w) {
59 if (needspace) putc(' ', fout);
60 needspace = 1;
61 if (!w || !*w) {fprintf(fout,"{}"); return;}
62 const char *p;
63 // see if it is a single word:
64 for (p = w; is_id(*p); p++) ;
65 if (!*p) {fprintf(fout,"%s",w); return;}
66 // see if there are matching braces:
67 int n = 0;
68 for (p = w; *p; p++) {
69 if (*p == '{') n++;
70 else if (*p == '}') {n--; if (n<0) break;}
71 }
72 int mismatched = (n != 0);
73 // write out brace-quoted string:
74 putc('{', fout);
75 for (; *w; w++) {
76 switch (*w) {
77 case '{':
78 case '}':
79 if (!mismatched) break;
80 case '\\':
81 case '#':
82 putc('\\',fout);
83 break;
84 }
85 putc(*w,fout);
86 }
87 putc('}', fout);
88 }
89
90 // write an arbitrary formatted word, or a comment, etc.
91 // if needspace is set, then one space is written before the string
92 // unless the format starts with a newline character ('\n'):
write_string(const char * format,...)93 void write_string(const char *format, ...) {
94 va_list args;
95 va_start(args, format);
96 if (needspace && *format != '\n') fputc(' ',fout);
97 vfprintf(fout, format, args);
98 va_end(args);
99 needspace = !isspace(format[strlen(format)-1] & 255);
100 }
101
102 // start a new line and indent it for a given nesting level:
write_indent(int n)103 void write_indent(int n) {
104 fputc('\n',fout);
105 while (n--) {fputc(' ',fout); fputc(' ',fout);}
106 needspace = 0;
107 }
108
109 // write a '{' at the given indenting level:
write_open(int)110 void write_open(int) {
111 if (needspace) fputc(' ',fout);
112 fputc('{',fout);
113 needspace = 0;
114 }
115
116 // write a '}' at the given indenting level:
write_close(int n)117 void write_close(int n) {
118 if (needspace) write_indent(n);
119 fputc('}',fout);
120 needspace = 1;
121 }
122
123 ////////////////////////////////////////////////////////////////
124 // BASIC FILE READING:
125
126 static FILE *fin;
127 static int lineno;
128 static const char *fname;
129
open_read(const char * s)130 int open_read(const char *s) {
131 lineno = 1;
132 if (!s) {fin = stdin; fname = "stdin"; return 1;}
133 FILE *f = fl_fopen(s,"r");
134 if (!f) return 0;
135 fin = f;
136 fname = s;
137 return 1;
138 }
139
close_read()140 int close_read() {
141 if (fin != stdin) {
142 int x = fclose(fin);
143 fin = 0;
144 return x >= 0;
145 }
146 return 1;
147 }
148
149 #include <FL/fl_message.H>
150
read_error(const char * format,...)151 void read_error(const char *format, ...) {
152 va_list args;
153 va_start(args, format);
154 if (!fin) {
155 char buffer[1024];
156 vsnprintf(buffer, sizeof(buffer), format, args);
157 fl_message("%s", buffer);
158 } else {
159 fprintf(stderr, "%s:%d: ", fname, lineno);
160 vfprintf(stderr, format, args);
161 fprintf(stderr, "\n");
162 }
163 va_end(args);
164 }
165
hexdigit(int x)166 static int hexdigit(int x) {
167 if (isdigit(x)) return x-'0';
168 if (isupper(x)) return x-'A'+10;
169 if (islower(x)) return x-'a'+10;
170 return 20;
171 }
172
173
read_quoted()174 static int read_quoted() { // read whatever character is after a \ .
175 int c,d,x;
176 switch(c = fgetc(fin)) {
177 case '\n': lineno++; return -1;
178 case 'a' : return('\a');
179 case 'b' : return('\b');
180 case 'f' : return('\f');
181 case 'n' : return('\n');
182 case 'r' : return('\r');
183 case 't' : return('\t');
184 case 'v' : return('\v');
185 case 'x' : /* read hex */
186 for (c=x=0; x<3; x++) {
187 int ch = fgetc(fin);
188 d = hexdigit(ch);
189 if (d > 15) {ungetc(ch,fin); break;}
190 c = (c<<4)+d;
191 }
192 break;
193 default: /* read octal */
194 if (c<'0' || c>'7') break;
195 c -= '0';
196 for (x=0; x<2; x++) {
197 int ch = fgetc(fin);
198 d = hexdigit(ch);
199 if (d>7) {ungetc(ch,fin); break;}
200 c = (c<<3)+d;
201 }
202 break;
203 }
204 return(c);
205 }
206
207 // return a word read from the file, or NULL at the EOF:
208 // This will skip all comments (# to end of line), and evaluate
209 // all \xxx sequences and use \ at the end of line to remove the newline.
210 // A word is any one of:
211 // a continuous string of non-space chars except { and } and #
212 // everything between matching {...} (unless wantbrace != 0)
213 // the characters '{' and '}'
214
215 static char *buffer;
216 static int buflen;
expand_buffer(int length)217 static void expand_buffer(int length) {
218 if (length >= buflen) {
219 if (!buflen) {
220 buflen = length+1;
221 buffer = (char*)malloc(buflen);
222 } else {
223 buflen = 2*buflen;
224 if (length >= buflen) buflen = length+1;
225 buffer = (char *)realloc((void *)buffer,buflen);
226 }
227 }
228 }
229
read_word(int wantbrace)230 const char *read_word(int wantbrace) {
231 int x;
232
233 // skip all the whitespace before it:
234 for (;;) {
235 x = getc(fin);
236 if (x < 0 && feof(fin)) { // eof
237 return 0;
238 } else if (x == '#') { // comment
239 do x = getc(fin); while (x >= 0 && x != '\n');
240 lineno++;
241 continue;
242 } else if (x == '\n') {
243 lineno++;
244 } else if (!isspace(x & 255)) {
245 break;
246 }
247 }
248
249 expand_buffer(100);
250
251 if (x == '{' && !wantbrace) {
252
253 // read in whatever is between braces
254 int length = 0;
255 int nesting = 0;
256 for (;;) {
257 x = getc(fin);
258 if (x<0) {read_error("Missing '}'"); break;}
259 else if (x == '#') { // embedded comment
260 do x = getc(fin); while (x >= 0 && x != '\n');
261 lineno++;
262 continue;
263 } else if (x == '\n') lineno++;
264 else if (x == '\\') {x = read_quoted(); if (x<0) continue;}
265 else if (x == '{') nesting++;
266 else if (x == '}') {if (!nesting--) break;}
267 buffer[length++] = x;
268 expand_buffer(length);
269 }
270 buffer[length] = 0;
271 return buffer;
272
273 } else if (x == '{' || x == '}') {
274 // all the punctuation is a word:
275 buffer[0] = x;
276 buffer[1] = 0;
277 return buffer;
278
279 } else {
280
281 // read in an unquoted word:
282 int length = 0;
283 for (;;) {
284 if (x == '\\') {x = read_quoted(); if (x<0) continue;}
285 else if (x<0 || isspace(x & 255) || x=='{' || x=='}' || x=='#') break;
286 buffer[length++] = x;
287 expand_buffer(length);
288 x = getc(fin);
289 }
290 ungetc(x, fin);
291 buffer[length] = 0;
292 return buffer;
293
294 }
295 }
296
297 ////////////////////////////////////////////////////////////////
298
299 // global int variables:
300 extern int i18n_type;
301 extern const char* i18n_include;
302 extern const char* i18n_function;
303 extern const char* i18n_file;
304 extern const char* i18n_set;
305
306
307 extern int header_file_set;
308 extern int code_file_set;
309 extern const char* header_file_name;
310 extern const char* code_file_name;
311
write_file(const char * filename,int selected_only)312 int write_file(const char *filename, int selected_only) {
313 if (!open_write(filename)) return 0;
314 write_string("# data file for the Fltk User Interface Designer (fluid)\n"
315 "version %.4f",FL_VERSION);
316 if(!include_H_from_C)
317 write_string("\ndo_not_include_H_from_C");
318 if(use_FL_COMMAND)
319 write_string("\nuse_FL_COMMAND");
320 if (i18n_type) {
321 write_string("\ni18n_type %d", i18n_type);
322 write_string("\ni18n_include %s", i18n_include);
323 switch (i18n_type) {
324 case 1 : /* GNU gettext */
325 write_string("\ni18n_function %s", i18n_function);
326 break;
327 case 2 : /* POSIX catgets */
328 if (i18n_file[0]) write_string("\ni18n_file %s", i18n_file);
329 write_string("\ni18n_set %s", i18n_set);
330 break;
331 }
332 }
333 if (!selected_only) {
334 write_string("\nheader_name"); write_word(header_file_name);
335 write_string("\ncode_name"); write_word(code_file_name);
336 }
337 for (Fl_Type *p = Fl_Type::first; p;) {
338 if (!selected_only || p->selected) {
339 p->write();
340 write_string("\n");
341 int q = p->level;
342 for (p = p->next; p && p->level > q; p = p->next) {/*empty*/}
343 } else {
344 p = p->next;
345 }
346 }
347 return close_write();
348 }
349
350 ////////////////////////////////////////////////////////////////
351 // read all the objects out of the input file:
352
353 void read_fdesign();
354
355 double read_version;
356
357 extern Fl_Type *Fl_Type_make(const char *tn);
358
read_children(Fl_Type * p,int paste)359 static void read_children(Fl_Type *p, int paste) {
360 Fl_Type::current = p;
361 for (;;) {
362 const char *c = read_word();
363 REUSE_C:
364 if (!c) {
365 if (p && !paste) read_error("Missing '}'");
366 break;
367 }
368
369 if (!strcmp(c,"}")) {
370 if (!p) read_error("Unexpected '}'");
371 break;
372 }
373
374 // this is the first word in a .fd file:
375 if (!strcmp(c,"Magic:")) {
376 read_fdesign();
377 return;
378 }
379
380 if (!strcmp(c,"version")) {
381 c = read_word();
382 read_version = strtod(c,0);
383 if (read_version<=0 || read_version>double(FL_VERSION+0.00001))
384 read_error("unknown version '%s'",c);
385 continue;
386 }
387
388 // back compatibility with Vincent Penne's original class code:
389 if (!p && !strcmp(c,"define_in_struct")) {
390 Fl_Type *t = Fl_Type_make("class");
391 t->name(read_word());
392 Fl_Type::current = p = t;
393 paste = 1; // stops "missing }" error
394 continue;
395 }
396
397 if (!strcmp(c,"do_not_include_H_from_C")) {
398 include_H_from_C=0;
399 goto CONTINUE;
400 }
401 if (!strcmp(c,"use_FL_COMMAND")) {
402 use_FL_COMMAND=1;
403 goto CONTINUE;
404 }
405 if (!strcmp(c,"i18n_type")) {
406 i18n_type = atoi(read_word());
407 goto CONTINUE;
408 }
409 if (!strcmp(c,"i18n_function")) {
410 i18n_function = strdup(read_word());
411 goto CONTINUE;
412 }
413 if (!strcmp(c,"i18n_file")) {
414 i18n_file = strdup(read_word());
415 goto CONTINUE;
416 }
417 if (!strcmp(c,"i18n_set")) {
418 i18n_set = strdup(read_word());
419 goto CONTINUE;
420 }
421 if (!strcmp(c,"i18n_include")) {
422 i18n_include = strdup(read_word());
423 goto CONTINUE;
424 }
425 if (!strcmp(c,"i18n_type"))
426 {
427 i18n_type = atoi(read_word());
428 goto CONTINUE;
429 }
430 if (!strcmp(c,"i18n_type"))
431 {
432 i18n_type = atoi(read_word());
433 goto CONTINUE;
434 }
435 if (!strcmp(c,"header_name")) {
436 if (!header_file_set) header_file_name = strdup(read_word());
437 else read_word();
438 goto CONTINUE;
439 }
440
441 if (!strcmp(c,"code_name")) {
442 if (!code_file_set) code_file_name = strdup(read_word());
443 else read_word();
444 goto CONTINUE;
445 }
446
447 if (!strcmp(c, "snap") || !strcmp(c, "gridx") || !strcmp(c, "gridy")) {
448 // grid settings are now global
449 read_word();
450 goto CONTINUE;
451 }
452
453 {Fl_Type *t = Fl_Type_make(c);
454 if (!t) {
455 read_error("Unknown word \"%s\"", c);
456 continue;
457 }
458 t->name(read_word());
459
460 c = read_word(1);
461 if (strcmp(c,"{") && t->is_class()) { // <prefix> <name>
462 ((Fl_Class_Type*)t)->prefix(t->name());
463 t->name(c);
464 c = read_word(1);
465 }
466
467 if (strcmp(c,"{")) {
468 read_error("Missing property list for %s\n",t->title());
469 goto REUSE_C;
470 }
471
472 t->open_ = 0;
473 for (;;) {
474 const char *cc = read_word();
475 if (!cc || !strcmp(cc,"}")) break;
476 t->read_property(cc);
477 }
478
479 if (!t->is_parent()) continue;
480 c = read_word(1);
481 if (strcmp(c,"{")) {
482 read_error("Missing child list for %s\n",t->title());
483 goto REUSE_C;
484 }
485 read_children(t, 0);}
486 Fl_Type::current = p;
487 CONTINUE:;
488 }
489 }
490
491 extern void deselect();
492
read_file(const char * filename,int merge)493 int read_file(const char *filename, int merge) {
494 Fl_Type *o;
495 read_version = 0.0;
496 if (!open_read(filename)) return 0;
497 if (merge) deselect(); else delete_all();
498 read_children(Fl_Type::current, merge);
499 Fl_Type::current = 0;
500 // Force menu items to be rebuilt...
501 for (o = Fl_Type::first; o; o = o->next)
502 if (o->is_menu_button()) o->add_child(0,0);
503 for (o = Fl_Type::first; o; o = o->next)
504 if (o->selected) {Fl_Type::current = o; break;}
505 selection_changed(Fl_Type::current);
506 return close_read();
507 }
508
509 ////////////////////////////////////////////////////////////////
510 // Read Forms and XForms fdesign files:
511
read_fdesign_line(const char * & name,const char * & value)512 int read_fdesign_line(const char*& name, const char*& value) {
513
514 int length = 0;
515 int x;
516 // find a colon:
517 for (;;) {
518 x = getc(fin);
519 if (x < 0 && feof(fin)) return 0;
520 if (x == '\n') {length = 0; continue;} // no colon this line...
521 if (!isspace(x & 255)) {
522 buffer[length++] = x;
523 expand_buffer(length);
524 }
525 if (x == ':') break;
526 }
527 int valueoffset = length;
528 buffer[length-1] = 0;
529
530 // skip to start of value:
531 for (;;) {
532 x = getc(fin);
533 if ((x < 0 && feof(fin)) || x == '\n' || !isspace(x & 255)) break;
534 }
535
536 // read the value:
537 for (;;) {
538 if (x == '\\') {x = read_quoted(); if (x<0) continue;}
539 else if (x == '\n') break;
540 buffer[length++] = x;
541 expand_buffer(length);
542 x = getc(fin);
543 }
544 buffer[length] = 0;
545 name = buffer;
546 value = buffer+valueoffset;
547 return 1;
548 }
549
550 int fdesign_flip;
551 int fdesign_magic;
552 #include <FL/Fl_Group.H>
553
554 static const char *class_matcher[] = {
555 "FL_CHECKBUTTON", "Fl_Check_Button",
556 "FL_ROUNDBUTTON", "Fl_Round_Button",
557 "FL_ROUND3DBUTTON", "Fl_Round_Button",
558 "FL_LIGHTBUTTON", "Fl_Light_Button",
559 "FL_FRAME", "Fl_Box",
560 "FL_LABELFRAME", "Fl_Box",
561 "FL_TEXT", "Fl_Box",
562 "FL_VALSLIDER", "Fl_Value_Slider",
563 "FL_MENU", "Fl_Menu_Button",
564 "3", "FL_BITMAP",
565 "1", "FL_BOX",
566 "71","FL_BROWSER",
567 "11","FL_BUTTON",
568 "4", "FL_CHART",
569 "42","FL_CHOICE",
570 "61","FL_CLOCK",
571 "25","FL_COUNTER",
572 "22","FL_DIAL",
573 "101","FL_FREE",
574 "31","FL_INPUT",
575 "12","Fl_Light_Button",
576 "41","FL_MENU",
577 "23","FL_POSITIONER",
578 "13","Fl_Round_Button",
579 "21","FL_SLIDER",
580 "2", "FL_BOX", // was FL_TEXT
581 "62","FL_TIMER",
582 "24","Fl_Value_Slider",
583 0};
584
read_fdesign()585 void read_fdesign() {
586 fdesign_magic = atoi(read_word());
587 fdesign_flip = (fdesign_magic < 13000);
588 Fl_Widget_Type *window = 0;
589 Fl_Widget_Type *group = 0;
590 Fl_Widget_Type *widget = 0;
591 if (!Fl_Type::current) {
592 Fl_Type *t = Fl_Type_make("Function");
593 t->name("create_the_forms()");
594 Fl_Type::current = t;
595 }
596 for (;;) {
597 const char *name;
598 const char *value;
599 if (!read_fdesign_line(name, value)) break;
600
601 if (!strcmp(name,"Name")) {
602
603 window = (Fl_Widget_Type*)Fl_Type_make("Fl_Window");
604 window->name(value);
605 window->label(value);
606 Fl_Type::current = widget = window;
607
608 } else if (!strcmp(name,"class")) {
609
610 if (!strcmp(value,"FL_BEGIN_GROUP")) {
611 group = widget = (Fl_Widget_Type*)Fl_Type_make("Fl_Group");
612 Fl_Type::current = group;
613 } else if (!strcmp(value,"FL_END_GROUP")) {
614 if (group) {
615 Fl_Group* g = (Fl_Group*)(group->o);
616 g->begin();
617 g->forms_end();
618 Fl_Group::current(0);
619 }
620 group = widget = 0;
621 Fl_Type::current = window;
622 } else {
623 for (int i = 0; class_matcher[i]; i += 2)
624 if (!strcmp(value,class_matcher[i])) {
625 value = class_matcher[i+1]; break;}
626 widget = (Fl_Widget_Type*)Fl_Type_make(value);
627 if (!widget) {
628 printf("class %s not found, using Fl_Button\n", value);
629 widget = (Fl_Widget_Type*)Fl_Type_make("Fl_Button");
630 }
631 }
632
633 } else if (widget) {
634 if (!widget->read_fdesign(name, value))
635 printf("Ignoring \"%s: %s\"\n", name, value);
636 }
637 }
638 }
639
640 //
641 // End of "$Id$".
642 //
643