aboutsummaryrefslogtreecommitdiff
path: root/src-old/sc_interp.c
diff options
context:
space:
mode:
authorThomas White <taw@bitwiz.me.uk>2019-02-19 18:17:56 +0100
committerThomas White <taw@bitwiz.me.uk>2019-02-19 18:17:56 +0100
commit24c20239779d0ec616adde651c594c7bf08d58c7 (patch)
treec1900bda6b2c5f58a66d4b8d89f4a946a24465dc /src-old/sc_interp.c
parent9fbe4d66565e6ab11c5022bcfe5ed0ddfb91dc7e (diff)
WIP
Diffstat (limited to 'src-old/sc_interp.c')
-rw-r--r--src-old/sc_interp.c1148
1 files changed, 1148 insertions, 0 deletions
diff --git a/src-old/sc_interp.c b/src-old/sc_interp.c
new file mode 100644
index 0000000..07f09a5
--- /dev/null
+++ b/src-old/sc_interp.c
@@ -0,0 +1,1148 @@
+/*
+ * sc_interp.c
+ *
+ * Copyright © 2014-2018 Thomas White <taw@bitwiz.org.uk>
+ *
+ * This file is part of Colloquium.
+ *
+ * Colloquium is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pango/pangocairo.h>
+#include <gdk/gdk.h>
+
+#include "imagestore.h"
+#include "sc_parse.h"
+#include "sc_interp.h"
+#include "presentation.h"
+#include "utils.h"
+
+
+struct sc_state
+{
+ PangoFontDescription *fontdesc;
+ PangoFont *font;
+ PangoAlignment alignment;
+ double col[4];
+ int ascent;
+ int height;
+ float paraspace[4];
+ char *constants[NUM_SC_CONSTANTS];
+
+ struct frame *fr; /* The current frame */
+};
+
+struct _scinterp
+{
+ PangoContext *pc;
+ PangoLanguage *lang;
+ ImageStore *is;
+
+ struct slide_constants *s_constants;
+ struct presentation_constants *p_constants;
+
+ struct sc_state *state;
+ int j; /* Index of the current state */
+ int max_state;
+
+ SCCallbackList *cbl;
+};
+
+struct _sccallbacklist
+{
+ int n_callbacks;
+ int max_callbacks;
+ char **names;
+ SCCallbackBoxFunc *box_funcs;
+ SCCallbackDrawFunc *draw_funcs;
+ SCCallbackClickFunc *click_funcs;
+ void **vps;
+};
+
+
+static int sc_interp_add_blocks(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss)
+{
+ while ( bl != NULL ) {
+ if ( sc_interp_add_block(scin, bl, ss) ) return 1;
+ bl = sc_block_next(bl);
+ }
+
+ return 0;
+}
+
+
+SCCallbackList *sc_callback_list_new()
+{
+ SCCallbackList *cbl;
+
+ cbl = malloc(sizeof(struct _sccallbacklist));
+ if ( cbl == NULL ) return NULL;
+
+ cbl->names = calloc(8, sizeof(char *));
+ if ( cbl->names == NULL ) {
+ free(cbl);
+ return NULL;
+ }
+
+ cbl->box_funcs = calloc(8, sizeof(cbl->box_funcs[0]));
+ if ( cbl->box_funcs == NULL ) {
+ free(cbl->names);
+ free(cbl);
+ return NULL;
+ }
+
+ cbl->draw_funcs = calloc(8, sizeof(cbl->draw_funcs[0]));
+ if ( cbl->draw_funcs == NULL ) {
+ free(cbl->box_funcs);
+ free(cbl->names);
+ free(cbl);
+ return NULL;
+ }
+
+ cbl->click_funcs = calloc(8, sizeof(cbl->click_funcs[0]));
+ if ( cbl->click_funcs == NULL ) {
+ free(cbl->draw_funcs);
+ free(cbl->box_funcs);
+ free(cbl->names);
+ free(cbl);
+ return NULL;
+ }
+
+ cbl->vps = calloc(8, sizeof(cbl->vps[0]));
+ if ( cbl->vps == NULL ) {
+ free(cbl->click_funcs);
+ free(cbl->draw_funcs);
+ free(cbl->box_funcs);
+ free(cbl->names);
+ free(cbl);
+ return NULL;
+ }
+
+ cbl->max_callbacks = 8;
+ cbl->n_callbacks = 0;
+
+ return cbl;
+}
+
+
+void sc_callback_list_free(SCCallbackList *cbl)
+{
+ int i;
+
+ if ( cbl == NULL ) return;
+
+ for ( i=0; i<cbl->n_callbacks; i++ ) {
+ free(cbl->names[i]);
+ }
+
+ free(cbl->names);
+ free(cbl->box_funcs);
+ free(cbl->draw_funcs);
+ free(cbl->vps);
+ free(cbl);
+
+}
+
+
+void sc_callback_list_add_callback(SCCallbackList *cbl, const char *name,
+ SCCallbackBoxFunc box_func,
+ SCCallbackDrawFunc draw_func,
+ SCCallbackClickFunc click_func,
+ void *vp)
+{
+ if ( cbl->n_callbacks == cbl->max_callbacks ) {
+
+ SCCallbackBoxFunc *box_funcs_new;
+ SCCallbackDrawFunc *draw_funcs_new;
+ SCCallbackClickFunc *click_funcs_new;
+ char **names_new;
+ void **vps_new;
+ int mcn = cbl->max_callbacks + 8;
+
+ names_new = realloc(cbl->names, mcn*sizeof(char *));
+ box_funcs_new = realloc(cbl->box_funcs,
+ mcn*sizeof(SCCallbackBoxFunc));
+ draw_funcs_new = realloc(cbl->draw_funcs,
+ mcn*sizeof(SCCallbackDrawFunc));
+ click_funcs_new = realloc(cbl->click_funcs,
+ mcn*sizeof(SCCallbackClickFunc));
+ vps_new = realloc(cbl->vps, mcn*sizeof(void *));
+
+ if ( (names_new == NULL) || (box_funcs_new == NULL)
+ || (vps_new == NULL) || (draw_funcs_new == NULL)
+ || (click_funcs_new == NULL) ) {
+ fprintf(stderr, "Failed to grow callback list\n");
+ return;
+ }
+
+ cbl->names = names_new;
+ cbl->box_funcs = box_funcs_new;
+ cbl->draw_funcs = draw_funcs_new;
+ cbl->click_funcs = click_funcs_new;
+ cbl->vps = vps_new;
+ cbl->max_callbacks = mcn;
+
+ }
+
+ cbl->names[cbl->n_callbacks] = strdup(name);
+ cbl->box_funcs[cbl->n_callbacks] = box_func;
+ cbl->draw_funcs[cbl->n_callbacks] = draw_func;
+ cbl->click_funcs[cbl->n_callbacks] = click_func;
+ cbl->vps[cbl->n_callbacks] = vp;
+ cbl->n_callbacks++;
+}
+
+
+void sc_interp_set_callbacks(SCInterpreter *scin, SCCallbackList *cbl)
+{
+ if ( scin->cbl != NULL ) {
+ fprintf(stderr, "WARNING: Interpreter already has a callback "
+ "list.\n");
+ }
+ scin->cbl = cbl;
+}
+
+
+static int check_callback(SCInterpreter *scin, SCBlock *bl)
+{
+ int i;
+ const char *name = sc_block_name(bl);
+ SCCallbackList *cbl = scin->cbl;
+
+ /* No callback list -> easy */
+ if ( cbl == NULL ) return 0;
+
+ /* No name -> definitely not a callback */
+ if ( name == NULL ) return 0;
+
+ for ( i=0; i<cbl->n_callbacks; i++ ) {
+
+ double w, h;
+ int r;
+ void *bvp;
+
+ if ( strcmp(cbl->names[i], name) != 0 ) continue;
+ r = cbl->box_funcs[i](scin, bl, &w, &h, &bvp, cbl->vps[i]);
+ if ( r ) {
+ struct sc_state *st = &scin->state[scin->j];
+ Paragraph *pnew;
+ pnew = add_callback_para(sc_interp_get_frame(scin),
+ bl, w, h,
+ cbl->draw_funcs[i],
+ cbl->click_funcs[i],
+ bvp, cbl->vps[i]);
+ if ( pnew != NULL ) {
+ set_para_spacing(pnew, st->paraspace);
+ }
+
+ }
+ return 1;
+
+ }
+
+ return 0;
+}
+
+
+PangoFontDescription *sc_interp_get_fontdesc(SCInterpreter *scin)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ return st->fontdesc;
+}
+
+
+double *sc_interp_get_fgcol(SCInterpreter *scin)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ return st->col;
+}
+
+
+static void set_frame_default_style(struct frame *fr, SCInterpreter *scin)
+{
+ if ( fr == NULL ) return;
+
+ if ( fr->fontdesc != NULL ) {
+ pango_font_description_free(fr->fontdesc);
+ }
+ fr->fontdesc = pango_font_description_copy(sc_interp_get_fontdesc(scin));
+ fr->col[0] = sc_interp_get_fgcol(scin)[0];
+ fr->col[1] = sc_interp_get_fgcol(scin)[1];
+ fr->col[2] = sc_interp_get_fgcol(scin)[2];
+ fr->col[3] = sc_interp_get_fgcol(scin)[3];
+}
+
+
+static void update_font(SCInterpreter *scin)
+{
+ PangoFontMetrics *metrics;
+ struct sc_state *st = &scin->state[scin->j];
+
+ if ( scin->pc == NULL ) return;
+
+ st->font = pango_font_map_load_font(pango_context_get_font_map(scin->pc),
+ scin->pc, st->fontdesc);
+ if ( st->font == NULL ) {
+ char *f = pango_font_description_to_string(st->fontdesc);
+ fprintf(stderr, "Couldn't load font '%s' (font map %p, pc %p)\n",
+ f, pango_context_get_font_map(scin->pc), scin->pc);
+ g_free(f);
+ return;
+ }
+
+ /* FIXME: Language for box */
+ metrics = pango_font_get_metrics(st->font, NULL);
+ st->ascent = pango_font_metrics_get_ascent(metrics);
+ st->height = st->ascent + pango_font_metrics_get_descent(metrics);
+ pango_font_metrics_unref(metrics);
+ set_frame_default_style(sc_interp_get_frame(scin), scin);
+}
+
+
+static void set_font(SCInterpreter *scin, const char *font_name)
+{
+ struct sc_state *st = &scin->state[scin->j];
+
+ st->fontdesc = pango_font_description_from_string(font_name);
+ if ( st->fontdesc == NULL ) {
+ fprintf(stderr, "Couldn't describe font.\n");
+ return;
+ }
+
+ update_font(scin);
+}
+
+
+static void copy_top_fontdesc(SCInterpreter *scin)
+{
+ struct sc_state *st = &scin->state[scin->j];
+
+ /* If this is the first stack frame, don't even check */
+ if ( scin->j == 0 ) return;
+
+ /* If the fontdesc at the top of the stack is the same as the one
+ * below, make a copy because we're about to do something to it (which
+ * should not affect the next level up). */
+ if ( st->fontdesc == scin->state[scin->j-1].fontdesc ) {
+ st->fontdesc = pango_font_description_copy(st->fontdesc);
+ }
+}
+
+
+static void set_fontsize(SCInterpreter *scin, const char *size_str)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ int size;
+ char *end;
+
+ if ( size_str[0] == '\0' ) return;
+
+ size = strtoul(size_str, &end, 10);
+ if ( end[0] != '\0' ) {
+ fprintf(stderr, _("Invalid font size '%s'\n"), size_str);
+ return;
+ }
+
+ copy_top_fontdesc(scin);
+ pango_font_description_set_size(st->fontdesc, size*PANGO_SCALE);
+ update_font(scin);
+}
+
+
+static void set_bold(SCInterpreter *scin)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ copy_top_fontdesc(scin);
+ pango_font_description_set_weight(st->fontdesc, PANGO_WEIGHT_BOLD);
+ update_font(scin);
+}
+
+
+static void set_oblique(SCInterpreter *scin)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ copy_top_fontdesc(scin);
+ pango_font_description_set_style(st->fontdesc, PANGO_STYLE_OBLIQUE);
+ update_font(scin);
+}
+
+
+static void set_italic(SCInterpreter *scin)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ copy_top_fontdesc(scin);
+ pango_font_description_set_style(st->fontdesc, PANGO_STYLE_ITALIC);
+ update_font(scin);
+}
+
+
+static void set_alignment(SCInterpreter *scin, PangoAlignment align)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ st->alignment = align;
+}
+
+
+/* This sets the colour for the font at the top of the stack */
+static void set_colour(SCInterpreter *scin, const char *colour)
+{
+ GdkRGBA col;
+ struct sc_state *st = &scin->state[scin->j];
+
+ if ( colour == NULL ) {
+ printf(_("Invalid colour\n"));
+ st->col[0] = 0.0;
+ st->col[1] = 0.0;
+ st->col[2] = 0.0;
+ st->col[3] = 1.0;
+ return;
+ }
+
+ gdk_rgba_parse(&col, colour);
+
+ st->col[0] = col.red;
+ st->col[1] = col.green;
+ st->col[2] = col.blue;
+ st->col[3] = col.alpha;
+ set_frame_default_style(sc_interp_get_frame(scin), scin);
+}
+
+
+static void set_bgcol(SCInterpreter *scin, const char *colour)
+{
+ GdkRGBA col;
+ struct frame *fr = sc_interp_get_frame(scin);
+
+ if ( fr == NULL ) return;
+
+ if ( colour == NULL ) {
+ printf(_("Invalid colour\n"));
+ return;
+ }
+
+ gdk_rgba_parse(&col, colour);
+
+ fr->bgcol[0] = col.red;
+ fr->bgcol[1] = col.green;
+ fr->bgcol[2] = col.blue;
+ fr->bgcol[3] = col.alpha;
+ fr->grad = GRAD_NONE;
+}
+
+
+static void set_bggrad(SCInterpreter *scin, const char *options,
+ GradientType grad)
+{
+ struct frame *fr = sc_interp_get_frame(scin);
+ GdkRGBA col1, col2;
+
+ if ( fr == NULL ) return;
+
+ if ( options == NULL ) {
+ printf(_("Invalid colour\n"));
+ return;
+ }
+
+ if ( parse_colour_duo(options, &col1, &col2) == 0 ) {
+
+ fr->bgcol[0] = col1.red;
+ fr->bgcol[1] = col1.green;
+ fr->bgcol[2] = col1.blue;
+ fr->bgcol[3] = col1.alpha;
+
+ fr->bgcol2[0] = col2.red;
+ fr->bgcol2[1] = col2.green;
+ fr->bgcol2[2] = col2.blue;
+ fr->bgcol2[3] = col2.alpha;
+
+ fr->grad = grad;
+
+ }
+}
+
+
+static char *get_constant(SCInterpreter *scin, unsigned int constant)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ if ( constant >= NUM_SC_CONSTANTS ) return NULL;
+ return st->constants[constant];
+}
+
+
+void sc_interp_set_constant(SCInterpreter *scin, unsigned int constant,
+ const char *val)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ if ( constant >= NUM_SC_CONSTANTS ) return;
+ if ( val == NULL ) return;
+ st->constants[constant] = strdup(val);
+}
+
+
+void sc_interp_save(SCInterpreter *scin)
+{
+ if ( scin->j+1 == scin->max_state ) {
+
+ struct sc_state *stack_new;
+
+ stack_new = realloc(scin->state, sizeof(struct sc_state)
+ * (scin->max_state+8));
+ if ( stack_new == NULL ) {
+ fprintf(stderr, "Failed to add to stack.\n");
+ return;
+ }
+
+ scin->state = stack_new;
+ scin->max_state += 8;
+
+ }
+
+ /* When n_fonts=0, we leave the first font uninitialised. This allows
+ * the stack to be "bootstrapped", but requires the first caller to do
+ * set_font and set_colour straight away. */
+ scin->state[scin->j+1] = scin->state[scin->j];
+
+ scin->j++;
+}
+
+
+void sc_interp_restore(SCInterpreter *scin)
+{
+ struct sc_state *st = &scin->state[scin->j];
+
+ if ( scin->j > 0 ) {
+
+ int i;
+
+ if ( st->fontdesc != scin->state[scin->j-1].fontdesc )
+ {
+ pango_font_description_free(st->fontdesc);
+ } /* else the font is the same as the previous one, and we
+ * don't need to free it just yet */
+
+ for ( i=0; i<NUM_SC_CONSTANTS; i++ ) {
+ if ( st->constants[i] != scin->state[scin->j-1].constants[i] ) {
+ free(st->constants[i]);
+ } /* same logic as above */
+ }
+ }
+
+ scin->j--;
+}
+
+
+struct frame *sc_interp_get_frame(SCInterpreter *scin)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ return st->fr;
+}
+
+
+static void set_frame(SCInterpreter *scin, struct frame *fr)
+{
+ struct sc_state *st = &scin->state[scin->j];
+ st->fr = fr;
+}
+
+
+SCInterpreter *sc_interp_new(PangoContext *pc, PangoLanguage *lang,
+ ImageStore *is, struct frame *top)
+{
+ SCInterpreter *scin;
+ struct sc_state *st;
+ int i;
+
+ scin = malloc(sizeof(SCInterpreter));
+ if ( scin == NULL ) return NULL;
+
+ scin->state = malloc(8*sizeof(struct sc_state));
+ if ( scin->state == NULL ) {
+ free(scin);
+ return NULL;
+ }
+ scin->j = 0;
+ scin->max_state = 8;
+
+ scin->pc = pc;
+ scin->is = is;
+ scin->s_constants = NULL;
+ scin->p_constants = NULL;
+ scin->cbl = NULL;
+ scin->lang = lang;
+
+ /* Initial state */
+ st = &scin->state[0];
+ st->fr = NULL;
+ st->paraspace[0] = 0.0;
+ st->paraspace[1] = 0.0;
+ st->paraspace[2] = 0.0;
+ st->paraspace[3] = 0.0;
+ st->fontdesc = NULL;
+ st->col[0] = 0.0;
+ st->col[1] = 0.0;
+ st->col[2] = 0.0;
+ st->col[3] = 1.0;
+ st->alignment = PANGO_ALIGN_LEFT;
+ for ( i=0; i<NUM_SC_CONSTANTS; i++ ) {
+ st->constants[i] = NULL;
+ }
+
+ /* The "ultimate" default font */
+ if ( scin->pc != NULL ) {
+ set_frame(scin, top);
+ set_font(scin, "Cantarell Regular 14");
+ set_colour(scin, "#000000");
+ }
+
+ return scin;
+}
+
+
+void sc_interp_destroy(SCInterpreter *scin)
+{
+ /* Empty the stack */
+ while ( scin->j > 0 ) {
+ sc_interp_restore(scin);
+ }
+
+ if ( scin->state[0].fontdesc != NULL ) {
+ pango_font_description_free(scin->state[0].fontdesc);
+ }
+
+ free(scin->state);
+ free(scin);
+}
+
+
+static void set_padding(struct frame *fr, const char *opts)
+{
+ float p[4];
+
+ if ( parse_tuple(opts, p) ) return;
+
+ if ( fr == NULL ) return;
+
+ fr->pad_l = p[0];
+ fr->pad_r = p[1];
+ fr->pad_t = p[2];
+ fr->pad_b = p[3];
+}
+
+
+static void set_paraspace(SCInterpreter *scin, const char *opts)
+{
+ float p[4];
+ struct sc_state *st = &scin->state[scin->j];
+
+ if ( parse_tuple(opts, p) ) return;
+
+ st->paraspace[0] = p[0];
+ st->paraspace[1] = p[1];
+ st->paraspace[2] = p[2];
+ st->paraspace[3] = p[3];
+
+ set_para_spacing(last_para(sc_interp_get_frame(scin)), p);
+}
+
+
+void update_geom(struct frame *fr)
+{
+ char geom[256];
+ snprintf(geom, 255, "%.1fux%.1fu+%.1f+%.1f",
+ fr->w, fr->h, fr->x, fr->y);
+
+ /* FIXME: What if there are other options? */
+ sc_block_set_options(fr->scblocks, strdup(geom));
+}
+
+
+static int calculate_dims(const char *opt, struct frame *parent,
+ double *wp, double *hp, double *xp, double *yp)
+{
+ LengthUnits h_units, w_units;
+
+ if ( parse_dims(opt, wp, hp, &w_units, &h_units, xp, yp) ) {
+ return 1;
+ }
+
+ if ( w_units == UNITS_FRAC ) {
+ if ( parent != NULL ) {
+ double pw = parent->w;
+ pw -= parent->pad_l;
+ pw -= parent->pad_r;
+ *wp = pw * *wp;
+ } else {
+ *wp = -1.0;
+ }
+
+ }
+ if ( h_units == UNITS_FRAC ) {
+ if ( parent != NULL ) {
+ double ph = parent->h;
+ ph -= parent->pad_t;
+ ph -= parent->pad_b;
+ *hp = ph * *hp;
+ } else {
+ *hp = -1.0;
+ }
+ }
+
+ return 0;
+}
+
+
+static int parse_frame_option(const char *opt, struct frame *fr,
+ struct frame *parent)
+{
+ if ( (index(opt, 'x') != NULL) && (index(opt, '+') != NULL)
+ && (index(opt, '+') != rindex(opt, '+')) ) {
+ return calculate_dims(opt, parent, &fr->w, &fr->h, &fr->x, &fr->y);
+ }
+
+ fprintf(stderr, _("Unrecognised frame option '%s'\n"), opt);
+
+ return 1;
+}
+
+
+static int parse_frame_options(struct frame *fr, struct frame *parent,
+ const char *opth)
+{
+ int i;
+ size_t len;
+ size_t start;
+ char *opt;
+
+ if ( opth == NULL ) return 1;
+
+ opt = strdup(opth);
+
+ len = strlen(opt);
+ start = 0;
+
+ for ( i=0; i<len; i++ ) {
+
+ /* FIXME: comma might be escaped or quoted */
+ if ( opt[i] == ',' ) {
+ opt[i] = '\0';
+ if ( parse_frame_option(opt+start, fr, parent) ) {
+ return 1;
+ }
+ start = i+1;
+ }
+
+ }
+
+ if ( start != len ) {
+ if ( parse_frame_option(opt+start, fr, parent) ) return 1;
+ }
+
+ free(opt);
+
+ return 0;
+}
+
+
+static int parse_image_option(const char *opt, struct frame *parent,
+ double *wp, double *hp, char **filenamep)
+{
+ if ( (index(opt, 'x') != NULL) && (index(opt, '+') != NULL)
+ && (index(opt, '+') != rindex(opt, '+')) ) {
+ double dum;
+ return calculate_dims(opt, NULL, wp, hp, &dum, &dum);
+ }
+
+ if ( strncmp(opt, "filename=\"", 10) == 0 ) {
+ char *fn;
+ fn = strdup(opt+10);
+ if ( fn[strlen(fn)-1] != '\"' ) {
+ fprintf(stderr, "Unterminated filename?\n");
+ free(fn);
+ return 1;
+ }
+ fn[strlen(fn)-1] = '\0';
+ *filenamep = fn;
+ return 0;
+ }
+
+ fprintf(stderr, _("Unrecognised image option '%s'\n"), opt);
+
+ return 1;
+}
+
+
+static int parse_image_options(const char *opth, struct frame *parent,
+ double *wp, double *hp, char **filenamep)
+{
+ int i;
+ size_t len;
+ size_t start;
+ char *opt;
+
+ if ( opth == NULL ) return 1;
+
+ opt = strdup(opth);
+
+ len = strlen(opt);
+ start = 0;
+
+ for ( i=0; i<len; i++ ) {
+
+ /* FIXME: comma might be escaped or quoted */
+ if ( opt[i] == ',' ) {
+ opt[i] = '\0';
+ if ( parse_image_option(opt+start, parent,
+ wp, hp, filenamep) ) return 1;
+ start = i+1;
+ }
+
+ }
+
+ if ( start != len ) {
+ if ( parse_image_option(opt+start, parent,
+ wp, hp, filenamep) ) return 1;
+ }
+
+ free(opt);
+
+ return 0;
+}
+
+
+static void maybe_recurse_before(SCInterpreter *scin, SCBlock *child)
+{
+ if ( child == NULL ) return;
+
+ sc_interp_save(scin);
+}
+
+
+static void maybe_recurse_after(SCInterpreter *scin, SCBlock *child,
+ Stylesheet *ss)
+{
+ if ( child == NULL ) return;
+
+ sc_interp_add_blocks(scin, child, ss);
+ sc_interp_restore(scin);
+}
+
+
+static void add_newpara(SCBlock *bl, SCInterpreter *scin)
+{
+ Paragraph *last_para;
+ Paragraph *para;
+ struct sc_state *st = &scin->state[scin->j];
+ struct frame *fr = sc_interp_get_frame(scin);
+
+ if ( fr->paras == NULL ) return;
+ last_para = fr->paras[fr->n_paras-1];
+
+ set_newline_at_end(last_para, bl);
+
+ /* The block after the \newpara will always be the first one of the
+ * next paragraph, by definition, even if it's \f or another \newpara */
+ para = create_paragraph(fr, sc_block_next(bl));
+ set_para_alignment(para, st->alignment);
+ set_para_spacing(para, st->paraspace);
+}
+
+
+/* Add the SCBlock to the text in 'frame', at the end */
+static int add_text(struct frame *fr, PangoContext *pc, SCBlock *bl,
+ PangoLanguage *lang, int editable, SCInterpreter *scin,
+ const char *real_text)
+{
+ const char *text = sc_block_contents(bl);
+ PangoFontDescription *fontdesc;
+ double *col;
+ struct sc_state *st = &scin->state[scin->j];
+ Paragraph *para;
+
+ /* Empty block? */
+ if ( text == NULL && real_text == NULL ) return 1;
+
+ fontdesc = sc_interp_get_fontdesc(scin);
+ col = sc_interp_get_fgcol(scin);
+
+ para = last_para(fr);
+ if ( (para == NULL) || (para_type(para) != PARA_TYPE_TEXT) ) {
+ /* Last paragraph is not text.
+ * or: no paragraphs yet.
+ * Either way: Create the first one */
+ para = create_paragraph(fr, bl);
+ }
+
+ set_para_alignment(para, st->alignment);
+ add_run(para, bl, fontdesc, col, real_text);
+ set_para_spacing(para, st->paraspace);
+
+ return 0;
+}
+
+
+static void apply_style(SCInterpreter *scin, Stylesheet *ss, const char *path)
+{
+ char *result;
+
+ if ( ss == NULL ) return;
+
+ /* Font */
+ result = stylesheet_lookup(ss, path, "font");
+ if ( result != NULL ) set_font(scin, result);
+
+ /* Foreground colour */
+ result = stylesheet_lookup(ss, path, "fgcol");
+ if ( result != NULL ) set_colour(scin, result);
+
+ /* Background (vertical gradient) */
+ result = stylesheet_lookup(ss, path, "bggradv");
+ if ( result != NULL ) set_bggrad(scin, result, GRAD_VERT);
+
+ /* Background (horizontal gradient) */
+ result = stylesheet_lookup(ss, path, "bggradh");
+ if ( result != NULL ) set_bggrad(scin, result, GRAD_HORIZ);
+
+ /* Background (solid colour) */
+ result = stylesheet_lookup(ss, path, "bgcol");
+ if ( result != NULL ) set_bgcol(scin, result);
+
+ /* Padding */
+ result = stylesheet_lookup(ss, path, "pad");
+ if ( result != NULL ) set_padding(sc_interp_get_frame(scin), result);
+
+ /* Paragraph spacing */
+ result = stylesheet_lookup(ss, path, "paraspace");
+ if ( result != NULL ) set_paraspace(scin, result);
+
+ /* Alignment */
+ result = stylesheet_lookup(ss, path, "alignment");
+ if ( result != NULL ) {
+ if ( strcmp(result, "center") == 0 ) {
+ set_alignment(scin, PANGO_ALIGN_CENTER);
+ }
+ if ( strcmp(result, "left") == 0 ) {
+ set_alignment(scin, PANGO_ALIGN_LEFT);
+ }
+ if ( strcmp(result, "right") == 0 ) {
+ set_alignment(scin, PANGO_ALIGN_RIGHT);
+ }
+ }
+}
+
+
+static void output_frame(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss,
+ const char *stylename)
+{
+ struct frame *fr;
+ SCBlock *child = sc_block_child(bl);
+ const char *options = sc_block_options(bl);
+ char *result;
+
+ fr = add_subframe(sc_interp_get_frame(scin));
+ if ( fr == NULL ) {
+ fprintf(stderr, _("Failed to add frame.\n"));
+ return;
+ }
+
+ fr->scblocks = bl;
+ fr->resizable = 1;
+
+ /* Lowest priority: current state of interpreter */
+ set_frame_default_style(fr, scin);
+
+ /* Next priority: geometry from stylesheet */
+ result = stylesheet_lookup(ss, stylename, "geometry");
+ if ( result != NULL ) {
+ parse_frame_options(fr, sc_interp_get_frame(scin), result);
+ }
+
+ /* Highest priority: parameters to \f (or \slidetitle etc) */
+ parse_frame_options(fr, sc_interp_get_frame(scin), options);
+
+ maybe_recurse_before(scin, child);
+ set_frame(scin, fr);
+ apply_style(scin, ss, stylename);
+ maybe_recurse_after(scin, child, ss);
+}
+
+
+static int check_outputs(SCBlock *bl, SCInterpreter *scin, Stylesheet *ss)
+{
+ const char *name = sc_block_name(bl);
+ const char *options = sc_block_options(bl);
+
+ if ( name == NULL ) {
+ add_text(sc_interp_get_frame(scin),
+ scin->pc, bl, scin->lang, 1, scin, NULL);
+
+ } else if ( strcmp(name, "image")==0 ) {
+ double w, h;
+ char *filename;
+ if ( parse_image_options(options, sc_interp_get_frame(scin),
+ &w, &h, &filename) == 0 )
+ {
+ add_image_para(sc_interp_get_frame(scin), bl,
+ filename, scin->is, w, h, 1);
+ free(filename);
+ } else {
+ fprintf(stderr, _("Invalid image options '%s'\n"),
+ options);
+ }
+
+ } else if ( strcmp(name, "f")==0 ) {
+ output_frame(scin, bl, ss, "$.slide.frame");
+
+ } else if ( strcmp(name, "slidetitle")==0 ) {
+ output_frame(scin, bl, ss, "$.slide.slidetitle");
+
+ } else if ( strcmp(name, "prestitle")==0 ) {
+ output_frame(scin, bl, ss, "$.slide.prestitle");
+
+ } else if ( strcmp(name, "author")==0 ) {
+ output_frame(scin, bl, ss, "$.slide.author");
+
+ } else if ( strcmp(name, "footer")==0 ) {
+ output_frame(scin, bl, ss, "$.slide.footer");
+
+ } else if ( strcmp(name, "newpara")==0 ) {
+ add_newpara(bl, scin);
+
+ } else if ( strcmp(name, "slidenumber")==0 ) {
+ char *con = get_constant(scin, SCCONST_SLIDENUMBER);
+ if ( con != NULL ) {
+ add_text(sc_interp_get_frame(scin), scin->pc, bl,
+ scin->lang, 1, scin, con);
+ }
+
+ } else {
+ return 0;
+ }
+
+ return 1; /* handled */
+}
+
+
+int sc_interp_add_block(SCInterpreter *scin, SCBlock *bl, Stylesheet *ss)
+{
+ const char *name = sc_block_name(bl);
+ const char *options = sc_block_options(bl);
+ SCBlock *child = sc_block_child(bl);
+
+ //printf("Running this --------->\n");
+ //show_sc_blocks(bl);
+ //printf("<------------\n");
+
+ if ( check_callback(scin, bl) ) {
+ /* Handled in check_callback, don't do anything else */
+
+ } else if ((sc_interp_get_frame(scin) != NULL)
+ && check_outputs(bl, scin, ss) ) {
+ /* Block handled as output thing */
+
+ } else if ( name == NULL ) {
+ /* Dummy to ensure name != NULL below */
+
+ } else if ( strcmp(name, "presentation") == 0 ) {
+ maybe_recurse_before(scin, child);
+ apply_style(scin, ss, "$.narrative");
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "slide") == 0 ) {
+ maybe_recurse_before(scin, child);
+ apply_style(scin, ss, "$.slide");
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "font") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_font(scin, options);
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "fontsize") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_fontsize(scin, options);
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "bold") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_bold(scin);
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "oblique") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_oblique(scin);
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "italic") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_italic(scin);
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "lalign") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_alignment(scin, PANGO_ALIGN_LEFT);
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "ralign") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_alignment(scin, PANGO_ALIGN_RIGHT);
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "center") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_alignment(scin, PANGO_ALIGN_CENTER);
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "fgcol") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_colour(scin, options);
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "pad") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_padding(sc_interp_get_frame(scin), options);
+ maybe_recurse_after(scin, child, ss);
+
+ } else if ( strcmp(name, "bgcol") == 0 ) {
+ set_bgcol(scin, options);
+
+ } else if ( strcmp(name, "bggradh") == 0 ) {
+ set_bggrad(scin, options, GRAD_HORIZ);
+
+ } else if ( strcmp(name, "bggradv") == 0 ) {
+ set_bggrad(scin, options, GRAD_VERT);
+
+ } else if ( strcmp(name, "paraspace") == 0 ) {
+ maybe_recurse_before(scin, child);
+ set_paraspace(scin, options);
+ maybe_recurse_after(scin, child, ss);
+
+ } else {
+
+ fprintf(stderr, "Don't know what to do with this:\n");
+ show_sc_block(bl, "");
+
+ }
+
+ return 0;
+}
+