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
36struct _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
62G_DEFINE_TYPE (GtkSidebarRow, gtk_sidebar_row, GTK_TYPE_LIST_BOX_ROW)
63
64enum
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
85static GParamSpec *properties [LAST_PROP];
86
87#ifdef HAVE_CLOUDPROVIDERS
88
89static void
90cloud_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
132static void
133gtk_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, v_object: self->sidebar);
144 break;
145
146 case PROP_START_ICON:
147 g_value_set_object (value, v_object: self->start_icon);
148 break;
149
150 case PROP_END_ICON:
151 g_value_set_object (value, v_object: self->end_icon);
152 break;
153
154 case PROP_LABEL:
155 g_value_set_string (value, v_string: self->label);
156 break;
157
158 case PROP_TOOLTIP:
159 g_value_set_string (value, v_string: self->tooltip);
160 break;
161
162 case PROP_EJECTABLE:
163 g_value_set_boolean (value, v_boolean: self->ejectable);
164 break;
165
166 case PROP_ORDER_INDEX:
167 g_value_set_int (value, v_int: self->order_index);
168 break;
169
170 case PROP_SECTION_TYPE:
171 g_value_set_enum (value, v_enum: self->section_type);
172 break;
173
174 case PROP_PLACE_TYPE:
175 g_value_set_enum (value, v_enum: self->place_type);
176 break;
177
178 case PROP_URI:
179 g_value_set_string (value, v_string: self->uri);
180 break;
181
182 case PROP_DRIVE:
183 g_value_set_object (value, v_object: self->drive);
184 break;
185
186 case PROP_VOLUME:
187 g_value_set_object (value, v_object: self->volume);
188 break;
189
190 case PROP_MOUNT:
191 g_value_set_object (value, v_object: self->mount);
192 break;
193
194 case PROP_CLOUD_PROVIDER_ACCOUNT:
195 g_value_set_object (value, v_object: self->cloud_provider_account);
196 break;
197
198 case PROP_PLACEHOLDER:
199 g_value_set_boolean (value, v_boolean: self->placeholder);
200 break;
201
202 default:
203 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
204 }
205}
206
207static void
208gtk_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), icon: 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), icon: self->end_icon);
245 gtk_widget_show (widget: self->end_icon_widget);
246 }
247 else
248 {
249 gtk_image_clear (GTK_IMAGE (self->end_icon_widget));
250 gtk_widget_hide (widget: self->end_icon_widget);
251 }
252 break;
253 }
254
255 case PROP_LABEL:
256 g_free (mem: self->label);
257 self->label = g_strdup (str: g_value_get_string (value));
258 gtk_label_set_text (GTK_LABEL (self->label_widget), str: self->label);
259 break;
260
261 case PROP_TOOLTIP:
262 g_free (mem: self->tooltip);
263 self->tooltip = g_strdup (str: g_value_get_string (value));
264 gtk_widget_set_tooltip_text (GTK_WIDGET (self), text: 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 (widget: self->eject_button);
271 else
272 gtk_widget_hide (widget: 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), mode: PANGO_ELLIPSIZE_NONE);
284 else
285 gtk_label_set_ellipsize (GTK_LABEL (self->label_widget), mode: 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 (mem: self->uri);
294 self->uri = g_strdup (str: 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 (mem: self->label);
336 self->label = NULL;
337 g_free (mem: 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 (mem: 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), css_class: "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
363static void
364on_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
375void
376gtk_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
382void
383gtk_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), duration: 0);
391
392 gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), FALSE);
393
394 gtk_revealer_set_transition_duration (GTK_REVEALER (self->revealer), duration: transition_duration);
395}
396
397void
398gtk_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), icon: 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), pspec: properties [PROP_START_ICON]);
412 }
413}
414
415void
416gtk_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), icon: 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), pspec: properties [PROP_END_ICON]);
431 }
432}
433
434static void
435gtk_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 (mem: self->label);
442 self->label = NULL;
443 g_free (mem: self->tooltip);
444 self->tooltip = NULL;
445 g_free (mem: 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
459static void
460gtk_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
467static void
468gtk_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 (name: "sidebar",
479 nick: "Sidebar",
480 blurb: "Sidebar",
481 GTK_TYPE_PLACES_SIDEBAR,
482 flags: (G_PARAM_READWRITE |
483 G_PARAM_CONSTRUCT_ONLY |
484 G_PARAM_STATIC_STRINGS));
485
486 properties [PROP_START_ICON] =
487 g_param_spec_object (name: "start-icon",
488 nick: "start-icon",
489 blurb: "The start icon.",
490 G_TYPE_ICON,
491 flags: (G_PARAM_READWRITE |
492 G_PARAM_STATIC_STRINGS));
493
494 properties [PROP_END_ICON] =
495 g_param_spec_object (name: "end-icon",
496 nick: "end-icon",
497 blurb: "The end icon.",
498 G_TYPE_ICON,
499 flags: (G_PARAM_READWRITE |
500 G_PARAM_STATIC_STRINGS));
501
502 properties [PROP_LABEL] =
503 g_param_spec_string (name: "label",
504 nick: "label",
505 blurb: "The label text.",
506 NULL,
507 flags: (G_PARAM_READWRITE |
508 G_PARAM_STATIC_STRINGS));
509
510 properties [PROP_TOOLTIP] =
511 g_param_spec_string (name: "tooltip",
512 nick: "Tooltip",
513 blurb: "Tooltip",
514 NULL,
515 flags: (G_PARAM_READWRITE |
516 G_PARAM_STATIC_STRINGS));
517
518 properties [PROP_EJECTABLE] =
519 g_param_spec_boolean (name: "ejectable",
520 nick: "Ejectable",
521 blurb: "Ejectable",
522 FALSE,
523 flags: (G_PARAM_READWRITE |
524 G_PARAM_STATIC_STRINGS));
525
526 properties [PROP_ORDER_INDEX] =
527 g_param_spec_int (name: "order-index",
528 nick: "OrderIndex",
529 blurb: "Order Index",
530 minimum: 0, G_MAXINT, default_value: 0,
531 flags: (G_PARAM_READWRITE |
532 G_PARAM_STATIC_STRINGS));
533
534 properties [PROP_SECTION_TYPE] =
535 g_param_spec_enum (name: "section-type",
536 nick: "section type",
537 blurb: "The section type.",
538 enum_type: GTK_TYPE_PLACES_SECTION_TYPE,
539 default_value: GTK_PLACES_SECTION_INVALID,
540 flags: (G_PARAM_READWRITE |
541 G_PARAM_STATIC_STRINGS |
542 G_PARAM_CONSTRUCT_ONLY));
543
544 properties [PROP_PLACE_TYPE] =
545 g_param_spec_enum (name: "place-type",
546 nick: "place type",
547 blurb: "The place type.",
548 enum_type: GTK_TYPE_PLACES_PLACE_TYPE,
549 default_value: GTK_PLACES_INVALID,
550 flags: (G_PARAM_READWRITE |
551 G_PARAM_STATIC_STRINGS |
552 G_PARAM_CONSTRUCT_ONLY));
553
554 properties [PROP_URI] =
555 g_param_spec_string (name: "uri",
556 nick: "Uri",
557 blurb: "Uri",
558 NULL,
559 flags: (G_PARAM_READWRITE |
560 G_PARAM_CONSTRUCT_ONLY |
561 G_PARAM_STATIC_STRINGS));
562
563 properties [PROP_DRIVE] =
564 g_param_spec_object (name: "drive",
565 nick: "Drive",
566 blurb: "Drive",
567 G_TYPE_DRIVE,
568 flags: (G_PARAM_READWRITE |
569 G_PARAM_CONSTRUCT_ONLY |
570 G_PARAM_STATIC_STRINGS));
571
572 properties [PROP_VOLUME] =
573 g_param_spec_object (name: "volume",
574 nick: "Volume",
575 blurb: "Volume",
576 G_TYPE_VOLUME,
577 flags: (G_PARAM_READWRITE |
578 G_PARAM_CONSTRUCT_ONLY |
579 G_PARAM_STATIC_STRINGS));
580
581 properties [PROP_MOUNT] =
582 g_param_spec_object (name: "mount",
583 nick: "Mount",
584 blurb: "Mount",
585 G_TYPE_MOUNT,
586 flags: (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 (name: "cloud-provider-account",
592 nick: "CloudProvidersAccount",
593 blurb: "CloudProvidersAccount",
594 G_TYPE_OBJECT,
595 flags: (G_PARAM_READWRITE |
596 G_PARAM_STATIC_STRINGS));
597
598 properties [PROP_PLACEHOLDER] =
599 g_param_spec_boolean (name: "placeholder",
600 nick: "Placeholder",
601 blurb: "Placeholder",
602 FALSE,
603 flags: (G_PARAM_READWRITE |
604 G_PARAM_CONSTRUCT_ONLY |
605 G_PARAM_STATIC_STRINGS));
606
607 g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: properties);
608
609 gtk_widget_class_set_template_from_resource (widget_class,
610 resource_name: "/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
623GtkSidebarRow*
624gtk_sidebar_row_clone (GtkSidebarRow *self)
625{
626 return g_object_new (GTK_TYPE_SIDEBAR_ROW,
627 first_property_name: "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
644GtkWidget*
645gtk_sidebar_row_get_eject_button (GtkSidebarRow *self)
646{
647 return self->eject_button;
648}
649
650void
651gtk_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 (widget: row->busy_spinner, visible: is_busy);
657}
658

source code of gtk/gtk/gtksidebarrow.c