Don't set first style as default
[colloquium.git] / src / stylesheet.c
1 /*
2  * stylesheet.c
3  *
4  * Copyright © 2013 Thomas White <taw@bitwiz.org.uk>
5  *
6  * This file is part of Colloquium.
7  *
8  * Colloquium 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 <stdlib.h>
29 #include <string.h>
30 #include <gtk/gtk.h>
31 #include <assert.h>
32
33 #include "presentation.h"
34 #include "stylesheet.h"
35 #include "loadsave.h"
36
37
38 struct _stylesheet
39 {
40         struct style          **styles;
41         int                     n_styles;
42
43         struct slide_template **templates;
44         int                     n_templates;
45
46         struct style           *default_style;
47 };
48
49
50 struct style *new_style(StyleSheet *ss, const char *name, const char *pname)
51 {
52         struct style *sty;
53         int n;
54         struct style **styles_new;
55
56         sty = calloc(1, sizeof(*sty));
57         if ( sty == NULL ) return NULL;
58
59         sty->name = strdup(name);
60         sty->pname = strdup(pname);
61
62         /* DS9K compatibility */
63         sty->lop.x = 0.0;
64         sty->lop.y = 0.0;
65         sty->lop.w = 100.0;
66         sty->lop.h = 100.0;
67
68         n = ss->n_styles;
69         styles_new = realloc(ss->styles, (n+1)*sizeof(sty));
70         if ( styles_new == NULL ) {
71                 free(sty->name);
72                 free(sty);
73                 return NULL;
74         }
75         ss->styles = styles_new;
76         ss->styles[n] = sty;
77         ss->n_styles = n+1;
78
79         return sty;
80 }
81
82
83 struct style *lookup_style(StyleSheet *ss, const char *pname)
84 {
85         int i;
86
87         for ( i=0; i<ss->n_styles; i++ ) {
88                 if ( strcmp(ss->styles[i]->pname, pname) == 0 ) {
89                         return ss->styles[i];
90                 }
91         }
92         return NULL;
93 }
94
95
96 void free_stylesheet(StyleSheet *ss)
97 {
98         int i;
99
100         for ( i=0; i<ss->n_styles; i++ ) {
101                 free(ss->styles[i]->name);
102                 free(ss->styles[i]->sc_prologue);
103                 free(ss->styles[i]);
104         }
105
106         free(ss->styles);
107         free(ss);
108 }
109
110
111 extern void *_binary_src_default_stylesheet_sty_start;
112 extern void *_binary_src_default_stylesheet_sty_size;
113
114 StyleSheet *default_stylesheet()
115 {
116         char *v;
117         struct ds_node *root;
118         struct ds_node *ss_root;
119         StyleSheet *ss;
120         size_t len;
121
122         len = (size_t)&_binary_src_default_stylesheet_sty_size;
123         v = malloc(len+1);
124         if ( v == NULL ) return NULL;
125
126         memcpy(v, &_binary_src_default_stylesheet_sty_start, len);
127         v[len] = '\0';
128
129         root = new_ds_node("root");
130         deserialize_memory(v, root);
131
132         ss_root = find_node(root, "stylesheet", 0);
133         if ( ss_root == NULL ) {
134                 fprintf(stderr, "Doesn't look like a stylesheet.\n");
135                 return NULL;
136         }
137         ss = tree_to_stylesheet(ss_root);
138         free_ds_tree(root);
139         free(v);
140         return ss;
141 }
142
143
144 static void get_field_f_units(struct ds_node *root, const char *key,
145                               double *val, LengthUnits *units)
146 {
147         char *s;
148         char *u;
149         double v;
150         char *check;
151
152         get_field_s(root, key, &s);
153         u = index(s, ' ');
154         if ( u == NULL ) {
155                 fprintf(stderr, "Invalid value '%s'\n", s);
156                 return;
157         }
158
159         u[0] = '\0';
160         u++;
161         v = strtod(s, &check);
162         if ( check == s ) {
163                 fprintf(stderr, "Invalid value '%s'\n", s);
164                 return;
165         }
166
167         if ( strcmp(u, "f") == 0 ) *units = UNITS_FRAC;
168         else if ( strcmp(u, "u") == 0 ) *units = UNITS_SLIDE;
169         else {
170                 fprintf(stderr, "Invalid unit '%s'\n", u);
171                 return;
172         }
173
174         *val = v;
175 }
176
177
178 static int read_template(struct slide_template *t, StyleSheet *ss,
179                          struct ds_node *node)
180 {
181         int i;
182         int top_style, r;
183
184         r = get_field_i(node, "top_style", &top_style);
185         if ( !r ) {
186                 t->top_style = ss->styles[top_style];
187         }
188
189         for ( i=0; i<node->n_children; i++ ) {
190
191                 struct style *sty;
192
193                 if ( strncmp(node->children[i]->key, "sty", 3) != 0 ) {
194                         continue;
195                 }
196
197                 sty = lookup_style(ss, node->children[i]->value);
198                 if ( sty == NULL ) {
199                         fprintf(stderr, "Couldn't find style '%s'\n",
200                                 node->children[i]->value);
201                 } else {
202                         add_to_template(t, sty);
203                 }
204
205         }
206
207         return 0;
208 }
209
210
211 static int read_style(struct style *sty, struct ds_node *root)
212 {
213         get_field_f(root, "margin_l", &sty->lop.margin_l);
214         get_field_f(root, "margin_r", &sty->lop.margin_r);
215         get_field_f(root, "margin_t", &sty->lop.margin_t);
216         get_field_f(root, "margin_b", &sty->lop.margin_b);
217         get_field_f(root, "pad_l", &sty->lop.pad_l);
218         get_field_f(root, "pad_r", &sty->lop.pad_r);
219         get_field_f(root, "pad_t", &sty->lop.pad_t);
220         get_field_f(root, "pad_b", &sty->lop.pad_b);
221         get_field_f(root, "x", &sty->lop.x);
222         get_field_f(root, "y", &sty->lop.y);
223         get_field_f_units(root, "w", &sty->lop.w, &sty->lop.w_units);
224         get_field_f_units(root, "h", &sty->lop.h, &sty->lop.h_units);
225         get_field_s(root, "prologue", &sty->sc_prologue);
226
227         return 0;
228 }
229
230
231 StyleSheet *tree_to_stylesheet(struct ds_node *root)
232 {
233         StyleSheet *ss;
234         struct ds_node *node;
235         int i;
236         char *ds;
237
238         ss = new_stylesheet();
239         if ( ss == NULL ) return NULL;
240
241         node = find_node(root, "styles", 0);
242         if ( node == NULL ) {
243                 fprintf(stderr, "Couldn't find styles\n");
244                 free_stylesheet(ss);
245                 return NULL;
246         }
247
248         for ( i=0; i<node->n_children; i++ ) {
249
250                 struct style *ns;
251                 char *v;
252
253                 get_field_s(node->children[i], "name", &v);
254                 if ( v == NULL ) {
255                         fprintf(stderr, "No name for style '%s'\n",
256                                 node->children[i]->key);
257                         continue;
258                 }
259
260                 ns = new_style(ss, v, node->children[i]->key);
261                 if ( ns == NULL ) {
262                         fprintf(stderr, "Couldn't create style for '%s'\n",
263                                 node->children[i]->key);
264                         continue;
265                 }
266
267                 if ( read_style(ns, node->children[i]) ) {
268                         fprintf(stderr, "Couldn't read style '%s'\n", v);
269                         continue;
270                 }
271
272         }
273
274         if ( get_field_s(root, "default_style", &ds) == 0 ) {
275                 ss->default_style = lookup_style(ss, ds);
276         }
277
278         node = find_node(root, "templates", 0);
279         if ( node == NULL ) {
280                 fprintf(stderr, "Couldn't find templates\n");
281                 free_stylesheet(ss);
282                 return NULL;
283         }
284
285         for ( i=0; i<node->n_children; i++ ) {
286
287                 struct slide_template *nt;
288                 char *v;
289
290                 get_field_s(node->children[i], "name", &v);
291                 if ( v == NULL ) {
292                         fprintf(stderr, "No name for template '%s'\n",
293                                 node->children[i]->key);
294                         continue;
295                 }
296
297                 nt = new_template(ss, v);
298                 if ( nt == NULL ) {
299                         fprintf(stderr, "Couldn't create template for '%s'\n",
300                                 node->children[i]->key);
301                         continue;
302                 }
303
304                 if ( read_template(nt, ss, node->children[i]) ) {
305                         fprintf(stderr, "Couldn't read template '%s'\n", v);
306                         continue;
307                 }
308
309         }
310
311         return ss;
312 }
313
314
315 static int fixup_styles(struct frame *fr, StyleSheet *ss)
316 {
317         int i;
318         char *t;
319         int n = 0;
320
321         if ( fr->style != NULL ) {
322
323                 /* FIXME: Horrible to do this using names and string comparisons
324                  *  - what if the stylesheets are in different languages?
325                  * Better would be to have "roles" for common types of style
326                  * and match using those. */
327
328                 t = fr->style->name;
329
330                 for ( i=0; i<ss->n_styles; i++ ) {
331                         if ( strcmp(t, ss->styles[i]->name) == 0 ) {
332                                 fr->style = ss->styles[i];
333                                 n = 1;
334                                 break;
335                         }
336                 }
337         }
338
339         for ( i=0; i<fr->num_children; i++ ) {
340                 n += fixup_styles(fr->children[i], ss);
341         }
342
343         return n;
344 }
345
346
347 static void fixup_templates(struct slide *s, StyleSheet *ss)
348 {
349         int i;
350         char *t;
351
352         if ( s->st != NULL ) {
353
354                 /* FIXME: Horrible to do this using names and string comparisons
355                  *  - what if the stylesheets are in different languages?
356                  * Better would be to have "roles" for common types of template
357                  * and match using those. */
358
359                 t = s->st->name;
360
361                 for ( i=0; i<ss->n_templates; i++ ) {
362                         if ( strcmp(t, ss->templates[i]->name) == 0 ) {
363                                 s->st = ss->templates[i];
364                                 break;
365                         }
366                 }
367         }
368 }
369
370
371 int replace_stylesheet(struct presentation *p, const char *filename)
372 {
373         FILE *fh;
374         struct ds_node *root;
375         struct ds_node *node;
376         StyleSheet *ss;
377         int i;
378
379         fh = fopen(filename, "r");
380         if ( fh == NULL ) return 1;
381
382         root = new_ds_node("root");
383         if ( root == NULL ) return 1;
384
385         if ( deserialize_file(fh, root) ) {
386                 fclose(fh);
387                 return 1;
388         }
389
390         fclose(fh);
391
392         node = find_node(root, "stylesheet", 0);
393         if ( node == NULL ) {
394                 free_ds_tree(root);
395                 return 1;
396         }
397         ss = tree_to_stylesheet(node);
398         if ( ss == NULL ) {
399                 fprintf(stderr, "Invalid style sheet\n");
400                 return 1;
401         }
402         free_ds_tree(root);
403
404         for ( i=0; i<p->num_slides; i++ ) {
405                 int n;
406                 n = fixup_styles(p->slides[i]->top, ss);
407                 printf("Fixed up %i styles\n", n);
408                 fixup_templates(p->slides[i], ss);
409         }
410
411         free_stylesheet(p->ss);
412         p->ss = ss;
413
414         return 0;
415 }
416
417
418 StyleSheet *new_stylesheet()
419 {
420         StyleSheet *ss;
421
422         ss = calloc(1, sizeof(struct _stylesheet));
423         if ( ss == NULL ) return NULL;
424
425         ss->n_styles = 0;
426         ss->styles = NULL;
427         ss->default_style = NULL;
428
429         return ss;
430 }
431
432
433 int save_stylesheet(StyleSheet *ss, const char *filename)
434 {
435         FILE *fh;
436         struct serializer ser;
437
438         fh = fopen(filename, "w");
439         if ( fh == NULL ) return 1;
440
441         /* Set up the serializer */
442         ser.fh = fh;
443         ser.stack_depth = 0;
444         ser.prefix = NULL;
445
446         fprintf(fh, "# Colloquium style sheet file\n");
447         serialize_f(&ser, "version", 0.1);
448
449         serialize_start(&ser, "stylesheet");
450         write_stylesheet(ss, &ser);
451         serialize_end(&ser);
452
453         fclose(fh);
454
455         return 0;
456 }
457
458
459 StyleSheet *load_stylesheet(const char *filename)
460 {
461         StyleSheet *ss;
462
463         ss = new_stylesheet();
464         if ( ss == NULL ) return NULL;
465
466         /* FIXME: Implement this */
467
468         return ss;
469 }
470
471
472 const char *units(LengthUnits un)
473 {
474         switch ( un ) {
475                 case UNITS_SLIDE : return "u";
476                 case UNITS_FRAC : return "f";
477         }
478         return "annoyingly unspecified units";
479 }
480
481
482 static void serialize_f_units(struct serializer *s, const char *key, double val,
483                               LengthUnits un)
484 {
485         char tmp[64];
486
487         snprintf(tmp, 63, "%.2f %s", val, units(un));
488         serialize_s(s, key, tmp);
489 }
490
491
492 void write_stylesheet(StyleSheet *ss, struct serializer *ser)
493 {
494         int i;
495
496         if ( ss->default_style != NULL ) {
497                 serialize_s(ser, "default_style", ss->default_style->pname);
498         }
499
500         serialize_start(ser, "styles");
501         for ( i=0; i<ss->n_styles; i++ ) {
502
503                 struct style *s = ss->styles[i];
504
505                 serialize_start(ser, s->pname);
506                 serialize_s(ser, "name", s->name);
507                 serialize_f(ser, "margin_l", s->lop.margin_l);
508                 serialize_f(ser, "margin_r", s->lop.margin_r);
509                 serialize_f(ser, "margin_t", s->lop.margin_t);
510                 serialize_f(ser, "margin_b", s->lop.margin_b);
511                 serialize_f(ser, "pad_l", s->lop.pad_l);
512                 serialize_f(ser, "pad_r", s->lop.pad_r);
513                 serialize_f(ser, "pad_t", s->lop.pad_t);
514                 serialize_f(ser, "pad_b", s->lop.pad_b);
515                 serialize_f_units(ser, "w", s->lop.w, s->lop.w_units);
516                 serialize_f_units(ser, "h", s->lop.h, s->lop.h_units);
517                 serialize_f(ser, "x", s->lop.x);
518                 serialize_f(ser, "y", s->lop.y);
519                 serialize_s(ser, "prologue", s->sc_prologue);
520                 serialize_end(ser);
521
522         }
523         serialize_end(ser);
524
525         serialize_start(ser, "templates");
526         for ( i=0; i<ss->n_templates; i++ ) {
527
528                 struct slide_template *t = ss->templates[i];
529                 char id[32];
530                 int j;
531
532                 snprintf(id, 31, "%i", i);
533
534                 serialize_start(ser, id);
535                 serialize_s(ser, "name", t->name);
536                 if (t->top_style != NULL ) {
537                         serialize_s(ser, "top_style", t->top_style->pname);
538                 }
539                 for ( j=0; j<t->n_styles; j++ ) {
540
541                         struct style *s = t->styles[j];
542                         char id[32];
543
544                         snprintf(id, 31, "sty%i", j);
545
546                         serialize_s(ser, id, s->pname);
547
548                 }
549                 serialize_end(ser);
550
551         }
552         serialize_end(ser);
553 }
554
555
556 struct style *default_style(StyleSheet *ss)
557 {
558         return ss->default_style;
559 }
560
561
562 struct slide_template *new_template(StyleSheet *ss, const char *name)
563 {
564         struct slide_template *t;
565         int n;
566         struct slide_template **templates_new;
567
568         t = calloc(1, sizeof(*t));
569         if ( t == NULL ) return NULL;
570
571         t->name = strdup(name);
572
573         n = ss->n_templates;
574         templates_new = realloc(ss->templates, (n+1)*sizeof(t));
575         if ( templates_new == NULL ) {
576                 free(t->name);
577                 free(t);
578                 return NULL;
579         }
580         ss->templates = templates_new;
581         ss->templates[n] = t;
582         ss->n_templates = n+1;
583
584         return t;
585 }
586
587
588 void add_to_template(struct slide_template *t, struct style *sty)
589 {
590         int n;
591         struct style **styles_new;
592
593         n = t->n_styles;
594         styles_new = realloc(t->styles, (n+1)*sizeof(sty));
595         if ( styles_new == NULL ) {
596                 fprintf(stderr, "Failed to add style '%s' to template '%s'.\n",
597                         sty->name, t->name);
598                 return;
599         }
600         t->styles = styles_new;
601         t->styles[n] = sty;
602         t->n_styles = n+1;
603 }
604
605
606 struct _styleiterator
607 {
608         int n;
609 };
610
611 struct style *style_first(StyleSheet *ss, StyleIterator **piter)
612 {
613         StyleIterator *iter;
614
615         if ( ss->n_styles == 0 ) return NULL;
616
617         iter = calloc(1, sizeof(StyleIterator));
618         if ( iter == NULL ) return NULL;
619
620         iter->n = 0;
621         *piter = iter;
622
623         return ss->styles[0];
624 }
625
626
627 struct style *style_next(StyleSheet *ss, StyleIterator *iter)
628 {
629         iter->n++;
630         if ( iter->n == ss->n_styles ) {
631                 free(iter);
632                 return NULL;
633         }
634
635         return ss->styles[iter->n];
636 }
637
638
639 struct _templateiterator
640 {
641         int n;
642 };
643
644 struct slide_template *template_first(StyleSheet *ss, TemplateIterator **piter)
645 {
646         TemplateIterator *iter;
647
648         if ( ss->n_templates == 0 ) return NULL;
649
650         iter = calloc(1, sizeof(TemplateIterator));
651         if ( iter == NULL ) return NULL;
652
653         iter->n = 0;
654         *piter = iter;
655
656         return ss->templates[0];
657 }
658
659
660 struct slide_template *template_next(StyleSheet *ss, TemplateIterator *iter)
661 {
662         iter->n++;
663         if ( iter->n == ss->n_templates ) {
664                 free(iter);
665                 return NULL;
666         }
667
668         return ss->templates[iter->n];
669 }