/* gtksidebarrow.c * * Copyright (C) 2015 Carlos Soriano <csoriano@gnome.org> * * This file is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of the * License, or (at your option) any later version. * * This file is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "config.h" #include "gtksidebarrowprivate.h" /* For section and place type enums */ #include "gtkplacessidebarprivate.h" #include "gtkwidget.h" #include "gtkimage.h" #include "gtklabel.h" #include "gtkstylecontext.h" #include "gtkrevealer.h" #include "gtkintl.h" #include "gtkspinner.h" #ifdef HAVE_CLOUDPROVIDERS #include <cloudproviders.h> #endif struct _GtkSidebarRow { GtkListBoxRow parent_instance; GIcon *start_icon; GIcon *end_icon; GtkWidget *start_icon_widget; GtkWidget *end_icon_widget; char *label; char *tooltip; GtkWidget *label_widget; gboolean ejectable; GtkWidget *eject_button; int order_index; GtkPlacesSidebarSectionType section_type; GtkPlacesSidebarPlaceType place_type; char *uri; GDrive *drive; GVolume *volume; GMount *mount; GObject *cloud_provider_account; gboolean placeholder; GtkPlacesSidebar *sidebar; GtkWidget *revealer; GtkWidget *busy_spinner; }; G_DEFINE_TYPE (GtkSidebarRow, gtk_sidebar_row, GTK_TYPE_LIST_BOX_ROW) enum { PROP_0, PROP_START_ICON, PROP_END_ICON, PROP_LABEL, PROP_TOOLTIP, PROP_EJECTABLE, PROP_SIDEBAR, PROP_ORDER_INDEX, PROP_SECTION_TYPE, PROP_PLACE_TYPE, PROP_URI, PROP_DRIVE, PROP_VOLUME, PROP_MOUNT, PROP_CLOUD_PROVIDER_ACCOUNT, PROP_PLACEHOLDER, LAST_PROP }; static GParamSpec *properties [LAST_PROP]; #ifdef HAVE_CLOUDPROVIDERS static void cloud_row_update (GtkSidebarRow *self) { CloudProvidersAccount *account; GIcon *end_icon; int provider_status; account = CLOUD_PROVIDERS_ACCOUNT (self->cloud_provider_account); provider_status = cloud_providers_account_get_status (account); switch (provider_status) { case CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE: end_icon = NULL; break; case CLOUD_PROVIDERS_ACCOUNT_STATUS_SYNCING: end_icon = g_themed_icon_new ("emblem-synchronizing-symbolic"); break; case CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR: end_icon = g_themed_icon_new ("dialog-warning-symbolic"); break; default: return; } g_object_set (self, "label", cloud_providers_account_get_name (account), NULL); g_object_set (self, "tooltip", cloud_providers_account_get_status_details (account), NULL); g_object_set (self, "end-icon", end_icon, NULL); if (end_icon != NULL) g_object_unref (end_icon); } #endif static void gtk_sidebar_row_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GtkSidebarRow *self = GTK_SIDEBAR_ROW (object); switch (prop_id) { case PROP_SIDEBAR: g_value_set_object (value, self->sidebar); break; case PROP_START_ICON: g_value_set_object (value, self->start_icon); break; case PROP_END_ICON: g_value_set_object (value, self->end_icon); break; case PROP_LABEL: g_value_set_string (value, self->label); break; case PROP_TOOLTIP: g_value_set_string (value, self->tooltip); break; case PROP_EJECTABLE: g_value_set_boolean (value, self->ejectable); break; case PROP_ORDER_INDEX: g_value_set_int (value, self->order_index); break; case PROP_SECTION_TYPE: g_value_set_int (value, self->section_type); break; case PROP_PLACE_TYPE: g_value_set_int (value, self->place_type); break; case PROP_URI: g_value_set_string (value, self->uri); break; case PROP_DRIVE: g_value_set_object (value, self->drive); break; case PROP_VOLUME: g_value_set_object (value, self->volume); break; case PROP_MOUNT: g_value_set_object (value, self->mount); break; case PROP_CLOUD_PROVIDER_ACCOUNT: g_value_set_object (value, self->cloud_provider_account); break; case PROP_PLACEHOLDER: g_value_set_boolean (value, self->placeholder); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void gtk_sidebar_row_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { GtkSidebarRow *self = GTK_SIDEBAR_ROW (object); switch (prop_id) { case PROP_SIDEBAR: self->sidebar = g_value_get_object (value); break; case PROP_START_ICON: { g_clear_object (&self->start_icon); object = g_value_get_object (value); if (object != NULL) { self->start_icon = G_ICON (g_object_ref (object)); gtk_image_set_from_gicon (GTK_IMAGE (self->start_icon_widget), self->start_icon); } else { gtk_image_clear (GTK_IMAGE (self->start_icon_widget)); } break; } case PROP_END_ICON: { g_clear_object (&self->end_icon); object = g_value_get_object (value); if (object != NULL) { self->end_icon = G_ICON (g_object_ref (object)); gtk_image_set_from_gicon (GTK_IMAGE (self->end_icon_widget), self->end_icon); gtk_widget_show (self->end_icon_widget); } else { gtk_image_clear (GTK_IMAGE (self->end_icon_widget)); gtk_widget_hide (self->end_icon_widget); } break; } case PROP_LABEL: g_free (self->label); self->label = g_strdup (g_value_get_string (value)); gtk_label_set_text (GTK_LABEL (self->label_widget), self->label); break; case PROP_TOOLTIP: g_free (self->tooltip); self->tooltip = g_strdup (g_value_get_string (value)); gtk_widget_set_tooltip_text (GTK_WIDGET (self), self->tooltip); break; case PROP_EJECTABLE: self->ejectable = g_value_get_boolean (value); if (self->ejectable) gtk_widget_show (self->eject_button); else gtk_widget_hide (self->eject_button); break; case PROP_ORDER_INDEX: self->order_index = g_value_get_int (value); break; case PROP_SECTION_TYPE: self->section_type = g_value_get_int (value); if (self->section_type == SECTION_COMPUTER || self->section_type == SECTION_OTHER_LOCATIONS) gtk_label_set_ellipsize (GTK_LABEL (self->label_widget), PANGO_ELLIPSIZE_NONE); else gtk_label_set_ellipsize (GTK_LABEL (self->label_widget), PANGO_ELLIPSIZE_END); break; case PROP_PLACE_TYPE: self->place_type = g_value_get_int (value); break; case PROP_URI: g_free (self->uri); self->uri = g_strdup (g_value_get_string (value)); break; case PROP_DRIVE: g_set_object (&self->drive, g_value_get_object (value)); break; case PROP_VOLUME: g_set_object (&self->volume, g_value_get_object (value)); break; case PROP_MOUNT: g_set_object (&self->mount, g_value_get_object (value)); break; case PROP_CLOUD_PROVIDER_ACCOUNT: #ifdef HAVE_CLOUDPROVIDERS if (self->cloud_provider_account != NULL) g_signal_handlers_disconnect_by_data (self->cloud_provider_account, self); self->cloud_provider_account = g_value_dup_object (value); if (self->cloud_provider_account != NULL) { g_signal_connect_swapped (self->cloud_provider_account, "notify::name", G_CALLBACK (cloud_row_update), self); g_signal_connect_swapped (self->cloud_provider_account, "notify::status", G_CALLBACK (cloud_row_update), self); g_signal_connect_swapped (self->cloud_provider_account, "notify::status-details", G_CALLBACK (cloud_row_update), self); } #endif break; case PROP_PLACEHOLDER: { self->placeholder = g_value_get_boolean (value); if (self->placeholder) { g_clear_object (&self->start_icon); g_clear_object (&self->end_icon); g_free (self->label); self->label = NULL; g_free (self->tooltip); self->tooltip = NULL; gtk_widget_set_tooltip_text (GTK_WIDGET (self), NULL); self->ejectable = FALSE; self->section_type = SECTION_BOOKMARKS; self->place_type = PLACES_BOOKMARK_PLACEHOLDER; g_free (self->uri); self->uri = NULL; g_clear_object (&self->drive); g_clear_object (&self->volume); g_clear_object (&self->mount); g_clear_object (&self->cloud_provider_account); gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (self), NULL); gtk_widget_add_css_class (GTK_WIDGET (self), "sidebar-placeholder-row"); } break; } default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void on_child_revealed (GObject *self, GParamSpec *pspec, gpointer user_data) { /* We need to hide the actual widget because if not the GtkListBoxRow will * still allocate the paddings, even if the revealer is not revealed, and * therefore the row will be still somewhat visible. */ if (!gtk_revealer_get_reveal_child (GTK_REVEALER (self))) gtk_widget_hide (GTK_WIDGET (GTK_SIDEBAR_ROW (user_data))); } void gtk_sidebar_row_reveal (GtkSidebarRow *self) { gtk_widget_show (GTK_WIDGET (self)); gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), TRUE); } void gtk_sidebar_row_hide (GtkSidebarRow *self, gboolean immediate) { guint transition_duration; transition_duration = gtk_revealer_get_transition_duration (GTK_REVEALER (self->revealer)); if (immediate) gtk_revealer_set_transition_duration (GTK_REVEALER (self->revealer), 0); gtk_revealer_set_reveal_child (GTK_REVEALER (self->revealer), FALSE); gtk_revealer_set_transition_duration (GTK_REVEALER (self->revealer), transition_duration); } void gtk_sidebar_row_set_start_icon (GtkSidebarRow *self, GIcon *icon) { g_return_if_fail (GTK_IS_SIDEBAR_ROW (self)); if (self->start_icon != icon) { g_set_object (&self->start_icon, icon); if (self->start_icon != NULL) gtk_image_set_from_gicon (GTK_IMAGE (self->start_icon_widget), self->start_icon); else gtk_image_clear (GTK_IMAGE (self->start_icon_widget)); g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_START_ICON]); } } void gtk_sidebar_row_set_end_icon (GtkSidebarRow *self, GIcon *icon) { g_return_if_fail (GTK_IS_SIDEBAR_ROW (self)); if (self->end_icon != icon) { g_set_object (&self->end_icon, icon); if (self->end_icon != NULL) gtk_image_set_from_gicon (GTK_IMAGE (self->end_icon_widget), self->end_icon); else if (self->end_icon_widget != NULL) gtk_image_clear (GTK_IMAGE (self->end_icon_widget)); g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_END_ICON]); } } static void gtk_sidebar_row_finalize (GObject *object) { GtkSidebarRow *self = GTK_SIDEBAR_ROW (object); g_clear_object (&self->start_icon); g_clear_object (&self->end_icon); g_free (self->label); self->label = NULL; g_free (self->tooltip); self->tooltip = NULL; g_free (self->uri); self->uri = NULL; g_clear_object (&self->drive); g_clear_object (&self->volume); g_clear_object (&self->mount); #ifdef HAVE_CLOUDPROVIDERS if (self->cloud_provider_account != NULL) g_signal_handlers_disconnect_by_data (self->cloud_provider_account, self); g_clear_object (&self->cloud_provider_account); #endif G_OBJECT_CLASS (gtk_sidebar_row_parent_class)->finalize (object); } static void gtk_sidebar_row_init (GtkSidebarRow *self) { gtk_widget_init_template (GTK_WIDGET (self)); gtk_widget_set_focus_on_click (GTK_WIDGET (self), FALSE); } static void gtk_sidebar_row_class_init (GtkSidebarRowClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->get_property = gtk_sidebar_row_get_property; object_class->set_property = gtk_sidebar_row_set_property; object_class->finalize = gtk_sidebar_row_finalize; properties [PROP_SIDEBAR] = g_param_spec_object ("sidebar", "Sidebar", "Sidebar", GTK_TYPE_PLACES_SIDEBAR, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); properties [PROP_START_ICON] = g_param_spec_object ("start-icon", "start-icon", "The start icon.", G_TYPE_ICON, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); properties [PROP_END_ICON] = g_param_spec_object ("end-icon", "end-icon", "The end icon.", G_TYPE_ICON, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); properties [PROP_LABEL] = g_param_spec_string ("label", "label", "The label text.", NULL, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); properties [PROP_TOOLTIP] = g_param_spec_string ("tooltip", "Tooltip", "Tooltip", NULL, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); properties [PROP_EJECTABLE] = g_param_spec_boolean ("ejectable", "Ejectable", "Ejectable", FALSE, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); properties [PROP_ORDER_INDEX] = g_param_spec_int ("order-index", "OrderIndex", "Order Index", 0, G_MAXINT, 0, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); properties [PROP_SECTION_TYPE] = g_param_spec_int ("section-type", "section type", "The section type.", SECTION_INVALID, N_SECTIONS, SECTION_INVALID, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); properties [PROP_PLACE_TYPE] = g_param_spec_int ("place-type", "place type", "The place type.", PLACES_INVALID, N_PLACES, PLACES_INVALID, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); properties [PROP_URI] = g_param_spec_string ("uri", "Uri", "Uri", NULL, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); properties [PROP_DRIVE] = g_param_spec_object ("drive", "Drive", "Drive", G_TYPE_DRIVE, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); properties [PROP_VOLUME] = g_param_spec_object ("volume", "Volume", "Volume", G_TYPE_VOLUME, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); properties [PROP_MOUNT] = g_param_spec_object ("mount", "Mount", "Mount", G_TYPE_MOUNT, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); properties [PROP_CLOUD_PROVIDER_ACCOUNT] = g_param_spec_object ("cloud-provider-account", "CloudProvidersAccount", "CloudProvidersAccount", G_TYPE_OBJECT, (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); properties [PROP_PLACEHOLDER] = g_param_spec_boolean ("placeholder", "Placeholder", "Placeholder", FALSE, (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_properties (object_class, LAST_PROP, properties); gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtksidebarrow.ui"); gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, start_icon_widget); gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, end_icon_widget); gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, label_widget); gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, eject_button); gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, revealer); gtk_widget_class_bind_template_child (widget_class, GtkSidebarRow, busy_spinner); gtk_widget_class_bind_template_callback (widget_class, on_child_revealed); gtk_widget_class_set_css_name (widget_class, I_("row")); } GtkSidebarRow* gtk_sidebar_row_clone (GtkSidebarRow *self) { return g_object_new (GTK_TYPE_SIDEBAR_ROW, "sidebar", self->sidebar, "start-icon", self->start_icon, "end-icon", self->end_icon, "label", self->label, "tooltip", self->tooltip, "ejectable", self->ejectable, "order-index", self->order_index, "section-type", self->section_type, "place-type", self->place_type, "uri", self->uri, "drive", self->drive, "volume", self->volume, "mount", self->mount, "cloud-provider-account", self->cloud_provider_account, NULL); } GtkWidget* gtk_sidebar_row_get_eject_button (GtkSidebarRow *self) { return self->eject_button; } void gtk_sidebar_row_set_busy (GtkSidebarRow *row, gboolean is_busy) { g_return_if_fail (GTK_IS_SIDEBAR_ROW (row)); gtk_widget_set_visible (row->busy_spinner, is_busy); }