1 | /* GTK - The GIMP Toolkit |
2 | * Copyright (C) 1998-2002 James Henstridge <james@daa.com.au> |
3 | * Copyright (C) 2006-2007 Async Open Source, |
4 | * Johan Dahlin <jdahlin@async.com.br>, |
5 | * Henrique Romano <henrique@async.com.br> |
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, see <http://www.gnu.org/licenses/>. |
19 | */ |
20 | |
21 | /** |
22 | * GtkBuilder: |
23 | * |
24 | * A `GtkBuilder` reads XML descriptions of a user interface and |
25 | * instantiates the described objects. |
26 | * |
27 | * To create a `GtkBuilder` from a user interface description, call |
28 | * [ctor@Gtk.Builder.new_from_file], [ctor@Gtk.Builder.new_from_resource] |
29 | * or [ctor@Gtk.Builder.new_from_string]. |
30 | * |
31 | * In the (unusual) case that you want to add user interface |
32 | * descriptions from multiple sources to the same `GtkBuilder` you can |
33 | * call [ctor@Gtk.Builder.new] to get an empty builder and populate it by |
34 | * (multiple) calls to [method@Gtk.Builder.add_from_file], |
35 | * [method@Gtk.Builder.add_from_resource] or |
36 | * [method@Gtk.Builder.add_from_string]. |
37 | * |
38 | * A `GtkBuilder` holds a reference to all objects that it has constructed |
39 | * and drops these references when it is finalized. This finalization can |
40 | * cause the destruction of non-widget objects or widgets which are not |
41 | * contained in a toplevel window. For toplevel windows constructed by a |
42 | * builder, it is the responsibility of the user to call |
43 | * [method@Gtk.Window.destroy] to get rid of them and all the widgets |
44 | * they contain. |
45 | * |
46 | * The functions [method@Gtk.Builder.get_object] and |
47 | * [method@Gtk.Builder.get_objects] can be used to access the widgets in |
48 | * the interface by the names assigned to them inside the UI description. |
49 | * Toplevel windows returned by these functions will stay around until the |
50 | * user explicitly destroys them with [method@Gtk.Window.destroy]. Other |
51 | * widgets will either be part of a larger hierarchy constructed by the |
52 | * builder (in which case you should not have to worry about their lifecycle), |
53 | * or without a parent, in which case they have to be added to some container |
54 | * to make use of them. Non-widget objects need to be reffed with |
55 | * g_object_ref() to keep them beyond the lifespan of the builder. |
56 | * |
57 | * # GtkBuilder UI Definitions |
58 | * |
59 | * `GtkBuilder` parses textual descriptions of user interfaces which are |
60 | * specified in XML format. We refer to these descriptions as “GtkBuilder |
61 | * UI definitions” or just “UI definitions” if the context is clear. |
62 | * |
63 | * The toplevel element is `<interface>`. It optionally takes a “domain” |
64 | * attribute, which will make the builder look for translated strings |
65 | * using `dgettext()` in the domain specified. This can also be done by |
66 | * calling [method@Gtk.Builder.set_translation_domain] on the builder. |
67 | * |
68 | * Objects are described by `<object>` elements, which can contain |
69 | * `<property>` elements to set properties, `<signal>` elements which |
70 | * connect signals to handlers, and `<child>` elements, which describe |
71 | * child objects (most often widgets inside a container, but also e.g. |
72 | * actions in an action group, or columns in a tree model). A `<child>` |
73 | * element contains an `<object>` element which describes the child object. |
74 | * |
75 | * The target toolkit version(s) are described by `<requires>` elements, |
76 | * the “lib” attribute specifies the widget library in question (currently |
77 | * the only supported value is “gtk”) and the “version” attribute specifies |
78 | * the target version in the form “`<major>`.`<minor>`”. `GtkBuilder` will |
79 | * error out if the version requirements are not met. |
80 | * |
81 | * Typically, the specific kind of object represented by an `<object>` |
82 | * element is specified by the “class” attribute. If the type has not |
83 | * been loaded yet, GTK tries to find the `get_type()` function from the |
84 | * class name by applying heuristics. This works in most cases, but if |
85 | * necessary, it is possible to specify the name of the `get_type()` |
86 | * function explicitly with the "type-func" attribute. |
87 | * |
88 | * Objects may be given a name with the “id” attribute, which allows the |
89 | * application to retrieve them from the builder with |
90 | * [method@Gtk.Builder.get_object]. An id is also necessary to use the |
91 | * object as property value in other parts of the UI definition. GTK |
92 | * reserves ids starting and ending with `___` (three consecutive |
93 | * underscores) for its own purposes. |
94 | * |
95 | * Setting properties of objects is pretty straightforward with the |
96 | * `<property>` element: the “name” attribute specifies the name of the |
97 | * property, and the content of the element specifies the value. |
98 | * If the “translatable” attribute is set to a true value, GTK uses |
99 | * `gettext()` (or `dgettext()` if the builder has a translation domain set) |
100 | * to find a translation for the value. This happens before the value |
101 | * is parsed, so it can be used for properties of any type, but it is |
102 | * probably most useful for string properties. It is also possible to |
103 | * specify a context to disambiguate short strings, and comments which |
104 | * may help the translators. |
105 | * |
106 | * `GtkBuilder` can parse textual representations for the most common |
107 | * property types: characters, strings, integers, floating-point numbers, |
108 | * booleans (strings like “TRUE”, “t”, “yes”, “y”, “1” are interpreted |
109 | * as %TRUE, strings like “FALSE”, “f”, “no”, “n”, “0” are interpreted |
110 | * as %FALSE), enumerations (can be specified by their name, nick or |
111 | * integer value), flags (can be specified by their name, nick, integer |
112 | * value, optionally combined with “|”, e.g. |
113 | * “GTK_INPUT_HINT_EMOJI|GTK_INPUT_HINT_LOWERCASE”) |
114 | * and colors (in a format understood by [method@Gdk.RGBA.parse]). |
115 | * |
116 | * `GVariant`s can be specified in the format understood by |
117 | * g_variant_parse(), and pixbufs can be specified as a filename of an |
118 | * image file to load. |
119 | * |
120 | * Objects can be referred to by their name and by default refer to |
121 | * objects declared in the local XML fragment and objects exposed via |
122 | * [method@Gtk.Builder.expose_object]. In general, `GtkBuilder` allows |
123 | * forward references to objects — declared in the local XML; an object |
124 | * doesn’t have to be constructed before it can be referred to. The |
125 | * exception to this rule is that an object has to be constructed before |
126 | * it can be used as the value of a construct-only property. |
127 | * |
128 | * It is also possible to bind a property value to another object's |
129 | * property value using the attributes "bind-source" to specify the |
130 | * source object of the binding, and optionally, "bind-property" and |
131 | * "bind-flags" to specify the source property and source binding flags |
132 | * respectively. Internally, `GtkBuilder` implements this using `GBinding` |
133 | * objects. For more information see g_object_bind_property(). |
134 | * |
135 | * Sometimes it is necessary to refer to widgets which have implicitly |
136 | * been constructed by GTK as part of a composite widget, to set |
137 | * properties on them or to add further children (e.g. the content area |
138 | * of a `GtkDialog`). This can be achieved by setting the “internal-child” |
139 | * property of the `<child>` element to a true value. Note that `GtkBuilder` |
140 | * still requires an `<object>` element for the internal child, even if it |
141 | * has already been constructed. |
142 | * |
143 | * A number of widgets have different places where a child can be added |
144 | * (e.g. tabs vs. page content in notebooks). This can be reflected in |
145 | * a UI definition by specifying the “type” attribute on a `<child>` |
146 | * The possible values for the “type” attribute are described in the |
147 | * sections describing the widget-specific portions of UI definitions. |
148 | * |
149 | * # Signal handlers and function pointers |
150 | * |
151 | * Signal handlers are set up with the `<signal>` element. The “name” |
152 | * attribute specifies the name of the signal, and the “handler” attribute |
153 | * specifies the function to connect to the signal. |
154 | * The remaining attributes, “after”, “swapped” and “object”, have the |
155 | * same meaning as the corresponding parameters of the |
156 | * g_signal_connect_object() or g_signal_connect_data() functions. A |
157 | * “last_modification_time” attribute is also allowed, but it does not |
158 | * have a meaning to the builder. |
159 | * |
160 | * If you rely on `GModule` support to lookup callbacks in the symbol table, |
161 | * the following details should be noted: |
162 | * |
163 | * When compiling applications for Windows, you must declare signal callbacks |
164 | * with %G_MODULE_EXPORT, or they will not be put in the symbol table. |
165 | * On Linux and Unix, this is not necessary; applications should instead |
166 | * be compiled with the -Wl,--export-dynamic `CFLAGS`, and linked against |
167 | * `gmodule-export-2.0`. |
168 | * |
169 | * # A GtkBuilder UI Definition |
170 | * |
171 | * ```xml |
172 | * <interface> |
173 | * <object class="GtkDialog" id="dialog1"> |
174 | * <child internal-child="content_area"> |
175 | * <object class="GtkBox" id="vbox1"> |
176 | * <child internal-child="action_area"> |
177 | * <object class="GtkBox" id="hbuttonbox1"> |
178 | * <child> |
179 | * <object class="GtkButton" id="ok_button"> |
180 | * <property name="label" translatable="yes">_Ok</property> |
181 | * <property name="use-underline">True</property> |
182 | * <signal name="clicked" handler="ok_button_clicked"/> |
183 | * </object> |
184 | * </child> |
185 | * </object> |
186 | * </child> |
187 | * </object> |
188 | * </child> |
189 | * </object> |
190 | * </interface> |
191 | * ``` |
192 | * |
193 | * Beyond this general structure, several object classes define their |
194 | * own XML DTD fragments for filling in the ANY placeholders in the DTD |
195 | * above. Note that a custom element in a <child> element gets parsed by |
196 | * the custom tag handler of the parent object, while a custom element in |
197 | * an <object> element gets parsed by the custom tag handler of the object. |
198 | * |
199 | * These XML fragments are explained in the documentation of the |
200 | * respective objects. |
201 | * |
202 | * A `<template>` tag can be used to define a widget class’s components. |
203 | * See the [GtkWidget documentation](class.Widget.html#building-composite-widgets-from-template-xml) for details. |
204 | */ |
205 | |
206 | #include "config.h" |
207 | #include <errno.h> /* errno */ |
208 | #include <stdlib.h> |
209 | #include <string.h> /* strlen */ |
210 | |
211 | #include "gtkbuilderprivate.h" |
212 | |
213 | #include "gtkbuildableprivate.h" |
214 | #include "gtkbuilderlistitemfactory.h" |
215 | #include "gtkbuilderscopeprivate.h" |
216 | #include "gtkdebug.h" |
217 | #include "gtkexpression.h" |
218 | #include "gtkmain.h" |
219 | #include "gtkicontheme.h" |
220 | #include "gtkintl.h" |
221 | #include "gtkprivate.h" |
222 | #include "gtkshortcutactionprivate.h" |
223 | #include "gtkshortcuttrigger.h" |
224 | #include "gtktestutils.h" |
225 | #include "gtktypebuiltins.h" |
226 | #include "gtkicontheme.h" |
227 | #include "gtkiconthemeprivate.h" |
228 | #include "gtkdebug.h" |
229 | |
230 | |
231 | static void gtk_builder_finalize (GObject *object); |
232 | static void gtk_builder_set_property (GObject *object, |
233 | guint prop_id, |
234 | const GValue *value, |
235 | GParamSpec *pspec); |
236 | static void gtk_builder_get_property (GObject *object, |
237 | guint prop_id, |
238 | GValue *value, |
239 | GParamSpec *pspec); |
240 | |
241 | enum { |
242 | PROP_0, |
243 | PROP_CURRENT_OBJECT, |
244 | PROP_SCOPE, |
245 | PROP_TRANSLATION_DOMAIN, |
246 | LAST_PROP |
247 | }; |
248 | |
249 | static GParamSpec *builder_props[LAST_PROP]; |
250 | |
251 | struct _GtkBuilder |
252 | { |
253 | GObject parent_instance; |
254 | }; |
255 | |
256 | struct _GtkBuilderClass |
257 | { |
258 | GObjectClass parent_class; |
259 | }; |
260 | |
261 | typedef struct |
262 | { |
263 | char *domain; |
264 | GHashTable *objects; |
265 | GSList *delayed_properties; |
266 | GPtrArray *signals; |
267 | GSList *bindings; |
268 | char *filename; |
269 | char *resource_prefix; |
270 | GType template_type; |
271 | GObject *current_object; |
272 | GtkBuilderScope *scope; |
273 | } GtkBuilderPrivate; |
274 | |
275 | G_DEFINE_TYPE_WITH_PRIVATE (GtkBuilder, gtk_builder, G_TYPE_OBJECT) |
276 | |
277 | static void |
278 | gtk_builder_dispose (GObject *object) |
279 | { |
280 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (GTK_BUILDER (object)); |
281 | |
282 | g_clear_object (&priv->current_object); |
283 | g_clear_object (&priv->scope); |
284 | |
285 | G_OBJECT_CLASS (gtk_builder_parent_class)->dispose (object); |
286 | } |
287 | |
288 | static void |
289 | gtk_builder_class_init (GtkBuilderClass *klass) |
290 | { |
291 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
292 | |
293 | gobject_class->dispose = gtk_builder_dispose; |
294 | gobject_class->finalize = gtk_builder_finalize; |
295 | gobject_class->set_property = gtk_builder_set_property; |
296 | gobject_class->get_property = gtk_builder_get_property; |
297 | |
298 | /** |
299 | * GtkBuilder:translation-domain: (attributes org.gtk.Property.get=gtk_builder_get_translation_domain org.gtk.Property.set=gtk_builder_set_translation_domain) |
300 | * |
301 | * The translation domain used when translating property values that |
302 | * have been marked as translatable. |
303 | * |
304 | * If the translation domain is %NULL, `GtkBuilder` uses gettext(), |
305 | * otherwise g_dgettext(). |
306 | */ |
307 | builder_props[PROP_TRANSLATION_DOMAIN] = |
308 | g_param_spec_string (name: "translation-domain" , |
309 | P_("Translation Domain" ), |
310 | P_("The translation domain used by gettext" ), |
311 | NULL, |
312 | GTK_PARAM_READWRITE); |
313 | |
314 | /** |
315 | * GtkBuilder:current-object: (attributes org.gtk.Property.get=gtk_builder_get_current_object org.gtk.Property.set=gtk_builder_set_current_object) |
316 | * |
317 | * The object the builder is evaluating for. |
318 | */ |
319 | builder_props[PROP_CURRENT_OBJECT] = |
320 | g_param_spec_object (name: "current-object" , |
321 | P_("Current object" ), |
322 | P_("The object the builder is evaluating for" ), |
323 | G_TYPE_OBJECT, |
324 | GTK_PARAM_READWRITE); |
325 | |
326 | /** |
327 | * GtkBuilder:scope: (attributes org.gtk.Property.get=gtk_builder_get_scope org.gtk.Property.set=gtk_builder_set_scope) |
328 | * |
329 | * The scope the builder is operating in |
330 | */ |
331 | builder_props[PROP_SCOPE] = |
332 | g_param_spec_object (name: "scope" , |
333 | P_("Scope" ), |
334 | P_("The scope the builder is operating in" ), |
335 | GTK_TYPE_BUILDER_SCOPE, |
336 | GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT); |
337 | |
338 | g_object_class_install_properties (oclass: gobject_class, n_pspecs: LAST_PROP, pspecs: builder_props); |
339 | } |
340 | |
341 | static void |
342 | gtk_builder_init (GtkBuilder *builder) |
343 | { |
344 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
345 | |
346 | priv->domain = NULL; |
347 | priv->objects = g_hash_table_new_full (hash_func: g_str_hash, key_equal_func: g_str_equal, |
348 | key_destroy_func: g_free, value_destroy_func: g_object_unref); |
349 | } |
350 | |
351 | |
352 | /* |
353 | * GObject virtual methods |
354 | */ |
355 | |
356 | static void |
357 | gtk_builder_finalize (GObject *object) |
358 | { |
359 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (GTK_BUILDER (object)); |
360 | |
361 | g_free (mem: priv->domain); |
362 | g_free (mem: priv->filename); |
363 | g_free (mem: priv->resource_prefix); |
364 | |
365 | #ifdef G_ENABLE_DEBUG |
366 | if (GTK_DEBUG_CHECK (BUILDER_OBJECTS)) |
367 | { |
368 | GHashTableIter iter; |
369 | gpointer key, value; |
370 | |
371 | g_hash_table_iter_init (iter: &iter, hash_table: priv->objects); |
372 | while (g_hash_table_iter_next (iter: &iter, key: &key, value: &value)) |
373 | { |
374 | if (G_OBJECT (value)->ref_count == 1) |
375 | g_message ("builder: %s with id %s unused" , |
376 | G_OBJECT_TYPE_NAME (value), (const char *)key); |
377 | } |
378 | } |
379 | #endif |
380 | |
381 | g_hash_table_destroy (hash_table: priv->objects); |
382 | if (priv->signals) |
383 | g_ptr_array_free (array: priv->signals, TRUE); |
384 | |
385 | G_OBJECT_CLASS (gtk_builder_parent_class)->finalize (object); |
386 | } |
387 | |
388 | static void |
389 | gtk_builder_set_property (GObject *object, |
390 | guint prop_id, |
391 | const GValue *value, |
392 | GParamSpec *pspec) |
393 | { |
394 | GtkBuilder *builder = GTK_BUILDER (object); |
395 | |
396 | switch (prop_id) |
397 | { |
398 | case PROP_CURRENT_OBJECT: |
399 | gtk_builder_set_current_object (builder, current_object: g_value_get_object (value)); |
400 | break; |
401 | |
402 | case PROP_SCOPE: |
403 | gtk_builder_set_scope (builder, scope: g_value_get_object (value)); |
404 | break; |
405 | |
406 | case PROP_TRANSLATION_DOMAIN: |
407 | gtk_builder_set_translation_domain (builder, domain: g_value_get_string (value)); |
408 | break; |
409 | |
410 | default: |
411 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
412 | break; |
413 | } |
414 | } |
415 | |
416 | static void |
417 | gtk_builder_get_property (GObject *object, |
418 | guint prop_id, |
419 | GValue *value, |
420 | GParamSpec *pspec) |
421 | { |
422 | GtkBuilder *builder = GTK_BUILDER (object); |
423 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
424 | |
425 | switch (prop_id) |
426 | { |
427 | case PROP_CURRENT_OBJECT: |
428 | g_value_set_object (value, v_object: priv->current_object); |
429 | break; |
430 | |
431 | case PROP_SCOPE: |
432 | g_value_set_object (value, v_object: priv->scope); |
433 | break; |
434 | |
435 | case PROP_TRANSLATION_DOMAIN: |
436 | g_value_set_string (value, v_string: priv->domain); |
437 | break; |
438 | |
439 | default: |
440 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
441 | break; |
442 | } |
443 | } |
444 | |
445 | /* |
446 | * GtkBuilder virtual methods |
447 | */ |
448 | |
449 | typedef struct |
450 | { |
451 | char *object; |
452 | GParamSpec *pspec; |
453 | char *value; |
454 | int line; |
455 | int col; |
456 | } DelayedProperty; |
457 | |
458 | typedef struct |
459 | { |
460 | GPtrArray *names; |
461 | GArray *values; |
462 | } ObjectProperties; |
463 | |
464 | |
465 | static void |
466 | object_properties_init (ObjectProperties *self) |
467 | { |
468 | self->names = NULL; |
469 | self->values = NULL; |
470 | } |
471 | |
472 | static void |
473 | object_properties_destroy (ObjectProperties *self) |
474 | { |
475 | if (self == NULL) |
476 | return; |
477 | |
478 | if (self->names) |
479 | g_ptr_array_unref (array: self->names); |
480 | |
481 | if (self->values) |
482 | g_array_unref (array: self->values); |
483 | } |
484 | |
485 | static void |
486 | object_properties_add (ObjectProperties *self, |
487 | const char *name, |
488 | const GValue *value) |
489 | { |
490 | if (!self->names) |
491 | { |
492 | self->names = g_ptr_array_sized_new (reserved_size: 8); |
493 | self->values = g_array_sized_new (FALSE, FALSE, element_size: sizeof (GValue), reserved_size: 8); |
494 | g_array_set_clear_func (array: self->values, clear_func: (GDestroyNotify) g_value_unset); |
495 | } |
496 | |
497 | g_ptr_array_add (array: self->names, data: (char *) name); |
498 | g_array_append_vals (array: self->values, data: value, len: 1); |
499 | |
500 | g_assert (self->names->len == self->values->len); |
501 | } |
502 | |
503 | static const char * |
504 | object_properties_get_name (const ObjectProperties *self, |
505 | guint idx) |
506 | { |
507 | g_assert (self->names); |
508 | |
509 | return g_ptr_array_index (self->names, idx); |
510 | } |
511 | |
512 | static GValue * |
513 | object_properties_get_value (const ObjectProperties *self, |
514 | guint idx) |
515 | { |
516 | g_assert (self->values); |
517 | |
518 | return &g_array_index (self->values, GValue, idx); |
519 | } |
520 | |
521 | static void |
522 | gtk_builder_get_parameters (GtkBuilder *builder, |
523 | GType object_type, |
524 | const char *object_name, |
525 | GPtrArray *properties, |
526 | GParamFlags filter_flags, |
527 | ObjectProperties *parameters, |
528 | ObjectProperties *filtered_parameters) |
529 | { |
530 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
531 | DelayedProperty *property; |
532 | GError *error = NULL; |
533 | |
534 | if (!properties) |
535 | return; |
536 | |
537 | for (guint i = 0; i < properties->len; i++) |
538 | { |
539 | PropertyInfo *prop = g_ptr_array_index (properties, i); |
540 | const char *property_name = prop->pspec->name; |
541 | GValue property_value = G_VALUE_INIT; |
542 | ObjectProperties *params; |
543 | |
544 | if (prop->applied) |
545 | continue; |
546 | |
547 | if ((prop->pspec->flags & filter_flags) != 0) |
548 | params = filtered_parameters; |
549 | else |
550 | params = parameters; |
551 | |
552 | if (!params) |
553 | continue; |
554 | |
555 | if (prop->value) |
556 | { |
557 | g_value_init (value: &property_value, G_PARAM_SPEC_VALUE_TYPE (prop->pspec)); |
558 | |
559 | if (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) == GTK_TYPE_EXPRESSION) |
560 | gtk_value_set_expression (value: &property_value, expression: prop->value); |
561 | else |
562 | g_assert_not_reached (); |
563 | } |
564 | else if (prop->bound && (!prop->text || prop->text->len == 0)) |
565 | { |
566 | /* Ignore properties with a binding and no value since they are |
567 | * only there for to express the binding. |
568 | */ |
569 | continue; |
570 | } |
571 | else if (G_IS_PARAM_SPEC_OBJECT (prop->pspec) && |
572 | (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_PIXBUF) && |
573 | (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_TEXTURE) && |
574 | (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GDK_TYPE_PAINTABLE) && |
575 | (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GTK_TYPE_SHORTCUT_TRIGGER) && |
576 | (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != GTK_TYPE_SHORTCUT_ACTION) && |
577 | (G_PARAM_SPEC_VALUE_TYPE (prop->pspec) != G_TYPE_FILE)) |
578 | { |
579 | GObject *object = g_hash_table_lookup (hash_table: priv->objects, |
580 | g_strstrip (prop->text->str)); |
581 | |
582 | if (object) |
583 | { |
584 | g_value_init (value: &property_value, G_OBJECT_TYPE (object)); |
585 | g_value_set_object (value: &property_value, v_object: object); |
586 | } |
587 | else |
588 | { |
589 | if (prop->pspec->flags & G_PARAM_CONSTRUCT_ONLY) |
590 | { |
591 | g_warning ("Failed to get construct only property " |
592 | "%s of %s with value '%s'" , |
593 | prop->pspec->name, object_name, prop->text->str); |
594 | continue; |
595 | } |
596 | /* Delay setting property */ |
597 | |
598 | prop->applied = TRUE; |
599 | |
600 | property = g_slice_new (DelayedProperty); |
601 | property->pspec = prop->pspec; |
602 | property->object = g_strdup (str: object_name); |
603 | property->value = g_strdup (str: prop->text->str); |
604 | property->line = prop->line; |
605 | property->col = prop->col; |
606 | priv->delayed_properties = g_slist_prepend (list: priv->delayed_properties, |
607 | data: property); |
608 | continue; |
609 | } |
610 | } |
611 | else if (!gtk_builder_value_from_string (builder, pspec: prop->pspec, |
612 | string: prop->text->str, |
613 | value: &property_value, |
614 | error: &error)) |
615 | { |
616 | g_warning ("Failed to set property %s.%s to %s: %s" , |
617 | g_type_name (object_type), prop->pspec->name, prop->text->str, |
618 | error->message); |
619 | g_error_free (error); |
620 | error = NULL; |
621 | continue; |
622 | } |
623 | |
624 | /* At this point, property_value has been set, and we need to either |
625 | * copy it to one of the two arrays, or unset it. |
626 | */ |
627 | g_assert (G_IS_VALUE (&property_value)); |
628 | object_properties_add (self: params, name: property_name, value: &property_value); |
629 | prop->applied = TRUE; |
630 | } |
631 | } |
632 | |
633 | static const char * |
634 | object_get_id (GObject *object) |
635 | { |
636 | if (GTK_IS_BUILDABLE (object)) |
637 | return gtk_buildable_get_buildable_id (GTK_BUILDABLE (object)); |
638 | else |
639 | return g_object_get_data (object, key: "gtk-builder-id" ); |
640 | } |
641 | |
642 | static GObject * |
643 | gtk_builder_get_internal_child (GtkBuilder *builder, |
644 | ObjectInfo *info, |
645 | const char *childname, |
646 | GError **error) |
647 | { |
648 | GObject *obj = NULL; |
649 | |
650 | while (!obj) |
651 | { |
652 | if (!info->parent) |
653 | break; |
654 | |
655 | info = (ObjectInfo*)((ChildInfo*)info->parent)->parent; |
656 | if (!info) |
657 | break; |
658 | |
659 | GTK_NOTE (BUILDER, |
660 | g_message ("Trying to get internal child %s from %s" , |
661 | childname, object_get_id (info->object))); |
662 | |
663 | if (GTK_IS_BUILDABLE (info->object)) |
664 | obj = gtk_buildable_get_internal_child (GTK_BUILDABLE (info->object), |
665 | builder, |
666 | childname); |
667 | }; |
668 | |
669 | if (!obj) |
670 | { |
671 | g_set_error (err: error, |
672 | GTK_BUILDER_ERROR, |
673 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
674 | format: "Unknown internal child: %s" , childname); |
675 | } |
676 | return obj; |
677 | } |
678 | |
679 | static inline void |
680 | object_set_id (GObject *object, |
681 | const char *id) |
682 | { |
683 | if (GTK_IS_BUILDABLE (object)) |
684 | gtk_buildable_set_buildable_id (GTK_BUILDABLE (object), id); |
685 | else |
686 | g_object_set_data_full (object, key: "gtk-builder-id" , data: g_strdup (str: id), destroy: g_free); |
687 | } |
688 | |
689 | void |
690 | _gtk_builder_add_object (GtkBuilder *builder, |
691 | const char *id, |
692 | GObject *object) |
693 | { |
694 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
695 | |
696 | object_set_id (object, id); |
697 | g_hash_table_insert (hash_table: priv->objects, key: g_strdup (str: id), g_object_ref (object)); |
698 | } |
699 | |
700 | void |
701 | gtk_builder_take_bindings (GtkBuilder *builder, |
702 | GObject *target, |
703 | GSList *bindings) |
704 | { |
705 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
706 | GSList *l; |
707 | |
708 | for (l = bindings; l; l = l->next) |
709 | { |
710 | CommonInfo *common_info = l->data; |
711 | |
712 | if (common_info->tag_type == TAG_BINDING) |
713 | { |
714 | BindingInfo *info = l->data; |
715 | info->target = target; |
716 | } |
717 | else if (common_info->tag_type == TAG_BINDING_EXPRESSION) |
718 | { |
719 | BindingExpressionInfo *info = l->data; |
720 | info->target = target; |
721 | } |
722 | else |
723 | { |
724 | g_assert_not_reached (); |
725 | } |
726 | } |
727 | |
728 | priv->bindings = g_slist_concat (list1: priv->bindings, list2: bindings); |
729 | } |
730 | |
731 | static void |
732 | ensure_special_construct_parameters (GtkBuilder *builder, |
733 | GType object_type, |
734 | ObjectProperties *construct_parameters) |
735 | { |
736 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
737 | GValue value = G_VALUE_INIT; |
738 | |
739 | if (g_type_is_a (type: object_type, GTK_TYPE_BUILDER_LIST_ITEM_FACTORY)) |
740 | { |
741 | g_value_init (value: &value, GTK_TYPE_BUILDER_SCOPE); |
742 | g_value_set_object (value: &value, v_object: priv->scope); |
743 | object_properties_add (self: construct_parameters, name: "scope" , value: &value); |
744 | } |
745 | } |
746 | |
747 | GObject * |
748 | _gtk_builder_construct (GtkBuilder *builder, |
749 | ObjectInfo *info, |
750 | GError **error) |
751 | { |
752 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
753 | ObjectProperties parameters, construct_parameters; |
754 | GObject *obj; |
755 | int i; |
756 | GParamFlags param_filter_flags; |
757 | |
758 | g_assert (info->type != G_TYPE_INVALID); |
759 | |
760 | if (priv->template_type != 0 && |
761 | g_type_is_a (type: info->type, is_a_type: priv->template_type)) |
762 | { |
763 | g_set_error (err: error, |
764 | GTK_BUILDER_ERROR, |
765 | code: GTK_BUILDER_ERROR_OBJECT_TYPE_REFUSED, |
766 | format: "Refused to build object of type '%s' because it " |
767 | "conforms to the template type '%s', avoiding infinite recursion." , |
768 | g_type_name (type: info->type), g_type_name (type: priv->template_type)); |
769 | return NULL; |
770 | } |
771 | |
772 | /* If there is a manual constructor (like UIManager), or if this is a |
773 | * reference to an internal child, then we filter out construct-only |
774 | * and warn that they cannot be set. |
775 | * |
776 | * Otherwise if we are calling g_object_new_with_properties(), we want |
777 | * to pass both G_PARAM_CONSTRUCT and G_PARAM_CONSTRUCT_ONLY to the |
778 | * object's constructor. |
779 | * |
780 | * Passing all construct properties to g_object_new_with_properties() |
781 | * slightly improves performance as the construct properties will only |
782 | * be set once. |
783 | */ |
784 | if (info->constructor || |
785 | (info->parent && |
786 | info->parent->tag_type == TAG_CHILD && |
787 | ((ChildInfo*)info->parent)->internal_child != NULL)) |
788 | param_filter_flags = G_PARAM_CONSTRUCT_ONLY; |
789 | else |
790 | param_filter_flags = G_PARAM_CONSTRUCT | G_PARAM_CONSTRUCT_ONLY; |
791 | |
792 | object_properties_init (self: ¶meters); |
793 | object_properties_init (self: &construct_parameters); |
794 | |
795 | gtk_builder_get_parameters (builder, object_type: info->type, |
796 | object_name: info->id, |
797 | properties: info->properties, |
798 | filter_flags: param_filter_flags, |
799 | parameters: ¶meters, |
800 | filtered_parameters: &construct_parameters); |
801 | |
802 | if (info->constructor) |
803 | { |
804 | GObject *constructor; |
805 | |
806 | constructor = g_hash_table_lookup (hash_table: priv->objects, key: info->constructor); |
807 | if (constructor == NULL) |
808 | { |
809 | g_set_error (err: error, |
810 | GTK_BUILDER_ERROR, |
811 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
812 | format: "Unknown object constructor for %s: %s" , |
813 | info->id, |
814 | info->constructor); |
815 | object_properties_destroy (self: ¶meters); |
816 | object_properties_destroy (self: &construct_parameters); |
817 | return NULL; |
818 | } |
819 | obj = gtk_buildable_construct_child (GTK_BUILDABLE (constructor), |
820 | builder, |
821 | name: info->id); |
822 | g_assert (obj != NULL); |
823 | if (construct_parameters.names->len > 0) |
824 | g_warning ("Can't pass in construct-only parameters to %s" , info->id); |
825 | } |
826 | else if (info->parent && |
827 | info->parent->tag_type == TAG_CHILD && |
828 | ((ChildInfo*)info->parent)->internal_child != NULL) |
829 | { |
830 | char *childname = ((ChildInfo*)info->parent)->internal_child; |
831 | obj = gtk_builder_get_internal_child (builder, info, childname, error); |
832 | if (!obj) |
833 | { |
834 | object_properties_destroy (self: ¶meters); |
835 | object_properties_destroy (self: &construct_parameters); |
836 | return NULL; |
837 | } |
838 | if (construct_parameters.names) |
839 | g_warning ("Can't pass in construct-only parameters to %s" , childname); |
840 | g_object_ref (obj); |
841 | } |
842 | else |
843 | { |
844 | ensure_special_construct_parameters (builder, object_type: info->type, construct_parameters: &construct_parameters); |
845 | |
846 | if (construct_parameters.names) |
847 | obj = g_object_new_with_properties (object_type: info->type, |
848 | n_properties: construct_parameters.names->len, |
849 | names: (const char **) construct_parameters.names->pdata, |
850 | values: (GValue *) construct_parameters.values->data); |
851 | else |
852 | obj = g_object_new (object_type: info->type, NULL); |
853 | |
854 | /* No matter what, make sure we have a reference. |
855 | * |
856 | * If it's an initially unowned object, sink it. |
857 | * If it's not initially unowned then we have the reference already. |
858 | * |
859 | * In the case that this is a window it will be sunk already and |
860 | * this is effectively a call to g_object_ref(). That's what |
861 | * we want. |
862 | */ |
863 | if (G_IS_INITIALLY_UNOWNED (obj)) |
864 | g_object_ref_sink (obj); |
865 | |
866 | GTK_NOTE (BUILDER, |
867 | g_message ("created %s of type %s" , info->id, g_type_name (info->type))); |
868 | } |
869 | object_properties_destroy (self: &construct_parameters); |
870 | |
871 | if (parameters.names) |
872 | { |
873 | GtkBuildableIface *iface = NULL; |
874 | gboolean custom_set_property = FALSE; |
875 | GtkBuildable *buildable = NULL; |
876 | |
877 | if (GTK_IS_BUILDABLE (obj)) |
878 | { |
879 | buildable = GTK_BUILDABLE (obj); |
880 | iface = GTK_BUILDABLE_GET_IFACE (obj); |
881 | if (iface->set_buildable_property) |
882 | custom_set_property = TRUE; |
883 | } |
884 | |
885 | if (custom_set_property) |
886 | { |
887 | for (i = 0; i < parameters.names->len; i++) |
888 | { |
889 | const char *name = object_properties_get_name (self: ¶meters, idx: i); |
890 | const GValue *value = object_properties_get_value (self: ¶meters, idx: i); |
891 | |
892 | iface->set_buildable_property (buildable, builder, name, value); |
893 | #ifdef G_ENABLE_DEBUG |
894 | if (GTK_DEBUG_CHECK (BUILDER)) |
895 | { |
896 | char *str = g_strdup_value_contents (value); |
897 | g_message ("set %s: %s = %s" , info->id, name, str); |
898 | g_free (mem: str); |
899 | } |
900 | #endif |
901 | } |
902 | } |
903 | else |
904 | { |
905 | g_object_setv (object: obj, |
906 | n_properties: parameters.names->len, |
907 | names: (const char **) parameters.names->pdata, |
908 | values: (GValue *) parameters.values->data); |
909 | #ifdef G_ENABLE_DEBUG |
910 | if (GTK_DEBUG_CHECK (BUILDER)) |
911 | { |
912 | for (i = 0; i < parameters.names->len; i++) |
913 | { |
914 | const char *name = object_properties_get_name (self: ¶meters, idx: i); |
915 | const GValue *value = object_properties_get_value (self: ¶meters, idx: i); |
916 | char *str = g_strdup_value_contents (value); |
917 | g_message ("set %s: %s = %s" , info->id, name, str); |
918 | g_free (mem: str); |
919 | } |
920 | } |
921 | #endif |
922 | } |
923 | } |
924 | |
925 | object_properties_destroy (self: ¶meters); |
926 | |
927 | /* put it in the hash table. */ |
928 | _gtk_builder_add_object (builder, id: info->id, object: obj); |
929 | |
930 | /* we already own a reference to obj. */ |
931 | g_object_unref (object: obj); |
932 | |
933 | return obj; |
934 | } |
935 | |
936 | void |
937 | _gtk_builder_apply_properties (GtkBuilder *builder, |
938 | ObjectInfo *info, |
939 | GError **error) |
940 | { |
941 | ObjectProperties parameters; |
942 | |
943 | g_assert (info->object != NULL); |
944 | g_assert (info->type != G_TYPE_INVALID); |
945 | |
946 | object_properties_init (self: ¶meters); |
947 | |
948 | /* Fetch all properties that are not construct-only */ |
949 | gtk_builder_get_parameters (builder, object_type: info->type, |
950 | object_name: info->id, |
951 | properties: info->properties, |
952 | filter_flags: G_PARAM_CONSTRUCT_ONLY, |
953 | parameters: ¶meters, NULL); |
954 | |
955 | if (parameters.names) |
956 | { |
957 | GtkBuildableIface *iface = NULL; |
958 | GtkBuildable *buildable = NULL; |
959 | gboolean custom_set_property = FALSE; |
960 | int i; |
961 | |
962 | if (GTK_IS_BUILDABLE (info->object)) |
963 | { |
964 | buildable = GTK_BUILDABLE (info->object); |
965 | iface = GTK_BUILDABLE_GET_IFACE (info->object); |
966 | if (iface->set_buildable_property) |
967 | custom_set_property = TRUE; |
968 | } |
969 | |
970 | if (custom_set_property) |
971 | { |
972 | for (i = 0; i < parameters.names->len; i++) |
973 | { |
974 | const char *name = object_properties_get_name (self: ¶meters, idx: i); |
975 | const GValue *value = object_properties_get_value (self: ¶meters, idx: i); |
976 | iface->set_buildable_property (buildable, builder, name, value); |
977 | #ifdef G_ENABLE_DEBUG |
978 | if (GTK_DEBUG_CHECK (BUILDER)) |
979 | { |
980 | char *str = g_strdup_value_contents (value); |
981 | g_message ("set %s: %s = %s" , info->id, name, str); |
982 | g_free (mem: str); |
983 | } |
984 | #endif |
985 | } |
986 | } |
987 | else |
988 | { |
989 | g_object_setv (object: info->object, |
990 | n_properties: parameters.names->len, |
991 | names: (const char **) parameters.names->pdata, |
992 | values: (GValue *) parameters.values->data); |
993 | #ifdef G_ENABLE_DEBUG |
994 | if (GTK_DEBUG_CHECK (BUILDER)) |
995 | { |
996 | for (i = 0; i < parameters.names->len; i++) |
997 | { |
998 | const char *name = object_properties_get_name (self: ¶meters, idx: i); |
999 | const GValue *value = object_properties_get_value (self: ¶meters, idx: i); |
1000 | char *str = g_strdup_value_contents (value); |
1001 | g_message ("set %s: %s = %s" , info->id, name, str); |
1002 | g_free (mem: str); |
1003 | } |
1004 | } |
1005 | #endif |
1006 | } |
1007 | } |
1008 | |
1009 | object_properties_destroy (self: ¶meters); |
1010 | } |
1011 | |
1012 | void |
1013 | _gtk_builder_add (GtkBuilder *builder, |
1014 | ChildInfo *child_info) |
1015 | { |
1016 | GObject *object; |
1017 | GObject *parent; |
1018 | |
1019 | /* Internal children are already added |
1020 | * Also prevent us from being called twice. |
1021 | */ |
1022 | if (!child_info || |
1023 | child_info->internal_child || |
1024 | child_info->added) |
1025 | return; |
1026 | |
1027 | object = child_info->object; |
1028 | if (!object) |
1029 | return; |
1030 | |
1031 | if (!child_info->parent) |
1032 | { |
1033 | g_warning ("%s: Not adding, No parent" , object_get_id (object)); |
1034 | return; |
1035 | } |
1036 | |
1037 | g_assert (object != NULL); |
1038 | |
1039 | parent = ((ObjectInfo*)child_info->parent)->object; |
1040 | |
1041 | GTK_NOTE (BUILDER, |
1042 | g_message ("adding %s to %s" , object_get_id (object), object_get_id (parent))); |
1043 | |
1044 | if (G_IS_LIST_STORE (ptr: parent)) |
1045 | { |
1046 | if (child_info->type != NULL) |
1047 | { |
1048 | GTK_BUILDER_WARN_INVALID_CHILD_TYPE (parent, child_info->type); |
1049 | } |
1050 | else |
1051 | { |
1052 | g_list_store_append (store: G_LIST_STORE (ptr: parent), item: object); |
1053 | } |
1054 | } |
1055 | else |
1056 | { |
1057 | g_assert (GTK_IS_BUILDABLE (parent)); |
1058 | gtk_buildable_add_child (GTK_BUILDABLE (parent), builder, child: object, |
1059 | type: child_info->type); |
1060 | } |
1061 | |
1062 | child_info->added = TRUE; |
1063 | } |
1064 | |
1065 | void |
1066 | _gtk_builder_add_signals (GtkBuilder *builder, |
1067 | GPtrArray *signals) |
1068 | { |
1069 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1070 | |
1071 | if (G_UNLIKELY (!priv->signals)) |
1072 | priv->signals = g_ptr_array_new_with_free_func (element_free_func: (GDestroyNotify)_free_signal_info); |
1073 | |
1074 | g_ptr_array_extend_and_steal (array_to_extend: priv->signals, array: signals); |
1075 | } |
1076 | |
1077 | static gboolean |
1078 | gtk_builder_apply_delayed_properties (GtkBuilder *builder, |
1079 | GError **error) |
1080 | { |
1081 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1082 | GSList *l, *props; |
1083 | gboolean result = TRUE; |
1084 | |
1085 | /* take the list over from the builder->priv. |
1086 | * |
1087 | * g_slist_reverse does not copy the list, so the list now |
1088 | * belongs to us (and we free it at the end of this function). |
1089 | */ |
1090 | props = g_slist_reverse (list: priv->delayed_properties); |
1091 | priv->delayed_properties = NULL; |
1092 | |
1093 | for (l = props; l; l = l->next) |
1094 | { |
1095 | DelayedProperty *property = l->data; |
1096 | GObject *object, *obj; |
1097 | |
1098 | if (result) |
1099 | { |
1100 | object = g_hash_table_lookup (hash_table: priv->objects, key: property->object); |
1101 | g_assert (object != NULL); |
1102 | |
1103 | obj = gtk_builder_lookup_object (builder, name: property->value, line: property->line, col: property->col, error); |
1104 | if (obj) |
1105 | g_object_set (object, first_property_name: property->pspec->name, obj, NULL); |
1106 | else |
1107 | result = FALSE; |
1108 | } |
1109 | |
1110 | g_free (mem: property->value); |
1111 | g_free (mem: property->object); |
1112 | g_slice_free (DelayedProperty, property); |
1113 | } |
1114 | g_slist_free (list: props); |
1115 | |
1116 | return result; |
1117 | } |
1118 | |
1119 | static inline gboolean |
1120 | gtk_builder_create_bindings (GtkBuilder *builder, |
1121 | GError **error) |
1122 | { |
1123 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1124 | GSList *l; |
1125 | gboolean result = TRUE; |
1126 | |
1127 | for (l = priv->bindings; l; l = l->next) |
1128 | { |
1129 | CommonInfo *common_info = l->data; |
1130 | |
1131 | if (common_info->tag_type == TAG_BINDING) |
1132 | { |
1133 | BindingInfo *info = l->data; |
1134 | GObject *source; |
1135 | |
1136 | source = gtk_builder_lookup_object (builder, name: info->source, line: info->line, col: info->col, error); |
1137 | if (source) |
1138 | g_object_bind_property (source, source_property: info->source_property, |
1139 | target: info->target, target_property: info->target_pspec->name, |
1140 | flags: info->flags); |
1141 | else |
1142 | error = NULL; |
1143 | |
1144 | _free_binding_info (info, NULL); |
1145 | } |
1146 | else if (common_info->tag_type == TAG_BINDING_EXPRESSION) |
1147 | { |
1148 | BindingExpressionInfo *info = l->data; |
1149 | GtkExpression *expression; |
1150 | GObject *object; |
1151 | |
1152 | if (info->object_name) |
1153 | { |
1154 | object = gtk_builder_lookup_object (builder, name: info->object_name, line: info->line, col: info->col, error); |
1155 | if (object == NULL) |
1156 | { |
1157 | error = NULL; |
1158 | result = FALSE; |
1159 | } |
1160 | } |
1161 | else if (priv->current_object) |
1162 | { |
1163 | object = priv->current_object; |
1164 | } |
1165 | else |
1166 | { |
1167 | object = info->target; |
1168 | } |
1169 | |
1170 | if (object) |
1171 | { |
1172 | expression = expression_info_construct (builder, info: info->expr, error); |
1173 | if (expression == NULL) |
1174 | { |
1175 | g_prefix_error (err: error, format: "%s:%d:%d: " , priv->filename, info->line, info->col); |
1176 | error = NULL; |
1177 | result = FALSE; |
1178 | } |
1179 | else |
1180 | { |
1181 | gtk_expression_bind (self: expression, target: info->target, property: info->target_pspec->name, this_: object); |
1182 | } |
1183 | } |
1184 | |
1185 | free_binding_expression_info (info); |
1186 | } |
1187 | else |
1188 | g_assert_not_reached (); |
1189 | } |
1190 | |
1191 | g_slist_free (list: priv->bindings); |
1192 | priv->bindings = NULL; |
1193 | return result; |
1194 | } |
1195 | |
1196 | /** |
1197 | * gtk_builder_new: |
1198 | * |
1199 | * Creates a new empty builder object. |
1200 | * |
1201 | * This function is only useful if you intend to make multiple calls |
1202 | * to [method@Gtk.Builder.add_from_file], [method@Gtk.Builder.add_from_resource] |
1203 | * or [method@Gtk.Builder.add_from_string] in order to merge multiple UI |
1204 | * descriptions into a single builder. |
1205 | * |
1206 | * Returns: a new (empty) `GtkBuilder` object |
1207 | */ |
1208 | GtkBuilder * |
1209 | gtk_builder_new (void) |
1210 | { |
1211 | return g_object_new (GTK_TYPE_BUILDER, NULL); |
1212 | } |
1213 | |
1214 | /** |
1215 | * gtk_builder_add_from_file: |
1216 | * @builder: a `GtkBuilder` |
1217 | * @filename: (type filename): the name of the file to parse |
1218 | * @error: (nullable): return location for an error |
1219 | * |
1220 | * Parses a file containing a UI definition and merges it with |
1221 | * the current contents of @builder. |
1222 | * |
1223 | * This function is useful if you need to call |
1224 | * [method@Gtk.Builder.set_current_object]) to add user data to |
1225 | * callbacks before loading GtkBuilder UI. Otherwise, you probably |
1226 | * want [ctor@Gtk.Builder.new_from_file] instead. |
1227 | * |
1228 | * If an error occurs, 0 will be returned and @error will be assigned a |
1229 | * `GError` from the `GTK_BUILDER_ERROR`, `G_MARKUP_ERROR` or `G_FILE_ERROR` |
1230 | * domains. |
1231 | * |
1232 | * It’s not really reasonable to attempt to handle failures of this |
1233 | * call. You should not use this function with untrusted files (ie: |
1234 | * files that are not part of your application). Broken `GtkBuilder` |
1235 | * files can easily crash your program, and it’s possible that memory |
1236 | * was leaked leading up to the reported failure. The only reasonable |
1237 | * thing to do when an error is detected is to call `g_error()`. |
1238 | * |
1239 | * Returns: %TRUE on success, %FALSE if an error occurred |
1240 | */ |
1241 | gboolean |
1242 | gtk_builder_add_from_file (GtkBuilder *builder, |
1243 | const char *filename, |
1244 | GError **error) |
1245 | { |
1246 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1247 | char *buffer; |
1248 | gsize length; |
1249 | GError *tmp_error; |
1250 | |
1251 | g_return_val_if_fail (GTK_IS_BUILDER (builder), 0); |
1252 | g_return_val_if_fail (filename != NULL, 0); |
1253 | g_return_val_if_fail (error == NULL || *error == NULL, 0); |
1254 | |
1255 | tmp_error = NULL; |
1256 | |
1257 | if (!g_file_get_contents (filename, contents: &buffer, length: &length, error: &tmp_error)) |
1258 | { |
1259 | g_propagate_error (dest: error, src: tmp_error); |
1260 | return FALSE; |
1261 | } |
1262 | |
1263 | g_free (mem: priv->filename); |
1264 | g_free (mem: priv->resource_prefix); |
1265 | priv->filename = g_strdup (str: filename); |
1266 | priv->resource_prefix = NULL; |
1267 | |
1268 | _gtk_builder_parser_parse_buffer (builder, filename, |
1269 | buffer, length: (gssize)length, |
1270 | NULL, |
1271 | error: &tmp_error); |
1272 | |
1273 | g_free (mem: buffer); |
1274 | |
1275 | if (tmp_error != NULL) |
1276 | { |
1277 | g_propagate_error (dest: error, src: tmp_error); |
1278 | return FALSE; |
1279 | } |
1280 | |
1281 | return TRUE; |
1282 | } |
1283 | |
1284 | /** |
1285 | * gtk_builder_add_objects_from_file: |
1286 | * @builder: a `GtkBuilder` |
1287 | * @filename: (type filename): the name of the file to parse |
1288 | * @object_ids: (array zero-terminated=1) (element-type utf8): nul-terminated array of objects to build |
1289 | * @error: (nullable): return location for an error |
1290 | * |
1291 | * Parses a file containing a UI definition building only the |
1292 | * requested objects and merges them with the current contents |
1293 | * of @builder. |
1294 | * |
1295 | * Upon errors, 0 will be returned and @error will be assigned a |
1296 | * `GError` from the %GTK_BUILDER_ERROR, %G_MARKUP_ERROR or %G_FILE_ERROR |
1297 | * domain. |
1298 | * |
1299 | * If you are adding an object that depends on an object that is not |
1300 | * its child (for instance a `GtkTreeView` that depends on its |
1301 | * `GtkTreeModel`), you have to explicitly list all of them in @object_ids. |
1302 | * |
1303 | * Returns: %TRUE on success, %FALSE if an error occurred |
1304 | */ |
1305 | gboolean |
1306 | gtk_builder_add_objects_from_file (GtkBuilder *builder, |
1307 | const char *filename, |
1308 | const char **object_ids, |
1309 | GError **error) |
1310 | { |
1311 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1312 | char *buffer; |
1313 | gsize length; |
1314 | GError *tmp_error; |
1315 | |
1316 | g_return_val_if_fail (GTK_IS_BUILDER (builder), 0); |
1317 | g_return_val_if_fail (filename != NULL, 0); |
1318 | g_return_val_if_fail (object_ids != NULL && object_ids[0] != NULL, 0); |
1319 | g_return_val_if_fail (error == NULL || *error == NULL, 0); |
1320 | |
1321 | tmp_error = NULL; |
1322 | |
1323 | if (!g_file_get_contents (filename, contents: &buffer, length: &length, error: &tmp_error)) |
1324 | { |
1325 | g_propagate_error (dest: error, src: tmp_error); |
1326 | return 0; |
1327 | } |
1328 | |
1329 | g_free (mem: priv->filename); |
1330 | g_free (mem: priv->resource_prefix); |
1331 | priv->filename = g_strdup (str: filename); |
1332 | priv->resource_prefix = NULL; |
1333 | |
1334 | _gtk_builder_parser_parse_buffer (builder, filename, |
1335 | buffer, length: (gssize)length, |
1336 | requested_objs: object_ids, |
1337 | error: &tmp_error); |
1338 | |
1339 | g_free (mem: buffer); |
1340 | |
1341 | if (tmp_error != NULL) |
1342 | { |
1343 | g_propagate_error (dest: error, src: tmp_error); |
1344 | return FALSE; |
1345 | } |
1346 | |
1347 | return TRUE; |
1348 | } |
1349 | |
1350 | /** |
1351 | * gtk_builder_extend_with_template: |
1352 | * @builder: a `GtkBuilder` |
1353 | * @object: the object that is being extended |
1354 | * @template_type: the type that the template is for |
1355 | * @buffer: the string to parse |
1356 | * @length: the length of @buffer (may be -1 if @buffer is nul-terminated) |
1357 | * @error: (nullable): return location for an error |
1358 | * |
1359 | * Main private entry point for building composite components |
1360 | * from template XML. |
1361 | * |
1362 | * This is exported purely to let `gtk-builder-tool` validate |
1363 | * templates, applications have no need to call this function. |
1364 | * |
1365 | * Returns: A positive value on success, 0 if an error occurred |
1366 | */ |
1367 | gboolean |
1368 | gtk_builder_extend_with_template (GtkBuilder *builder, |
1369 | GObject *object, |
1370 | GType template_type, |
1371 | const char *buffer, |
1372 | gssize length, |
1373 | GError **error) |
1374 | { |
1375 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1376 | GError *tmp_error; |
1377 | char *filename; |
1378 | |
1379 | g_return_val_if_fail (GTK_IS_BUILDER (builder), 0); |
1380 | g_return_val_if_fail (G_IS_OBJECT (object), 0); |
1381 | g_return_val_if_fail (g_type_name (template_type) != NULL, 0); |
1382 | g_return_val_if_fail (g_type_is_a (G_OBJECT_TYPE (object), template_type), 0); |
1383 | g_return_val_if_fail (buffer && buffer[0], 0); |
1384 | |
1385 | tmp_error = NULL; |
1386 | |
1387 | g_free (mem: priv->filename); |
1388 | g_free (mem: priv->resource_prefix); |
1389 | priv->filename = g_strdup (str: "." ); |
1390 | priv->resource_prefix = NULL; |
1391 | priv->template_type = template_type; |
1392 | |
1393 | filename = g_strconcat (string1: "<" , g_type_name (type: template_type), " template>" , NULL); |
1394 | gtk_builder_expose_object (builder, name: g_type_name (type: template_type), object); |
1395 | _gtk_builder_parser_parse_buffer (builder, filename, |
1396 | buffer, length, |
1397 | NULL, |
1398 | error: &tmp_error); |
1399 | g_free (mem: filename); |
1400 | |
1401 | if (tmp_error != NULL) |
1402 | { |
1403 | g_propagate_error (dest: error, src: tmp_error); |
1404 | return FALSE; |
1405 | } |
1406 | |
1407 | return TRUE; |
1408 | } |
1409 | |
1410 | /** |
1411 | * gtk_builder_add_from_resource: |
1412 | * @builder: a `GtkBuilder` |
1413 | * @resource_path: the path of the resource file to parse |
1414 | * @error: (nullable): return location for an erro |
1415 | * |
1416 | * Parses a resource file containing a UI definition |
1417 | * and merges it with the current contents of @builder. |
1418 | * |
1419 | * This function is useful if you need to call |
1420 | * [method@Gtk.Builder.set_current_object] to add user data to |
1421 | * callbacks before loading GtkBuilder UI. Otherwise, you probably |
1422 | * want [ctor@Gtk.Builder.new_from_resource] instead. |
1423 | * |
1424 | * If an error occurs, 0 will be returned and @error will be assigned a |
1425 | * `GError` from the %GTK_BUILDER_ERROR, %G_MARKUP_ERROR or %G_RESOURCE_ERROR |
1426 | * domain. |
1427 | * |
1428 | * It’s not really reasonable to attempt to handle failures of this |
1429 | * call. The only reasonable thing to do when an error is detected is |
1430 | * to call g_error(). |
1431 | * |
1432 | * Returns: %TRUE on success, %FALSE if an error occurred |
1433 | */ |
1434 | gboolean |
1435 | gtk_builder_add_from_resource (GtkBuilder *builder, |
1436 | const char *resource_path, |
1437 | GError **error) |
1438 | { |
1439 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1440 | GError *tmp_error; |
1441 | GBytes *data; |
1442 | char *filename_for_errors; |
1443 | char *slash; |
1444 | |
1445 | g_return_val_if_fail (GTK_IS_BUILDER (builder), 0); |
1446 | g_return_val_if_fail (resource_path != NULL, 0); |
1447 | g_return_val_if_fail (error == NULL || *error == NULL, 0); |
1448 | |
1449 | tmp_error = NULL; |
1450 | |
1451 | data = g_resources_lookup_data (path: resource_path, lookup_flags: 0, error: &tmp_error); |
1452 | if (data == NULL) |
1453 | { |
1454 | g_propagate_error (dest: error, src: tmp_error); |
1455 | return 0; |
1456 | } |
1457 | |
1458 | g_free (mem: priv->filename); |
1459 | g_free (mem: priv->resource_prefix); |
1460 | priv->filename = g_strdup (str: "." ); |
1461 | |
1462 | slash = strrchr (s: resource_path, c: '/'); |
1463 | if (slash != NULL) |
1464 | priv->resource_prefix = g_strndup (str: resource_path, n: slash - resource_path + 1); |
1465 | else |
1466 | priv->resource_prefix = g_strdup (str: "/" ); |
1467 | |
1468 | filename_for_errors = g_strconcat (string1: "<resource>" , resource_path, NULL); |
1469 | |
1470 | _gtk_builder_parser_parse_buffer (builder, filename: filename_for_errors, |
1471 | buffer: g_bytes_get_data (bytes: data, NULL), length: g_bytes_get_size (bytes: data), |
1472 | NULL, |
1473 | error: &tmp_error); |
1474 | |
1475 | g_free (mem: filename_for_errors); |
1476 | g_bytes_unref (bytes: data); |
1477 | |
1478 | if (tmp_error != NULL) |
1479 | { |
1480 | g_propagate_error (dest: error, src: tmp_error); |
1481 | return FALSE; |
1482 | } |
1483 | |
1484 | return TRUE; |
1485 | } |
1486 | |
1487 | /** |
1488 | * gtk_builder_add_objects_from_resource: |
1489 | * @builder: a `GtkBuilder` |
1490 | * @resource_path: the path of the resource file to parse |
1491 | * @object_ids: (array zero-terminated=1) (element-type utf8): nul-terminated array of objects to build |
1492 | * @error: (nullable): return location for an error |
1493 | * |
1494 | * Parses a resource file containing a UI definition, building |
1495 | * only the requested objects and merges them with the current |
1496 | * contents of @builder. |
1497 | * |
1498 | * Upon errors, 0 will be returned and @error will be assigned a |
1499 | * `GError` from the %GTK_BUILDER_ERROR, %G_MARKUP_ERROR or %G_RESOURCE_ERROR |
1500 | * domain. |
1501 | * |
1502 | * If you are adding an object that depends on an object that is not |
1503 | * its child (for instance a `GtkTreeView` that depends on its |
1504 | * `GtkTreeModel`), you have to explicitly list all of them in @object_ids. |
1505 | * |
1506 | * Returns: %TRUE on success, %FALSE if an error occurred |
1507 | */ |
1508 | gboolean |
1509 | gtk_builder_add_objects_from_resource (GtkBuilder *builder, |
1510 | const char *resource_path, |
1511 | const char **object_ids, |
1512 | GError **error) |
1513 | { |
1514 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1515 | GError *tmp_error; |
1516 | GBytes *data; |
1517 | char *filename_for_errors; |
1518 | char *slash; |
1519 | |
1520 | g_return_val_if_fail (GTK_IS_BUILDER (builder), 0); |
1521 | g_return_val_if_fail (resource_path != NULL, 0); |
1522 | g_return_val_if_fail (object_ids != NULL && object_ids[0] != NULL, 0); |
1523 | g_return_val_if_fail (error == NULL || *error == NULL, 0); |
1524 | |
1525 | tmp_error = NULL; |
1526 | |
1527 | data = g_resources_lookup_data (path: resource_path, lookup_flags: 0, error: &tmp_error); |
1528 | if (data == NULL) |
1529 | { |
1530 | g_propagate_error (dest: error, src: tmp_error); |
1531 | return FALSE; |
1532 | } |
1533 | |
1534 | g_free (mem: priv->filename); |
1535 | g_free (mem: priv->resource_prefix); |
1536 | priv->filename = g_strdup (str: "." ); |
1537 | |
1538 | slash = strrchr (s: resource_path, c: '/'); |
1539 | if (slash != NULL) |
1540 | priv->resource_prefix = g_strndup (str: resource_path, n: slash - resource_path + 1); |
1541 | else |
1542 | priv->resource_prefix = g_strdup (str: "/" ); |
1543 | |
1544 | filename_for_errors = g_strconcat (string1: "<resource>" , resource_path, NULL); |
1545 | |
1546 | _gtk_builder_parser_parse_buffer (builder, filename: filename_for_errors, |
1547 | buffer: g_bytes_get_data (bytes: data, NULL), length: g_bytes_get_size (bytes: data), |
1548 | requested_objs: object_ids, |
1549 | error: &tmp_error); |
1550 | g_free (mem: filename_for_errors); |
1551 | g_bytes_unref (bytes: data); |
1552 | |
1553 | if (tmp_error != NULL) |
1554 | { |
1555 | g_propagate_error (dest: error, src: tmp_error); |
1556 | return FALSE; |
1557 | } |
1558 | |
1559 | return TRUE; |
1560 | } |
1561 | |
1562 | /** |
1563 | * gtk_builder_add_from_string: |
1564 | * @builder: a `GtkBuilder` |
1565 | * @buffer: the string to parse |
1566 | * @length: the length of @buffer (may be -1 if @buffer is nul-terminated) |
1567 | * @error: (nullable): return location for an error |
1568 | * |
1569 | * Parses a string containing a UI definition and merges it |
1570 | * with the current contents of @builder. |
1571 | * |
1572 | * This function is useful if you need to call |
1573 | * [method@Gtk.Builder.set_current_object] to add user data to |
1574 | * callbacks before loading `GtkBuilder` UI. Otherwise, you probably |
1575 | * want [ctor@Gtk.Builder.new_from_string] instead. |
1576 | * |
1577 | * Upon errors %FALSE will be returned and @error will be assigned a |
1578 | * `GError` from the %GTK_BUILDER_ERROR, %G_MARKUP_ERROR or |
1579 | * %G_VARIANT_PARSE_ERROR domain. |
1580 | * |
1581 | * It’s not really reasonable to attempt to handle failures of this |
1582 | * call. The only reasonable thing to do when an error is detected is |
1583 | * to call g_error(). |
1584 | * |
1585 | * Returns: %TRUE on success, %FALSE if an error occurred |
1586 | */ |
1587 | gboolean |
1588 | gtk_builder_add_from_string (GtkBuilder *builder, |
1589 | const char *buffer, |
1590 | gssize length, |
1591 | GError **error) |
1592 | { |
1593 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1594 | GError *tmp_error; |
1595 | |
1596 | g_return_val_if_fail (GTK_IS_BUILDER (builder), 0); |
1597 | g_return_val_if_fail (buffer != NULL, 0); |
1598 | g_return_val_if_fail (error == NULL || *error == NULL, 0); |
1599 | |
1600 | tmp_error = NULL; |
1601 | |
1602 | g_free (mem: priv->filename); |
1603 | g_free (mem: priv->resource_prefix); |
1604 | priv->filename = g_strdup (str: "." ); |
1605 | priv->resource_prefix = NULL; |
1606 | |
1607 | _gtk_builder_parser_parse_buffer (builder, filename: "<input>" , |
1608 | buffer, length, |
1609 | NULL, |
1610 | error: &tmp_error); |
1611 | if (tmp_error != NULL) |
1612 | { |
1613 | g_propagate_error (dest: error, src: tmp_error); |
1614 | return FALSE; |
1615 | } |
1616 | |
1617 | return TRUE; |
1618 | } |
1619 | |
1620 | /** |
1621 | * gtk_builder_add_objects_from_string: |
1622 | * @builder: a `GtkBuilder` |
1623 | * @buffer: the string to parse |
1624 | * @length: the length of @buffer (may be -1 if @buffer is nul-terminated) |
1625 | * @object_ids: (array zero-terminated=1) (element-type utf8): nul-terminated array of objects to build |
1626 | * @error: (nullable): return location for an error |
1627 | * |
1628 | * Parses a string containing a UI definition, building only the |
1629 | * requested objects and merges them with the current contents of |
1630 | * @builder. |
1631 | * |
1632 | * Upon errors %FALSE will be returned and @error will be assigned a |
1633 | * `GError` from the %GTK_BUILDER_ERROR or %G_MARKUP_ERROR domain. |
1634 | * |
1635 | * If you are adding an object that depends on an object that is not |
1636 | * its child (for instance a `GtkTreeView` that depends on its |
1637 | * `GtkTreeModel`), you have to explicitly list all of them in @object_ids. |
1638 | * |
1639 | * Returns: %TRUE on success, %FALSE if an error occurred |
1640 | */ |
1641 | gboolean |
1642 | gtk_builder_add_objects_from_string (GtkBuilder *builder, |
1643 | const char *buffer, |
1644 | gssize length, |
1645 | const char **object_ids, |
1646 | GError **error) |
1647 | { |
1648 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1649 | GError *tmp_error; |
1650 | |
1651 | g_return_val_if_fail (GTK_IS_BUILDER (builder), 0); |
1652 | g_return_val_if_fail (buffer != NULL, 0); |
1653 | g_return_val_if_fail (object_ids != NULL && object_ids[0] != NULL, 0); |
1654 | g_return_val_if_fail (error == NULL || *error == NULL, 0); |
1655 | |
1656 | tmp_error = NULL; |
1657 | |
1658 | g_free (mem: priv->filename); |
1659 | g_free (mem: priv->resource_prefix); |
1660 | priv->filename = g_strdup (str: "." ); |
1661 | priv->resource_prefix = NULL; |
1662 | |
1663 | _gtk_builder_parser_parse_buffer (builder, filename: "<input>" , |
1664 | buffer, length, |
1665 | requested_objs: object_ids, |
1666 | error: &tmp_error); |
1667 | |
1668 | if (tmp_error != NULL) |
1669 | { |
1670 | g_propagate_error (dest: error, src: tmp_error); |
1671 | return FALSE; |
1672 | } |
1673 | |
1674 | return TRUE; |
1675 | } |
1676 | |
1677 | /** |
1678 | * gtk_builder_get_object: |
1679 | * @builder: a `GtkBuilder` |
1680 | * @name: name of object to get |
1681 | * |
1682 | * Gets the object named @name. |
1683 | * |
1684 | * Note that this function does not increment the reference count |
1685 | * of the returned object. |
1686 | * |
1687 | * Returns: (nullable) (transfer none): the object named @name |
1688 | */ |
1689 | GObject * |
1690 | gtk_builder_get_object (GtkBuilder *builder, |
1691 | const char *name) |
1692 | { |
1693 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1694 | |
1695 | g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); |
1696 | g_return_val_if_fail (name != NULL, NULL); |
1697 | |
1698 | return g_hash_table_lookup (hash_table: priv->objects, key: name); |
1699 | } |
1700 | |
1701 | /** |
1702 | * gtk_builder_get_objects: |
1703 | * @builder: a `GtkBuilder` |
1704 | * |
1705 | * Gets all objects that have been constructed by @builder. |
1706 | * |
1707 | * Note that this function does not increment the reference |
1708 | * counts of the returned objects. |
1709 | * |
1710 | * Returns: (element-type GObject) (transfer container): a |
1711 | * newly-allocated `GSList` containing all the objects |
1712 | * constructed by the `GtkBuilder instance`. It should be |
1713 | * freed by g_slist_free() |
1714 | */ |
1715 | GSList * |
1716 | gtk_builder_get_objects (GtkBuilder *builder) |
1717 | { |
1718 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1719 | GSList *objects = NULL; |
1720 | GObject *object; |
1721 | GHashTableIter iter; |
1722 | |
1723 | g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); |
1724 | |
1725 | g_hash_table_iter_init (iter: &iter, hash_table: priv->objects); |
1726 | while (g_hash_table_iter_next (iter: &iter, NULL, value: (gpointer *)&object)) |
1727 | objects = g_slist_prepend (list: objects, data: object); |
1728 | |
1729 | return g_slist_reverse (list: objects); |
1730 | } |
1731 | |
1732 | /** |
1733 | * gtk_builder_set_translation_domain: (attributes org.gtk.Method.set_property=translation-domain) |
1734 | * @builder: a `GtkBuilder` |
1735 | * @domain: (nullable): the translation domain |
1736 | * |
1737 | * Sets the translation domain of @builder. |
1738 | */ |
1739 | void |
1740 | gtk_builder_set_translation_domain (GtkBuilder *builder, |
1741 | const char *domain) |
1742 | { |
1743 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1744 | char *new_domain; |
1745 | |
1746 | g_return_if_fail (GTK_IS_BUILDER (builder)); |
1747 | |
1748 | new_domain = g_strdup (str: domain); |
1749 | g_free (mem: priv->domain); |
1750 | priv->domain = new_domain; |
1751 | |
1752 | g_object_notify_by_pspec (G_OBJECT (builder), pspec: builder_props[PROP_TRANSLATION_DOMAIN]); |
1753 | } |
1754 | |
1755 | /** |
1756 | * gtk_builder_get_translation_domain: (attributes org.gtk.Method.get_property=translation-domain) |
1757 | * @builder: a `GtkBuilder` |
1758 | * |
1759 | * Gets the translation domain of @builder. |
1760 | * |
1761 | * Returns: (transfer none) (nullable): the translation domain |
1762 | */ |
1763 | const char * |
1764 | gtk_builder_get_translation_domain (GtkBuilder *builder) |
1765 | { |
1766 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1767 | |
1768 | g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); |
1769 | |
1770 | return priv->domain; |
1771 | } |
1772 | |
1773 | /** |
1774 | * gtk_builder_expose_object: |
1775 | * @builder: a `GtkBuilder` |
1776 | * @name: the name of the object exposed to the builder |
1777 | * @object: the object to expose |
1778 | * |
1779 | * Add @object to the @builder object pool so it can be |
1780 | * referenced just like any other object built by builder. |
1781 | */ |
1782 | void |
1783 | gtk_builder_expose_object (GtkBuilder *builder, |
1784 | const char *name, |
1785 | GObject *object) |
1786 | { |
1787 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1788 | |
1789 | g_return_if_fail (GTK_IS_BUILDER (builder)); |
1790 | g_return_if_fail (name && name[0]); |
1791 | g_return_if_fail (!g_hash_table_contains (priv->objects, name)); |
1792 | |
1793 | object_set_id (object, id: name); |
1794 | g_hash_table_insert (hash_table: priv->objects, |
1795 | key: g_strdup (str: name), |
1796 | g_object_ref (object)); |
1797 | } |
1798 | |
1799 | /** |
1800 | * gtk_builder_get_current_object: (attributes org.gtk.Method.get_property=current-object) |
1801 | * @builder: a `GtkBuilder` |
1802 | * |
1803 | * Gets the current object set via gtk_builder_set_current_object(). |
1804 | * |
1805 | * Returns: (nullable) (transfer none): the current object |
1806 | */ |
1807 | GObject * |
1808 | gtk_builder_get_current_object (GtkBuilder *builder) |
1809 | { |
1810 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1811 | |
1812 | g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); |
1813 | |
1814 | return priv->current_object; |
1815 | } |
1816 | |
1817 | /** |
1818 | * gtk_builder_set_current_object: (attributes org.gtk.Method.set_property=current-object) |
1819 | * @builder: a `GtkBuilder` |
1820 | * @current_object: (nullable) (transfer none): the new current object |
1821 | * |
1822 | * Sets the current object for the @builder. |
1823 | * |
1824 | * The current object can be thought of as the `this` object that the |
1825 | * builder is working for and will often be used as the default object |
1826 | * when an object is optional. |
1827 | * |
1828 | * [method@Gtk.Widget.init_template] for example will set the current |
1829 | * object to the widget the template is inited for. For functions like |
1830 | * [ctor@Gtk.Builder.new_from_resource], the current object will be %NULL. |
1831 | */ |
1832 | void |
1833 | gtk_builder_set_current_object (GtkBuilder *builder, |
1834 | GObject *current_object) |
1835 | { |
1836 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1837 | |
1838 | g_return_if_fail (GTK_IS_BUILDER (builder)); |
1839 | g_return_if_fail (current_object || G_IS_OBJECT (current_object)); |
1840 | |
1841 | if (!g_set_object (&priv->current_object, current_object)) |
1842 | return; |
1843 | |
1844 | g_object_notify_by_pspec (G_OBJECT (builder), pspec: builder_props[PROP_CURRENT_OBJECT]); |
1845 | } |
1846 | |
1847 | /** |
1848 | * gtk_builder_get_scope: (attributes org.gtk.Method.get_property=scope) |
1849 | * @builder: a `GtkBuilder` |
1850 | * |
1851 | * Gets the scope in use that was set via gtk_builder_set_scope(). |
1852 | * |
1853 | * Returns: (transfer none): the current scope |
1854 | */ |
1855 | GtkBuilderScope * |
1856 | gtk_builder_get_scope (GtkBuilder *builder) |
1857 | { |
1858 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1859 | |
1860 | g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); |
1861 | |
1862 | return priv->scope; |
1863 | } |
1864 | |
1865 | /** |
1866 | * gtk_builder_set_scope: (attributes org.gtk.Method.set_property=scope) |
1867 | * @builder: a `GtkBuilder` |
1868 | * @scope: (nullable) (transfer none): the scope to use |
1869 | * |
1870 | * Sets the scope the builder should operate in. |
1871 | * |
1872 | * If @scope is %NULL, a new [class@Gtk.BuilderCScope] will be created. |
1873 | */ |
1874 | void |
1875 | gtk_builder_set_scope (GtkBuilder *builder, |
1876 | GtkBuilderScope *scope) |
1877 | { |
1878 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1879 | |
1880 | g_return_if_fail (GTK_IS_BUILDER (builder)); |
1881 | g_return_if_fail (scope == NULL || GTK_IS_BUILDER_SCOPE (scope)); |
1882 | |
1883 | if (scope && priv->scope == scope) |
1884 | return; |
1885 | |
1886 | g_clear_object (&priv->scope); |
1887 | |
1888 | if (scope) |
1889 | priv->scope = g_object_ref (scope); |
1890 | else |
1891 | priv->scope = gtk_builder_cscope_new (); |
1892 | |
1893 | g_object_notify_by_pspec (G_OBJECT (builder), pspec: builder_props[PROP_SCOPE]); |
1894 | } |
1895 | |
1896 | static gboolean |
1897 | gtk_builder_connect_signals (GtkBuilder *builder, |
1898 | GError **error) |
1899 | { |
1900 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
1901 | GObject *object; |
1902 | GObject *connect_object; |
1903 | gboolean result = TRUE; |
1904 | |
1905 | if (!priv->signals || |
1906 | priv->signals->len == 0) |
1907 | return TRUE; |
1908 | |
1909 | for (guint i = 0; i < priv->signals->len; i++) |
1910 | { |
1911 | SignalInfo *signal = g_ptr_array_index (priv->signals, i); |
1912 | GClosure *closure; |
1913 | |
1914 | g_assert (signal != NULL); |
1915 | g_assert (signal->id != 0); |
1916 | |
1917 | object = g_hash_table_lookup (hash_table: priv->objects, key: signal->object_name); |
1918 | g_assert (object != NULL); |
1919 | |
1920 | connect_object = NULL; |
1921 | |
1922 | if (signal->connect_object_name) |
1923 | { |
1924 | connect_object = g_hash_table_lookup (hash_table: priv->objects, |
1925 | key: signal->connect_object_name); |
1926 | if (!connect_object) |
1927 | { |
1928 | g_set_error (err: error, |
1929 | GTK_BUILDER_ERROR, code: GTK_BUILDER_ERROR_INVALID_ID, |
1930 | format: "Could not lookup object %s on signal %s of object %s" , |
1931 | signal->connect_object_name, g_signal_name (signal_id: signal->id), |
1932 | signal->object_name); |
1933 | break; |
1934 | } |
1935 | } |
1936 | |
1937 | closure = gtk_builder_create_closure (builder, |
1938 | function_name: signal->handler, |
1939 | flags: signal->flags & G_CONNECT_SWAPPED ? TRUE : FALSE, |
1940 | object: connect_object, |
1941 | error); |
1942 | |
1943 | if (closure == NULL) |
1944 | { |
1945 | result = false; |
1946 | break; |
1947 | } |
1948 | |
1949 | g_signal_connect_closure_by_id (instance: object, |
1950 | signal_id: signal->id, |
1951 | detail: signal->detail, |
1952 | closure, |
1953 | after: signal->flags & G_CONNECT_AFTER ? TRUE : FALSE); |
1954 | } |
1955 | |
1956 | g_ptr_array_free (array: priv->signals, TRUE); |
1957 | priv->signals = NULL; |
1958 | |
1959 | return result; |
1960 | } |
1961 | |
1962 | gboolean |
1963 | _gtk_builder_finish (GtkBuilder *builder, |
1964 | GError **error) |
1965 | { |
1966 | return gtk_builder_apply_delayed_properties (builder, error) && |
1967 | gtk_builder_create_bindings (builder, error) && |
1968 | gtk_builder_connect_signals (builder, error); |
1969 | } |
1970 | |
1971 | /** |
1972 | * gtk_builder_value_from_string: |
1973 | * @builder: a `GtkBuilder` |
1974 | * @pspec: the `GParamSpec` for the property |
1975 | * @string: the string representation of the value |
1976 | * @value: (out): the `GValue` to store the result in |
1977 | * @error: (nullable): return location for an error |
1978 | * |
1979 | * Demarshals a value from a string. |
1980 | * |
1981 | * This function calls g_value_init() on the @value argument, |
1982 | * so it need not be initialised beforehand. |
1983 | * |
1984 | * Can handle char, uchar, boolean, int, uint, long, |
1985 | * ulong, enum, flags, float, double, string, `GdkRGBA` and |
1986 | * `GtkAdjustment` type values. |
1987 | * |
1988 | * Upon errors %FALSE will be returned and @error will be |
1989 | * assigned a `GError` from the %GTK_BUILDER_ERROR domain. |
1990 | * |
1991 | * Returns: %TRUE on success |
1992 | */ |
1993 | gboolean |
1994 | gtk_builder_value_from_string (GtkBuilder *builder, |
1995 | GParamSpec *pspec, |
1996 | const char *string, |
1997 | GValue *value, |
1998 | GError **error) |
1999 | { |
2000 | g_return_val_if_fail (GTK_IS_BUILDER (builder), FALSE); |
2001 | g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE); |
2002 | g_return_val_if_fail (string != NULL, FALSE); |
2003 | g_return_val_if_fail (value != NULL, FALSE); |
2004 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
2005 | |
2006 | /* |
2007 | * GParamSpecUnichar has the internal type G_TYPE_UINT, |
2008 | * so we cannot handle this in the switch, do it separately |
2009 | */ |
2010 | if (G_IS_PARAM_SPEC_UNICHAR (pspec)) |
2011 | { |
2012 | gunichar c; |
2013 | g_value_init (value, G_TYPE_UINT); |
2014 | c = g_utf8_get_char_validated (p: string, max_len: -1); |
2015 | if (c != 0 && c != (gunichar)-1 && c != (gunichar)-2) |
2016 | g_value_set_uint (value, v_uint: c); |
2017 | return TRUE; |
2018 | } |
2019 | |
2020 | /* |
2021 | * GParamSpecVariant can specify a GVariantType which can help with |
2022 | * parsing, so we need to take care of that here. |
2023 | */ |
2024 | if (G_IS_PARAM_SPEC_VARIANT (pspec)) |
2025 | { |
2026 | GParamSpecVariant *variant_pspec = G_PARAM_SPEC_VARIANT (pspec); |
2027 | const GVariantType *type; |
2028 | GVariant *variant; |
2029 | |
2030 | g_value_init (value, G_TYPE_VARIANT); |
2031 | |
2032 | /* The GVariant parser doesn't deal with indefinite types */ |
2033 | if (g_variant_type_is_definite (type: variant_pspec->type)) |
2034 | type = variant_pspec->type; |
2035 | else |
2036 | type = NULL; |
2037 | |
2038 | variant = g_variant_parse (type, text: string, NULL, NULL, error); |
2039 | if (variant == NULL) |
2040 | return FALSE; |
2041 | g_value_take_variant (value, variant); |
2042 | return TRUE; |
2043 | } |
2044 | |
2045 | return gtk_builder_value_from_string_type (builder, |
2046 | G_PARAM_SPEC_VALUE_TYPE (pspec), |
2047 | string, value, error); |
2048 | } |
2049 | |
2050 | gboolean |
2051 | _gtk_builder_boolean_from_string (const char *string, |
2052 | gboolean *value, |
2053 | GError **error) |
2054 | { |
2055 | if (string[0] == '\0') |
2056 | goto error; |
2057 | else if (string[1] == '\0') |
2058 | { |
2059 | char c; |
2060 | |
2061 | c = string[0]; |
2062 | if (c == '1' || |
2063 | c == 'y' || c == 't' || |
2064 | c == 'Y' || c == 'T') |
2065 | *value = TRUE; |
2066 | else if (c == '0' || |
2067 | c == 'n' || c == 'f' || |
2068 | c == 'N' || c == 'F') |
2069 | *value = FALSE; |
2070 | else |
2071 | goto error; |
2072 | } |
2073 | else |
2074 | { |
2075 | if (g_ascii_strcasecmp (s1: string, s2: "true" ) == 0 || |
2076 | g_ascii_strcasecmp (s1: string, s2: "yes" ) == 0) |
2077 | *value = TRUE; |
2078 | else if (g_ascii_strcasecmp (s1: string, s2: "false" ) == 0 || |
2079 | g_ascii_strcasecmp (s1: string, s2: "no" ) == 0) |
2080 | *value = FALSE; |
2081 | else |
2082 | goto error; |
2083 | } |
2084 | |
2085 | return TRUE; |
2086 | |
2087 | error: |
2088 | g_set_error (err: error, |
2089 | GTK_BUILDER_ERROR, |
2090 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2091 | format: "Could not parse boolean '%s'" , |
2092 | string); |
2093 | return FALSE; |
2094 | } |
2095 | |
2096 | |
2097 | /** |
2098 | * gtk_builder_value_from_string_type: |
2099 | * @builder: a `GtkBuilder` |
2100 | * @type: the `GType` of the value |
2101 | * @string: the string representation of the value |
2102 | * @value: (out): the `GValue` to store the result in |
2103 | * @error: (nullable): return location for an error |
2104 | * |
2105 | * Demarshals a value from a string. |
2106 | * |
2107 | * Unlike [method@Gtk.Builder.value_from_string], this function |
2108 | * takes a `GType` instead of `GParamSpec`. |
2109 | * |
2110 | * Calls g_value_init() on the @value argument, so it |
2111 | * need not be initialised beforehand. |
2112 | * |
2113 | * Upon errors %FALSE will be returned and @error will be |
2114 | * assigned a `GError` from the %GTK_BUILDER_ERROR domain. |
2115 | * |
2116 | * Returns: %TRUE on success |
2117 | */ |
2118 | gboolean |
2119 | gtk_builder_value_from_string_type (GtkBuilder *builder, |
2120 | GType type, |
2121 | const char *string, |
2122 | GValue *value, |
2123 | GError **error) |
2124 | { |
2125 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
2126 | gboolean ret = TRUE; |
2127 | |
2128 | g_return_val_if_fail (string != NULL, FALSE); |
2129 | g_return_val_if_fail (error == NULL || *error == NULL, FALSE); |
2130 | |
2131 | g_value_init (value, g_type: type); |
2132 | |
2133 | switch (G_TYPE_FUNDAMENTAL (type)) |
2134 | { |
2135 | case G_TYPE_CHAR: |
2136 | g_value_set_schar (value, v_char: string[0]); |
2137 | break; |
2138 | case G_TYPE_UCHAR: |
2139 | g_value_set_uchar (value, v_uchar: (guchar)string[0]); |
2140 | break; |
2141 | case G_TYPE_BOOLEAN: |
2142 | { |
2143 | gboolean b; |
2144 | |
2145 | if (!_gtk_builder_boolean_from_string (string, value: &b, error)) |
2146 | { |
2147 | ret = FALSE; |
2148 | break; |
2149 | } |
2150 | g_value_set_boolean (value, v_boolean: b); |
2151 | break; |
2152 | } |
2153 | case G_TYPE_INT: |
2154 | case G_TYPE_LONG: |
2155 | case G_TYPE_INT64: |
2156 | { |
2157 | gint64 l; |
2158 | char *endptr = NULL; |
2159 | |
2160 | errno = 0; |
2161 | l = g_ascii_strtoll (nptr: string, endptr: &endptr, base: 0); |
2162 | if (errno || endptr == string) |
2163 | { |
2164 | g_set_error (err: error, |
2165 | GTK_BUILDER_ERROR, |
2166 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2167 | format: "Could not parse integer '%s'" , |
2168 | string); |
2169 | ret = FALSE; |
2170 | break; |
2171 | } |
2172 | if (G_VALUE_HOLDS_INT (value)) |
2173 | g_value_set_int (value, v_int: l); |
2174 | else if (G_VALUE_HOLDS_LONG (value)) |
2175 | g_value_set_long (value, v_long: l); |
2176 | else |
2177 | g_value_set_int64 (value, v_int64: l); |
2178 | break; |
2179 | } |
2180 | case G_TYPE_UINT: |
2181 | case G_TYPE_ULONG: |
2182 | case G_TYPE_UINT64: |
2183 | { |
2184 | guint64 ul; |
2185 | char *endptr = NULL; |
2186 | errno = 0; |
2187 | ul = g_ascii_strtoull (nptr: string, endptr: &endptr, base: 0); |
2188 | if (errno || endptr == string) |
2189 | { |
2190 | g_set_error (err: error, |
2191 | GTK_BUILDER_ERROR, |
2192 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2193 | format: "Could not parse unsigned integer '%s'" , |
2194 | string); |
2195 | ret = FALSE; |
2196 | break; |
2197 | } |
2198 | if (G_VALUE_HOLDS_UINT (value)) |
2199 | g_value_set_uint (value, v_uint: ul); |
2200 | else if (G_VALUE_HOLDS_ULONG (value)) |
2201 | g_value_set_ulong (value, v_ulong: ul); |
2202 | else |
2203 | g_value_set_uint64 (value, v_uint64: ul); |
2204 | break; |
2205 | } |
2206 | case G_TYPE_ENUM: |
2207 | { |
2208 | int enum_value; |
2209 | if (!_gtk_builder_enum_from_string (type, string, enum_value: &enum_value, error)) |
2210 | { |
2211 | ret = FALSE; |
2212 | break; |
2213 | } |
2214 | g_value_set_enum (value, v_enum: enum_value); |
2215 | break; |
2216 | } |
2217 | case G_TYPE_FLAGS: |
2218 | { |
2219 | guint flags_value; |
2220 | |
2221 | if (!_gtk_builder_flags_from_string (type, string, value: &flags_value, error)) |
2222 | { |
2223 | ret = FALSE; |
2224 | break; |
2225 | } |
2226 | g_value_set_flags (value, v_flags: flags_value); |
2227 | break; |
2228 | } |
2229 | case G_TYPE_FLOAT: |
2230 | case G_TYPE_DOUBLE: |
2231 | { |
2232 | double d; |
2233 | char *endptr = NULL; |
2234 | errno = 0; |
2235 | d = g_ascii_strtod (nptr: string, endptr: &endptr); |
2236 | if (errno || endptr == string) |
2237 | { |
2238 | g_set_error (err: error, |
2239 | GTK_BUILDER_ERROR, |
2240 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2241 | format: "Could not parse double '%s'" , |
2242 | string); |
2243 | ret = FALSE; |
2244 | break; |
2245 | } |
2246 | if (G_VALUE_HOLDS_FLOAT (value)) |
2247 | g_value_set_float (value, v_float: d); |
2248 | else |
2249 | g_value_set_double (value, v_double: d); |
2250 | break; |
2251 | } |
2252 | case G_TYPE_STRING: |
2253 | g_value_set_string (value, v_string: string); |
2254 | break; |
2255 | case G_TYPE_VARIANT: |
2256 | { |
2257 | GVariant *variant; |
2258 | |
2259 | variant = g_variant_parse (NULL, text: string, NULL, NULL, error); |
2260 | if (value != NULL) |
2261 | g_value_take_variant (value, variant); |
2262 | else |
2263 | ret = FALSE; |
2264 | } |
2265 | break; |
2266 | case G_TYPE_BOXED: |
2267 | if (G_VALUE_HOLDS (value, GDK_TYPE_RGBA)) |
2268 | { |
2269 | GdkRGBA rgba = { 0 }; |
2270 | |
2271 | if (gdk_rgba_parse (rgba: &rgba, spec: string)) |
2272 | g_value_set_boxed (value, v_boxed: &rgba); |
2273 | else |
2274 | { |
2275 | g_set_error (err: error, |
2276 | GTK_BUILDER_ERROR, |
2277 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2278 | format: "Could not parse RGBA color '%s'" , |
2279 | string); |
2280 | ret = FALSE; |
2281 | } |
2282 | } |
2283 | else if (G_VALUE_HOLDS (value, GDK_TYPE_CONTENT_FORMATS)) |
2284 | { |
2285 | GdkContentFormats *formats; |
2286 | |
2287 | formats = gdk_content_formats_parse (string); |
2288 | if (formats) |
2289 | g_value_take_boxed (value, v_boxed: formats); |
2290 | else |
2291 | { |
2292 | g_set_error (err: error, |
2293 | GTK_BUILDER_ERROR, |
2294 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2295 | format: "Could not parse GdkContentFormats '%s'" , |
2296 | string); |
2297 | ret = FALSE; |
2298 | } |
2299 | } |
2300 | else if (G_VALUE_HOLDS (value, GSK_TYPE_TRANSFORM)) |
2301 | { |
2302 | GskTransform *transform; |
2303 | |
2304 | if (gsk_transform_parse (string, out_transform: &transform)) |
2305 | g_value_take_boxed (value, v_boxed: transform); |
2306 | else |
2307 | { |
2308 | g_set_error (err: error, |
2309 | GTK_BUILDER_ERROR, |
2310 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2311 | format: "Could not parse transform '%s'" , |
2312 | string); |
2313 | ret = FALSE; |
2314 | } |
2315 | } |
2316 | else if (G_VALUE_HOLDS (value, G_TYPE_STRV)) |
2317 | { |
2318 | char **vector = g_strsplit (string, delimiter: "\n" , max_tokens: 0); |
2319 | g_value_take_boxed (value, v_boxed: vector); |
2320 | } |
2321 | else if (G_VALUE_HOLDS (value, G_TYPE_BYTES)) |
2322 | { |
2323 | g_value_take_boxed (value, v_boxed: g_bytes_new (data: string, size: strlen (s: string))); |
2324 | } |
2325 | else |
2326 | { |
2327 | g_set_error (err: error, |
2328 | GTK_BUILDER_ERROR, |
2329 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2330 | format: "Could not parse '%s' as a %s" , |
2331 | string, G_VALUE_TYPE_NAME (value)); |
2332 | ret = FALSE; |
2333 | } |
2334 | break; |
2335 | case G_TYPE_OBJECT: |
2336 | case G_TYPE_INTERFACE: |
2337 | if (G_VALUE_HOLDS (value, GDK_TYPE_PAINTABLE) || |
2338 | G_VALUE_HOLDS (value, GDK_TYPE_TEXTURE)) |
2339 | { |
2340 | GObject *object = g_hash_table_lookup (hash_table: priv->objects, key: string); |
2341 | char *filename; |
2342 | GError *tmp_error = NULL; |
2343 | GdkTexture *texture = NULL; |
2344 | |
2345 | if (object) |
2346 | { |
2347 | if (g_type_is_a (G_OBJECT_TYPE (object), G_VALUE_TYPE (value))) |
2348 | { |
2349 | g_value_set_object (value, v_object: object); |
2350 | return TRUE; |
2351 | } |
2352 | else |
2353 | { |
2354 | g_set_error (err: error, |
2355 | GTK_BUILDER_ERROR, |
2356 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2357 | format: "Could not load image '%s': " |
2358 | " '%s' is already used as object id for a %s" , |
2359 | string, string, G_OBJECT_TYPE_NAME (object)); |
2360 | return FALSE; |
2361 | } |
2362 | } |
2363 | |
2364 | filename = _gtk_builder_get_resource_path (builder, string); |
2365 | if (filename != NULL) |
2366 | { |
2367 | texture = gdk_texture_new_from_resource (resource_path: filename); |
2368 | } |
2369 | else |
2370 | { |
2371 | GFile *file; |
2372 | |
2373 | filename = _gtk_builder_get_absolute_filename (builder, string); |
2374 | file = g_file_new_for_path (path: filename); |
2375 | texture = gdk_texture_new_from_file (file, error: &tmp_error); |
2376 | g_object_unref (object: file); |
2377 | } |
2378 | |
2379 | g_free (mem: filename); |
2380 | |
2381 | if (!texture) |
2382 | { |
2383 | g_warning ("Could not load image '%s': %s" , string, tmp_error->message); |
2384 | g_error_free (error: tmp_error); |
2385 | |
2386 | texture = gdk_texture_new_from_resource (IMAGE_MISSING_RESOURCE_PATH); |
2387 | } |
2388 | |
2389 | g_value_take_object (value, v_object: texture); |
2390 | |
2391 | ret = TRUE; |
2392 | } |
2393 | else if (G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF)) |
2394 | { |
2395 | char *filename; |
2396 | GError *tmp_error = NULL; |
2397 | GdkPixbuf *pixbuf = NULL; |
2398 | GObject *object; |
2399 | |
2400 | object = g_hash_table_lookup (hash_table: priv->objects, key: string); |
2401 | |
2402 | if (object) |
2403 | { |
2404 | if (g_type_is_a (G_OBJECT_TYPE (object), G_VALUE_TYPE (value))) |
2405 | { |
2406 | g_value_set_object (value, v_object: object); |
2407 | return TRUE; |
2408 | } |
2409 | else |
2410 | { |
2411 | g_set_error (err: error, |
2412 | GTK_BUILDER_ERROR, |
2413 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2414 | format: "Could not load image '%s': " |
2415 | " '%s' is already used as object id for a %s" , |
2416 | string, string, G_OBJECT_TYPE_NAME (object)); |
2417 | return FALSE; |
2418 | } |
2419 | } |
2420 | |
2421 | filename = _gtk_builder_get_resource_path (builder, string); |
2422 | if (filename != NULL) |
2423 | { |
2424 | GInputStream *stream = g_resources_open_stream (path: filename, lookup_flags: 0, error: &tmp_error); |
2425 | if (stream != NULL) |
2426 | { |
2427 | pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error: &tmp_error); |
2428 | g_object_unref (object: stream); |
2429 | } |
2430 | } |
2431 | else |
2432 | { |
2433 | filename = _gtk_builder_get_absolute_filename (builder, string); |
2434 | pixbuf = gdk_pixbuf_new_from_file (filename, error: &tmp_error); |
2435 | } |
2436 | |
2437 | if (pixbuf == NULL) |
2438 | { |
2439 | g_warning ("Could not load image '%s': %s" , string, tmp_error->message); |
2440 | g_error_free (error: tmp_error); |
2441 | |
2442 | pixbuf = gdk_pixbuf_new_from_resource (IMAGE_MISSING_RESOURCE_PATH, NULL); |
2443 | } |
2444 | |
2445 | g_value_take_object (value, v_object: pixbuf); |
2446 | |
2447 | g_free (mem: filename); |
2448 | |
2449 | ret = TRUE; |
2450 | } |
2451 | else if (G_VALUE_HOLDS (value, G_TYPE_FILE)) |
2452 | { |
2453 | GFile *file; |
2454 | |
2455 | if (g_hash_table_contains (hash_table: priv->objects, key: string)) |
2456 | { |
2457 | g_set_error (err: error, |
2458 | GTK_BUILDER_ERROR, |
2459 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2460 | format: "Could not create file '%s': " |
2461 | " '%s' is already used as object id" , |
2462 | string, string); |
2463 | return FALSE; |
2464 | } |
2465 | |
2466 | file = g_file_new_for_uri (uri: string); |
2467 | g_value_set_object (value, v_object: file); |
2468 | g_object_unref (G_OBJECT (file)); |
2469 | |
2470 | ret = TRUE; |
2471 | } |
2472 | else if (G_VALUE_HOLDS (value, GTK_TYPE_SHORTCUT_TRIGGER)) |
2473 | { |
2474 | GtkShortcutTrigger *trigger = gtk_shortcut_trigger_parse_string (string); |
2475 | |
2476 | if (trigger) |
2477 | g_value_take_object (value, v_object: trigger); |
2478 | else |
2479 | { |
2480 | g_set_error (err: error, |
2481 | GTK_BUILDER_ERROR, |
2482 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2483 | format: "Could not parse shortcut trigger '%s'" , |
2484 | string); |
2485 | ret = FALSE; |
2486 | } |
2487 | } |
2488 | else if (G_VALUE_HOLDS (value, GTK_TYPE_SHORTCUT_ACTION)) |
2489 | { |
2490 | GtkShortcutAction *action = gtk_shortcut_action_parse_builder (builder, string, error); |
2491 | |
2492 | /* Works for success and failure (NULL) case */ |
2493 | g_value_take_object (value, v_object: action); |
2494 | } |
2495 | else |
2496 | { |
2497 | GObject *object = g_hash_table_lookup (hash_table: priv->objects, key: string); |
2498 | |
2499 | if (object && g_value_type_compatible (G_OBJECT_TYPE (object), dest_type: type)) |
2500 | { |
2501 | g_value_set_object (value, v_object: object); |
2502 | } |
2503 | else if (object) |
2504 | { |
2505 | g_set_error (err: error, |
2506 | GTK_BUILDER_ERROR, |
2507 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2508 | format: "Object named \"%s\" is of type \"%s\" which is not compatible with expected type \%s\"" , |
2509 | string, G_OBJECT_TYPE_NAME (object), g_type_name (type)); |
2510 | ret = FALSE; |
2511 | } |
2512 | else |
2513 | { |
2514 | g_set_error (err: error, |
2515 | GTK_BUILDER_ERROR, |
2516 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2517 | format: "No object named \"%s\"" , string); |
2518 | ret = FALSE; |
2519 | } |
2520 | } |
2521 | break; |
2522 | case G_TYPE_POINTER: |
2523 | if (G_VALUE_HOLDS (value, G_TYPE_GTYPE)) |
2524 | { |
2525 | GType resolved_type; |
2526 | |
2527 | resolved_type = gtk_builder_get_type_from_name (builder, type_name: string); |
2528 | if (resolved_type == G_TYPE_INVALID) |
2529 | { |
2530 | g_set_error (err: error, |
2531 | GTK_BUILDER_ERROR, |
2532 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2533 | format: "Unsupported GType '%s' for value of type 'GType'" , string); |
2534 | return FALSE; |
2535 | } |
2536 | g_value_set_gtype (value, v_gtype: resolved_type); |
2537 | |
2538 | ret = TRUE; |
2539 | } |
2540 | else |
2541 | ret = FALSE; |
2542 | break; |
2543 | default: |
2544 | ret = FALSE; |
2545 | break; |
2546 | } |
2547 | |
2548 | /* Catch unassigned error for object types as well as any unsupported types. |
2549 | * While parsing GtkBuilder; object types are deserialized |
2550 | * without calling gtk_builder_value_from_string_type(). |
2551 | */ |
2552 | if (!ret && error && *error == NULL) |
2553 | g_set_error (err: error, |
2554 | GTK_BUILDER_ERROR, |
2555 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2556 | format: "Unsupported GType '%s'" , g_type_name (type)); |
2557 | |
2558 | return ret; |
2559 | } |
2560 | |
2561 | gboolean |
2562 | _gtk_builder_enum_from_string (GType type, |
2563 | const char *string, |
2564 | int *enum_value, |
2565 | GError **error) |
2566 | { |
2567 | GEnumClass *eclass; |
2568 | GEnumValue *ev; |
2569 | char *endptr; |
2570 | int value; |
2571 | gboolean ret; |
2572 | |
2573 | g_return_val_if_fail (G_TYPE_IS_ENUM (type), FALSE); |
2574 | g_return_val_if_fail (string != NULL, FALSE); |
2575 | |
2576 | ret = TRUE; |
2577 | |
2578 | endptr = NULL; |
2579 | errno = 0; |
2580 | value = g_ascii_strtoull (nptr: string, endptr: &endptr, base: 0); |
2581 | if (errno == 0 && endptr != string) /* parsed a number */ |
2582 | *enum_value = value; |
2583 | else |
2584 | { |
2585 | eclass = g_type_class_ref (type); |
2586 | ev = g_enum_get_value_by_nick (enum_class: eclass, nick: string); |
2587 | if (!ev) |
2588 | ev = g_enum_get_value_by_name (enum_class: eclass, name: string); |
2589 | |
2590 | if (ev) |
2591 | *enum_value = ev->value; |
2592 | else |
2593 | { |
2594 | g_set_error (err: error, |
2595 | GTK_BUILDER_ERROR, |
2596 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2597 | format: "Could not parse enum: '%s'" , |
2598 | string); |
2599 | ret = FALSE; |
2600 | } |
2601 | |
2602 | g_type_class_unref (g_class: eclass); |
2603 | } |
2604 | |
2605 | return ret; |
2606 | } |
2607 | |
2608 | gboolean |
2609 | _gtk_builder_flags_from_string (GType type, |
2610 | const char *string, |
2611 | guint *flags_value, |
2612 | GError **error) |
2613 | { |
2614 | GFlagsClass *fclass; |
2615 | char *endptr, *prevptr; |
2616 | guint i, j, value; |
2617 | char *flagstr; |
2618 | GFlagsValue *fv; |
2619 | const char *flag; |
2620 | gunichar ch; |
2621 | gboolean eos, ret; |
2622 | |
2623 | g_return_val_if_fail (G_TYPE_IS_FLAGS (type), FALSE); |
2624 | g_return_val_if_fail (string != 0, FALSE); |
2625 | |
2626 | ret = TRUE; |
2627 | |
2628 | endptr = NULL; |
2629 | errno = 0; |
2630 | value = g_ascii_strtoull (nptr: string, endptr: &endptr, base: 0); |
2631 | if (errno == 0 && endptr != string) /* parsed a number */ |
2632 | *flags_value = value; |
2633 | else |
2634 | { |
2635 | fclass = g_type_class_ref (type); |
2636 | |
2637 | flagstr = g_strdup (str: string); |
2638 | for (value = i = j = 0; ; i++) |
2639 | { |
2640 | |
2641 | eos = flagstr[i] == '\0'; |
2642 | |
2643 | if (!eos && flagstr[i] != '|') |
2644 | continue; |
2645 | |
2646 | flag = &flagstr[j]; |
2647 | endptr = &flagstr[i]; |
2648 | |
2649 | if (!eos) |
2650 | { |
2651 | flagstr[i++] = '\0'; |
2652 | j = i; |
2653 | } |
2654 | |
2655 | /* trim spaces */ |
2656 | for (;;) |
2657 | { |
2658 | ch = g_utf8_get_char (p: flag); |
2659 | if (!g_unichar_isspace (c: ch)) |
2660 | break; |
2661 | flag = g_utf8_next_char (flag); |
2662 | } |
2663 | |
2664 | while (endptr > flag) |
2665 | { |
2666 | prevptr = g_utf8_prev_char (p: endptr); |
2667 | ch = g_utf8_get_char (p: prevptr); |
2668 | if (!g_unichar_isspace (c: ch)) |
2669 | break; |
2670 | endptr = prevptr; |
2671 | } |
2672 | |
2673 | if (endptr > flag) |
2674 | { |
2675 | *endptr = '\0'; |
2676 | |
2677 | fv = NULL; |
2678 | |
2679 | if (!fv) |
2680 | fv = g_flags_get_value_by_name (flags_class: fclass, name: flag); |
2681 | |
2682 | if (!fv) |
2683 | fv = g_flags_get_value_by_nick (flags_class: fclass, nick: flag); |
2684 | |
2685 | if (fv) |
2686 | value |= fv->value; |
2687 | else |
2688 | { |
2689 | g_set_error (err: error, |
2690 | GTK_BUILDER_ERROR, |
2691 | code: GTK_BUILDER_ERROR_INVALID_VALUE, |
2692 | format: "Unknown flag: '%s'" , |
2693 | flag); |
2694 | ret = FALSE; |
2695 | break; |
2696 | } |
2697 | } |
2698 | |
2699 | if (eos) |
2700 | { |
2701 | *flags_value = value; |
2702 | break; |
2703 | } |
2704 | } |
2705 | |
2706 | g_free (mem: flagstr); |
2707 | |
2708 | g_type_class_unref (g_class: fclass); |
2709 | } |
2710 | |
2711 | return ret; |
2712 | } |
2713 | |
2714 | /** |
2715 | * gtk_builder_get_type_from_name: |
2716 | * @builder: a `GtkBuilder` |
2717 | * @type_name: type name to lookup |
2718 | * |
2719 | * Looks up a type by name. |
2720 | * |
2721 | * This is using the virtual function that `GtkBuilder` has |
2722 | * for that purpose. This is mainly used when implementing |
2723 | * the `GtkBuildable` interface on a type. |
2724 | * |
2725 | * Returns: the `GType` found for @type_name or %G_TYPE_INVALID |
2726 | * if no type was found |
2727 | */ |
2728 | GType |
2729 | gtk_builder_get_type_from_name (GtkBuilder *builder, |
2730 | const char *type_name) |
2731 | { |
2732 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
2733 | GType type; |
2734 | |
2735 | g_return_val_if_fail (GTK_IS_BUILDER (builder), G_TYPE_INVALID); |
2736 | g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID); |
2737 | |
2738 | type = gtk_builder_scope_get_type_from_name (self: priv->scope, builder, type_name); |
2739 | if (type == G_TYPE_INVALID) |
2740 | return G_TYPE_INVALID; |
2741 | |
2742 | if (G_TYPE_IS_CLASSED (type)) |
2743 | g_type_class_unref (g_class: g_type_class_ref (type)); |
2744 | |
2745 | return type; |
2746 | } |
2747 | |
2748 | GQuark |
2749 | gtk_builder_error_quark (void) |
2750 | { |
2751 | return g_quark_from_static_string (string: "gtk-builder-error-quark" ); |
2752 | } |
2753 | |
2754 | char * |
2755 | _gtk_builder_get_resource_path (GtkBuilder *builder, const char *string) |
2756 | { |
2757 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
2758 | |
2759 | if (g_str_has_prefix (str: string, prefix: "resource:///" )) |
2760 | return g_uri_unescape_string (escaped_string: string + 11, illegal_characters: "/" ); |
2761 | |
2762 | if (g_path_is_absolute (file_name: string) || |
2763 | priv->resource_prefix == NULL) |
2764 | return NULL; |
2765 | |
2766 | return g_build_path (separator: "/" , first_element: priv->resource_prefix, string, NULL); |
2767 | } |
2768 | |
2769 | char * |
2770 | _gtk_builder_get_absolute_filename (GtkBuilder *builder, |
2771 | const char *string) |
2772 | { |
2773 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
2774 | char *filename; |
2775 | char *dirname = NULL; |
2776 | |
2777 | if (g_path_is_absolute (file_name: string)) |
2778 | return g_strdup (str: string); |
2779 | |
2780 | if (priv->filename && |
2781 | strcmp (s1: priv->filename, s2: "." ) != 0) |
2782 | { |
2783 | dirname = g_path_get_dirname (file_name: priv->filename); |
2784 | |
2785 | if (strcmp (s1: dirname, s2: "." ) == 0) |
2786 | { |
2787 | g_free (mem: dirname); |
2788 | dirname = g_get_current_dir (); |
2789 | } |
2790 | } |
2791 | else |
2792 | dirname = g_get_current_dir (); |
2793 | |
2794 | filename = g_build_filename (first_element: dirname, string, NULL); |
2795 | g_free (mem: dirname); |
2796 | |
2797 | return filename; |
2798 | } |
2799 | |
2800 | GType |
2801 | _gtk_builder_get_template_type (GtkBuilder *builder) |
2802 | { |
2803 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
2804 | |
2805 | return priv->template_type; |
2806 | } |
2807 | |
2808 | /** |
2809 | * gtk_builder_create_closure: |
2810 | * @builder: a `GtkBuilder` |
2811 | * @function_name: name of the function to look up |
2812 | * @flags: closure creation flags |
2813 | * @object: (nullable): Object to create the closure with |
2814 | * @error: (nullable): return location for an error |
2815 | * |
2816 | * Creates a closure to invoke the function called @function_name. |
2817 | * |
2818 | * This is using the create_closure() implementation of @builder's |
2819 | * [iface@Gtk.BuilderScope]. |
2820 | * |
2821 | * If no closure could be created, %NULL will be returned and @error |
2822 | * will be set. |
2823 | * |
2824 | * Returns: (nullable): A new closure for invoking @function_name |
2825 | */ |
2826 | GClosure * |
2827 | gtk_builder_create_closure (GtkBuilder *builder, |
2828 | const char *function_name, |
2829 | GtkBuilderClosureFlags flags, |
2830 | GObject *object, |
2831 | GError **error) |
2832 | { |
2833 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
2834 | |
2835 | g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL); |
2836 | g_return_val_if_fail (function_name, NULL); |
2837 | g_return_val_if_fail (object == NULL || G_IS_OBJECT (object), NULL); |
2838 | g_return_val_if_fail (error == NULL || *error == NULL, NULL); |
2839 | |
2840 | return gtk_builder_scope_create_closure (self: priv->scope, builder, function_name, flags, object, error); |
2841 | } |
2842 | |
2843 | /** |
2844 | * gtk_builder_new_from_file: (constructor) |
2845 | * @filename: (type filename): filename of user interface description file |
2846 | * |
2847 | * Parses the UI definition in the file @filename. |
2848 | * |
2849 | * If there is an error opening the file or parsing the description then |
2850 | * the program will be aborted. You should only ever attempt to parse |
2851 | * user interface descriptions that are shipped as part of your program. |
2852 | * |
2853 | * Returns: (transfer full): a `GtkBuilder` containing the described interface |
2854 | */ |
2855 | GtkBuilder * |
2856 | gtk_builder_new_from_file (const char *filename) |
2857 | { |
2858 | GError *error = NULL; |
2859 | GtkBuilder *builder; |
2860 | |
2861 | builder = gtk_builder_new (); |
2862 | if (!gtk_builder_add_from_file (builder, filename, error: &error)) |
2863 | g_error ("failed to add UI from file %s: %s" , filename, error->message); |
2864 | |
2865 | return builder; |
2866 | } |
2867 | |
2868 | /** |
2869 | * gtk_builder_new_from_resource: (constructor) |
2870 | * @resource_path: a `GResource` resource path |
2871 | * |
2872 | * Parses the UI definition at @resource_path. |
2873 | * |
2874 | * If there is an error locating the resource or parsing the |
2875 | * description, then the program will be aborted. |
2876 | * |
2877 | * Returns: (transfer full): a `GtkBuilder` containing the described interface |
2878 | */ |
2879 | GtkBuilder * |
2880 | gtk_builder_new_from_resource (const char *resource_path) |
2881 | { |
2882 | GError *error = NULL; |
2883 | GtkBuilder *builder; |
2884 | |
2885 | builder = gtk_builder_new (); |
2886 | if (!gtk_builder_add_from_resource (builder, resource_path, error: &error)) |
2887 | g_error ("failed to add UI from resource %s: %s" , resource_path, error->message); |
2888 | |
2889 | return builder; |
2890 | } |
2891 | |
2892 | /** |
2893 | * gtk_builder_new_from_string: (constructor) |
2894 | * @string: a user interface (XML) description |
2895 | * @length: the length of @string, or -1 |
2896 | * |
2897 | * Parses the UI definition in @string. |
2898 | * |
2899 | * If @string is %NULL-terminated, then @length should be -1. |
2900 | * If @length is not -1, then it is the length of @string. |
2901 | * |
2902 | * If there is an error parsing @string then the program will be |
2903 | * aborted. You should not attempt to parse user interface description |
2904 | * from untrusted sources. |
2905 | * |
2906 | * Returns: (transfer full): a `GtkBuilder` containing the interface described by @string |
2907 | */ |
2908 | GtkBuilder * |
2909 | gtk_builder_new_from_string (const char *string, |
2910 | gssize length) |
2911 | { |
2912 | GError *error = NULL; |
2913 | GtkBuilder *builder; |
2914 | |
2915 | builder = gtk_builder_new (); |
2916 | if (!gtk_builder_add_from_string (builder, buffer: string, length, error: &error)) |
2917 | g_error ("failed to add UI: %s" , error->message); |
2918 | |
2919 | return builder; |
2920 | } |
2921 | |
2922 | /*< private > |
2923 | * _gtk_builder_prefix_error: |
2924 | * @builder: a `GtkBuilder` |
2925 | * @context: the `GtkBuildableParseContext` |
2926 | * @error: an error |
2927 | * |
2928 | * Calls g_prefix_error() to prepend a filename:line:column marker |
2929 | * to the given error. The filename is taken from @builder, and |
2930 | * the line and column are obtained by calling |
2931 | * g_markup_parse_context_get_position(). |
2932 | * |
2933 | * This is intended to be called on errors returned by |
2934 | * g_markup_collect_attributes() in a start_element vfunc. |
2935 | */ |
2936 | void |
2937 | _gtk_builder_prefix_error (GtkBuilder *builder, |
2938 | GtkBuildableParseContext *context, |
2939 | GError **error) |
2940 | { |
2941 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
2942 | int line, col; |
2943 | |
2944 | gtk_buildable_parse_context_get_position (context, line_number: &line, char_number: &col); |
2945 | g_prefix_error (err: error, format: "%s:%d:%d " , priv->filename, line, col); |
2946 | } |
2947 | |
2948 | /*< private > |
2949 | * _gtk_builder_error_unhandled_tag: |
2950 | * @builder: a `GtkBuilder` |
2951 | * @context: the `GtkBuildableParseContext` |
2952 | * @object: name of the object that is being handled |
2953 | * @element_name: name of the element whose start tag is being handled |
2954 | * @error: return location for the error |
2955 | * |
2956 | * Sets @error to a suitable error indicating that an @element_name |
2957 | * tag is not expected in the custom markup for @object. |
2958 | * |
2959 | * This is intended to be called in a start_element vfunc. |
2960 | */ |
2961 | void |
2962 | _gtk_builder_error_unhandled_tag (GtkBuilder *builder, |
2963 | GtkBuildableParseContext *context, |
2964 | const char *object, |
2965 | const char *element_name, |
2966 | GError **error) |
2967 | { |
2968 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
2969 | int line, col; |
2970 | |
2971 | gtk_buildable_parse_context_get_position (context, line_number: &line, char_number: &col); |
2972 | g_set_error (err: error, |
2973 | GTK_BUILDER_ERROR, |
2974 | code: GTK_BUILDER_ERROR_UNHANDLED_TAG, |
2975 | format: "%s:%d:%d Unsupported tag for %s: <%s>" , |
2976 | priv->filename, line, col, |
2977 | object, element_name); |
2978 | } |
2979 | |
2980 | /*< private > |
2981 | * @builder: a `GtkBuilder` |
2982 | * @context: the `GtkBuildableParseContext` |
2983 | * @parent_name: the name of the expected parent element |
2984 | * @error: return location for an error |
2985 | * |
2986 | * Checks that the parent element of the currently handled |
2987 | * start tag is @parent_name and set @error if it isn't. |
2988 | * |
2989 | * This is intended to be called in start_element vfuncs to |
2990 | * ensure that element nesting is as intended. |
2991 | * |
2992 | * Returns: %TRUE if @parent_name is the parent element |
2993 | */ |
2994 | gboolean |
2995 | _gtk_builder_check_parent (GtkBuilder *builder, |
2996 | GtkBuildableParseContext *context, |
2997 | const char *parent_name, |
2998 | GError **error) |
2999 | { |
3000 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
3001 | GPtrArray *stack; |
3002 | int line, col; |
3003 | const char *parent; |
3004 | const char *element; |
3005 | |
3006 | stack = gtk_buildable_parse_context_get_element_stack (context); |
3007 | |
3008 | element = g_ptr_array_index (stack, stack->len - 1); |
3009 | parent = stack->len > 1 ? g_ptr_array_index (stack, stack->len - 2) : "" ; |
3010 | |
3011 | if (g_str_equal (v1: parent_name, v2: parent) || |
3012 | (g_str_equal (v1: parent_name, v2: "object" ) && g_str_equal (v1: parent, v2: "template" ))) |
3013 | return TRUE; |
3014 | |
3015 | gtk_buildable_parse_context_get_position (context, line_number: &line, char_number: &col); |
3016 | g_set_error (err: error, |
3017 | GTK_BUILDER_ERROR, |
3018 | code: GTK_BUILDER_ERROR_INVALID_TAG, |
3019 | format: "%s:%d:%d Can't use <%s> here" , |
3020 | priv->filename, line, col, element); |
3021 | |
3022 | return FALSE; |
3023 | } |
3024 | |
3025 | GObject * |
3026 | gtk_builder_lookup_object (GtkBuilder *builder, |
3027 | const char *name, |
3028 | int line, |
3029 | int col, |
3030 | GError **error) |
3031 | { |
3032 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
3033 | GObject *obj; |
3034 | |
3035 | obj = g_hash_table_lookup (hash_table: priv->objects, key: name); |
3036 | if (obj == NULL) |
3037 | { |
3038 | g_set_error (err: error, |
3039 | GTK_BUILDER_ERROR, code: GTK_BUILDER_ERROR_INVALID_ID, |
3040 | format: "%s:%d:%d Object with ID %s not found" , |
3041 | priv->filename, line, col, name); |
3042 | } |
3043 | |
3044 | return obj; |
3045 | } |
3046 | |
3047 | /*< private > |
3048 | * @builder: a `GtkBuilder` |
3049 | * @name: object name to look up |
3050 | * @line: line number where @name was encountered |
3051 | * @col: column number where @name was encountered |
3052 | * |
3053 | * Looks up an object by name. Similar to gtk_builder_get_object(), |
3054 | * but sets an error if lookup fails during custom_tag_end, |
3055 | * custom_finished or parser_finished vfuncs. |
3056 | * |
3057 | * The reason for doing things this way is that these vfuncs don't |
3058 | * take a GError** parameter to return an error. |
3059 | * |
3060 | * Returns: the found object |
3061 | */ |
3062 | GObject * |
3063 | _gtk_builder_lookup_object (GtkBuilder *builder, |
3064 | const char *name, |
3065 | int line, |
3066 | int col) |
3067 | { |
3068 | GtkBuilderPrivate *priv = gtk_builder_get_instance_private (self: builder); |
3069 | GObject *obj; |
3070 | GError *error = NULL; |
3071 | |
3072 | obj = g_hash_table_lookup (hash_table: priv->objects, key: name); |
3073 | error = (GError *) g_object_get_data (G_OBJECT (builder), key: "lookup-error" ); |
3074 | |
3075 | if (!obj && !error) |
3076 | { |
3077 | g_set_error (err: &error, |
3078 | GTK_BUILDER_ERROR, code: GTK_BUILDER_ERROR_INVALID_ID, |
3079 | format: "%s:%d:%d Object with ID %s not found" , |
3080 | priv->filename, line, col, name); |
3081 | g_object_set_data_full (G_OBJECT (builder), key: "lookup-error" , |
3082 | data: error, destroy: (GDestroyNotify)g_error_free); |
3083 | } |
3084 | |
3085 | return obj; |
3086 | } |
3087 | |
3088 | /*< private > |
3089 | * _gtk_builder_lookup_failed: |
3090 | * @GtkBuilder: a `GtkBuilder` |
3091 | * @error: return location for error |
3092 | * |
3093 | * Finds whether any object lookups have failed. |
3094 | * |
3095 | * Returns: %TRUE if @error has been set |
3096 | */ |
3097 | gboolean |
3098 | _gtk_builder_lookup_failed (GtkBuilder *builder, |
3099 | GError **error) |
3100 | { |
3101 | GError *lookup_error; |
3102 | |
3103 | lookup_error = (GError*) g_object_steal_data (G_OBJECT (builder), key: "lookup-error" ); |
3104 | if (lookup_error) |
3105 | { |
3106 | g_propagate_error (dest: error, src: lookup_error); |
3107 | return TRUE; |
3108 | } |
3109 | |
3110 | return FALSE; |
3111 | } |
3112 | |