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