/* * sc_interp.c * * Copyright © 2014 Thomas White * * 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 . * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "sc_parse.h" #include "sc_interp.h" #include "shape.h" #include "wrap.h" struct macro { char *name; SCBlock *bl; struct macro *prev; /* Previous declaration, or NULL */ }; struct sc_state { PangoFontDescription *fontdesc; PangoFont *font; double col[4]; int ascent; int height; struct frame *fr; /* The current frame */ int n_macros; int max_macros; struct macro *macros; /* Contents need to be copied on push */ }; struct _scinterp { PangoContext *pc; PangoLanguage *lang; struct slide_constants *s_constants; struct presentation_constants *p_constants; struct sc_state *state; int j; /* Index of the current state */ int max_state; int output; }; PangoFont *sc_interp_get_font(SCInterpreter *scin) { struct sc_state *st = &scin->state[scin->j]; return st->font; } 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; } int sc_interp_get_ascent(SCInterpreter *scin) { struct sc_state *st = &scin->state[scin->j]; return st->ascent; } int sc_interp_get_height(SCInterpreter *scin) { struct sc_state *st = &scin->state[scin->j]; return st->height; } static void set_font(SCInterpreter *scin, const char *font_name) { PangoFontMetrics *metrics; 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; } st->font = pango_font_map_load_font(pango_context_get_font_map(scin->pc), scin->pc, st->fontdesc); if ( st->font == NULL ) { fprintf(stderr, "Couldn't load font.\n"); 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); } /* 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; } static void set_frame_bgcolour(struct frame *fr, const char *colour) { GdkRGBA col; if ( colour == NULL ) { printf("Invalid colour\n"); fr->bgcol[0] = 0.0; fr->bgcol[1] = 0.0; fr->bgcol[2] = 0.0; fr->bgcol[3] = 1.0; 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; } 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 ) { 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 */ } 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, struct frame *top) { SCInterpreter *scin; struct sc_state *st; 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->s_constants = NULL; scin->p_constants = NULL; scin->output = 0; st = &scin->state[0]; st->n_macros = 0; st->max_macros = 16; st->macros = malloc(16*sizeof(struct macro)); if ( st->macros == NULL ) { free(scin->state); free(scin); return NULL; } /* FIXME: Determine proper language (somehow...) */ scin->lang = pango_language_from_string("en_GB"); /* The "ultimate" default font */ set_font(scin, "Sans 12"); set_colour(scin, "#000000"); set_frame(scin, top); return scin; } void sc_interp_destroy(SCInterpreter *scin) { /* Empty the stack */ while ( scin->j > 0 ) { sc_interp_restore(scin); } pango_font_description_free(scin->state[0].fontdesc); free(scin); } static LengthUnits get_units(const char *t) { size_t len = strlen(t); if ( t[len-1] == 'f' ) return UNITS_FRAC; if ( t[len-1] == 'u' ) return UNITS_SLIDE; fprintf(stderr, "Invalid units in '%s'\n", t); return UNITS_SLIDE; } static int parse_dims(const char *opt, struct frame *parent, double *wp, double *hp, double *xp, double *yp) { char *w; char *h; char *x; char *y; char *check; LengthUnits h_units, w_units; /* Looks like a dimension/position thing */ w = strdup(opt); h = index(w, 'x'); h[0] = '\0'; h++; x = index(h, '+'); if ( x == NULL ) goto invalid; x[0] = '\0'; x++; y = index(x, '+'); if ( x == NULL ) goto invalid; y[0] = '\0'; y++; *wp = strtod(w, &check); if ( check == w ) goto invalid; w_units = get_units(w); if ( w_units == UNITS_FRAC ) { double pw = parent->w; pw -= parent->pad_l; pw -= parent->pad_r; *wp = pw * *wp; } *hp = strtod(h, &check); if ( check == h ) goto invalid; h_units = get_units(h); if ( h_units == UNITS_FRAC ) { double ph = parent->h; ph -= parent->pad_t; ph -= parent->pad_b; *hp = ph * *hp; } *xp= strtod(x, &check); if ( check == x ) goto invalid; *yp = strtod(y, &check); if ( check == y ) goto invalid; return 0; invalid: fprintf(stderr, "Invalid dimensions '%s'\n", opt); return 1; } 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 parse_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; ioutput ) return; if ( child == NULL ) return; sc_interp_save(scin); } static void maybe_recurse_after(SCInterpreter *scin, SCBlock *child, SCBlock *output) { if ( !scin->output ) return; if ( child == NULL ) return; sc_interp_add_blocks(scin, child, output); sc_interp_restore(scin); } static int check_outputs(SCBlock *bl, SCInterpreter *scin) { const char *name = sc_block_name(bl); const char *options = sc_block_options(bl); const char *contents = sc_block_contents(bl); SCBlock *child = sc_block_child(bl); if ( name == NULL ) { split_words(sc_interp_get_frame(scin)->boxes, scin->pc, bl, contents, scin->lang, 1, scin); } else if ( strcmp(name, "bgcol") == 0 ) { maybe_recurse_before(scin, child); set_frame_bgcolour(sc_interp_get_frame(scin), options); maybe_recurse_after(scin, child, 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_box(sc_interp_get_frame(scin)->boxes, filename, w, h, 1); free(filename); } else { fprintf(stderr, "Invalid image options '%s'\n", options); } } else if ( strcmp(name, "slidenumber")==0) { if ( scin->s_constants != NULL ) { char *tmp = malloc(64); if ( tmp != NULL ) { snprintf(tmp, 63, "%i", scin->s_constants->slide_number); split_words(sc_interp_get_frame(scin)->boxes, scin->pc, bl, tmp, scin->lang, 0, scin); } } else { printf("No slide constants.\n"); } } else if ( strcmp(name, "f")==0 ) { struct frame *fr = sc_block_frame(bl); if ( fr != NULL ) { free(fr->boxes->boxes); free(fr->boxes); fr->boxes = malloc(sizeof(struct wrap_line)); initialise_line(fr->boxes); } if ( fr == NULL ) { fr = add_subframe(sc_interp_get_frame(scin)); sc_block_set_frame(bl, fr); fr->scblocks = child; } if ( fr == NULL ) { fprintf(stderr, "Failed to add frame.\n"); return 1; } parse_frame_options(fr, sc_interp_get_frame(scin), options); maybe_recurse_before(scin, child); set_frame(scin, fr); maybe_recurse_after(scin, child, NULL); } else { return 0; } return 1; /* handled */ } static int add_macro(struct macro *m, char *mname, SCBlock *bl) { m->name = mname; m->bl = bl; m->prev = NULL; /* FIXME: Stacking */ return 0; } static int try_add_macro(SCInterpreter *scin, const char *options, SCBlock *bl) { struct sc_state *st = &scin->state[scin->j]; char *nn; char *comma; int i; nn = strdup(options); comma = strchr(nn, ','); if ( comma != NULL ) { comma[0] = '\0'; } for ( i=0; in_macros; i++ ) { if ( strcmp(st->macros[i].name, nn) == 0 ) { return add_macro(&st->macros[i], nn, bl); } } if ( st->max_macros == st->n_macros ) { struct macro *macros_new; macros_new = realloc(st->macros, sizeof(struct macro) * (st->max_macros+16)); if ( macros_new == NULL ) { fprintf(stderr, "Failed to add macro.\n"); return 1; } st->macros = macros_new; st->max_macros += 16; } i = st->n_macros++; return add_macro(&st->macros[i], nn, bl); } static int check_macro(const char *name, SCInterpreter *scin) { int i; struct sc_state *st = &scin->state[scin->j]; for ( i=0; in_macros; i++ ) { if ( strcmp(st->macros[i].name, name) == 0 ) { return 1; } } return 0; } static void exec_macro(const char *name, SCInterpreter *scin) { int i; struct sc_state *st = &scin->state[scin->j]; for ( i=0; in_macros; i++ ) { if ( strcmp(st->macros[i].name, name) == 0 ) { sc_interp_add_blocks(scin, st->macros[i].bl, NULL); return; } } } int sc_interp_add_blocks(SCInterpreter *scin, SCBlock *bl, SCBlock *output) { while ( bl != NULL ) { const char *name = sc_block_name(bl); const char *options = sc_block_options(bl); SCBlock *child = sc_block_child(bl); if ( bl == output ) scin->output = 1; if ( scin->output && (sc_interp_get_frame(scin) != NULL) && check_outputs(bl, scin) ) { /* Block handled as output thing */ } else if ( name == NULL ) { /* Dummy to ensure name != NULL below */ } else if ( strcmp(name, "ss") == 0 ) { try_add_macro(scin, options, sc_block_child(bl)); } else if ( strcmp(name, "font") == 0 ) { maybe_recurse_before(scin, child); set_font(scin, options); maybe_recurse_after(scin, child, output); } else if ( strcmp(name, "fgcol") == 0 ) { maybe_recurse_before(scin, child); set_colour(scin, options); maybe_recurse_after(scin, child, output); } else if ( check_macro(name, scin) ) { maybe_recurse_before(scin, child); exec_macro(name, scin); maybe_recurse_after(scin, child, output); } else if ( strcmp(name, "notes") == 0 ) { /* FIXME: Do something with notes */ } else if ( strcmp(name, "pad") == 0 ) { maybe_recurse_before(scin, child); /* FIXME: Implement padding */ maybe_recurse_after(scin, child, output); } else if ( strcmp(name, "slide") == 0 ) { maybe_recurse_before(scin, child); maybe_recurse_after(scin, child, output); } else { fprintf(stderr, "Don't know what to do with this:\n"); show_sc_block(bl, ""); } if ( bl == output ) return 0; bl = sc_block_next(bl); } return 0; }