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 | |
58 | struct _GtkBuilderListItemFactory |
59 | { |
60 | GtkListItemFactory parent_instance; |
61 | |
62 | GtkBuilderScope *scope; |
63 | GBytes *bytes; |
64 | GBytes *data; |
65 | char *resource; |
66 | }; |
67 | |
68 | struct _GtkBuilderListItemFactoryClass |
69 | { |
70 | GtkListItemFactoryClass parent_class; |
71 | }; |
72 | |
73 | enum { |
74 | PROP_0, |
75 | PROP_BYTES, |
76 | PROP_RESOURCE, |
77 | PROP_SCOPE, |
78 | |
79 | N_PROPS |
80 | }; |
81 | |
82 | G_DEFINE_TYPE (GtkBuilderListItemFactory, gtk_builder_list_item_factory, GTK_TYPE_LIST_ITEM_FACTORY) |
83 | |
84 | static GParamSpec *properties[N_PROPS] = { NULL, }; |
85 | |
86 | static void |
87 | gtk_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 | |
121 | static void |
122 | gtk_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 | |
149 | static gboolean |
150 | gtk_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 | |
187 | static void |
188 | gtk_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 | |
236 | static void |
237 | gtk_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 | |
249 | static void |
250 | gtk_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 | |
300 | static void |
301 | gtk_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 | **/ |
315 | GtkListItemFactory * |
316 | gtk_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 | **/ |
337 | GtkListItemFactory * |
338 | gtk_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 | */ |
359 | GBytes * |
360 | gtk_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 | */ |
375 | const char * |
376 | gtk_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 | */ |
391 | GtkBuilderScope * |
392 | gtk_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 | |