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 "gtklistitemwidgetprivate.h"
23
24 #include "gtkbinlayout.h"
25 #include "gtkeventcontrollerfocus.h"
26 #include "gtkeventcontrollermotion.h"
27 #include "gtkgestureclick.h"
28 #include "gtkintl.h"
29 #include "gtklistitemfactoryprivate.h"
30 #include "gtklistitemprivate.h"
31 #include "gtklistbaseprivate.h"
32 #include "gtkmain.h"
33 #include "gtkselectionmodel.h"
34 #include "gtkwidget.h"
35 #include "gtkwidgetprivate.h"
36
37 typedef struct _GtkListItemWidgetPrivate GtkListItemWidgetPrivate;
38 struct _GtkListItemWidgetPrivate
39 {
40 GtkListItemFactory *factory;
41 GtkListItem *list_item;
42
43 GObject *item;
44 guint position;
45 gboolean selected;
46 gboolean single_click_activate;
47 };
48
49 enum {
50 PROP_0,
51 PROP_FACTORY,
52 PROP_SINGLE_CLICK_ACTIVATE,
53
54 N_PROPS
55 };
56
57 enum
58 {
59 ACTIVATE_SIGNAL,
60 LAST_SIGNAL
61 };
62
63 G_DEFINE_TYPE_WITH_PRIVATE (GtkListItemWidget, gtk_list_item_widget, GTK_TYPE_WIDGET)
64
65 static GParamSpec *properties[N_PROPS] = { NULL, };
66 static guint signals[LAST_SIGNAL] = { 0 };
67
68 static void
gtk_list_item_widget_activate_signal(GtkListItemWidget * self)69 gtk_list_item_widget_activate_signal (GtkListItemWidget *self)
70 {
71 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
72
73 if (priv->list_item && !priv->list_item->activatable)
74 return;
75
76 gtk_widget_activate_action (GTK_WIDGET (self),
77 "list.activate-item",
78 "u",
79 priv->position);
80 }
81
82 static gboolean
gtk_list_item_widget_focus(GtkWidget * widget,GtkDirectionType direction)83 gtk_list_item_widget_focus (GtkWidget *widget,
84 GtkDirectionType direction)
85 {
86 GtkWidget *child, *focus_child;
87
88 /* The idea of this function is the following:
89 * 1. If any child can take focus, do not ever attempt
90 * to take focus.
91 * 2. Otherwise, if this item is selectable or activatable,
92 * allow focusing this widget.
93 *
94 * This makes sure every item in a list is focusable for
95 * activation and selection handling, but no useless widgets
96 * get focused and moving focus is as fast as possible.
97 */
98
99 focus_child = gtk_widget_get_focus_child (widget);
100 if (focus_child && gtk_widget_child_focus (focus_child, direction))
101 return TRUE;
102
103 for (child = focus_child ? gtk_widget_get_next_sibling (focus_child)
104 : gtk_widget_get_first_child (widget);
105 child;
106 child = gtk_widget_get_next_sibling (child))
107 {
108 if (gtk_widget_child_focus (child, direction))
109 return TRUE;
110 }
111
112 if (focus_child)
113 return FALSE;
114
115 if (gtk_widget_is_focus (widget))
116 return FALSE;
117
118 return gtk_widget_grab_focus (widget);
119 }
120
121 static gboolean
gtk_list_item_widget_grab_focus(GtkWidget * widget)122 gtk_list_item_widget_grab_focus (GtkWidget *widget)
123 {
124 GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
125 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
126 GtkWidget *child;
127
128 for (child = gtk_widget_get_first_child (widget);
129 child;
130 child = gtk_widget_get_next_sibling (child))
131 {
132 if (gtk_widget_grab_focus (child))
133 return TRUE;
134 }
135
136 if (priv->list_item == NULL ||
137 !priv->list_item->selectable)
138 return FALSE;
139
140 return GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->grab_focus (widget);
141 }
142
143 static void
gtk_list_item_widget_root(GtkWidget * widget)144 gtk_list_item_widget_root (GtkWidget *widget)
145 {
146 GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
147 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
148
149 GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->root (widget);
150
151 if (priv->factory)
152 gtk_list_item_factory_setup (priv->factory, self);
153 }
154
155 static void
gtk_list_item_widget_unroot(GtkWidget * widget)156 gtk_list_item_widget_unroot (GtkWidget *widget)
157 {
158 GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
159 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
160
161 GTK_WIDGET_CLASS (gtk_list_item_widget_parent_class)->unroot (widget);
162
163 if (priv->list_item)
164 gtk_list_item_factory_teardown (priv->factory, self);
165 }
166
167 static void
gtk_list_item_widget_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)168 gtk_list_item_widget_set_property (GObject *object,
169 guint property_id,
170 const GValue *value,
171 GParamSpec *pspec)
172 {
173 GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (object);
174
175 switch (property_id)
176 {
177 case PROP_FACTORY:
178 gtk_list_item_widget_set_factory (self, g_value_get_object (value));
179 break;
180
181 case PROP_SINGLE_CLICK_ACTIVATE:
182 gtk_list_item_widget_set_single_click_activate (self, g_value_get_boolean (value));
183 break;
184
185 default:
186 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
187 break;
188 }
189 }
190
191 static void
gtk_list_item_widget_dispose(GObject * object)192 gtk_list_item_widget_dispose (GObject *object)
193 {
194 GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (object);
195 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
196
197 g_assert (priv->list_item == NULL);
198
199 g_clear_object (&priv->item);
200 g_clear_object (&priv->factory);
201
202 G_OBJECT_CLASS (gtk_list_item_widget_parent_class)->dispose (object);
203 }
204
205 static void
gtk_list_item_widget_select_action(GtkWidget * widget,const char * action_name,GVariant * parameter)206 gtk_list_item_widget_select_action (GtkWidget *widget,
207 const char *action_name,
208 GVariant *parameter)
209 {
210 GtkListItemWidget *self = GTK_LIST_ITEM_WIDGET (widget);
211 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
212 gboolean modify, extend;
213
214 if (priv->list_item && !priv->list_item->selectable)
215 return;
216
217 g_variant_get (parameter, "(bb)", &modify, &extend);
218
219 gtk_widget_activate_action (GTK_WIDGET (self),
220 "list.select-item",
221 "(ubb)",
222 priv->position, modify, extend);
223 }
224
225 static void
gtk_list_item_widget_class_init(GtkListItemWidgetClass * klass)226 gtk_list_item_widget_class_init (GtkListItemWidgetClass *klass)
227 {
228 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
229 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
230
231 klass->activate_signal = gtk_list_item_widget_activate_signal;
232
233 widget_class->focus = gtk_list_item_widget_focus;
234 widget_class->grab_focus = gtk_list_item_widget_grab_focus;
235 widget_class->root = gtk_list_item_widget_root;
236 widget_class->unroot = gtk_list_item_widget_unroot;
237
238 gobject_class->set_property = gtk_list_item_widget_set_property;
239 gobject_class->dispose = gtk_list_item_widget_dispose;
240
241 properties[PROP_FACTORY] =
242 g_param_spec_object ("factory",
243 "Factory",
244 "Factory managing this list item",
245 GTK_TYPE_LIST_ITEM_FACTORY,
246 G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
247
248 properties[PROP_SINGLE_CLICK_ACTIVATE] =
249 g_param_spec_boolean ("single-click-activate",
250 "Single click activate",
251 "Activate on single click",
252 FALSE,
253 G_PARAM_WRITABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
254
255 g_object_class_install_properties (gobject_class, N_PROPS, properties);
256
257 signals[ACTIVATE_SIGNAL] =
258 g_signal_new (I_("activate-keybinding"),
259 G_OBJECT_CLASS_TYPE (gobject_class),
260 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
261 G_STRUCT_OFFSET (GtkListItemWidgetClass, activate_signal),
262 NULL, NULL,
263 NULL,
264 G_TYPE_NONE, 0);
265
266 gtk_widget_class_set_activate_signal (widget_class, signals[ACTIVATE_SIGNAL]);
267
268 /**
269 * GtkListItem|listitem.select:
270 * @modify: %TRUE to toggle the existing selection, %FALSE to select
271 * @extend: %TRUE to extend the selection
272 *
273 * Changes selection if the item is selectable.
274 * If the item is not selectable, nothing happens.
275 *
276 * This function will emit the list.select-item action and the resulting
277 * behavior, in particular the interpretation of @modify and @extend
278 * depends on the view containing this listitem. See for example
279 * GtkListView|list.select-item or GtkGridView|list.select-item.
280 */
281 gtk_widget_class_install_action (widget_class,
282 "listitem.select",
283 "(bb)",
284 gtk_list_item_widget_select_action);
285
286 gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_Return, 0,
287 "activate-keybinding", 0);
288 gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_ISO_Enter, 0,
289 "activate-keybinding", 0);
290 gtk_widget_class_add_binding_signal (widget_class, GDK_KEY_KP_Enter, 0,
291 "activate-keybinding", 0);
292
293 /* note that some of these may get overwritten by child widgets,
294 * such as GtkTreeExpander */
295 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, 0,
296 "listitem.select", "(bb)", TRUE, FALSE);
297 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK,
298 "listitem.select", "(bb)", TRUE, FALSE);
299 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_SHIFT_MASK,
300 "listitem.select", "(bb)", TRUE, FALSE);
301 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
302 "listitem.select", "(bb)", TRUE, FALSE);
303 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, 0,
304 "listitem.select", "(bb)", TRUE, FALSE);
305 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK,
306 "listitem.select", "(bb)", TRUE, FALSE);
307 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_SHIFT_MASK,
308 "listitem.select", "(bb)", TRUE, FALSE);
309 gtk_widget_class_add_binding_action (widget_class, GDK_KEY_KP_Space, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
310 "listitem.select", "(bb)", TRUE, FALSE);
311
312 /* This gets overwritten by gtk_list_item_widget_new() but better safe than sorry */
313 gtk_widget_class_set_css_name (widget_class, I_("row"));
314 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
315 }
316
317 static void
gtk_list_item_widget_click_gesture_pressed(GtkGestureClick * gesture,int n_press,double x,double y,GtkListItemWidget * self)318 gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture,
319 int n_press,
320 double x,
321 double y,
322 GtkListItemWidget *self)
323 {
324 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
325 GtkWidget *widget = GTK_WIDGET (self);
326
327 if (priv->list_item && !priv->list_item->selectable && !priv->list_item->activatable)
328 {
329 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
330 return;
331 }
332
333 if (!priv->list_item || priv->list_item->activatable)
334 {
335 if (n_press == 2 && !priv->single_click_activate)
336 {
337 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
338 gtk_widget_activate_action (GTK_WIDGET (self),
339 "list.activate-item",
340 "u",
341 priv->position);
342 }
343 }
344
345 gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE);
346
347 if (gtk_widget_get_focus_on_click (widget))
348 gtk_widget_grab_focus (widget);
349 }
350
351 static void
gtk_list_item_widget_click_gesture_released(GtkGestureClick * gesture,int n_press,double x,double y,GtkListItemWidget * self)352 gtk_list_item_widget_click_gesture_released (GtkGestureClick *gesture,
353 int n_press,
354 double x,
355 double y,
356 GtkListItemWidget *self)
357 {
358 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
359
360 if (!priv->list_item || priv->list_item->activatable)
361 {
362 if (n_press == 1 && priv->single_click_activate)
363 {
364 gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
365 gtk_widget_activate_action (GTK_WIDGET (self),
366 "list.activate-item",
367 "u",
368 priv->position);
369 return;
370 }
371 }
372
373 if (!priv->list_item || priv->list_item->selectable)
374 {
375 GdkModifierType state;
376 GdkEvent *event;
377 gboolean extend, modify;
378
379 event = gtk_gesture_get_last_event (GTK_GESTURE (gesture),
380 gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)));
381 state = gdk_event_get_modifier_state (event);
382 extend = (state & GDK_SHIFT_MASK) != 0;
383 modify = (state & GDK_CONTROL_MASK) != 0;
384
385 gtk_widget_activate_action (GTK_WIDGET (self),
386 "list.select-item",
387 "(ubb)",
388 priv->position, modify, extend);
389 }
390
391 gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
392 }
393
394 static void
gtk_list_item_widget_enter_cb(GtkEventControllerFocus * controller,GtkListItemWidget * self)395 gtk_list_item_widget_enter_cb (GtkEventControllerFocus *controller,
396 GtkListItemWidget *self)
397 {
398 GtkWidget *widget = GTK_WIDGET (self);
399 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
400
401 gtk_widget_activate_action (widget,
402 "list.scroll-to-item",
403 "u",
404 priv->position);
405 }
406
407 static void
gtk_list_item_widget_hover_cb(GtkEventControllerMotion * controller,double x,double y,GtkListItemWidget * self)408 gtk_list_item_widget_hover_cb (GtkEventControllerMotion *controller,
409 double x,
410 double y,
411 GtkListItemWidget *self)
412 {
413 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
414
415 if (!priv->single_click_activate)
416 return;
417
418 if (!priv->list_item || priv->list_item->selectable)
419 {
420 gtk_widget_activate_action (GTK_WIDGET (self),
421 "list.select-item",
422 "(ubb)",
423 priv->position, FALSE, FALSE);
424 }
425 }
426
427 static void
gtk_list_item_widget_click_gesture_canceled(GtkGestureClick * gesture,GdkEventSequence * sequence,GtkListItemWidget * self)428 gtk_list_item_widget_click_gesture_canceled (GtkGestureClick *gesture,
429 GdkEventSequence *sequence,
430 GtkListItemWidget *self)
431 {
432 gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
433 }
434
435 static void
gtk_list_item_widget_init(GtkListItemWidget * self)436 gtk_list_item_widget_init (GtkListItemWidget *self)
437 {
438 GtkEventController *controller;
439 GtkGesture *gesture;
440
441 gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
442
443 gesture = gtk_gesture_click_new ();
444 gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
445 GTK_PHASE_BUBBLE);
446 gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture),
447 FALSE);
448 gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
449 GDK_BUTTON_PRIMARY);
450 g_signal_connect (gesture, "pressed",
451 G_CALLBACK (gtk_list_item_widget_click_gesture_pressed), self);
452 g_signal_connect (gesture, "released",
453 G_CALLBACK (gtk_list_item_widget_click_gesture_released), self);
454 g_signal_connect (gesture, "cancel",
455 G_CALLBACK (gtk_list_item_widget_click_gesture_canceled), self);
456 gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
457
458 controller = gtk_event_controller_focus_new ();
459 g_signal_connect (controller, "enter", G_CALLBACK (gtk_list_item_widget_enter_cb), self);
460 gtk_widget_add_controller (GTK_WIDGET (self), controller);
461
462 controller = gtk_event_controller_motion_new ();
463 g_signal_connect (controller, "enter", G_CALLBACK (gtk_list_item_widget_hover_cb), self);
464 gtk_widget_add_controller (GTK_WIDGET (self), controller);
465 }
466
467 GtkWidget *
gtk_list_item_widget_new(GtkListItemFactory * factory,const char * css_name,GtkAccessibleRole role)468 gtk_list_item_widget_new (GtkListItemFactory *factory,
469 const char *css_name,
470 GtkAccessibleRole role)
471 {
472 g_return_val_if_fail (css_name != NULL, NULL);
473
474 return g_object_new (GTK_TYPE_LIST_ITEM_WIDGET,
475 "css-name", css_name,
476 "accessible-role", role,
477 "factory", factory,
478 NULL);
479 }
480
481 void
gtk_list_item_widget_update(GtkListItemWidget * self,guint position,gpointer item,gboolean selected)482 gtk_list_item_widget_update (GtkListItemWidget *self,
483 guint position,
484 gpointer item,
485 gboolean selected)
486 {
487 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
488
489 if (priv->list_item)
490 gtk_list_item_factory_update (priv->factory, self, position, item, selected);
491 else
492 gtk_list_item_widget_default_update (self, NULL, position, item, selected);
493
494 if (selected)
495 gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED, FALSE);
496 else
497 gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
498
499 gtk_accessible_update_state (GTK_ACCESSIBLE (self),
500 GTK_ACCESSIBLE_STATE_SELECTED, selected,
501 -1);
502 }
503
504 void
gtk_list_item_widget_default_setup(GtkListItemWidget * self,GtkListItem * list_item)505 gtk_list_item_widget_default_setup (GtkListItemWidget *self,
506 GtkListItem *list_item)
507 {
508 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
509
510 priv->list_item = list_item;
511 list_item->owner = self;
512
513 if (list_item->child)
514 gtk_list_item_widget_add_child (self, list_item->child);
515
516 gtk_list_item_widget_set_activatable (self, list_item->activatable);
517
518 if (priv->item)
519 g_object_notify (G_OBJECT (list_item), "item");
520 if (priv->position != GTK_INVALID_LIST_POSITION)
521 g_object_notify (G_OBJECT (list_item), "position");
522 if (priv->selected)
523 g_object_notify (G_OBJECT (list_item), "selected");
524 }
525
526 void
gtk_list_item_widget_default_teardown(GtkListItemWidget * self,GtkListItem * list_item)527 gtk_list_item_widget_default_teardown (GtkListItemWidget *self,
528 GtkListItem *list_item)
529 {
530 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
531
532 g_assert (priv->list_item == list_item);
533
534 priv->list_item = NULL;
535 list_item->owner = NULL;
536
537 if (list_item->child)
538 gtk_list_item_widget_remove_child (self, list_item->child);
539
540 gtk_list_item_widget_set_activatable (self, FALSE);
541
542 if (priv->item)
543 g_object_notify (G_OBJECT (list_item), "item");
544 if (priv->position != GTK_INVALID_LIST_POSITION)
545 g_object_notify (G_OBJECT (list_item), "position");
546 if (priv->selected)
547 g_object_notify (G_OBJECT (list_item), "selected");
548 }
549
550 void
gtk_list_item_widget_default_update(GtkListItemWidget * self,GtkListItem * list_item,guint position,gpointer item,gboolean selected)551 gtk_list_item_widget_default_update (GtkListItemWidget *self,
552 GtkListItem *list_item,
553 guint position,
554 gpointer item,
555 gboolean selected)
556 {
557 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
558
559 /* FIXME: It's kinda evil to notify external objects from here... */
560
561 if (g_set_object (&priv->item, item))
562 {
563 if (list_item)
564 g_object_notify (G_OBJECT (list_item), "item");
565 }
566
567 if (priv->position != position)
568 {
569 priv->position = position;
570 if (list_item)
571 g_object_notify (G_OBJECT (list_item), "position");
572 }
573
574 if (priv->selected != selected)
575 {
576 priv->selected = selected;
577 if (list_item)
578 g_object_notify (G_OBJECT (list_item), "selected");
579 }
580 }
581
582 void
gtk_list_item_widget_set_factory(GtkListItemWidget * self,GtkListItemFactory * factory)583 gtk_list_item_widget_set_factory (GtkListItemWidget *self,
584 GtkListItemFactory *factory)
585 {
586 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
587
588 if (priv->factory == factory)
589 return;
590
591 if (priv->factory)
592 {
593 if (priv->list_item)
594 gtk_list_item_factory_teardown (factory, self);
595 g_clear_object (&priv->factory);
596 }
597
598 if (factory)
599 {
600 priv->factory = g_object_ref (factory);
601
602 if (gtk_widget_get_root (GTK_WIDGET (self)))
603 gtk_list_item_factory_setup (factory, self);
604 }
605
606 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FACTORY]);
607 }
608
609 void
gtk_list_item_widget_set_single_click_activate(GtkListItemWidget * self,gboolean single_click_activate)610 gtk_list_item_widget_set_single_click_activate (GtkListItemWidget *self,
611 gboolean single_click_activate)
612 {
613 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
614
615 if (priv->single_click_activate == single_click_activate)
616 return;
617
618 priv->single_click_activate = single_click_activate;
619
620 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SINGLE_CLICK_ACTIVATE]);
621 }
622
623 void
gtk_list_item_widget_set_activatable(GtkListItemWidget * self,gboolean activatable)624 gtk_list_item_widget_set_activatable (GtkListItemWidget *self,
625 gboolean activatable)
626 {
627 if (activatable)
628 gtk_widget_add_css_class (GTK_WIDGET (self), "activatable");
629 else
630 gtk_widget_remove_css_class (GTK_WIDGET (self), "activatable");
631 }
632
633 void
gtk_list_item_widget_add_child(GtkListItemWidget * self,GtkWidget * child)634 gtk_list_item_widget_add_child (GtkListItemWidget *self,
635 GtkWidget *child)
636 {
637 gtk_widget_set_parent (child, GTK_WIDGET (self));
638 }
639
640 void
gtk_list_item_widget_reorder_child(GtkListItemWidget * self,GtkWidget * child,guint position)641 gtk_list_item_widget_reorder_child (GtkListItemWidget *self,
642 GtkWidget *child,
643 guint position)
644 {
645 GtkWidget *widget = GTK_WIDGET (self);
646 GtkWidget *sibling = NULL;
647
648 if (position > 0)
649 {
650 GtkWidget *c;
651 guint i;
652
653 for (c = gtk_widget_get_first_child (widget), i = 0;
654 c;
655 c = gtk_widget_get_next_sibling (c), i++)
656 {
657 if (i + 1 == position)
658 {
659 sibling = c;
660 break;
661 }
662 }
663 }
664
665 if (child != sibling)
666 gtk_widget_insert_after (child, widget, sibling);
667 }
668
669 void
gtk_list_item_widget_remove_child(GtkListItemWidget * self,GtkWidget * child)670 gtk_list_item_widget_remove_child (GtkListItemWidget *self,
671 GtkWidget *child)
672 {
673 gtk_widget_unparent (child);
674 }
675
676 GtkListItem *
gtk_list_item_widget_get_list_item(GtkListItemWidget * self)677 gtk_list_item_widget_get_list_item (GtkListItemWidget *self)
678 {
679 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
680
681 return priv->list_item;
682 }
683
684 guint
gtk_list_item_widget_get_position(GtkListItemWidget * self)685 gtk_list_item_widget_get_position (GtkListItemWidget *self)
686 {
687 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
688
689 return priv->position;
690 }
691
692 gpointer
gtk_list_item_widget_get_item(GtkListItemWidget * self)693 gtk_list_item_widget_get_item (GtkListItemWidget *self)
694 {
695 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
696
697 return priv->item;
698 }
699
700 gboolean
gtk_list_item_widget_get_selected(GtkListItemWidget * self)701 gtk_list_item_widget_get_selected (GtkListItemWidget *self)
702 {
703 GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
704
705 return priv->selected;
706 }
707
708