1/*
2 * Copyright © 2019 Benjamin Otte
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authors: Benjamin Otte <otte@gnome.org>
18 */
19
20#include "config.h"
21
22#include "gtkbuilderlistitemfactory.h"
23
24#include "gtkbuilder.h"
25#include "gtkbuilderprivate.h"
26#include "gtkintl.h"
27#include "gtklistitemfactoryprivate.h"
28#include "gtklistitemprivate.h"
29
30/**
31 * GtkBuilderListItemFactory:
32 *
33 * `GtkBuilderListItemFactory` is a `GtkListItemFactory` that creates
34 * widgets by instantiating `GtkBuilder` UI templates.
35 *
36 * The templates must be extending `GtkListItem`, and typically use
37 * `GtkExpression`s to obtain data from the items in the model.
38 *
39 * Example:
40 * ```xml
41 * <interface>
42 * <template class="GtkListItem">
43 * <property name="child">
44 * <object class="GtkLabel">
45 * <property name="xalign">0</property>
46 * <binding name="label">
47 * <lookup name="name" type="SettingsKey">
48 * <lookup name="item">GtkListItem</lookup>
49 * </lookup>
50 * </binding>
51 * </object>
52 * </property>
53 * </template>
54 * </interface>
55 * ```
56 */
57
58struct _GtkBuilderListItemFactory
59{
60 GtkListItemFactory parent_instance;
61
62 GtkBuilderScope *scope;
63 GBytes *bytes;
64 GBytes *data;
65 char *resource;
66};
67
68struct _GtkBuilderListItemFactoryClass
69{
70 GtkListItemFactoryClass parent_class;
71};
72
73enum {
74 PROP_0,
75 PROP_BYTES,
76 PROP_RESOURCE,
77 PROP_SCOPE,
78
79 N_PROPS
80};
81
82G_DEFINE_TYPE (GtkBuilderListItemFactory, gtk_builder_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY)
83
84static GParamSpec *properties[N_PROPS] = { NULL, };
85
86static void
87gtk_builder_list_item_factory_setup (GtkListItemFactory *factory,
88 GtkListItemWidget *widget,
89 GtkListItem *list_item)
90{
91 GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (factory);
92 GtkBuilder *builder;
93 GError *error = NULL;
94
95 GTK_LIST_ITEM_FACTORY_CLASS (gtk_builder_list_item_factory_parent_class)->setup (factory, widget, list_item);
96
97 builder = gtk_builder_new ();
98
99 gtk_builder_set_current_object (builder, G_OBJECT (list_item));
100 if (self->scope)
101 gtk_builder_set_scope (builder, scope: self->scope);
102
103 if (!gtk_builder_extend_with_template (builder, G_OBJECT (list_item), G_OBJECT_TYPE (list_item),
104 buffer: (const char *)g_bytes_get_data (bytes: self->data, NULL),
105 length: g_bytes_get_size (bytes: self->data),
106 error: &error))
107 {
108 g_critical ("Error building template for list item: %s", error->message);
109 g_error_free (error);
110
111 /* This should never happen, if the template XML cannot be built
112 * then it is a critical programming error.
113 */
114 g_object_unref (object: builder);
115 return;
116 }
117
118 g_object_unref (object: builder);
119}
120
121static void
122gtk_builder_list_item_factory_get_property (GObject *object,
123 guint property_id,
124 GValue *value,
125 GParamSpec *pspec)
126{
127 GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
128
129 switch (property_id)
130 {
131 case PROP_BYTES:
132 g_value_set_boxed (value, v_boxed: self->bytes);
133 break;
134
135 case PROP_RESOURCE:
136 g_value_set_string (value, v_string: self->resource);
137 break;
138
139 case PROP_SCOPE:
140 g_value_set_object (value, v_object: self->scope);
141 break;
142
143 default:
144 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
145 break;
146 }
147}
148
149static gboolean
150gtk_builder_list_item_factory_set_bytes (GtkBuilderListItemFactory *self,
151 GBytes *bytes)
152{
153 if (bytes == NULL)
154 return FALSE;
155
156 if (self->bytes)
157 {
158 g_critical ("Data for GtkBuilderListItemFactory has already been set.");
159 return FALSE;
160 }
161
162 self->bytes = g_bytes_ref (bytes);
163
164 if (!_gtk_buildable_parser_is_precompiled (data: g_bytes_get_data (bytes, NULL), data_len: g_bytes_get_size (bytes)))
165 {
166 GError *error = NULL;
167 GBytes *data;
168
169 data = _gtk_buildable_parser_precompile (text: g_bytes_get_data (bytes, NULL),
170 text_len: g_bytes_get_size (bytes),
171 error: &error);
172 if (data == NULL)
173 {
174 g_warning ("Failed to precompile template for GtkBuilderListItemFactory: %s", error->message);
175 g_error_free (error);
176 self->data = g_bytes_ref (bytes);
177 }
178 else
179 {
180 self->data = data;
181 }
182 }
183
184 return TRUE;
185}
186
187static void
188gtk_builder_list_item_factory_set_property (GObject *object,
189 guint property_id,
190 const GValue *value,
191 GParamSpec *pspec)
192{
193 GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
194
195 switch (property_id)
196 {
197 case PROP_BYTES:
198 gtk_builder_list_item_factory_set_bytes (self, bytes: g_value_get_boxed (value));
199 break;
200
201 case PROP_RESOURCE:
202 {
203 GError *error = NULL;
204 GBytes *bytes;
205 const char *resource;
206
207 resource = g_value_get_string (value);
208 if (resource == NULL)
209 break;
210
211 bytes = g_resources_lookup_data (path: resource, lookup_flags: 0, error: &error);
212 if (bytes)
213 {
214 if (gtk_builder_list_item_factory_set_bytes (self, bytes))
215 self->resource = g_strdup (str: resource);
216 g_bytes_unref (bytes);
217 }
218 else
219 {
220 g_critical ("Unable to load resource for list item template: %s", error->message);
221 g_error_free (error);
222 }
223 }
224 break;
225
226 case PROP_SCOPE:
227 self->scope = g_value_dup_object (value);
228 break;
229
230 default:
231 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
232 break;
233 }
234}
235
236static void
237gtk_builder_list_item_factory_finalize (GObject *object)
238{
239 GtkBuilderListItemFactory *self = GTK_BUILDER_LIST_ITEM_FACTORY (object);
240
241 g_clear_object (&self->scope);
242 g_bytes_unref (bytes: self->bytes);
243 g_bytes_unref (bytes: self->data);
244 g_free (mem: self->resource);
245
246 G_OBJECT_CLASS (gtk_builder_list_item_factory_parent_class)->finalize (object);
247}
248
249static void
250gtk_builder_list_item_factory_class_init (GtkBuilderListItemFactoryClass *klass)
251{
252 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
253 GtkListItemFactoryClass *factory_class = GTK_LIST_ITEM_FACTORY_CLASS (klass);
254
255 gobject_class->finalize = gtk_builder_list_item_factory_finalize;
256 gobject_class->get_property = gtk_builder_list_item_factory_get_property;
257 gobject_class->set_property = gtk_builder_list_item_factory_set_property;
258
259 factory_class->setup = gtk_builder_list_item_factory_setup;
260
261 /**
262 * GtkBuilderListItemFactory:bytes: (attributes org.gtk.Property.get=gtk_builder_list_item_factory_get_bytes)
263 *
264 * `GBytes` containing the UI definition.
265 */
266 properties[PROP_BYTES] =
267 g_param_spec_boxed (name: "bytes",
268 P_("Bytes"),
269 P_("bytes containing the UI definition"),
270 G_TYPE_BYTES,
271 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
272
273 /**
274 * GtkBuilderListItemFactory:resource: (attributes org.gtk.Property.get=gtk_builder_list_item_factory_get_resource)
275 *
276 * Path of the resource containing the UI definition.
277 */
278 properties[PROP_RESOURCE] =
279 g_param_spec_string (name: "resource",
280 P_("Resource"),
281 P_("resource containing the UI definition"),
282 NULL,
283 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
284
285 /**
286 * GtkBuilderListItemFactory:scope: (attributes org.gtk.Property.get=gtk_builder_list_item_factory_get_scope)
287 *
288 * `GtkBuilderScope` to use when instantiating listitems
289 */
290 properties[PROP_SCOPE] =
291 g_param_spec_object (name: "scope",
292 P_("Scope"),
293 P_("scope to use when instantiating listitems"),
294 GTK_TYPE_BUILDER_SCOPE,
295 flags: G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
296
297 g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_PROPS, pspecs: properties);
298}
299
300static void
301gtk_builder_list_item_factory_init (GtkBuilderListItemFactory *self)
302{
303}
304
305/**
306 * gtk_builder_list_item_factory_new_from_bytes:
307 * @scope: (nullable) (transfer none): A scope to use when instantiating
308 * @bytes: the `GBytes` containing the ui file to instantiate
309 *
310 * Creates a new `GtkBuilderListItemFactory` that instantiates widgets
311 * using @bytes as the data to pass to `GtkBuilder`.
312 *
313 * Returns: a new `GtkBuilderListItemFactory`
314 **/
315GtkListItemFactory *
316gtk_builder_list_item_factory_new_from_bytes (GtkBuilderScope *scope,
317 GBytes *bytes)
318{
319 g_return_val_if_fail (bytes != NULL, NULL);
320
321 return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
322 first_property_name: "bytes", bytes,
323 "scope", scope,
324 NULL);
325}
326
327/**
328 * gtk_builder_list_item_factory_new_from_resource:
329 * @scope: (nullable) (transfer none): A scope to use when instantiating
330 * @resource_path: valid path to a resource that contains the data
331 *
332 * Creates a new `GtkBuilderListItemFactory` that instantiates widgets
333 * using data read from the given @resource_path to pass to `GtkBuilder`.
334 *
335 * Returns: a new `GtkBuilderListItemFactory`
336 **/
337GtkListItemFactory *
338gtk_builder_list_item_factory_new_from_resource (GtkBuilderScope *scope,
339 const char *resource_path)
340{
341 g_return_val_if_fail (scope == NULL || GTK_IS_BUILDER_SCOPE (scope), NULL);
342 g_return_val_if_fail (resource_path != NULL, NULL);
343
344 return g_object_new (GTK_TYPE_BUILDER_LIST_ITEM_FACTORY,
345 first_property_name: "resource", resource_path,
346 "scope", scope,
347 NULL);
348}
349
350/**
351 * gtk_builder_list_item_factory_get_bytes: (attributes org.gtk.Method.get_property=bytes)
352 * @self: a `GtkBuilderListItemFactory`
353 *
354 * Gets the data used as the `GtkBuilder` UI template for constructing
355 * listitems.
356 *
357 * Returns: (transfer none): The `GtkBuilder` data
358 */
359GBytes *
360gtk_builder_list_item_factory_get_bytes (GtkBuilderListItemFactory *self)
361{
362 g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
363
364 return self->bytes;
365}
366
367/**
368 * gtk_builder_list_item_factory_get_resource: (attributes org.gtk.Method.get_property=resource)
369 * @self: a `GtkBuilderListItemFactory`
370 *
371 * If the data references a resource, gets the path of that resource.
372 *
373 * Returns: (transfer none) (nullable): The path to the resource
374 */
375const char *
376gtk_builder_list_item_factory_get_resource (GtkBuilderListItemFactory *self)
377{
378 g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
379
380 return self->resource;
381}
382
383/**
384 * gtk_builder_list_item_factory_get_scope: (attributes org.gtk.Method.get_property=scope)
385 * @self: a `GtkBuilderListItemFactory`
386 *
387 * Gets the scope used when constructing listitems.
388 *
389 * Returns: (transfer none) (nullable): The scope used when constructing listitems
390 */
391GtkBuilderScope *
392gtk_builder_list_item_factory_get_scope (GtkBuilderListItemFactory *self)
393{
394 g_return_val_if_fail (GTK_IS_BUILDER_LIST_ITEM_FACTORY (self), NULL);
395
396 return self->scope;
397}
398

source code of gtk/gtk/gtkbuilderlistitemfactory.c