summaryrefslogtreecommitdiff
path: root/tools/glade/glade/glade-parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/glade/glade/glade-parser.c')
-rw-r--r--tools/glade/glade/glade-parser.c1415
1 files changed, 1415 insertions, 0 deletions
diff --git a/tools/glade/glade/glade-parser.c b/tools/glade/glade/glade-parser.c
new file mode 100644
index 00000000..cb6dee60
--- /dev/null
+++ b/tools/glade/glade/glade-parser.c
@@ -0,0 +1,1415 @@
+/* -*- Mode: C; c-basic-offset: 4 -*- */
+/*
+ * Glade - a GTK+ User Interface Builder
+ * Copyright (C) 1998 Damon Chaplin
+ *
+ * glade-parser.c: functions for parsing glade-2.0 files
+ * Copyright (C) 1998-2002 James Henstridge <james@daa.com.au>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * This file comes from libglade. Though I've had to make a few minor changes
+ * to use it in Glade. Try to keep the versions in sync as much as possible.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* GLADE: We don't want the strings translated in Glade. */
+/*#ifdef ENABLE_NLS*/
+#if 0
+# include <libintl.h>
+#else
+# define textdomain(String) (String)
+# define dgettext(Domain, String) (String)
+#endif
+
+#include <libxml/parser.h>
+
+#include "glade-parser.h"
+
+typedef enum {
+ PARSER_START,
+ PARSER_GLADE_INTERFACE,
+ PARSER_REQUIRES,
+ PARSER_WIDGET,
+ PARSER_WIDGET_PROPERTY,
+ PARSER_WIDGET_ATK,
+ PARSER_WIDGET_ATK_PROPERTY,
+ PARSER_WIDGET_ATK_ACTION,
+ PARSER_WIDGET_ATK_RELATION,
+ PARSER_WIDGET_AFTER_ATK,
+ PARSER_WIDGET_SIGNAL,
+ PARSER_WIDGET_AFTER_SIGNAL,
+ PARSER_WIDGET_ACCEL,
+ PARSER_WIDGET_AFTER_ACCEL,
+ PARSER_WIDGET_CHILD,
+ PARSER_WIDGET_CHILD_AFTER_WIDGET,
+ PARSER_WIDGET_CHILD_PACKING,
+ PARSER_WIDGET_CHILD_PACKING_PROPERTY,
+ PARSER_WIDGET_CHILD_AFTER_PACKING,
+ PARSER_WIDGET_CHILD_PLACEHOLDER,
+ PARSER_WIDGET_CHILD_AFTER_PLACEHOLDER,
+ PARSER_FINISH,
+ PARSER_UNKNOWN
+} ParserState;
+
+typedef struct _GladeParseState GladeParseState;
+struct _GladeParseState {
+ ParserState state;
+
+ const gchar *domain;
+
+ guint unknown_depth; /* handle recursive unrecognised tags */
+ ParserState prev_state; /* the last `known' state we were in */
+
+ guint widget_depth;
+ GString *content;
+
+ GladeInterface *interface;
+ GladeWidgetInfo *widget;
+
+ enum {PROP_NONE, PROP_WIDGET, PROP_ATK, PROP_CHILD } prop_type;
+ gchar *prop_name;
+ gchar *prop_agent;
+ gboolean translate_prop;
+ gchar *prop_translator_comments;
+ gboolean prop_context_prefix;
+ GArray *props;
+
+ GArray *signals;
+ GArray *atk_actions;
+ GArray *relations;
+ GArray *accels;
+};
+
+static gchar *
+alloc_string(GladeInterface *interface, const gchar *string)
+{
+ gchar *s;
+
+ s = g_hash_table_lookup(interface->strings, string);
+ if (!s) {
+ s = g_strdup(string);
+ g_hash_table_insert(interface->strings, s, s);
+ }
+ return s;
+}
+
+static GladeWidgetInfo *
+create_widget_info(GladeInterface *interface, const xmlChar **attrs)
+{
+ GladeWidgetInfo *info = g_new0(GladeWidgetInfo, 1);
+ int i;
+
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "class"))
+ info->class = alloc_string(interface, attrs[i+1]);
+ else if (!strcmp(attrs[i], "id"))
+ info->name = alloc_string(interface, attrs[i+1]);
+ else
+ g_warning("unknown attribute `%s' for <widget>.", attrs[i]);
+ }
+ if (info->class == NULL || info->name == NULL)
+ g_warning("<widget> element missing required attributes!");
+ if (info->name)
+ g_hash_table_insert(interface->names, info->name, info);
+ return info;
+}
+
+static inline void
+flush_properties(GladeParseState *state)
+{
+ if (state->props == NULL)
+ return;
+ switch (state->prop_type) {
+ case PROP_NONE:
+ break;
+ case PROP_WIDGET:
+ if (state->widget->properties)
+ g_warning("we already read all the props for this key. Leaking");
+ state->widget->properties = (GladeProperty *)state->props->data;
+ state->widget->n_properties = state->props->len;
+ g_array_free(state->props, FALSE);
+ break;
+ case PROP_ATK:
+ if (state->widget->atk_props)
+ g_warning("we already read all the ATK props for this key. Leaking");
+ state->widget->atk_props = (GladeProperty *)state->props->data;
+ state->widget->n_atk_props = state->props->len;
+ g_array_free(state->props, FALSE);
+ break;
+ case PROP_CHILD:
+ if (state->widget->n_children == 0) {
+ g_warning("no children, but have child properties!");
+ g_array_free(state->props, TRUE);
+ } else {
+ GladeChildInfo *info = &state->widget->children[
+ state->widget->n_children-1];
+ if (info->properties)
+ g_warning("we already read all the child props for this key. Leaking");
+ info->properties = (GladeProperty *)state->props->data;
+ info->n_properties = state->props->len;
+ g_array_free(state->props, FALSE);
+ }
+ break;
+ }
+ state->prop_type = PROP_NONE;
+ state->prop_name = NULL;
+ state->prop_agent = NULL;
+ state->prop_translator_comments = NULL;
+ state->prop_context_prefix = FALSE;
+ state->props = NULL;
+}
+
+static inline void
+flush_signals(GladeParseState *state)
+{
+ if (state->signals) {
+ state->widget->signals = (GladeSignalInfo *)state->signals->data;
+ state->widget->n_signals = state->signals->len;
+ g_array_free(state->signals, FALSE);
+ }
+ state->signals = NULL;
+}
+
+static inline void
+flush_actions(GladeParseState *state)
+{
+ if (state->atk_actions) {
+ state->widget->atk_actions = (GladeAtkActionInfo *)state->atk_actions->data;
+ state->widget->n_atk_actions = state->atk_actions->len;
+ g_array_free(state->atk_actions, FALSE);
+ }
+ state->atk_actions = NULL;
+}
+
+static inline void
+flush_relations(GladeParseState *state)
+{
+ if (state->relations) {
+ state->widget->relations = (GladeAtkRelationInfo *)state->relations->data;
+ state->widget->n_relations = state->relations->len;
+ g_array_free(state->relations, FALSE);
+ }
+ state->relations = NULL;
+}
+
+static inline void
+flush_accels(GladeParseState *state)
+{
+ if (state->accels) {
+ state->widget->accels = (GladeAccelInfo *)state->accels->data;
+ state->widget->n_accels = state->accels->len;
+ g_array_free(state->accels, FALSE);
+ }
+ state->accels = NULL;
+}
+
+static inline void
+handle_atk_action(GladeParseState *state, const xmlChar **attrs)
+{
+ gint i;
+ GladeAtkActionInfo info = { 0 };
+
+ flush_properties(state);
+
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "action_name"))
+ info.action_name = alloc_string(state->interface, attrs[i+1]);
+ else if (!strcmp(attrs[i], "description"))
+ info.description = alloc_string(state->interface, attrs[i+1]);
+ else
+ g_warning("unknown attribute `%s' for <action>.", attrs[i]);
+ }
+ if (info.action_name == NULL) {
+ g_warning("required <atkaction> attribute 'action_name' missing!!!");
+ return;
+ }
+ if (!state->atk_actions)
+ state->atk_actions = g_array_new(FALSE, FALSE,
+ sizeof(GladeAtkActionInfo));
+ g_array_append_val(state->atk_actions, info);
+}
+
+static inline void
+handle_atk_relation(GladeParseState *state, const xmlChar **attrs)
+{
+ gint i;
+ GladeAtkRelationInfo info = { 0 };
+
+ flush_properties(state);
+
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "target"))
+ info.target = alloc_string(state->interface, attrs[i+1]);
+ else if (!strcmp(attrs[i], "type"))
+ info.type = alloc_string(state->interface, attrs[i+1]);
+ else
+ g_warning("unknown attribute `%s' for <signal>.", attrs[i]);
+ }
+ if (info.target == NULL || info.type == NULL) {
+ g_warning("required <atkrelation> attributes ('target' and/or 'type') missing!!!");
+ return;
+ }
+ if (!state->relations)
+ state->relations = g_array_new(FALSE, FALSE,
+ sizeof(GladeAtkRelationInfo));
+ g_array_append_val(state->relations, info);
+}
+
+static inline void
+handle_signal(GladeParseState *state, const xmlChar **attrs)
+{
+ GladeSignalInfo info = { 0 };
+ gint i;
+
+ flush_properties(state);
+
+ info.after = FALSE;
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "name"))
+ info.name = alloc_string(state->interface, attrs[i+1]);
+ else if (!strcmp(attrs[i], "handler"))
+ info.handler = alloc_string(state->interface, attrs[i+1]);
+ else if (!strcmp(attrs[i], "after"))
+ info.after = attrs[i+1][0] == 'y';
+ else if (!strcmp(attrs[i], "object"))
+ info.object = alloc_string(state->interface, attrs[i+1]);
+ else if (!strcmp(attrs[i], "last_modification_time"))
+ info.last_modification_time = alloc_string(state->interface, attrs[i+1]);
+ else
+ g_warning("unknown attribute `%s' for <signal>.", attrs[i]);
+ }
+ if (info.name == NULL || info.handler == NULL) {
+ g_warning("required <signal> attributes missing!!!");
+ return;
+ }
+ if (!state->signals)
+ state->signals = g_array_new(FALSE, FALSE,
+ sizeof(GladeSignalInfo));
+ g_array_append_val(state->signals, info);
+}
+
+static inline void
+handle_accel(GladeParseState *state, const xmlChar **attrs)
+{
+ GladeAccelInfo info = { 0 };
+ gint i;
+
+ flush_properties(state);
+ flush_signals(state);
+ flush_actions(state);
+ flush_relations(state);
+
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "key"))
+ info.key = gdk_keyval_from_name(attrs[i+1]);
+ else if (!strcmp(attrs[i], "modifiers")) {
+ const xmlChar *pos = attrs[i+1];
+
+ info.modifiers = 0;
+ while (pos[0])
+ if (!strncmp(pos, "GDK_", 4)) {
+ pos += 4;
+ if (!strncmp(pos, "SHIFT_MASK", 10)) {
+ info.modifiers |= GDK_SHIFT_MASK;
+ pos += 10;
+ } else if (!strncmp(pos, "LOCK_MASK", 9)) {
+ info.modifiers |= GDK_LOCK_MASK;
+ pos += 9;
+ } else if (!strncmp(pos, "CONTROL_MASK", 12)) {
+ info.modifiers |= GDK_CONTROL_MASK;
+ pos += 12;
+ } else if (!strncmp(pos, "MOD", 3) &&
+ !strncmp(pos+4, "_MASK", 5)) {
+ switch (pos[3]) {
+ case '1':
+ info.modifiers |= GDK_MOD1_MASK; break;
+ case '2':
+ info.modifiers |= GDK_MOD2_MASK; break;
+ case '3':
+ info.modifiers |= GDK_MOD3_MASK; break;
+ case '4':
+ info.modifiers |= GDK_MOD4_MASK; break;
+ case '5':
+ info.modifiers |= GDK_MOD5_MASK; break;
+ }
+ pos += 9;
+ } else if (!strncmp(pos, "BUTTON", 6) &&
+ !strncmp(pos+7, "_MASK", 5)) {
+ switch (pos[6]) {
+ case '1':
+ info.modifiers |= GDK_BUTTON1_MASK; break;
+ case '2':
+ info.modifiers |= GDK_BUTTON2_MASK; break;
+ case '3':
+ info.modifiers |= GDK_BUTTON3_MASK; break;
+ case '4':
+ info.modifiers |= GDK_BUTTON4_MASK; break;
+ case '5':
+ info.modifiers |= GDK_BUTTON5_MASK; break;
+ }
+ pos += 12;
+ } else if (!strncmp(pos, "RELEASE_MASK", 12)) {
+ info.modifiers |= GDK_RELEASE_MASK;
+ pos += 12;
+ } else
+ pos++;
+ } else
+ pos++;
+ } else if (!strcmp(attrs[i], "signal"))
+ info.signal = alloc_string(state->interface, attrs[i+1]);
+ else
+ g_warning("unknown attribute `%s' for <accelerator>.", attrs[i]);
+ }
+ if (info.key == 0 || info.signal == NULL) {
+ g_warning("required <accelerator> attributes missing!!!");
+ return;
+ }
+ if (!state->accels)
+ state->accels = g_array_new(FALSE, FALSE,
+ sizeof(GladeAccelInfo));
+ g_array_append_val(state->accels, info);
+}
+
+static inline void
+handle_child(GladeParseState *state, const xmlChar **attrs)
+{
+ GladeChildInfo *info;
+ gint i;
+
+ /* make sure all of these are flushed */
+ flush_properties(state);
+ flush_signals(state);
+ flush_actions(state);
+ flush_relations(state);
+ flush_accels(state);
+
+ state->widget->n_children++;
+ state->widget->children = g_renew(GladeChildInfo, state->widget->children,
+ state->widget->n_children);
+ info = &state->widget->children[state->widget->n_children-1];
+ info->internal_child = NULL;
+ info->properties = NULL;
+ info->n_properties = 0;
+ info->child = NULL;
+
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "internal-child"))
+ info->internal_child = alloc_string(state->interface, attrs[i+1]);
+ else
+ g_warning("unknown attribute `%s' for <child>.", attrs[i]);
+ }
+}
+
+static void
+glade_parser_start_document(GladeParseState *state)
+{
+ state->state = PARSER_START;
+
+ state->unknown_depth = 0;
+ state->prev_state = PARSER_UNKNOWN;
+
+ state->widget_depth = 0;
+ state->content = g_string_sized_new(128);
+
+ state->interface = g_new0(GladeInterface, 1);
+ state->interface->names = g_hash_table_new(g_str_hash, g_str_equal);
+ state->interface->strings = g_hash_table_new_full(g_str_hash,
+ g_str_equal,
+ (GDestroyNotify)g_free,
+ NULL);
+ state->widget = NULL;
+
+ state->prop_type = PROP_NONE;
+ state->prop_name = NULL;
+ state->prop_agent = NULL;
+ state->translate_prop = FALSE;
+ state->prop_translator_comments = NULL;
+ state->prop_context_prefix = FALSE;
+ state->props = NULL;
+
+ state->signals = NULL;
+ state->accels = NULL;
+}
+
+static void
+glade_parser_end_document(GladeParseState *state)
+{
+ g_string_free(state->content, TRUE);
+
+ if (state->unknown_depth != 0)
+ g_warning("unknown_depth != 0 (%d)", state->unknown_depth);
+ if (state->widget_depth != 0)
+ g_warning("widget_depth != 0 (%d)", state->widget_depth);
+}
+
+static void
+glade_parser_start_element(GladeParseState *state,
+ const xmlChar *name, const xmlChar **attrs)
+{
+ int i;
+
+ switch (state->state) {
+ case PARSER_START:
+ if (!strcmp(name, "glade-interface")) {
+ state->state = PARSER_GLADE_INTERFACE;
+#if 0
+ /* check for correct XML namespace */
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "xmlns") &&
+ !strcmp(attrs[i+1], "...")) {
+ g_warning("bad XML namespace `%s'.", attrs[i+1]);
+ } else
+ g_warning("unknown attribute `%s' for <glade-interface>",
+ attrs[i]);
+ }
+#endif
+ } else {
+ g_warning("Expected <glade-interface>. Got <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_GLADE_INTERFACE:
+ if (!strcmp(name, "requires")) {
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "lib")) {
+ GladeInterface *iface = state->interface;
+
+ /* add to the list of requirements for this module */
+ iface->n_requires++;
+ iface->requires = g_renew(gchar *, iface->requires,
+ iface->n_requires);
+ iface->requires[iface->n_requires-1] =
+ alloc_string(iface, attrs[i+1]);
+ } else
+ g_warning("unknown attribute `%s' for <requires>.",
+ attrs[i]);
+ }
+ state->state = PARSER_REQUIRES;
+ } else if (!strcmp(name, "widget")) {
+ GladeInterface *iface = state->interface;
+
+ iface->n_toplevels++;
+ iface->toplevels = g_renew(GladeWidgetInfo *, iface->toplevels,
+ iface->n_toplevels);
+ state->widget = create_widget_info(iface, attrs);
+ iface->toplevels[iface->n_toplevels-1] = state->widget;
+
+ state->widget_depth++;
+ state->prop_type = PROP_NONE;
+ state->prop_name = NULL;
+ state->prop_agent = NULL;
+ state->prop_translator_comments = NULL;
+ state->prop_context_prefix = FALSE;
+ state->props = NULL;
+ state->signals = NULL;
+ state->accels = NULL;
+
+ state->state = PARSER_WIDGET;
+ } else {
+ g_warning("Unexpected element <%s> inside <glade-interface>.",
+ name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_REQUIRES:
+ g_warning("<requires> element should be empty. Found <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_WIDGET:
+ if (!strcmp(name, "property")) {
+ if (state->prop_type != PROP_NONE &&
+ state->prop_type != PROP_WIDGET)
+ g_warning("non widget properties defined here (oh no!)");
+ state->prop_type = PROP_WIDGET;
+ state->translate_prop = FALSE;
+ state->prop_context_prefix = FALSE;
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "name"))
+ state->prop_name = alloc_string(state->interface,
+ attrs[i+1]);
+ else if (!strcmp(attrs[i], "translatable"))
+ state->translate_prop = !strcmp(attrs[i+1], "yes");
+ else if (!strcmp(attrs[i], "agent"))
+ state->prop_agent = alloc_string(state->interface,
+ attrs[i+1]);
+ else if (!strcmp(attrs[i], "comments"))
+ state->prop_translator_comments = alloc_string(state->interface,
+ attrs[i+1]);
+ else if (!strcmp(attrs[i], "context"))
+ state->prop_context_prefix = !strcmp(attrs[i+1], "yes");
+ else
+ g_warning("unknown attribute `%s' for <property>.",
+ attrs[i]);
+ }
+ state->state = PARSER_WIDGET_PROPERTY;
+ } else if (!strcmp(name, "accessibility")) {
+ flush_properties(state);
+
+ if (attrs != NULL && attrs[0] != NULL)
+ g_warning("<accessibility> element should have no attributes");
+ state->state = PARSER_WIDGET_ATK;
+ } else if (!strcmp(name, "signal")) {
+ handle_signal(state, attrs);
+ state->state = PARSER_WIDGET_SIGNAL;
+ } else if (!strcmp(name, "accelerator")) {
+ handle_accel(state, attrs);
+ state->state = PARSER_WIDGET_ACCEL;
+ } else if (!strcmp(name, "child")) {
+ handle_child(state, attrs);
+ state->state = PARSER_WIDGET_CHILD;
+ } else {
+ g_warning("Unexpected element <%s> inside <widget>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_WIDGET_PROPERTY:
+ g_warning("<property> element should be empty. Found <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_WIDGET_ATK:
+ if (!strcmp(name, "atkproperty")) {
+ if (state->prop_type != PROP_NONE &&
+ state->prop_type != PROP_ATK)
+ g_warning("non atk properties defined here (oh no!)");
+ state->prop_type = PROP_ATK;
+ state->translate_prop = FALSE;
+ state->prop_context_prefix = FALSE;
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "name"))
+ state->prop_name = alloc_string(state->interface,
+ attrs[i+1]);
+ else if (!strcmp(attrs[i], "translatable"))
+ state->translate_prop = !strcmp(attrs[i+1], "yes");
+ else if (!strcmp(attrs[i], "comments"))
+ state->prop_translator_comments = alloc_string(state->interface,
+ attrs[i+1]);
+ else if (!strcmp(attrs[i], "context"))
+ state->prop_context_prefix = !strcmp(attrs[i+1], "yes");
+ else
+ g_warning("unknown attribute `%s' for <atkproperty>.",
+ attrs[i]);
+ }
+ state->state = PARSER_WIDGET_ATK_PROPERTY;
+ } else if (!strcmp(name, "atkaction")) {
+ handle_atk_action(state, attrs);
+ state->state = PARSER_WIDGET_ATK_ACTION;
+ } else if (!strcmp(name, "atkrelation")) {
+ handle_atk_relation(state, attrs);
+ state->state = PARSER_WIDGET_ATK_RELATION;
+ } else {
+ g_warning("Unexpected element <%s> inside <accessibility>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_WIDGET_ATK_PROPERTY:
+ if (!strcmp(name, "accessibility")) {
+ state->state = PARSER_WIDGET_ATK;
+ } else {
+ g_warning("Unexpected element <%s> inside <atkproperty>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_WIDGET_ATK_ACTION:
+ g_warning("<atkaction> element should be empty. Found <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_WIDGET_ATK_RELATION:
+ g_warning("<atkrelation> element should be empty. Found <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_WIDGET_AFTER_ATK:
+ if (!strcmp(name, "signal")) {
+ handle_signal(state, attrs);
+ state->state = PARSER_WIDGET_SIGNAL;
+ } else if (!strcmp(name, "accelerator")) {
+ handle_accel(state, attrs);
+ state->state = PARSER_WIDGET_ACCEL;
+ } else if (!strcmp(name, "child")) {
+ handle_child(state, attrs);
+ state->state = PARSER_WIDGET_CHILD;
+ } else {
+ g_warning("Unexpected element <%s> inside <widget>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_WIDGET_SIGNAL:
+ g_warning("<signal> element should be empty. Found <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_WIDGET_AFTER_SIGNAL:
+ if (!strcmp(name, "accelerator")) {
+ handle_accel(state, attrs);
+ state->state = PARSER_WIDGET_ACCEL;
+ } else if (!strcmp(name, "child")) {
+ handle_child(state, attrs);
+ state->state = PARSER_WIDGET_CHILD;
+ } else {
+ g_warning("Unexpected element <%s> inside <widget>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_WIDGET_ACCEL:
+ g_warning("<accelerator> element should be empty. Found <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_WIDGET_AFTER_ACCEL:
+ if (!strcmp(name, "child")) {
+ handle_child(state, attrs);
+ state->state = PARSER_WIDGET_CHILD;
+ } else {
+ g_warning("Unexpected element <%s> inside <widget>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_WIDGET_CHILD:
+ if (!strcmp(name, "widget")) {
+ GladeWidgetInfo *parent = state->widget;
+ GladeChildInfo *info = &parent->children[parent->n_children-1];
+
+ if (info->child)
+ g_warning("widget pointer already set!! not good");
+
+ state->widget = create_widget_info(state->interface, attrs);
+ info->child = state->widget;
+ info->child->parent = parent;
+
+ state->widget_depth++;
+ state->prop_type = PROP_NONE;
+ state->prop_name = NULL;
+ state->prop_agent = NULL;
+ state->prop_translator_comments = NULL;
+ state->prop_context_prefix = FALSE;
+ state->props = NULL;
+ state->signals = NULL;
+ state->accels = NULL;
+
+ state->state = PARSER_WIDGET;
+ } else if (!strcmp(name, "placeholder")) {
+ /* this isn't a real child, so knock off the last ChildInfo */
+ /* GLADE: We need to know about placeholders so we leave the
+ ChildInfo there. If the 'child' field of the GladeWidgetInfo
+ struct is NULL it is a placeholder. */
+ /*state->widget->n_children--;*/
+ state->state = PARSER_WIDGET_CHILD_PLACEHOLDER;
+ } else {
+ g_warning("Unexpected element <%s> inside <child>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_WIDGET_CHILD_AFTER_WIDGET:
+ if (!strcmp(name, "packing")) {
+ state->state = PARSER_WIDGET_CHILD_PACKING;
+ } else {
+ g_warning("Unexpected element <%s> inside <child>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_WIDGET_CHILD_PACKING:
+ if (!strcmp(name, "property")) {
+ if (state->prop_type != PROP_NONE &&
+ state->prop_type != PROP_CHILD)
+ g_warning("non child properties defined here (oh no!)");
+ state->prop_type = PROP_CHILD;
+ state->translate_prop = FALSE;
+ for (i = 0; attrs && attrs[i] != NULL; i += 2) {
+ if (!strcmp(attrs[i], "name"))
+ state->prop_name = alloc_string(state->interface,
+ attrs[i+1]);
+ else if (!strcmp(attrs[i], "translatable"))
+ state->translate_prop = !strcmp(attrs[i+1], "yes");
+ else if (!strcmp(attrs[i], "agent"))
+ state->prop_agent = alloc_string(state->interface,
+ attrs[i+1]);
+ else if (!strcmp(attrs[i], "comments"))
+ state->prop_translator_comments = alloc_string(state->interface,
+ attrs[i+1]);
+ else if (!strcmp(attrs[i], "context"))
+ state->prop_context_prefix = !strcmp(attrs[i+1], "yes");
+ else
+ g_warning("unknown attribute `%s' for <property>.",
+ attrs[i]);
+ }
+ state->state = PARSER_WIDGET_CHILD_PACKING_PROPERTY;
+ } else {
+ g_warning("Unexpected element <%s> inside <child>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ }
+ break;
+ case PARSER_WIDGET_CHILD_PACKING_PROPERTY:
+ g_warning("<property> element should be empty. Found <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_WIDGET_CHILD_AFTER_PACKING:
+ g_warning("<child> should have no elements after <packing>. Found <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_WIDGET_CHILD_PLACEHOLDER:
+ g_warning("<placeholder> should be empty. Found <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_WIDGET_CHILD_AFTER_PLACEHOLDER:
+ /* this is a placeholder <child> element -- ignore extra elements */
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_FINISH:
+ g_warning("There should be no elements here. Found <%s>.", name);
+ state->prev_state = state->state;
+ state->state = PARSER_UNKNOWN;
+ state->unknown_depth++;
+ break;
+ case PARSER_UNKNOWN:
+ state->unknown_depth++;
+ break;
+ }
+ /* truncate the content string ... */
+ g_string_truncate(state->content, 0);
+}
+
+static void
+glade_parser_end_element(GladeParseState *state, const xmlChar *name)
+{
+ GladeProperty prop;
+ switch (state->state) {
+ case PARSER_START:
+ g_warning("should not be closing any elements in this state");
+ break;
+ case PARSER_GLADE_INTERFACE:
+ if (strcmp(name, "glade-interface") != 0)
+ g_warning("should find </glade-interface> here. Found </%s>",
+ name);
+ state->state = PARSER_FINISH;
+ break;
+ case PARSER_REQUIRES:
+ if (strcmp(name, "requires") != 0)
+ g_warning("should find </requires> here. Found </%s>", name);
+ state->state = PARSER_GLADE_INTERFACE;
+ break;
+ case PARSER_WIDGET:
+ case PARSER_WIDGET_AFTER_ATK:
+ case PARSER_WIDGET_AFTER_SIGNAL:
+ case PARSER_WIDGET_AFTER_ACCEL:
+ if (strcmp(name, "widget") != 0)
+ g_warning("should find </widget> here. Found </%s>", name);
+ flush_properties(state);
+ flush_signals(state);
+ flush_actions(state);
+ flush_relations(state);
+ flush_accels(state);
+ state->widget = state->widget->parent;
+ state->widget_depth--;
+
+ if (state->widget_depth == 0)
+ state->state = PARSER_GLADE_INTERFACE;
+ else
+ state->state = PARSER_WIDGET_CHILD_AFTER_WIDGET;
+ break;
+ case PARSER_WIDGET_PROPERTY:
+ if (strcmp(name, "property") != 0)
+ g_warning("should find </property> here. Found </%s>", name);
+ if (!state->props)
+ state->props = g_array_new(FALSE, FALSE, sizeof(GladeProperty));
+ prop.name = state->prop_name;
+ prop.agent = state->prop_agent;
+ prop.translator_comments = state->prop_translator_comments;
+ prop.translatable = state->translate_prop;
+ prop.context_prefix = state->prop_context_prefix;
+ if (state->translate_prop && state->content->str[0] != '\0') {
+ prop.value = alloc_string(state->interface,
+ dgettext(state->domain, state->content->str));
+ } else {
+ prop.value = alloc_string(state->interface, state->content->str);
+ }
+ g_array_append_val(state->props, prop);
+ state->prop_name = NULL;
+ state->prop_agent = NULL;
+ state->prop_translator_comments = NULL;
+ state->state = PARSER_WIDGET;
+ break;
+ case PARSER_WIDGET_ATK:
+ if (strcmp(name, "accessibility") != 0)
+ g_warning("should find </accessibility> here. Found </%s>", name);
+ flush_properties(state); /* flush the ATK properties */
+ state->state = PARSER_WIDGET_AFTER_ATK;
+ break;
+ case PARSER_WIDGET_ATK_PROPERTY:
+ if (strcmp(name, "atkproperty") != 0)
+ g_warning("should find </atkproperty> here. Found </%s>", name);
+ if (!state->props)
+ state->props = g_array_new(FALSE, FALSE, sizeof(GladeProperty));
+ prop.name = state->prop_name;
+ prop.translator_comments = state->prop_translator_comments;
+ prop.translatable = state->translate_prop;
+ prop.context_prefix = state->prop_context_prefix;
+ if (state->translate_prop && state->content->str[0] != '\0') {
+ prop.value = alloc_string(state->interface,
+ dgettext(state->domain, state->content->str));
+ } else {
+ prop.value = alloc_string(state->interface, state->content->str);
+ }
+ g_array_append_val(state->props, prop);
+ state->prop_name = NULL;
+ state->prop_translator_comments = NULL;
+ state->state = PARSER_WIDGET_ATK;
+ break;
+ case PARSER_WIDGET_ATK_ACTION:
+ if (strcmp(name, "atkaction") != 0)
+ g_warning("should find </atkaction> here. Found </%s>", name);
+ state->prop_name = NULL;
+ state->state = PARSER_WIDGET_ATK;
+ break;
+ case PARSER_WIDGET_ATK_RELATION:
+ if (strcmp(name, "atkrelation") != 0)
+ g_warning("should find </atkrelation> here. Found </%s>", name);
+ state->prop_name = NULL;
+ state->state = PARSER_WIDGET_ATK;
+ break;
+ case PARSER_WIDGET_SIGNAL:
+ if (strcmp(name, "signal") != 0)
+ g_warning("should find </signal> here. Found </%s>", name);
+ state->state = PARSER_WIDGET_AFTER_ATK;
+ break;
+ case PARSER_WIDGET_ACCEL:
+ if (strcmp(name, "accelerator") != 0)
+ g_warning("should find </accelerator> here. Found </%s>", name);
+ state->state = PARSER_WIDGET_AFTER_SIGNAL;
+ break;
+ case PARSER_WIDGET_CHILD:
+ if (strcmp(name, "child") != 0)
+ g_warning("should find </child> here. Found </%s>", name);
+ /* if we are ending the element in this state, then there
+ * hasn't been a <widget> element inside this <child>
+ * element. (If there was, then we would be in
+ * PARSER_WIDGET_CHILD_AFTER_WIDGET state. */
+ g_warning("no <widget> element found inside <child>. Discarding");
+ g_free(state->widget->children[
+ state->widget->n_children-1].properties);
+ state->widget->n_children--;
+ state->state = PARSER_WIDGET_AFTER_ACCEL;
+ break;
+ case PARSER_WIDGET_CHILD_AFTER_WIDGET:
+ if (strcmp(name, "child") != 0)
+ g_warning("should find </child> here. Found </%s>", name);
+ state->state = PARSER_WIDGET_AFTER_ACCEL;
+ break;
+ case PARSER_WIDGET_CHILD_PACKING:
+ if (strcmp(name, "packing") != 0)
+ g_warning("should find </packing> here. Found </%s>", name);
+ state->state = PARSER_WIDGET_CHILD_AFTER_PACKING;
+ flush_properties(state); /* flush the properties. */
+ break;
+ case PARSER_WIDGET_CHILD_PACKING_PROPERTY:
+ if (strcmp(name, "property") != 0)
+ g_warning("should find </property> here. Found </%s>", name);
+ if (!state->props)
+ state->props = g_array_new(FALSE, FALSE, sizeof(GladeProperty));
+ prop.name = state->prop_name;
+ prop.agent = state->prop_agent;
+ prop.translator_comments = state->prop_translator_comments;
+ prop.translatable = state->translate_prop;
+ prop.context_prefix = state->prop_context_prefix;
+ if (state->translate_prop && state->content->str[0] != '\0') {
+ prop.value = alloc_string(state->interface,
+ dgettext(state->domain, state->content->str));
+ } else {
+ prop.value = alloc_string(state->interface, state->content->str);
+ }
+ g_array_append_val(state->props, prop);
+ state->prop_name = NULL;
+ state->prop_agent = NULL;
+ state->prop_translator_comments = NULL;
+ state->state = PARSER_WIDGET_CHILD_PACKING;
+ break;
+ case PARSER_WIDGET_CHILD_AFTER_PACKING:
+ if (strcmp(name, "child") != 0)
+ g_warning("should find </child> here. Found </%s>", name);
+ state->state = PARSER_WIDGET_AFTER_ACCEL;
+ break;
+ case PARSER_WIDGET_CHILD_PLACEHOLDER:
+ if (strcmp(name, "placeholder") != 0)
+ g_warning("should find </placeholder> here. Found </%s>", name);
+ state->state = PARSER_WIDGET_CHILD_AFTER_PLACEHOLDER;
+ break;
+ case PARSER_WIDGET_CHILD_AFTER_PLACEHOLDER:
+ if (strcmp(name, "child") != 0)
+ g_warning("should find </child> here. Found </%s>", name);
+ state->state = PARSER_WIDGET_AFTER_ACCEL;
+ break;
+ case PARSER_FINISH:
+ g_warning("should not be closing any elements in this state");
+ break;
+ case PARSER_UNKNOWN:
+ state->unknown_depth--;
+ if (state->unknown_depth == 0)
+ state->state = state->prev_state;
+ break;
+ }
+}
+
+static void
+glade_parser_characters(GladeParseState *state, const xmlChar *chars, int len)
+{
+ switch (state->state) {
+ case PARSER_WIDGET_PROPERTY:
+ case PARSER_WIDGET_ATK_PROPERTY:
+ case PARSER_WIDGET_CHILD_PACKING_PROPERTY:
+ g_string_append_len(state->content, chars, len);
+ break;
+ default:
+ /* don't care about content in any other states */
+ break;
+ }
+}
+
+static xmlEntityPtr
+glade_parser_get_entity(GladeParseState *state, const xmlChar *name)
+{
+ return xmlGetPredefinedEntity(name);
+}
+
+static void
+glade_parser_warning(GladeParseState *state, const char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ g_logv("XML", G_LOG_LEVEL_WARNING, msg, args);
+ va_end(args);
+}
+
+static void
+glade_parser_error(GladeParseState *state, const char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ g_logv("XML", G_LOG_LEVEL_CRITICAL, msg, args);
+ va_end(args);
+}
+
+static void
+glade_parser_fatal_error(GladeParseState *state, const char *msg, ...)
+{
+ va_list args;
+
+ va_start(args, msg);
+ g_logv("XML", G_LOG_LEVEL_ERROR, msg, args);
+ va_end(args);
+}
+
+static xmlSAXHandler glade_parser = {
+ 0, /* internalSubset */
+ 0, /* isStandalone */
+ 0, /* hasInternalSubset */
+ 0, /* hasExternalSubset */
+ 0, /* resolveEntity */
+ (getEntitySAXFunc)glade_parser_get_entity, /* getEntity */
+ 0, /* entityDecl */
+ 0, /* notationDecl */
+ 0, /* attributeDecl */
+ 0, /* elementDecl */
+ 0, /* unparsedEntityDecl */
+ 0, /* setDocumentLocator */
+ (startDocumentSAXFunc)glade_parser_start_document, /* startDocument */
+ (endDocumentSAXFunc)glade_parser_end_document, /* endDocument */
+ (startElementSAXFunc)glade_parser_start_element, /* startElement */
+ (endElementSAXFunc)glade_parser_end_element, /* endElement */
+ 0, /* reference */
+ (charactersSAXFunc)glade_parser_characters, /* characters */
+ 0, /* ignorableWhitespace */
+ 0, /* processingInstruction */
+ (commentSAXFunc)0, /* comment */
+ (warningSAXFunc)glade_parser_warning, /* warning */
+ (errorSAXFunc)glade_parser_error, /* error */
+ (fatalErrorSAXFunc)glade_parser_fatal_error, /* fatalError */
+};
+
+static void
+widget_info_free(GladeWidgetInfo *info)
+{
+ gint i;
+
+ /* GLADE: info can be NULL for placeholders. */
+ /*g_return_if_fail(info != NULL);*/
+ if (!info)
+ return;
+
+ g_free(info->properties);
+ g_free(info->atk_props);
+ g_free(info->signals);
+ g_free(info->accels);
+
+ for (i = 0; i < info->n_children; i++) {
+ g_free(info->children[i].properties);
+ widget_info_free(info->children[i].child);
+ }
+ g_free(info->children);
+ g_free(info);
+}
+
+/**
+ * glade_interface_destroy
+ * @interface: the GladeInterface structure.
+ *
+ * Frees a GladeInterface structure.
+ */
+void
+glade_interface_destroy(GladeInterface *interface)
+{
+ gint i;
+
+ g_return_if_fail(interface != NULL);
+
+ /* free requirements */
+ g_free(interface->requires);
+
+ for (i = 0; i < interface->n_toplevels; i++)
+ widget_info_free(interface->toplevels[i]);
+ g_free(interface->toplevels);
+
+ g_hash_table_destroy(interface->names);
+
+ /* free the strings hash table. The destroy notify will take care
+ * of the strings. */
+ g_hash_table_destroy(interface->strings);
+
+ g_free(interface);
+}
+
+/**
+ * glade_parser_parse_file
+ * @file: the filename of the glade XML file.
+ * @domain: the translation domain for the XML file.
+ *
+ * This function parses a Glade XML interface file to a GladeInterface
+ * object (which is libglade's internal representation of the
+ * interface data).
+ *
+ * Generally, user code won't need to call this function. Instead, it
+ * should go through the GladeXML interfaces.
+ *
+ * Returns: the GladeInterface structure for the XML file.
+ */
+GladeInterface *
+glade_parser_parse_file(const gchar *file, const gchar *domain)
+{
+ int old_substitute_entities;
+ int result;
+ GladeParseState state = { 0 };
+
+ state.interface = NULL;
+ if (domain)
+ state.domain = domain;
+ else
+ state.domain = textdomain(NULL);
+
+ old_substitute_entities = xmlSubstituteEntitiesDefault(1);
+ result = xmlSAXUserParseFile(&glade_parser, &state, file);
+ xmlSubstituteEntitiesDefault(old_substitute_entities);
+
+ if (result < 0) {
+ g_warning("document not well formed!");
+ if (state.interface)
+ glade_interface_destroy (state.interface);
+ return NULL;
+ }
+ if (state.state != PARSER_FINISH) {
+ g_warning("did not finish in PARSER_FINISH state!");
+ if (state.interface)
+ glade_interface_destroy(state.interface);
+ return NULL;
+ }
+ return state.interface;
+}
+
+/**
+ * glade_parser_parse_buffer
+ * @buffer: a buffer in memory containing XML data.
+ * @len: the length of @buffer.
+ * @domain: the translation domain for the XML file.
+ *
+ * This function is similar to glade_parser_parse_file, except that it
+ * parses XML data from a buffer in memory. This could be used to
+ * embed an interface into the executable, for instance.
+ *
+ * Generally, user code won't need to call this function. Instead, it
+ * should go through the GladeXML interfaces.
+ *
+ * Returns: the GladeInterface structure for the XML buffer.
+ */
+GladeInterface *
+glade_parser_parse_buffer(const gchar *buffer, gint len, const gchar *domain)
+{
+ int old_substitute_entities;
+ int result;
+ GladeParseState state = { 0 };
+
+ state.interface = NULL;
+ if (domain)
+ state.domain = domain;
+ else
+ state.domain = textdomain(NULL);
+
+ old_substitute_entities = xmlSubstituteEntitiesDefault(1);
+ result = xmlSAXUserParseMemory(&glade_parser, &state, buffer, len);
+ xmlSubstituteEntitiesDefault(old_substitute_entities);
+
+ if (result < 0) {
+ g_warning("document not well formed!");
+ if (state.interface)
+ glade_interface_destroy (state.interface);
+ return NULL;
+ }
+ if (state.state != PARSER_FINISH) {
+ g_warning("did not finish in PARSER_FINISH state!");
+ if (state.interface)
+ glade_interface_destroy(state.interface);
+ return NULL;
+ }
+ return state.interface;
+}
+
+static void
+dump_widget(xmlNode *parent, GladeWidgetInfo *info, gint indent)
+{
+ xmlNode *widget = xmlNewNode(NULL, "widget");
+ gint i, j;
+
+ xmlSetProp(widget, "class", info->class);
+ xmlSetProp(widget, "id", info->name);
+ xmlAddChild(parent, widget);
+ xmlNodeAddContent(widget, "\n");
+
+ for (i = 0; i < info->n_properties; i++) {
+ xmlNode *node;
+
+ for (j = 0; j < indent + 1; j++)
+ xmlNodeAddContent(widget, " ");
+ node = xmlNewNode(NULL, "property");
+ xmlSetProp(node, "name", info->properties[i].name);
+ xmlNodeSetContent(node, info->properties[i].value);
+ xmlAddChild(widget, node);
+ xmlNodeAddContent(widget, "\n");
+ }
+
+ if (info->n_atk_props != 0) {
+ xmlNode *atk;
+
+ for (j = 0; j < indent + 1; j++)
+ xmlNodeAddContent(widget, " ");
+ atk = xmlNewNode(NULL, "accessibility");
+ xmlAddChild(widget, atk);
+ xmlNodeAddContent(widget, "\n");
+ xmlNodeAddContent(atk, "\n");
+
+ for (i = 0; i < info->n_atk_props; i++) {
+ xmlNode *node;
+
+ for (j = 0; j < indent + 2; j++)
+ xmlNodeAddContent(atk, " ");
+ node = xmlNewNode(NULL, "property");
+ xmlSetProp(node, "name", info->atk_props[i].name);
+ xmlNodeSetContent(node, info->atk_props[i].value);
+ xmlAddChild(atk, node);
+ xmlNodeAddContent(atk, "\n");
+ }
+ for (j = 0; j < indent + 1; j++)
+ xmlNodeAddContent(atk, " ");
+ }
+
+ for (i = 0; i < info->n_signals; i++) {
+ xmlNode *node;
+
+ for (j = 0; j < indent + 1; j++)
+ xmlNodeAddContent(widget, " ");
+
+ node = xmlNewNode(NULL, "signal");
+ xmlSetProp(node, "name", info->signals[i].name);
+ xmlSetProp(node, "handler", info->signals[i].handler);
+ if (info->signals[i].after)
+ xmlSetProp(node, "after", "yes");
+ if (info->signals[i].object)
+ xmlSetProp(node, "object", info->signals[i].object);
+ xmlAddChild(widget, node);
+ xmlNodeAddContent(widget, "\n");
+ }
+
+ for (i = 0; i < info->n_accels; i++) {
+ xmlNode *node;
+
+ for (j = 0; j < indent + 1; j++)
+ xmlNodeAddContent(widget, " ");
+
+ node = xmlNewNode(NULL, "accelerator");
+ xmlSetProp(node, "key", gdk_keyval_name(info->accels[i].key));
+ xmlSetProp(node, "modifier", "something"/*info->accels[i].modifiers*/);
+ xmlSetProp(node, "signal", info->accels[i].signal);
+ xmlAddChild(widget, node);
+ xmlNodeAddContent(widget, "\n");
+ }
+
+ for (i = 0; i < info->n_children; i++) {
+ xmlNode *child;
+ GladeChildInfo *childinfo = &info->children[i];
+ gint k;
+
+ for (j = 0; j < indent + 1; j++)
+ xmlNodeAddContent(widget, " ");
+
+ child = xmlNewNode(NULL, "child");
+ if (childinfo->internal_child)
+ xmlSetProp(child, "internal-child", childinfo->internal_child);
+ xmlAddChild(widget, child);
+ xmlNodeAddContent(widget, "\n");
+ xmlNodeAddContent(child, "\n");
+
+ for (k = 0; k < childinfo->n_properties; k++) {
+ xmlNode *node;
+
+ for (j = 0; j < indent + 2; j++)
+ xmlNodeAddContent(child, " ");
+ node = xmlNewNode(NULL, "property");
+ xmlSetProp(node, "name", childinfo->properties[i].name);
+ xmlNodeSetContent(node, childinfo->properties[i].value);
+ xmlAddChild(child, node);
+ xmlNodeAddContent(child, "\n");
+ }
+
+ for (j = 0; j < indent + 2; j++)
+ xmlNodeAddContent(child, " ");
+ dump_widget(child, childinfo->child, indent + 2);
+ xmlNodeAddContent(child, "\n");
+
+ for (j = 0; j < indent + 1; j++)
+ xmlNodeAddContent(child, " ");
+ }
+
+ for (j = 0; j < indent; j++)
+ xmlNodeAddContent(widget, " ");
+}
+
+/**
+ * glade_interface_dump
+ * @interface: the GladeInterface
+ * @filename: the filename to write the interface data to.
+ *
+ * This function dumps the contents of a GladeInterface into a file as
+ * XML. It is intended mainly as a debugging tool.
+ */
+void
+glade_interface_dump(GladeInterface *interface, const gchar *filename)
+{
+ xmlDoc *doc;
+ xmlNode *root;
+ gint i;
+
+ doc = xmlNewDoc("1.0");
+ doc->standalone = FALSE;
+ xmlCreateIntSubset(doc, "glade-interface",
+ NULL, "glade-2.0.dtd");
+ root = xmlNewNode(NULL, "glade-interface");
+ xmlDocSetRootElement(doc, root);
+
+ xmlNodeAddContent(root, "\n");
+
+ for (i = 0; i < interface->n_requires; i++) {
+ xmlNode *node = xmlNewNode(NULL, "requires");
+
+ xmlSetProp(node, "lib", interface->requires[i]);
+
+ xmlNodeAddContent(root, " ");
+ xmlAddChild(root, node);
+ xmlNodeAddContent(root, "\n");
+ }
+
+ for (i = 0; i < interface->n_toplevels; i++) {
+ xmlNodeAddContent(root, " ");
+ dump_widget(root, interface->toplevels[i], 1);
+ xmlNodeAddContent(root, "\n");
+ }
+
+ /* output */
+
+ xmlSaveFileEnc(filename, doc, "UTF-8");
+ xmlFreeDoc(doc);
+}
+
+#if 0
+int
+main(int argc, char **argv) {
+ gtk_init(&argc, &argv);
+ if (argc > 1) {
+ GladeInterface *interface = glade_parser_parse_file(argv[1]);
+ g_message("output: %p", interface);
+ if (interface) {
+ glade_interface_dump(interface, "/dev/stdout");
+ glade_interface_destroy(interface);
+ }
+ } else
+ g_message("need filename");
+ return 0;
+}
+#endif