Move stylesheet deserialization to stylesheet.c
[colloquium.git] / src / loadsave.c
1 /*
2  * loadsave.c
3  *
4  * Colloquium - A tiny presentation program
5  *
6  * Copyright (c) 2011 Thomas White <taw@bitwiz.org.uk>
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <string.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include <assert.h>
33
34 #include "presentation.h"
35 #include "objects.h"
36 #include "stylesheet.h"
37 #include "slide_render.h"
38 #include "mainwindow.h"
39
40
41 static int alloc_children(struct ds_node *node)
42 {
43         struct ds_node **new;
44
45         new = realloc(node->children,
46                       node->max_children*sizeof(*node->children));
47         if ( new == NULL ) return 1;
48
49         node->children = new;
50         return 0;
51 }
52
53
54 static struct ds_node *new_ds_node(const char *key)
55 {
56         struct ds_node *new;
57
58         new = malloc(sizeof(*new));
59         if ( new == NULL ) return NULL;
60
61         new->key = strdup(key);
62         if ( new->key == NULL ) {
63                 free(new);
64                 return NULL;
65         }
66
67         new->value = NULL;
68         new->n_children = 0;
69         new->max_children = 32;
70         new->children = NULL;
71
72         if ( alloc_children(new) ) {
73                 free(new);
74                 return NULL;
75         }
76
77         return new;
78 }
79
80
81 static struct ds_node *add_child(struct ds_node *node, const char *key)
82 {
83         struct ds_node *new;
84
85         new = new_ds_node(key);
86         if ( new == NULL ) return NULL;
87
88         if ( node->n_children >= new->max_children ) {
89                 new->max_children += 32;
90                 if ( alloc_children(node) ) {
91                         free(new);
92                         return NULL;
93                 }
94         }
95
96         node->children[node->n_children++] = new;
97
98         return new;
99 }
100
101
102 static void UNUSED show_tree(struct ds_node *root, const char *path)
103 {
104         char newpath[1024];
105         int i;
106
107         snprintf(newpath, 1023, "%s%s/", path, root->key);
108
109         printf("%s\n", newpath);
110         for ( i=0; i<root->n_children; i++ ) {
111                 printf("     %s => %s\n", root->children[i]->key,
112                                           root->children[i]->value);
113         }
114
115         for ( i=0; i<root->n_children; i++ ) {
116                 if ( root->children[i]->n_children > 0 ) {
117                         printf("\n");
118                         show_tree(root->children[i], newpath);
119                 }
120         }
121 }
122
123
124 struct ds_node *find_node(struct ds_node *root, const char *path)
125 {
126         size_t start, len;
127         char element[1024];
128         struct ds_node *cur = root;
129
130         len = strlen(path);
131
132         start = 0;
133         while ( start < len ) {
134
135                 size_t pos, i;
136                 int child;
137                 int found = 0;
138
139                 pos = 0;
140                 for ( i=start; i<len; i++ ) {
141
142                         if ( path[i] == '/' ) break;
143                         element[pos++] = path[i];
144
145                 }
146                 element[pos++] = '\0';
147                 if ( element[0] == '\0' ) {
148                         goto out;
149                 }
150                 start = i+1;
151
152                 for ( child=0; child<cur->n_children; child++ ) {
153
154                         const char *this_key = cur->children[child]->key;
155
156                         if ( strcmp(this_key, element) == 0 ) {
157                                 cur = cur->children[child];
158                                 found = 1;
159                                 break;
160                         }
161
162                 }
163
164                 if ( !found ) {
165
166                         cur = add_child(cur, element);
167                         if ( cur == NULL ) {
168                                 return NULL;  /* Error */
169                         }
170
171                 }
172
173         }
174
175 out:
176         return cur;
177 }
178
179
180 static int deserialize_file(FILE *fh, struct ds_node *root)
181 {
182         char *rval = NULL;
183         struct ds_node *cur_node = root;
184
185         do {
186                 size_t i;
187                 char line[1024];
188                 size_t len, s_start;
189                 size_t s_equals = 0;
190                 size_t s_val = 0;
191                 size_t s_openbracket = 0;
192                 size_t s_closebracket = 0;
193                 int h_start = 0;
194                 int h_equals = 0;
195                 int h_val = 0;
196                 int h_openbracket = 0;
197                 int h_closebracket = 0;
198
199                 rval = fgets(line, 1023, fh);
200                 if ( rval == NULL ) {
201                         if ( ferror(fh) ) printf("Read error!\n");
202                         continue;
203                 }
204
205                 len = strlen(line);
206                 s_start = len-1;
207
208                 for ( i=0; i<len-1; i++ ) {
209                         if ( !h_start && !isspace(line[i]) ) {
210                                 s_start = i;
211                                 h_start = 1;
212                         }
213                         if ( !h_val && h_equals && !isspace(line[i]) ) {
214                                 s_val = i;
215                                 h_val = 1;
216                         }
217                         if ( !h_equals && (line[i] == '=') ) {
218                                 s_equals = i;
219                                 h_equals = 1;
220                         }
221                         if ( !h_openbracket && (line[i] == '[') ) {
222                                 s_openbracket = i;
223                                 h_openbracket = 1;
224                         }
225                         if ( h_openbracket && !h_closebracket
226                              && (line[i] == ']') )
227                         {
228                                 s_closebracket = i;
229                                 h_closebracket = 1;
230                         }
231                 }
232
233                 if ( (h_openbracket && !h_closebracket)
234                   || (!h_openbracket && h_closebracket) )
235                 {
236                         fprintf(stderr, "Mismatched square brackets: %s", line);
237                         continue;
238                 }
239
240                 if ( !h_openbracket && !h_equals ) continue;
241
242                 if ( !h_openbracket && (!h_start || !h_val || !h_equals) ) {
243                         fprintf(stderr, "Incomplete assignment: %s", line);
244                         continue;
245                 }
246
247                 if ( h_equals && (h_openbracket || h_closebracket) ) {
248                         fprintf(stderr, "Brackets and equals: %s", line);
249                         continue;
250                 }
251
252                 if ( !h_openbracket ) {
253
254                         size_t pos = 0;
255                         char key[1024];
256                         char value[1024];
257                         struct ds_node *node;
258
259                         for ( i=s_start; i<s_equals; i++ ) {
260                                 if ( !isspace(line[i]) ) key[pos++] = line[i];
261                         }
262                         key[pos] = '\0';
263
264                         pos = 0;
265                         for ( i=s_val; i<len; i++ ) {
266                                 if ( line[i] != '\n' ) value[pos++] = line[i];
267                         }
268                         value[pos] = '\0';
269
270                         node = find_node(cur_node, key);
271                         node->value = strdup(value);
272
273                 } else {
274
275                         size_t pos = 0;
276                         char path[1024];
277
278                         for ( i=s_openbracket+1; i<s_closebracket; i++ ) {
279                                 if ( !isspace(line[i]) ) path[pos++] = line[i];
280                         }
281                         path[pos] = '\0';
282                         cur_node = find_node(root, path);
283
284                 }
285
286         } while ( rval != NULL );
287
288         return 0;
289 }
290
291
292 static void free_ds_tree(struct ds_node *root)
293 {
294         int i;
295
296         for ( i=0; i<root->n_children; i++ ) {
297                 if ( root->children[i]->n_children > 0 ) {
298                         free_ds_tree(root->children[i]);
299                 }
300         }
301
302         free(root->key);
303         free(root->value);  /* Might free(NULL), but that's fine */
304         free(root);
305 }
306
307
308 int get_field_f(struct ds_node *root, const char *key, double *val)
309 {
310         struct ds_node *node;
311         double v;
312         char *check;
313
314         node = find_node(root, key);
315         if ( node == NULL ) {
316                 fprintf(stderr, "Couldn't find field '%s'\n", key);
317                 return 1;
318         }
319
320         v = strtod(node->value, &check);
321         if ( check == node->value ) {
322                 fprintf(stderr, "Invalid value for '%s'\n", key);
323                 return 1;
324         }
325
326         *val = v;
327
328         return 0;
329 }
330
331
332 int get_field_i(struct ds_node *root, const char *key, int *val)
333 {
334         struct ds_node *node;
335         int v;
336         char *check;
337
338         node = find_node(root, key);
339         if ( node == NULL ) {
340                 fprintf(stderr, "Couldn't find field '%s'\n", key);
341                 return 1;
342         }
343
344         v = strtol(node->value, &check, 0);
345         if ( check == node->value ) {
346                 fprintf(stderr, "Invalid value for '%s'\n", key);
347                 return 1;
348         }
349
350         *val = v;
351
352         return 0;
353 }
354
355
356 int get_field_s(struct ds_node *root, const char *key, char **val)
357 {
358         struct ds_node *node;
359         char *v;
360         size_t i, len, s1, s2;
361         int hq;
362
363         node = find_node(root, key);
364         if ( node == NULL ) {
365                 fprintf(stderr, "Couldn't find field '%s'\n", key);
366                 return 1;
367         }
368
369         len = strlen(node->value);
370         hq = 0;
371         for ( i=0; i<len; i++ ) {
372                 if ( node->value[i] == '"' ) {
373                         s1 = i;
374                         hq = 1;
375                         break;
376                 }
377         }
378         if ( !hq ) {
379                 fprintf(stderr, "No quotes in '%s'\n", node->value);
380                 return 1;
381         }
382
383         for ( i=len-1; i>=0; i-- ) {
384                 if ( node->value[i] == '"' ) {
385                         s2 = i;
386                         break;
387                 }
388         }
389
390         if ( s1 == s2 ) {
391                 fprintf(stderr, "Mismatchd quotes in '%s'\n", node->value);
392                 return 1;
393         }
394
395         v = malloc(s2-s1+1);
396         if ( v == NULL ) {
397                 fprintf(stderr, "Failed to allocate space for '%s'\n", key);
398                 return 1;
399         }
400
401         strncpy(v, node->value+s1+1, s2-s1-1);
402         v[s2-s1-1] = '\0';
403
404         *val = v;
405
406         return 0;
407 }
408
409
410 static enum objtype text_to_type(const char *t)
411 {
412         if ( strcmp(t, "text") == 0 ) return OBJ_TEXT;
413         if ( strcmp(t, "image") == 0 ) return OBJ_IMAGE;
414
415         return OBJ_UNKNOWN;
416 }
417
418
419 static const char *type_text(enum objtype t)
420 {
421         switch ( t )
422         {
423                 case OBJ_TEXT : return "text";
424                 case OBJ_IMAGE : return "image";
425                 default : return "unknown";
426         }
427 }
428
429
430
431 static struct object *tree_to_object(struct presentation *p,
432                                      struct ds_node *root)
433 {
434         struct object *o = NULL;
435         char *v;
436
437         if ( get_field_s(root, "type", &v) ) return NULL;
438
439         switch ( text_to_type(v) ) {
440
441                 case OBJ_TEXT :
442                 o = p->text_tool->deserialize(p, root, p->text_tool);
443                 break;
444
445                 case OBJ_IMAGE :
446                 o = p->image_tool->deserialize(p, root, p->image_tool);
447                 break;
448
449                 default :
450                 fprintf(stderr, "Unrecognised object type '%s'\n", v);
451                 break;
452
453         }
454
455         return o;
456 }
457
458
459 static struct slide *tree_to_slide(struct presentation *p, struct ds_node *root)
460 {
461         struct slide *s;
462         int i;
463
464         s = new_slide();
465         s->parent = p;
466
467         /* Loop over objects */
468         for ( i=0; i<root->n_children; i++ ) {
469
470                 struct object *o;
471
472                 o = tree_to_object(p, root->children[i]);
473                 if ( o != NULL ) {
474                         add_object_to_slide(s, o);
475                         o->update_object(o);
476                 }
477
478         }
479
480         redraw_slide(s);
481
482         return s;
483 }
484
485
486 static int tree_to_slides(struct ds_node *root, struct presentation *p)
487 {
488         int i;
489
490         for ( i=0; i<root->n_children; i++ ) {
491
492                 struct slide *s;
493
494                 s = tree_to_slide(p, root->children[i]);
495                 if ( s != NULL ) {
496                         insert_slide(p, s, p->num_slides-1);
497                         redraw_slide(s);
498                 }
499
500         }
501
502         return 0;
503 }
504
505
506 int tree_to_presentation(struct ds_node *root, struct presentation *p)
507 {
508         struct ds_node *node;
509         char *check;
510         int i;
511
512         p->cur_edit_slide = NULL;
513         p->cur_proj_slide = NULL;
514
515         node = find_node(root, "slide-properties/width");
516         if ( node == NULL ) return 1;
517         p->slide_width = strtod(node->value, &check);
518         if ( check == node->value ) {
519                 fprintf(stderr, "Invalid slide width\n");
520                 return 1;
521         }
522
523         node = find_node(root, "slide-properties/height");
524         if ( node == NULL ) return 1;
525         p->slide_height = strtod(node->value, &check);
526         if ( check == node->value ) {
527                 fprintf(stderr, "Invalid slide height\n");
528                 return 1;
529         }
530
531         node = find_node(root, "stylesheet");
532         if ( node != NULL ) {
533                 free_stylesheet(p->ss);
534                 p->ss = tree_to_stylesheet(node);
535                 if ( p->ss == NULL ) {
536                         fprintf(stderr, "Invalid style sheet\n");
537                         return 1;
538                 }
539         }
540
541         for ( i=0; i<p->num_slides; i++ ) {
542                 free_slide(p->slides[i]);
543                 p->num_slides = 0;
544         }
545
546         node = find_node(root, "slides");
547         if ( node != NULL ) {
548                 tree_to_slides(node, p);
549                 if ( p->num_slides == 0 ) {
550                         fprintf(stderr, "Failed to load any slides\n");
551                         p->cur_edit_slide = add_slide(p, 0);
552                         return 1;
553                 }
554         }
555
556         p->cur_edit_slide = p->slides[0];
557         redraw_slide(p->cur_edit_slide);
558
559         return 0;
560 }
561
562
563 int load_presentation(struct presentation *p, const char *filename)
564 {
565         FILE *fh;
566         struct ds_node *root;
567         int r;
568
569         assert(p->completely_empty);
570
571         fh = fopen(filename, "r");
572         if ( fh == NULL ) return 1;
573
574         root = new_ds_node("root");
575         if ( root == NULL ) return 1;
576
577         if ( deserialize_file(fh, root) ) {
578                 fclose(fh);
579                 return 1;
580         }
581
582         r = tree_to_presentation(root, p);
583         free_ds_tree(root);
584
585         fclose(fh);
586
587         if ( r ) {
588                 p->cur_edit_slide = new_slide();
589                 insert_slide(p, p->cur_edit_slide, 0);
590                 p->completely_empty = 1;
591                 return r;  /* Error */
592         }
593
594         assert(p->filename == NULL);
595         p->filename = strdup(filename);
596         update_titlebar(p);
597
598         p->cur_edit_slide = p->slides[0];
599
600         return 0;
601 }
602
603
604 static void rebuild_prefix(struct serializer *ser)
605 {
606         int i;
607         size_t sz = 1;  /* Space for terminator */
608
609         for ( i=0; i<ser->stack_depth; i++ ) {
610                 sz += strlen(ser->stack[i]) + 1;
611         }
612
613         free(ser->prefix);
614         ser->prefix = malloc(sz);
615         if ( ser->prefix == NULL ) return;  /* Probably bad! */
616
617         ser->prefix[0] = '\0';
618         for ( i=0; i<ser->stack_depth; i++ ) {
619                 if ( i != 0 ) strcat(ser->prefix, "/");
620                 strcat(ser->prefix, ser->stack[i]);
621         }
622 }
623
624
625 void serialize_start(struct serializer *ser, const char *id)
626 {
627         ser->stack[ser->stack_depth++] = strdup(id);
628         rebuild_prefix(ser);
629         ser->empty_set = 1;
630 }
631
632
633 static void check_prefix_output(struct serializer *ser)
634 {
635         if ( ser->empty_set ) {
636                 ser->empty_set = 0;
637                 if ( ser->prefix != NULL ) {
638                         fprintf(ser->fh, "\n");
639                         fprintf(ser->fh, "[%s]\n", ser->prefix);
640                 }
641         }
642 }
643
644
645 void serialize_s(struct serializer *ser, const char *key, const char *val)
646 {
647         check_prefix_output(ser);
648         fprintf(ser->fh, "%s = \"%s\"\n", key, val);
649 }
650
651
652 void serialize_f(struct serializer *ser, const char *key, double val)
653 {
654         check_prefix_output(ser);
655         fprintf(ser->fh, "%s = %.2f\n", key, val);
656 }
657
658
659 void serialize_b(struct serializer *ser, const char *key, int val)
660 {
661         check_prefix_output(ser);
662         fprintf(ser->fh, "%s = %i\n", key, val);
663 }
664
665
666 void serialize_end(struct serializer *ser)
667 {
668         free(ser->stack[--ser->stack_depth]);
669         rebuild_prefix(ser);
670 }
671
672
673 int save_presentation(struct presentation *p, const char *filename)
674 {
675         FILE *fh;
676         int i;
677         struct serializer ser;
678         char *old_fn;
679
680         fh = fopen(filename, "w");
681         if ( fh == NULL ) return 1;
682
683         /* Set up the serializer */
684         ser.fh = fh;
685         ser.stack_depth = 0;
686         ser.prefix = NULL;
687
688         fprintf(fh, "# Colloquium presentation file\n");
689         serialize_f(&ser, "version", 0.1);
690
691         serialize_start(&ser, "slide-properties");
692         serialize_f(&ser, "width", p->slide_width);
693         serialize_f(&ser, "height", p->slide_height);
694         serialize_end(&ser);
695
696         serialize_start(&ser, "stylesheet");
697         write_stylesheet(p->ss, &ser);
698         serialize_end(&ser);
699
700         serialize_start(&ser, "slides");
701         for ( i=0; i<p->num_slides; i++ ) {
702
703                 int j;
704                 struct slide *s;
705                 char s_id[32];
706
707                 s = p->slides[i];
708
709                 snprintf(s_id, 31, "%i", i);
710                 serialize_start(&ser, s_id);
711                 for ( j=0; j<s->num_objects; j++ ) {
712
713                         struct object *o = s->objects[j];
714                         char o_id[32];
715
716                         if ( o->empty ) continue;
717                         snprintf(o_id, 31, "%i", j);
718
719                         serialize_start(&ser, o_id);
720                         serialize_s(&ser, "type", type_text(o->type));
721                         o->serialize(o, &ser);
722                         serialize_end(&ser);
723
724                 }
725                 serialize_end(&ser);
726
727         }
728         serialize_end(&ser);
729
730         /* Slightly fiddly because someone might
731          * do save_presentation(p, p->filename) */
732         old_fn = p->filename;
733         p->filename = strdup(filename);
734         if ( old_fn != NULL ) free(old_fn);
735         update_titlebar(p);
736
737         fclose(fh);
738         return 0;
739 }