1 /*
2  * Copyright (c) 2016, 2017 Red Hat, Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
12  * License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17  *
18  * Author: Debarshi Ray <debarshir@gnome.org>
19  *
20  */
21 
22 #include "gd-main-box-generic.h"
23 #include "gd-main-box-item.h"
24 
25 enum
26 {
27   ITEM_ACTIVATED,
28   SELECTION_CHANGED,
29   SELECTION_MODE_REQUEST,
30   NUM_SIGNALS
31 };
32 
33 static guint signals[NUM_SIGNALS] = { 0, };
34 
G_DEFINE_INTERFACE(GdMainBoxGeneric,gd_main_box_generic,GTK_TYPE_WIDGET)35 G_DEFINE_INTERFACE (GdMainBoxGeneric, gd_main_box_generic, GTK_TYPE_WIDGET)
36 
37 static void
38 gd_main_box_generic_mark_range_as_selected (GdMainBoxGeneric *self, gint first_element, gint last_element)
39 {
40   gint i;
41 
42   if (first_element > last_element)
43     {
44       gint tmp;
45 
46       tmp = first_element;
47       first_element = last_element;
48       last_element = tmp;
49     }
50 
51   for (i = first_element; i <= last_element; i++)
52     {
53       GdMainBoxChild *child;
54 
55       child = gd_main_box_generic_get_child_at_index (self, i);
56       gd_main_box_generic_select_child (self, child);
57     }
58 }
59 
60 static void
gd_main_box_generic_select_range(GdMainBoxGeneric * self,GdMainBoxChild * child)61 gd_main_box_generic_select_range (GdMainBoxGeneric *self, GdMainBoxChild *child)
62 {
63   GListModel *model;
64   const gchar *last_selected_id;
65   gint index;
66   gint other_index = -1;
67   guint n_items;
68 
69   model = gd_main_box_generic_get_model (self);
70   n_items = g_list_model_get_n_items (model);
71 
72   last_selected_id = gd_main_box_generic_get_last_selected_id (self);
73   index = gd_main_box_child_get_index (child);
74 
75   if (last_selected_id != NULL)
76     {
77       guint i;
78 
79       for (i = 0; i < n_items; i++)
80         {
81           GdMainBoxItem *item;
82           const gchar *id;
83 
84           item = GD_MAIN_BOX_ITEM (g_list_model_get_object (model, i));
85           id = gd_main_box_item_get_id (item);
86 
87 	  if (g_strcmp0 (id, last_selected_id) == 0)
88             {
89               other_index = (gint) i;
90               g_object_unref (item);
91               break;
92             }
93 
94           g_object_unref (item);
95 	}
96     }
97 
98   if (other_index == -1)
99     {
100       gint i;
101 
102       for (i = index - 1; i >= 0; i--)
103         {
104           GdMainBoxChild *other;
105 
106           other = gd_main_box_generic_get_child_at_index (self, i);
107           if (gd_main_box_child_get_selected (other))
108             {
109               other_index = i;
110               break;
111 	    }
112 	}
113     }
114 
115   if (other_index == -1)
116     {
117       gint i;
118 
119       for (i = index + 1; i < (gint) n_items; i++)
120         {
121           GdMainBoxChild *other;
122 
123           other = gd_main_box_generic_get_child_at_index (self, i);
124           if (gd_main_box_child_get_selected (other))
125             {
126               other_index = i;
127               break;
128             }
129         }
130     }
131 
132   if (other_index == -1)
133     gd_main_box_generic_select_child (self, child);
134   else
135     gd_main_box_generic_mark_range_as_selected (self, index, other_index);
136 }
137 
138 static void
gd_main_box_generic_default_init(GdMainBoxGenericInterface * iface)139 gd_main_box_generic_default_init (GdMainBoxGenericInterface *iface)
140 {
141   GParamSpec *pspec;
142 
143   /**
144    * GdMainBoxGeneric:last-selected-id:
145    *
146    * A unique ID to identify the #GdMainBoxItem object that was most
147    * recently selected.
148    */
149   pspec = g_param_spec_string ("last-selected-id",
150                                "ID",
151                                "A unique ID to identify the most recently selected item",
152                                NULL,
153                                G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
154   g_object_interface_install_property (iface, pspec);
155 
156   /**
157    * GdMainBoxGeneric:model:
158    *
159    * A #GListModel that is rendered by the #GdMainBoxGeneric widget.
160    */
161   pspec = g_param_spec_object ("model",
162                                "Model",
163                                "A model that is rendered by the widget",
164                                G_TYPE_LIST_MODEL,
165                                G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
166   g_object_interface_install_property (iface, pspec);
167 
168   /**
169    * GdMainBoxGeneric:gd-selection-mode:
170    *
171    * Whether the #GdMainBoxGeneric widget is in selection mode.
172    */
173   pspec = g_param_spec_boolean ("gd-selection-mode",
174                                 "Selection Mode",
175                                 "Whether the widget is in selection mode",
176                                 FALSE,
177                                 G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
178   g_object_interface_install_property (iface, pspec);
179 
180   /**
181    * GdMainBoxGeneric:show-primary-text:
182    *
183    * Whether the #GdMainBoxGeneric widget is going to show the
184    * primary-text of each #GdMainBoxItem.
185    */
186   pspec = g_param_spec_boolean ("show-primary-text",
187                                 "Show Primary Text",
188                                 "Whether each GdMainBoxItem's primary-text is going to be shown",
189                                 FALSE,
190                                 G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
191   g_object_interface_install_property (iface, pspec);
192 
193   /**
194    * GdMainBoxGeneric:show-secondary-text:
195    *
196    * Whether the #GdMainBoxGeneric widget is going to show the
197    * secondary-text of each #GdMainBoxItem.
198    */
199   pspec = g_param_spec_boolean ("show-secondary-text",
200                                 "Show Secondary Text",
201                                 "Whether each GdMainBoxItem's secondary-text is going to be shown",
202                                 FALSE,
203                                 G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
204   g_object_interface_install_property (iface, pspec);
205 
206   signals[ITEM_ACTIVATED] = g_signal_new ("item-activated",
207                                           GD_TYPE_MAIN_BOX_GENERIC,
208                                           G_SIGNAL_RUN_LAST,
209                                           0,
210                                           NULL,
211                                           NULL,
212                                           g_cclosure_marshal_VOID__OBJECT,
213                                           G_TYPE_NONE,
214                                           1,
215                                           GD_TYPE_MAIN_BOX_CHILD);
216 
217   signals[SELECTION_CHANGED] = g_signal_new ("selection-changed",
218                                              GD_TYPE_MAIN_BOX_GENERIC,
219                                              G_SIGNAL_RUN_LAST,
220                                              0,
221                                              NULL,
222                                              NULL,
223                                              g_cclosure_marshal_VOID__VOID,
224                                              G_TYPE_NONE,
225                                              0);
226 
227   signals[SELECTION_MODE_REQUEST] = g_signal_new ("selection-mode-request",
228                                                   GD_TYPE_MAIN_BOX_GENERIC,
229                                                   G_SIGNAL_RUN_LAST,
230                                                   0,
231                                                   NULL,
232                                                   NULL,
233                                                   g_cclosure_marshal_VOID__VOID,
234                                                   G_TYPE_NONE,
235                                                   0);
236 }
237 
238 /**
239  * gd_main_box_generic_get_child_at_index:
240  * @self:
241  * @index:
242  *
243  * Returns: (transfer none): The child at @index.
244  */
245 GdMainBoxChild *
gd_main_box_generic_get_child_at_index(GdMainBoxGeneric * self,gint index)246 gd_main_box_generic_get_child_at_index (GdMainBoxGeneric *self, gint index)
247 {
248   GdMainBoxGenericInterface *iface;
249 
250   g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), NULL);
251 
252   iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
253 
254   return (* iface->get_child_at_index) (self, index);
255 }
256 
257 /**
258  * gd_main_box_generic_get_last_selected_id:
259  * @self:
260  *
261  * Returns: (transfer none): The ID of the most recently selected #GdMainBoxItem.
262  */
263 const gchar *
gd_main_box_generic_get_last_selected_id(GdMainBoxGeneric * self)264 gd_main_box_generic_get_last_selected_id (GdMainBoxGeneric *self)
265 {
266   GdMainBoxGenericInterface *iface;
267 
268   g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), NULL);
269 
270   iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
271 
272   return (* iface->get_last_selected_id) (self);
273 }
274 
275 /**
276  * gd_main_box_generic_get_model:
277  * @self:
278  *
279  * Returns: (transfer none): The associated model
280  */
281 GListModel *
gd_main_box_generic_get_model(GdMainBoxGeneric * self)282 gd_main_box_generic_get_model (GdMainBoxGeneric *self)
283 {
284   GdMainBoxGenericInterface *iface;
285 
286   g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), NULL);
287 
288   iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
289 
290   return (* iface->get_model) (self);
291 }
292 
293 /**
294  * gd_main_box_generic_get_selected_children:
295  * @self:
296  *
297  * Returns: (element-type GdMainBoxChild) (transfer container): The
298  * selected children
299  */
300 GList *
gd_main_box_generic_get_selected_children(GdMainBoxGeneric * self)301 gd_main_box_generic_get_selected_children (GdMainBoxGeneric *self)
302 {
303   GdMainBoxGenericInterface *iface;
304 
305   g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), NULL);
306 
307   iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
308 
309   return (* iface->get_selected_children) (self);
310 }
311 
312 /**
313  * gd_main_box_generic_get_selection_mode:
314  * @self:
315  *
316  * Returns: (transfer none): Whether @self is in selection mode
317  */
318 gboolean
gd_main_box_generic_get_selection_mode(GdMainBoxGeneric * self)319 gd_main_box_generic_get_selection_mode (GdMainBoxGeneric *self)
320 {
321   gboolean selection_mode;
322 
323   g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), FALSE);
324 
325   g_object_get (self, "gd-selection-mode", &selection_mode, NULL);
326   return selection_mode;
327 }
328 
329 /**
330  * gd_main_box_generic_get_show_primary_text:
331  * @self:
332  *
333  * Returns: (transfer none): Whether @self is going to show the
334  * primary-text of each #GdMainBoxItem
335  */
336 gboolean
gd_main_box_generic_get_show_primary_text(GdMainBoxGeneric * self)337 gd_main_box_generic_get_show_primary_text (GdMainBoxGeneric *self)
338 {
339   gboolean show_primary_text;
340 
341   g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), FALSE);
342 
343   g_object_get (self, "show-primary-text", &show_primary_text, NULL);
344   return show_primary_text;
345 }
346 
347 /**
348  * gd_main_box_generic_get_show_secondary_text:
349  * @self:
350  *
351  * Returns: (transfer none): Whether @self is going to show the
352  * secondary-text of each #GdMainBoxItem
353  */
354 gboolean
gd_main_box_generic_get_show_secondary_text(GdMainBoxGeneric * self)355 gd_main_box_generic_get_show_secondary_text (GdMainBoxGeneric *self)
356 {
357   gboolean show_secondary_text;
358 
359   g_return_val_if_fail (GD_IS_MAIN_BOX_GENERIC (self), FALSE);
360 
361   g_object_get (self, "show-secondary-text", &show_secondary_text, NULL);
362   return show_secondary_text;
363 }
364 
365 void
gd_main_box_generic_select_all(GdMainBoxGeneric * self)366 gd_main_box_generic_select_all (GdMainBoxGeneric *self)
367 {
368   GdMainBoxGenericInterface *iface;
369 
370   g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
371 
372   iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
373 
374   (* iface->select_all) (self);
375 }
376 
377 void
gd_main_box_generic_select_child(GdMainBoxGeneric * self,GdMainBoxChild * child)378 gd_main_box_generic_select_child (GdMainBoxGeneric *self, GdMainBoxChild *child)
379 {
380   GdMainBoxGenericInterface *iface;
381 
382   g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
383   g_return_if_fail (GD_IS_MAIN_BOX_CHILD (child));
384 
385   iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
386 
387   (* iface->select_child) (self, child);
388 }
389 
390 /**
391  * gd_main_box_generic_set_model:
392  * @self:
393  * @model: (allow-none):
394  *
395  */
396 void
gd_main_box_generic_set_model(GdMainBoxGeneric * self,GListModel * model)397 gd_main_box_generic_set_model (GdMainBoxGeneric *self, GListModel *model)
398 {
399   g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
400   g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
401 
402   g_object_set (self, "model", model, NULL);
403 }
404 
405 /**
406  * gd_main_box_generic_set_selection_mode:
407  * @self:
408  * @selection_mode:
409  *
410  */
411 void
gd_main_box_generic_set_selection_mode(GdMainBoxGeneric * self,gboolean selection_mode)412 gd_main_box_generic_set_selection_mode (GdMainBoxGeneric *self, gboolean selection_mode)
413 {
414   g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
415   g_object_set (self, "gd-selection-mode", selection_mode, NULL);
416 }
417 
418 /**
419  * gd_main_box_generic_set_show_primary_text:
420  * @self:
421  * @show_primary_text:
422  *
423  */
424 void
gd_main_box_generic_set_show_primary_text(GdMainBoxGeneric * self,gboolean show_primary_text)425 gd_main_box_generic_set_show_primary_text (GdMainBoxGeneric *self, gboolean show_primary_text)
426 {
427   g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
428   g_object_set (self, "show-primary-text", show_primary_text, NULL);
429 }
430 
431 /**
432  * gd_main_box_generic_set_show_secondary_text:
433  * @self:
434  * @show_secondary_text:
435  *
436  */
437 void
gd_main_box_generic_set_show_secondary_text(GdMainBoxGeneric * self,gboolean show_secondary_text)438 gd_main_box_generic_set_show_secondary_text (GdMainBoxGeneric *self, gboolean show_secondary_text)
439 {
440   g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
441   g_object_set (self, "show-secondary-text", show_secondary_text, NULL);
442 }
443 
444 void
gd_main_box_generic_unselect_all(GdMainBoxGeneric * self)445 gd_main_box_generic_unselect_all (GdMainBoxGeneric *self)
446 {
447   GdMainBoxGenericInterface *iface;
448 
449   g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
450 
451   iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
452 
453   (* iface->unselect_all) (self);
454 }
455 
456 void
gd_main_box_generic_unselect_child(GdMainBoxGeneric * self,GdMainBoxChild * child)457 gd_main_box_generic_unselect_child (GdMainBoxGeneric *self, GdMainBoxChild *child)
458 {
459   GdMainBoxGenericInterface *iface;
460 
461   g_return_if_fail (GD_IS_MAIN_BOX_GENERIC (self));
462   g_return_if_fail (GD_IS_MAIN_BOX_CHILD (child));
463 
464   iface = GD_MAIN_BOX_GENERIC_GET_IFACE (self);
465 
466   (* iface->unselect_child) (self, child);
467 }
468 
469 void
gd_main_box_generic_toggle_selection_for_child(GdMainBoxGeneric * self,GdMainBoxChild * child,gboolean select_range)470 gd_main_box_generic_toggle_selection_for_child (GdMainBoxGeneric *self,
471                                                 GdMainBoxChild *child,
472                                                 gboolean select_range)
473 {
474   GListModel *model;
475 
476   model = gd_main_box_generic_get_model (self);
477   if (model == NULL)
478     return;
479 
480   if (gd_main_box_child_get_selected (child))
481     {
482       gd_main_box_generic_unselect_child (self, child);
483     }
484   else
485     {
486       if (select_range)
487         gd_main_box_generic_select_range (self, child);
488       else
489         gd_main_box_generic_select_child (self, child);
490     }
491 }
492