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