summaryrefslogtreecommitdiff
path: root/panels
diff options
context:
space:
mode:
Diffstat (limited to 'panels')
-rw-r--r--panels/network/Makefile.am6
-rw-r--r--panels/network/cc-wifi-panel.c631
-rw-r--r--panels/network/cc-wifi-panel.h34
-rw-r--r--panels/network/gnome-wifi-panel.desktop.in.in18
-rw-r--r--panels/network/network.gresource.xml4
-rw-r--r--panels/network/wifi.ui327
6 files changed, 1018 insertions, 2 deletions
diff --git a/panels/network/Makefile.am b/panels/network/Makefile.am
index ed206d7d8..fb2b891b9 100644
--- a/panels/network/Makefile.am
+++ b/panels/network/Makefile.am
@@ -39,7 +39,9 @@ libnetwork_la_SOURCES = \
network-dialogs.c \
network-dialogs.h \
cc-network-panel.c \
- cc-network-panel.h
+ cc-network-panel.h \
+ cc-wifi-panel.c \
+ cc-wifi-panel.h
libnetwork_la_LIBADD = $(PANEL_LIBS) $(NETWORK_PANEL_LIBS) $(NETWORK_MANAGER_LIBS) $(builddir)/connection-editor/libconnection-editor.la
@@ -54,7 +56,7 @@ cc-network-resources.h: network.gresource.xml $(resource_files)
@INTLTOOL_DESKTOP_RULE@
desktopdir = $(datadir)/applications
-desktop_in_files = gnome-network-panel.desktop.in
+desktop_in_files = gnome-network-panel.desktop.in gnome-wifi-panel.desktop.in
desktop_DATA = $(desktop_in_files:.desktop.in=.desktop)
CLEANFILES = $(desktop_in_files) $(desktop_DATA) $(BUILT_SOURCES)
diff --git a/panels/network/cc-wifi-panel.c b/panels/network/cc-wifi-panel.c
new file mode 100644
index 000000000..89fcc3db6
--- /dev/null
+++ b/panels/network/cc-wifi-panel.c
@@ -0,0 +1,631 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2017 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "cc-network-resources.h"
+#include "cc-wifi-panel.h"
+#include "net-device.h"
+#include "net-device-wifi.h"
+#include "network-dialogs.h"
+
+#include "shell/list-box-helper.h"
+
+#include <glib/gi18n.h>
+#include <NetworkManager.h>
+
+typedef enum
+{
+ OPERATION_NULL,
+ OPERATION_SHOW_DEVICE,
+ OPERATION_CREATE_WIFI,
+ OPERATION_CONNECT_HIDDEN,
+ OPERATION_CONNECT_8021X
+} CmdlineOperation;
+
+struct _CcWifiPanel
+{
+ CcPanel parent;
+
+ /* RFKill (Airplane Mode) */
+ GDBusProxy *rfkill_proxy;
+ GtkSwitch *rfkill_switch;
+ GtkWidget *rfkill_widget;
+
+ /* Main widgets */
+ GtkStack *center_stack;
+ GtkStack *header_stack;
+ GtkStack *main_stack;
+ GtkSizeGroup *sizegroup;
+ GtkStack *stack;
+
+ NMClient *client;
+
+ GPtrArray *devices;
+
+ GCancellable *cancellable;
+
+ /* Command-line arguments */
+ CmdlineOperation arg_operation;
+ gchar *arg_device;
+ gchar *arg_access_point;
+};
+
+static void rfkill_switch_notify_activate_cb (GtkSwitch *rfkill_switch,
+ GParamSpec *pspec,
+ CcWifiPanel *self);
+
+static void update_devices_names (CcWifiPanel *self);
+
+G_DEFINE_TYPE (CcWifiPanel, cc_wifi_panel, CC_TYPE_PANEL)
+
+enum
+{
+ PROP_0,
+ PROP_PARAMETERS,
+ N_PROPS
+};
+
+/* Auxiliary methods */
+
+static void
+add_wifi_device (CcWifiPanel *self,
+ NMDevice *device)
+{
+ GtkWidget *header_widget;
+ NetObject *net_device;
+
+ /* Only manage Wi-Fi devices */
+ if (!NM_IS_DEVICE_WIFI (device))
+ return;
+
+ /* Create the NetDevice */
+ net_device = g_object_new (NET_TYPE_DEVICE_WIFI,
+ "panel", self,
+ "removable", FALSE,
+ "cancellable", self->cancellable,
+ "client", self->client,
+ "nm-device", device,
+ "id", nm_device_get_udi (device),
+ NULL);
+
+ net_object_add_to_stack (net_device, self->stack, self->sizegroup);
+
+ /* And add to the header widgets */
+ header_widget = net_device_wifi_get_header_widget (NET_DEVICE_WIFI (net_device));
+
+ gtk_stack_add_named (self->header_stack, header_widget, net_object_get_id (net_device));
+
+ /* Setup custom title properties */
+ g_ptr_array_add (self->devices, net_device);
+
+ update_devices_names (self);
+}
+
+static void
+check_main_stack_page (CcWifiPanel *self)
+{
+ const gchar *nm_version;
+
+ nm_version = nm_client_get_version (self->client);
+
+ if (!nm_version)
+ gtk_stack_set_visible_child_name (self->main_stack, "nm-not-running");
+ else if (self->devices->len == 0)
+ gtk_stack_set_visible_child_name (self->main_stack, "no-wifi-devices");
+ else
+ gtk_stack_set_visible_child_name (self->main_stack, "wifi-connections");
+
+}
+
+static void
+load_wifi_devices (CcWifiPanel *self)
+{
+ const GPtrArray *devices;
+ guint i;
+
+ devices = nm_client_get_devices (self->client);
+
+ /* Cold-plug existing devices */
+ for (i = 0; i < devices->len; i++)
+ add_wifi_device (self, g_ptr_array_index (devices, i));
+
+ check_main_stack_page (self);
+}
+
+static inline gboolean
+get_cached_rfkill_property (CcWifiPanel *self,
+ const gchar *property)
+{
+ g_autoptr (GVariant) result;
+
+ result = g_dbus_proxy_get_cached_property (self->rfkill_proxy, property);
+ return result ? g_variant_get_boolean (result) : FALSE;
+}
+
+static void
+sync_airplane_mode_switch (CcWifiPanel *self)
+{
+ gboolean enabled, should_show, hw_enabled;
+
+ enabled = get_cached_rfkill_property (self, "HasAirplaneMode");
+ should_show = get_cached_rfkill_property (self, "ShouldShowAirplaneMode");
+
+ gtk_widget_set_visible (GTK_WIDGET (self->rfkill_widget), enabled && should_show);
+ if (!enabled || !should_show)
+ return;
+
+ enabled = get_cached_rfkill_property (self, "AirplaneMode");
+ hw_enabled = get_cached_rfkill_property (self, "HardwareAirplaneMode");
+
+ enabled |= hw_enabled;
+
+ if (enabled != gtk_switch_get_active (self->rfkill_switch))
+ {
+ g_signal_handlers_block_by_func (self->rfkill_switch,
+ rfkill_switch_notify_activate_cb,
+ self);
+ gtk_switch_set_active (self->rfkill_switch, enabled);
+ g_signal_handlers_unblock_by_func (self->rfkill_switch,
+ rfkill_switch_notify_activate_cb,
+ self);
+ }
+
+ gtk_widget_set_sensitive (GTK_WIDGET (self->rfkill_switch), !hw_enabled);
+
+ check_main_stack_page (self);
+}
+
+static void
+update_devices_names (CcWifiPanel *self)
+{
+ guint number_of_devices = self->devices->len;
+
+ if (number_of_devices == 1)
+ {
+ GtkWidget *title_widget;
+ NetObject *net_device;
+
+ net_device = g_ptr_array_index (self->devices, 0);
+ title_widget = net_device_wifi_get_title_widget (NET_DEVICE_WIFI (net_device));
+
+ gtk_stack_add_named (self->center_stack, title_widget, "single");
+ gtk_stack_set_visible_child_name (self->center_stack, "single");
+
+ net_object_set_title (net_device, _("Wi-Fi"));
+ }
+ else
+ {
+ GtkWidget *single_page_widget;
+ guint i;
+
+ for (i = 0; i < number_of_devices; i++)
+ {
+ NetObject *object;
+ NMDevice *device;
+
+ object = g_ptr_array_index (self->devices, i);
+ device = net_device_get_nm_device (NET_DEVICE (object));
+
+ net_object_set_title (object, nm_device_get_product (device));
+ }
+
+ /* Remove the widget at the "single" page */
+ single_page_widget = gtk_stack_get_child_by_name (self->center_stack, "single");
+
+ if (single_page_widget)
+ {
+ g_object_ref (single_page_widget);
+ gtk_container_remove (GTK_CONTAINER (self->center_stack), single_page_widget);
+ }
+
+ /* Show the stack-switcher page */
+ gtk_stack_set_visible_child_name (self->center_stack, "many");
+ }
+}
+
+/* Command-line arguments */
+
+static void
+reset_command_line_args (CcWifiPanel *self)
+{
+ self->arg_operation = OPERATION_NULL;
+ g_clear_pointer (&self->arg_device, g_free);
+ g_clear_pointer (&self->arg_access_point, g_free);
+}
+
+static gboolean
+handle_argv_for_device (CcWifiPanel *self,
+ NetObject *net_object)
+{
+ GtkWidget *toplevel;
+ NMDevice *device;
+ gboolean ret;
+
+ toplevel = cc_shell_get_toplevel (cc_panel_get_shell (CC_PANEL (self)));
+ device = net_device_get_nm_device (NET_DEVICE (net_object));
+ ret = FALSE;
+
+ if (self->arg_operation == OPERATION_CREATE_WIFI)
+ {
+ cc_network_panel_create_wifi_network (toplevel, self->client);
+ ret = TRUE;
+ }
+ else if (self->arg_operation == OPERATION_CONNECT_HIDDEN)
+ {
+ cc_network_panel_connect_to_hidden_network (toplevel, self->client);
+ ret = TRUE;
+ }
+ else if (g_str_equal (nm_object_get_path (NM_OBJECT (device)), self->arg_device))
+ {
+ if (self->arg_operation == OPERATION_CONNECT_8021X)
+ {
+ cc_network_panel_connect_to_8021x_network (toplevel,
+ self->client,
+ device,
+ self->arg_access_point);
+ ret = TRUE;
+ }
+ else if (self->arg_operation == OPERATION_SHOW_DEVICE)
+ {
+ gtk_stack_set_visible_child_name (self->stack, net_object_get_id (net_object));
+ ret = TRUE;
+ }
+ }
+
+ if (ret)
+ reset_command_line_args (self);
+
+ return ret;
+}
+
+static void
+handle_argv (CcWifiPanel *self)
+{
+ guint i;
+
+ if (self->arg_operation == OPERATION_NULL)
+ return;
+
+ for (i = 0; i < self->devices->len; i++)
+ {
+ if (handle_argv_for_device (self, g_ptr_array_index (self->devices, i)))
+ break;
+ }
+}
+
+static GPtrArray *
+variant_av_to_string_array (GVariant *array)
+{
+ GVariantIter iter;
+ GVariant *v;
+ GPtrArray *strv;
+ gsize count;
+
+ count = g_variant_iter_init (&iter, array);
+ strv = g_ptr_array_sized_new (count + 1);
+
+ while (g_variant_iter_next (&iter, "v", &v))
+ {
+ g_ptr_array_add (strv, (gpointer) g_variant_get_string (v, NULL));
+ g_variant_unref (v);
+ }
+
+ g_ptr_array_add (strv, NULL); /* NULL-terminate the strv data array */
+ return strv;
+}
+
+static gboolean
+verify_argv (CcWifiPanel *self,
+ const char **args)
+{
+ switch (self->arg_operation)
+ {
+ case OPERATION_CONNECT_8021X:
+ case OPERATION_SHOW_DEVICE:
+ if (!self->arg_device)
+ {
+ g_warning ("Operation %s requires an object path", args[0]);
+ return FALSE;
+ }
+ default:
+ return TRUE;
+ }
+}
+
+/* Callbacks */
+
+static void
+device_added_cb (NMClient *client,
+ NMDevice *device,
+ CcWifiPanel *self)
+{
+ add_wifi_device (self, device);
+ check_main_stack_page (self);
+}
+
+static void
+device_removed_cb (NMClient *client,
+ NMDevice *device,
+ CcWifiPanel *self)
+{
+ GtkWidget *child;
+ const gchar *id;
+ guint i;
+
+ if (!NM_IS_DEVICE_WIFI (device))
+ return;
+
+ id = nm_device_get_udi (device);
+
+ /* Destroy all stack pages related to this device */
+ child = gtk_stack_get_child_by_name (self->stack, id);
+ gtk_widget_destroy (child);
+
+ child = gtk_stack_get_child_by_name (self->header_stack, id);
+ gtk_widget_destroy (child);
+
+ /* Remove from the devices list */
+ for (i = 0; i < self->devices->len; i++)
+ {
+ NetObject *object = g_ptr_array_index (self->devices, 0);
+
+ if (g_strcmp0 (net_object_get_id (object), id) == 0)
+ {
+ g_ptr_array_remove (self->devices, object);
+ break;
+ }
+ }
+
+ /* Update the title widget */
+ update_devices_names (self);
+
+ /* And check which page should be visible */
+ check_main_stack_page (self);
+}
+
+static void
+rfkill_proxy_acquired_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CcWifiPanel *self = CC_WIFI_PANEL (user_data);
+ GError *error = NULL;
+
+ self->rfkill_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+
+ if (error)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_printerr ("Error creating rfkill proxy: %s\n", error->message);
+
+ g_error_free (error);
+ return;
+ }
+
+ sync_airplane_mode_switch (self);
+}
+
+static void
+rfkill_switch_notify_activate_cb (GtkSwitch *rfkill_switch,
+ GParamSpec *pspec,
+ CcWifiPanel *self)
+{
+ gboolean enable;
+
+ enable = gtk_switch_get_active (rfkill_switch);
+
+ g_dbus_proxy_call (self->rfkill_proxy,
+ "org.freedesktop.DBus.Properties.Set",
+ g_variant_new_parsed ("('org.gnome.SettingsDaemon.Rfkill',"
+ "'AirplaneMode', %v)",
+ g_variant_new_boolean (enable)),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ self->cancellable,
+ NULL,
+ NULL);
+}
+
+/* Overrides */
+
+static const gchar *
+cc_wifi_panel_get_help_uri (CcPanel *panel)
+{
+ return "help:gnome-help/wifi";
+}
+
+static GtkWidget *
+cc_wifi_panel_get_title_widget (CcPanel *panel)
+{
+ CcWifiPanel *self = CC_WIFI_PANEL (panel);
+
+ return GTK_WIDGET (self->center_stack);
+}
+
+static void
+cc_wifi_panel_constructed (GObject *object)
+{
+ CcWifiPanel *self;
+ CcShell *shell;
+
+ self = CC_WIFI_PANEL (object);
+ shell = cc_panel_get_shell (CC_PANEL (object));
+
+ G_OBJECT_CLASS (cc_wifi_panel_parent_class)->constructed (object);
+
+ cc_shell_embed_widget_in_header (shell, GTK_WIDGET (self->header_stack));
+}
+
+static void
+cc_wifi_panel_finalize (GObject *object)
+{
+ CcWifiPanel *self = (CcWifiPanel *)object;
+
+ g_cancellable_cancel (self->cancellable);
+
+ g_clear_object (&self->cancellable);
+ g_clear_object (&self->client);
+ g_clear_object (&self->rfkill_proxy);
+
+ g_clear_pointer (&self->devices, g_ptr_array_unref);
+
+ reset_command_line_args (self);
+
+ G_OBJECT_CLASS (cc_wifi_panel_parent_class)->finalize (object);
+}
+
+static void
+cc_wifi_panel_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+cc_wifi_panel_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcWifiPanel *self = CC_WIFI_PANEL (object);
+ GVariant *parameters;
+
+ switch (prop_id)
+ {
+ case PROP_PARAMETERS:
+ reset_command_line_args (self);
+
+ parameters = g_value_get_variant (value);
+
+ if (parameters)
+ {
+ GPtrArray *array;
+ const gchar **args;
+
+ array = variant_av_to_string_array (parameters);
+ args = (const gchar **) array->pdata;
+
+ if (args[0])
+ {
+ if (g_str_equal (args[0], "create-wifi"))
+ self->arg_operation = OPERATION_CREATE_WIFI;
+ else if (g_str_equal (args[0], "connect-hidden-wifi"))
+ self->arg_operation = OPERATION_CONNECT_HIDDEN;
+ else if (g_str_equal (args[0], "connect-8021x-wifi"))
+ self->arg_operation = OPERATION_CONNECT_8021X;
+ else if (g_str_equal (args[0], "show-device"))
+ self->arg_operation = OPERATION_SHOW_DEVICE;
+ else
+ self->arg_operation = OPERATION_NULL;
+ }
+
+ if (args[0] && args[1])
+ self->arg_device = g_strdup (args[1]);
+ if (args[0] && args[1] && args[2])
+ self->arg_access_point = g_strdup (args[2]);
+
+ if (!verify_argv (self, (const char **) args))
+ {
+ reset_command_line_args (self);
+ g_ptr_array_unref (array);
+ return;
+ }
+
+ g_ptr_array_unref (array);
+
+ handle_argv (self);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+cc_wifi_panel_class_init (CcWifiPanelClass *klass)
+{
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
+
+ panel_class->get_help_uri = cc_wifi_panel_get_help_uri;
+ panel_class->get_title_widget = cc_wifi_panel_get_title_widget;
+
+ object_class->constructed = cc_wifi_panel_constructed;
+ object_class->finalize = cc_wifi_panel_finalize;
+ object_class->get_property = cc_wifi_panel_get_property;
+ object_class->set_property = cc_wifi_panel_set_property;
+
+ gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/network/wifi.ui");
+
+ gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, center_stack);
+ gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, header_stack);
+ gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, main_stack);
+ gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, rfkill_switch);
+ gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, rfkill_widget);
+ gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, sizegroup);
+ gtk_widget_class_bind_template_child (widget_class, CcWifiPanel, stack);
+
+ gtk_widget_class_bind_template_callback (widget_class, rfkill_switch_notify_activate_cb);
+
+ g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
+}
+
+static void
+cc_wifi_panel_init (CcWifiPanel *self)
+{
+ g_resources_register (cc_network_get_resource ());
+
+ gtk_widget_init_template (GTK_WIDGET (self));
+
+ self->cancellable = g_cancellable_new ();
+ self->devices = g_ptr_array_new_with_free_func (g_object_unref);
+
+ /* Load NetworkManager */
+ self->client = nm_client_new (NULL, NULL);
+
+ g_signal_connect (self->client,
+ "device-added",
+ G_CALLBACK (device_added_cb),
+ self);
+
+ g_signal_connect (self->client,
+ "device-removed",
+ G_CALLBACK (device_removed_cb),
+ self);
+
+ /* Load Wi-Fi devices */
+ load_wifi_devices (self);
+
+ /* Acquire Airplane Mode proxy */
+ g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ "org.gnome.SettingsDaemon.Rfkill",
+ "/org/gnome/SettingsDaemon/Rfkill",
+ "org.gnome.SettingsDaemon.Rfkill",
+ self->cancellable,
+ rfkill_proxy_acquired_cb,
+ self);
+
+ /* Handle comment-line arguments after loading devices */
+ handle_argv (self);
+}
diff --git a/panels/network/cc-wifi-panel.h b/panels/network/cc-wifi-panel.h
new file mode 100644
index 000000000..43ad4bf96
--- /dev/null
+++ b/panels/network/cc-wifi-panel.h
@@ -0,0 +1,34 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2017 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef CC_WIFI_PANEL_H
+#define CC_WIFI_PANEL_H
+
+#include <shell/cc-panel.h>
+
+G_BEGIN_DECLS
+
+#define CC_TYPE_WIFI_PANEL (cc_wifi_panel_get_type())
+
+G_DECLARE_FINAL_TYPE (CcWifiPanel, cc_wifi_panel, CC, WIFI_PANEL, CcPanel)
+
+G_END_DECLS
+
+#endif /* CC_WIFI_PANEL_H */
+
diff --git a/panels/network/gnome-wifi-panel.desktop.in.in b/panels/network/gnome-wifi-panel.desktop.in.in
new file mode 100644
index 000000000..b37e650b8
--- /dev/null
+++ b/panels/network/gnome-wifi-panel.desktop.in.in
@@ -0,0 +1,18 @@
+[Desktop Entry]
+# Translators: Add soft hyphens to your translations so that the icon view won't clip your translations. See https://bugzilla.gnome.org/show_bug.cgi?id=647087#c13 for details
+_Name=Wi-Fi
+_Comment=Control how you connect to Wi-Fi networks
+Exec=gnome-control-center wifi
+Icon=network-wireless
+Terminal=false
+Type=Application
+NoDisplay=true
+StartupNotify=true
+Categories=GNOME;GTK;Settings;HardwareSettings;X-GNOME-Settings-Panel;X-GNOME-ConnectivitySettings;
+OnlyShowIn=GNOME;Unity;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gnome-control-center
+X-GNOME-Bugzilla-Component=network
+X-GNOME-Bugzilla-Version=@VERSION@
+# Translators: those are keywords for the wi-fi control-center panel
+_Keywords=Network;Wireless;Wi-Fi;Wifi;IP;LAN;Broadband;DNS;
diff --git a/panels/network/network.gresource.xml b/panels/network/network.gresource.xml
index 9dbb815fb..3532b7cae 100644
--- a/panels/network/network.gresource.xml
+++ b/panels/network/network.gresource.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/control-center/network">
+ <!-- Network panel -->
<file preprocess="xml-stripblanks">network.ui</file>
<file preprocess="xml-stripblanks">network-proxy.ui</file>
<file preprocess="xml-stripblanks">network-vpn.ui</file>
@@ -8,5 +9,8 @@
<file preprocess="xml-stripblanks">network-simple.ui</file>
<file preprocess="xml-stripblanks">network-mobile.ui</file>
<file preprocess="xml-stripblanks">network-ethernet.ui</file>
+
+ <!-- Wi-Fi panel -->
+ <file preprocess="xml-stripblanks">wifi.ui</file>
</gresource>
</gresources>
diff --git a/panels/network/wifi.ui b/panels/network/wifi.ui
new file mode 100644
index 000000000..7252748ce
--- /dev/null
+++ b/panels/network/wifi.ui
@@ -0,0 +1,327 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <requires lib="gtk+" version="3.20"/>
+ <template class="CcWifiPanel" parent="CcPanel">
+ <property name="visible">True</property>
+ <property name="can-focus">False</property>
+ <child>
+ <object class="GtkStack" id="main_stack">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="homogeneous">False</property>
+ <property name="transition_type">crossfade</property>
+
+ <!-- "No Wi-Fi Adapter" page -->
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="expand">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="orientation">vertical</property>
+ <property name="margin">18</property>
+ <property name="spacing">18</property>
+ <child type="center">
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">network-wireless-no-route-symbolic</property>
+ <property name="pixel_size">128</property>
+ <style>
+ <class name="dim-label" />
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="wrap">True</property>
+ <property name="label" translatable="yes">Make sure you have a Wi-Fi adapter plugged and turned on</property>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="wrap">True</property>
+ <property name="label" translatable="yes">No Wi-Fi Adapter Found</property>
+ <attributes>
+ <attribute name="scale" value="1.42" />
+ </attributes>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="name">no-wifi-devices</property>
+ </packing>
+ </child>
+
+ <!-- Wi-Fi connections and devices -->
+ <child>
+ <object class="GtkScrolledWindow">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="hscrollbar-policy">never</property>
+ <child>
+ <object class="GtkViewport">
+ <property name="visible">True</property>
+ <property name="shadow-type">none</property>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="border-width">24</property>
+ <property name="row-spacing">18</property>
+
+ <!-- Empty boxes to enforce 1/3 width for the main widgets -->
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ <property name="height">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ </object>
+ <packing>
+ <property name="left-attach">2</property>
+ <property name="top-attach">0</property>
+ <property name="height">3</property>
+ </packing>
+ </child>
+
+ <!-- Airplane Mode -->
+ <child>
+ <object class="GtkFrame" id="rfkill_widget">
+ <property name="visible">True</property>
+ <property name="vexpand">False</property>
+ <child>
+ <object class="GtkListBox">
+ <property name="visible">True</property>
+ <property name="selection-mode">none</property>
+ <child>
+ <object class="GtkListBoxRow">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="activatable">False</property>
+ <child>
+ <object class="GtkGrid">
+ <property name="visible">True</property>
+ <property name="border-width">12</property>
+ <property name="column-spacing">12</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Airplane Mode</property>
+ <property name="xalign">0.0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="label" translatable="yes">Disables Wi-Fi, Bluetooth and mobile broadband</property>
+ <property name="xalign">0.0</property>
+ <attributes>
+ <attribute name="scale" value="0.88"/>
+ </attributes>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ </object>
+ <packing>
+ <property name="left-attach">0</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="rfkill_switch">
+ <property name="visible">True</property>
+ <property name="can-focus">True</property>
+ <property name="valign">center</property>
+ <signal name="notify::active" handler="rfkill_switch_notify_activate_cb" object="CcWifiPanel" swapped="no" />
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ <property name="height">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">0</property>
+ </packing>
+ </child>
+
+ <!-- Visible Networks label & spinner -->
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="halign">start</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="label" translatable="yes">Visible Networks</property>
+ <property name="xalign">0.0</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSpinner" id="spinner">
+ <property name="hexpand">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">1</property>
+ </packing>
+ </child>
+
+ <!-- Stack with a listbox for each Wi-Fi device -->
+ <child>
+ <object class="GtkFrame">
+ <property name="visible">True</property>
+ <property name="valign">start</property>
+ <style>
+ <class name="view" />
+ </style>
+ <child>
+ <object class="GtkStack" id="stack">
+ <property name="visible">True</property>
+ <property name="hexpand">True</property>
+ <property name="vexpand">True</property>
+ <property name="transition_type">crossfade</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="left-attach">1</property>
+ <property name="top-attach">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="name">wifi-connections</property>
+ </packing>
+ </child>
+
+ <!-- "NetworkManager Not Running" page -->
+ <child>
+ <object class="GtkBox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="expand">True</property>
+ <property name="halign">center</property>
+ <property name="valign">center</property>
+ <property name="orientation">vertical</property>
+ <property name="margin">18</property>
+ <property name="spacing">18</property>
+ <child type="center">
+ <object class="GtkImage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">face-sad-symbolic</property>
+ <property name="pixel_size">128</property>
+ <style>
+ <class name="dim-label" />
+ </style>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="wrap">True</property>
+ <property name="label" translatable="yes">NetworkManager needs to be running</property>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="wrap">True</property>
+ <property name="label" translatable="yes">Oops, something has gone wrong. Please contact your software vendor.</property>
+ <attributes>
+ <attribute name="scale" value="1.42" />
+ </attributes>
+ </object>
+ <packing>
+ <property name="pack-type">end</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="name">nm-not-running</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </template>
+
+ <!-- End Stack -->
+ <object class="GtkStack" id="header_stack">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">end</property>
+ </object>
+
+ <!-- Center Widget -->
+ <object class="GtkStack" id="center_stack">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">center</property>
+ <child>
+ <object class="GtkStackSwitcher">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stack">stack</property>
+ </object>
+ <packing>
+ <property name="name">many</property>
+ </packing>
+ </child>
+ </object>
+
+ <object class="GtkSizeGroup" id="sizegroup" />
+</interface>