1/* gtkbuilderparser.c
2 * Copyright (C) 2006-2007 Async Open Source,
3 * Johan Dahlin <jdahlin@async.com.br>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "config.h"
20
21#include "gtkbuilderprivate.h"
22
23#include "gtkbuildableprivate.h"
24#include "gtkbuilderscopeprivate.h"
25#include "gtkdebug.h"
26#include "gtkintl.h"
27#include "gtktypebuiltins.h"
28#include "gtkversion.h"
29#include "gdkprofilerprivate.h"
30
31#include "gtkprivate.h"
32
33#include <gio/gio.h>
34#include <string.h>
35
36
37typedef struct
38{
39 const GtkBuildableParser *last_parser;
40 gpointer last_user_data;
41 int last_depth;
42} GtkBuildableParserStack;
43
44static void
45pop_subparser_stack (GtkBuildableParseContext *context)
46{
47 GtkBuildableParserStack *stack = &g_array_index (context->subparser_stack, GtkBuildableParserStack,
48 context->subparser_stack->len - 1);
49
50 context->awaiting_pop = TRUE;
51 context->held_user_data = context->user_data;
52
53 context->user_data = stack->last_user_data;
54 context->parser = stack->last_parser;
55
56 g_array_set_size (array: context->subparser_stack, length: context->subparser_stack->len - 1);
57}
58
59static void
60possibly_finish_subparser (GtkBuildableParseContext *context)
61{
62 GtkBuildableParserStack *stack;
63
64 if (!context->subparser_stack ||
65 context->subparser_stack->len == 0)
66 return;
67
68 stack = &g_array_index (context->subparser_stack, GtkBuildableParserStack,
69 context->subparser_stack->len - 1);
70
71 if (stack->last_depth == context->tag_stack->len)
72 pop_subparser_stack (context);
73}
74
75static void
76proxy_start_element (GMarkupParseContext *gm_context,
77 const char *element_name,
78 const char **attribute_names,
79 const char **attribute_values,
80 gpointer user_data,
81 GError **error)
82{
83 GtkBuildableParseContext *context = user_data;
84
85 // Due to the way GMarkup works we're sure this will live until the end_element callback
86 g_ptr_array_add (array: context->tag_stack, data: (char *)element_name);
87
88 if (context->parser->start_element)
89 context->parser->start_element (context, element_name,
90 attribute_names, attribute_values,
91 context->user_data, error);
92}
93
94static void
95proxy_end_element (GMarkupParseContext *gm_context,
96 const char *element_name,
97 gpointer user_data,
98 GError **error)
99{
100 GtkBuildableParseContext *context = user_data;
101
102 possibly_finish_subparser (context);
103
104 if (context->parser->end_element)
105 context->parser->end_element (context, element_name, context->user_data, error);
106
107 g_ptr_array_set_size (array: context->tag_stack, length: context->tag_stack->len - 1);
108}
109
110static void
111proxy_text (GMarkupParseContext *gm_context,
112 const char *text,
113 gsize text_len,
114 gpointer user_data,
115 GError **error)
116{
117 GtkBuildableParseContext *context = user_data;
118
119 if (context->parser->text)
120 context->parser->text (context, text, text_len, context->user_data, error);
121}
122
123static void
124proxy_error (GMarkupParseContext *gm_context,
125 GError *error,
126 gpointer user_data)
127{
128 GtkBuildableParseContext *context = user_data;
129
130 if (context->parser->error)
131 context->parser->error (context, error, context->user_data);
132
133 /* report the error all the way up to free all the user-data */
134
135 if (!context->subparser_stack)
136 return;
137
138 while (context->subparser_stack->len > 0)
139 {
140 pop_subparser_stack (context);
141 context->awaiting_pop = FALSE; /* already been freed */
142
143 if (context->parser->error)
144 context->parser->error (context, error, context->user_data);
145 }
146}
147
148static const GMarkupParser gmarkup_parser = {
149 proxy_start_element,
150 proxy_end_element,
151 proxy_text,
152 NULL,
153 proxy_error,
154};
155
156static void
157gtk_buildable_parse_context_init (GtkBuildableParseContext *context,
158 const GtkBuildableParser *parser,
159 gpointer user_data)
160{
161 context->internal_callbacks = &gmarkup_parser;
162 context->ctx = NULL;
163
164 context->parser = parser;
165 context->user_data = user_data;
166
167 context->subparser_stack = NULL;
168 context->tag_stack = g_ptr_array_new ();
169 context->held_user_data = NULL;
170 context->awaiting_pop = FALSE;
171}
172
173static void
174gtk_buildable_parse_context_free (GtkBuildableParseContext *context)
175{
176 if (context->subparser_stack)
177 g_array_unref (array: context->subparser_stack);
178
179 g_ptr_array_unref (array: context->tag_stack);
180}
181
182static gboolean
183gtk_buildable_parse_context_parse (GtkBuildableParseContext *context,
184 const char *text,
185 gssize text_len,
186 GError **error)
187{
188 gboolean res;
189
190 if (_gtk_buildable_parser_is_precompiled (data: text, data_len: text_len))
191 {
192 res = _gtk_buildable_parser_replay_precompiled (context, data: text, data_len: text_len, error);
193 }
194 else
195 {
196 context->ctx = g_markup_parse_context_new (parser: context->internal_callbacks,
197 flags: G_MARKUP_TREAT_CDATA_AS_TEXT,
198 user_data: context, NULL);
199 res = g_markup_parse_context_parse (context: context->ctx, text, text_len, error);
200 g_markup_parse_context_free (context: context->ctx);
201 }
202
203 return res;
204}
205
206
207/**
208 * gtk_buildable_parse_context_push:
209 * @context: a `GtkBuildableParseContext`
210 * @parser: a `GtkBuildableParser`
211 * @user_data: user data to pass to `GtkBuildableParser` functions
212 *
213 * Temporarily redirects markup data to a sub-parser.
214 *
215 * This function may only be called from the start_element handler of
216 * a `GtkBuildableParser`. It must be matched with a corresponding call to
217 * gtk_buildable_parse_context_pop() in the matching end_element handler
218 * (except in the case that the parser aborts due to an error).
219 *
220 * All tags, text and other data between the matching tags is
221 * redirected to the subparser given by @parser. @user_data is used
222 * as the user_data for that parser. @user_data is also passed to the
223 * error callback in the event that an error occurs. This includes
224 * errors that occur in subparsers of the subparser.
225 *
226 * The end tag matching the start tag for which this call was made is
227 * handled by the previous parser (which is given its own user_data)
228 * which is why gtk_buildable_parse_context_pop() is provided to allow "one
229 * last access" to the @user_data provided to this function. In the
230 * case of error, the @user_data provided here is passed directly to
231 * the error callback of the subparser and gtk_buildable_parse_context_pop()
232 * should not be called. In either case, if @user_data was allocated
233 * then it ought to be freed from both of these locations.
234 *
235 * This function is not intended to be directly called by users
236 * interested in invoking subparsers. Instead, it is intended to be
237 * used by the subparsers themselves to implement a higher-level
238 * interface.
239 *
240 * For an example of how to use this, see g_markup_parse_context_push() which
241 * has the same kind of API.
242 **/
243void
244gtk_buildable_parse_context_push (GtkBuildableParseContext *context,
245 const GtkBuildableParser *parser,
246 gpointer user_data)
247{
248 GtkBuildableParserStack stack = { 0 };
249
250 stack.last_parser = context->parser;
251 stack.last_user_data = context->user_data;
252 stack.last_depth = context->tag_stack->len; // If at end_element time we're this deep, then pop it
253
254 context->parser = parser;
255 context->user_data = user_data;
256
257 if (!context->subparser_stack)
258 context->subparser_stack = g_array_new (FALSE, FALSE, element_size: sizeof (GtkBuildableParserStack));
259
260 g_array_append_val (context->subparser_stack, stack);
261}
262
263/**
264 * gtk_buildable_parse_context_pop:
265 * @context: a `GtkBuildableParseContext`
266 *
267 * Completes the process of a temporary sub-parser redirection.
268 *
269 * This function exists to collect the user_data allocated by a
270 * matching call to gtk_buildable_parse_context_push(). It must be called
271 * in the end_element handler corresponding to the start_element
272 * handler during which gtk_buildable_parse_context_push() was called.
273 * You must not call this function from the error callback -- the
274 * @user_data is provided directly to the callback in that case.
275 *
276 * This function is not intended to be directly called by users
277 * interested in invoking subparsers. Instead, it is intended to
278 * be used by the subparsers themselves to implement a higher-level
279 * interface.
280 *
281 * Returns: the user data passed to gtk_buildable_parse_context_push()
282 */
283gpointer
284gtk_buildable_parse_context_pop (GtkBuildableParseContext *context)
285{
286 gpointer user_data;
287
288 if (!context->awaiting_pop)
289 possibly_finish_subparser (context);
290
291 g_assert (context->awaiting_pop);
292
293 context->awaiting_pop = FALSE;
294
295 user_data = context->held_user_data;
296 context->held_user_data = NULL;
297
298 return user_data;
299}
300
301/**
302 * gtk_buildable_parse_context_get_element:
303 * @context: a `GtkBuildablParseContext`
304 *
305 * Retrieves the name of the currently open element.
306 *
307 * If called from the start_element or end_element handlers this will
308 * give the element_name as passed to those functions. For the parent
309 * elements, see gtk_buildable_parse_context_get_element_stack().
310 *
311 * Returns: (nullable): the name of the currently open element
312 */
313const char *
314gtk_buildable_parse_context_get_element (GtkBuildableParseContext *context)
315{
316 if (context->tag_stack->len > 0)
317 return g_ptr_array_index (context->tag_stack, context->tag_stack->len - 1);
318 return NULL;
319}
320
321/**
322 * gtk_buildable_parse_context_get_element_stack:
323 * @context: a `GtkBuildableParseContext`
324 *
325 * Retrieves the element stack from the internal state of the parser.
326 *
327 * The returned `GPtrArray` is an array of strings where the last item is
328 * the currently open tag (as would be returned by
329 * gtk_buildable_parse_context_get_element()) and the previous item is its
330 * immediate parent.
331 *
332 * This function is intended to be used in the start_element and
333 * end_element handlers where gtk_buildable_parse_context_get_element()
334 * would merely return the name of the element that is being
335 * processed.
336 *
337 * Returns: (transfer none) (element-type utf8): the element stack, which must not be modified
338 */
339GPtrArray *
340gtk_buildable_parse_context_get_element_stack (GtkBuildableParseContext *context)
341{
342 return context->tag_stack;
343}
344
345/**
346 * gtk_buildable_parse_context_get_position:
347 * @context: a `GtkBuildableParseContext`
348 * @line_number: (out) (optional): return location for a line number
349 * @char_number: (out) (optional): return location for a char-on-line number
350 *
351 * Retrieves the current line number and the number of the character on
352 * that line. Intended for use in error messages; there are no strict
353 * semantics for what constitutes the "current" line number other than
354 * "the best number we could come up with for error messages."
355 */
356void
357gtk_buildable_parse_context_get_position (GtkBuildableParseContext *context,
358 int *line_number,
359 int *char_number)
360
361{
362 if (context->ctx)
363 g_markup_parse_context_get_position (context: context->ctx, line_number, char_number);
364 else
365 {
366 if (line_number)
367 *line_number = 0;
368 if (char_number)
369 *char_number = 0;
370 }
371}
372
373static void free_property_info (PropertyInfo *info);
374static void free_object_info (ObjectInfo *info);
375
376
377static inline void
378state_push (ParserData *data, gpointer info)
379{
380 g_ptr_array_add (array: data->stack, data: info);
381}
382
383static inline gpointer
384state_peek (ParserData *data)
385{
386 if (!data->stack ||
387 data->stack->len == 0)
388 return NULL;
389
390 return g_ptr_array_index (data->stack, data->stack->len - 1);
391}
392
393static inline gpointer
394state_pop (ParserData *data)
395{
396 gpointer old = NULL;
397
398 g_assert (data->stack);
399
400 old = state_peek (data);
401 g_assert (old);
402 data->stack->len --;
403 return old;
404}
405#define state_peek_info(data, st) ((st*)state_peek(data))
406#define state_pop_info(data, st) ((st*)state_pop(data))
407
408static void
409error_missing_attribute (ParserData *data,
410 const char *tag,
411 const char *attribute,
412 GError **error)
413{
414 int line, col;
415
416 gtk_buildable_parse_context_get_position (context: &data->ctx, line_number: &line, char_number: &col);
417
418 g_set_error (err: error,
419 GTK_BUILDER_ERROR,
420 code: GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
421 format: "%s:%d:%d <%s> requires attribute '%s'",
422 data->filename, line, col, tag, attribute);
423}
424
425static void
426error_invalid_tag (ParserData *data,
427 const char *tag,
428 const char *expected,
429 GError **error)
430{
431 int line, col;
432
433 gtk_buildable_parse_context_get_position (context: &data->ctx, line_number: &line, char_number: &col);
434
435 if (expected)
436 g_set_error (err: error,
437 GTK_BUILDER_ERROR,
438 code: GTK_BUILDER_ERROR_INVALID_TAG,
439 format: "%s:%d:%d <%s> is not a valid tag here, expected a <%s> tag",
440 data->filename, line, col, tag, expected);
441 else
442 g_set_error (err: error,
443 GTK_BUILDER_ERROR,
444 code: GTK_BUILDER_ERROR_INVALID_TAG,
445 format: "%s:%d:%d <%s> is not a valid tag here",
446 data->filename, line, col, tag);
447}
448
449static void
450error_unhandled_tag (ParserData *data,
451 const char *tag,
452 GError **error)
453{
454 int line, col;
455
456 gtk_buildable_parse_context_get_position (context: &data->ctx, line_number: &line, char_number: &col);
457 g_set_error (err: error,
458 GTK_BUILDER_ERROR,
459 code: GTK_BUILDER_ERROR_UNHANDLED_TAG,
460 format: "%s:%d:%d Unhandled tag: <%s>",
461 data->filename, line, col, tag);
462}
463
464static GObject *
465builder_construct (ParserData *data,
466 ObjectInfo *object_info,
467 GError **error)
468{
469 GObject *object;
470
471 g_assert (object_info != NULL);
472
473 if (object_info->object == NULL)
474 {
475 object = _gtk_builder_construct (builder: data->builder, info: object_info, error);
476 if (!object)
477 return NULL;
478 }
479 else
480 {
481 /* We're building a template, the object is already set and
482 * we just want to resolve the properties at the right time
483 */
484 object = object_info->object;
485 _gtk_builder_apply_properties (builder: data->builder, info: object_info, error);
486 }
487
488 g_assert (G_IS_OBJECT (object));
489
490 object_info->object = object;
491
492 return object;
493}
494
495static void
496parse_requires (ParserData *data,
497 const char *element_name,
498 const char **names,
499 const char **values,
500 GError **error)
501{
502 RequiresInfo *req_info;
503 const char *library = NULL;
504 const char *version = NULL;
505 char **split;
506 int version_major = 0;
507 int version_minor = 0;
508
509 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
510 first_type: G_MARKUP_COLLECT_STRING, first_attr: "lib", &library,
511 G_MARKUP_COLLECT_STRING, "version", &version,
512 G_MARKUP_COLLECT_INVALID))
513 {
514 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
515 return;
516 }
517
518 if (!(split = g_strsplit (string: version, delimiter: ".", max_tokens: 2)) || !split[0] || !split[1])
519 {
520 g_set_error (err: error,
521 GTK_BUILDER_ERROR,
522 code: GTK_BUILDER_ERROR_INVALID_VALUE,
523 format: "'version' attribute has malformed value '%s'", version);
524 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
525 return;
526 }
527 version_major = g_ascii_strtoll (nptr: split[0], NULL, base: 10);
528 version_minor = g_ascii_strtoll (nptr: split[1], NULL, base: 10);
529 g_strfreev (str_array: split);
530
531 req_info = g_slice_new0 (RequiresInfo);
532 req_info->library = g_strdup (str: library);
533 req_info->major = version_major;
534 req_info->minor = version_minor;
535 state_push (data, info: req_info);
536 req_info->tag_type = TAG_REQUIRES;
537}
538
539static gboolean
540is_requested_object (const char *object,
541 ParserData *data)
542{
543 int i;
544
545 for (i = 0; data->requested_objects[i]; ++i)
546 {
547 if (g_strcmp0 (str1: data->requested_objects[i], str2: object) == 0)
548 return TRUE;
549 }
550
551 return FALSE;
552}
553
554static void
555parse_object (GtkBuildableParseContext *context,
556 ParserData *data,
557 const char *element_name,
558 const char **names,
559 const char **values,
560 GError **error)
561{
562 ObjectInfo *object_info;
563 ChildInfo* child_info;
564 GType object_type = G_TYPE_INVALID;
565 const char *object_class = NULL;
566 const char *constructor = NULL;
567 const char *type_func = NULL;
568 const char *object_id = NULL;
569 char *internal_id = NULL;
570 int line;
571 gpointer line_ptr;
572 gboolean has_duplicate;
573
574 child_info = state_peek_info (data, ChildInfo);
575 if (child_info && child_info->tag_type == TAG_OBJECT)
576 {
577 error_invalid_tag (data, tag: element_name, NULL, error);
578 return;
579 }
580
581 /* Even though 'class' is a mandatory attribute, we don't flag its
582 * absence here because it's supposed to throw
583 * GTK_BUILDER_ERROR_MISSING_ATTRIBUTE, not
584 * G_MARKUP_ERROR_MISSING_ATTRIBUTE. It's handled immediately
585 * afterwards.
586 */
587 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
588 first_type: G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, first_attr: "class", &object_class,
589 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "constructor", &constructor,
590 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "type-func", &type_func,
591 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "id", &object_id,
592 G_MARKUP_COLLECT_INVALID))
593 {
594 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
595 return;
596 }
597
598 if (!object_class)
599 {
600 error_missing_attribute (data, tag: element_name, attribute: "class", error);
601 return;
602 }
603
604 if (type_func)
605 {
606 /* Call the GType function, and return the GType, it's guaranteed afterwards
607 * that g_type_from_name on the name will return our GType
608 */
609 object_type = gtk_builder_scope_get_type_from_function (self: gtk_builder_get_scope (builder: data->builder), builder: data->builder, function_name: type_func);
610 if (object_type == G_TYPE_INVALID)
611 {
612 g_set_error (err: error,
613 GTK_BUILDER_ERROR,
614 code: GTK_BUILDER_ERROR_INVALID_TYPE_FUNCTION,
615 format: "Invalid type function '%s'", type_func);
616 _gtk_builder_prefix_error (builder: data->builder, context, error);
617 return;
618 }
619 }
620 else
621 {
622 g_assert_nonnull (object_class);
623
624 object_type = gtk_builder_get_type_from_name (builder: data->builder, type_name: object_class);
625 if (object_type == G_TYPE_INVALID)
626 {
627 g_set_error (err: error,
628 GTK_BUILDER_ERROR,
629 code: GTK_BUILDER_ERROR_INVALID_VALUE,
630 format: "Invalid object type '%s'", object_class);
631 _gtk_builder_prefix_error (builder: data->builder, context, error);
632 return;
633 }
634 }
635
636 if (!object_id)
637 {
638 internal_id = g_strdup_printf (format: "___object_%d___", ++data->object_counter);
639 object_id = internal_id;
640 }
641
642 ++data->cur_object_level;
643
644 /* check if we reached a requested object (if it is specified) */
645 if (data->requested_objects && !data->inside_requested_object)
646 {
647 if (is_requested_object (object: object_id, data))
648 {
649 data->requested_object_level = data->cur_object_level;
650
651 GTK_NOTE (BUILDER,
652 g_message ("requested object \"%s\" found at level %d",
653 object_id, data->requested_object_level));
654
655 data->inside_requested_object = TRUE;
656 }
657 else
658 {
659 g_free (mem: internal_id);
660 return;
661 }
662 }
663
664 object_info = g_slice_new0 (ObjectInfo);
665 object_info->tag_type = TAG_OBJECT;
666 object_info->type = object_type;
667 object_info->oclass = g_type_class_ref (type: object_type);
668 object_info->id = (internal_id) ? internal_id : g_strdup (str: object_id);
669 object_info->constructor = g_strdup (str: constructor);
670 object_info->parent = (CommonInfo*)child_info;
671 state_push (data, info: object_info);
672
673 has_duplicate = g_hash_table_lookup_extended (hash_table: data->object_ids, lookup_key: object_id, NULL, value: &line_ptr);
674 if (has_duplicate != 0)
675 {
676 g_set_error (err: error,
677 GTK_BUILDER_ERROR,
678 code: GTK_BUILDER_ERROR_DUPLICATE_ID,
679 format: "Duplicate object ID '%s' (previously on line %d)",
680 object_id, GPOINTER_TO_INT (line_ptr));
681 _gtk_builder_prefix_error (builder: data->builder, context, error);
682 return;
683 }
684
685 gtk_buildable_parse_context_get_position (context, line_number: &line, NULL);
686 g_hash_table_insert (hash_table: data->object_ids, key: g_strdup (str: object_id), GINT_TO_POINTER (line));
687}
688
689static void
690parse_template (GtkBuildableParseContext *context,
691 ParserData *data,
692 const char *element_name,
693 const char **names,
694 const char **values,
695 GError **error)
696{
697 ObjectInfo *object_info;
698 const char *object_class = NULL;
699 const char *parent_class = NULL;
700 int line;
701 gpointer line_ptr;
702 gboolean has_duplicate;
703 GType template_type;
704 GType parsed_type;
705
706 template_type = _gtk_builder_get_template_type (builder: data->builder);
707
708 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
709 first_type: G_MARKUP_COLLECT_STRING, first_attr: "class", &object_class,
710 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "parent", &parent_class,
711 G_MARKUP_COLLECT_INVALID))
712 {
713 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
714 return;
715 }
716
717 if (template_type == 0)
718 {
719 g_set_error (err: error,
720 GTK_BUILDER_ERROR,
721 code: GTK_BUILDER_ERROR_UNHANDLED_TAG,
722 format: "Template declaration (class '%s', parent '%s') where templates aren't supported",
723 object_class, parent_class ? parent_class : "GtkWidget");
724 _gtk_builder_prefix_error (builder: data->builder, context, error);
725 return;
726 }
727 else if (state_peek (data) != NULL)
728 {
729 error_invalid_tag (data, tag: "template", NULL, error);
730 return;
731 }
732
733 parsed_type = g_type_from_name (name: object_class);
734 if (template_type != parsed_type)
735 {
736 g_set_error (err: error,
737 GTK_BUILDER_ERROR,
738 code: GTK_BUILDER_ERROR_TEMPLATE_MISMATCH,
739 format: "Parsed template definition for type '%s', expected type '%s'",
740 object_class, g_type_name (type: template_type));
741 _gtk_builder_prefix_error (builder: data->builder, context, error);
742 return;
743 }
744
745 if (parent_class)
746 {
747 GType parent_type = g_type_from_name (name: parent_class);
748 GType expected_type = g_type_parent (type: parsed_type);
749
750 if (parent_type == G_TYPE_INVALID)
751 {
752 g_set_error (err: error, GTK_BUILDER_ERROR,
753 code: GTK_BUILDER_ERROR_INVALID_VALUE,
754 format: "Invalid template parent type '%s'", parent_class);
755 _gtk_builder_prefix_error (builder: data->builder, context, error);
756 return;
757 }
758 if (parent_type != expected_type)
759 {
760 g_set_error (err: error, GTK_BUILDER_ERROR,
761 code: GTK_BUILDER_ERROR_TEMPLATE_MISMATCH,
762 format: "Template parent type '%s' does not match instance parent type '%s'.",
763 parent_class, g_type_name (type: expected_type));
764 _gtk_builder_prefix_error (builder: data->builder, context, error);
765 return;
766 }
767 }
768
769 ++data->cur_object_level;
770
771 object_info = g_slice_new0 (ObjectInfo);
772 object_info->tag_type = TAG_TEMPLATE;
773 object_info->type = parsed_type;
774 object_info->oclass = g_type_class_ref (type: parsed_type);
775 object_info->id = g_strdup (str: object_class);
776 object_info->object = gtk_builder_get_object (builder: data->builder, name: object_class);
777 state_push (data, info: object_info);
778
779 has_duplicate = g_hash_table_lookup_extended (hash_table: data->object_ids, lookup_key: object_class, NULL, value: &line_ptr);
780 if (has_duplicate != 0)
781 {
782 g_set_error (err: error,
783 GTK_BUILDER_ERROR,
784 code: GTK_BUILDER_ERROR_DUPLICATE_ID,
785 format: "Duplicate object ID '%s' (previously on line %d)",
786 object_class, GPOINTER_TO_INT (line_ptr));
787 _gtk_builder_prefix_error (builder: data->builder, context, error);
788 return;
789 }
790
791 gtk_buildable_parse_context_get_position (context, line_number: &line, NULL);
792 g_hash_table_insert (hash_table: data->object_ids, key: g_strdup (str: object_class), GINT_TO_POINTER (line));
793}
794
795
796static void
797free_object_info (ObjectInfo *info)
798{
799 /* Do not free the signal items, which GtkBuilder takes ownership of */
800 g_type_class_unref (g_class: info->oclass);
801 if (info->signals)
802 g_ptr_array_free (array: info->signals, TRUE);
803 if (info->properties)
804 g_ptr_array_free (array: info->properties, TRUE);
805 g_free (mem: info->constructor);
806 g_free (mem: info->id);
807 g_slice_free (ObjectInfo, info);
808}
809
810static void
811parse_child (ParserData *data,
812 const char *element_name,
813 const char **names,
814 const char **values,
815 GError **error)
816
817{
818 ObjectInfo* object_info;
819 ChildInfo *child_info;
820 const char *type = NULL;
821 const char *internal_child = NULL;
822
823 object_info = state_peek_info (data, ObjectInfo);
824 if (!object_info ||
825 !(object_info->tag_type == TAG_OBJECT ||
826 object_info->tag_type == TAG_TEMPLATE))
827 {
828 error_invalid_tag (data, tag: element_name, NULL, error);
829 return;
830 }
831
832 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
833 first_type: G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, first_attr: "type", &type,
834 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "internal-child", &internal_child,
835 G_MARKUP_COLLECT_INVALID))
836 {
837 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
838 return;
839 }
840
841 child_info = g_slice_new0 (ChildInfo);
842 child_info->tag_type = TAG_CHILD;
843 child_info->type = g_strdup (str: type);
844 child_info->internal_child = g_strdup (str: internal_child);
845 child_info->parent = (CommonInfo*)object_info;
846 state_push (data, info: child_info);
847
848 object_info->object = builder_construct (data, object_info, error);
849}
850
851static void
852free_child_info (ChildInfo *info)
853{
854 g_free (mem: info->type);
855 g_free (mem: info->internal_child);
856 g_slice_free (ChildInfo, info);
857}
858
859static void
860parse_property (ParserData *data,
861 const char *element_name,
862 const char **names,
863 const char **values,
864 GError **error)
865{
866 PropertyInfo *info;
867 const char *name = NULL;
868 const char *context = NULL;
869 const char *bind_source = NULL;
870 const char *bind_property = NULL;
871 const char *bind_flags_str = NULL;
872 GBindingFlags bind_flags = G_BINDING_DEFAULT;
873 gboolean translatable = FALSE;
874 ObjectInfo *object_info;
875 GParamSpec *pspec = NULL;
876 int line, col;
877
878 object_info = state_peek_info (data, ObjectInfo);
879 if (!object_info ||
880 !(object_info->tag_type == TAG_OBJECT ||
881 object_info->tag_type == TAG_TEMPLATE))
882 {
883 error_invalid_tag (data, tag: element_name, NULL, error);
884 return;
885 }
886
887 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
888 first_type: G_MARKUP_COLLECT_STRING, first_attr: "name", &name,
889 G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "translatable", &translatable,
890 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "comments", NULL,
891 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "context", &context,
892 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "bind-source", &bind_source,
893 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "bind-property", &bind_property,
894 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "bind-flags", &bind_flags_str,
895 G_MARKUP_COLLECT_INVALID))
896 {
897 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
898 return;
899 }
900
901 pspec = g_object_class_find_property (oclass: object_info->oclass, property_name: name);
902
903 if (!pspec)
904 {
905 g_set_error (err: error,
906 GTK_BUILDER_ERROR,
907 code: GTK_BUILDER_ERROR_INVALID_PROPERTY,
908 format: "Invalid property: %s.%s",
909 g_type_name (type: object_info->type), name);
910 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
911 return;
912 }
913
914 if (bind_flags_str)
915 {
916 if (!_gtk_builder_flags_from_string (G_TYPE_BINDING_FLAGS, string: bind_flags_str, value: &bind_flags, error))
917 {
918 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
919 return;
920 }
921 }
922
923 gtk_buildable_parse_context_get_position (context: &data->ctx, line_number: &line, char_number: &col);
924
925 if (bind_source)
926 {
927 BindingInfo *binfo;
928
929 binfo = g_slice_new0 (BindingInfo);
930 binfo->tag_type = TAG_BINDING;
931 binfo->target = NULL;
932 binfo->target_pspec = pspec;
933 binfo->source = g_strdup (str: bind_source);
934 binfo->source_property = bind_property ? g_strdup (str: bind_property) : g_strdup (str: name);
935 binfo->flags = bind_flags;
936 binfo->line = line;
937 binfo->col = col;
938
939 object_info->bindings = g_slist_prepend (list: object_info->bindings, data: binfo);
940 }
941 else if (bind_property)
942 {
943 error_missing_attribute (data, tag: element_name,
944 attribute: "bind-source",
945 error);
946 return;
947 }
948
949 info = g_slice_new0 (PropertyInfo);
950 info->tag_type = TAG_PROPERTY;
951 info->pspec = pspec;
952 info->text = g_string_new (init: "");
953 info->translatable = translatable;
954 info->bound = bind_source != NULL;
955 info->context = g_strdup (str: context);
956 info->line = line;
957 info->col = col;
958
959 state_push (data, info);
960}
961
962static void
963parse_binding (ParserData *data,
964 const char *element_name,
965 const char **names,
966 const char **values,
967 GError **error)
968{
969 BindingExpressionInfo *info;
970 const char *name = NULL;
971 const char *object_name = NULL;
972 ObjectInfo *object_info;
973 GParamSpec *pspec = NULL;
974
975 object_info = state_peek_info (data, ObjectInfo);
976 if (!object_info ||
977 !(object_info->tag_type == TAG_OBJECT ||
978 object_info->tag_type == TAG_TEMPLATE))
979 {
980 error_invalid_tag (data, tag: element_name, NULL, error);
981 return;
982 }
983
984 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
985 first_type: G_MARKUP_COLLECT_STRING, first_attr: "name", &name,
986 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object_name,
987 G_MARKUP_COLLECT_INVALID))
988 {
989 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
990 return;
991 }
992
993 pspec = g_object_class_find_property (oclass: object_info->oclass, property_name: name);
994
995 if (!pspec)
996 {
997 g_set_error (err: error,
998 GTK_BUILDER_ERROR,
999 code: GTK_BUILDER_ERROR_INVALID_PROPERTY,
1000 format: "Invalid property: %s.%s",
1001 g_type_name (type: object_info->type), name);
1002 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1003 return;
1004 }
1005 else if (pspec->flags & G_PARAM_CONSTRUCT_ONLY)
1006 {
1007 g_set_error (err: error,
1008 GTK_BUILDER_ERROR,
1009 code: GTK_BUILDER_ERROR_INVALID_PROPERTY,
1010 format: "%s.%s is a construct-only property",
1011 g_type_name (type: object_info->type), name);
1012 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1013 return;
1014 }
1015 else if (!(pspec->flags & G_PARAM_WRITABLE))
1016 {
1017 g_set_error (err: error,
1018 GTK_BUILDER_ERROR,
1019 code: GTK_BUILDER_ERROR_INVALID_PROPERTY,
1020 format: "%s.%s is a non-writable property",
1021 g_type_name (type: object_info->type), name);
1022 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1023 return;
1024 }
1025
1026
1027 info = g_slice_new0 (BindingExpressionInfo);
1028 info->tag_type = TAG_BINDING_EXPRESSION;
1029 info->target = NULL;
1030 info->target_pspec = pspec;
1031 info->object_name = g_strdup (str: object_name);
1032 gtk_buildable_parse_context_get_position (context: &data->ctx, line_number: &info->line, char_number: &info->col);
1033
1034 state_push (data, info);
1035}
1036
1037static void
1038free_property_info (PropertyInfo *info)
1039{
1040 if (info->value)
1041 {
1042 if (G_PARAM_SPEC_VALUE_TYPE (info->pspec) == GTK_TYPE_EXPRESSION)
1043 gtk_expression_unref (self: info->value);
1044 else
1045 g_assert_not_reached();
1046 }
1047 g_string_free (string: info->text, TRUE);
1048 g_free (mem: info->context);
1049 g_slice_free (PropertyInfo, info);
1050}
1051
1052static void
1053free_expression_info (ExpressionInfo *info)
1054{
1055 switch (info->expression_type)
1056 {
1057 case EXPRESSION_EXPRESSION:
1058 g_clear_pointer (&info->expression, gtk_expression_unref);
1059 break;
1060
1061 case EXPRESSION_CONSTANT:
1062 g_string_free (string: info->constant.text, TRUE);
1063 break;
1064
1065 case EXPRESSION_CLOSURE:
1066 g_free (mem: info->closure.function_name);
1067 g_free (mem: info->closure.object_name);
1068 g_slist_free_full (list: info->closure.params, free_func: (GDestroyNotify) free_expression_info);
1069 break;
1070
1071 case EXPRESSION_PROPERTY:
1072 g_clear_pointer (&info->property.expression, free_expression_info);
1073 g_free (mem: info->property.property_name);
1074 break;
1075
1076 default:
1077 g_assert_not_reached ();
1078 break;
1079 }
1080 g_slice_free (ExpressionInfo, info);
1081}
1082
1083static gboolean
1084check_expression_parent (ParserData *data)
1085{
1086 CommonInfo *common_info = state_peek_info (data, CommonInfo);
1087
1088 if (common_info == NULL)
1089 return FALSE;
1090
1091 if (common_info->tag_type == TAG_PROPERTY)
1092 {
1093 PropertyInfo *prop_info = (PropertyInfo *) common_info;
1094
1095 return G_PARAM_SPEC_VALUE_TYPE (prop_info->pspec) == GTK_TYPE_EXPRESSION;
1096 }
1097 else if (common_info->tag_type == TAG_BINDING_EXPRESSION)
1098 {
1099 BindingExpressionInfo *expr_info = (BindingExpressionInfo *) common_info;
1100
1101 return expr_info->expr == NULL;
1102 }
1103 else if (common_info->tag_type == TAG_EXPRESSION)
1104 {
1105 ExpressionInfo *expr_info = (ExpressionInfo *) common_info;
1106
1107 switch (expr_info->expression_type)
1108 {
1109 case EXPRESSION_CLOSURE:
1110 return TRUE;
1111 case EXPRESSION_CONSTANT:
1112 return FALSE;
1113 case EXPRESSION_PROPERTY:
1114 return expr_info->property.expression == NULL;
1115 case EXPRESSION_EXPRESSION:
1116 default:
1117 g_assert_not_reached ();
1118 return FALSE;
1119 }
1120 }
1121
1122 return FALSE;
1123}
1124
1125static void
1126parse_constant_expression (ParserData *data,
1127 const char *element_name,
1128 const char **names,
1129 const char **values,
1130 GError **error)
1131{
1132 ExpressionInfo *info;
1133 const char *type_name = NULL;
1134 GType type;
1135
1136 if (!check_expression_parent (data))
1137 {
1138 error_invalid_tag (data, tag: element_name, NULL, error);
1139 return;
1140 }
1141
1142 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
1143 first_type: G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, first_attr: "type", &type_name,
1144 G_MARKUP_COLLECT_INVALID))
1145 {
1146 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1147 return;
1148 }
1149
1150 if (type_name == NULL)
1151 type = G_TYPE_INVALID;
1152 else
1153 {
1154 type = gtk_builder_get_type_from_name (builder: data->builder, type_name);
1155 if (type == G_TYPE_INVALID)
1156 {
1157 g_set_error (err: error,
1158 GTK_BUILDER_ERROR,
1159 code: GTK_BUILDER_ERROR_INVALID_VALUE,
1160 format: "Invalid type '%s'", type_name);
1161 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1162 return;
1163 }
1164 }
1165
1166 info = g_slice_new0 (ExpressionInfo);
1167 info->tag_type = TAG_EXPRESSION;
1168 info->expression_type = EXPRESSION_CONSTANT;
1169 info->constant.type = type;
1170 info->constant.text = g_string_new (NULL);
1171
1172 state_push (data, info);
1173}
1174
1175static void
1176parse_closure_expression (ParserData *data,
1177 const char *element_name,
1178 const char **names,
1179 const char **values,
1180 GError **error)
1181{
1182 ExpressionInfo *info;
1183 const char *type_name;
1184 const char *function_name;
1185 const char *object_name = NULL;
1186 gboolean swapped = -1;
1187 GType type;
1188
1189 if (!check_expression_parent (data))
1190 {
1191 error_invalid_tag (data, tag: element_name, NULL, error);
1192 return;
1193 }
1194
1195 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
1196 first_type: G_MARKUP_COLLECT_STRING, first_attr: "type", &type_name,
1197 G_MARKUP_COLLECT_STRING, "function", &function_name,
1198 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object_name,
1199 G_MARKUP_COLLECT_TRISTATE|G_MARKUP_COLLECT_OPTIONAL, "swapped", &swapped,
1200 G_MARKUP_COLLECT_INVALID))
1201 {
1202 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1203 return;
1204 }
1205
1206 type = gtk_builder_get_type_from_name (builder: data->builder, type_name);
1207 if (type == G_TYPE_INVALID)
1208 {
1209 g_set_error (err: error,
1210 GTK_BUILDER_ERROR,
1211 code: GTK_BUILDER_ERROR_INVALID_VALUE,
1212 format: "Invalid type '%s'", type_name);
1213 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1214 return;
1215 }
1216
1217 /* Swapped defaults to FALSE except when object is set */
1218 if (swapped == -1)
1219 {
1220 if (object_name)
1221 swapped = TRUE;
1222 else
1223 swapped = FALSE;
1224 }
1225
1226 info = g_slice_new0 (ExpressionInfo);
1227 info->tag_type = TAG_EXPRESSION;
1228 info->expression_type = EXPRESSION_CLOSURE;
1229 info->closure.type = type;
1230 info->closure.swapped = swapped;
1231 info->closure.function_name = g_strdup (str: function_name);
1232 info->closure.object_name = g_strdup (str: object_name);
1233
1234 state_push (data, info);
1235}
1236
1237static void
1238parse_lookup_expression (ParserData *data,
1239 const char *element_name,
1240 const char **names,
1241 const char **values,
1242 GError **error)
1243{
1244 ExpressionInfo *info;
1245 const char *property_name;
1246 const char *type_name = NULL;
1247 GType type;
1248
1249 if (!check_expression_parent (data))
1250 {
1251 error_invalid_tag (data, tag: element_name, NULL, error);
1252 return;
1253 }
1254
1255 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
1256 first_type: G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, first_attr: "type", &type_name,
1257 G_MARKUP_COLLECT_STRING, "name", &property_name,
1258 G_MARKUP_COLLECT_INVALID))
1259 {
1260 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1261 return;
1262 }
1263
1264 if (type_name == NULL)
1265 {
1266 type = G_TYPE_INVALID;
1267 }
1268 else
1269 {
1270 type = gtk_builder_get_type_from_name (builder: data->builder, type_name);
1271 if (type == G_TYPE_INVALID)
1272 {
1273 g_set_error (err: error,
1274 GTK_BUILDER_ERROR,
1275 code: GTK_BUILDER_ERROR_INVALID_VALUE,
1276 format: "Invalid type '%s'", type_name);
1277 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1278 return;
1279 }
1280 }
1281
1282 info = g_slice_new0 (ExpressionInfo);
1283 info->tag_type = TAG_EXPRESSION;
1284 info->expression_type = EXPRESSION_PROPERTY;
1285 info->property.this_type = type;
1286 info->property.property_name = g_strdup (str: property_name);
1287
1288 state_push (data, info);
1289}
1290
1291GtkExpression *
1292expression_info_construct (GtkBuilder *builder,
1293 ExpressionInfo *info,
1294 GError **error)
1295{
1296 switch (info->expression_type)
1297 {
1298 case EXPRESSION_EXPRESSION:
1299 break;
1300
1301 case EXPRESSION_CONSTANT:
1302 {
1303 GtkExpression *expr;
1304
1305 if (info->constant.type == G_TYPE_INVALID)
1306 {
1307 GObject *o = gtk_builder_lookup_object (builder, name: info->constant.text->str, line: 0, col: 0, error);
1308 if (o == NULL)
1309 return NULL;
1310
1311 expr = gtk_object_expression_new (object: o);
1312 }
1313 else
1314 {
1315 GValue value = G_VALUE_INIT;
1316
1317 if (!gtk_builder_value_from_string_type (builder,
1318 type: info->constant.type,
1319 string: info->constant.text->str,
1320 value: &value,
1321 error))
1322 return NULL;
1323
1324 if (G_VALUE_HOLDS_OBJECT (&value))
1325 expr = gtk_object_expression_new (object: g_value_get_object (value: &value));
1326 else
1327 expr = gtk_constant_expression_new_for_value (value: &value);
1328
1329 g_value_unset (value: &value);
1330 }
1331
1332 g_string_free (string: info->constant.text, TRUE);
1333 info->expression_type = EXPRESSION_EXPRESSION;
1334 info->expression = expr;
1335 }
1336 break;
1337
1338 case EXPRESSION_CLOSURE:
1339 {
1340 GObject *object;
1341 GClosure *closure;
1342 guint i, n_params;
1343 GtkExpression **params;
1344 GtkExpression *expression;
1345 GSList *l;
1346
1347 if (info->closure.object_name)
1348 {
1349 object = gtk_builder_lookup_object (builder, name: info->closure.object_name, line: 0, col: 0, error);
1350 if (object == NULL)
1351 return NULL;
1352 }
1353 else
1354 {
1355 object = NULL;
1356 }
1357
1358 closure = gtk_builder_create_closure (builder,
1359 function_name: info->closure.function_name,
1360 flags: info->closure.swapped,
1361 object,
1362 error);
1363 if (closure == NULL)
1364 return NULL;
1365 n_params = g_slist_length (list: info->closure.params);
1366 params = g_newa (GtkExpression *, n_params);
1367 i = n_params;
1368 for (l = info->closure.params; l; l = l->next)
1369 {
1370 params[--i] = expression_info_construct (builder, info: l->data, error);
1371 if (params[i] == NULL)
1372 return NULL;
1373 }
1374 expression = gtk_closure_expression_new (value_type: info->closure.type, closure, n_params, params);
1375 g_free (mem: info->closure.function_name);
1376 g_free (mem: info->closure.object_name);
1377 g_slist_free_full (list: info->closure.params, free_func: (GDestroyNotify) free_expression_info);
1378 info->expression_type = EXPRESSION_EXPRESSION;
1379 info->expression = expression;
1380 }
1381 break;
1382
1383 case EXPRESSION_PROPERTY:
1384 {
1385 GtkExpression *expression;
1386 GType type;
1387 GParamSpec *pspec;
1388
1389 if (info->property.expression)
1390 {
1391 expression = expression_info_construct (builder, info: info->property.expression, error);
1392 if (expression == NULL)
1393 return NULL;
1394 g_clear_pointer (&info->property.expression, free_expression_info);
1395 }
1396 else
1397 expression = NULL;
1398
1399 if (info->property.this_type != G_TYPE_INVALID)
1400 type = info->property.this_type;
1401 else if (expression != NULL)
1402 type = gtk_expression_get_value_type (self: expression);
1403 else
1404 {
1405 g_set_error (err: error,
1406 GTK_BUILDER_ERROR,
1407 code: GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
1408 format: "Lookups require a type attribute if they don't have an expression.");
1409 return NULL;
1410 }
1411
1412 if (g_type_is_a (type, G_TYPE_OBJECT))
1413 {
1414 GObjectClass *class = g_type_class_ref (type);
1415 pspec = g_object_class_find_property (oclass: class, property_name: info->property.property_name);
1416 g_type_class_unref (g_class: class);
1417 }
1418 else if (g_type_is_a (type, G_TYPE_INTERFACE))
1419 {
1420 GTypeInterface *iface = g_type_default_interface_ref (g_type: type);
1421 pspec = g_object_interface_find_property (g_iface: iface, property_name: info->property.property_name);
1422 g_type_default_interface_unref (g_iface: iface);
1423 }
1424 else
1425 {
1426 g_set_error (err: error,
1427 GTK_BUILDER_ERROR,
1428 code: GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
1429 format: "Type `%s` does not support properties",
1430 g_type_name (type));
1431 return NULL;
1432 }
1433
1434 if (pspec == NULL)
1435 {
1436 g_set_error (err: error,
1437 GTK_BUILDER_ERROR,
1438 code: GTK_BUILDER_ERROR_MISSING_ATTRIBUTE,
1439 format: "Type `%s` does not have a property name `%s`",
1440 g_type_name (type), info->property.property_name);
1441 return NULL;
1442 }
1443
1444 expression = gtk_property_expression_new_for_pspec (expression, pspec);
1445
1446 g_free (mem: info->property.property_name);
1447 info->expression_type = EXPRESSION_EXPRESSION;
1448 info->expression = expression;
1449 }
1450 break;
1451
1452 default:
1453 g_return_val_if_reached (NULL);
1454 }
1455
1456 return gtk_expression_ref (self: info->expression);
1457}
1458
1459static void
1460parse_signal (ParserData *data,
1461 const char *element_name,
1462 const char **names,
1463 const char **values,
1464 GError **error)
1465{
1466 SignalInfo *info;
1467 const char *name;
1468 const char *handler = NULL;
1469 const char *object = NULL;
1470 gboolean after = FALSE;
1471 gboolean swapped = -1;
1472 ObjectInfo *object_info;
1473 guint id = 0;
1474 GQuark detail = 0;
1475
1476 object_info = state_peek_info (data, ObjectInfo);
1477 if (!object_info ||
1478 !(object_info->tag_type == TAG_OBJECT||
1479 object_info->tag_type == TAG_TEMPLATE))
1480 {
1481 error_invalid_tag (data, tag: element_name, NULL, error);
1482 return;
1483 }
1484
1485 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
1486 first_type: G_MARKUP_COLLECT_STRING, first_attr: "name", &name,
1487 G_MARKUP_COLLECT_STRING, "handler", &handler,
1488 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "object", &object,
1489 G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, "last_modification_time", NULL,
1490 G_MARKUP_COLLECT_BOOLEAN|G_MARKUP_COLLECT_OPTIONAL, "after", &after,
1491 G_MARKUP_COLLECT_TRISTATE|G_MARKUP_COLLECT_OPTIONAL, "swapped", &swapped,
1492 G_MARKUP_COLLECT_INVALID))
1493 {
1494 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1495 return;
1496 }
1497
1498 if (!g_signal_parse_name (detailed_signal: name, itype: object_info->type, signal_id_p: &id, detail_p: &detail, FALSE))
1499 {
1500 g_set_error (err: error,
1501 GTK_BUILDER_ERROR,
1502 code: GTK_BUILDER_ERROR_INVALID_SIGNAL,
1503 format: "Invalid signal '%s' for type '%s'",
1504 name, g_type_name (type: object_info->type));
1505 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1506 return;
1507 }
1508
1509 /* Swapped defaults to FALSE except when object is set */
1510 if (swapped == -1)
1511 {
1512 if (object)
1513 swapped = TRUE;
1514 else
1515 swapped = FALSE;
1516 }
1517
1518 info = g_slice_new0 (SignalInfo);
1519 info->id = id;
1520 info->detail = detail;
1521 info->handler = g_strdup (str: handler);
1522 if (after)
1523 info->flags |= G_CONNECT_AFTER;
1524 if (swapped)
1525 info->flags |= G_CONNECT_SWAPPED;
1526 info->connect_object_name = g_strdup (str: object);
1527 state_push (data, info);
1528
1529 info->tag_type = TAG_SIGNAL;
1530}
1531
1532/* Called by GtkBuilder */
1533void
1534_free_signal_info (SignalInfo *info,
1535 gpointer user_data)
1536{
1537 g_free (mem: info->handler);
1538 g_free (mem: info->connect_object_name);
1539 g_free (mem: info->object_name);
1540 g_slice_free (SignalInfo, info);
1541}
1542
1543void
1544_free_binding_info (BindingInfo *info,
1545 gpointer user)
1546{
1547 g_free (mem: info->source);
1548 g_free (mem: info->source_property);
1549 g_slice_free (BindingInfo, info);
1550}
1551
1552void
1553free_binding_expression_info (BindingExpressionInfo *info)
1554{
1555 if (info->expr)
1556 free_expression_info (info: info->expr);
1557 g_free (mem: info->object_name);
1558 g_slice_free (BindingExpressionInfo, info);
1559}
1560
1561static void
1562free_requires_info (RequiresInfo *info,
1563 gpointer user_data)
1564{
1565 g_free (mem: info->library);
1566 g_slice_free (RequiresInfo, info);
1567}
1568
1569static void
1570parse_interface (ParserData *data,
1571 const char *element_name,
1572 const char **names,
1573 const char **values,
1574 GError **error)
1575{
1576 const char *domain = NULL;
1577
1578 if (!g_markup_collect_attributes (element_name, attribute_names: names, attribute_values: values, error,
1579 first_type: G_MARKUP_COLLECT_STRING|G_MARKUP_COLLECT_OPTIONAL, first_attr: "domain", &domain,
1580 G_MARKUP_COLLECT_INVALID))
1581 {
1582 _gtk_builder_prefix_error (builder: data->builder, context: &data->ctx, error);
1583 return;
1584 }
1585
1586 if (domain)
1587 {
1588 if (data->domain && strcmp (s1: data->domain, s2: domain) != 0)
1589 {
1590 g_warning ("%s: interface domain '%s' overrides programmatic value '%s'",
1591 data->filename, domain, data->domain);
1592 g_free (mem: data->domain);
1593 }
1594
1595 data->domain = g_strdup (str: domain);
1596 gtk_builder_set_translation_domain (builder: data->builder, domain: data->domain);
1597 }
1598}
1599
1600static SubParser *
1601create_subparser (GObject *object,
1602 GObject *child,
1603 const char *element_name,
1604 GtkBuildableParser *parser,
1605 gpointer user_data)
1606{
1607 SubParser *subparser;
1608
1609 subparser = g_slice_new0 (SubParser);
1610 subparser->object = object;
1611 subparser->child = child;
1612 subparser->tagname = g_strdup (str: element_name);
1613 subparser->start = element_name;
1614 subparser->parser = g_memdup2 (mem: parser, byte_size: sizeof (GtkBuildableParser));
1615 subparser->data = user_data;
1616
1617 return subparser;
1618}
1619
1620static void
1621free_subparser (SubParser *subparser)
1622{
1623 g_free (mem: subparser->tagname);
1624 g_slice_free (SubParser, subparser);
1625}
1626
1627static gboolean
1628subparser_start (GtkBuildableParseContext *context,
1629 const char *element_name,
1630 const char **names,
1631 const char **values,
1632 ParserData *data,
1633 GError **error)
1634{
1635 SubParser *subparser = data->subparser;
1636
1637 if (!subparser->start &&
1638 strcmp (s1: element_name, s2: subparser->tagname) == 0)
1639 subparser->start = element_name;
1640
1641 if (subparser->start)
1642 {
1643 if (subparser->parser->start_element)
1644 subparser->parser->start_element (context,
1645 element_name, names, values,
1646 subparser->data, error);
1647 return FALSE;
1648 }
1649 return TRUE;
1650}
1651
1652static void
1653subparser_end (GtkBuildableParseContext *context,
1654 const char *element_name,
1655 ParserData *data,
1656 GError **error)
1657{
1658 if (data->subparser->parser->end_element)
1659 data->subparser->parser->end_element (context, element_name,
1660 data->subparser->data, error);
1661
1662 if (*error)
1663 return;
1664
1665 if (strcmp (s1: data->subparser->start, s2: element_name) != 0)
1666 return;
1667
1668 gtk_buildable_custom_tag_end (GTK_BUILDABLE (data->subparser->object),
1669 builder: data->builder,
1670 child: data->subparser->child,
1671 tagname: element_name,
1672 data: data->subparser->data);
1673 g_free (mem: data->subparser->parser);
1674
1675 if (_gtk_builder_lookup_failed (builder: data->builder, error))
1676 return;
1677
1678 if (GTK_BUILDABLE_GET_IFACE (data->subparser->object)->custom_finished)
1679 data->custom_finalizers = g_slist_prepend (list: data->custom_finalizers,
1680 data: data->subparser);
1681 else
1682 free_subparser (subparser: data->subparser);
1683
1684 data->subparser = NULL;
1685}
1686
1687static gboolean
1688parse_custom (GtkBuildableParseContext *context,
1689 const char *element_name,
1690 const char **names,
1691 const char **values,
1692 ParserData *data,
1693 GError **error)
1694{
1695 CommonInfo* parent_info;
1696 GtkBuildableParser parser;
1697 gpointer subparser_data;
1698 GObject *object;
1699 GObject *child;
1700
1701 parent_info = state_peek_info (data, CommonInfo);
1702 if (!parent_info)
1703 return FALSE;
1704
1705 if (parent_info->tag_type == TAG_OBJECT ||
1706 parent_info->tag_type == TAG_TEMPLATE)
1707 {
1708 ObjectInfo* object_info = (ObjectInfo*)parent_info;
1709 if (!object_info->object)
1710 {
1711 object_info->object = builder_construct (data, object_info, error);
1712 if (!object_info->object)
1713 return TRUE; /* A GError is already set */
1714 }
1715 g_assert (object_info->object);
1716 object = object_info->object;
1717 child = NULL;
1718 }
1719 else if (parent_info->tag_type == TAG_CHILD)
1720 {
1721 ChildInfo* child_info = (ChildInfo*)parent_info;
1722
1723 _gtk_builder_add (builder: data->builder, child_info);
1724
1725 object = ((ObjectInfo*)child_info->parent)->object;
1726 child = child_info->object;
1727 }
1728 else
1729 return FALSE;
1730
1731 if (!gtk_buildable_custom_tag_start (GTK_BUILDABLE (object),
1732 builder: data->builder,
1733 child,
1734 tagname: element_name,
1735 parser: &parser,
1736 data: &subparser_data))
1737 return FALSE;
1738
1739 data->subparser = create_subparser (object, child, element_name,
1740 parser: &parser, user_data: subparser_data);
1741
1742 if (parser.start_element)
1743 parser.start_element (context,
1744 element_name, names, values,
1745 subparser_data, error);
1746 return TRUE;
1747}
1748
1749static void
1750start_element (GtkBuildableParseContext *context,
1751 const char *element_name,
1752 const char **names,
1753 const char **values,
1754 gpointer user_data,
1755 GError **error)
1756{
1757 ParserData *data = (ParserData*)user_data;
1758
1759#ifdef G_ENABLE_DEBUG
1760 if (GTK_DEBUG_CHECK (BUILDER))
1761 {
1762 GString *tags = g_string_new (init: "");
1763 int i;
1764 for (i = 0; names[i]; i++)
1765 g_string_append_printf (string: tags, format: "%s=\"%s\" ", names[i], values[i]);
1766
1767 if (i)
1768 {
1769 g_string_insert_c (string: tags, pos: 0, c: ' ');
1770 g_string_truncate (string: tags, len: tags->len - 1);
1771 }
1772 g_message ("<%s%s>", element_name, tags->str);
1773 g_string_free (string: tags, TRUE);
1774 }
1775#endif
1776
1777 if (!data->last_element && strcmp (s1: element_name, s2: "interface") != 0)
1778 {
1779 error_unhandled_tag (data, tag: element_name, error);
1780 return;
1781 }
1782 data->last_element = element_name;
1783
1784 if (data->subparser)
1785 {
1786 if (!subparser_start (context, element_name, names, values, data, error))
1787 return;
1788 }
1789
1790 if (strcmp (s1: element_name, s2: "object") == 0)
1791 parse_object (context, data, element_name, names, values, error);
1792 else if (data->requested_objects && !data->inside_requested_object)
1793 {
1794 /* If outside a requested object, simply ignore this tag */
1795 }
1796 else if (strcmp (s1: element_name, s2: "property") == 0)
1797 parse_property (data, element_name, names, values, error);
1798 else if (strcmp (s1: element_name, s2: "binding") == 0)
1799 parse_binding (data, element_name, names, values, error);
1800 else if (strcmp (s1: element_name, s2: "child") == 0)
1801 parse_child (data, element_name, names, values, error);
1802 else if (strcmp (s1: element_name, s2: "signal") == 0)
1803 parse_signal (data, element_name, names, values, error);
1804 else if (strcmp (s1: element_name, s2: "template") == 0)
1805 parse_template (context, data, element_name, names, values, error);
1806 else if (strcmp (s1: element_name, s2: "requires") == 0)
1807 parse_requires (data, element_name, names, values, error);
1808 else if (strcmp (s1: element_name, s2: "interface") == 0)
1809 parse_interface (data, element_name, names, values, error);
1810 else if (strcmp (s1: element_name, s2: "constant") == 0)
1811 parse_constant_expression (data, element_name, names, values, error);
1812 else if (strcmp (s1: element_name, s2: "closure") == 0)
1813 parse_closure_expression (data, element_name, names, values, error);
1814 else if (strcmp (s1: element_name, s2: "lookup") == 0)
1815 parse_lookup_expression (data, element_name, names, values, error);
1816 else if (strcmp (s1: element_name, s2: "menu") == 0)
1817 _gtk_builder_menu_start (parser_data: data, element_name, attribute_names: names, attribute_values: values, error);
1818 else if (strcmp (s1: element_name, s2: "placeholder") == 0)
1819 {
1820 /* placeholder has no special treatmeant, but it needs an
1821 * if clause to avoid an error below.
1822 */
1823 }
1824 else if (!parse_custom (context, element_name, names, values, data, error))
1825 error_unhandled_tag (data, tag: element_name, error);
1826}
1827
1828const char *
1829_gtk_builder_parser_translate (const char *domain,
1830 const char *context,
1831 const char *text)
1832{
1833 const char *s;
1834
1835 if (context)
1836 s = g_dpgettext2 (domain, context, msgid: text);
1837 else
1838 s = g_dgettext (domain, msgid: text);
1839
1840 return s;
1841}
1842
1843static void
1844end_element (GtkBuildableParseContext *context,
1845 const char *element_name,
1846 gpointer user_data,
1847 GError **error)
1848{
1849 ParserData *data = (ParserData*)user_data;
1850
1851 GTK_NOTE (BUILDER, g_message ("</%s>", element_name));
1852
1853 if (data->subparser && data->subparser->start)
1854 {
1855 subparser_end (context, element_name, data, error);
1856 return;
1857 }
1858
1859 if (data->requested_objects && !data->inside_requested_object)
1860 {
1861 /* If outside a requested object, simply ignore this tag */
1862 }
1863 else if (strcmp (s1: element_name, s2: "property") == 0)
1864 {
1865 PropertyInfo *prop_info = state_pop_info (data, PropertyInfo);
1866 CommonInfo *info = state_peek_info (data, CommonInfo);
1867
1868 g_assert (info != NULL);
1869
1870 /* Normal properties */
1871 if (info->tag_type == TAG_OBJECT ||
1872 info->tag_type == TAG_TEMPLATE)
1873 {
1874 ObjectInfo *object_info = (ObjectInfo*)info;
1875
1876 if (prop_info->translatable && prop_info->text->len)
1877 {
1878 const char *translated;
1879
1880 translated = _gtk_builder_parser_translate (domain: data->domain,
1881 context: prop_info->context,
1882 text: prop_info->text->str);
1883 g_string_assign (string: prop_info->text, rval: translated);
1884 }
1885
1886 if (G_UNLIKELY (!object_info->properties))
1887 object_info->properties = g_ptr_array_new_with_free_func (element_free_func: (GDestroyNotify)free_property_info);
1888
1889 g_ptr_array_add (array: object_info->properties, data: prop_info);
1890 }
1891 else
1892 g_assert_not_reached ();
1893 }
1894 else if (strcmp (s1: element_name, s2: "binding") == 0)
1895 {
1896 BindingExpressionInfo *binfo = state_pop_info (data, BindingExpressionInfo);
1897 CommonInfo *info = state_peek_info (data, CommonInfo);
1898
1899 g_assert (info != NULL);
1900
1901 if (binfo->expr == NULL)
1902 {
1903 g_set_error (err: error,
1904 GTK_BUILDER_ERROR,
1905 code: GTK_BUILDER_ERROR_INVALID_TAG,
1906 format: "Binding tag requires an expression");
1907 free_binding_expression_info (info: binfo);
1908 }
1909 else if (info->tag_type == TAG_OBJECT ||
1910 info->tag_type == TAG_TEMPLATE)
1911 {
1912 ObjectInfo *object_info = (ObjectInfo*)info;
1913 object_info->bindings = g_slist_prepend (list: object_info->bindings, data: binfo);
1914 }
1915 else
1916 g_assert_not_reached ();
1917 }
1918 else if (strcmp (s1: element_name, s2: "object") == 0 ||
1919 strcmp (s1: element_name, s2: "template") == 0)
1920 {
1921 ObjectInfo *object_info = state_pop_info (data, ObjectInfo);
1922 ChildInfo* child_info = state_peek_info (data, ChildInfo);
1923 PropertyInfo* prop_info = state_peek_info (data, PropertyInfo);
1924
1925 if (child_info && child_info->tag_type != TAG_CHILD)
1926 child_info = NULL;
1927 if (prop_info && prop_info->tag_type != TAG_PROPERTY)
1928 prop_info = NULL;
1929
1930 if (data->requested_objects && data->inside_requested_object &&
1931 (data->cur_object_level == data->requested_object_level))
1932 {
1933 GTK_NOTE (BUILDER,
1934 g_message ("requested object end found at level %d",
1935 data->requested_object_level));
1936
1937 data->inside_requested_object = FALSE;
1938 }
1939
1940 --data->cur_object_level;
1941
1942 g_assert (data->cur_object_level >= 0);
1943
1944 object_info->object = builder_construct (data, object_info, error);
1945 if (!object_info->object)
1946 {
1947 free_object_info (info: object_info);
1948 return;
1949 }
1950 if (child_info)
1951 child_info->object = object_info->object;
1952 if (prop_info)
1953 g_string_assign (string: prop_info->text, rval: object_info->id);
1954
1955 if (GTK_IS_BUILDABLE (object_info->object) &&
1956 GTK_BUILDABLE_GET_IFACE (object_info->object)->parser_finished)
1957 g_ptr_array_add (array: data->finalizers, data: object_info->object);
1958
1959 if (object_info->signals)
1960 {
1961 _gtk_builder_add_signals (builder: data->builder, signals: object_info->signals);
1962 object_info->signals = NULL;
1963 }
1964
1965 if (object_info->bindings)
1966 {
1967 gtk_builder_take_bindings (builder: data->builder, target: object_info->object, bindings: object_info->bindings);
1968 object_info->bindings = NULL;
1969 }
1970
1971 free_object_info (info: object_info);
1972 }
1973 else if (strcmp (s1: element_name, s2: "child") == 0)
1974 {
1975 ChildInfo *child_info = state_pop_info (data, ChildInfo);
1976
1977 _gtk_builder_add (builder: data->builder, child_info);
1978
1979 free_child_info (info: child_info);
1980 }
1981 else if (strcmp (s1: element_name, s2: "signal") == 0)
1982 {
1983 SignalInfo *signal_info = state_pop_info (data, SignalInfo);
1984 ObjectInfo *object_info = (ObjectInfo*)state_peek_info (data, CommonInfo);
1985 g_assert (object_info != NULL);
1986 signal_info->object_name = g_strdup (str: object_info->id);
1987
1988 if (G_UNLIKELY (!object_info->signals))
1989 object_info->signals = g_ptr_array_new ();
1990
1991 g_ptr_array_add (array: object_info->signals, data: signal_info);
1992 }
1993 else if (strcmp (s1: element_name, s2: "constant") == 0 ||
1994 strcmp (s1: element_name, s2: "closure") == 0 ||
1995 strcmp (s1: element_name, s2: "lookup") == 0)
1996 {
1997 ExpressionInfo *expression_info = state_pop_info (data, ExpressionInfo);
1998 CommonInfo *parent_info = state_peek_info (data, CommonInfo);
1999 g_assert (parent_info != NULL);
2000
2001 if (parent_info->tag_type == TAG_BINDING_EXPRESSION)
2002 {
2003 BindingExpressionInfo *expr_info = (BindingExpressionInfo *) parent_info;
2004
2005 expr_info->expr = expression_info;
2006 }
2007 else if (parent_info->tag_type == TAG_PROPERTY)
2008 {
2009 PropertyInfo *prop_info = (PropertyInfo *) parent_info;
2010
2011 prop_info->value = expression_info_construct (builder: data->builder, info: expression_info, error);
2012 free_expression_info (info: expression_info);
2013 }
2014 else if (parent_info->tag_type == TAG_EXPRESSION)
2015 {
2016 ExpressionInfo *expr_info = (ExpressionInfo *) parent_info;
2017
2018 switch (expr_info->expression_type)
2019 {
2020 case EXPRESSION_CLOSURE:
2021 expr_info->closure.params = g_slist_prepend (list: expr_info->closure.params, data: expression_info);
2022 break;
2023 case EXPRESSION_PROPERTY:
2024 expr_info->property.expression = expression_info;
2025 break;
2026 case EXPRESSION_EXPRESSION:
2027 case EXPRESSION_CONSTANT:
2028 default:
2029 g_assert_not_reached ();
2030 break;
2031 }
2032 }
2033 else
2034 {
2035 g_assert_not_reached ();
2036 }
2037 }
2038 else if (strcmp (s1: element_name, s2: "requires") == 0)
2039 {
2040 RequiresInfo *req_info = state_pop_info (data, RequiresInfo);
2041
2042 /* TODO: Allow third party widget developers to check their
2043 * required versions, possibly throw a signal allowing them
2044 * to check their library versions here.
2045 */
2046 if (!strcmp (s1: req_info->library, s2: "gtk"))
2047 {
2048 if (req_info->major == 4 && req_info->minor == 0)
2049 {
2050 /* We allow 3.99.x to pass as 4.0 */
2051 }
2052 else if (gtk_check_version (required_major: req_info->major, required_minor: req_info->minor, required_micro: 0) != NULL)
2053 {
2054 g_set_error (err: error,
2055 GTK_BUILDER_ERROR,
2056 code: GTK_BUILDER_ERROR_VERSION_MISMATCH,
2057 format: "Required GTK version %d.%d, current version is %d.%d",
2058 req_info->major, req_info->minor,
2059 GTK_MAJOR_VERSION, GTK_MINOR_VERSION);
2060 _gtk_builder_prefix_error (builder: data->builder, context, error);
2061 }
2062 }
2063 free_requires_info (info: req_info, NULL);
2064 }
2065 else if (strcmp (s1: element_name, s2: "interface") == 0)
2066 {
2067 }
2068 else if (strcmp (s1: element_name, s2: "menu") == 0)
2069 {
2070 _gtk_builder_menu_end (parser_data: data);
2071 }
2072 else if (strcmp (s1: element_name, s2: "placeholder") == 0)
2073 {
2074 }
2075 else
2076 {
2077 g_set_error (err: error,
2078 GTK_BUILDER_ERROR,
2079 code: GTK_BUILDER_ERROR_UNHANDLED_TAG,
2080 format: "Unhandled tag: <%s>", element_name);
2081 _gtk_builder_prefix_error (builder: data->builder, context, error);
2082 }
2083}
2084
2085/* Called for character data */
2086/* text is not nul-terminated */
2087static void
2088text (GtkBuildableParseContext *context,
2089 const char *text,
2090 gsize text_len,
2091 gpointer user_data,
2092 GError **error)
2093{
2094 ParserData *data = (ParserData*)user_data;
2095 CommonInfo *info;
2096
2097 if (data->subparser && data->subparser->start)
2098 {
2099 GError *tmp_error = NULL;
2100
2101 if (data->subparser->parser->text)
2102 data->subparser->parser->text (context, text, text_len,
2103 data->subparser->data, &tmp_error);
2104 if (tmp_error)
2105 g_propagate_error (dest: error, src: tmp_error);
2106 return;
2107 }
2108
2109 if (!data->stack || data->stack->len == 0)
2110 return;
2111
2112 info = state_peek_info (data, CommonInfo);
2113 g_assert (info != NULL);
2114
2115 if (strcmp (s1: gtk_buildable_parse_context_get_element (context), s2: "property") == 0)
2116 {
2117 PropertyInfo *prop_info = (PropertyInfo*)info;
2118
2119 g_string_append_len (string: prop_info->text, val: text, len: text_len);
2120 }
2121 else if (strcmp (s1: gtk_buildable_parse_context_get_element (context), s2: "constant") == 0)
2122 {
2123 ExpressionInfo *expr_info = (ExpressionInfo *) info;
2124
2125 g_string_append_len (string: expr_info->constant.text, val: text, len: text_len);
2126 }
2127 else if (strcmp (s1: gtk_buildable_parse_context_get_element (context), s2: "lookup") == 0)
2128 {
2129 ExpressionInfo *expr_info = (ExpressionInfo *) info;
2130
2131 while (g_ascii_isspace (*text) && text_len > 0)
2132 {
2133 text++;
2134 text_len--;
2135 }
2136 while (text_len > 0 && g_ascii_isspace (text[text_len - 1]))
2137 text_len--;
2138 if (expr_info->property.expression == NULL && text_len > 0)
2139 {
2140 ExpressionInfo *constant = g_slice_new0 (ExpressionInfo);
2141 constant->tag_type = TAG_EXPRESSION;
2142 constant->expression_type = EXPRESSION_CONSTANT;
2143 constant->constant.type = G_TYPE_INVALID;
2144 constant->constant.text = g_string_new_len (init: text, len: text_len);
2145 expr_info->property.expression = constant;
2146 }
2147 }
2148}
2149
2150static const GtkBuildableParser parser = {
2151 start_element,
2152 end_element,
2153 text,
2154 NULL,
2155};
2156
2157void
2158_gtk_builder_parser_parse_buffer (GtkBuilder *builder,
2159 const char *filename,
2160 const char *buffer,
2161 gssize length,
2162 const char **requested_objs,
2163 GError **error)
2164{
2165 const char * domain;
2166 ParserData data;
2167 GSList *l;
2168 gint64 before = GDK_PROFILER_CURRENT_TIME;
2169
2170 /* Store the original domain so that interface domain attribute can be
2171 * applied for the builder and the original domain can be restored after
2172 * parsing has finished. This allows subparsers to translate elements with
2173 * gtk_builder_get_translation_domain() without breaking the ABI or API
2174 */
2175 domain = gtk_builder_get_translation_domain (builder);
2176
2177 memset (s: &data, c: 0, n: sizeof (ParserData));
2178 data.builder = builder;
2179 data.filename = filename;
2180 data.domain = g_strdup (str: domain);
2181 data.object_ids = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal,
2182 key_destroy_func: (GDestroyNotify)g_free, NULL);
2183 data.stack = g_ptr_array_new ();
2184 data.finalizers = g_ptr_array_new ();
2185
2186 if (requested_objs)
2187 {
2188 data.inside_requested_object = FALSE;
2189 data.requested_objects = requested_objs;
2190 }
2191 else
2192 {
2193 /* get all the objects */
2194 data.inside_requested_object = TRUE;
2195 }
2196
2197 gtk_buildable_parse_context_init (context: &data.ctx, parser: &parser, user_data: &data);
2198
2199 if (!gtk_buildable_parse_context_parse (context: &data.ctx, text: buffer, text_len: length, error))
2200 goto out;
2201
2202 if (_gtk_builder_lookup_failed (builder, error))
2203 goto out;
2204
2205 if (!_gtk_builder_finish (builder, error))
2206 goto out;
2207
2208 /* Custom parser_finished */
2209 data.custom_finalizers = g_slist_reverse (list: data.custom_finalizers);
2210 for (l = data.custom_finalizers; l; l = l->next)
2211 {
2212 SubParser *sub = (SubParser*)l->data;
2213
2214 gtk_buildable_custom_finished (GTK_BUILDABLE (sub->object),
2215 builder,
2216 child: sub->child,
2217 tagname: sub->tagname,
2218 data: sub->data);
2219 if (_gtk_builder_lookup_failed (builder, error))
2220 goto out;
2221 }
2222
2223 /* Common parser_finished, for all created objects */
2224 for (guint i = 0; i < data.finalizers->len; i++)
2225 {
2226 GtkBuildable *buildable = g_ptr_array_index (data.finalizers, i);
2227
2228 gtk_buildable_parser_finished (GTK_BUILDABLE (buildable), builder);
2229 if (_gtk_builder_lookup_failed (builder, error))
2230 goto out;
2231 }
2232
2233 out:
2234
2235 g_slist_free_full (list: data.custom_finalizers, free_func: (GDestroyNotify)free_subparser);
2236 g_free (mem: data.domain);
2237 g_hash_table_destroy (hash_table: data.object_ids);
2238 g_ptr_array_free (array: data.stack, TRUE);
2239 g_ptr_array_free (array: data.finalizers, TRUE);
2240 gtk_buildable_parse_context_free (context: &data.ctx);
2241
2242 /* restore the original domain */
2243 gtk_builder_set_translation_domain (builder, domain);
2244
2245 if (GDK_PROFILER_IS_RUNNING)
2246 {
2247 guint64 after = GDK_PROFILER_CURRENT_TIME;
2248 if (after - before > 500000) /* half a millisecond */
2249 {
2250 gdk_profiler_add_mark (before, after - before, "builder load", filename);
2251 }
2252 }
2253}
2254

source code of gtk/gtk/gtkbuilderparser.c