1 | /* |
2 | * Copyright © 2018 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 "gtklistitemprivate.h" |
23 | |
24 | #include "gtkintl.h" |
25 | |
26 | /** |
27 | * GtkListItem: |
28 | * |
29 | * `GtkListItem` is used by list widgets to represent items in a `GListModel`. |
30 | * |
31 | * The `GtkListItem`s are managed by the list widget (with its factory) |
32 | * and cannot be created by applications, but they need to be populated |
33 | * by application code. This is done by calling [method@Gtk.ListItem.set_child]. |
34 | * |
35 | * `GtkListItem`s exist in 2 stages: |
36 | * |
37 | * 1. The unbound stage where the listitem is not currently connected to |
38 | * an item in the list. In that case, the [property@Gtk.ListItem:item] |
39 | * property is set to %NULL. |
40 | * |
41 | * 2. The bound stage where the listitem references an item from the list. |
42 | * The [property@Gtk.ListItem:item] property is not %NULL. |
43 | */ |
44 | |
45 | struct _GtkListItemClass |
46 | { |
47 | GObjectClass parent_class; |
48 | }; |
49 | |
50 | enum |
51 | { |
52 | PROP_0, |
53 | PROP_ACTIVATABLE, |
54 | PROP_CHILD, |
55 | PROP_ITEM, |
56 | PROP_POSITION, |
57 | PROP_SELECTABLE, |
58 | PROP_SELECTED, |
59 | |
60 | N_PROPS |
61 | }; |
62 | |
63 | G_DEFINE_TYPE (GtkListItem, gtk_list_item, G_TYPE_OBJECT) |
64 | |
65 | static GParamSpec *properties[N_PROPS] = { NULL, }; |
66 | |
67 | static void |
68 | gtk_list_item_dispose (GObject *object) |
69 | { |
70 | GtkListItem *self = GTK_LIST_ITEM (object); |
71 | |
72 | g_assert (self->owner == NULL); /* would hold a reference */ |
73 | g_clear_object (&self->child); |
74 | |
75 | G_OBJECT_CLASS (gtk_list_item_parent_class)->dispose (object); |
76 | } |
77 | |
78 | static void |
79 | gtk_list_item_get_property (GObject *object, |
80 | guint property_id, |
81 | GValue *value, |
82 | GParamSpec *pspec) |
83 | { |
84 | GtkListItem *self = GTK_LIST_ITEM (object); |
85 | |
86 | switch (property_id) |
87 | { |
88 | case PROP_ACTIVATABLE: |
89 | g_value_set_boolean (value, v_boolean: self->activatable); |
90 | break; |
91 | |
92 | case PROP_CHILD: |
93 | g_value_set_object (value, v_object: self->child); |
94 | break; |
95 | |
96 | case PROP_ITEM: |
97 | if (self->owner) |
98 | g_value_set_object (value, v_object: gtk_list_item_widget_get_item (self: self->owner)); |
99 | break; |
100 | |
101 | case PROP_POSITION: |
102 | if (self->owner) |
103 | g_value_set_uint (value, v_uint: gtk_list_item_widget_get_position (self: self->owner)); |
104 | else |
105 | g_value_set_uint (value, GTK_INVALID_LIST_POSITION); |
106 | break; |
107 | |
108 | case PROP_SELECTABLE: |
109 | g_value_set_boolean (value, v_boolean: self->selectable); |
110 | break; |
111 | |
112 | case PROP_SELECTED: |
113 | if (self->owner) |
114 | g_value_set_boolean (value, v_boolean: gtk_list_item_widget_get_selected (self: self->owner)); |
115 | else |
116 | g_value_set_boolean (value, FALSE); |
117 | break; |
118 | |
119 | default: |
120 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
121 | break; |
122 | } |
123 | } |
124 | |
125 | static void |
126 | gtk_list_item_set_property (GObject *object, |
127 | guint property_id, |
128 | const GValue *value, |
129 | GParamSpec *pspec) |
130 | { |
131 | GtkListItem *self = GTK_LIST_ITEM (object); |
132 | |
133 | switch (property_id) |
134 | { |
135 | case PROP_ACTIVATABLE: |
136 | gtk_list_item_set_activatable (self, activatable: g_value_get_boolean (value)); |
137 | break; |
138 | |
139 | case PROP_CHILD: |
140 | gtk_list_item_set_child (self, child: g_value_get_object (value)); |
141 | break; |
142 | |
143 | case PROP_SELECTABLE: |
144 | gtk_list_item_set_selectable (self, selectable: g_value_get_boolean (value)); |
145 | break; |
146 | |
147 | default: |
148 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); |
149 | break; |
150 | } |
151 | } |
152 | |
153 | static void |
154 | gtk_list_item_class_init (GtkListItemClass *klass) |
155 | { |
156 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
157 | |
158 | gobject_class->dispose = gtk_list_item_dispose; |
159 | gobject_class->get_property = gtk_list_item_get_property; |
160 | gobject_class->set_property = gtk_list_item_set_property; |
161 | |
162 | /** |
163 | * GtkListItem:activatable: (attributes org.gtk.Property.get=gtk_list_item_get_activatable org.gtk.Property.set=gtk_list_item_set_activatable) |
164 | * |
165 | * If the item can be activated by the user. |
166 | */ |
167 | properties[PROP_ACTIVATABLE] = |
168 | g_param_spec_boolean (name: "activatable" , |
169 | P_("Activatable" ), |
170 | P_("If the item can be activated by the user" ), |
171 | TRUE, |
172 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
173 | |
174 | /** |
175 | * GtkListItem:child: (attributes org.gtk.Property.get=gtk_list_item_get_child org.gtk.Property.set=gtk_list_item_set_child) |
176 | * |
177 | * Widget used for display. |
178 | */ |
179 | properties[PROP_CHILD] = |
180 | g_param_spec_object (name: "child" , |
181 | P_("Child" ), |
182 | P_("Widget used for display" ), |
183 | GTK_TYPE_WIDGET, |
184 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
185 | |
186 | /** |
187 | * GtkListItem:item: (attributes org.gtk.Property.get=gtk_list_item_get_item) |
188 | * |
189 | * Displayed item. |
190 | */ |
191 | properties[PROP_ITEM] = |
192 | g_param_spec_object (name: "item" , |
193 | P_("Item" ), |
194 | P_("Displayed item" ), |
195 | G_TYPE_OBJECT, |
196 | flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
197 | |
198 | /** |
199 | * GtkListItem:position: (attributes org.gtk.Property.get=gtk_list_item_get_position) |
200 | * |
201 | * Position of the item. |
202 | */ |
203 | properties[PROP_POSITION] = |
204 | g_param_spec_uint (name: "position" , |
205 | P_("Position" ), |
206 | P_("Position of the item" ), |
207 | minimum: 0, G_MAXUINT, GTK_INVALID_LIST_POSITION, |
208 | flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
209 | |
210 | /** |
211 | * GtkListItem:selectable: (attributes org.gtk.Property.get=gtk_list_item_get_selectable org.gtk.Property.set=gtk_list_item_set_selectable) |
212 | * |
213 | * If the item can be selected by the user. |
214 | */ |
215 | properties[PROP_SELECTABLE] = |
216 | g_param_spec_boolean (name: "selectable" , |
217 | P_("Selectable" ), |
218 | P_("If the item can be selected by the user" ), |
219 | TRUE, |
220 | flags: G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
221 | |
222 | /** |
223 | * GtkListItem:selected: (attributes org.gtk.Property.get=gtk_list_item_get_selected) |
224 | * |
225 | * If the item is currently selected. |
226 | */ |
227 | properties[PROP_SELECTED] = |
228 | g_param_spec_boolean (name: "selected" , |
229 | P_("Selected" ), |
230 | P_("If the item is currently selected" ), |
231 | FALSE, |
232 | flags: G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); |
233 | |
234 | g_object_class_install_properties (oclass: gobject_class, n_pspecs: N_PROPS, pspecs: properties); |
235 | } |
236 | |
237 | static void |
238 | gtk_list_item_init (GtkListItem *self) |
239 | { |
240 | self->selectable = TRUE; |
241 | self->activatable = TRUE; |
242 | } |
243 | |
244 | GtkListItem * |
245 | gtk_list_item_new (void) |
246 | { |
247 | return g_object_new (GTK_TYPE_LIST_ITEM, NULL); |
248 | } |
249 | |
250 | void |
251 | gtk_list_item_do_notify (GtkListItem *list_item, |
252 | gboolean notify_item, |
253 | gboolean notify_position, |
254 | gboolean notify_selected) |
255 | { |
256 | GObject *object = G_OBJECT (list_item); |
257 | |
258 | if (notify_item) |
259 | g_object_notify_by_pspec (object, pspec: properties[PROP_ITEM]); |
260 | if (notify_position) |
261 | g_object_notify_by_pspec (object, pspec: properties[PROP_POSITION]); |
262 | if (notify_selected) |
263 | g_object_notify_by_pspec (object, pspec: properties[PROP_SELECTED]); |
264 | } |
265 | |
266 | /** |
267 | * gtk_list_item_get_item: (attributes org.gtk.Method.get_property=item) |
268 | * @self: a `GtkListItem` |
269 | * |
270 | * Gets the model item that associated with @self. |
271 | * |
272 | * If @self is unbound, this function returns %NULL. |
273 | * |
274 | * Returns: (nullable) (transfer none) (type GObject): The item displayed |
275 | **/ |
276 | gpointer |
277 | gtk_list_item_get_item (GtkListItem *self) |
278 | { |
279 | g_return_val_if_fail (GTK_IS_LIST_ITEM (self), NULL); |
280 | |
281 | if (self->owner == NULL) |
282 | return NULL; |
283 | |
284 | return gtk_list_item_widget_get_item (self: self->owner); |
285 | } |
286 | |
287 | /** |
288 | * gtk_list_item_get_child: (attributes org.gtk.Method.get_property=child) |
289 | * @self: a `GtkListItem` |
290 | * |
291 | * Gets the child previously set via gtk_list_item_set_child() or |
292 | * %NULL if none was set. |
293 | * |
294 | * Returns: (transfer none) (nullable): The child |
295 | */ |
296 | GtkWidget * |
297 | gtk_list_item_get_child (GtkListItem *self) |
298 | { |
299 | g_return_val_if_fail (GTK_IS_LIST_ITEM (self), NULL); |
300 | |
301 | return self->child; |
302 | } |
303 | |
304 | /** |
305 | * gtk_list_item_set_child: (attributes org.gtk.Method.set_property=child) |
306 | * @self: a `GtkListItem` |
307 | * @child: (nullable): The list item's child or %NULL to unset |
308 | * |
309 | * Sets the child to be used for this listitem. |
310 | * |
311 | * This function is typically called by applications when |
312 | * setting up a listitem so that the widget can be reused when |
313 | * binding it multiple times. |
314 | */ |
315 | void |
316 | gtk_list_item_set_child (GtkListItem *self, |
317 | GtkWidget *child) |
318 | { |
319 | g_return_if_fail (GTK_IS_LIST_ITEM (self)); |
320 | g_return_if_fail (child == NULL || GTK_IS_WIDGET (child)); |
321 | |
322 | if (self->child == child) |
323 | return; |
324 | |
325 | if (self->child && self->owner) |
326 | gtk_list_item_widget_remove_child (self: self->owner, child: self->child); |
327 | |
328 | g_clear_object (&self->child); |
329 | |
330 | if (child) |
331 | { |
332 | g_object_ref_sink (child); |
333 | self->child = child; |
334 | |
335 | if (self->owner) |
336 | gtk_list_item_widget_add_child (self: self->owner, child); |
337 | } |
338 | |
339 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_ITEM]); |
340 | } |
341 | |
342 | /** |
343 | * gtk_list_item_get_position: (attributes org.gtk.Method.get_property=position) |
344 | * @self: a `GtkListItem` |
345 | * |
346 | * Gets the position in the model that @self currently displays. |
347 | * |
348 | * If @self is unbound, %GTK_INVALID_LIST_POSITION is returned. |
349 | * |
350 | * Returns: The position of this item |
351 | */ |
352 | guint |
353 | gtk_list_item_get_position (GtkListItem *self) |
354 | { |
355 | g_return_val_if_fail (GTK_IS_LIST_ITEM (self), GTK_INVALID_LIST_POSITION); |
356 | |
357 | if (self->owner == NULL) |
358 | return GTK_INVALID_LIST_POSITION; |
359 | |
360 | return gtk_list_item_widget_get_position (self: self->owner); |
361 | } |
362 | |
363 | /** |
364 | * gtk_list_item_get_selected: (attributes org.gtk.Method.get_property=selected) |
365 | * @self: a `GtkListItem` |
366 | * |
367 | * Checks if the item is displayed as selected. |
368 | * |
369 | * The selected state is maintained by the liste widget and its model |
370 | * and cannot be set otherwise. |
371 | * |
372 | * Returns: %TRUE if the item is selected. |
373 | */ |
374 | gboolean |
375 | gtk_list_item_get_selected (GtkListItem *self) |
376 | { |
377 | g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE); |
378 | |
379 | if (self->owner == NULL) |
380 | return FALSE; |
381 | |
382 | return gtk_list_item_widget_get_selected (self: self->owner); |
383 | } |
384 | |
385 | /** |
386 | * gtk_list_item_get_selectable: (attributes org.gtk.Method.get_property=selectable) |
387 | * @self: a `GtkListItem` |
388 | * |
389 | * Checks if a list item has been set to be selectable via |
390 | * gtk_list_item_set_selectable(). |
391 | * |
392 | * Do not confuse this function with [method@Gtk.ListItem.get_selected]. |
393 | * |
394 | * Returns: %TRUE if the item is selectable |
395 | */ |
396 | gboolean |
397 | gtk_list_item_get_selectable (GtkListItem *self) |
398 | { |
399 | g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE); |
400 | |
401 | return self->selectable; |
402 | } |
403 | |
404 | /** |
405 | * gtk_list_item_set_selectable: (attributes org.gtk.Method.set_property=selectable) |
406 | * @self: a `GtkListItem` |
407 | * @selectable: if the item should be selectable |
408 | * |
409 | * Sets @self to be selectable. |
410 | * |
411 | * If an item is selectable, clicking on the item or using the keyboard |
412 | * will try to select or unselect the item. If this succeeds is up to |
413 | * the model to determine, as it is managing the selected state. |
414 | * |
415 | * Note that this means that making an item non-selectable has no |
416 | * influence on the selected state at all. A non-selectable item |
417 | * may still be selected. |
418 | * |
419 | * By default, list items are selectable. When rebinding them to |
420 | * a new item, they will also be reset to be selectable by GTK. |
421 | */ |
422 | void |
423 | gtk_list_item_set_selectable (GtkListItem *self, |
424 | gboolean selectable) |
425 | { |
426 | g_return_if_fail (GTK_IS_LIST_ITEM (self)); |
427 | |
428 | if (self->selectable == selectable) |
429 | return; |
430 | |
431 | self->selectable = selectable; |
432 | |
433 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_SELECTABLE]); |
434 | } |
435 | |
436 | /** |
437 | * gtk_list_item_get_activatable: (attributes org.gtk.Method.get_property=activatable) |
438 | * @self: a `GtkListItem` |
439 | * |
440 | * Checks if a list item has been set to be activatable via |
441 | * gtk_list_item_set_activatable(). |
442 | * |
443 | * Returns: %TRUE if the item is activatable |
444 | */ |
445 | gboolean |
446 | gtk_list_item_get_activatable (GtkListItem *self) |
447 | { |
448 | g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE); |
449 | |
450 | return self->activatable; |
451 | } |
452 | |
453 | /** |
454 | * gtk_list_item_set_activatable: (attributes org.gtk.Method.set_property=activatable) |
455 | * @self: a `GtkListItem` |
456 | * @activatable: if the item should be activatable |
457 | * |
458 | * Sets @self to be activatable. |
459 | * |
460 | * If an item is activatable, double-clicking on the item, using |
461 | * the Return key or calling gtk_widget_activate() will activate |
462 | * the item. Activating instructs the containing view to handle |
463 | * activation. `GtkListView` for example will be emitting the |
464 | * [signal@Gtk.ListView::activate] signal. |
465 | * |
466 | * By default, list items are activatable. |
467 | */ |
468 | void |
469 | gtk_list_item_set_activatable (GtkListItem *self, |
470 | gboolean activatable) |
471 | { |
472 | g_return_if_fail (GTK_IS_LIST_ITEM (self)); |
473 | |
474 | if (self->activatable == activatable) |
475 | return; |
476 | |
477 | self->activatable = activatable; |
478 | |
479 | if (self->owner) |
480 | gtk_list_item_widget_set_activatable (self: self->owner, activatable); |
481 | |
482 | g_object_notify_by_pspec (G_OBJECT (self), pspec: properties[PROP_ACTIVATABLE]); |
483 | } |
484 | |