aboutsummaryrefslogtreecommitdiffstats
path: root/lib/egg/egg-markup.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/egg/egg-markup.c')
-rw-r--r--lib/egg/egg-markup.c438
1 files changed, 438 insertions, 0 deletions
diff --git a/lib/egg/egg-markup.c b/lib/egg/egg-markup.c
new file mode 100644
index 000000000..feae85a00
--- /dev/null
+++ b/lib/egg/egg-markup.c
@@ -0,0 +1,438 @@
+#include <string.h>
+#include "egg-markup.h"
+#include "eggtoolbar.h"
+
+#ifndef _
+# define _(String) (String)
+# define N_(String) (String)
+#endif
+
+typedef enum {
+ STATE_START,
+ STATE_ROOT,
+ STATE_MENU,
+ STATE_TOOLBAR,
+ STATE_POPUPS,
+ STATE_ITEM,
+ STATE_END
+} ParseState;
+
+typedef struct _ParseContext ParseContext;
+struct _ParseContext
+{
+ /* parser state information */
+ ParseState state;
+ ParseState prev_state;
+
+ /* function to call when we finish off a toplevel widget */
+ EggWidgetFunc widget_func;
+ gpointer user_data;
+
+ /* GdkAccelGroup to use for menus */
+ GtkAccelGroup *accel_group;
+
+ /* info about the widget we are constructing at the moment */
+ GtkWidget *top;
+ gchar *type;
+ gchar *name;
+
+ /* the current container we are working on */
+ GtkWidget *current;
+
+ /* the ActionGroup used to create menu items */
+ EggActionGroup *action_group;
+};
+
+static void
+start_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ const gchar **attribute_names,
+ const gchar **attribute_values,
+ gpointer user_data,
+ GError **error)
+{
+ ParseContext *ctx = user_data;
+ gboolean raise_error = TRUE;
+ gchar *error_attr = NULL;
+
+ switch (element_name[0])
+ {
+ case 'R':
+ if (ctx->state == STATE_START && !strcmp(element_name, "Root"))
+ {
+ ctx->state = STATE_ROOT;
+ raise_error = FALSE;
+ }
+ break;
+ case 'm':
+ if (ctx->state == STATE_ROOT && !strcmp(element_name, "menu"))
+ {
+ ctx->state = STATE_MENU;
+
+ ctx->top = ctx->current = gtk_menu_bar_new();
+ ctx->type = "menu";
+ ctx->name = NULL;
+
+ raise_error = FALSE;
+ }
+ else if (ctx->state == STATE_MENU && !strcmp(element_name, "menuitem"))
+ {
+ gint i;
+ const gchar *action_name = NULL;
+ EggAction *action = NULL;
+
+ ctx->state = STATE_ITEM;
+
+ for (i = 0; attribute_names[i] != NULL; i++)
+ {
+ if (!strcmp(attribute_names[i], "verb"))
+ {
+ action_name = attribute_values[i];
+ action = egg_action_group_get_action(ctx->action_group,
+ action_name);
+ }
+ }
+
+ if (action)
+ {
+ GtkWidget *widget = egg_action_create_menu_item(action);
+
+ gtk_container_add(GTK_CONTAINER(ctx->current), widget);
+ gtk_widget_show(widget);
+ }
+ else
+ {
+ g_warning("could not find action '%s'",
+ action_name ? action_name : "(null)");
+ }
+
+ raise_error = FALSE;
+ }
+ break;
+ case 'd':
+ if (ctx->state == STATE_ROOT && !strcmp(element_name, "dockitem"))
+ {
+ gint i;
+
+ ctx->state = STATE_TOOLBAR;
+
+ ctx->top = ctx->current = egg_toolbar_new();
+ ctx->type = "toolbar";
+ for (i = 0; attribute_names[i] != NULL; i++)
+ {
+ if (!strcmp(attribute_names[i], "name"))
+ ctx->name = g_strdup(attribute_values[i]);
+ }
+
+ raise_error = FALSE;
+ }
+ break;
+ case 'p':
+ if (ctx->state == STATE_ROOT && !strcmp(element_name, "popups"))
+ {
+ ctx->state = STATE_POPUPS;
+ raise_error = FALSE;
+ }
+ else if (ctx->state == STATE_POPUPS &&!strcmp(element_name, "popup"))
+ {
+ gint i;
+
+ ctx->state = STATE_MENU;
+
+ ctx->top = ctx->current = gtk_menu_new();
+ gtk_menu_set_accel_group(GTK_MENU(ctx->current), ctx->accel_group);
+ ctx->type = "popup";
+ for (i = 0; attribute_names[i] != NULL; i++)
+ {
+ if (!strcmp(attribute_names[i], "name"))
+ {
+ ctx->name = g_strdup(attribute_values[i]);
+ gtk_menu_set_title(GTK_MENU(ctx->current), ctx->name);
+ }
+ else if (!strcmp(attribute_names[i], "tearoff"))
+ {
+ GtkWidget *tearoff = gtk_tearoff_menu_item_new();
+
+ gtk_container_add(GTK_CONTAINER(ctx->current), tearoff);
+ gtk_widget_show(tearoff);
+ }
+ }
+
+ raise_error = FALSE;
+ }
+ break;
+ case 's':
+ if (ctx->state == STATE_MENU && !strcmp(element_name, "submenu"))
+ {
+ gint i;
+ const gchar *label = NULL;
+ gboolean tearoff = FALSE;
+ GtkWidget *widget;
+
+ ctx->state = STATE_MENU;
+ for (i = 0; attribute_names[i] != NULL; i++)
+ {
+ if (!strcmp(attribute_names[i], "label"))
+ label = g_strdup(attribute_values[i]);
+ else if (!strcmp(attribute_names[i], "tearoff"))
+ tearoff = TRUE;
+ }
+ widget = gtk_menu_item_new_with_label(label);
+ gtk_label_set_use_underline(GTK_LABEL(GTK_BIN(widget)->child), TRUE);
+ gtk_container_add(GTK_CONTAINER(ctx->current), widget);
+ gtk_widget_show(widget);
+
+ ctx->current = gtk_menu_new();
+ gtk_menu_set_accel_group(GTK_MENU(ctx->current), ctx->accel_group);
+ gtk_menu_set_title(GTK_MENU(ctx->current), label);
+ gtk_menu_item_set_submenu(GTK_MENU_ITEM(widget), ctx->current);
+
+ if (tearoff)
+ {
+ GtkWidget *tearoff = gtk_tearoff_menu_item_new();
+
+ gtk_container_add(GTK_CONTAINER(ctx->current), tearoff);
+ gtk_widget_show(tearoff);
+ }
+
+ raise_error = FALSE;
+ }
+ else if ((ctx->state == STATE_MENU || ctx->state == STATE_TOOLBAR) &&
+ !strcmp(element_name, "separator"))
+ {
+ ctx->state = STATE_ITEM;
+
+ if (GTK_IS_MENU_SHELL(ctx->current))
+ {
+ GtkWidget *widget = gtk_separator_menu_item_new();
+ gtk_container_add(GTK_CONTAINER(ctx->current), widget);
+ gtk_widget_show(widget);
+ }
+ else /* toolbar */
+ {
+ EggToolItem *item = egg_tool_item_new ();
+ egg_toolbar_insert_tool_item (EGG_TOOLBAR(ctx->current), item, -1);
+ gtk_widget_show (GTK_WIDGET (item));
+ }
+
+ raise_error = FALSE;
+ }
+ break;
+ case 't':
+ if (ctx->state == STATE_TOOLBAR && !strcmp(element_name, "toolitem"))
+ {
+ gint i;
+ const gchar *action_name = NULL;
+ EggAction *action = NULL;
+
+ ctx->state = STATE_ITEM;
+
+ for (i = 0; attribute_names[i] != NULL; i++)
+ {
+ if (!strcmp(attribute_names[i], "verb"))
+ {
+ action_name = attribute_values[i];
+ action = egg_action_group_get_action(ctx->action_group,
+ action_name);
+ }
+ }
+
+ if (action)
+ {
+ GtkWidget *widget = egg_action_create_tool_item (action);
+
+ gtk_container_add (GTK_CONTAINER (ctx->current), widget);
+ }
+ else
+ {
+ g_warning("could not find action '%s'",
+ action_name ? action_name : "(null)");
+ }
+
+ raise_error = FALSE;
+ }
+ break;
+ };
+
+ if (raise_error)
+ {
+ gint line_number, char_number;
+
+ g_markup_parse_context_get_position (context,
+ &line_number, &char_number);
+
+ if (error_attr)
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
+ _("Unknown attribute '%s' on line %d char %d"),
+ error_attr,
+ line_number, char_number);
+ else
+ g_set_error (error,
+ G_MARKUP_ERROR,
+ G_MARKUP_ERROR_UNKNOWN_ELEMENT,
+ _("Unknown tag '%s' on line %d char %d"),
+ element_name,
+ line_number, char_number);
+ }
+}
+
+static void
+end_element_handler (GMarkupParseContext *context,
+ const gchar *element_name,
+ gpointer user_data,
+ GError **error)
+{
+ ParseContext *ctx = user_data;
+ GtkWidget *widget;
+
+ switch (ctx->state)
+ {
+ case STATE_START:
+ g_warning("shouldn't get any end tags at this point");
+ /* should do a GError here */
+ break;
+ case STATE_ROOT:
+ ctx->state = STATE_END;
+ break;
+ case STATE_MENU:
+ widget = GTK_IS_MENU(ctx->current) ?
+ gtk_menu_get_attach_widget(GTK_MENU(ctx->current)) : NULL;
+ if (widget) /* not back to the toplevel ... */
+ {
+ ctx->current = widget->parent;
+ ctx->state = STATE_MENU;
+ }
+ else
+ {
+ if (GTK_IS_MENU(ctx->current)) /* must be a popup */
+ ctx->state = STATE_POPUPS;
+ else
+ ctx->state = STATE_ROOT;
+
+ /* notify */
+ (* ctx->widget_func)(ctx->top, ctx->type, ctx->name, ctx->user_data);
+ ctx->top = NULL;
+ ctx->type = NULL;
+ g_free(ctx->name);
+ ctx->name = NULL;
+ ctx->current = NULL;
+ }
+ break;
+ case STATE_TOOLBAR:
+ ctx->state = STATE_ROOT;
+
+ /* notify */
+ (* ctx->widget_func)(ctx->top, ctx->type, ctx->name, ctx->user_data);
+ ctx->top = NULL;
+ ctx->type = NULL;
+ g_free(ctx->name);
+ ctx->name = NULL;
+ ctx->current = NULL;
+ break;
+ case STATE_POPUPS:
+ ctx->state = STATE_ROOT;
+ break;
+ case STATE_ITEM:
+ if (GTK_IS_MENU_SHELL(ctx->current))
+ ctx->state = STATE_MENU;
+ else
+ ctx->state = STATE_TOOLBAR;
+ break;
+ case STATE_END:
+ g_warning("shouldn't get any end tags at this point");
+ /* should do a GError here */
+ break;
+ }
+}
+
+static void
+cleanup (GMarkupParseContext *context,
+ GError *error,
+ gpointer user_data)
+{
+ ParseContext *ctx = user_data;
+
+ gtk_widget_destroy(ctx->top);
+ ctx->top = NULL;
+ ctx->type = NULL;
+ g_free(ctx->name);
+ ctx->name = NULL;
+ ctx->current = NULL;
+}
+
+
+static GMarkupParser ui_parser = {
+ start_element_handler,
+ end_element_handler,
+ NULL,
+ NULL,
+ cleanup
+};
+
+
+gboolean
+egg_create_from_string (EggActionGroup *action_group,
+ EggWidgetFunc widget_func, gpointer user_data,
+ GtkAccelGroup *accel_group,
+ const gchar *buffer, guint length,
+ GError **error)
+{
+ ParseContext ctx = { 0 };
+ GMarkupParseContext *context;
+ gboolean res = TRUE;
+
+ g_return_val_if_fail(EGG_IS_ACTION_GROUP(action_group), FALSE);
+ g_return_val_if_fail(widget_func != NULL, FALSE);
+ g_return_val_if_fail(GTK_IS_ACCEL_GROUP(accel_group), FALSE);
+ g_return_val_if_fail(buffer != NULL, FALSE);
+
+ ctx.state = STATE_START;
+ ctx.widget_func = widget_func;
+ ctx.user_data = user_data;
+ ctx.accel_group = accel_group;
+ ctx.top = NULL;
+ ctx.type = NULL;
+ ctx.name = NULL;
+ ctx.current = NULL;
+ ctx.action_group = action_group;
+
+ context = g_markup_parse_context_new(&ui_parser, 0, &ctx, NULL);
+ if (length < 0)
+ length = strlen(buffer);
+
+ if (g_markup_parse_context_parse(context, buffer, length, error))
+ {
+ if (!g_markup_parse_context_end_parse(context, error))
+ res = FALSE;
+ }
+ else
+ res = FALSE;
+
+ g_markup_parse_context_free (context);
+
+ return res;
+}
+
+gboolean
+egg_create_from_file (EggActionGroup *action_group,
+ EggWidgetFunc widget_func,
+ gpointer user_data,
+ GtkAccelGroup *accel_group,
+ const gchar *filename,
+ GError **error)
+{
+ gchar *buffer;
+ gint length;
+ gboolean res;
+
+ if (!g_file_get_contents (filename, &buffer, &length, error))
+ return FALSE;
+
+ res = egg_create_from_string(action_group, widget_func, user_data,
+ accel_group, buffer, length, error);
+ g_free(buffer);
+
+ return res;
+}