You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

651 lines
16KB

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