diff options
Diffstat (limited to 'lib/egg/egg-markup.c')
-rw-r--r-- | lib/egg/egg-markup.c | 438 |
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; +} |