summaryrefslogtreecommitdiff
path: root/opt/opt-config.c
diff options
context:
space:
mode:
Diffstat (limited to 'opt/opt-config.c')
-rw-r--r--opt/opt-config.c759
1 files changed, 759 insertions, 0 deletions
diff --git a/opt/opt-config.c b/opt/opt-config.c
new file mode 100644
index 0000000..963ce12
--- /dev/null
+++ b/opt/opt-config.c
@@ -0,0 +1,759 @@
+#include "opt.h"
+#include <stdio.h> /* for scanf */
+#include <string.h> /* for strcmp */
+
+/*
+ <opt>
+ <defaults>
+ <bullet font="" color="">
+ <title font="" color="">
+ <offsets border="" title-spacing="" bullet-border="" bullet-spacing=""/>
+ <background img="" | color= "" />
+ <transition style="cube|flip|fade" />
+ </defualts>
+ <slide>
+ <background img="" | color= "" />
+ <title font="" color=""></title>
+ <bullet font="" color="" symbol="none"></bullet>
+ <img src="" />
+ <code width="xx"></code>
+ <transition style="cube|flip|fade" />
+ </slide>
+ <transition type=""/>
+ ....
+
+ </opt>
+ */
+
+typedef struct OptParseInfo OptParseInfo;
+
+typedef enum
+{
+ INITIAL,
+ IN_OPT,
+ IN_DEFAULTS,
+ IN_DEFAULTS_TITLE,
+ IN_DEFAULTS_BULLET,
+ IN_DEFAULTS_TRANS,
+ IN_DEFAULTS_BG,
+ IN_FONTS,
+ IN_OFFSETS,
+ IN_SLIDE,
+ IN_TITLE,
+ IN_BULLET,
+ IN_TRANS,
+ IN_IMG,
+ IN_BG,
+ FINAL
+}
+OptParseState;
+
+
+typedef enum
+{
+ TAG_UNKNOWN = 0,
+ TAG_OPT,
+ TAG_DEFAULTS,
+ TAG_DEFAULTS_TITLE,
+ TAG_DEFAULTS_BULLET,
+ TAG_DEFAULTS_TRANS,
+ TAG_DEFAULTS_BG,
+ TAG_SLIDE,
+ TAG_TITLE,
+ TAG_BULLET,
+ TAG_TRANS,
+ TAG_IMG,
+ TAG_BG
+
+} OptParseTag;
+
+const struct { gchar *name; OptTransitionStyle style; } _style_lookup[] =
+ {
+ { "cube", OPT_TRANSITION_CUBE },
+ { "page", OPT_TRANSITION_PAGE },
+ { "flip", OPT_TRANSITION_FLIP },
+ { "zoom", OPT_TRANSITION_ZOOM },
+ { "yzflip", OPT_TRANSITION_YZ_FLIP },
+ { "fade", OPT_TRANSITION_FADE },
+ { NULL, 0 }
+ };
+
+
+struct OptParseInfo
+{
+ OptShow *show;
+ OptParseState state;
+ OptSlide *slide;
+
+ GdkPixbuf *default_bg;
+
+ GString *title_buf;
+ gchar *title_font;
+ ClutterColor title_color;
+ ClutterColor title_default_color;
+
+ GString *bullet_buf;
+ gchar *bullet_font;
+ ClutterColor bullet_color;
+ ClutterColor bullet_default_color;
+ OptSlideBulletSymbol bullet_sym;
+
+ OptTransitionStyle style_default;
+};
+
+static void
+color_from_string (const gchar *spec, ClutterColor *color)
+{
+ if (spec[0] == '#' && strlen(spec) == 9)
+ {
+ guint32 result;
+ if (sscanf (spec+1, "%x", &result))
+ {
+ color->red = result >> 24 & 0xff;
+ color->green = (result >> 16) & 0xff;
+ color->blue = (result >> 8) & 0xff;
+ color->alpha = result & 0xff;
+ return;
+ }
+ }
+
+ g_warning("unable to parse '%s' as a color in format #RRGGBBAA", spec);
+}
+
+static OptTransitionStyle
+lookup_style (const gchar *name)
+{
+ gint i = 0;
+
+ while (_style_lookup[i].name != NULL)
+ {
+ if (!strcmp(name, _style_lookup[i].name))
+ return _style_lookup[i].style;
+
+ i++;
+ }
+
+ return OPT_TRANSITION_ANY;
+}
+
+static int
+expect_tag (GMarkupParseContext *context,
+ const gchar *actor_name,
+ GError **error,
+ ...)
+{
+ va_list vap;
+ const char *expected;
+ int n_expected = 0;
+
+ va_start (vap, error);
+ expected = va_arg (vap, const char *);
+ while (expected)
+ {
+ int value = va_arg (vap, int);
+ n_expected++;
+
+ if (strcmp (expected, actor_name) == 0)
+ return value;
+
+ expected = va_arg (vap, const char *);
+ }
+
+ va_end (vap);
+
+ if (n_expected == 0)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected tag '%s', no tags expected",
+ actor_name);
+ }
+ else
+ {
+ GString *tag_string = g_string_new (NULL);
+
+ va_start (vap, error);
+ expected = va_arg (vap, const char *);
+ while (expected)
+ {
+ va_arg (vap, int);
+
+ if (tag_string->len)
+ g_string_append (tag_string, ", ");
+ g_string_append (tag_string, expected);
+
+ expected = va_arg (vap, const char *);
+ }
+
+ va_end (vap);
+
+ if (n_expected == 1)
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected tag '%s', expected '%s'",
+ actor_name, tag_string->str);
+ else
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected tag '%s', expected one of: %s",
+ actor_name, tag_string->str);
+
+ g_string_free (tag_string, TRUE);
+ }
+
+ return 0;
+}
+
+static gboolean
+extract_attrs (GMarkupParseContext *context,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ GError **error,
+ ...)
+{
+ va_list vap;
+ const char *name;
+ gboolean *attr_map;
+ gboolean nattrs = 0;
+ int i;
+
+ for (i = 0; attribute_names[i]; i++)
+ nattrs++;
+
+ attr_map = g_new0 (gboolean, nattrs);
+
+ va_start (vap, error);
+ name = va_arg (vap, const char *);
+ while (name)
+ {
+ gboolean mandatory = va_arg (vap, gboolean);
+ const char **loc = va_arg (vap, const char **);
+ gboolean found = FALSE;
+
+ for (i = 0; attribute_names[i]; i++)
+ {
+ if (!attr_map[i] && strcmp (attribute_names[i], name) == 0)
+ {
+ if (found)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Duplicate attribute '%s'", name);
+ return FALSE;
+ }
+
+ *loc = attribute_values[i];
+ found = TRUE;
+ attr_map[i] = TRUE;
+ }
+ }
+
+ if (!found && mandatory)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Missing attribute '%s'", name);
+ return FALSE;
+ }
+
+ name = va_arg (vap, const char *);
+ }
+
+ for (i = 0; i < nattrs; i++)
+ if (!attr_map[i])
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+ "Unknown attribute '%s'", attribute_names[i]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static void
+opt_parse_on_start_actor (GMarkupParseContext *context,
+ const gchar *actor_name,
+ const gchar **attr_names,
+ const gchar **attr_values,
+ gpointer user_data,
+ GError **error)
+{
+ OptParseTag tag;
+ OptParseInfo *info = user_data;
+
+ switch (info->state)
+ {
+ case INITIAL:
+ if (expect_tag (context, actor_name, error, "opt", TAG_OPT, NULL)
+ && extract_attrs (context, attr_names, attr_values, error, NULL))
+ info->state = IN_OPT;
+ break;
+
+ /***** Top level, just defaults and slide *****/
+
+ case IN_OPT:
+ tag = expect_tag (context, actor_name, error,
+ "defaults", TAG_DEFAULTS,
+ "slide", TAG_SLIDE,
+ NULL);
+ switch (tag)
+ {
+ case TAG_DEFAULTS:
+ info->state = IN_DEFAULTS;
+ break;
+ case TAG_SLIDE:
+ {
+ OptTransition *trans;
+
+ info->state = IN_SLIDE;
+ info->slide = opt_slide_new (info->show);
+
+ g_object_set (info->show, "background", info->default_bg, NULL);
+
+ trans = opt_transition_new (info->style_default);
+ opt_transition_set_from (trans, info->slide);
+ opt_slide_set_transition (info->slide, trans);
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ /***** Default tags *****/
+
+ case IN_DEFAULTS:
+ tag = expect_tag (context, actor_name, error,
+ "title", TAG_DEFAULTS_TITLE,
+ "bullet", TAG_DEFAULTS_BULLET,
+ "transition", TAG_DEFAULTS_TRANS,
+ "background", TAG_DEFAULTS_BG,
+ NULL);
+ switch (tag)
+ {
+ case TAG_DEFAULTS_TRANS:
+ {
+ const char *style_str = NULL;
+
+ if (extract_attrs (context, attr_names, attr_values, error,
+ "style", FALSE, &style_str,
+ NULL))
+ {
+ info->style_default = lookup_style (style_str);
+ }
+ }
+ info->state = IN_DEFAULTS_TRANS;
+ break;
+ case TAG_DEFAULTS_TITLE:
+ {
+ const char *color = NULL;
+ const char *font = NULL;
+
+ if (extract_attrs (context, attr_names, attr_values, error,
+ "font", FALSE, &font,
+ "color", FALSE, &color,
+ NULL))
+ {
+ if (font)
+ g_object_set(info->show,
+ "title-font", font,
+ NULL);
+
+ if (color)
+ {
+ color_from_string (color, &info->title_default_color);
+ }
+ }
+ }
+ info->state = IN_DEFAULTS_TITLE;
+ break;
+
+ case TAG_DEFAULTS_BULLET:
+ {
+ const char *color = NULL;
+ const char *font = NULL;
+
+ if (extract_attrs (context, attr_names, attr_values, error,
+ "font", FALSE, &font,
+ "color", FALSE, &color,
+
+ NULL))
+ {
+ if (font)
+ g_object_set(info->show,
+ "bullet-font", font,
+ NULL);
+
+ if (color)
+ {
+ color_from_string (color, &info->bullet_default_color);
+
+ opt_show_set_bullet_color (info->show,
+ &info->bullet_default_color);
+ }
+ }
+ }
+ info->state = IN_DEFAULTS_BULLET;
+ break;
+ case TAG_DEFAULTS_BG:
+ {
+ const char *src = NULL;
+
+ if (extract_attrs (context, attr_names, attr_values, error,
+ "src", TRUE, &src,
+ NULL))
+ {
+ GdkPixbuf *pic;
+
+ pic = gdk_pixbuf_new_from_file_at_size (src,
+ CLUTTER_STAGE_WIDTH(),
+ CLUTTER_STAGE_HEIGHT(),
+ NULL);
+
+ if (pic == NULL)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unable to load '%s'", src);
+ }
+
+ info->default_bg = pic;
+ }
+ }
+ info->state = IN_DEFAULTS_BG;
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ break;
+
+ /***** Slide Tags *****/
+
+ case IN_SLIDE:
+ tag = expect_tag (context, actor_name, error,
+ "title", TAG_TITLE,
+ "bullet", TAG_BULLET,
+ "img", TAG_IMG,
+ "transition", TAG_TRANS,
+ "background", TAG_BG,
+ NULL);
+ switch (tag)
+ {
+ case TAG_BG:
+ {
+ const char *src = NULL;
+
+ if (extract_attrs (context, attr_names, attr_values, error,
+ "src", TRUE, &src,
+ NULL))
+ {
+ GdkPixbuf *pic = NULL;
+
+ pic = gdk_pixbuf_new_from_file_at_size (src,
+ CLUTTER_STAGE_WIDTH(),
+ CLUTTER_STAGE_HEIGHT(),
+ NULL);
+
+ if (pic == NULL)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unable to load '%s'", src);
+ }
+
+ opt_slide_set_background_pixbuf (info->slide, pic);
+
+ g_object_unref (pic);
+ }
+ info->state = IN_BG;
+ }
+ break;
+ case TAG_TRANS:
+ {
+ const char *style_str = NULL;
+
+ if (extract_attrs (context, attr_names, attr_values, error,
+ "style", TRUE, &style_str,
+ NULL))
+ {
+ OptTransitionStyle style;
+ OptTransition *trans;
+
+ style = lookup_style (style_str);
+
+ trans = opt_slide_get_transition (info->slide);
+ opt_transition_set_style (trans, style);
+ }
+ info->state = IN_TRANS;
+ }
+ break;
+ case TAG_IMG:
+ {
+ gchar *img_path = NULL;
+
+ if (extract_attrs (context, attr_names, attr_values, error,
+ "src", TRUE, &img_path,
+ NULL))
+ {
+ GdkPixbuf *pix = NULL;
+ ClutterActor *pic = NULL;
+
+ pix = gdk_pixbuf_new_from_file (img_path, NULL);
+
+ if (pix == NULL)
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unable to load '%s'", img_path);
+ }
+ else
+ {
+ pic = clutter_texture_new ();
+ clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (pic),
+ gdk_pixbuf_get_pixels (pix),
+ gdk_pixbuf_get_has_alpha (pix),
+ gdk_pixbuf_get_width (pix),
+ gdk_pixbuf_get_height (pix),
+ gdk_pixbuf_get_rowstride (pix),
+ 4, 0,
+ NULL);
+
+ opt_slide_add_bullet (info->slide, pic);
+ }
+ }
+ info->state = IN_IMG;
+ }
+ break;
+
+ case TAG_TITLE:
+ {
+ const char *color = NULL;
+ const char *font = NULL;
+
+ info->state = IN_TITLE;
+ info->title_buf = g_string_new("");
+ info->title_font = NULL;
+ info->title_color = info->title_default_color;
+
+ if (extract_attrs (context, attr_names, attr_values, error,
+ "font", FALSE, &font,
+ "color", FALSE, &color,
+ NULL))
+ {
+ if (font)
+ info->title_font = g_strdup(font);
+
+ if (color)
+ color_from_string(color, &info->title_color);
+
+ }
+ }
+ break;
+
+ case TAG_BULLET:
+ {
+ const char *color = NULL;
+ const char *font = NULL;
+ const char *sym = NULL;
+
+ info->state = IN_BULLET;
+ info->bullet_buf = g_string_new("");
+ info->bullet_font = NULL;
+ info->bullet_color = info->bullet_default_color;
+ info->bullet_sym = OPT_BULLET_REGULAR;
+
+ if (extract_attrs (context, attr_names, attr_values, error,
+ "font", FALSE, &font,
+ "color", FALSE, &color,
+ "symbol", FALSE, &sym,
+ NULL))
+ {
+ if (font)
+ info->bullet_font = g_strdup(font);
+
+ if (color)
+ color_from_string(color, &info->bullet_color);
+
+ if (sym && !strcmp(sym, "none"))
+ info->bullet_sym = OPT_BULLET_NONE;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+static void
+opt_parse_on_end_actor (GMarkupParseContext *context,
+ const gchar *actor_name,
+ gpointer user_data,
+ GError **error)
+{
+ OptParseInfo *info = user_data;
+
+ switch (info->state)
+ {
+ case INITIAL:
+ g_assert_not_reached ();
+ break;
+ case IN_OPT:
+ info->state = FINAL;
+ break;
+ case IN_SLIDE:
+ opt_show_add_slide (info->show, info->slide);
+ info->state = IN_OPT;
+ info->slide = NULL;
+ break;
+ case IN_DEFAULTS:
+ info->state = IN_OPT;
+ break;
+ case IN_DEFAULTS_TITLE:
+ case IN_DEFAULTS_BULLET:
+ case IN_DEFAULTS_TRANS:
+ case IN_DEFAULTS_BG:
+ info->state = IN_DEFAULTS;
+ break;
+ case IN_BG:
+ case IN_IMG:
+ info->state = IN_SLIDE;
+ break;
+ case IN_TITLE:
+ opt_slide_set_title (info->slide,
+ info->title_buf->str,
+ info->title_font,
+ &info->title_color);
+ g_string_free (info->title_buf, TRUE);
+
+ if (info->title_font)
+ g_free (info->title_font);
+ info->title_font = NULL;
+ info->bullet_buf = NULL;
+ info->state = IN_SLIDE;
+ break;
+ case IN_BULLET:
+ opt_slide_add_bullet_text_item (info->slide,
+ info->bullet_buf->str,
+ info->bullet_font,
+ info->bullet_sym,
+ &info->bullet_color);
+ g_string_free (info->bullet_buf, TRUE);
+ if (info->bullet_font)
+ g_free (info->bullet_font);
+ info->bullet_font = NULL;
+ info->bullet_buf = NULL;
+ info->state = IN_SLIDE;
+ break;
+ case IN_TRANS:
+ info->state = IN_SLIDE;
+ break;
+ case FINAL:
+ g_assert_not_reached ();
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void
+opt_parse_on_text (GMarkupParseContext *context,
+ const gchar *text,
+ gsize text_len,
+ gpointer user_data,
+ GError **error)
+{
+ int i;
+ OptParseInfo *info = user_data;
+
+ switch (info->state)
+ {
+ case IN_TITLE:
+ g_string_append_len (info->title_buf, text, text_len);
+ break;
+ case IN_BULLET:
+ g_string_append_len (info->bullet_buf, text, text_len);
+ break;
+ case INITIAL:
+ case IN_IMG:
+ case IN_OPT:
+ case IN_DEFAULTS:
+ case IN_DEFAULTS_TITLE:
+ case IN_DEFAULTS_BULLET:
+ case IN_DEFAULTS_TRANS:
+ case IN_DEFAULTS_BG:
+ case IN_FONTS:
+ case IN_OFFSETS:
+ case IN_SLIDE:
+ case IN_BG:
+ case IN_TRANS:
+ case FINAL:
+ for (i = 0; i < text_len; i++)
+ if (!g_ascii_isspace (text[i]))
+ {
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "Unexpected text '%s' in presentation file",
+ text);
+ return;
+ }
+ break;
+ }
+}
+
+
+gboolean
+opt_config_load (OptShow *show,
+ const gchar *filename,
+ GError **error)
+{
+ GMarkupParseContext *context;
+ OptParseInfo info;
+ char *contents;
+ gsize len;
+ gboolean result;
+
+ const GMarkupParser parser =
+ {
+ opt_parse_on_start_actor,
+ opt_parse_on_end_actor,
+ opt_parse_on_text,
+ NULL,
+ NULL
+ };
+
+ memset (&info, 0, sizeof(OptParseInfo));
+
+ info.state = INITIAL;
+ info.show = show;
+ /*
+ info.bullet_default_color = { 0, 0, 0, 0xff };
+ info.title_default_color = { 0, 0, 0, 0xff };
+ */
+ info.style_default = OPT_TRANSITION_FADE;
+
+ if (!g_file_get_contents (filename, &contents, &len, error))
+ return FALSE;
+
+ context = g_markup_parse_context_new (&parser, 0, &info, NULL);
+ result = g_markup_parse_context_parse (context, contents, len, error);
+
+ return result;
+}