[network-manager-applet] connection-editor: Add VLAN connection support



commit af406e5f2e206cbd4f756d935466f31210a77669
Author: Dan Winship <danw gnome org>
Date:   Tue Jul 31 10:58:39 2012 -0400

    connection-editor: Add VLAN connection support

 src/connection-editor/Makefile.am            |    5 +-
 src/connection-editor/ce-page-vlan.ui        |  223 +++++++++
 src/connection-editor/new-connection.c       |    6 +
 src/connection-editor/nm-connection-editor.c |    4 +
 src/connection-editor/page-vlan.c            |  676 ++++++++++++++++++++++++++
 src/connection-editor/page-vlan.h            |   62 +++
 6 files changed, 975 insertions(+), 1 deletions(-)
---
diff --git a/src/connection-editor/Makefile.am b/src/connection-editor/Makefile.am
index 85a1492..08cbb7f 100644
--- a/src/connection-editor/Makefile.am
+++ b/src/connection-editor/Makefile.am
@@ -52,6 +52,8 @@ nm_connection_editor_SOURCES = \
 	page-vpn.c \
 	page-bond.h \
 	page-bond.c \
+	page-vlan.h \
+	page-vlan.c \
 	vpn-helpers.h \
 	vpn-helpers.c \
 	ip4-routes-dialog.h \
@@ -94,7 +96,8 @@ ui_DATA = \
 	ce-page-dsl.ui \
 	ce-page-mobile.ui \
 	ce-page-ppp.ui \
-	ce-ppp-auth-methods.ui
+	ce-ppp-auth-methods.ui \
+	ce-page-vlan.ui
 
 BUILT_SOURCES = nm-connection-editor-service-glue.h
 
diff --git a/src/connection-editor/ce-page-vlan.ui b/src/connection-editor/ce-page-vlan.ui
new file mode 100644
index 0000000..081c8de
--- /dev/null
+++ b/src/connection-editor/ce-page-vlan.ui
@@ -0,0 +1,223 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkAdjustment" id="adjustment2">
+    <property name="upper">100</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkAdjustment" id="vlan_id_adjustment">
+    <property name="upper">4095</property>
+    <property name="step_increment">1</property>
+    <property name="page_increment">10</property>
+  </object>
+  <object class="GtkTable" id="VlanPage">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="border_width">12</property>
+    <property name="n_rows">5</property>
+    <property name="n_columns">2</property>
+    <property name="column_spacing">12</property>
+    <property name="row_spacing">6</property>
+    <child>
+      <object class="GtkLabel" id="vlan_parent_label">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">_Parent interface:</property>
+        <property name="use_underline">True</property>
+      </object>
+      <packing>
+        <property name="x_options">GTK_FILL</property>
+        <property name="y_options"></property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="vlan_name_label">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">VLAN interface _name:</property>
+        <property name="use_underline">True</property>
+        <property name="mnemonic_widget">vlan_name_entry</property>
+      </object>
+      <packing>
+        <property name="top_attach">2</property>
+        <property name="bottom_attach">3</property>
+        <property name="x_options">GTK_FILL</property>
+        <property name="y_options"></property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="vlan_cloned_mac_label">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">_Cloned MAC address:</property>
+        <property name="use_underline">True</property>
+        <property name="mnemonic_widget">vlan_cloned_mac_entry</property>
+      </object>
+      <packing>
+        <property name="top_attach">3</property>
+        <property name="bottom_attach">4</property>
+        <property name="x_options">GTK_FILL</property>
+        <property name="y_options"></property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkEntry" id="vlan_cloned_mac_entry">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="invisible_char">â</property>
+        <property name="invisible_char_set">True</property>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="right_attach">2</property>
+        <property name="top_attach">3</property>
+        <property name="bottom_attach">4</property>
+        <property name="y_options">GTK_FILL</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="vlan_mtu_label">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">_MTU:</property>
+        <property name="use_underline">True</property>
+        <property name="mnemonic_widget">vlan_mtu</property>
+      </object>
+      <packing>
+        <property name="top_attach">4</property>
+        <property name="bottom_attach">5</property>
+        <property name="x_options">GTK_FILL</property>
+        <property name="y_options"></property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkHBox" id="hbox1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="spacing">6</property>
+        <child>
+          <object class="GtkSpinButton" id="vlan_mtu">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="invisible_char">â</property>
+            <property name="invisible_char_set">True</property>
+            <property name="adjustment">vlan_id_adjustment</property>
+            <property name="climb_rate">1</property>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkLabel" id="label29">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">bytes</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="right_attach">2</property>
+        <property name="top_attach">4</property>
+        <property name="bottom_attach">5</property>
+        <property name="x_options">GTK_FILL</property>
+        <property name="y_options">GTK_FILL</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkLabel" id="vlan_id_label">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="xalign">0</property>
+        <property name="label" translatable="yes">VLAN _id:</property>
+        <property name="use_underline">True</property>
+        <property name="mnemonic_widget">vlan_id_entry</property>
+      </object>
+      <packing>
+        <property name="top_attach">1</property>
+        <property name="bottom_attach">2</property>
+        <property name="y_options">GTK_FILL</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkAlignment" id="vlan_parent_alignment">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <placeholder/>
+        </child>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="right_attach">2</property>
+        <property name="y_options">GTK_FILL</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkSpinButton" id="vlan_id_entry">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="max_length">4</property>
+        <property name="invisible_char">â</property>
+        <property name="width_chars">4</property>
+        <property name="invisible_char_set">True</property>
+        <property name="adjustment">adjustment2</property>
+        <property name="numeric">True</property>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="right_attach">2</property>
+        <property name="top_attach">1</property>
+        <property name="bottom_attach">2</property>
+        <property name="y_options">GTK_FILL</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkEntry" id="vlan_name_entry">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="invisible_char">â</property>
+      </object>
+      <packing>
+        <property name="left_attach">1</property>
+        <property name="right_attach">2</property>
+        <property name="top_attach">2</property>
+        <property name="bottom_attach">3</property>
+        <property name="y_options">GTK_FILL</property>
+      </packing>
+    </child>
+  </object>
+  <object class="GtkListStore" id="vlan_name_type_model">
+    <columns>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes" comments="In context, this means &quot;concatenate the device name and the VLAN ID number together&quot;">Device name + number</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes" comments="LEAVE &quot;vlan&quot; UNTRANSLATED. In context, this means &quot;concatenate the string 'vlan' and the VLAN ID number together&quot;.">"vlan" + number</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkListStore" id="vlan_parent_model">
+    <columns>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+</interface>
diff --git a/src/connection-editor/new-connection.c b/src/connection-editor/new-connection.c
index 69fc0a6..4dc20bd 100644
--- a/src/connection-editor/new-connection.c
+++ b/src/connection-editor/new-connection.c
@@ -32,6 +32,7 @@
 #include "page-dsl.h"
 #include "page-infiniband.h"
 #include "page-bond.h"
