Save the stylesheet
[colloquium.git] / src / stylesheet.c
index 857f0f2..dbafd8c 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * stylesheet.c
  *
- * Copyright © 2013 Thomas White <taw@bitwiz.org.uk>
+ * Copyright © 2013-2018 Thomas White <taw@bitwiz.org.uk>
  *
  * This file is part of Colloquium.
  *
 #include <config.h>
 #endif
 
+#include <json-glib/json-glib.h>
 #include <stdlib.h>
 #include <string.h>
-#include <gtk/gtk.h>
-#include <assert.h>
+#include <stdio.h>
+#include <gio/gio.h>
+#include <gdk/gdk.h>
 
-#include "presentation.h"
 #include "stylesheet.h"
-#include "loadsave.h"
+#include "utils.h"
 
 
-struct _stylesheet
-{
-       struct style          **styles;
-       int                     n_styles;
-
-       struct slide_template **templates;
-       int                     n_templates;
-
-       struct style           *default_style;
+struct _stylesheet {
+       JsonNode *root;
 };
 
 
-struct style *new_style(StyleSheet *ss, const char *name, const char *pname)
-{
-       struct style *sty;
-       int n;
-       struct style **styles_new;
-
-       sty = calloc(1, sizeof(*sty));
-       if ( sty == NULL ) return NULL;
-
-       sty->name = strdup(name);
-       sty->pname = strdup(pname);
-
-       /* DS9K compatibility */
-       sty->lop.x = 0.0;
-       sty->lop.y = 0.0;
-       sty->lop.w = 100.0;
-       sty->lop.h = 100.0;
-
-       n = ss->n_styles;
-       styles_new = realloc(ss->styles, (n+1)*sizeof(sty));
-       if ( styles_new == NULL ) {
-               free(sty->name);
-               free(sty);
-               return NULL;
-       }
-       ss->styles = styles_new;
-       ss->styles[n] = sty;
-       ss->n_styles = n+1;
-
-       return sty;
-}
-
-
-struct style *lookup_style(StyleSheet *ss, const char *pname)
-{
-       int i;
-
-       for ( i=0; i<ss->n_styles; i++ ) {
-               if ( strcmp(ss->styles[i]->pname, pname) == 0 ) {
-                       return ss->styles[i];
-               }
-       }
-       return NULL;
-}
-
-
-void free_stylesheet(StyleSheet *ss)
-{
-       int i;
-
-       for ( i=0; i<ss->n_styles; i++ ) {
-               free(ss->styles[i]->name);
-               free(ss->styles[i]->sc_prologue);
-               free(ss->styles[i]);
-       }
-
-       free(ss->styles);
-       free(ss);
-}
-
-
-extern void *_binary_src_default_stylesheet_sty_start;
-extern void *_binary_src_default_stylesheet_sty_size;
-
-StyleSheet *default_stylesheet()
+static int find_comma(const char *a)
 {
-       char *v;
-       struct ds_node *root;
-       struct ds_node *ss_root;
-       StyleSheet *ss;
-       size_t len;
-
-       len = (size_t)&_binary_src_default_stylesheet_sty_size;
-       v = malloc(len+1);
-       if ( v == NULL ) return NULL;
-
-       memcpy(v, &_binary_src_default_stylesheet_sty_start, len);
-       v[len] = '\0';
-
-       root = new_ds_node("root");
-       deserialize_memory(v, root);
-
-       ss_root = find_node(root, "stylesheet", 0);
-       if ( ss_root == NULL ) {
-               fprintf(stderr, "Doesn't look like a stylesheet.\n");
-               return NULL;
-       }
-       ss = tree_to_stylesheet(ss_root);
-       free_ds_tree(root);
-       free(v);
-       return ss;
+       int i = 0;
+       int in_brackets = 0;
+       size_t len = strlen(a);
+
+       do {
+               if ( (a[i] == ',') && !in_brackets ) return i;
+               if ( a[i] == '(' ) in_brackets++;
+               if ( a[i] == ')' ) in_brackets--;
+               i++;
+       } while ( i < len );
+       return 0;
 }
 
 
-static void get_field_f_units(struct ds_node *root, const char *key,
-                              double *val, LengthUnits *units)
+int parse_colour_duo(const char *a, GdkRGBA *col1, GdkRGBA *col2)
 {
-       char *s;
-       char *u;
-       double v;
-       char *check;
-
-       get_field_s(root, key, &s);
-       u = index(s, ' ');
-       if ( u == NULL ) {
-               fprintf(stderr, "Invalid value '%s'\n", s);
-               return;
-       }
+       char *acopy;
+       int cpos;
 
-       u[0] = '\0';
-       u++;
-       v = strtod(s, &check);
-       if ( check == s ) {
-               fprintf(stderr, "Invalid value '%s'\n", s);
-               return;
-       }
+       acopy = strdup(a);
+       if ( acopy == NULL ) return 1;
 
-       if ( strcmp(u, "f") == 0 ) *units = UNITS_FRAC;
-       else if ( strcmp(u, "u") == 0 ) *units = UNITS_SLIDE;
-       else {
-               fprintf(stderr, "Invalid unit '%s'\n", u);
-               return;
+       cpos = find_comma(acopy);
+       if ( cpos == 0 ) {
+               fprintf(stderr, _("Invalid bg gradient spec '%s'\n"), a);
+               return 1;
        }
 
-       *val = v;
-}
+       acopy[cpos] = '\0';
 
-
-static int read_template(struct slide_template *t, StyleSheet *ss,
-                         struct ds_node *node)
-{
-       int i;
-       char *top_style;
-
-       if ( !get_field_s(node, "top_style", &top_style) ) {
-               t->top_style = lookup_style(ss, top_style);
-               free(top_style);
-       } else {
-               t->top_style = NULL;
+       if ( gdk_rgba_parse(col1, acopy) != TRUE ) {
+               fprintf(stderr, _("Failed to parse colour: %s\n"), acopy);
        }
-
-       for ( i=0; i<node->n_children; i++ ) {
-
-               char *sn;
-
-               if ( strncmp(node->children[i]->key, "sty", 3) != 0 ) {
-                       continue;
-               }
-
-               /* This looks a bit weird, but it's done so that the contents
-                * can go through the same validation as other fields */
-               if ( !get_field_s(node, node->children[i]->key, &sn) ) {
-
-                       struct style *sty;
-
-                       sty = lookup_style(ss, sn);
-                       if ( sty == NULL ) {
-                               fprintf(stderr, "Couldn't find style '%s'\n",
-                                       sn);
-                       } else {
-                               add_to_template(t, sty);
-                       }
-
-                       free(sn);
-               }
-
+       if ( gdk_rgba_parse(col2, &acopy[cpos+1]) != TRUE ) {
+               fprintf(stderr, _("Failed to parse colour: %s\n"), &acopy[cpos+1]);
        }
 
+       free(acopy);
        return 0;
 }
 
 
-static int read_style(struct style *sty, struct ds_node *root)
+Stylesheet *stylesheet_load(GFile *file)
 {
-       get_field_f(root, "margin_l", &sty->lop.margin_l);
-       get_field_f(root, "margin_r", &sty->lop.margin_r);
-       get_field_f(root, "margin_t", &sty->lop.margin_t);
-       get_field_f(root, "margin_b", &sty->lop.margin_b);
-       get_field_f(root, "pad_l", &sty->lop.pad_l);
-       get_field_f(root, "pad_r", &sty->lop.pad_r);
-       get_field_f(root, "pad_t", &sty->lop.pad_t);
-       get_field_f(root, "pad_b", &sty->lop.pad_b);
-       get_field_f(root, "x", &sty->lop.x);
-       get_field_f(root, "y", &sty->lop.y);
-       get_field_f_units(root, "w", &sty->lop.w, &sty->lop.w_units);
-       get_field_f_units(root, "h", &sty->lop.h, &sty->lop.h_units);
-       get_field_s(root, "prologue", &sty->sc_prologue);
+       JsonParser *parser;
+       gboolean r;
+       GError *err = NULL;
+       Stylesheet *ss;
+       char *everything;
+       gsize len;
 
-       return 0;
-}
+       printf("Trying stylesheet '%s'\n", g_file_get_uri(file));
 
-
-StyleSheet *tree_to_stylesheet(struct ds_node *root)
-{
-       StyleSheet *ss;
-       struct ds_node *node;
-       int i;
-       char *ds;
-
-       ss = new_stylesheet();
+       ss = calloc(1, sizeof(Stylesheet));
        if ( ss == NULL ) return NULL;
 
-       node = find_node(root, "styles", 0);
-       if ( node == NULL ) {
-               fprintf(stderr, "Couldn't find styles\n");
-               free_stylesheet(ss);
-               return NULL;
-       }
-
-       for ( i=0; i<node->n_children; i++ ) {
-
-               struct style *ns;
-               char *v;
-
-               get_field_s(node->children[i], "name", &v);
-               if ( v == NULL ) {
-                       fprintf(stderr, "No name for style '%s'\n",
-                               node->children[i]->key);
-                       continue;
-               }
-
-               ns = new_style(ss, v, node->children[i]->key);
-               if ( ns == NULL ) {
-                       fprintf(stderr, "Couldn't create style for '%s'\n",
-                               node->children[i]->key);
-                       continue;
-               }
+       parser = json_parser_new();
 
-               if ( read_style(ns, node->children[i]) ) {
-                       fprintf(stderr, "Couldn't read style '%s'\n", v);
-                       continue;
-               }
-
-       }
-
-       if ( get_field_s(root, "default_style", &ds) == 0 ) {
-               ss->default_style = lookup_style(ss, ds);
-               if ( ss->default_style == NULL ) {
-                       fprintf(stderr, "Failed to find default style '%s'\n",
-                               ds);
-               }
-       } else {
-               ss->default_style = NULL;
+       if ( !g_file_load_contents(file, NULL, &everything, &len, NULL, NULL) ) {
+               fprintf(stderr, _("Failed to load stylesheet '%s'\n"),
+                       g_file_get_uri(file));
+               return NULL;
        }
 
-       node = find_node(root, "templates", 0);
-       if ( node == NULL ) {
-               fprintf(stderr, "Couldn't find templates\n");
-               free_stylesheet(ss);
+       r = json_parser_load_from_data(parser, everything, len, &err);
+       if ( r == FALSE ) {
+               fprintf(stderr, "Failed to load style sheet: '%s'\n", err->message);
                return NULL;
        }
 
-       for ( i=0; i<node->n_children; i++ ) {
-
-               struct slide_template *nt;
-               char *v;
-
-               get_field_s(node->children[i], "name", &v);
-               if ( v == NULL ) {
-                       fprintf(stderr, "No name for template '%s'\n",
-                               node->children[i]->key);
-                       continue;
-               }
-
-               nt = new_template(ss, v);
-               if ( nt == NULL ) {
-                       fprintf(stderr, "Couldn't create template for '%s'\n",
-                               node->children[i]->key);
-                       continue;
-               }
-
-               if ( read_template(nt, ss, node->children[i]) ) {
-                       fprintf(stderr, "Couldn't read template '%s'\n", v);
-                       continue;
-               }
-
-       }
+       ss->root = json_parser_steal_root(parser);
+       g_object_unref(parser);
 
        return ss;
 }
 
 
-static int fixup_styles(struct frame *fr, StyleSheet *ss)
+static JsonObject *find_stylesheet_object(Stylesheet *ss, const char *path,
+                                          JsonNode **freeme)
 {
-       int i;
-       char *t;
-       int n = 0;
-
-       if ( fr->style != NULL ) {
+       JsonNode *node;
+       JsonObject *obj;
+       JsonArray *array;
+       GError *err = NULL;
 
-               /* FIXME: Horrible to do this using names and string comparisons
-                *  - what if the stylesheets are in different languages?
-                * Better would be to have "roles" for common types of style
-                * and match using those. */
+       node = json_path_query(path, ss->root, &err);
+       array = json_node_get_array(node);
 
-               t = fr->style->name;
-
-               for ( i=0; i<ss->n_styles; i++ ) {
-                       if ( strcmp(t, ss->styles[i]->name) == 0 ) {
-                               fr->style = ss->styles[i];
-                               n = 1;
-                               break;
-                       }
-               }
+       if ( json_array_get_length(array) != 1 ) {
+               json_node_unref(node);
+               fprintf(stderr, "More than one result in SS lookup (%s)!\n", path);
+               return NULL;
        }
 
-       for ( i=0; i<fr->num_children; i++ ) {
-               n += fixup_styles(fr->children[i], ss);
+       obj = json_array_get_object_element(array, 0);
+       if ( obj == NULL ) {
+               printf("%s not a JSON object\n", path);
+               json_node_unref(node);
+               return NULL;
        }
 
-       return n;
+       *freeme = node;
+       return obj;
 }
 
 
-static void fixup_templates(struct slide *s, StyleSheet *ss)
+char *stylesheet_lookup(Stylesheet *ss, const char *path, const char *key)
 {
-       int i;
-       char *t;
+       JsonObject *obj;
+       char *ret = NULL;
+       JsonNode *node = NULL;
 
-       if ( s->st != NULL ) {
+       if ( ss == NULL ) {
+               fprintf(stderr, _("No stylesheet!\n"));
+               return NULL;
+       }
 
-               /* FIXME: Horrible to do this using names and string comparisons
-                *  - what if the stylesheets are in different languages?
-                * Better would be to have "roles" for common types of template
-                * and match using those. */
+       obj = find_stylesheet_object(ss, path, &node);
 
-               t = s->st->name;
+       if ( json_object_has_member(obj, key) ) {
 
-               for ( i=0; i<ss->n_templates; i++ ) {
-                       if ( strcmp(t, ss->templates[i]->name) == 0 ) {
-                               s->st = ss->templates[i];
-                               break;
-                       }
+               const gchar *v;
+               v = json_object_get_string_member(obj, key);
+               if ( v != NULL ) {
+                       ret = strdup(v);
+               } else {
+                       fprintf(stderr, "Error retrieving %s.%s\n", path, key);
                }
 
-               s->top->style = s->st->top_style;
+       } /* else not found, too bad */
 
-       }
+       if ( node != NULL ) json_node_unref(node);
+       return ret;
 }
 
 
-int replace_stylesheet(struct presentation *p, const char *filename)
+int stylesheet_set(Stylesheet *ss, const char *path, const char *key,
+                    const char *new_val)
 {
-       FILE *fh;
-       struct ds_node *root;
-       struct ds_node *node;
-       StyleSheet *ss;
-       int i;
-
-       fh = fopen(filename, "r");
-       if ( fh == NULL ) return 1;
+       JsonObject *obj;
+       JsonNode *node = NULL;
+       int r = 1;
 
-       root = new_ds_node("root");
-       if ( root == NULL ) return 1;
-
-       if ( deserialize_file(fh, root) ) {
-               fclose(fh);
-               return 1;
-       }
-
-       fclose(fh);
-
-       node = find_node(root, "stylesheet", 0);
-       if ( node == NULL ) {
-               free_ds_tree(root);
-               return 1;
-       }
-       ss = tree_to_stylesheet(node);
        if ( ss == NULL ) {
-               fprintf(stderr, "Invalid style sheet\n");
+               fprintf(stderr, _("No stylesheet!\n"));
                return 1;
        }
-       free_ds_tree(root);
-
-       for ( i=0; i<p->num_slides; i++ ) {
-               fixup_styles(p->slides[i]->top, ss);
-               fixup_templates(p->slides[i], ss);
-       }
 
-       free_stylesheet(p->ss);
-       p->ss = ss;
+       obj = find_stylesheet_object(ss, path, &node);
+       if ( obj != NULL ) {
+               json_object_set_string_member(obj, key, new_val);
+               r = 0;
+       } /* else most likely the object (e.g. "$.slide", "$.slide.frame",
+          * "$.narrative" etc doesn't exist */
 
-       return 0;
+       if ( node != NULL ) json_node_unref(node);
+       return r;
 }
 
 
-StyleSheet *new_stylesheet()
+int stylesheet_delete(Stylesheet *ss, const char *path, const char *key)
 {
-       StyleSheet *ss;
-
-       ss = calloc(1, sizeof(struct _stylesheet));
-       if ( ss == NULL ) return NULL;
-
-       ss->n_styles = 0;
-       ss->styles = NULL;
-       ss->default_style = NULL;
-
-       return ss;
-}
-
-
-int save_stylesheet(StyleSheet *ss, const char *filename)
-{
-       FILE *fh;
-       struct serializer ser;
-
-       fh = fopen(filename, "w");
-       if ( fh == NULL ) return 1;
-
-       /* Set up the serializer */
-       ser.fh = fh;
-       ser.stack_depth = 0;
-       ser.prefix = NULL;
-
-       fprintf(fh, "# Colloquium style sheet file\n");
-       serialize_f(&ser, "version", 0.1);
-
-       serialize_start(&ser, "stylesheet");
-       write_stylesheet(ss, &ser);
-       serialize_end(&ser);
-
-       fclose(fh);
-
-       return 0;
-}
-
-
-StyleSheet *load_stylesheet(const char *filename)
-{
-       StyleSheet *ss;
-
-       ss = new_stylesheet();
-       if ( ss == NULL ) return NULL;
-
-       /* FIXME: Implement this */
-
-       return ss;
-}
-
-
-const char *units(LengthUnits un)
-{
-       switch ( un ) {
-               case UNITS_SLIDE : return "u";
-               case UNITS_FRAC : return "f";
-       }
-       return "annoyingly unspecified units";
-}
-
-
-static void serialize_f_units(struct serializer *s, const char *key, double val,
-                              LengthUnits un)
-{
-       char tmp[64];
-
-       snprintf(tmp, 63, "%.2f %s", val, units(un));
-       serialize_s(s, key, tmp);
-}
-
-
-void write_stylesheet(StyleSheet *ss, struct serializer *ser)
-{
-       int i;
-
-       if ( ss->default_style != NULL ) {
-               serialize_s(ser, "default_style", ss->default_style->pname);
-       }
-
-       serialize_start(ser, "styles");
-       for ( i=0; i<ss->n_styles; i++ ) {
-
-               struct style *s = ss->styles[i];
-
-               serialize_start(ser, s->pname);
-               serialize_s(ser, "name", s->name);
-               serialize_f(ser, "margin_l", s->lop.margin_l);
-               serialize_f(ser, "margin_r", s->lop.margin_r);
-               serialize_f(ser, "margin_t", s->lop.margin_t);
-               serialize_f(ser, "margin_b", s->lop.margin_b);
-               serialize_f(ser, "pad_l", s->lop.pad_l);
-               serialize_f(ser, "pad_r", s->lop.pad_r);
-               serialize_f(ser, "pad_t", s->lop.pad_t);
-               serialize_f(ser, "pad_b", s->lop.pad_b);
-               serialize_f_units(ser, "w", s->lop.w, s->lop.w_units);
-               serialize_f_units(ser, "h", s->lop.h, s->lop.h_units);
-               serialize_f(ser, "x", s->lop.x);
-               serialize_f(ser, "y", s->lop.y);
-               serialize_s(ser, "prologue", s->sc_prologue);
-               serialize_end(ser);
-
-       }
-       serialize_end(ser);
-
-       serialize_start(ser, "templates");
-       for ( i=0; i<ss->n_templates; i++ ) {
-
-               struct slide_template *t = ss->templates[i];
-               char id[32];
-               int j;
-
-               snprintf(id, 31, "%i", i);
-
-               serialize_start(ser, id);
-               serialize_s(ser, "name", t->name);
-               if (t->top_style != NULL ) {
-                       serialize_s(ser, "top_style", t->top_style->pname);
-               }
-               for ( j=0; j<t->n_styles; j++ ) {
-
-                       struct style *s = t->styles[j];
-                       char id[32];
-
-                       snprintf(id, 31, "sty%i", j);
-
-                       serialize_s(ser, id, s->pname);
-
-               }
-               serialize_end(ser);
+       JsonObject *obj;
+       JsonNode *node = NULL;
+       int r = 1;
 
+       if ( ss == NULL ) {
+               fprintf(stderr, _("No stylesheet!\n"));
+               return 1;
        }
-       serialize_end(ser);
-}
 
+       obj = find_stylesheet_object(ss, path, &node);
+       if ( obj != NULL ) {
+               json_object_remove_member(obj, key);
+               r = 0;
+       } /* else most likely the object (e.g. "$.slide", "$.slide.frame",
+          * "$.narrative" etc doesn't exist */
 
-struct style *default_style(StyleSheet *ss)
-{
-       return ss->default_style;
+       if ( node != NULL ) json_node_unref(node);
+       return r;
 }
 
 
-struct slide_template *new_template(StyleSheet *ss, const char *name)
+void stylesheet_free(Stylesheet *ss)
 {
-       struct slide_template *t;
-       int n;
-       struct slide_template **templates_new;
-
-       t = calloc(1, sizeof(*t));
-       if ( t == NULL ) return NULL;
-
-       t->name = strdup(name);
-
-       n = ss->n_templates;
-       templates_new = realloc(ss->templates, (n+1)*sizeof(t));
-       if ( templates_new == NULL ) {
-               free(t->name);
-               free(t);
-               return NULL;
-       }
-       ss->templates = templates_new;
-       ss->templates[n] = t;
-       ss->n_templates = n+1;
-
-       return t;
-}
-
-
-void add_to_template(struct slide_template *t, struct style *sty)
-{
-       int n;
-       struct style **styles_new;
-
-       n = t->n_styles;
-       styles_new = realloc(t->styles, (n+1)*sizeof(sty));
-       if ( styles_new == NULL ) {
-               fprintf(stderr, "Failed to add style '%s' to template '%s'.\n",
-                       sty->name, t->name);
-               return;
-       }
-       t->styles = styles_new;
-       t->styles[n] = sty;
-       t->n_styles = n+1;
+       g_object_unref(ss->root);
+       free(ss);
 }
 
 
-struct _styleiterator
-{
-       int n;
-};
-
-struct style *style_first(StyleSheet *ss, StyleIterator **piter)
+int stylesheet_save(Stylesheet *ss, GFile *file)
 {
-       StyleIterator *iter;
-
-       if ( ss->n_styles == 0 ) return NULL;
+       JsonGenerator *gen;
+       GError *error = NULL;
+       GFileOutputStream *fh;
 
-       iter = calloc(1, sizeof(StyleIterator));
-       if ( iter == NULL ) return NULL;
-
-       iter->n = 0;
-       *piter = iter;
-
-       return ss->styles[0];
-}
-
-
-struct style *style_next(StyleSheet *ss, StyleIterator *iter)
-{
-       iter->n++;
-       if ( iter->n == ss->n_styles ) {
-               free(iter);
-               return NULL;
+       fh = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error);
+       if ( fh == NULL ) {
+               fprintf(stderr, _("Open failed: %s\n"), error->message);
+               return 1;
        }
 
-       return ss->styles[iter->n];
-}
-
-
-struct _templateiterator
-{
-       int n;
-};
-
-struct slide_template *template_first(StyleSheet *ss, TemplateIterator **piter)
-{
-       TemplateIterator *iter;
-
-       if ( ss->n_templates == 0 ) return NULL;
-
-       iter = calloc(1, sizeof(TemplateIterator));
-       if ( iter == NULL ) return NULL;
-
-       iter->n = 0;
-       *piter = iter;
-
-       return ss->templates[0];
-}
-
-
-struct slide_template *template_next(StyleSheet *ss, TemplateIterator *iter)
-{
-       iter->n++;
-       if ( iter->n == ss->n_templates ) {
-               free(iter);
-               return NULL;
+       gen = json_generator_new();
+       json_generator_set_root(gen, ss->root);
+       json_generator_set_pretty(gen, TRUE);
+       json_generator_set_indent(gen, 1);
+       json_generator_set_indent_char(gen, '\t');
+       if ( !json_generator_to_stream(gen, G_OUTPUT_STREAM(fh), NULL, &error) ) {
+               fprintf(stderr, _("Open failed: %s\n"), error->message);
+               return 1;
        }
-
-       return ss->templates[iter->n];
+       g_object_unref(fh);
+       return 0;
 }