1 /* gtksidebarrow.c
2 *
3 * Copyright (C) 2015 Carlos Soriano <csoriano@gnome.org>
4 *
5 * This file is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as
7 * published by the Free Software Foundation; either version 2.1 of the
8 * License, or (at your option) any later version.
9 *
10 * This file is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "config.h"
20
21 #include "gtksidebarrowprivate.h"
22 /* For section and place type enums */
23 #include "gtkplacessidebarprivate.h"
24 #include "gtkplacessidebar.h"
25 #include "gtkwidget.h"
26 #include "gtkimage.h"
27 #include "gtklabel.h"
28 #include "gtkstylecontext.h"
29 #include "gtkrevealer.h"
30 #include "gtkselection.h"
31 #include "gtkspinner.h"
32
33 #ifdef HAVE_CLOUDPROVIDERS
34 #include <cloudproviders/cloudprovidersaccount.h>
35 #endif
36
37 struct _GtkSidebarRow
38 {
39 GtkListBoxRow parent_instance;
40 GIcon *start_icon;
41 GIcon *end_icon;
42 GtkWidget *start_icon_widget;
43 GtkWidget *end_icon_widget;
44 gchar *label;
45 gchar *tooltip;
46 GtkWidget *label_widget;
47 gboolean ejectable;
48 GtkWidget *eject_button;
49 gint order_index;
50 GtkPlacesSidebarSectionType section_type;
51 GtkPlacesSidebarPlaceType place_type;
52 gchar *uri;
53 GDrive *drive;
54 GVolume *volume;
55 GMount *mount;
56 GObject *cloud_provider_account;
57 gboolean placeholder;
58 GtkPlacesSidebar *sidebar;
59 GtkWidget *event_box;
60 GtkWidget *revealer;
61 GtkWidget *busy_spinner;
62 };
63
64 G_DEFINE_TYPE (GtkSidebarRow, gtk_sidebar_row, GTK_TYPE_LIST_BOX_ROW)
65
66 enum
67 {
68 PROP_0,
69 PROP_START_ICON,
70 PROP_END_ICON,
71 PROP_LABEL,
72 PROP_TOOLTIP,
73 PROP_EJECTABLE,
74 PROP_SIDEBAR,
75 PROP_ORDER_INDEX,
76 PROP_SECTION_TYPE,
77 PROP_PLACE_TYPE,
78 PROP_URI,
79 PROP_DRIVE,
80 PROP_VOLUME,
81 PROP_MOUNT,
82 PROP_CLOUD_PROVIDER_ACCOUNT,
83 PROP_PLACEHOLDER,
84 LAST_PROP
85 };
86
87 static GParamSpec *properties [LAST_PROP];
88
89 #ifdef HAVE_CLOUDPROVIDERS
90
91 static void
cloud_row_update(GtkSidebarRow * self)92 cloud_row_update (GtkSidebarRow *self)
93 {
94 CloudProvidersAccount *account;
95 GIcon *end_icon;
96 gint provider_status;
97
98 account = CLOUD_PROVIDERS_ACCOUNT (self->cloud_provider_account);
99 provider_status = cloud_providers_account_get_status (account);
100 switch (provider_status)
101 {
102 case CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE:
103 end_icon = NULL;
104 break;
105
106 case CLOUD_PROVIDERS_ACCOUNT_STATUS_SYNCING:
107 end_icon = g_themed_icon_new ("emblem-synchronizing-symbolic");
108 break;
109
110 case CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR:
111 end_icon = g_themed_icon_new ("dialog-warning-symbolic");
112 break;
113
114 default:
115 return;
116 }
117
118 g_object_set (self,
119 "label", cloud_providers_account_get_name (account),
120 NULL);
121 g_object_set (self,
122 "tooltip", cloud_providers_account_get_status_details (account),
123 NULL);
124 g_object_set (self,
125 "end-icon", end_icon,
126 NULL);
127
128 if (end_icon != NULL)
129 g_object_unref (end_icon);
130 }
131
132 #endif
133
134 static void
gtk_sidebar_row_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)135 gtk_sidebar_row_get_property (GObject *object,
136 guint prop_id,
137 GValue *value,
138 GParamSpec *pspec)
139 {
140 GtkSidebarRow *self = GTK_SIDEBAR_ROW (object);
141
142 switch (prop_id)
143 {
144 case PROP_SIDEBAR:
145 g_value_set_object (value, self->sidebar);
146 break;
147
148 case PROP_START_ICON:
149 g_value_set_object (value, self->start_icon);
150 break;
151
152 case PROP_END_ICON:
153 g_value_set_object (value, self->end_icon);
154 break;
155
156 case PROP_LABEL:
157 g_value_set_string (value, self->label);
158 break;
159
160 case PROP_TOOLTIP:
161 g_value_set_string (value, self->tooltip);
162 break;
163
164 case PROP_EJECTABLE:
165 g_value_set_boolean (value, self->ejectable);
166 break;
167
168 case PROP_ORDER_INDEX:
169 g_value_set_int (value, self->order_index);
170 break;
171
172 case PROP_SECTION_TYPE:
173 g_value_set_int (value, self->section_type);
174 break;
175
176 case PROP_PLACE_TYPE:
177 g_value_set_int (value, self->place_type);
178 break;
179
180 case PROP_URI:
181 g_value_set_string (value, self->uri);
182 break;
183
184 case PROP_DRIVE:
185 g_value_set_object (value, self->drive);
186 break;
187
188 case PROP_VOLUME:
189 g_value_set_object (value, self->volume);
190 break;
191
192 case PROP_MOUNT:
193 g_value_set_object (value, self->mount);
194 break;
195
196 case PROP_CLOUD_PROVIDER_ACCOUNT:
197 g_value_set_object (value, self->cloud_provider_account);
198 break;
199
200 case PROP_PLACEHOLDER:
201 g_value_set_boolean (value, self->placeholder);
202 break;
203
204 default:
205 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
206 }
207 }
208
209 static void
gtk_sidebar_row_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)210 gtk_sidebar_row_set_property (GObject *object,
211 guint prop_id,
212 const GValue *value,
213 GParamSpec *pspec)
214 {
215 GtkSidebarRow *self = GTK_SIDEBAR_ROW (object);
216 GtkStyleContext *context;
217
218 switch (prop_id)
219 {
220 case PROP_SIDEBAR:
221 self->sidebar = g_value_get_object (value);
222 break;
223
224 case PROP_START_ICON:
225 {
226 g_clear_object (&self->start_icon);
227 object = g_value_get_object (value);
228 if (object != NULL)
229 {
230 self->start_icon = G_ICON (g_object_ref (object));
231 gtk_image_set_from_gicon (GTK_IMAGE (self->start_icon_widget),
232 self->start_icon,
233 GTK_ICON_SIZE_MENU);
234 }
235 else
236 {
237 gtk_image_clear (GTK_IMAGE (self->start_icon_widget));
238 }
239 break;
240 }
241
242 case PROP_END_ICON:
243 {
244 g_clear_object (&self->end_icon);
245 object = g_value_get_object (value);
246 if (object != NULL)
247 {
248 self->end_icon = G_ICON (g_object_ref (object));
249 gtk_image_set_from_gicon (GTK_IMAGE (self->end_icon_widget),
250 self->end_icon,
251 GTK_ICON_SIZE_MENU);
252 gtk_widget_show (self->end_icon_widget);
253 }
254 else
255 {
256 gtk_image_clear (GTK_IMAGE (self->end_icon_widget));
257 gtk_widget_hide (self->end_icon_widget);
258 }
259 break;
260 }
261
262 case PROP_LABEL:
263 g_free (self->label);
264 self->label = g_strdup (g_value_get_string (value));
265 gtk_label_set_text (GTK_LABEL (self->label_widget), self->label);
266 break;
267
268 case PROP_TOOLTIP:
269 g_free (self->tooltip);
270 self->tooltip = g_strdup (g_value_get_string (value));
271 gtk_widget_set_tooltip_text (GTK_WIDGET (self), self->tooltip);
272 break;
273
274 case PROP_EJECTABLE:
275 self->ejectable = g_value_get_boolean (value);
276 if (self->ejectable)
277 gtk_widget_show (self->eject_button);
278 else
279 gtk_widget_hide (self->eject_button);
280 break;
281
282 case PROP_ORDER_INDEX:
283 self->order_index = g_value_get_int (value);
284 break;
285
286 case PROP_SECTION_TYPE:
287 self->section_type = g_value_get_int (value);
288 if (self->section_type == SECTION_COMPUTER ||
289 self->section_type == SECTION_OTHER_LOCATIONS)
290 gtk_label_set_ellipsize (GTK_LABEL (self->label_widget), PANGO_ELLIPSIZE_NONE);
291 else
292 gtk_label_set_ellipsize (GTK_LABEL (self->label_widget), PANGO_ELLIPSIZE_END);
293 break;
294
295 case PROP_PLACE_TYPE:
296 self->place_type = g_value_get_int (value);
297 break;
298
299 case PROP_URI:
300 g_free (self->uri);
301 self->uri = g_strdup (g_value_get_string (value));
302 break;
303
304 case PROP_DRIVE:
305 g_set_object (&self->drive, g_value_get_object (value));
306 break;
307
308 case PROP_VOLUME:
309 g_set_object (&self->volume, g_value_get_object (value));
310 break;
311
312 case PROP_MOUNT:
313 g_set_object (&self->mount, g_value_get_object (value));
314 break;
315
316 case PROP_CLOUD_PROVIDER_ACCOUNT:
317 #ifdef HAVE_CLOUDPROVIDERS
318 if (self->cloud_provider_account != NULL)
319 g_signal_handlers_disconnect_by_data (self->cloud_provider_account, self);
320
321 self->cloud_provider_account = g_value_dup_object (value);
322
323 if (self->cloud_provider_account != NULL)
324 {
325 g_signal_connect_swapped (self->cloud_provider_account, "notify::name",
326 G_CALLBACK (cloud_row_update), self);
327 g_signal_connect_swapped (self->cloud_provider_account, "notify::status",
328 G_CALLBACK (cloud_row_update), self);
329 g_signal_connect_swapped (self->cloud_provider_account, "notify::status-details",
330 G_CALLBACK (cloud_row_update), self);
331 }
332 #endif
333 break;
334
335 case PROP_PLACEHOLDER:
336 {
337 self->placeholder = g_value_get_boolean (value);
338 if (self->placeholder)
339 {
340 g_clear_object (&self->start_icon);
341 g_clear_object (&self->end_icon);
342 g_free (self->label);
343 self->label = NULL;
344 g_free (self->tooltip);
345 self->tooltip = NULL;
346 gtk_widget_set_tooltip_text (GTK_WIDGET (self), NULL);
347 self->ejectable = FALSE;
348 self->section_type = SECTION_BOOKMARKS;
349 self->place_type = PLACES_BOOKMARK_PLACEHOLDER;
350 g_free (self->uri);
351 self->uri = NULL;
352 g_clear_object (&self->drive);
353 g_clear_object (&self->volume);
354 g_clear_object (&self->mount);
355 g_clear_object (&self->cloud_provider_account);
356
357 gtk_container_foreach (GTK_CONTAINER (self),
358 (GtkCallback) gtk_widget_destroy,
359 NULL);
360
361 context = gtk_widget_get_style_context (GTK_WIDGET (self));
362 gtk_style_context_add_class (context, "sidebar-placeholder-row");
363 }
364
365 break;
366 }
367
368 default:
369 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
370 }
371 }
372
373 static void
on_child_revealed(GObject * self,GParamSpec * pspec,gpointer user_data)374 on_child_revealed (GObject *self,
375 GParamSpec *pspec,
376 gpointer user_data)
377 {
378 /* We need to hide the actual widget because if not the GtkListBoxRow will
379 * still allocate the paddings, even if the revealer is not revealed, and
380 * therefore the row will be still somewhat visible. */
381 if (!gtk_revealer_get_reveal_child (GTK_REVEALER (self)))
382 gtk_widget_hide (GTK_WIDGET (GTK_SIDEBAR_ROW (user_data)));
383 }
384
385 void
gtk_sidebar_row_reveal(GtkSidebarRow * self)386 gtk_sidebar_row_reveal (GtkSidebarRow *self)
387 {
388 gtk_widget_show_all (GTK_WIDGET (self));
389 gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), TRUE);
390 }
391
392 void
gtk_sidebar_row_hide(GtkSidebarRow * self,gboolean inmediate)393 gtk_sidebar_row_hide (GtkSidebarRow *self,
394 gboolean inmediate)
395 {
396 guint transition_duration;
397
398 transition_duration = gtk_revealer_get_transition_duration (GTK_REVEALER (self->revealer));
399 if (inmediate)
400 gtk_revealer_set_transition_duration (GTK_REVEALER (self->revealer), 0);
401
402 gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), FALSE);
403
404 gtk_revealer_set_transition_duration (GTK_REVEALER (self->revealer), transition_duration);
405 }
406
407 void
gtk_sidebar_row_set_start_icon(GtkSidebarRow * self,GIcon * icon)408 gtk_sidebar_row_set_start_icon (GtkSidebarRow *self,
409 GIcon *icon)
410 {
411 g_return_if_fail (GTK_IS_SIDEBAR_ROW (self));
412
413 if (self->start_icon != icon)
414 {
415 g_set_object (&self->start_icon, icon);
416 if (self->start_icon != NULL)
417 gtk_image_set_from_gicon (GTK_IMAGE (self->start_icon_widget), self->start_icon,
418 GTK_ICON_SIZE_MENU);
419 else
420 gtk_image_clear (GTK_IMAGE (self->start_icon_widget));
421
422 g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_START_ICON]);
423 }
424 }
425
426 void
gtk_sidebar_row_set_end_icon(GtkSidebarRow * self,GIcon * icon)427 gtk_sidebar_row_set_end_icon (GtkSidebarRow *self,
428 GIcon *icon)
429 {
430 g_return_if_fail (GTK_IS_SIDEBAR_ROW (self));
431
432 if (self->end_icon != icon)
433 {
434 g_set_object (&self->end_icon, icon);
435 if (self->end_icon != NULL)
436 gtk_image_set_from_gicon (GTK_IMAGE (self->end_icon_widget), self->end_icon,
437 GTK_ICON_SIZE_MENU);
438 else
439 if (self->end_icon_widget != NULL)
440 gtk_image_clear (GTK_IMAGE (self->end_icon_widget));
441
442 g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_END_ICON]);
443 }
444 }
445
446 static void
gtk_sidebar_row_finalize(GObject * object)447 gtk_sidebar_row_finalize (GObject *object)
448 {
449 GtkSidebarRow *self = GTK_SIDEBAR_ROW (object);
450
451 g_clear_object (&self->start_icon);
452 g_clear_object (&self->end_icon);
453 g_free (self->label);
454 self->label = NULL;
455 g_free (self->tooltip);
456 self->tooltip = NULL;
457 g_free (self->uri);
458 self->uri = NULL;
459 g_clear_object (&self->drive);
460 g_clear_object (&self->volume);
461 g_clear_object (&self->mount);
462 #ifdef HAVE_CLOUDPROVIDERS
463 if (self->cloud_provider_account != NULL)
464 g_signal_handlers_disconnect_by_data (self->cloud_provider_account, self);
465 g_clear_object (&self->cloud_provider_account);
466 #endif
467
468 G_OBJECT_CLASS (gtk_sidebar_row_parent_class)->finalize (object);
469 }
470
471 static void
gtk_sidebar_row_init(GtkSidebarRow * self)472 gtk_sidebar_row_init (GtkSidebarRow *self)
473 {
474 gtk_widget_init_template (GTK_WIDGET (self));
475 }
476
477 static void
gtk_sidebar_row_class_init(GtkSidebarRowClass * klass)478 gtk_sidebar_row_class_init (GtkSidebarRowClass *klass)
479 {
480 GObjectClass *object_class = G_OBJECT_CLASS (klass);
481 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
482
483 object_class->get_property = gtk_sidebar_row_get_property;
484 object_class->set_property = gtk_sidebar_row_set_property;
485 object_class->finalize = gtk_sidebar_row_finalize;
486
487 properties [PROP_SIDEBAR] =
488 g_param_spec_object ("sidebar",
489 "Sidebar",
490 "Sidebar",
491 GTK_TYPE_PLACES_SIDEBAR,
492 (G_PARAM_READWRITE |
493 G_PARAM_CONSTRUCT_ONLY |
494 G_PARAM_STATIC_STRINGS));
495
496 properties [PROP_START_ICON] =
497 g_param_spec_object ("start-icon",
498 "start-icon",
499 "The start icon.",
500 G_TYPE_ICON,
501 (G_PARAM_READWRITE |
502 G_PARAM_STATIC_STRINGS));
503
504 properties [PROP_END_ICON] =
505 g_param_spec_object ("end-icon",
506 "end-icon",
507 "The end icon.",
508 G_TYPE_ICON,
509 (G_PARAM_READWRITE |
510 G_PARAM_STATIC_STRINGS));
511
512 properties [PROP_LABEL] =
513 g_param_spec_string ("label",
514 "label",
515 "The label text.",
516 NULL,
517 (G_PARAM_READWRITE |
518 G_PARAM_STATIC_STRINGS));
519
520 properties [PROP_TOOLTIP] =
521 g_param_spec_string ("tooltip",
522 "Tooltip",
523 "Tooltip",
524 NULL,
525 (G_PARAM_READWRITE |
526 G_PARAM_STATIC_STRINGS));
527
528 properties [PROP_EJECTABLE] =
529 g_param_spec_boolean ("ejectable",
530 "Ejectable",
531 "Ejectable",
532 FALSE,
533 (G_PARAM_READWRITE |
534 G_PARAM_STATIC_STRINGS));
535
536 properties [PROP_ORDER_INDEX] =
537 g_param_spec_int ("order-index",
538 "OrderIndex",
539 "Order Index",
540 0, G_MAXINT, 0,
541 (G_PARAM_READWRITE |
542 G_PARAM_STATIC_STRINGS));
543
544 properties [PROP_SECTION_TYPE] =
545 g_param_spec_int ("section-type",
546 "section type",
547 "The section type.",
548 SECTION_INVALID, N_SECTIONS, SECTION_INVALID,
549 (G_PARAM_READWRITE |
550 G_PARAM_STATIC_STRINGS |
551 G_PARAM_CONSTRUCT_ONLY));
552
553 properties [PROP_PLACE_TYPE] =
554 g_param_spec_int ("place-type",
555 "place type",
556 "The place type.",
557 PLACES_INVALID, N_PLACES, PLACES_INVALID,
558 (G_PARAM_READWRITE |
559 G_PARAM_STATIC_STRINGS |
560 G_PARAM_CONSTRUCT_ONLY));
561
562 properties [PROP_URI] =
563 g_param_spec_string ("uri",
564 "Uri",
565 "Uri",
566 NULL,
567 (G_PARAM_READWRITE |
568 G_PARAM_CONSTRUCT_ONLY |
569 G_PARAM_STATIC_STRINGS));
570
571 properties [PROP_DRIVE] =
572 g_param_spec_object ("drive",
573 "Drive",
574 "Drive",
575 G_TYPE_DRIVE,
576 (G_PARAM_READWRITE |
577 G_PARAM_CONSTRUCT_ONLY |
578 G_PARAM_STATIC_STRINGS));
579
580 properties [PROP_VOLUME] =
581 g_param_spec_object ("volume",
582 "Volume",
583 "Volume",
584 G_TYPE_VOLUME,
585 (G_PARAM_READWRITE |
586 G_PARAM_CONSTRUCT_ONLY |
587 G_PARAM_STATIC_STRINGS));
588
589 properties [PROP_MOUNT] =
590 g_param_spec_object ("mount",
591 "Mount",
592 "Mount",
593 G_TYPE_MOUNT,
594 (G_PARAM_READWRITE |
595 G_PARAM_CONSTRUCT_ONLY |
596 G_PARAM_STATIC_STRINGS));
597
598 properties [PROP_CLOUD_PROVIDER_ACCOUNT] =
599 g_param_spec_object ("cloud-provider-account",
600 "CloudProvidersAccount",
601 "CloudProvidersAccount",
602 G_TYPE_OBJECT,
603 (G_PARAM_READWRITE |
604 G_PARAM_STATIC_STRINGS));
605
606 properties [PROP_PLACEHOLDER] =
607 g_param_spec_boolean ("placeholder",
608 "Placeholder",
609 "Placeholder",
610 FALSE,
611 (G_PARAM_READWRITE |
612 G_PARAM_CONSTRUCT_ONLY |
613 G_PARAM_STATIC_STRINGS));
614
615 g_object_class_install_properties (object_class, LAST_PROP, properties);
616
617 gtk_widget_class_set_template_from_resource (widget_class,
618 "/org/gtk/libgtk/ui/gtksidebarrow.ui");
619
620 gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, start_icon_widget);
621 gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, end_icon_widget);
622 gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, label_widget);
623 gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, eject_button);
624 gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, event_box);
625 gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, revealer);
626 gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, busy_spinner);
627
628 gtk_widget_class_bind_template_callback (widget_class, on_child_revealed);
629 gtk_widget_class_set_css_name (widget_class, "row");
630 }
631
632 GtkSidebarRow*
gtk_sidebar_row_clone(GtkSidebarRow * self)633 gtk_sidebar_row_clone (GtkSidebarRow *self)
634 {
635 return g_object_new (GTK_TYPE_SIDEBAR_ROW,
636 "sidebar", self->sidebar,
637 "start-icon", self->start_icon,
638 "end-icon", self->end_icon,
639 "label", self->label,
640 "tooltip", self->tooltip,
641 "ejectable", self->ejectable,
642 "order-index", self->order_index,
643 "section-type", self->section_type,
644 "place-type", self->place_type,
645 "uri", self->uri,
646 "drive", self->drive,
647 "volume", self->volume,
648 "mount", self->mount,
649 "cloud-provider-account", self->cloud_provider_account,
650 NULL);
651 }
652
653 GtkWidget*
gtk_sidebar_row_get_eject_button(GtkSidebarRow * self)654 gtk_sidebar_row_get_eject_button (GtkSidebarRow *self)
655 {
656 return self->eject_button;
657 }
658
659 GtkWidget*
gtk_sidebar_row_get_event_box(GtkSidebarRow * self)660 gtk_sidebar_row_get_event_box (GtkSidebarRow *self)
661 {
662 return self->event_box;
663 }
664
665 void
gtk_sidebar_row_set_busy(GtkSidebarRow * row,gboolean is_busy)666 gtk_sidebar_row_set_busy (GtkSidebarRow *row,
667 gboolean is_busy)
668 {
669 g_return_if_fail (GTK_IS_SIDEBAR_ROW (row));
670
671 gtk_widget_set_visible (row->busy_spinner, is_busy);
672 }
673