#include #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; }