source: trunk/third/libglade2/glade/glade-parser.c @ 21576

Revision 21576, 43.3 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21575, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; c-basic-offset: 4 -*-
2 * libglade - a library for building interfaces from XML files at runtime
3 * Copyright (C) 1998-2002  James Henstridge <james@daa.com.au>
4 *
5 * glade-parser.c: functions for parsing glade-2.0 files
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA  02111-1307, USA.
21 */
22#ifdef HAVE_CONFIG_H
23#  include <config.h>
24#endif
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29
30#ifdef ENABLE_NLS
31#  include <libintl.h>
32#else
33#  define textdomain(String) (String)
34#  define dgettext(Domain, String) (String)
35#endif
36
37#include <libxml/parser.h>
38
39#include "glade-parser.h"
40#include "glade-private.h"
41
42typedef enum {
43    PARSER_START,
44    PARSER_GLADE_INTERFACE,
45    PARSER_REQUIRES,
46    PARSER_WIDGET,
47    PARSER_WIDGET_PROPERTY,
48    PARSER_WIDGET_ATK,
49    PARSER_WIDGET_ATK_PROPERTY,
50    PARSER_WIDGET_ATK_ACTION,
51    PARSER_WIDGET_ATK_RELATION,
52    PARSER_WIDGET_AFTER_ATK,
53    PARSER_WIDGET_SIGNAL,
54    PARSER_WIDGET_AFTER_SIGNAL,
55    PARSER_WIDGET_ACCEL,
56    PARSER_WIDGET_AFTER_ACCEL,
57    PARSER_WIDGET_CHILD,
58    PARSER_WIDGET_CHILD_AFTER_WIDGET,
59    PARSER_WIDGET_CHILD_PACKING,
60    PARSER_WIDGET_CHILD_PACKING_PROPERTY,
61    PARSER_WIDGET_CHILD_AFTER_PACKING,
62    PARSER_WIDGET_CHILD_PLACEHOLDER,
63    PARSER_WIDGET_CHILD_AFTER_PLACEHOLDER,
64    PARSER_FINISH,
65    PARSER_UNKNOWN
66} ParserState;
67
68#ifdef DEBUG
69static const gchar *state_names[] = {
70    "START",
71    "GLADE_INTERFACE",
72    "REQUIRES",
73    "WIDGET",
74    "WIDGET_PROPERTY",
75    "WIDGET_ATK",
76    "WIDGET_ATK_PROPERTY",
77    "WIDGET_ATK_ACTION",
78    "WIDGET_ATK_RELATION",
79    "WIDGET_AFTER_ATK",
80    "WIDGET_SIGNAL",
81    "WIDGET_AFTER_SIGNAL",
82    "WIDGET_ACCEL",
83    "WIDGET_AFTER_ACCEL",
84    "WIDGET_CHILD",
85    "WIDGET_CHILD_AFTER_WIDGET",
86    "WIDGET_CHILD_PACKING",
87    "WIDGET_CHILD_PACKING_PROPERTY",
88    "WIDGET_CHILD_AFTER_PACKING",
89    "WIDGET_CHILD_PLACEHOLDER",
90    "WIDGET_CHILD_AFTER_PLACEHOLDER",
91    "FINISH",
92    "UNKNOWN",
93};
94#endif
95
96typedef struct _GladeParseState GladeParseState;
97struct _GladeParseState {
98    ParserState state;
99
100    const gchar *domain;
101
102    guint unknown_depth;    /* handle recursive unrecognised tags */
103    ParserState prev_state; /* the last `known' state we were in */
104
105    guint widget_depth;
106    GString *content;
107
108    GladeInterface *interface;
109    GladeWidgetInfo *widget;
110
111    enum {PROP_NONE, PROP_WIDGET, PROP_ATK, PROP_CHILD } prop_type;
112    gchar *prop_name;
113    gboolean translate_prop;
114    gboolean context_prop;
115    GArray *props;
116
117    GArray *signals;
118    GArray *atk_actions;
119    GArray *relations;
120    GArray *accels;
121};
122
123static gchar *
124alloc_string(GladeInterface *interface, const gchar *string)
125{
126    gchar *s;
127
128    s = g_hash_table_lookup(interface->strings, string);
129    if (!s) {
130        s = g_strdup(string);
131        g_hash_table_insert(interface->strings, s, s);
132    }
133    return s;
134}
135
136static gchar *
137alloc_propname(GladeInterface *interface, const gchar *string)
138{
139    static GString *norm_str;
140    gint i;
141
142    if (!norm_str)
143        norm_str = g_string_new_len(NULL, 64);
144
145    /* assign the string to norm_str */
146    g_string_assign(norm_str, string);
147    /* convert all dashes to underscores */
148    for (i = 0; i < norm_str->len; i++)
149        if (norm_str->str[i] == '-')
150            norm_str->str[i] = '_';
151
152    return alloc_string(interface, norm_str->str);
153}
154
155static GladeWidgetInfo *
156create_widget_info(GladeInterface *interface, const xmlChar **attrs)
157{
158    GladeWidgetInfo *info = g_new0(GladeWidgetInfo, 1);
159    int i;
160
161    for (i = 0; attrs && attrs[i] != NULL; i += 2) {
162        if (!strcmp(attrs[i], "class"))
163            info->classname = alloc_string(interface, attrs[i+1]);
164        else if (!strcmp(attrs[i], "id"))
165            info->name = alloc_string(interface, attrs[i+1]);
166        else
167            g_warning("unknown attribute `%s' for <widget>.", attrs[i]);
168    }
169    if (info->classname == NULL || info->name == NULL)
170        g_warning("<widget> element missing required attributes!");
171    g_hash_table_insert(interface->names, info->name, info);
172    return info;
173}
174
175static inline void
176flush_properties(GladeParseState *state)
177{
178    if (state->props == NULL)
179        return;
180    switch (state->prop_type) {
181    case PROP_NONE:
182        break;
183    case PROP_WIDGET:
184        if (state->widget->properties)
185            g_warning("we already read all the props for this key.  Leaking");
186        state->widget->properties = (GladeProperty *)state->props->data;
187        state->widget->n_properties = state->props->len;
188        g_array_free(state->props, FALSE);
189        break;
190    case PROP_ATK:
191        if (state->widget->atk_props)
192            g_warning("we already read all the ATK props for this key.  Leaking");
193        state->widget->atk_props = (GladeProperty *)state->props->data;
194        state->widget->n_atk_props = state->props->len;
195        g_array_free(state->props, FALSE);
196        break;
197    case PROP_CHILD:
198        if (state->widget->n_children == 0) {
199            g_warning("no children, but have child properties!");
200            g_array_free(state->props, TRUE);
201        } else {
202            GladeChildInfo *info = &state->widget->children[
203                                                state->widget->n_children-1];
204            if (info->properties)
205                g_warning("we already read all the child props for this key.  Leaking");
206            info->properties = (GladeProperty *)state->props->data;
207            info->n_properties = state->props->len;
208            g_array_free(state->props, FALSE);
209        }
210        break;
211    }
212    state->prop_type = PROP_NONE;
213    state->prop_name = NULL;
214    state->props = NULL;
215}
216
217static inline void
218flush_signals(GladeParseState *state)
219{
220    if (state->signals) {
221        state->widget->signals = (GladeSignalInfo *)state->signals->data;
222        state->widget->n_signals = state->signals->len;
223        g_array_free(state->signals, FALSE);
224    }
225    state->signals = NULL;
226}
227
228static inline void
229flush_actions(GladeParseState *state)
230{
231    if (state->atk_actions) {
232        state->widget->atk_actions = (GladeAtkActionInfo *)state->atk_actions->data;
233        state->widget->n_atk_actions = state->atk_actions->len;
234        g_array_free(state->atk_actions, FALSE);
235    }
236    state->atk_actions = NULL;
237}
238
239static inline void
240flush_relations(GladeParseState *state)
241{
242    if (state->relations) {
243        state->widget->relations = (GladeAtkRelationInfo *)state->relations->data;
244        state->widget->n_relations = state->relations->len;
245        g_array_free(state->relations, FALSE);
246    }
247    state->relations = NULL;
248}
249
250static inline void
251flush_accels(GladeParseState *state)
252{
253    if (state->accels) {
254        state->widget->accels = (GladeAccelInfo *)state->accels->data;
255        state->widget->n_accels = state->accels->len;
256        g_array_free(state->accels, FALSE);
257    }
258    state->accels = NULL;
259}
260
261static inline void
262handle_atk_action(GladeParseState *state, const xmlChar **attrs)
263{
264    gint i;
265    GladeAtkActionInfo info = { NULL };
266
267    flush_properties(state);
268
269    for (i = 0; attrs && attrs[i] != NULL; i += 2) {
270        if (!strcmp(attrs[i], "action_name"))
271            info.action_name = alloc_string(state->interface, attrs[i+1]);
272        else if (!strcmp(attrs[i], "description"))
273            info.description = alloc_string(state->interface,
274                                    dgettext(state->domain, attrs[i+1]));
275        else
276            g_warning("unknown attribute `%s' for <action>.", attrs[i]);
277    }
278    if (info.action_name == NULL) {
279        g_warning("required <atkaction> attribute 'action_name' missing!!!");
280        return;
281    }
282    if (!state->atk_actions)
283        state->atk_actions = g_array_new(FALSE, FALSE,
284                                     sizeof(GladeAtkActionInfo));
285    g_array_append_val(state->atk_actions, info);
286}
287
288static inline void
289handle_atk_relation(GladeParseState *state, const xmlChar **attrs)
290{
291    gint i;
292    GladeAtkRelationInfo info = { NULL };
293
294    flush_properties(state);
295
296    for (i = 0; attrs && attrs[i] != NULL; i += 2) {
297        if (!strcmp(attrs[i], "target"))
298            info.target = alloc_string(state->interface, attrs[i+1]);
299        else if (!strcmp(attrs[i], "type"))
300            info.type = alloc_string(state->interface, attrs[i+1]);
301        else
302            g_warning("unknown attribute `%s' for <signal>.", attrs[i]);
303    }
304    if (info.target == NULL || info.type == NULL) {
305        g_warning("required <atkrelation> attributes ('target' and/or 'type') missing!!!");
306        return;
307    }
308    if (!state->relations)
309        state->relations = g_array_new(FALSE, FALSE,
310                                     sizeof(GladeAtkRelationInfo));
311    g_array_append_val(state->relations, info);
312}
313
314static inline void
315handle_signal(GladeParseState *state, const xmlChar **attrs)
316{
317    GladeSignalInfo info = { NULL };
318    gint i;
319
320    flush_properties(state);
321
322    info.after = FALSE;
323    for (i = 0; attrs && attrs[i] != NULL; i += 2) {
324        if (!strcmp(attrs[i], "name"))
325            info.name = alloc_string(state->interface, attrs[i+1]);
326        else if (!strcmp(attrs[i], "handler"))
327            info.handler = alloc_string(state->interface, attrs[i+1]);
328        else if (!strcmp(attrs[i], "after"))
329            info.after = attrs[i+1][0] == 'y';
330        else if (!strcmp(attrs[i], "object"))
331            info.object = alloc_string(state->interface, attrs[i+1]);
332        else if (!strcmp(attrs[i], "last_modification_time"))
333            /* Do nothing. */;
334        else
335            g_warning("unknown attribute `%s' for <signal>.", attrs[i]);
336    }
337    if (info.name == NULL || info.handler == NULL) {
338        g_warning("required <signal> attributes missing!!!");
339        return;
340    }
341    if (!state->signals)
342        state->signals = g_array_new(FALSE, FALSE,
343                                     sizeof(GladeSignalInfo));
344    g_array_append_val(state->signals, info);
345}
346
347static inline void
348handle_accel(GladeParseState *state, const xmlChar **attrs)
349{
350    GladeAccelInfo info = { 0 };
351    gint i;
352
353    flush_properties(state);
354    flush_signals(state);
355    flush_actions(state);
356    flush_relations(state);
357
358    for (i = 0; attrs && attrs[i] != NULL; i += 2) {
359        if (!strcmp(attrs[i], "key"))
360            info.key = gdk_keyval_from_name(attrs[i+1]);
361        else if (!strcmp(attrs[i], "modifiers")) {
362            const xmlChar *pos = attrs[i+1];
363
364            info.modifiers = 0;
365            while (pos[0])
366                if (!strncmp(pos, "GDK_", 4)) {
367                    pos += 4;
368                    if (!strncmp(pos, "SHIFT_MASK", 10)) {
369                        info.modifiers |= GDK_SHIFT_MASK;
370                        pos += 10;
371                    } else if (!strncmp(pos, "LOCK_MASK", 9)) {
372                        info.modifiers |= GDK_LOCK_MASK;
373                        pos += 9;
374                    } else if (!strncmp(pos, "CONTROL_MASK", 12)) {
375                        info.modifiers |= GDK_CONTROL_MASK;
376                        pos += 12;
377                    } else if (!strncmp(pos, "MOD", 3) &&
378                               !strncmp(pos+4, "_MASK", 5)) {
379                        switch (pos[3]) {
380                        case '1':
381                            info.modifiers |= GDK_MOD1_MASK; break;
382                        case '2':
383                            info.modifiers |= GDK_MOD2_MASK; break;
384                        case '3':
385                            info.modifiers |= GDK_MOD3_MASK; break;
386                        case '4':
387                            info.modifiers |= GDK_MOD4_MASK; break;
388                        case '5':
389                            info.modifiers |= GDK_MOD5_MASK; break;
390                        }
391                        pos += 9;
392                    } else if (!strncmp(pos, "BUTTON", 6) &&
393                               !strncmp(pos+7, "_MASK", 5)) {
394                        switch (pos[6]) {
395                        case '1':
396                            info.modifiers |= GDK_BUTTON1_MASK; break;
397                        case '2':
398                            info.modifiers |= GDK_BUTTON2_MASK; break;
399                        case '3':
400                            info.modifiers |= GDK_BUTTON3_MASK; break;
401                        case '4':
402                            info.modifiers |= GDK_BUTTON4_MASK; break;
403                        case '5':
404                            info.modifiers |= GDK_BUTTON5_MASK; break;
405                        }
406                        pos += 12;
407                    } else if (!strncmp(pos, "RELEASE_MASK", 12)) {
408                        info.modifiers |= GDK_RELEASE_MASK;
409                        pos += 12;
410                    } else
411                        pos++;
412               } else
413                   pos++;
414        } else if (!strcmp(attrs[i], "signal"))
415            info.signal = alloc_string(state->interface, attrs[i+1]);
416        else
417            g_warning("unknown attribute `%s' for <accelerator>.", attrs[i]);
418    }
419    if (info.key == 0 || info.signal == NULL) {
420        g_warning("required <accelerator> attributes missing!!!");
421        return;
422    }
423    if (!state->accels)
424        state->accels = g_array_new(FALSE, FALSE,
425                                    sizeof(GladeAccelInfo));
426    g_array_append_val(state->accels, info);
427}
428
429static inline void
430handle_child(GladeParseState *state, const xmlChar **attrs)
431{
432    GladeChildInfo *info;
433    gint i;
434
435    /* make sure all of these are flushed */
436    flush_properties(state);
437    flush_signals(state);
438    flush_actions(state);
439    flush_relations(state);
440    flush_accels(state);
441
442    state->widget->n_children++;
443    state->widget->children = g_renew(GladeChildInfo, state->widget->children,
444                                      state->widget->n_children);
445    info = &state->widget->children[state->widget->n_children-1];
446    info->internal_child = NULL;
447    info->properties = NULL;
448    info->n_properties = 0;
449    info->child = NULL;
450
451    for (i = 0; attrs && attrs[i] != NULL; i += 2) {
452        if (!strcmp(attrs[i], "internal-child"))
453            info->internal_child = alloc_string(state->interface, attrs[i+1]);
454        else
455            g_warning("unknown attribute `%s' for <child>.", attrs[i]);
456    }
457}
458
459static void
460glade_parser_start_document(GladeParseState *state)
461{
462    state->state = PARSER_START;
463
464    state->unknown_depth = 0;
465    state->prev_state = PARSER_UNKNOWN;
466
467    state->widget_depth = 0;
468    state->content = g_string_sized_new(128);
469
470    state->interface = g_new0(GladeInterface, 1);
471    state->interface->names = g_hash_table_new(g_str_hash, g_str_equal);
472    state->interface->strings = g_hash_table_new_full(g_str_hash,
473                                                      g_str_equal,
474                                                      (GDestroyNotify)g_free,
475                                                      NULL);
476    state->widget = NULL;
477
478    state->prop_type = PROP_NONE;
479    state->prop_name = NULL;
480    state->translate_prop = FALSE;
481    state->props = NULL;
482
483    state->signals = NULL;
484    state->accels = NULL;
485}
486
487static void
488glade_parser_end_document(GladeParseState *state)
489{
490    g_string_free(state->content, TRUE);
491
492    if (state->unknown_depth != 0)
493        g_warning("unknown_depth != 0 (%d)", state->unknown_depth);
494    if (state->widget_depth != 0)
495        g_warning("widget_depth != 0 (%d)", state->widget_depth);
496}
497
498static void
499glade_parser_start_element(GladeParseState *state,
500                           const xmlChar *name, const xmlChar **attrs)
501{
502    int i;
503
504    GLADE_NOTE(PARSER, g_message("<%s> in state %s",
505                                 name, state_names[state->state]));
506
507    switch (state->state) {
508    case PARSER_START:
509        if (!strcmp(name, "glade-interface")) {
510            state->state = PARSER_GLADE_INTERFACE;
511#if 0
512            /* check for correct XML namespace */
513            for (i = 0; attrs && attrs[i] != NULL; i += 2) {
514                if (!strcmp(attrs[i], "xmlns") &&
515                    !strcmp(attrs[i+1], "...")) {
516                    g_warning("bad XML namespace `%s'.", attrs[i+1]);
517                } else
518                    g_warning("unknown attribute `%s' for <glade-interface>",
519                              attrs[i]);
520            }
521#endif
522        } else {
523            g_warning("Expected <glade-interface>.  Got <%s>.", name);
524            state->prev_state = state->state;
525            state->state = PARSER_UNKNOWN;
526            state->unknown_depth++;
527        }
528        break;
529    case PARSER_GLADE_INTERFACE:
530        if (!strcmp(name, "requires")) {
531            for (i = 0; attrs && attrs[i] != NULL; i += 2) {
532                if (!strcmp(attrs[i], "lib")) {
533                    GladeInterface *iface = state->interface;
534
535                    /* add to the list of requirements for this module */
536                    iface->n_requires++;
537                    iface->requires = g_renew(gchar *, iface->requires,
538                                              iface->n_requires);
539                    iface->requires[iface->n_requires-1] =
540                        alloc_string(iface, attrs[i+1]);
541                } else
542                    g_warning("unknown attribute `%s' for <requires>.",
543                              attrs[i]);
544            }
545            state->state = PARSER_REQUIRES;
546        } else if (!strcmp(name, "widget")) {
547            GladeInterface *iface = state->interface;
548
549            iface->n_toplevels++;
550            iface->toplevels = g_renew(GladeWidgetInfo *, iface->toplevels,
551                                       iface->n_toplevels);
552            state->widget = create_widget_info(iface, attrs);
553            iface->toplevels[iface->n_toplevels-1] = state->widget;
554
555            state->widget_depth++;
556            state->prop_type = PROP_NONE;
557            state->prop_name = NULL;
558            state->props = NULL;
559            state->signals = NULL;
560            state->accels = NULL;
561
562            state->state = PARSER_WIDGET;
563        } else {
564            g_warning("Unexpected element <%s> inside <glade-interface>.",
565                      name);
566            state->prev_state = state->state;
567            state->state = PARSER_UNKNOWN;
568            state->unknown_depth++;
569        }
570        break;
571    case PARSER_REQUIRES:
572        g_warning("<requires> element should be empty.  Found <%s>.", name);
573        state->prev_state = state->state;
574        state->state = PARSER_UNKNOWN;
575        state->unknown_depth++;
576        break;
577    case PARSER_WIDGET:
578        if (!strcmp(name, "property")) {
579            gboolean bad_agent = FALSE;
580
581            if (state->prop_type != PROP_NONE &&
582                state->prop_type != PROP_WIDGET)
583                g_warning("non widget properties defined here (oh no!)");
584            state->translate_prop = FALSE;
585            for (i = 0; attrs && attrs[i] != NULL; i += 2) {
586                if (!strcmp(attrs[i], "name"))
587                    state->prop_name = alloc_propname(state->interface,
588                                                      attrs[i+1]);
589                else if (!strcmp(attrs[i], "translatable"))
590                    state->translate_prop = !strcmp(attrs[i+1], "yes");
591                else if (!strcmp(attrs[i], "context"))
592                    state->context_prop = !strcmp(attrs[i+1], "yes");
593                else if (!strcmp(attrs[i], "agent"))
594                    bad_agent = strcmp(attrs[i], "libglade") != 0;
595                else if (!strcmp(attrs[i], "comments"))
596                    /* Do nothing. */;
597                else
598                    g_warning("unknown attribute `%s' for <property>.",
599                              attrs[i]);
600            }
601            if (bad_agent) {
602                /* ignore the property ... */
603                state->prev_state = state->state;
604                state->state = PARSER_UNKNOWN;
605                state->unknown_depth++;
606            } else {
607                state->prop_type = PROP_WIDGET;
608                state->state = PARSER_WIDGET_PROPERTY;
609            }
610        } else if (!strcmp(name, "accessibility")) {
611            flush_properties(state);
612
613            if (attrs != NULL && attrs[0] != NULL)
614                g_warning("<accessibility> element should have no attributes");
615            state->state = PARSER_WIDGET_ATK;
616        } else if (!strcmp(name, "signal")) {
617            handle_signal(state, attrs);
618            state->state = PARSER_WIDGET_SIGNAL;
619        } else if (!strcmp(name, "accelerator")) {
620            handle_accel(state, attrs);
621            state->state = PARSER_WIDGET_ACCEL;
622        } else if (!strcmp(name, "child")) {
623            handle_child(state, attrs);
624            state->state = PARSER_WIDGET_CHILD;
625        } else {
626            g_warning("Unexpected element <%s> inside <widget>.", name);
627            state->prev_state = state->state;
628            state->state = PARSER_UNKNOWN;
629            state->unknown_depth++;
630        }
631        break;
632    case PARSER_WIDGET_PROPERTY:
633        g_warning("<property> element should be empty.  Found <%s>.", name);
634        state->prev_state = state->state;
635        state->state = PARSER_UNKNOWN;
636        state->unknown_depth++;
637        break;
638    case PARSER_WIDGET_ATK:
639        if (!strcmp(name, "atkproperty")) {
640            if (state->prop_type != PROP_NONE &&
641                state->prop_type != PROP_ATK)
642                g_warning("non atk properties defined here (oh no!)");
643            state->prop_type = PROP_ATK;
644            state->translate_prop = FALSE;
645            for (i = 0; attrs && attrs[i] != NULL; i += 2) {
646                if (!strcmp(attrs[i], "name"))
647                    state->prop_name = alloc_propname(state->interface,
648                                                      attrs[i+1]);
649                else if (!strcmp(attrs[i], "translatable"))
650                    state->translate_prop = !strcmp(attrs[i+1], "yes");
651                else if (!strcmp(attrs[i], "context"))
652                    state->context_prop = !strcmp(attrs[i+1], "yes");
653                else if (!strcmp(attrs[i], "comments"))
654                    /* Do nothing. */;
655                else
656                    g_warning("unknown attribute `%s' for <atkproperty>.",
657                              attrs[i]);
658            }
659            state->state = PARSER_WIDGET_ATK_PROPERTY;
660        } else if (!strcmp(name, "atkaction")) {
661            handle_atk_action(state, attrs);
662            state->state = PARSER_WIDGET_ATK_ACTION;
663        } else if (!strcmp(name, "atkrelation")) {
664            handle_atk_relation(state, attrs);
665            state->state = PARSER_WIDGET_ATK_RELATION;
666        } else {
667            g_warning("Unexpected element <%s> inside <accessibility>.", name);
668            state->prev_state = state->state;
669            state->state = PARSER_UNKNOWN;
670            state->unknown_depth++;
671        }
672        break;
673    case PARSER_WIDGET_ATK_PROPERTY:
674        if (!strcmp(name, "accessibility")) {
675            state->state = PARSER_WIDGET_ATK;
676        } else {
677            g_warning("Unexpected element <%s> inside <atkproperty>.", name);
678            state->prev_state = state->state;
679            state->state = PARSER_UNKNOWN;
680            state->unknown_depth++;
681        }
682        break;
683    case PARSER_WIDGET_ATK_ACTION:
684        g_warning("<atkaction> element should be empty.  Found <%s>.", name);
685        state->prev_state = state->state;
686        state->state = PARSER_UNKNOWN;
687        state->unknown_depth++;
688        break;
689    case PARSER_WIDGET_ATK_RELATION:
690        g_warning("<atkrelation> element should be empty.  Found <%s>.", name);
691        state->prev_state = state->state;
692        state->state = PARSER_UNKNOWN;
693        state->unknown_depth++;
694        break;
695    case PARSER_WIDGET_AFTER_ATK:
696        if (!strcmp(name, "signal")) {
697            handle_signal(state, attrs);
698            state->state = PARSER_WIDGET_SIGNAL;
699        } else if (!strcmp(name, "accelerator")) {
700            handle_accel(state, attrs);
701            state->state = PARSER_WIDGET_ACCEL;
702        } else if (!strcmp(name, "child")) {
703            handle_child(state, attrs);
704            state->state = PARSER_WIDGET_CHILD;
705        } else {
706            g_warning("Unexpected element <%s> inside <widget>.", name);
707            state->prev_state = state->state;
708            state->state = PARSER_UNKNOWN;
709            state->unknown_depth++;
710        }
711        break;
712    case PARSER_WIDGET_SIGNAL:
713        g_warning("<signal> element should be empty.  Found <%s>.", name);
714        state->prev_state = state->state;
715        state->state = PARSER_UNKNOWN;
716        state->unknown_depth++;
717        break;
718    case PARSER_WIDGET_AFTER_SIGNAL:
719        if (!strcmp(name, "accelerator")) {
720            handle_accel(state, attrs);
721            state->state = PARSER_WIDGET_ACCEL;
722        } else if (!strcmp(name, "child")) {
723            handle_child(state, attrs);
724            state->state = PARSER_WIDGET_CHILD;
725        } else {
726            g_warning("Unexpected element <%s> inside <widget>.", name);
727            state->prev_state = state->state;
728            state->state = PARSER_UNKNOWN;
729            state->unknown_depth++;
730        }
731        break;
732    case PARSER_WIDGET_ACCEL:
733        g_warning("<accelerator> element should be empty.  Found <%s>.", name);
734        state->prev_state = state->state;
735        state->state = PARSER_UNKNOWN;
736        state->unknown_depth++;
737        break;
738    case PARSER_WIDGET_AFTER_ACCEL:
739        if (!strcmp(name, "child")) {
740            handle_child(state, attrs);
741            state->state = PARSER_WIDGET_CHILD;
742        } else {
743            g_warning("Unexpected element <%s> inside <widget>.", name);
744            state->prev_state = state->state;
745            state->state = PARSER_UNKNOWN;
746            state->unknown_depth++;
747        }
748        break;
749    case PARSER_WIDGET_CHILD:
750        if (!strcmp(name, "widget")) {
751            GladeWidgetInfo *parent = state->widget;
752            GladeChildInfo *info = &parent->children[parent->n_children-1];
753
754            if (info->child)
755                g_warning("widget pointer already set!! not good");
756
757            state->widget = create_widget_info(state->interface, attrs);
758            info->child = state->widget;
759            info->child->parent = parent;
760
761            state->widget_depth++;
762            state->prop_type = PROP_NONE;
763            state->prop_name = NULL;
764            state->props = NULL;
765            state->signals = NULL;
766            state->accels = NULL;
767
768            state->state = PARSER_WIDGET;
769        } else if (!strcmp(name, "placeholder")) {
770            /* this isn't a real child, so knock off  the last ChildInfo */
771            state->widget->n_children--;
772            state->state = PARSER_WIDGET_CHILD_PLACEHOLDER;
773        } else {
774            g_warning("Unexpected element <%s> inside <child>.", name);
775            state->prev_state = state->state;
776            state->state = PARSER_UNKNOWN;
777            state->unknown_depth++;
778        }
779        break;
780    case PARSER_WIDGET_CHILD_AFTER_WIDGET:
781        if (!strcmp(name, "packing")) {
782            state->state = PARSER_WIDGET_CHILD_PACKING;
783        } else {
784            g_warning("Unexpected element <%s> inside <child>.", name);
785            state->prev_state = state->state;
786            state->state = PARSER_UNKNOWN;
787            state->unknown_depth++;
788        }
789        break;
790    case PARSER_WIDGET_CHILD_PACKING:
791        if (!strcmp(name, "property")) {
792            gboolean bad_agent = FALSE;
793
794            if (state->prop_type != PROP_NONE &&
795                state->prop_type != PROP_CHILD)
796                g_warning("non child properties defined here (oh no!)");
797            state->translate_prop = FALSE;
798            for (i = 0; attrs && attrs[i] != NULL; i += 2) {
799                if (!strcmp(attrs[i], "name"))
800                    state->prop_name = alloc_propname(state->interface,
801                                                      attrs[i+1]);
802                else if (!strcmp(attrs[i], "translatable"))
803                    state->translate_prop = !strcmp(attrs[i+1], "yes");
804                else if (!strcmp(attrs[i], "context"))
805                    state->context_prop = !strcmp(attrs[i+1], "yes");
806                else if (!strcmp(attrs[i], "agent"))
807                    bad_agent = strcmp(attrs[i], "libglade") != 0;
808                else if (!strcmp(attrs[i], "comments"))
809                    /* Do nothing. */;
810                else
811                    g_warning("unknown attribute `%s' for <property>.",
812                              attrs[i]);
813            }
814            if (bad_agent) {
815                /* ignore the property ... */
816                state->prev_state = state->state;
817                state->state = PARSER_UNKNOWN;
818                state->unknown_depth++;
819            } else {
820                state->prop_type = PROP_CHILD;
821                state->state = PARSER_WIDGET_CHILD_PACKING_PROPERTY;
822            }
823        } else {
824            g_warning("Unexpected element <%s> inside <child>.", name);
825            state->prev_state = state->state;
826            state->state = PARSER_UNKNOWN;
827            state->unknown_depth++;
828        }
829        break;
830    case PARSER_WIDGET_CHILD_PACKING_PROPERTY:
831        g_warning("<property> element should be empty.  Found <%s>.", name);
832        state->prev_state = state->state;
833        state->state = PARSER_UNKNOWN;
834        state->unknown_depth++;
835        break;
836    case PARSER_WIDGET_CHILD_AFTER_PACKING:
837        g_warning("<child> should have no elements after <packing>.  Found <%s>.", name);
838        state->prev_state = state->state;
839        state->state = PARSER_UNKNOWN;
840        state->unknown_depth++;
841        break;
842    case PARSER_WIDGET_CHILD_PLACEHOLDER:
843        g_warning("<placeholder> should be empty.  Found <%s>.", name);
844        state->prev_state = state->state;
845        state->state = PARSER_UNKNOWN;
846        state->unknown_depth++;
847        break;
848    case PARSER_WIDGET_CHILD_AFTER_PLACEHOLDER:
849        /* this is a placeholder <child> element -- ignore extra elements */
850        state->prev_state = state->state;
851        state->state = PARSER_UNKNOWN;
852        state->unknown_depth++;
853        break;
854    case PARSER_FINISH:
855        g_warning("There should be no elements here.  Found <%s>.", name);
856        state->prev_state = state->state;
857        state->state = PARSER_UNKNOWN;
858        state->unknown_depth++;
859        break;
860    case PARSER_UNKNOWN:
861        state->unknown_depth++;
862        break;
863    }
864    /* truncate the content string ... */
865    g_string_truncate(state->content, 0);
866}
867
868static void
869glade_parser_end_element(GladeParseState *state, const xmlChar *name)
870{
871    GladeProperty prop;
872
873    GLADE_NOTE(PARSER, g_message("</%s> in state %s",
874                                 name, state_names[state->state]));
875
876    switch (state->state) {
877    case PARSER_START:
878        g_warning("should not be closing any elements in this state");
879        break;
880    case PARSER_GLADE_INTERFACE:
881        if (strcmp(name, "glade-interface") != 0)
882            g_warning("should find </glade-interface> here.  Found </%s>",
883                      name);
884        state->state = PARSER_FINISH;
885        break;
886    case PARSER_REQUIRES:
887        if (strcmp(name, "requires") != 0)
888            g_warning("should find </requires> here.  Found </%s>", name);
889        state->state = PARSER_GLADE_INTERFACE;
890        break;
891    case PARSER_WIDGET:
892    case PARSER_WIDGET_AFTER_ATK:
893    case PARSER_WIDGET_AFTER_SIGNAL:
894    case PARSER_WIDGET_AFTER_ACCEL:
895        if (strcmp(name, "widget") != 0)
896            g_warning("should find </widget> here.  Found </%s>", name);
897        flush_properties(state);
898        flush_signals(state);
899        flush_actions(state);
900        flush_relations(state);
901        flush_accels(state);
902        state->widget = state->widget->parent;
903        state->widget_depth--;
904
905        if (state->widget_depth == 0)
906            state->state = PARSER_GLADE_INTERFACE;
907        else
908            state->state = PARSER_WIDGET_CHILD_AFTER_WIDGET;
909        break;
910    case PARSER_WIDGET_PROPERTY:
911        if (strcmp(name, "property") != 0)
912            g_warning("should find </property> here.  Found </%s>", name);
913        if (!state->props)
914            state->props = g_array_new(FALSE, FALSE, sizeof(GladeProperty));
915        prop.name = state->prop_name;
916        if (state->translate_prop && state->content->str[0] != '\0') {
917            if (state->context_prop)
918                prop.value = alloc_string(state->interface,
919                                          g_strip_context(state->content->str,
920                                                           dgettext(state->domain, state->content->str)));
921            else
922                prop.value = alloc_string(state->interface,
923                                          dgettext(state->domain, state->content->str));
924        } else {
925            prop.value = alloc_string(state->interface, state->content->str);
926        }
927        g_array_append_val(state->props, prop);
928        state->prop_name = NULL;
929        state->state = PARSER_WIDGET;
930        break;
931    case PARSER_WIDGET_ATK:
932        if (strcmp(name, "accessibility") != 0)
933            g_warning("should find </accessibility> here.  Found </%s>", name);
934        flush_properties(state); /* flush the ATK properties */
935        state->state = PARSER_WIDGET_AFTER_ATK;
936        break;
937    case PARSER_WIDGET_ATK_PROPERTY:
938        if (strcmp(name, "atkproperty") != 0)
939            g_warning("should find </atkproperty> here.  Found </%s>", name);
940        if (!state->props)
941            state->props = g_array_new(FALSE, FALSE, sizeof(GladeProperty));
942        prop.name = state->prop_name;
943        if (state->translate_prop && state->content->str[0] != '\0') {
944            if (state->context_prop)
945                prop.value = alloc_string(state->interface,
946                                          g_strip_context(state->content->str,
947                                                           dgettext(state->domain, state->content->str)));
948            else
949                prop.value = alloc_string(state->interface,
950                                          dgettext(state->domain, state->content->str));
951        } else {
952            prop.value = alloc_string(state->interface, state->content->str);
953        }
954        g_array_append_val(state->props, prop);
955        state->prop_name = NULL;
956        state->state = PARSER_WIDGET_ATK;
957        break;
958    case PARSER_WIDGET_ATK_ACTION:
959        if (strcmp(name, "atkaction") != 0)
960            g_warning("should find </atkaction> here.  Found </%s>", name);
961        state->prop_name = NULL;
962        state->state = PARSER_WIDGET_ATK;
963        break;
964    case PARSER_WIDGET_ATK_RELATION:
965        if (strcmp(name, "atkrelation") != 0)
966            g_warning("should find </atkrelation> here.  Found </%s>", name);
967        state->prop_name = NULL;
968        state->state = PARSER_WIDGET_ATK;
969        break;
970    case PARSER_WIDGET_SIGNAL:
971        if (strcmp(name, "signal") != 0)
972            g_warning("should find </signal> here.  Found </%s>", name);
973        state->state = PARSER_WIDGET_AFTER_ATK;
974        break;
975    case PARSER_WIDGET_ACCEL:
976        if (strcmp(name, "accelerator") != 0)
977            g_warning("should find </accelerator> here.  Found </%s>", name);
978        state->state = PARSER_WIDGET_AFTER_SIGNAL;
979        break;
980    case PARSER_WIDGET_CHILD:
981        if (strcmp(name, "child") != 0)
982            g_warning("should find </child> here.  Found </%s>", name);
983        /* if we are ending the element in this state, then there
984         * hasn't been a <widget> element inside this <child>
985         * element. (If there was, then we would be in
986         * PARSER_WIDGET_CHILD_AFTER_WIDGET state. */
987        g_warning("no <widget> element found inside <child>.  Discarding");
988        g_free(state->widget->children[
989                        state->widget->n_children-1].properties);
990        state->widget->n_children--;
991        state->state = PARSER_WIDGET_AFTER_ACCEL;
992        break;
993    case PARSER_WIDGET_CHILD_AFTER_WIDGET:
994        if (strcmp(name, "child") != 0)
995            g_warning("should find </child> here.  Found </%s>", name);
996        state->state = PARSER_WIDGET_AFTER_ACCEL;
997        break;
998    case PARSER_WIDGET_CHILD_PACKING:
999        if (strcmp(name, "packing") != 0)
1000            g_warning("should find </packing> here.  Found </%s>", name);
1001        state->state = PARSER_WIDGET_CHILD_AFTER_PACKING;
1002        flush_properties(state); /* flush the properties. */
1003        break;
1004    case PARSER_WIDGET_CHILD_PACKING_PROPERTY:
1005        if (strcmp(name, "property") != 0)
1006            g_warning("should find </property> here.  Found </%s>", name);
1007        if (!state->props)
1008            state->props = g_array_new(FALSE, FALSE, sizeof(GladeProperty));
1009        prop.name = state->prop_name;
1010        if (state->translate_prop && state->content->str[0] != '\0') {
1011            if (state->context_prop)
1012                prop.value = alloc_string(state->interface,
1013                                          g_strip_context(state->content->str,
1014                                                           dgettext(state->domain, state->content->str)));
1015            else
1016                prop.value = alloc_string(state->interface,
1017                                          dgettext(state->domain, state->content->str));
1018        } else {
1019            prop.value = alloc_string(state->interface, state->content->str);
1020        }
1021        g_array_append_val(state->props, prop);
1022        state->prop_name = NULL;
1023        state->state = PARSER_WIDGET_CHILD_PACKING;
1024        break;
1025    case PARSER_WIDGET_CHILD_AFTER_PACKING:
1026        if (strcmp(name, "child") != 0)
1027            g_warning("should find </child> here.  Found </%s>", name);
1028        state->state = PARSER_WIDGET_AFTER_ACCEL;
1029        break;
1030    case PARSER_WIDGET_CHILD_PLACEHOLDER:
1031        if (strcmp(name, "placeholder") != 0)
1032            g_warning("should find </placeholder> here.  Found </%s>", name);
1033        state->state = PARSER_WIDGET_CHILD_AFTER_PLACEHOLDER;
1034        break;
1035    case PARSER_WIDGET_CHILD_AFTER_PLACEHOLDER:
1036        if (strcmp(name, "child") != 0)
1037            g_warning("should find </child> here.  Found </%s>", name);
1038        state->state = PARSER_WIDGET_AFTER_ACCEL;
1039        break;
1040    case PARSER_FINISH:
1041        g_warning("should not be closing any elements in this state");
1042        break;
1043    case PARSER_UNKNOWN:
1044        state->unknown_depth--;
1045        if (state->unknown_depth == 0)
1046            state->state = state->prev_state;
1047        break;
1048    }
1049}
1050
1051static void
1052glade_parser_characters(GladeParseState *state, const xmlChar *chars, int len)
1053{
1054    switch (state->state) {
1055    case PARSER_WIDGET_PROPERTY:
1056    case PARSER_WIDGET_ATK_PROPERTY:
1057    case PARSER_WIDGET_CHILD_PACKING_PROPERTY:
1058        g_string_append_len(state->content, chars, len);
1059        break;
1060    default:
1061        /* don't care about content in any other states */
1062        break;
1063    }
1064}
1065
1066static xmlEntityPtr
1067glade_parser_get_entity(GladeParseState *state, const xmlChar *name)
1068{
1069    return xmlGetPredefinedEntity(name);
1070}
1071
1072static void
1073glade_parser_warning(GladeParseState *state, const char *msg, ...)
1074{
1075    va_list args;
1076
1077    va_start(args, msg);
1078    g_logv("XML", G_LOG_LEVEL_WARNING, msg, args);
1079    va_end(args);
1080}
1081
1082static void
1083glade_parser_error(GladeParseState *state, const char *msg, ...)
1084{
1085    va_list args;
1086
1087    va_start(args, msg);
1088    g_logv("XML", G_LOG_LEVEL_CRITICAL, msg, args);
1089    va_end(args);
1090}
1091
1092static void
1093glade_parser_fatal_error(GladeParseState *state, const char *msg, ...)
1094{
1095    va_list args;
1096
1097    va_start(args, msg);
1098    g_logv("XML", G_LOG_LEVEL_ERROR, msg, args);
1099    va_end(args);
1100}
1101
1102static xmlSAXHandler glade_parser = {
1103    (internalSubsetSAXFunc)NULL,                       /* internalSubset */
1104    (isStandaloneSAXFunc)NULL,                         /* isStandalone */
1105    (hasInternalSubsetSAXFunc)NULL,                    /* hasInternalSubset */
1106    (hasExternalSubsetSAXFunc)NULL,                    /* hasExternalSubset */
1107    (resolveEntitySAXFunc)NULL,                        /* resolveEntity */
1108    (getEntitySAXFunc)glade_parser_get_entity,         /* getEntity */
1109    (entityDeclSAXFunc)NULL,                           /* entityDecl */
1110    (notationDeclSAXFunc)NULL,                         /* notationDecl */
1111    (attributeDeclSAXFunc)NULL,                        /* attributeDecl */
1112    (elementDeclSAXFunc)NULL,                          /* elementDecl */
1113    (unparsedEntityDeclSAXFunc)NULL,                   /* unparsedEntityDecl */
1114    (setDocumentLocatorSAXFunc)NULL,                   /* setDocumentLocator */
1115    (startDocumentSAXFunc)glade_parser_start_document, /* startDocument */
1116    (endDocumentSAXFunc)glade_parser_end_document,     /* endDocument */
1117    (startElementSAXFunc)glade_parser_start_element,   /* startElement */
1118    (endElementSAXFunc)glade_parser_end_element,       /* endElement */
1119    (referenceSAXFunc)NULL,                            /* reference */
1120    (charactersSAXFunc)glade_parser_characters,        /* characters */
1121    (ignorableWhitespaceSAXFunc)NULL,               /* ignorableWhitespace */
1122    (processingInstructionSAXFunc)NULL,             /* processingInstruction */
1123    (commentSAXFunc)NULL,                              /* comment */
1124    (warningSAXFunc)glade_parser_warning,              /* warning */
1125    (errorSAXFunc)glade_parser_error,                  /* error */
1126    (fatalErrorSAXFunc)glade_parser_fatal_error,       /* fatalError */
1127};
1128
1129static void
1130widget_info_free(GladeWidgetInfo *info)
1131{
1132    gint i;
1133
1134    g_return_if_fail(info != NULL);
1135
1136    g_free(info->properties);
1137    g_free(info->atk_props);
1138    g_free(info->signals);
1139    g_free(info->atk_actions);
1140    g_free(info->relations);
1141    g_free(info->accels);
1142
1143    for (i = 0; i < info->n_children; i++) {
1144        g_free(info->children[i].properties);
1145        widget_info_free(info->children[i].child);
1146    }
1147    g_free(info->children);
1148    g_free(info);
1149}
1150
1151/**
1152 * glade_interface_destroy
1153 * @interface: the GladeInterface structure.
1154 *
1155 * Frees a GladeInterface structure.
1156 */
1157void
1158glade_interface_destroy(GladeInterface *interface)
1159{
1160    gint i;
1161
1162    g_return_if_fail(interface != NULL);
1163
1164    /* free requirements */
1165    g_free(interface->requires);
1166
1167    for (i = 0; i < interface->n_toplevels; i++)
1168        widget_info_free(interface->toplevels[i]);
1169    g_free(interface->toplevels);
1170
1171    g_hash_table_destroy(interface->names);
1172
1173    /* free the strings hash table.  The destroy notify will take care
1174     * of the strings. */
1175    g_hash_table_destroy(interface->strings);
1176
1177    g_free(interface);
1178}
1179
1180/**
1181 * glade_parser_parse_file
1182 * @file: the filename of the glade XML file.
1183 * @domain: the translation domain for the XML file.
1184 *
1185 * This function parses a Glade XML interface file to a GladeInterface
1186 * object (which is libglade's internal representation of the
1187 * interface data).
1188 *
1189 * Generally, user code won't need to call this function.  Instead, it
1190 * should go through the GladeXML interfaces.
1191 *
1192 * Returns: the GladeInterface structure for the XML file.
1193 */
1194GladeInterface *
1195glade_parser_parse_file(const gchar *file, const gchar *domain)
1196{
1197    GladeParseState state = { 0 };
1198
1199    if (!g_file_test(file, G_FILE_TEST_IS_REGULAR)) {
1200        g_warning("could not find glade file '%s'", file);
1201        return NULL;
1202    }
1203
1204    state.interface = NULL;
1205    if (domain)
1206        state.domain = domain;
1207    else
1208        state.domain = textdomain(NULL);
1209
1210    if (xmlSAXUserParseFile(&glade_parser, &state, file) < 0) {
1211        g_warning("document not well formed");
1212        if (state.interface)
1213            glade_interface_destroy (state.interface);
1214        return NULL;
1215    }
1216    if (state.state != PARSER_FINISH) {
1217        g_warning("did not finish in PARSER_FINISH state");
1218        if (state.interface)
1219            glade_interface_destroy(state.interface);
1220        return NULL;
1221    }
1222    return state.interface;
1223}
1224
1225/**
1226 * glade_parser_parse_buffer
1227 * @buffer: a buffer in memory containing XML data.
1228 * @len: the length of @buffer.
1229 * @domain: the translation domain for the XML file.
1230 *
1231 * This function is similar to glade_parser_parse_file, except that it
1232 * parses XML data from a buffer in memory.  This could be used to
1233 * embed an interface into the executable, for instance.
1234 *
1235 * Generally, user code won't need to call this function.  Instead, it
1236 * should go through the GladeXML interfaces.
1237 *
1238 * Returns: the GladeInterface structure for the XML buffer.
1239 */
1240GladeInterface *
1241glade_parser_parse_buffer(const gchar *buffer, gint len, const gchar *domain)
1242{
1243    GladeParseState state = { 0 };
1244
1245    state.interface = NULL;
1246    if (domain)
1247        state.domain = domain;
1248    else
1249        state.domain = textdomain(NULL);
1250
1251    if (xmlSAXUserParseMemory(&glade_parser, &state, buffer, len) < 0) {
1252        g_warning("document not well formed!");
1253        if (state.interface)
1254            glade_interface_destroy (state.interface);
1255        return NULL;
1256    }
1257    if (state.state != PARSER_FINISH) {
1258        g_warning("did not finish in PARSER_FINISH state!");
1259        if (state.interface)
1260            glade_interface_destroy(state.interface);
1261        return NULL;
1262    }
1263    return state.interface;
1264}
1265
1266static void
1267dump_widget(xmlNode *parent, GladeWidgetInfo *info, gint indent)
1268{
1269    xmlNode *widget = xmlNewNode(NULL, "widget");
1270    gint i, j;
1271
1272    xmlSetProp(widget, "class", info->classname);
1273    xmlSetProp(widget, "id", info->name);
1274    xmlAddChild(parent, widget);
1275    xmlNodeAddContent(widget, "\n");
1276
1277    for (i = 0; i < info->n_properties; i++) {
1278        xmlNode *node;
1279
1280        for (j = 0; j < indent + 1; j++)
1281            xmlNodeAddContent(widget, "  ");
1282        node = xmlNewNode(NULL, "property");
1283        xmlSetProp(node, "name", info->properties[i].name);
1284        xmlNodeSetContent(node, info->properties[i].value);
1285        xmlAddChild(widget, node);
1286        xmlNodeAddContent(widget, "\n");
1287    }
1288
1289    if (info->n_atk_props != 0) {
1290        xmlNode *atk;
1291
1292        for (j = 0; j < indent + 1; j++)
1293            xmlNodeAddContent(widget, "  ");
1294        atk = xmlNewNode(NULL, "accessibility");
1295        xmlAddChild(widget, atk);
1296        xmlNodeAddContent(widget, "\n");
1297        xmlNodeAddContent(atk, "\n");
1298
1299        for (i = 0; i < info->n_atk_props; i++) {
1300            xmlNode *node;
1301
1302            for (j = 0; j < indent + 2; j++)
1303                xmlNodeAddContent(atk, "  ");
1304            node = xmlNewNode(NULL, "property");
1305            xmlSetProp(node, "name", info->atk_props[i].name);
1306            xmlNodeSetContent(node, info->atk_props[i].value);
1307            xmlAddChild(atk, node);
1308            xmlNodeAddContent(atk, "\n");
1309        }
1310        for (j = 0; j < indent + 1; j++)
1311            xmlNodeAddContent(atk, "  ");
1312    }
1313
1314    for (i = 0; i < info->n_signals; i++) {
1315        xmlNode *node;
1316
1317        for (j = 0; j < indent + 1; j++)
1318            xmlNodeAddContent(widget, "  ");
1319
1320        node = xmlNewNode(NULL, "signal");
1321        xmlSetProp(node, "name", info->signals[i].name);
1322        xmlSetProp(node, "handler", info->signals[i].handler);
1323        if (info->signals[i].after)
1324            xmlSetProp(node, "after", "yes");
1325        if (info->signals[i].object)
1326            xmlSetProp(node, "object", info->signals[i].object);
1327        xmlAddChild(widget, node);
1328        xmlNodeAddContent(widget, "\n");
1329    }
1330
1331    for (i = 0; i < info->n_accels; i++) {
1332        xmlNode *node;
1333
1334        for (j = 0; j < indent + 1; j++)
1335            xmlNodeAddContent(widget, "  ");
1336
1337        node = xmlNewNode(NULL, "accelerator");
1338        xmlSetProp(node, "key", gdk_keyval_name(info->accels[i].key));
1339        xmlSetProp(node, "modifier", "something"/*info->accels[i].modifiers*/);
1340        xmlSetProp(node, "signal", info->accels[i].signal);
1341        xmlAddChild(widget, node);
1342        xmlNodeAddContent(widget, "\n");
1343    }
1344
1345    for (i = 0; i < info->n_children; i++) {
1346        xmlNode *child;
1347        GladeChildInfo *childinfo = &info->children[i];
1348        gint k;
1349
1350        for (j = 0; j < indent + 1; j++)
1351            xmlNodeAddContent(widget, "  ");
1352
1353        child = xmlNewNode(NULL, "child");
1354        if (childinfo->internal_child)
1355            xmlSetProp(child, "internal-child", childinfo->internal_child);
1356        xmlAddChild(widget, child);
1357        xmlNodeAddContent(widget, "\n");
1358        xmlNodeAddContent(child, "\n");
1359
1360        for (k = 0; k < childinfo->n_properties; k++) {
1361            xmlNode *node;
1362
1363            for (j = 0; j < indent + 2; j++)
1364                xmlNodeAddContent(child, "  ");
1365            node = xmlNewNode(NULL, "property");
1366            xmlSetProp(node, "name", childinfo->properties[i].name);
1367            xmlNodeSetContent(node, childinfo->properties[i].value);
1368            xmlAddChild(child, node);
1369            xmlNodeAddContent(child, "\n");
1370        }
1371
1372        for (j = 0; j < indent + 2; j++)
1373            xmlNodeAddContent(child, "  ");
1374        dump_widget(child, childinfo->child, indent + 2);
1375        xmlNodeAddContent(child, "\n");
1376
1377        for (j = 0; j < indent + 1; j++)
1378            xmlNodeAddContent(child, "  ");
1379    }
1380
1381    for (j = 0; j < indent; j++)
1382        xmlNodeAddContent(widget, "  ");
1383}
1384
1385/**
1386 * glade_interface_dump
1387 * @interface: the GladeInterface
1388 * @filename: the filename to write the interface data to.
1389 *
1390 * This function dumps the contents of a GladeInterface into a file as
1391 * XML.  It is intended mainly as a debugging tool.
1392 */
1393void
1394glade_interface_dump(GladeInterface *interface, const gchar *filename)
1395{
1396    xmlDoc *doc;
1397    xmlNode *root;
1398    gint i;
1399
1400    doc = xmlNewDoc("1.0");
1401    doc->standalone = FALSE;
1402    xmlCreateIntSubset(doc, "glade-interface",
1403                       NULL, "glade-2.0.dtd");
1404    root = xmlNewNode(NULL, "glade-interface");
1405    xmlDocSetRootElement(doc, root);
1406
1407    xmlNodeAddContent(root, "\n");
1408
1409    for (i = 0; i < interface->n_requires; i++) {
1410        xmlNode *node = xmlNewNode(NULL, "requires");
1411
1412        xmlSetProp(node, "lib", interface->requires[i]);
1413
1414        xmlNodeAddContent(root, "  ");
1415        xmlAddChild(root, node);
1416        xmlNodeAddContent(root, "\n");
1417    }
1418
1419    for (i = 0; i < interface->n_toplevels; i++) {
1420        xmlNodeAddContent(root, "  ");
1421        dump_widget(root, interface->toplevels[i], 1);
1422        xmlNodeAddContent(root, "\n");
1423    }
1424
1425    /* output */
1426   
1427    xmlSaveFileEnc(filename, doc, "UTF-8");
1428    xmlFreeDoc(doc);
1429}
1430
1431#if 0
1432int
1433main(int argc, char **argv) {
1434    gtk_init(&argc, &argv);
1435    if (argc > 1) {
1436        GladeInterface *interface = glade_parser_parse_file(argv[1]);
1437        g_message("output: %p", interface);
1438        if (interface) {
1439            glade_interface_dump(interface, "/dev/stdout");
1440            glade_interface_destroy(interface);
1441        }
1442    } else
1443        g_message("need filename");
1444    return 0;
1445}
1446#endif
Note: See TracBrowser for help on using the repository browser.