/* * Copyright (C) 2014 Bastien Nocera * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ #include "config.h" #include #include #include #include "cc-sharing-networks.h" #include "org.gnome.SettingsDaemon.Sharing.h" #include "gsd-sharing-enums.h" #include "shell/list-box-helper.h" struct _CcSharingNetworksPrivate { GtkWidget *listbox; GtkWidget *current_row; GtkWidget *current_label; GtkWidget *current_icon; GtkWidget *current_switch; GtkWidget *no_network_row; char *service_name; GsdSharing *proxy; CcSharingStatus status; GList *networks; /* list of CcSharingNetwork */ }; G_DEFINE_TYPE_WITH_CODE (CcSharingNetworks, cc_sharing_networks, GTK_TYPE_GRID, G_ADD_PRIVATE (CcSharingNetworks)) enum { PROP_0, PROP_PROXY, PROP_SERVICE_NAME, PROP_STATUS }; static void cc_sharing_networks_class_init (CcSharingNetworksClass *klass); static void cc_sharing_networks_init (CcSharingNetworks *self); static void cc_sharing_networks_finalize (GObject *object); static void cc_sharing_update_networks_box (CcSharingNetworks *self); typedef struct { char *uuid; char *network_name; char *carrier_type; } CcSharingNetwork; static void cc_sharing_network_free (gpointer data) { CcSharingNetwork *net = data; g_free (net->uuid); g_free (net->network_name); g_free (net->carrier_type); g_free (net); } static void cc_sharing_networks_update_status (CcSharingNetworks *self) { CcSharingStatus status; if (self->priv->networks == NULL) status = CC_SHARING_STATUS_OFF; else if (gtk_widget_is_visible (self->priv->current_switch) && gtk_switch_get_active (GTK_SWITCH (self->priv->current_switch))) status = CC_SHARING_STATUS_ACTIVE; else status = CC_SHARING_STATUS_ENABLED; if (status != self->priv->status) { self->priv->status = status; g_object_notify (G_OBJECT (self), "status"); } } static void cc_sharing_update_networks (CcSharingNetworks *self) { GVariant *networks; char *uuid, *network_name, *carrier_type; GVariantIter iter; GError *error = NULL; g_list_free_full (self->priv->networks, cc_sharing_network_free); self->priv->networks = NULL; if (!gsd_sharing_call_list_networks_sync (self->priv->proxy, self->priv->service_name, &networks, NULL, &error)) { g_warning ("couldn't list networks: %s", error->message); g_dbus_proxy_set_cached_property (G_DBUS_PROXY (self->priv->proxy), "SharingStatus", g_variant_new_uint32 (GSD_SHARING_STATUS_OFFLINE)); g_error_free (error); cc_list_box_adjust_scrolling (GTK_LIST_BOX (self->priv->listbox)); return; } g_variant_iter_init (&iter, networks); while (g_variant_iter_next (&iter, "(sss)", &uuid, &network_name, &carrier_type)) { CcSharingNetwork *net; net = g_new0 (CcSharingNetwork, 1); net->uuid = uuid; net->network_name = network_name; net->carrier_type = carrier_type; self->priv->networks = g_list_prepend (self->priv->networks, net); } self->priv->networks = g_list_reverse (self->priv->networks); cc_list_box_adjust_scrolling (GTK_LIST_BOX (self->priv->listbox)); g_variant_unref (networks); } static void cc_sharing_networks_remove_network (GtkWidget *button, CcSharingNetworks *self) { GtkWidget *row; GError *error = NULL; gboolean ret; const char *uuid; row = g_object_get_data (G_OBJECT (button), "row"); uuid = g_object_get_data (G_OBJECT (row), "uuid"); ret = gsd_sharing_call_disable_service_sync (self->priv->proxy, self->priv->service_name, uuid, NULL, &error); if (!ret) { g_warning ("Failed to remove service %s: %s", self->priv->service_name, error->message); g_error_free (error); } cc_sharing_update_networks (self); cc_sharing_update_networks_box (self); } static gboolean cc_sharing_networks_enable_network (GtkSwitch *widget, gboolean state, gpointer user_data) { CcSharingNetworks *self = user_data; GError *error = NULL; gboolean ret; if (state) { ret = gsd_sharing_call_enable_service_sync (self->priv->proxy, self->priv->service_name, NULL, &error); } else { ret = gsd_sharing_call_disable_service_sync (self->priv->proxy, self->priv->service_name, gsd_sharing_get_current_network (self->priv->proxy), NULL, &error); } if (ret) { gtk_switch_set_state (widget, state); } else { g_warning ("Failed to %s service %s: %s", state ? "enable" : "disable", self->priv->service_name, error->message); g_error_free (error); g_signal_handlers_block_by_func (widget, cc_sharing_networks_enable_network, self); gtk_switch_set_active (widget, !state); g_signal_handlers_unblock_by_func (widget, cc_sharing_networks_enable_network, self); } cc_sharing_update_networks (self); cc_sharing_networks_update_status (self); return TRUE; } static GtkWidget * cc_sharing_networks_new_row (const char *uuid, const char *network_name, const char *carrier_type, CcSharingNetworks *self) { GtkWidget *row, *box, *w; const char *icon_name; row = gtk_list_box_row_new (); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_set_border_width (GTK_CONTAINER (box), 12); gtk_container_add (GTK_CONTAINER (row), box); if (g_strcmp0 (carrier_type, "802-11-wireless") == 0) { icon_name = "network-wireless-offline-symbolic"; } else if (g_strcmp0 (carrier_type, "802-3-ethernet") == 0) { icon_name = "network-wired-disconnected-symbolic"; } else { icon_name = "network-wired-symbolic"; } w = gtk_image_new_from_icon_name (icon_name, GTK_ICON_SIZE_MENU); gtk_widget_set_margin_end (w, 12); gtk_container_add (GTK_CONTAINER (box), w); /* Label */ w = gtk_label_new (network_name); gtk_container_add (GTK_CONTAINER (box), w); /* Remove button */ w = gtk_button_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); gtk_button_set_relief (GTK_BUTTON (w), GTK_RELIEF_NONE); gtk_widget_set_margin_top (w, 3); gtk_widget_set_margin_bottom (w, 3); gtk_widget_set_margin_end (w, 12); gtk_widget_set_valign (w, GTK_ALIGN_CENTER); gtk_box_pack_end (GTK_BOX (box), w, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (w), "clicked", G_CALLBACK (cc_sharing_networks_remove_network), self); g_object_set_data (G_OBJECT (w), "row", row); g_object_set_data_full (G_OBJECT (row), "uuid", g_strdup (uuid), g_free); gtk_widget_show_all (row); return row; } static GtkWidget * cc_sharing_networks_new_current_row (CcSharingNetworks *self) { GtkWidget *row, *box, *w; row = gtk_list_box_row_new (); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_set_border_width (GTK_CONTAINER (box), 12); gtk_container_add (GTK_CONTAINER (row), box); /* Icon */ w = gtk_image_new_from_icon_name ("image-missing", GTK_ICON_SIZE_MENU); gtk_widget_set_margin_end (w, 12); gtk_container_add (GTK_CONTAINER (box), w); self->priv->current_icon = w; /* Label */ w = gtk_label_new (""); gtk_container_add (GTK_CONTAINER (box), w); self->priv->current_label = w; w = gtk_switch_new (); gtk_widget_set_margin_top (w, 3); gtk_widget_set_margin_bottom (w, 3); gtk_widget_set_margin_end (w, 12); gtk_widget_set_valign (w, GTK_ALIGN_CENTER); gtk_box_pack_end (GTK_BOX (box), w, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (w), "state-set", G_CALLBACK (cc_sharing_networks_enable_network), self); self->priv->current_switch = w; g_object_set_data (G_OBJECT (w), "row", row); gtk_widget_show_all (box); return row; } static GtkWidget * cc_sharing_networks_new_no_network_row (CcSharingNetworks *self) { GtkWidget *row, *box, *w; row = gtk_list_box_row_new (); box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_container_set_border_width (GTK_CONTAINER (box), 12); gtk_container_add (GTK_CONTAINER (row), box); /* Label */ w = gtk_label_new (_("No networks selected for sharing")); gtk_widget_set_hexpand (w, TRUE); gtk_widget_set_halign (w, GTK_ALIGN_CENTER); gtk_style_context_add_class (gtk_widget_get_style_context (w), "dim-label"); gtk_container_add (GTK_CONTAINER (box), w); gtk_widget_show_all (box); return row; } static void cc_sharing_update_networks_box (CcSharingNetworks *self) { gboolean current_visible; const char *current_network; GList *children, *l; children = gtk_container_get_children (GTK_CONTAINER (self->priv->listbox)); for (l = children; l != NULL; l = l->next) { GtkWidget *row = l->data; if (row != self->priv->current_row && row != self->priv->no_network_row) gtk_widget_destroy (row); } g_list_free (children); current_network = gsd_sharing_get_current_network (self->priv->proxy); if (current_network != NULL && !g_str_equal (current_network, "")) { gboolean available; const char *carrier_type, *icon_name, *current_network_name; gtk_widget_show (self->priv->current_row); current_visible = TRUE; /* Network name */ g_object_set_data_full (G_OBJECT (self->priv->current_row), "uuid", g_strdup (current_network), g_free); current_network_name = gsd_sharing_get_current_network_name (self->priv->proxy); gtk_label_set_label (GTK_LABEL (self->priv->current_label), current_network_name); /* Icon */ carrier_type = gsd_sharing_get_carrier_type (self->priv->proxy); if (g_strcmp0 (carrier_type, "802-11-wireless") == 0) { icon_name = "network-wireless-signal-excellent-symbolic"; } else if (g_strcmp0 (carrier_type, "802-3-ethernet") == 0) { icon_name = "network-wired-symbolic"; } else { icon_name = "network-wired-symbolic"; } gtk_image_set_from_icon_name (GTK_IMAGE (self->priv->current_icon), icon_name, GTK_ICON_SIZE_SMALL_TOOLBAR); /* State */ available = gsd_sharing_get_sharing_status (self->priv->proxy) == GSD_SHARING_STATUS_AVAILABLE; gtk_widget_set_sensitive (self->priv->current_switch, available); //FIXME add a subtitle explaining why it's disabled } else { gtk_widget_hide (self->priv->current_row); current_visible = FALSE; } for (l = self->priv->networks; l != NULL; l = l->next) { CcSharingNetwork *net = l->data; GtkWidget *row; if (g_strcmp0 (net->uuid, current_network) == 0) { g_signal_handlers_block_by_func (self->priv->current_switch, cc_sharing_networks_enable_network, self); gtk_switch_set_state (GTK_SWITCH (self->priv->current_switch), TRUE); g_signal_handlers_unblock_by_func (self->priv->current_switch, cc_sharing_networks_enable_network, self); continue; } row = cc_sharing_networks_new_row (net->uuid, net->network_name, net->carrier_type, self); gtk_list_box_insert (GTK_LIST_BOX (self->priv->listbox), row, -1); } if (self->priv->networks == NULL && !current_visible) { gtk_widget_show (self->priv->no_network_row); } else { gtk_widget_hide (self->priv->no_network_row); } cc_sharing_networks_update_status (self); cc_list_box_adjust_scrolling (GTK_LIST_BOX (self->priv->listbox)); } static void current_network_changed (GObject *object, GParamSpec *pspec, CcSharingNetworks *self) { cc_sharing_update_networks (self); cc_sharing_update_networks_box (self); } static void cc_sharing_networks_constructed (GObject *object) { CcSharingNetworks *self; G_OBJECT_CLASS (cc_sharing_networks_parent_class)->constructed (object); self = CC_SHARING_NETWORKS (object); gtk_list_box_set_header_func (GTK_LIST_BOX (self->priv->listbox), cc_list_box_update_header_func, NULL, NULL); cc_list_box_setup_scrolling (GTK_LIST_BOX (self->priv->listbox), 3); self->priv->current_row = cc_sharing_networks_new_current_row (self); gtk_list_box_insert (GTK_LIST_BOX (self->priv->listbox), self->priv->current_row, -1); g_object_set_data (G_OBJECT (self), "switch", self->priv->current_switch); self->priv->no_network_row = cc_sharing_networks_new_no_network_row (self); gtk_list_box_insert (GTK_LIST_BOX (self->priv->listbox), self->priv->no_network_row, -1); cc_sharing_update_networks (self); cc_sharing_update_networks_box (self); g_signal_connect (self->priv->proxy, "notify::current-network", G_CALLBACK (current_network_changed), self); } static void cc_sharing_networks_init (CcSharingNetworks *self) { gtk_widget_init_template (GTK_WIDGET (self)); self->priv = cc_sharing_networks_get_instance_private (self); } GtkWidget * cc_sharing_networks_new (GDBusProxy *proxy, const char *service_name) { g_return_val_if_fail (GSD_IS_SHARING (proxy), NULL); g_return_val_if_fail (service_name != NULL, NULL); return GTK_WIDGET (g_object_new (CC_TYPE_SHARING_NETWORKS, "proxy", proxy, "service-name", service_name, NULL)); } static void cc_sharing_networks_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { CcSharingNetworks *self; self = CC_SHARING_NETWORKS (object); switch (prop_id) { case PROP_SERVICE_NAME: self->priv->service_name = g_value_dup_string (value); break; case PROP_PROXY: self->priv->proxy = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cc_sharing_networks_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CcSharingNetworks *self; self = CC_SHARING_NETWORKS (object); switch (prop_id) { case PROP_STATUS: g_value_set_uint (value, self->priv->status); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cc_sharing_networks_finalize (GObject *object) { CcSharingNetworks *self; g_return_if_fail (object != NULL); g_return_if_fail (CC_IS_SHARING_NETWORKS (object)); self = CC_SHARING_NETWORKS (object); g_return_if_fail (self->priv != NULL); g_clear_object (&self->priv->proxy); g_clear_pointer (&self->priv->service_name, g_free); if (self->priv->networks != NULL) { g_list_free_full (self->priv->networks, cc_sharing_network_free); self->priv->networks = NULL; } G_OBJECT_CLASS (cc_sharing_networks_parent_class)->finalize (object); } static void cc_sharing_networks_class_init (CcSharingNetworksClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->set_property = cc_sharing_networks_set_property; object_class->get_property = cc_sharing_networks_get_property; object_class->finalize = cc_sharing_networks_finalize; object_class->constructed = cc_sharing_networks_constructed; g_object_class_install_property (object_class, PROP_PROXY, g_param_spec_object ("proxy", "proxy", "proxy", GSD_TYPE_SHARING_PROXY, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_SERVICE_NAME, g_param_spec_string ("service-name", "service-name", "service-name", NULL, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); g_object_class_install_property (object_class, PROP_STATUS, g_param_spec_uint ("status", "status", "status", CC_SHARING_STATUS_UNSET, CC_SHARING_STATUS_ACTIVE + 1, CC_SHARING_STATUS_OFF, G_PARAM_READABLE)); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/sharing/networks.ui"); gtk_widget_class_bind_template_child_private (widget_class, CcSharingNetworks, listbox); } /* * vim: sw=2 ts=8 cindent noai bs=2 */