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
45struct _GtkListItemClass
46{
47 GObjectClass parent_class;
48};
49
50enum
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
63G_DEFINE_TYPE (GtkListItem, gtk_list_item, G_TYPE_OBJECT)
64
65static GParamSpec *properties[N_PROPS] = { NULL, };
66
67static void
68gtk_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
78static void
79gtk_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
125static void
126gtk_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
153static void
154gtk_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
237static void
238gtk_list_item_init (GtkListItem *self)
239{
240 self->selectable = TRUE;
241 self->activatable = TRUE;
242}
243
244GtkListItem *
245gtk_list_item_new (void)
246{
247 return g_object_new (GTK_TYPE_LIST_ITEM, NULL);
248}
249
250void
251gtk_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 **/
276gpointer
277gtk_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 */
296GtkWidget *
297gtk_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 */
315void
316gtk_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 */
352guint
353gtk_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 */
374gboolean
375gtk_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 */
396gboolean
397gtk_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 */
422void
423gtk_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 */
445gboolean
446gtk_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 */
468void
469gtk_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

source code of gtk/gtk/gtklistitem.c