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 | |
---|
42 | typedef 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 |
---|
69 | static 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 | |
---|
96 | typedef struct _GladeParseState GladeParseState; |
---|
97 | struct _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 | |
---|
123 | static gchar * |
---|
124 | alloc_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 | |
---|
136 | static gchar * |
---|
137 | alloc_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 | |
---|
155 | static GladeWidgetInfo * |
---|
156 | create_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 | |
---|
175 | static inline void |
---|
176 | flush_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 | |
---|
217 | static inline void |
---|
218 | flush_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 | |
---|
228 | static inline void |
---|
229 | flush_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 | |
---|
239 | static inline void |
---|
240 | flush_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 | |
---|
250 | static inline void |
---|
251 | flush_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 | |
---|
261 | static inline void |
---|
262 | handle_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 | |
---|
288 | static inline void |
---|
289 | handle_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 | |
---|
314 | static inline void |
---|
315 | handle_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 | |
---|
347 | static inline void |
---|
348 | handle_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 | |
---|
429 | static inline void |
---|
430 | handle_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 | |
---|
459 | static void |
---|
460 | glade_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 | |
---|
487 | static void |
---|
488 | glade_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 | |
---|
498 | static void |
---|
499 | glade_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 | |
---|
868 | static void |
---|
869 | glade_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 | |
---|
1051 | static void |
---|
1052 | glade_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 | |
---|
1066 | static xmlEntityPtr |
---|
1067 | glade_parser_get_entity(GladeParseState *state, const xmlChar *name) |
---|
1068 | { |
---|
1069 | return xmlGetPredefinedEntity(name); |
---|
1070 | } |
---|
1071 | |
---|
1072 | static void |
---|
1073 | glade_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 | |
---|
1082 | static void |
---|
1083 | glade_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 | |
---|
1092 | static void |
---|
1093 | glade_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 | |
---|
1102 | static 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 | |
---|
1129 | static void |
---|
1130 | widget_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 | */ |
---|
1157 | void |
---|
1158 | glade_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 | */ |
---|
1194 | GladeInterface * |
---|
1195 | glade_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 | */ |
---|
1240 | GladeInterface * |
---|
1241 | glade_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 | |
---|
1266 | static void |
---|
1267 | dump_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 | */ |
---|
1393 | void |
---|
1394 | glade_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 |
---|
1432 | int |
---|
1433 | main(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 |
---|