Initialise fgcol
[colloquium.git] / src / sc_interp.c
1 /*
2  * sc_interp.c
3  *
4  * Copyright © 2014-2018 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 <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <pango/pangocairo.h>
32 #include <gdk/gdk.h>
33
34 #include "imagestore.h"
35 #include "sc_parse.h"
36 #include "sc_interp.h"
37 #include "presentation.h"
38 #include "utils.h"
39
40
41 struct macro
42 {
43         char *name;
44         SCBlock *bl;
45         struct macro *prev;  /* Previous declaration, or NULL */
46 };
47
48
49 struct template
50 {
51         char *name;
52         SCBlock *bl;
53 };
54
55
56 struct sc_state
57 {
58         PangoFontDescription *fontdesc;
59         PangoFont *font;
60         PangoAlignment alignment;
61         double col[4];
62         int ascent;
63         int height;
64         float paraspace[4];
65
66         int have_size;
67         double slide_width;
68         double slide_height;
69
70         struct frame *fr;  /* The current frame */
71
72         int n_macros;
73         int max_macros;
74         struct macro *macros;  /* Contents need to be copied on push */
75
76         int n_templates;
77         int max_templates;
78         struct template *templates;
79
80         SCBlock *macro_contents;  /* If running a macro, the child block of the caller */
81         SCBlock *macro_real_block;  /* If running a macro, the block which called the macro */
82         int macro_editable;  /* If running a macro, whether this bit can be edited or not */
83 };
84
85
86 struct _scinterp
87 {
88         PangoContext *pc;
89         PangoLanguage *lang;
90         ImageStore *is;
91
92         struct slide_constants *s_constants;
93         struct presentation_constants *p_constants;
94
95         struct sc_state *state;
96         int j;  /* Index of the current state */
97         int max_state;
98
99         SCCallbackList *cbl;
100 };
101
102 struct _sccallbacklist
103 {
104         int n_callbacks;
105         int max_callbacks;
106         char **names;
107         SCCallbackBoxFunc *box_funcs;
108         SCCallbackDrawFunc *draw_funcs;
109         SCCallbackClickFunc *click_funcs;
110         void **vps;
111 };
112
113
114 SCCallbackList *sc_callback_list_new()
115 {
116         SCCallbackList *cbl;
117
118         cbl = malloc(sizeof(struct _sccallbacklist));
119         if ( cbl == NULL ) return NULL;
120
121         cbl->names = calloc(8, sizeof(char *));
122         if ( cbl->names == NULL ) return NULL;
123
124         cbl->box_funcs = calloc(8, sizeof(cbl->box_funcs[0]));
125         if ( cbl->box_funcs == NULL ) return NULL;
126
127         cbl->draw_funcs = calloc(8, sizeof(cbl->draw_funcs[0]));
128         if ( cbl->draw_funcs == NULL ) return NULL;
129
130         cbl->click_funcs = calloc(8, sizeof(cbl->click_funcs[0]));
131         if ( cbl->click_funcs == NULL ) return NULL;
132
133         cbl->vps = calloc(8, sizeof(cbl->vps[0]));
134         if ( cbl->vps == NULL ) return NULL;
135
136         cbl->max_callbacks = 8;
137         cbl->n_callbacks = 0;
138
139         return cbl;
140 }
141
142
143 void sc_callback_list_free(SCCallbackList *cbl)
144 {
145         int i;
146
147         if ( cbl == NULL ) return;
148
149         for ( i=0; i<cbl->n_callbacks; i++ ) {
150                 free(cbl->names[i]);
151         }
152
153         free(cbl->names);
154         free(cbl->box_funcs);
155         free(cbl->draw_funcs);
156         free(cbl->vps);
157         free(cbl);
158
159 }
160
161
162 void sc_callback_list_add_callback(SCCallbackList *cbl, const char *name,
163                                    SCCallbackBoxFunc box_func,
164                                    SCCallbackDrawFunc draw_func,
165                                    SCCallbackClickFunc click_func,
166                                    void *vp)
167 {
168         if ( cbl->n_callbacks == cbl->max_callbacks ) {
169
170                 SCCallbackBoxFunc *box_funcs_new;
171                 SCCallbackDrawFunc *draw_funcs_new;
172                 SCCallbackClickFunc *click_funcs_new;
173                 char **names_new;
174                 void **vps_new;
175                 int mcn = cbl->max_callbacks + 8;
176
177                 names_new = realloc(cbl->names, mcn*sizeof(char *));
178                 box_funcs_new = realloc(cbl->box_funcs,
179                                         mcn*sizeof(SCCallbackBoxFunc));
180                 draw_funcs_new = realloc(cbl->draw_funcs,
181                                         mcn*sizeof(SCCallbackDrawFunc));
182                 click_funcs_new = realloc(cbl->click_funcs,
183                                         mcn*sizeof(SCCallbackClickFunc));
184                 vps_new = realloc(cbl->vps, mcn*sizeof(void *));
185
186                 if ( (names_new == NULL) || (box_funcs_new == NULL)
187                   || (vps_new == NULL) || (draw_funcs_new == NULL)
188                   || (click_funcs_new == NULL) ) {
189                         fprintf(stderr, _("Failed to grow callback list\n"));
190                         return;
191                 }
192
193                 cbl->names = names_new;
194                 cbl->box_funcs = box_funcs_new;
195                 cbl->draw_funcs = draw_funcs_new;
196                 cbl->click_funcs = click_funcs_new;
197                 cbl->vps = vps_new;
198                 cbl->max_callbacks = mcn;
199
200         }
201
202         cbl->names[cbl->n_callbacks] = strdup(name);
203         cbl->box_funcs[cbl->n_callbacks] = box_func;
204         cbl->draw_funcs[cbl->n_callbacks] = draw_func;
205         cbl->click_funcs[cbl->n_callbacks] = click_func;
206         cbl->vps[cbl->n_callbacks] = vp;
207         cbl->n_callbacks++;
208 }
209
210
211 void sc_interp_set_callbacks(SCInterpreter *scin, SCCallbackList *cbl)
212 {
213         if ( scin->cbl != NULL ) {
214                 fprintf(stderr, _("WARNING: Interpreter already has a callback "
215                                   "list.\n"));
216         }
217         scin->cbl = cbl;
218 }
219
220
221 static int check_callback(SCInterpreter *scin, SCBlock *bl)
222 {
223         int i;
224         const char *name = sc_block_name(bl);
225         SCCallbackList *cbl = scin->cbl;
226
227         /* No callback list -> easy */
228         if ( cbl == NULL ) return 0;
229
230         /* No name -> definitely not a callback */
231         if ( name == NULL ) return 0;
232
233         for ( i=0; i<cbl->n_callbacks; i++ ) {
234
235                 double w, h;
236                 int r;
237                 void *bvp;
238                 SCBlock *rbl;
239
240                 rbl = bl;
241                 if ( sc_interp_get_macro_real_block(scin) != NULL ) {
242                         bl = sc_interp_get_macro_real_block(scin);
243                 }
244
245                 if ( strcmp(cbl->names[i], name) != 0 ) continue;
246                 r = cbl->box_funcs[i](scin, bl, &w, &h, &bvp, cbl->vps[i]);
247                 if ( r )  {
248                         struct sc_state *st = &scin->state[scin->j];
249                         Paragraph *pnew;
250                         pnew = add_callback_para(sc_interp_get_frame(scin),
251                                                  bl, rbl, w, h,
252                                                  cbl->draw_funcs[i],
253                                                  cbl->click_funcs[i],
254                                                  bvp, cbl->vps[i]);
255                         if ( pnew != NULL ) {
256                                 set_para_spacing(pnew, st->paraspace);
257                         }
258
259                 }
260                 return 1;
261
262         }
263
264         return 0;
265 }
266
267
268 PangoFont *sc_interp_get_font(SCInterpreter *scin)
269 {
270         struct sc_state *st = &scin->state[scin->j];
271         return st->font;
272 }
273
274
275 PangoFontDescription *sc_interp_get_fontdesc(SCInterpreter *scin)
276 {
277         struct sc_state *st = &scin->state[scin->j];
278         return st->fontdesc;
279 }
280
281
282 double *sc_interp_get_fgcol(SCInterpreter *scin)
283 {
284         struct sc_state *st = &scin->state[scin->j];
285         return st->col;
286 }
287
288
289 int sc_interp_get_ascent(SCInterpreter *scin)
290 {
291         struct sc_state *st = &scin->state[scin->j];
292         return st->ascent;
293 }
294
295
296 int sc_interp_get_height(SCInterpreter *scin)
297 {
298         struct sc_state *st = &scin->state[scin->j];
299         return st->height;
300 }
301
302
303 static void set_frame_default_style(struct frame *fr, SCInterpreter *scin)
304 {
305         if ( fr == NULL ) return;
306
307         if ( fr->fontdesc != NULL ) {
308                 pango_font_description_free(fr->fontdesc);
309         }
310         fr->fontdesc = pango_font_description_copy(sc_interp_get_fontdesc(scin));
311         fr->col[0] = sc_interp_get_fgcol(scin)[0];
312         fr->col[1] = sc_interp_get_fgcol(scin)[1];
313         fr->col[2] = sc_interp_get_fgcol(scin)[2];
314         fr->col[3] = sc_interp_get_fgcol(scin)[3];
315 }
316
317
318 static void update_font(SCInterpreter *scin)
319 {
320         PangoFontMetrics *metrics;
321         struct sc_state *st = &scin->state[scin->j];
322
323         if ( scin->pc == NULL ) return;
324
325         st->font = pango_font_map_load_font(pango_context_get_font_map(scin->pc),
326                                             scin->pc, st->fontdesc);
327         if ( st->font == NULL ) {
328                 char *f = pango_font_description_to_string(st->fontdesc);
329                 fprintf(stderr, _("Couldn't load font '%s' (font map %p, pc %p)\n"),
330                         f, pango_context_get_font_map(scin->pc), scin->pc);
331                 g_free(f);
332                 return;
333         }
334
335         /* FIXME: Language for box */
336         metrics = pango_font_get_metrics(st->font, NULL);
337         st->ascent = pango_font_metrics_get_ascent(metrics);
338         st->height = st->ascent + pango_font_metrics_get_descent(metrics);
339         pango_font_metrics_unref(metrics);
340         set_frame_default_style(sc_interp_get_frame(scin), scin);
341 }
342
343
344 static void set_font(SCInterpreter *scin, const char *font_name)
345 {
346         struct sc_state *st = &scin->state[scin->j];
347
348         st->fontdesc = pango_font_description_from_string(font_name);
349         if ( st->fontdesc == NULL ) {
350                 fprintf(stderr, _("Couldn't describe font.\n"));
351                 return;
352         }
353
354         update_font(scin);
355 }
356
357
358 static void copy_top_fontdesc(SCInterpreter *scin)
359 {
360         struct sc_state *st = &scin->state[scin->j];
361
362         /* If this is the first stack frame, don't even check */
363         if ( scin->j == 0 ) return;
364
365         /* If the fontdesc at the top of the stack is the same as the one
366          * below, make a copy because we're about to do something to it (which
367          * should not affect the next level up). */
368         if ( st->fontdesc == scin->state[scin->j-1].fontdesc ) {
369                 st->fontdesc = pango_font_description_copy(st->fontdesc);
370         }
371 }
372
373
374 static void set_fontsize(SCInterpreter *scin, const char *size_str)
375 {
376         struct sc_state *st = &scin->state[scin->j];
377         int size;
378         char *end;
379
380         if ( size_str[0] == '\0' ) return;
381
382         size = strtoul(size_str, &end, 10);
383         if ( end[0] != '\0' ) {
384                 fprintf(stderr, _("Invalid font size '%s'\n"), size_str);
385                 return;
386         }
387
388         copy_top_fontdesc(scin);
389         pango_font_description_set_size(st->fontdesc, size*PANGO_SCALE);
390         update_font(scin);
391 }
392
393
394 static void set_bold(SCInterpreter *scin)
395 {
396         struct sc_state *st = &scin->state[scin->j];
397         copy_top_fontdesc(scin);
398         pango_font_description_set_weight(st->fontdesc, PANGO_WEIGHT_BOLD);
399         update_font(scin);
400 }
401
402
403 static void set_oblique(SCInterpreter *scin)
404 {
405         struct sc_state *st = &scin->state[scin->j];
406         copy_top_fontdesc(scin);
407         pango_font_description_set_style(st->fontdesc, PANGO_STYLE_OBLIQUE);
408         update_font(scin);
409 }
410
411
412 static void set_italic(SCInterpreter *scin)
413 {
414         struct sc_state *st = &scin->state[scin->j];
415         copy_top_fontdesc(scin);
416         pango_font_description_set_style(st->fontdesc, PANGO_STYLE_ITALIC);
417         update_font(scin);
418 }
419
420
421 static void set_alignment(SCInterpreter *scin, PangoAlignment align)
422 {
423         struct sc_state *st = &scin->state[scin->j];
424         st->alignment = align;
425 }
426
427
428 /* This sets the colour for the font at the top of the stack */
429 static void set_colour(SCInterpreter *scin, const char *colour)
430 {
431         GdkRGBA col;
432         struct sc_state *st = &scin->state[scin->j];
433
434         if ( colour == NULL ) {
435                 printf(_("Invalid colour\n"));
436                 st->col[0] = 0.0;
437                 st->col[1] = 0.0;
438                 st->col[2] = 0.0;
439                 st->col[3] = 1.0;
440                 return;
441         }
442
443         gdk_rgba_parse(&col, colour);
444
445         st->col[0] = col.red;
446         st->col[1] = col.green;
447         st->col[2] = col.blue;
448         st->col[3] = col.alpha;
449         set_frame_default_style(sc_interp_get_frame(scin), scin);
450 }
451
452
453 static void set_frame_bgcolour(struct frame *fr, const char *colour)
454 {
455         GdkRGBA col;
456
457         if ( fr == NULL ) return;
458
459         if ( colour == NULL ) {
460                 printf(_("Invalid colour\n"));
461                 fr->bgcol[0] = 0.0;
462                 fr->bgcol[1] = 0.0;
463                 fr->bgcol[2] = 0.0;
464                 fr->bgcol[3] = 1.0;
465                 return;
466         }
467
468         gdk_rgba_parse(&col, colour);
469
470         fr->bgcol[0] = col.red;
471         fr->bgcol[1] = col.green;
472         fr->bgcol[2] = col.blue;
473         fr->bgcol[3] = col.alpha;
474         fr->grad = GRAD_NONE;
475 }
476
477
478 static void set_frame_bggrad(struct frame *fr, const char *options,
479                              GradientType grad)
480 {
481         GdkRGBA col1, col2;
482         char *n2;
483         char *optcopy = strdup(options);
484
485         if ( fr == NULL ) return;
486
487         if ( options == NULL ) {
488                 fprintf(stderr, _("Invalid bg gradient spec '%s'\n"), options);
489                 return;
490         }
491
492         n2 = strchr(optcopy, ',');
493         if ( n2 == NULL ) {
494                 fprintf(stderr, _("Invalid bg gradient spec '%s'\n"), options);
495                 return;
496         }
497
498         n2[0] = '\0';
499
500         gdk_rgba_parse(&col1, optcopy);
501         gdk_rgba_parse(&col2, &n2[1]);
502
503         fr->bgcol[0] = col1.red;
504         fr->bgcol[1] = col1.green;
505         fr->bgcol[2] = col1.blue;
506         fr->bgcol[3] = col1.alpha;
507
508         fr->bgcol2[0] = col2.red;
509         fr->bgcol2[1] = col2.green;
510         fr->bgcol2[2] = col2.blue;
511         fr->bgcol2[3] = col2.alpha;
512
513         fr->grad = grad;
514
515         free(optcopy);
516 }
517
518
519 void sc_interp_save(SCInterpreter *scin)
520 {
521         if ( scin->j+1 == scin->max_state ) {
522
523                 struct sc_state *stack_new;
524
525                 stack_new = realloc(scin->state, sizeof(struct sc_state)
526                                      * (scin->max_state+8));
527                 if ( stack_new == NULL ) {
528                         fprintf(stderr, _("Failed to add to stack.\n"));
529                         return;
530                 }
531
532                 scin->state = stack_new;
533                 scin->max_state += 8;
534
535         }
536
537         /* When n_fonts=0, we leave the first font uninitialised.  This allows
538          * the stack to be "bootstrapped", but requires the first caller to do
539          * set_font and set_colour straight away. */
540         scin->state[scin->j+1] = scin->state[scin->j];
541
542         scin->j++;
543 }
544
545
546 void sc_interp_restore(SCInterpreter *scin)
547 {
548         struct sc_state *st = &scin->state[scin->j];
549
550         if ( scin->j > 0 ) {
551                 if ( st->fontdesc != scin->state[scin->j-1].fontdesc )
552                 {
553                         pango_font_description_free(st->fontdesc);
554                 } /* else the font is the same as the previous one, and we
555                    * don't need to free it just yet */
556         }
557
558         scin->j--;
559 }
560
561
562 struct frame *sc_interp_get_frame(SCInterpreter *scin)
563 {
564         struct sc_state *st = &scin->state[scin->j];
565         return st->fr;
566 }
567
568
569 SCBlock *sc_interp_get_macro_real_block(SCInterpreter *scin)
570 {
571         struct sc_state *st = &scin->state[scin->j];
572         return st->macro_real_block;
573 }
574
575
576 static void set_frame(SCInterpreter *scin, struct frame *fr)
577 {
578         struct sc_state *st = &scin->state[scin->j];
579         st->fr = fr;
580 }
581
582
583 SCInterpreter *sc_interp_new(PangoContext *pc, PangoLanguage *lang,
584                              ImageStore *is, struct frame *top)
585 {
586         SCInterpreter *scin;
587         struct sc_state *st;
588
589         scin = malloc(sizeof(SCInterpreter));
590         if ( scin == NULL ) return NULL;
591
592         scin->state = malloc(8*sizeof(struct sc_state));
593         if ( scin->state == NULL ) {
594                 free(scin);
595                 return NULL;
596         }
597         scin->j = 0;
598         scin->max_state = 8;
599
600         scin->pc = pc;
601         scin->is = is;
602         scin->s_constants = NULL;
603         scin->p_constants = NULL;
604         scin->cbl = NULL;
605
606         st = &scin->state[0];
607         st->n_macros = 0;
608         st->max_macros = 16;
609         st->macros = malloc(16*sizeof(struct macro));
610         if ( st->macros == NULL ) {
611                 free(scin->state);
612                 free(scin);
613                 return NULL;
614         }
615         st->n_templates = 0;
616         st->max_templates = 16;
617         st->templates = malloc(16*sizeof(struct template));
618         if ( st->templates == NULL ) {
619                 free(scin->state);
620                 free(scin);
621                 return NULL;
622         }
623         st->macro_contents = NULL;
624         st->macro_real_block = NULL;
625         st->macro_editable = 0;
626         st->fr = NULL;
627         st->paraspace[0] = 0.0;
628         st->paraspace[1] = 0.0;
629         st->paraspace[2] = 0.0;
630         st->paraspace[3] = 0.0;
631         st->fontdesc = NULL;
632         st->have_size = 0;
633         st->col[0] = 0.0;
634         st->col[1] = 0.0;
635         st->col[2] = 0.0;
636         st->col[3] = 1.0;
637         st->alignment = PANGO_ALIGN_LEFT;
638
639         scin->lang = lang;
640
641         /* The "ultimate" default font */
642         if ( scin->pc != NULL ) {
643                 set_font(scin, "Cantarell Regular 14");
644                 set_colour(scin, "#000000");
645                 set_frame(scin, top);
646         }
647
648         return scin;
649 }
650
651
652 void sc_interp_destroy(SCInterpreter *scin)
653 {
654         /* FIXME: Free all templates and macros */
655
656         /* Empty the stack */
657         while ( scin->j > 0 ) {
658                 sc_interp_restore(scin);
659         }
660
661         if ( scin->state[0].fontdesc != NULL ) {
662                 pango_font_description_free(scin->state[0].fontdesc);
663         }
664
665         free(scin->state);
666         free(scin);
667 }
668
669
670 static int parse_double(const char *a, float v[2])
671 {
672         int nn;
673
674         nn = sscanf(a, "%fx%f", &v[0], &v[1]);
675         if ( nn != 2 ) {
676                 fprintf(stderr, _("Invalid size '%s'\n"), a);
677                 return 1;
678         }
679
680         return 0;
681 }
682
683
684 static int parse_tuple(const char *a, float v[4])
685 {
686         int nn;
687
688         nn = sscanf(a, "%f,%f,%f,%f", &v[0], &v[1], &v[2], &v[3]);
689         if ( nn != 4 ) {
690                 fprintf(stderr, _("Invalid tuple '%s'\n"), a);
691                 return 1;
692         }
693
694         return 0;
695 }
696
697
698 static void set_padding(struct frame *fr, const char *opts)
699 {
700         float p[4];
701
702         if ( parse_tuple(opts, p) ) return;
703
704         fr->pad_l = p[0];
705         fr->pad_r = p[1];
706         fr->pad_t = p[2];
707         fr->pad_b = p[3];
708 }
709
710
711 static void set_paraspace(SCInterpreter *scin, const char *opts)
712 {
713         float p[4];
714         struct sc_state *st = &scin->state[scin->j];
715
716         if ( parse_tuple(opts, p) ) return;
717
718         st->paraspace[0] = p[0];
719         st->paraspace[1] = p[1];
720         st->paraspace[2] = p[2];
721         st->paraspace[3] = p[3];
722
723         set_para_spacing(last_para(sc_interp_get_frame(scin)), p);
724 }
725
726
727 static void set_slide_size(SCInterpreter *scin, const char *opts)
728 {
729         float p[2];
730         struct sc_state *st = &scin->state[scin->j];
731
732         if ( parse_double(opts, p) ) return;
733
734         st->slide_width = p[0];
735         st->slide_height = p[1];
736         st->have_size = 1;
737 }
738
739
740 void update_geom(struct frame *fr)
741 {
742         char geom[256];
743         snprintf(geom, 255, "%.1fux%.1fu+%.1f+%.1f",
744                  fr->w, fr->h, fr->x, fr->y);
745
746         /* FIXME: What if there are other options? */
747         sc_block_set_options(fr->scblocks, strdup(geom));
748 }
749
750
751 static LengthUnits get_units(const char *t)
752 {
753         size_t len = strlen(t);
754
755         if ( t[len-1] == 'f' ) return UNITS_FRAC;
756         if ( t[len-1] == 'u' ) return UNITS_SLIDE;
757
758         fprintf(stderr, _("Invalid units in '%s'\n"), t);
759         return UNITS_SLIDE;
760 }
761
762
763 static int parse_dims(const char *opt, struct frame *parent,
764                       double *wp, double *hp, double *xp, double *yp)
765 {
766         char *w;
767         char *h;
768         char *x;
769         char *y;
770         char *check;
771         LengthUnits h_units, w_units;
772
773         /* Looks like a dimension/position thing */
774         w = strdup(opt);
775         h = index(w, 'x');
776         h[0] = '\0';  h++;
777
778         x = index(h, '+');
779         if ( x == NULL ) goto invalid;
780         x[0] = '\0';  x++;
781
782         y = index(x, '+');
783         if ( x == NULL ) goto invalid;
784         y[0] = '\0';  y++;
785
786         *wp = strtod(w, &check);
787         if ( check == w ) goto invalid;
788         w_units = get_units(w);
789         if ( w_units == UNITS_FRAC ) {
790                 if ( parent != NULL ) {
791                         double pw = parent->w;
792                         pw -= parent->pad_l;
793                         pw -= parent->pad_r;
794                         *wp = pw * *wp;
795                 } else {
796                         *wp = -1.0;
797                 }
798
799         }
800
801         *hp = strtod(h, &check);
802         if ( check == h ) goto invalid;
803         h_units = get_units(h);
804         if ( h_units == UNITS_FRAC ) {
805                 if ( parent != NULL ) {
806                         double ph = parent->h;
807                         ph -= parent->pad_t;
808                         ph -= parent->pad_b;
809                         *hp = ph * *hp;
810                 } else {
811                         *hp = -1.0;
812                 }
813         }
814
815         *xp= strtod(x, &check);
816         if ( check == x ) goto invalid;
817         *yp = strtod(y, &check);
818         if ( check == y ) goto invalid;
819
820         return 0;
821
822 invalid:
823         fprintf(stderr, _("Invalid dimensions '%s'\n"), opt);
824         return 1;
825 }
826
827
828 static int parse_frame_option(const char *opt, struct frame *fr,
829                               struct frame *parent)
830 {
831         if ( (index(opt, 'x') != NULL) && (index(opt, '+') != NULL)
832           && (index(opt, '+') != rindex(opt, '+')) ) {
833                 return parse_dims(opt, parent, &fr->w, &fr->h, &fr->x, &fr->y);
834         }
835
836         fprintf(stderr, _("Unrecognised frame option '%s'\n"), opt);
837
838         return 1;
839 }
840
841
842 static int parse_frame_options(struct frame *fr, struct frame *parent,
843                                const char *opth)
844 {
845         int i;
846         size_t len;
847         size_t start;
848         char *opt;
849
850         if ( opth == NULL ) return 1;
851
852         opt = strdup(opth);
853
854         len = strlen(opt);
855         start = 0;
856
857         for ( i=0; i<len; i++ ) {
858
859                 /* FIXME: comma might be escaped or quoted */
860                 if ( opt[i] == ',' ) {
861                         opt[i] = '\0';
862                         if ( parse_frame_option(opt+start, fr, parent) ) {
863                                 return 1;
864                         }
865                         start = i+1;
866                 }
867
868         }
869
870         if ( start != len ) {
871                 if ( parse_frame_option(opt+start, fr, parent) ) return 1;
872         }
873
874         free(opt);
875
876         return 0;
877 }
878
879
880 static int parse_image_option(const char *opt, struct frame *parent,
881                               double *wp, double *hp, char **filenamep)
882 {
883         if ( (index(opt, 'x') != NULL) && (index(opt, '+') != NULL)
884           && (index(opt, '+') != rindex(opt, '+')) ) {
885                 double dum;
886                 return parse_dims(opt, NULL, wp, hp, &dum, &dum);
887         }
888
889         if ( strncmp(opt, "filename=\"", 10) == 0 ) {
890                 char *fn;
891                 fn = strdup(opt+10);
892                 if ( fn[strlen(fn)-1] != '\"' ) {
893                         fprintf(stderr, _("Unterminated filename?\n"));
894                         free(fn);
895                         return 1;
896                 }
897                 fn[strlen(fn)-1] = '\0';
898                 *filenamep = fn;
899                 return 0;
900         }
901
902         fprintf(stderr, _("Unrecognised image option '%s'\n"), opt);
903
904         return 1;
905 }
906
907
908 static int parse_image_options(const char *opth, struct frame *parent,
909                                double *wp, double *hp, char **filenamep)
910 {
911         int i;
912         size_t len;
913         size_t start;
914         char *opt;
915
916         if ( opth == NULL ) return 1;
917
918         opt = strdup(opth);
919
920         len = strlen(opt);
921         start = 0;
922
923         for ( i=0; i<len; i++ ) {
924
925                 /* FIXME: comma might be escaped or quoted */
926                 if ( opt[i] == ',' ) {
927                         opt[i] = '\0';
928                         if ( parse_image_option(opt+start, parent,
929                                                 wp, hp, filenamep) ) return 1;
930                         start = i+1;
931                 }
932
933         }
934
935         if ( start != len ) {
936                 if ( parse_image_option(opt+start, parent,
937                                         wp, hp, filenamep) ) return 1;
938         }
939
940         free(opt);
941
942         return 0;
943 }
944
945
946 static void maybe_recurse_before(SCInterpreter *scin, SCBlock *child)
947 {
948         if ( child == NULL ) return;
949
950         sc_interp_save(scin);
951 }
952
953
954 static void maybe_recurse_after(SCInterpreter *scin, SCBlock *child)
955 {
956         if ( child == NULL ) return;
957
958         sc_interp_add_blocks(scin, child);
959         sc_interp_restore(scin);
960 }
961
962
963 static int in_macro(SCInterpreter *scin)
964 {
965         struct sc_state *st = &scin->state[scin->j];
966         if ( st->macro_contents == NULL ) return 0;
967         return 1;
968 }
969
970
971 static void add_newpara(struct frame *fr, SCBlock *bl, SCBlock *mrb)
972 {
973         Paragraph *last_para;
974
975         if ( fr->paras == NULL ) return;
976         last_para = fr->paras[fr->n_paras-1];
977
978         set_newline_at_end(last_para, bl);
979
980         /* The block after the \newpara will always be the first one of the
981          * next paragraph, by definition, even if it's \f or another \newpara */
982         create_paragraph(fr, sc_block_next(mrb), sc_block_next(bl));
983 }
984
985
986 /* Add the SCBlock to the text in 'frame', at the end */
987 static int add_text(struct frame *fr, PangoContext *pc, SCBlock *bl,
988                     PangoLanguage *lang, int editable, SCInterpreter *scin)
989 {
990         const char *text = sc_block_contents(bl);
991         PangoFontDescription *fontdesc;
992         double *col;
993         struct sc_state *st = &scin->state[scin->j];
994         SCBlock *rbl;
995         Paragraph *para;
996
997         /* Empty block? */
998         if ( text == NULL ) return 1;
999
1000         fontdesc = sc_interp_get_fontdesc(scin);
1001         col = sc_interp_get_fgcol(scin);
1002
1003         rbl = bl;
1004         if ( st->macro_real_block != NULL ) {
1005                 bl = st->macro_real_block;
1006         }
1007
1008         para = last_para(fr);
1009         if ( (para == NULL) || (para_type(para) != PARA_TYPE_TEXT) ) {
1010                 /* Last paragraph is not text.
1011                  *  or: no paragraphs yet.
1012                  *    Either way: Create the first one */
1013                 para = create_paragraph(fr, bl, rbl);
1014         }
1015
1016         set_para_alignment(para, st->alignment);
1017         add_run(para, bl, rbl, fontdesc, col);
1018         set_para_spacing(para, st->paraspace);
1019
1020         return 0;
1021 }
1022
1023
1024 static int check_outputs(SCBlock *bl, SCInterpreter *scin)
1025 {
1026         const char *name = sc_block_name(bl);
1027         const char *options = sc_block_options(bl);
1028         SCBlock *child = sc_block_child(bl);
1029         struct sc_state *st = &scin->state[scin->j];
1030
1031         if ( name == NULL ) {
1032                 add_text(sc_interp_get_frame(scin),
1033                          scin->pc, bl, scin->lang, 1, scin);
1034
1035         } else if ( strcmp(name, "image")==0 ) {
1036                 double w, h;
1037                 char *filename;
1038                 if ( parse_image_options(options, sc_interp_get_frame(scin),
1039                                          &w, &h, &filename) == 0 )
1040                 {
1041                         SCBlock *rbl = bl;
1042                         if ( st->macro_real_block != NULL ) {
1043                                 bl = st->macro_real_block;
1044                         }
1045                         add_image_para(sc_interp_get_frame(scin), bl, rbl,
1046                                        filename, scin->is, w, h, 1);
1047                         free(filename);
1048                 } else {
1049                         fprintf(stderr, _("Invalid image options '%s'\n"),
1050                                 options);
1051                 }
1052
1053         } else if ( strcmp(name, "f")==0 ) {
1054
1055                 struct frame *fr;
1056
1057                 fr = add_subframe(sc_interp_get_frame(scin));
1058                 fr->scblocks = bl;
1059                 if ( in_macro(scin) ) {
1060                         fr->resizable = 0;
1061                 } else {
1062                         fr->resizable = 1;
1063                 }
1064                 if ( fr == NULL ) {
1065                         fprintf(stderr, _("Failed to add frame.\n"));
1066                         return 1;
1067                 }
1068
1069                 set_frame_default_style(fr, scin);
1070
1071                 parse_frame_options(fr, sc_interp_get_frame(scin), options);
1072
1073                 maybe_recurse_before(scin, child);
1074                 set_frame(scin, fr);
1075                 maybe_recurse_after(scin, child);
1076
1077         } else if ( strcmp(name, "newpara")==0 ) {
1078
1079                 struct frame *fr = sc_interp_get_frame(scin);
1080                 SCBlock *rbl = bl;
1081                 if ( st->macro_real_block != NULL ) {
1082                         bl = st->macro_real_block;
1083                 }
1084                 add_newpara(fr, bl, rbl);
1085
1086         } else {
1087                 return 0;
1088         }
1089
1090         return 1;  /* handled */
1091 }
1092
1093
1094 static int check_macro(const char *name, SCInterpreter *scin)
1095 {
1096         int i;
1097         struct sc_state *st = &scin->state[scin->j];
1098
1099         if ( name == NULL ) return 0;
1100
1101         for ( i=0; i<st->n_macros; i++ ) {
1102                 if ( strcmp(st->macros[i].name, name) == 0 ) {
1103                         return 1;
1104                 }
1105         }
1106
1107         return 0;
1108 }
1109
1110
1111 static void exec_macro(SCBlock *bl, SCInterpreter *scin, SCBlock *child)
1112 {
1113         struct sc_state *st = &scin->state[scin->j];
1114         int i;
1115         const char *name;
1116
1117         name = sc_block_name(bl);
1118         for ( i=0; i<st->n_macros; i++ ) {
1119                 if ( strcmp(st->macros[i].name, name) == 0 ) {
1120                         sc_interp_save(scin);
1121                         scin->state[scin->j].macro_real_block = bl;
1122                         scin->state[scin->j].macro_contents = child;
1123                         sc_interp_add_blocks(scin, scin->state[scin->j].macros[i].bl);
1124                         sc_interp_restore(scin);
1125                         break; /* Stop iterating, because "st" is now invalid */
1126                 }
1127         }
1128 }
1129
1130
1131 static void run_macro_contents(SCInterpreter *scin)
1132 {
1133         struct sc_state *st = &scin->state[scin->j];
1134         SCBlock *contents = st->macro_contents;
1135
1136         sc_interp_save(scin);
1137         scin->state[scin->j].macro_real_block = NULL;
1138         sc_interp_add_blocks(scin, contents);
1139         sc_interp_restore(scin);
1140 }
1141
1142
1143 static void run_editable(SCInterpreter *scin, SCBlock *contents)
1144 {
1145         //struct sc_state *st = &scin->state[scin->j];
1146
1147         sc_interp_save(scin);
1148         //scin->state[scin->j].macro_real_block = NULL;
1149         scin->state[scin->j].macro_editable = 1;
1150         sc_interp_add_blocks(scin, contents);
1151         sc_interp_restore(scin);
1152 }
1153
1154
1155 int sc_interp_add_blocks(SCInterpreter *scin, SCBlock *bl)
1156 {
1157         //printf("Running this --------->\n");
1158         //show_sc_blocks(bl);
1159         //printf("<------------\n");
1160
1161         while ( bl != NULL ) {
1162
1163                 const char *name = sc_block_name(bl);
1164                 const char *options = sc_block_options(bl);
1165                 SCBlock *child = sc_block_child(bl);
1166
1167                 if ( check_macro(name, scin) ) {
1168                         sc_interp_save(scin);
1169                         exec_macro(bl, scin, child);
1170                         sc_interp_restore(scin);
1171
1172                 } else if ( check_callback(scin, bl) ) {
1173                         /* Handled in check_callback, don't do anything else */
1174
1175                 } else if ((sc_interp_get_frame(scin) != NULL)
1176                   && check_outputs(bl, scin) ) {
1177                         /* Block handled as output thing */
1178
1179                 } else if ( name == NULL ) {
1180                         /* Dummy to ensure name != NULL below */
1181
1182                 } else if ( strcmp(name, "font") == 0 ) {
1183                         maybe_recurse_before(scin, child);
1184                         set_font(scin, options);
1185                         maybe_recurse_after(scin, child);
1186
1187                 } else if ( strcmp(name, "fontsize") == 0 ) {
1188                         maybe_recurse_before(scin, child);
1189                         set_fontsize(scin, options);
1190                         maybe_recurse_after(scin, child);
1191
1192                 } else if ( strcmp(name, "bold") == 0 ) {
1193                         maybe_recurse_before(scin, child);
1194                         set_bold(scin);
1195                         maybe_recurse_after(scin, child);
1196
1197                 } else if ( strcmp(name, "oblique") == 0 ) {
1198                         maybe_recurse_before(scin, child);
1199                         set_oblique(scin);
1200                         maybe_recurse_after(scin, child);
1201
1202                 } else if ( strcmp(name, "italic") == 0 ) {
1203                         maybe_recurse_before(scin, child);
1204                         set_italic(scin);
1205                         maybe_recurse_after(scin, child);
1206
1207                 } else if ( strcmp(name, "lalign") == 0 ) {
1208                         maybe_recurse_before(scin, child);
1209                         set_alignment(scin, PANGO_ALIGN_LEFT);
1210                         maybe_recurse_after(scin, child);
1211
1212                 } else if ( strcmp(name, "ralign") == 0 ) {
1213                         maybe_recurse_before(scin, child);
1214                         set_alignment(scin, PANGO_ALIGN_RIGHT);
1215                         maybe_recurse_after(scin, child);
1216
1217                 } else if ( strcmp(name, "center") == 0 ) {
1218                         maybe_recurse_before(scin, child);
1219                         set_alignment(scin, PANGO_ALIGN_CENTER);
1220                         maybe_recurse_after(scin, child);
1221
1222                 } else if ( strcmp(name, "fgcol") == 0 ) {
1223                         maybe_recurse_before(scin, child);
1224                         set_colour(scin, options);
1225                         maybe_recurse_after(scin, child);
1226
1227                 } else if ( strcmp(name, "contents") == 0 ) {
1228                         run_macro_contents(scin);
1229
1230                 } else if ( strcmp(name, "editable") == 0 ) {
1231                         run_editable(scin, child);
1232
1233                 } else if ( strcmp(name, "pad") == 0 ) {
1234                         maybe_recurse_before(scin, child);
1235                         set_padding(sc_interp_get_frame(scin), options);
1236                         maybe_recurse_after(scin, child);
1237
1238                 } else if ( strcmp(name, "bgcol") == 0 ) {
1239                         maybe_recurse_before(scin, child);
1240                         set_frame_bgcolour(sc_interp_get_frame(scin), options);
1241                         maybe_recurse_after(scin, child);
1242
1243                 } else if ( strcmp(name, "bggradh") == 0 ) {
1244                         set_frame_bggrad(sc_interp_get_frame(scin), options,
1245                                          GRAD_HORIZ);
1246
1247                 } else if ( strcmp(name, "bggradv") == 0 ) {
1248                         set_frame_bggrad(sc_interp_get_frame(scin), options,
1249                                          GRAD_VERT);
1250
1251                 } else if ( strcmp(name, "paraspace") == 0 ) {
1252                         maybe_recurse_before(scin, child);
1253                         set_paraspace(scin, options);
1254                         maybe_recurse_after(scin, child);
1255
1256                 } else {
1257
1258                         //fprintf(stderr, "Don't know what to do with this:\n");
1259                         //show_sc_block(bl, "");
1260
1261                 }
1262
1263                 bl = sc_block_next(bl);
1264
1265         }
1266
1267         return 0;
1268 }
1269
1270
1271 static int try_add_macro(SCInterpreter *scin, const char *options, SCBlock *bl)
1272 {
1273         struct sc_state *st = &scin->state[scin->j];
1274         char *nn;
1275         char *comma;
1276         int i;
1277
1278         nn = strdup(options);
1279         comma = strchr(nn, ',');
1280         if ( comma != NULL ) {
1281                 comma[0] = '\0';
1282         }
1283
1284         for ( i=0; i<st->n_macros; i++ ) {
1285                 if ( strcmp(st->macros[i].name, nn) == 0 ) {
1286                         st->macros[i].name = nn;
1287                         st->macros[i].bl = bl;
1288                         st->macros[i].prev = NULL;  /* FIXME: Stacking */
1289                         return 0;
1290                 }
1291         }
1292
1293         if ( st->max_macros == st->n_macros ) {
1294
1295                 struct macro *macros_new;
1296
1297                 macros_new = realloc(st->macros, sizeof(struct macro)
1298                                      * (st->max_macros+16));
1299                 if ( macros_new == NULL ) {
1300                         fprintf(stderr, _("Failed to add macro.\n"));
1301                         return 1;
1302                 }
1303
1304                 st->macros = macros_new;
1305                 st->max_macros += 16;
1306
1307         }
1308
1309         i = st->n_macros++;
1310
1311         st->macros[i].name = nn;
1312         st->macros[i].bl = bl;
1313         st->macros[i].prev = NULL;  /* FIXME: Stacking */
1314
1315         return 0;
1316 }
1317
1318
1319 static int try_add_template(SCInterpreter *scin, const char *options, SCBlock *bl)
1320 {
1321         struct sc_state *st = &scin->state[scin->j];
1322         char *nn;
1323         char *comma;
1324         int i;
1325
1326         nn = strdup(options);
1327         comma = strchr(nn, ',');
1328         if ( comma != NULL ) {
1329                 comma[0] = '\0';
1330         }
1331
1332         for ( i=0; i<st->n_templates; i++ ) {
1333                 if ( strcmp(st->templates[i].name, nn) == 0 ) {
1334                         fprintf(stderr, _("Duplicate template '%s'\n"), nn);
1335                         return 0;
1336                 }
1337         }
1338
1339         if ( st->max_templates == st->n_templates ) {
1340
1341                 struct template *templates_new;
1342
1343                 templates_new = realloc(st->templates, sizeof(struct template)
1344                                      * (st->max_templates+16));
1345                 if ( templates_new == NULL ) {
1346                         fprintf(stderr, _("Failed to add templates\n"));
1347                         return 1;
1348                 }
1349
1350                 st->templates = templates_new;
1351                 st->max_templates += 16;
1352
1353         }
1354
1355         i = st->n_templates++;
1356
1357         st->templates[i].name = nn;
1358         st->templates[i].bl = bl;
1359
1360         return 0;
1361 }
1362
1363 void add_macro(SCInterpreter *scin, const char *mname, const char *contents)
1364 {
1365         SCBlock *bl = sc_parse(contents);
1366         try_add_macro(scin, mname, bl);
1367 }
1368
1369
1370 void sc_interp_run_stylesheet(SCInterpreter *scin, SCBlock *bl)
1371 {
1372         if ( bl == NULL ) return;
1373
1374         if ( strcmp(sc_block_name(bl), "stylesheet") != 0 ) {
1375                 fprintf(stderr, _("Style sheet isn't a style sheet.\n"));
1376                 return;
1377         }
1378
1379         bl = sc_block_child(bl);
1380
1381         while ( bl != NULL ) {
1382
1383                 const char *name = sc_block_name(bl);
1384                 const char *options = sc_block_options(bl);
1385
1386                 if ( name == NULL ) {
1387
1388                         /* Do nothing */
1389
1390                 } else if ( strcmp(name, "ss") == 0 ) {
1391                         try_add_macro(scin, options, sc_block_child(bl));
1392
1393                 } else if ( strcmp(name, "template") == 0 ) {
1394                         try_add_template(scin, options, sc_block_child(bl));
1395
1396                 } else if ( strcmp(name, "font") == 0 ) {
1397                         set_font(scin, options);
1398
1399                 } else if ( strcmp(name, "fgcol") == 0 ) {
1400                         set_colour(scin, options);
1401
1402                 } else if ( strcmp(name, "bgcol") == 0 ) {
1403                         set_frame_bgcolour(sc_interp_get_frame(scin), options);
1404
1405                 } else if ( strcmp(name, "bggradh") == 0 ) {
1406                         set_frame_bggrad(sc_interp_get_frame(scin), options,
1407                                          GRAD_HORIZ);
1408
1409                 } else if ( strcmp(name, "bggradv") == 0 ) {
1410                         set_frame_bggrad(sc_interp_get_frame(scin), options,
1411                                          GRAD_VERT);
1412
1413                 } else if ( strcmp(name, "paraspace") == 0 ) {
1414                         set_paraspace(scin, options);
1415
1416                 } else if ( strcmp(name, "slidesize") == 0 ) {
1417                         set_slide_size(scin, options);
1418
1419                 }
1420
1421                 bl = sc_block_next(bl);
1422
1423         }
1424 }
1425
1426
1427 int sc_interp_get_slide_size(SCInterpreter *scin, double *w, double *h)
1428 {
1429         if ( !scin->state->have_size ) return 1;
1430         *w = scin->state->slide_width;
1431         *h = scin->state->slide_height;
1432         return 0;
1433 }
1434
1435
1436 struct template_id *sc_interp_get_templates(SCInterpreter *scin, int *np)
1437 {
1438         struct template_id *list;
1439         int i;
1440
1441         list = malloc(sizeof(struct template_id)*scin->state->n_templates);
1442         if ( list == NULL ) return NULL;
1443
1444         for ( i=0; i<scin->state->n_templates; i++ ) {
1445                 list[i].name = strdup(scin->state->templates[i].name);
1446                 list[i].friendlyname = strdup(scin->state->templates[i].name);
1447                 list[i].scblock = sc_block_copy(scin->state->templates[i].bl);
1448         }
1449
1450         *np = scin->state->n_templates;
1451         return list;
1452 }