+#include "page-vlan.h"
 #include "page-vpn.h"
 #include "vpn-helpers.h"
 
@@ -138,6 +139,11 @@ get_connection_type_list (void)
 	data.virtual = TRUE;
 	g_array_append_val (array, data);
 
+	data.name = _("VLAN");
+	data.new_connection_func = vlan_connection_new;
+	data.setting_type = NM_TYPE_SETTING_VLAN;
+	g_array_append_val (array, data);
+
 	/* Add "VPN" only if there are plugins */
 	vpn_plugins_hash = vpn_get_plugins (NULL);
 	have_vpn_plugins  = vpn_plugins_hash && g_hash_table_size (vpn_plugins_hash);
diff --git a/src/connection-editor/nm-connection-editor.c b/src/connection-editor/nm-connection-editor.c
index 802b41c..a17e365 100644
--- a/src/connection-editor/nm-connection-editor.c
+++ b/src/connection-editor/nm-connection-editor.c
@@ -69,6 +69,7 @@
 #include "page-wimax.h"
 #include "page-infiniband.h"
 #include "page-bond.h"
+#include "page-vlan.h"
 #include "ce-polkit-button.h"
 #include "vpn-helpers.h"
 
@@ -858,6 +859,9 @@ nm_connection_editor_set_connection (NMConnectionEditor *editor,
 	} else if (!strcmp (connection_type, NM_SETTING_BOND_SETTING_NAME)) {
 		if (!add_page (editor, ce_page_bond_new, editor->connection, error))
 			goto out;
+	} else if (!strcmp (connection_type, NM_SETTING_VLAN_SETTING_NAME)) {
+		if (!add_page (editor, ce_page_vlan_new, editor->connection, error))
+			goto out;
 	} else {
 		g_warning ("Unhandled setting type '%s'", connection_type);
 	}
