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