diff --git a/src/connection-editor/page-vlan.c b/src/connection-editor/page-vlan.c
new file mode 100644
index 0000000..a2b478c
--- /dev/null
+++ b/src/connection-editor/page-vlan.c
@@ -0,0 +1,676 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Connection editor -- Connection editor for NetworkManager
+ *
+ * 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.
+ *
+ * (C) Copyright 2008 - 2011 Red Hat, Inc.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <net/ethernet.h>
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+
+#include <nm-setting-connection.h>
+#include <nm-setting-vlan.h>
+#include <nm-device-ethernet.h>
+#include <nm-utils.h>
+
+#include "page-vlan.h"
+
+G_DEFINE_TYPE (CEPageVlan, ce_page_vlan, CE_TYPE_PAGE)
+
+#define CE_PAGE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CE_TYPE_PAGE_VLAN, CEPageVlanPrivate))
+
+typedef struct {
+	char *label;
+	NMDevice *device;
+	NMConnection *connection;
+} VlanParent;
+
+typedef struct {
+	NMSettingVlan *setting;
+	NMSetting *s_hw;
+
+	VlanParent **parents;
+	char **parent_labels;
+
+	GtkComboBox *parent;
+	GtkEntry *parent_entry;
+	GtkSpinButton *id_entry;
+	GtkEntry *name_entry;
+	GtkEntry *cloned_mac;
+	GtkSpinButton *mtu;
+
+	char *last_parent;
+	int last_id;
+} CEPageVlanPrivate;
+
+static void
+vlan_private_init (CEPageVlan *self)
+{
+	CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+	GtkBuilder *builder;
+	GtkWidget *align;
+	GtkLabel *label;
+
+	builder = CE_PAGE (self)->builder;
+
+#if GTK_CHECK_VERSION(2,24,0)
+	priv->parent = GTK_COMBO_BOX (gtk_combo_box_text_new_with_entry ());
+	gtk_combo_box_set_entry_text_column (priv->parent, 0);
+#else
+	priv->parent = GTK_COMBO_BOX (gtk_combo_box_entry_new_text ());
+	gtk_combo_box_entry_set_text_column (GTK_COMBO_BOX_ENTRY (priv->parent), 0);
+#endif
+	priv->parent_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->parent)));
+
+	align = GTK_WIDGET (gtk_builder_get_object (builder, "vlan_parent_alignment"));
+	gtk_container_add (GTK_CONTAINER (align), GTK_WIDGET (priv->parent));
+	gtk_widget_show_all (GTK_WIDGET (priv->parent));
+
+	/* Set mnemonic widget for parent label */
+	label = GTK_LABEL (GTK_WIDGET (gtk_builder_get_object (builder, "vlan_parent_label")));
+	gtk_label_set_mnemonic_widget (label, GTK_WIDGET (priv->parent));
+
+	priv->id_entry = GTK_SPIN_BUTTON (GTK_WIDGET (gtk_builder_get_object (builder, "vlan_id_entry")));
+	priv->name_entry = GTK_ENTRY (GTK_WIDGET (gtk_builder_get_object (builder, "vlan_name_entry")));
+	priv->cloned_mac = GTK_ENTRY (GTK_WIDGET (gtk_builder_get_object (builder, "vlan_cloned_mac_entry")));
+	priv->mtu = GTK_SPIN_BUTTON (GTK_WIDGET (gtk_builder_get_object (builder, "vlan_mtu")));
+}
+
+static void
+stuff_changed (GtkWidget *w, gpointer user_data)
+{
+	ce_page_changed (CE_PAGE (user_data));
+}
+
+static void name_changed (GtkWidget *widget, gpointer user_data);
+
+static void
+sync_iface (CEPageVlan *self, GtkEntry *changed_entry)
+{
+	CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+	const char *iface, *iface_end, *parent_text;
+	char *new_iface, *end;
+	int iface_id, iface_len, parent_iface_len, id;
+	gboolean vlan_style_name;
+
+	iface = gtk_entry_get_text (priv->name_entry);
+	if (!*iface)
+		return;
+
+	if (g_str_has_prefix (iface, "vlan")) {
+		iface_end = iface + 4;
+		iface_id = strtoul (iface_end, &end, 10);
+		vlan_style_name = TRUE;
+	} else if ((iface_end = strchr (iface, '.'))) {
+		iface_id = strtoul (iface_end + 1, &end, 10);
+		vlan_style_name = FALSE;
+	} else
+		return;
+	if (*end)
+		return;
+	iface_len = iface_end - iface;
+
+	parent_text = gtk_entry_get_text (priv->parent_entry);
+	parent_iface_len = strcspn (parent_text, " ");
+	id = gtk_spin_button_get_value_as_int (priv->id_entry);
+
+	if (changed_entry == priv->name_entry) {
+		/* The user changed the interface name. If it now matches
+		 * parent and id, then update the last_* members, so we'll
+		 * start keeping it in sync again.
+		 */
+		if (iface_id == id)
+			priv->last_id = iface_id;
+		else
+			priv->last_id = -1;
+
+		g_free (priv->last_parent);
+		if (   iface_len == parent_iface_len
+		    && !strncmp (iface, parent_text, iface_len))
+			priv->last_parent = g_strndup (iface, iface_len);
+		else
+			priv->last_parent = NULL;
+		return;
+	}
+
+	/* The user changed the parent or ID; if the previous parent and
+	 * ID matched the interface name, then update the interface name
+	 * to match the new one as well.
+	 */
+	if (iface_id != priv->last_id)
+		return;
+	if (   !vlan_style_name
+	    && priv->last_parent
+	    && strncmp (iface, priv->last_parent, iface_len) != 0)
+		return;
+
+	if (vlan_style_name) {
+		new_iface = g_strdup_printf ("vlan%d", id);
+	} else if (changed_entry == priv->parent_entry) {
+		new_iface = g_strdup_printf ("%.*s.%d",
+		                             parent_iface_len,
+		                             parent_text, id);
+	} else {
+		new_iface = g_strdup_printf ("%.*s.%d", iface_len, iface, id);
+	}
+
+	g_signal_handlers_block_by_func (priv->name_entry, G_CALLBACK (name_changed), self);
+	gtk_entry_set_text (priv->name_entry, new_iface);
+	g_signal_handlers_unblock_by_func (priv->name_entry, G_CALLBACK (name_changed), self);
+
+	g_free (new_iface);
+
+	if (changed_entry == priv->parent_entry) {
+		g_free (priv->last_parent);
+		priv->last_parent = g_strndup (parent_text, parent_iface_len);
+	} else if (changed_entry == GTK_ENTRY (priv->id_entry))
+		priv->last_id = id;
+}
+
+static void
+parent_changed (GtkWidget *widget, gpointer user_data)
+{
+	CEPageVlan *self = user_data;
+	CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+	int parent_id;
+
+	parent_id = gtk_combo_box_get_active (GTK_COMBO_BOX (priv->parent));
+	if (parent_id > -1 && priv->parents[parent_id]->device != NULL) {
+		gtk_widget_set_sensitive (GTK_WIDGET (priv->cloned_mac), TRUE);
+		gtk_widget_set_sensitive (GTK_WIDGET (priv->mtu), TRUE);
+	} else {
+		gtk_widget_set_sensitive (GTK_WIDGET (priv->cloned_mac), FALSE);
+		gtk_entry_set_text (priv->cloned_mac, "");
+		gtk_widget_set_sensitive (GTK_WIDGET (priv->mtu), FALSE);
+		gtk_spin_button_set_value (priv->mtu, 1500);
+	}
+
+	sync_iface (self, priv->parent_entry);
+	ce_page_changed (CE_PAGE (self));
+}
+
+static void
+name_changed (GtkWidget *w, gpointer user_data)
+{
+	CEPageVlan *self = user_data;
+	CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+
+	sync_iface (self, priv->name_entry);
+	ce_page_changed (CE_PAGE (self));
+}
+
+static void
+id_changed (GtkWidget *w, gpointer user_data)
+{
+	CEPageVlan *self = user_data;
+	CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+
+	sync_iface (self, GTK_ENTRY (priv->id_entry));
+	ce_page_changed (CE_PAGE (self));
+}
+
+static int
+sort_parents (gconstpointer a, gconstpointer b)
+{
+	VlanParent *pa = *(VlanParent **)a;
+	VlanParent *pb = *(VlanParent **)b;
+
+	return strcmp (pa->label, pb->label);
+}
+
+static GSList *
+get_vlan_devices (CEPageVlan *self)
+{
+	const GPtrArray *devices_array;
+	GSList *devices;
+	NMDevice *device;
+	int i;
+	
+	devices_array = nm_client_get_devices (CE_PAGE (self)->client);
+	devices = NULL;
+	for (i = 0; devices_array && (i < devices_array->len); i++) {
+		device = devices_array->pdata[i];
+
+		/* FIXME: this supported-device-types logic belongs in NM somewhere. */
+		if (!NM_IS_DEVICE_ETHERNET (device))
+			continue;
+
+		devices = g_slist_prepend (devices, device);
+	}
+
+	return devices;
+}
+
+static void
+build_vlan_parent_list (CEPageVlan *self, GSList *devices)
+{
+	CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+	GSList *connections, *c_iter, *d_iter;
+	GPtrArray *parents;
+	VlanParent *parent;
+	NMDevice *device;
+	const char *iface, *mac, *id;
+	int i;
+
+	parents = g_ptr_array_new ();
+
+	/* Devices with no L2 configuration can spawn VLANs directly. At the
+	 * moment, this means just Ethernet.
+	 */
+	for (d_iter = devices; d_iter; d_iter = d_iter->next) {
+		device = d_iter->data;
+
+		if (!NM_IS_DEVICE_ETHERNET (device))
+			continue;
+
+		parent = g_slice_new (VlanParent);
+		parent->device = device;
+		parent->connection = NULL;
+
+		iface = nm_device_get_iface (device);
+		mac = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device));
+		parent->label = g_strdup_printf ("%s (%s)", iface, mac);
+
+		g_ptr_array_add (parents, parent);
+	}
+
+	/* Otherwise, VLANs have to be built on top of configured connections */
+	connections = nm_remote_settings_list_connections (CE_PAGE (self)->settings);
+	for (c_iter = connections; c_iter; c_iter = c_iter->next) {
+		NMConnection *candidate = c_iter->data;
+		NMSettingConnection *s_con = nm_connection_get_setting_connection (candidate);
+
+		if (nm_setting_connection_get_master (s_con))
+			continue;
+
+		for (d_iter = devices; d_iter; d_iter = d_iter->next) {
+			device = d_iter->data;
+
+			if (nm_device_connection_valid (device, candidate)) {
+				parent = g_slice_new (VlanParent);
+				parent->device = device;
+				parent->connection = candidate;
+
+				iface = nm_device_get_iface (device);
+				id = nm_setting_connection_get_id (s_con);
+
+				parent->label = g_strdup_printf ("%s (%s)", iface, id);
+				g_ptr_array_add (parents, parent);
+				/* no break here; the connection may apply to multiple devices */
+			}
+		}
+	}
+
+	g_slist_free (connections);
+
+	g_ptr_array_sort (parents, sort_parents);
+	g_ptr_array_add (parents, NULL);
+
+	priv->parent_labels = g_new (char *, parents->len);
+	priv->parents = (VlanParent **)g_ptr_array_free (parents, FALSE);
+
+	for (i = 0; priv->parents[i]; i++)
+		priv->parent_labels[i] = priv->parents[i]->label;
+	priv->parent_labels[i] = NULL;
+}
+
+static void
+populate_ui (CEPageVlan *self)
+{
+	CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+	GSList *devices, *d_iter;
+	NMConnection *parent_connection = NULL;
+	NMDevice *device, *parent_device = NULL;
+	const char *parent, *iface, *current_parent;
+	int i, mtu_def, mtu_val;
+
+	devices = get_vlan_devices (self);
+
+	/* Parent */
+	build_vlan_parent_list (self, devices);
+
+	parent = nm_setting_vlan_get_parent (priv->setting);
+	if (parent) {
+		/* UUID? */
+		parent_connection = (NMConnection *)nm_remote_settings_get_connection_by_uuid (CE_PAGE (self)->settings, parent);
+		if (!parent_connection) {
+			/* Interface name? */
+			for (d_iter = devices; d_iter; d_iter = d_iter->next) {
+				device = d_iter->data;
+
+				if (!g_strcmp0 (parent, nm_device_get_iface (device))) {
+					parent_device = device;
+					break;
+				}
+			}
+		}
+	}
+
+	/* If NMSettingVlan:parent didn't indicate a device, but we have a
+	 * wired setting, figure out the device from that.
+	 */
+	if (priv->s_hw && !parent_device) {
+		const GByteArray *mac;
+		const char *device_mac_str;
+		char *mac_str;
+
+		if (NM_IS_SETTING_WIRED (priv->s_hw))
+			mac = nm_setting_wired_get_mac_address (NM_SETTING_WIRED (priv->s_hw));
+		else
+			mac = NULL;
+
+		if (mac) {
+			mac_str = nm_utils_hwaddr_ntoa (mac->data, ARPHRD_ETHER);
+
+			for (d_iter = devices; d_iter; d_iter = d_iter->next) {
+				device = d_iter->data;
+
+				if (NM_IS_DEVICE_ETHERNET (device))
+					device_mac_str = nm_device_ethernet_get_permanent_hw_address (NM_DEVICE_ETHERNET (device));
+				else
+					device_mac_str = NULL;
+
+				if (!g_strcmp0 (mac_str, device_mac_str)) {
+					parent_device = device;
+					break;
+				}
+			}
+		}
+	}
+
+	current_parent = parent;
+	if (parent_device || parent_connection) {
+		for (i = 0; priv->parents[i]; i++) {
+			if (parent_device && parent_device != priv->parents[i]->device)
+				continue;
+			if (parent_connection && parent_connection != priv->parents[i]->connection)
+				continue;
+
+			current_parent = priv->parents[i]->label;
+			break;
+		}
+	}
+	ce_page_setup_mac_combo (CE_PAGE (self), priv->parent, current_parent, priv->parent_labels);
+	g_signal_connect (priv->parent, "changed", G_CALLBACK (parent_changed), self);
+
+	if (current_parent)
+		priv->last_parent = g_strndup (current_parent, strcspn (current_parent, " "));
+
+	/* Name */
+	iface = nm_setting_vlan_get_interface_name (priv->setting);
+	if (iface)
+		gtk_entry_set_text (priv->name_entry, iface);
+	g_signal_connect (priv->name_entry, "changed", G_CALLBACK (name_changed), self);
+
+	/* ID */
+	priv->last_id = nm_setting_vlan_get_id (priv->setting);
+	gtk_spin_button_set_value (priv->id_entry, priv->last_id);
+	g_signal_connect (priv->id_entry, "value-changed", G_CALLBACK (id_changed), self);
+
+	/* Cloned MAC address */
+	if (NM_IS_SETTING_WIRED (priv->s_hw)) {
+		ce_page_mac_to_entry (nm_setting_wired_get_cloned_mac_address (NM_SETTING_WIRED (priv->s_hw)),
+		                      ARPHRD_ETHER, priv->cloned_mac);
+	}
+	g_signal_connect (priv->cloned_mac, "changed", G_CALLBACK (stuff_changed), self);
+
+	/* MTU */
+	if (NM_IS_SETTING_WIRED (priv->s_hw)) {
+		mtu_def = ce_get_property_default (priv->s_hw, NM_SETTING_WIRED_MTU);
+		mtu_val = nm_setting_wired_get_mtu (NM_SETTING_WIRED (priv->s_hw));
+	} else {
+		mtu_def = mtu_val = 1500;
+	}
+	g_signal_connect (priv->mtu, "output",
+	                  G_CALLBACK (ce_spin_output_with_default),
+	                  GINT_TO_POINTER (mtu_def));
+
+	gtk_spin_button_set_value (priv->mtu, (gdouble) mtu_val);
+	g_signal_connect (priv->mtu, "value-changed", G_CALLBACK (stuff_changed), self);
+
+	g_slist_free (devices);
+}
+
+static void
+finish_setup (CEPageVlan *self, gpointer unused, GError *error, gpointer user_data)
+{
+	if (error)
+		return;
+
+	populate_ui (self);
+}
+
+CEPage *
+ce_page_vlan_new (NMConnection *connection,
+                  GtkWindow *parent_window,
+                  NMClient *client,
+                  NMRemoteSettings *settings,
+                  const char **out_secrets_setting_name,
+                  GError **error)
+{
+	CEPageVlan *self;
+	CEPageVlanPrivate *priv;
+
+	self = CE_PAGE_VLAN (ce_page_new (CE_TYPE_PAGE_VLAN,
+	                                  connection,
+	                                  parent_window,
+	                                  client,
+	                                  settings,
+	                                  UIDIR "/ce-page-vlan.ui",
+	                                  "VlanPage",
+	                                  _("VLAN")));
+	if (!self) {
+		g_set_error_literal (error, NMA_ERROR, NMA_ERROR_GENERIC, _("Could not load vlan user interface."));
+		return NULL;
+	}
+
+	vlan_private_init (self);
+	priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+
+	priv->setting = nm_connection_get_setting_vlan (connection);
+	if (!priv->setting) {
+		priv->setting = NM_SETTING_VLAN (nm_setting_vlan_new ());
+		nm_connection_add_setting (connection, NM_SETTING (priv->setting));
+	}
+	priv->s_hw = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
+
+	g_signal_connect (self, "initialized", G_CALLBACK (finish_setup), NULL);
+
+	return CE_PAGE (self);
+}
+
+static void
+ui_to_setting (CEPageVlan *self)
+{
+	CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+	NMConnection *connection = CE_PAGE (self)->connection;
+	NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
+	GByteArray *cloned_mac = NULL;
+	VlanParent *parent = NULL;
+	int parent_id, vid;
+	const char *parent_iface = NULL, *parent_uuid = NULL;
+	const char *slave_type;
+	const char *iface;
+	GType hwtype;
+	gboolean mtu_set;
+	int mtu;
+
+	parent_id = gtk_combo_box_get_active (GTK_COMBO_BOX (priv->parent));
+	if (parent_id == -1)
+		parent_iface = gtk_entry_get_text (priv->parent_entry);
+	else {
+		parent = priv->parents[parent_id];
+		if (parent->connection)
+			parent_uuid = nm_connection_get_uuid (parent->connection);
+		if (parent->device)
+			parent_iface = nm_device_get_iface (parent->device);
+	}
+
+	g_assert (parent_uuid != NULL || parent_iface != NULL);
+
+	slave_type = nm_setting_connection_get_slave_type (s_con);
+	if (parent_uuid) {
+		/* Update NMSettingConnection:master if it's set, but don't
+		 * set it if it's not.
+		 */
+		if (!g_strcmp0 (slave_type, NM_SETTING_VLAN_SETTING_NAME)) {
+			g_object_set (s_con,
+			              NM_SETTING_CONNECTION_MASTER, parent_uuid,
+			              NULL);
+		}
+	} else if (!g_strcmp0 (slave_type, NM_SETTING_VLAN_SETTING_NAME)) {
+		g_object_set (s_con,
+		              NM_SETTING_CONNECTION_MASTER, NULL,
+		              NM_SETTING_CONNECTION_SLAVE_TYPE, NULL,
+		              NULL);
+	}
+
+	if (parent && NM_IS_DEVICE_ETHERNET (parent->device))
+		hwtype = NM_TYPE_SETTING_WIRED;
+	else
+		hwtype = G_TYPE_NONE;
+
+	if (priv->s_hw && G_OBJECT_TYPE (priv->s_hw) != hwtype) {
+		nm_connection_remove_setting (connection, G_OBJECT_TYPE (priv->s_hw));
+		priv->s_hw = NULL;
+	}
+
+	iface = gtk_entry_get_text (priv->name_entry);
+	vid = gtk_spin_button_get_value_as_int (priv->id_entry);
+
+	g_object_set (priv->setting,
+	              NM_SETTING_VLAN_PARENT, parent_uuid ? parent_uuid : parent_iface,
+	              NM_SETTING_VLAN_INTERFACE_NAME, iface,
+	              NM_SETTING_VLAN_ID, vid,
+	              NULL);
+
+	if (hwtype != G_TYPE_NONE) {
+		cloned_mac = ce_page_entry_to_mac (priv->cloned_mac, ARPHRD_ETHER, NULL);
+		mtu_set = g_ascii_isdigit (*gtk_entry_get_text (GTK_ENTRY (priv->mtu)));
+		mtu = gtk_spin_button_get_value_as_int (priv->mtu);
+
+		if (cloned_mac || mtu_set) {
+			if (!priv->s_hw) {
+				priv->s_hw = g_object_new (hwtype, NULL);
+				nm_connection_add_setting (connection, priv->s_hw);
+			}
+
+			g_object_set (priv->s_hw,
+			              NM_SETTING_WIRED_CLONED_MAC_ADDRESS, cloned_mac,
+			              NM_SETTING_WIRED_MTU, (guint32) mtu,
+			              NULL);
+
+			if (cloned_mac)
+				g_byte_array_free (cloned_mac, TRUE);
+		} else if (priv->s_hw) {
+			nm_connection_remove_setting (connection, G_OBJECT_TYPE (priv->s_hw));
+			priv->s_hw = NULL;
+		}
+	}
+}
+
+static gboolean
+validate (CEPage *page, NMConnection *connection, GError **error)
+{
+	CEPageVlan *self = CE_PAGE_VLAN (page);
+	CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+	gboolean invalid = FALSE;
+	GByteArray *ignore;
+	int parent_id;
+	const char *parent_iface;
+
+	parent_id = gtk_combo_box_get_active (GTK_COMBO_BOX (priv->parent));
+	if (parent_id == -1) {
+		parent_iface = gtk_entry_get_text (priv->parent_entry);
+		if (!nm_utils_iface_valid_name (parent_iface))
+			return FALSE;
+	}
+
+	ignore = ce_page_entry_to_mac (priv->cloned_mac, ARPHRD_ETHER, &invalid);
+	if (invalid)
+		return FALSE;
+	if (ignore)
+		g_byte_array_free (ignore, TRUE);
+
+	ui_to_setting (self);
+
+	if (   priv->s_hw
+	    && !nm_setting_verify (priv->s_hw, NULL, error))
+		return FALSE;
+
+	return nm_setting_verify (NM_SETTING (priv->setting), NULL, error);
+}
+
+static void
+finalize (GObject *object)
+{
+	CEPageVlan *self = CE_PAGE_VLAN (object);
+	CEPageVlanPrivate *priv = CE_PAGE_VLAN_GET_PRIVATE (self);
+	int i;
+
+	g_free (priv->last_parent);
+
+	for (i = 0; priv->parents[i]; i++)
+		g_slice_free (VlanParent, priv->parents[i]);
+	g_free (priv->parents);
+
+	G_OBJECT_CLASS (ce_page_vlan_parent_class)->finalize (object);
+}
+
+static void
+ce_page_vlan_init (CEPageVlan *self)
+{
+}
+
+static void
+ce_page_vlan_class_init (CEPageVlanClass *vlan_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (vlan_class);
+	CEPageClass *parent_class = CE_PAGE_CLASS (vlan_class);
+
+	g_type_class_add_private (object_class, sizeof (CEPageVlanPrivate));
+
+	/* virtual methods */
+	object_class->finalize = finalize;
+	parent_class->validate = validate;
+}
+
+
+void
+vlan_connection_new (GtkWindow *parent,
+                     const char *detail,
+                     NMRemoteSettings *settings,
+                     PageNewConnectionResultFunc result_func,
+                     gpointer user_data)
+{
+	NMConnection *connection;
+
+	connection = ce_page_new_connection (_("VLAN connection %d"),
+	                                     NM_SETTING_VLAN_SETTING_NAME,
+	                                     TRUE,
+	                                     settings,
+	                                     user_data);
+	nm_connection_add_setting (connection, nm_setting_vlan_new ());
+
+	(*result_func) (connection, FALSE, NULL, user_data);
+}
+
diff --git a/src/connection-editor/page-vlan.h b/src/connection-editor/page-vlan.h
new file mode 100644
index 0000000..0e66a06
--- /dev/null
+++ b/src/connection-editor/page-vlan.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Connection editor -- Connection editor for NetworkManager
+ *
+ * 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.
+ *
+ * (C) Copyright 2008 - 2011 Red Hat, Inc.
+ */
+
+#ifndef __PAGE_VLAN_H__
+#define __PAGE_VLAN_H__
+
+#include <nm-connection.h>
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "ce-page.h"
+
+#define CE_TYPE_PAGE_VLAN            (ce_page_vlan_get_type ())
+#define CE_PAGE_VLAN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), CE_TYPE_PAGE_VLAN, CEPageVlan))
+#define CE_PAGE_VLAN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), CE_TYPE_PAGE_VLAN, CEPageVlanClass))
+#define CE_IS_PAGE_VLAN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CE_TYPE_PAGE_VLAN))
+#define CE_IS_PAGE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), CE_TYPE_PAGE_VLAN))
+#define CE_PAGE_VLAN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), CE_TYPE_PAGE_VLAN, CEPageVlanClass))
+
+typedef struct {
+	CEPage parent;
+} CEPageVlan;
+
+typedef struct {
+	CEPageClass parent;
+} CEPageVlanClass;
+
+GType ce_page_vlan_get_type (void);
+
+CEPage *ce_page_vlan_new (NMConnection *connection,
+                          GtkWindow *parent,
+                          NMClient *client,
+                          NMRemoteSettings *settings,
+                          const char **out_secrets_setting_name,
+                          GError **error);
+
+void vlan_connection_new (GtkWindow *parent,
+                          const char *detail,
+                          NMRemoteSettings *settings,
+                          PageNewConnectionResultFunc result_func,
+                          gpointer user_data);
+
+#endif  /* __PAGE_VLAN_H__ */
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]