summaryrefslogtreecommitdiff
path: root/clients/tui
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2014-07-22 17:55:13 -0400
committerDan Winship <danw@gnome.org>2014-07-30 15:56:19 -0400
commit3d25d70461b7ed07c8b998620975daf149dc60d0 (patch)
treed22443121a8aeed393e6f3f341f335242475fff1 /clients/tui
parent25dac5760b66794a8bfc559fa84a9db2c53b9eef (diff)
downloadNetworkManager-3d25d70461b7ed07c8b998620975daf149dc60d0.tar.gz
clients: reorganize source tree, put all the installed clients together
Create a new clients/ subdirectory at the top level, and move cli/ and tui/ into it, as well as nm-online.c (which was previously in test/, which made no sense). cli/ was split into two subdirectories, src/ and completion/. While this does simplify things (given that the completion file and the binary both need to be named "nmcli"), it bloats the source tree, and we can work around it by just renaming the completion file at install time. Then we can combine the two directories into one and just have it all under clients/cli/.
Diffstat (limited to 'clients/tui')
-rw-r--r--clients/tui/Makefile.am125
-rw-r--r--clients/tui/newt/Makefile.am53
-rw-r--r--clients/tui/newt/nmt-newt-button-box.c386
-rw-r--r--clients/tui/newt/nmt-newt-button-box.h65
-rw-r--r--clients/tui/newt/nmt-newt-button.c261
-rw-r--r--clients/tui/newt/nmt-newt-button.h53
-rw-r--r--clients/tui/newt/nmt-newt-checkbox.c237
-rw-r--r--clients/tui/newt/nmt-newt-checkbox.h53
-rw-r--r--clients/tui/newt/nmt-newt-component.c309
-rw-r--r--clients/tui/newt/nmt-newt-component.h57
-rw-r--r--clients/tui/newt/nmt-newt-container.c252
-rw-r--r--clients/tui/newt/nmt-newt-container.h61
-rw-r--r--clients/tui/newt/nmt-newt-entry-numeric.c215
-rw-r--r--clients/tui/newt/nmt-newt-entry-numeric.h51
-rw-r--r--clients/tui/newt/nmt-newt-entry.c540
-rw-r--r--clients/tui/newt/nmt-newt-entry.h74
-rw-r--r--clients/tui/newt/nmt-newt-form.c753
-rw-r--r--clients/tui/newt/nmt-newt-form.h66
-rw-r--r--clients/tui/newt/nmt-newt-grid.c472
-rw-r--r--clients/tui/newt/nmt-newt-grid.h72
-rw-r--r--clients/tui/newt/nmt-newt-hacks.c103
-rw-r--r--clients/tui/newt/nmt-newt-hacks.h42
-rw-r--r--clients/tui/newt/nmt-newt-label.c326
-rw-r--r--clients/tui/newt/nmt-newt-label.h66
-rw-r--r--clients/tui/newt/nmt-newt-listbox.c548
-rw-r--r--clients/tui/newt/nmt-newt-listbox.h71
-rw-r--r--clients/tui/newt/nmt-newt-popup.c345
-rw-r--r--clients/tui/newt/nmt-newt-popup.h62
-rw-r--r--clients/tui/newt/nmt-newt-section.c409
-rw-r--r--clients/tui/newt/nmt-newt-section.h57
-rw-r--r--clients/tui/newt/nmt-newt-separator.c66
-rw-r--r--clients/tui/newt/nmt-newt-separator.h49
-rw-r--r--clients/tui/newt/nmt-newt-stack.c368
-rw-r--r--clients/tui/newt/nmt-newt-stack.h61
-rw-r--r--clients/tui/newt/nmt-newt-textbox.c295
-rw-r--r--clients/tui/newt/nmt-newt-textbox.h59
-rw-r--r--clients/tui/newt/nmt-newt-toggle-button.c237
-rw-r--r--clients/tui/newt/nmt-newt-toggle-button.h54
-rw-r--r--clients/tui/newt/nmt-newt-types.h50
-rw-r--r--clients/tui/newt/nmt-newt-utils.c392
-rw-r--r--clients/tui/newt/nmt-newt-utils.h53
-rw-r--r--clients/tui/newt/nmt-newt-widget.c668
-rw-r--r--clients/tui/newt/nmt-newt-widget.h118
-rw-r--r--clients/tui/newt/nmt-newt.h39
-rw-r--r--clients/tui/nm-editor-bindings.c1676
-rw-r--r--clients/tui/nm-editor-bindings.h99
-rw-r--r--clients/tui/nm-editor-utils.c423
-rw-r--r--clients/tui/nm-editor-utils.h44
-rw-r--r--clients/tui/nmt-address-list.c286
-rw-r--r--clients/tui/nmt-address-list.h57
-rw-r--r--clients/tui/nmt-connect-connection-list.c741
-rw-r--r--clients/tui/nmt-connect-connection-list.h61
-rw-r--r--clients/tui/nmt-device-entry.c594
-rw-r--r--clients/tui/nmt-device-entry.h61
-rw-r--r--clients/tui/nmt-edit-connection-list.c574
-rw-r--r--clients/tui/nmt-edit-connection-list.h61
-rw-r--r--clients/tui/nmt-editor-page.c229
-rw-r--r--clients/tui/nmt-editor-page.h57
-rw-r--r--clients/tui/nmt-editor.c332
-rw-r--r--clients/tui/nmt-editor.h51
-rw-r--r--clients/tui/nmt-ip-entry.c268
-rw-r--r--clients/tui/nmt-ip-entry.h52
-rw-r--r--clients/tui/nmt-mac-entry.c238
-rw-r--r--clients/tui/nmt-mac-entry.h54
-rw-r--r--clients/tui/nmt-mtu-entry.c192
-rw-r--r--clients/tui/nmt-mtu-entry.h49
-rw-r--r--clients/tui/nmt-page-bond.c436
-rw-r--r--clients/tui/nmt-page-bond.h50
-rw-r--r--clients/tui/nmt-page-bridge-port.c92
-rw-r--r--clients/tui/nmt-page-bridge-port.h49
-rw-r--r--clients/tui/nmt-page-bridge.c153
-rw-r--r--clients/tui/nmt-page-bridge.h50
-rw-r--r--clients/tui/nmt-page-device.c153
-rw-r--r--clients/tui/nmt-page-device.h51
-rw-r--r--clients/tui/nmt-page-ethernet.c97
-rw-r--r--clients/tui/nmt-page-ethernet.h50
-rw-r--r--clients/tui/nmt-page-grid.c458
-rw-r--r--clients/tui/nmt-page-grid.h62
-rw-r--r--clients/tui/nmt-page-infiniband.c107
-rw-r--r--clients/tui/nmt-page-infiniband.h50
-rw-r--r--clients/tui/nmt-page-ip4.c203
-rw-r--r--clients/tui/nmt-page-ip4.h51
-rw-r--r--clients/tui/nmt-page-ip6.c201
-rw-r--r--clients/tui/nmt-page-ip6.h51
-rw-r--r--clients/tui/nmt-page-main.c328
-rw-r--r--clients/tui/nmt-page-main.h51
-rw-r--r--clients/tui/nmt-page-team-port.c125
-rw-r--r--clients/tui/nmt-page-team-port.h49
-rw-r--r--clients/tui/nmt-page-team.c195
-rw-r--r--clients/tui/nmt-page-team.h50
-rw-r--r--clients/tui/nmt-page-vlan.c163
-rw-r--r--clients/tui/nmt-page-vlan.h50
-rw-r--r--clients/tui/nmt-page-wifi.c389
-rw-r--r--clients/tui/nmt-page-wifi.h50
-rw-r--r--clients/tui/nmt-password-dialog.c346
-rw-r--r--clients/tui/nmt-password-dialog.h57
-rw-r--r--clients/tui/nmt-password-fields.c315
-rw-r--r--clients/tui/nmt-password-fields.h55
-rw-r--r--clients/tui/nmt-route-editor.c220
-rw-r--r--clients/tui/nmt-route-editor.h51
-rw-r--r--clients/tui/nmt-route-entry.c328
-rw-r--r--clients/tui/nmt-route-entry.h51
-rw-r--r--clients/tui/nmt-route-table.c398
-rw-r--r--clients/tui/nmt-route-table.h49
-rw-r--r--clients/tui/nmt-secret-agent.c646
-rw-r--r--clients/tui/nmt-secret-agent.h57
-rw-r--r--clients/tui/nmt-slave-list.c267
-rw-r--r--clients/tui/nmt-slave-list.h52
-rw-r--r--clients/tui/nmt-utils.c151
-rw-r--r--clients/tui/nmt-utils.h46
-rw-r--r--clients/tui/nmt-widget-list.c496
-rw-r--r--clients/tui/nmt-widget-list.h69
-rw-r--r--clients/tui/nmtui-connect.c353
-rw-r--r--clients/tui/nmtui-connect.h28
-rw-r--r--clients/tui/nmtui-edit.c510
-rw-r--r--clients/tui/nmtui-edit.h44
-rw-r--r--clients/tui/nmtui-hostname.c124
-rw-r--r--clients/tui/nmtui-hostname.h28
-rw-r--r--clients/tui/nmtui.c310
-rw-r--r--clients/tui/nmtui.h34
-rw-r--r--clients/tui/vpn-helpers.c424
-rw-r--r--clients/tui/vpn-helpers.h39
122 files changed, 24254 insertions, 0 deletions
diff --git a/clients/tui/Makefile.am b/clients/tui/Makefile.am
new file mode 100644
index 0000000000..7ca1b9196c
--- /dev/null
+++ b/clients/tui/Makefile.am
@@ -0,0 +1,125 @@
+if BUILD_NMTUI
+
+SUBDIRS = newt .
+
+AM_CPPFLAGS= \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/include \
+ -I$(top_srcdir)/libnm-util \
+ -I$(top_builddir)/libnm-util \
+ -I$(top_srcdir)/libnm-glib \
+ -I$(srcdir)/newt \
+ $(GLIB_CFLAGS) \
+ $(NEWT_CFLAGS) \
+ $(DBUS_CFLAGS) \
+ $(GUDEV_CFLAGS) \
+ -DG_LOG_DOMAIN=\""nmtui"\" \
+ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+ -DLOCALEDIR=\""$(localedir)"\" \
+ $(NULL)
+
+bin_PROGRAMS = nmtui
+links = nmtui-edit nmtui-connect nmtui-hostname
+
+install-exec-hook:
+ for link in $(links); do \
+ ln -f $(DESTDIR)$(bindir)/nmtui $(DESTDIR)$(bindir)/$$link; \
+ done
+
+uninstall-hook:
+ for link in $(links); do \
+ rm -f $(DESTDIR)$(bindir)/$$link; \
+ done
+
+nmtui_SOURCES = \
+ nmtui.c \
+ nmtui.h \
+ nmtui-connect.c \
+ nmtui-connect.h \
+ nmtui-edit.c \
+ nmtui-edit.h \
+ nmtui-hostname.c \
+ nmtui-hostname.h \
+ \
+ nm-editor-bindings.c \
+ nm-editor-bindings.h \
+ nm-editor-utils.c \
+ nm-editor-utils.h \
+ \
+ nmt-address-list.c \
+ nmt-address-list.h \
+ nmt-connect-connection-list.c \
+ nmt-connect-connection-list.h \
+ nmt-device-entry.c \
+ nmt-device-entry.h \
+ nmt-edit-connection-list.c \
+ nmt-edit-connection-list.h \
+ nmt-editor-page.c \
+ nmt-editor-page.h \
+ nmt-editor.c \
+ nmt-editor.h \
+ nmt-ip-entry.c \
+ nmt-ip-entry.h \
+ nmt-mac-entry.c \
+ nmt-mac-entry.h \
+ nmt-mtu-entry.c \
+ nmt-mtu-entry.h \
+ nmt-page-bond.c \
+ nmt-page-bond.h \
+ nmt-page-bridge.c \
+ nmt-page-bridge.h \
+ nmt-page-bridge-port.c \
+ nmt-page-bridge-port.h \
+ nmt-page-device.c \
+ nmt-page-device.h \
+ nmt-page-ethernet.c \
+ nmt-page-ethernet.h \
+ nmt-page-grid.c \
+ nmt-page-grid.h \
+ nmt-page-infiniband.c \
+ nmt-page-infiniband.h \
+ nmt-page-ip4.c \
+ nmt-page-ip4.h \
+ nmt-page-ip6.c \
+ nmt-page-ip6.h \
+ nmt-page-main.c \
+ nmt-page-main.h \
+ nmt-page-team.c \
+ nmt-page-team.h \
+ nmt-page-team-port.c \
+ nmt-page-team-port.h \
+ nmt-page-vlan.c \
+ nmt-page-vlan.h \
+ nmt-page-wifi.c \
+ nmt-page-wifi.h \
+ nmt-password-dialog.c \
+ nmt-password-dialog.h \
+ nmt-password-fields.c \
+ nmt-password-fields.h \
+ nmt-route-editor.c \
+ nmt-route-editor.h \
+ nmt-route-entry.c \
+ nmt-route-entry.h \
+ nmt-route-table.c \
+ nmt-route-table.h \
+ nmt-secret-agent.c \
+ nmt-secret-agent.h \
+ nmt-slave-list.c \
+ nmt-slave-list.h \
+ nmt-utils.c \
+ nmt-utils.h \
+ nmt-widget-list.c \
+ nmt-widget-list.h \
+ $(NULL)
+
+nmtui_LDADD = \
+ $(top_builddir)/libnm-util/libnm-util.la \
+ $(top_builddir)/libnm-glib/libnm-glib.la \
+ $(builddir)/newt/libnmt-newt.a \
+ $(GUDEV_LIBS) \
+ $(DBUS_LIBS) \
+ $(NEWT_LIBS) \
+ $(GLIB_LIBS) \
+ $(NULL)
+
+endif
diff --git a/clients/tui/newt/Makefile.am b/clients/tui/newt/Makefile.am
new file mode 100644
index 0000000000..e572238b2a
--- /dev/null
+++ b/clients/tui/newt/Makefile.am
@@ -0,0 +1,53 @@
+AM_CPPFLAGS= \
+ -I$(top_srcdir)/include \
+ -DG_LOG_DOMAIN=\""nmtui"\" \
+ $(GLIB_CFLAGS) \
+ $(NEWT_CFLAGS) \
+ $(NULL)
+
+noinst_LIBRARIES = libnmt-newt.a
+
+libnmt_newt_a_SOURCES = \
+ nmt-newt.h \
+ nmt-newt-types.h \
+ nmt-newt-button.c \
+ nmt-newt-button.h \
+ nmt-newt-button-box.c \
+ nmt-newt-button-box.h \
+ nmt-newt-checkbox.c \
+ nmt-newt-checkbox.h \
+ nmt-newt-component.c \
+ nmt-newt-component.h \
+ nmt-newt-container.c \
+ nmt-newt-container.h \
+ nmt-newt-entry.c \
+ nmt-newt-entry.h \
+ nmt-newt-entry-numeric.c \
+ nmt-newt-entry-numeric.h \
+ nmt-newt-form.c \
+ nmt-newt-form.h \
+ nmt-newt-grid.c \
+ nmt-newt-grid.h \
+ nmt-newt-hacks.c \
+ nmt-newt-hacks.h \
+ nmt-newt-label.c \
+ nmt-newt-label.h \
+ nmt-newt-listbox.c \
+ nmt-newt-listbox.h \
+ nmt-newt-popup.c \
+ nmt-newt-popup.h \
+ nmt-newt-section.c \
+ nmt-newt-section.h \
+ nmt-newt-separator.c \
+ nmt-newt-separator.h \
+ nmt-newt-stack.c \
+ nmt-newt-stack.h \
+ nmt-newt-textbox.c \
+ nmt-newt-textbox.h \
+ nmt-newt-toggle-button.c \
+ nmt-newt-toggle-button.h \
+ nmt-newt-utils.c \
+ nmt-newt-utils.h \
+ nmt-newt-widget.c \
+ nmt-newt-widget.h \
+ $(NULL)
diff --git a/clients/tui/newt/nmt-newt-button-box.c b/clients/tui/newt/nmt-newt-button-box.c
new file mode 100644
index 0000000000..4fba1daf92
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-button-box.c
@@ -0,0 +1,386 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-button-box
+ * @short_description: A container for #NmtNewtButtons
+ *
+ * #NmtNewtButtonBox is a container for creating and holding
+ * #NmtNewtButtons.
+ *
+ * A button box can be either horizontally or vertically laid out, and
+ * has two sections within it: the "start" (left or top) and "end"
+ * (right or bottom). Buttons are added from left to right or top to bottom
+ * within each of the two sections.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-button-box.h"
+#include "nmt-newt-button.h"
+
+G_DEFINE_TYPE (NmtNewtButtonBox, nmt_newt_button_box, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_NEWT_BUTTON_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxPrivate))
+
+typedef struct {
+ NmtNewtButtonBoxOrientation orientation;
+ GPtrArray *start_buttons, *end_buttons;
+} NmtNewtButtonBoxPrivate;
+
+enum {
+ PROP_0,
+
+ PROP_ORIENTATION,
+
+ LAST_PROP
+};
+
+/**
+ * NmtNewtButtonBoxOrientation:
+ * @NMT_NEWT_BUTTON_BOX_HORIZONTAL: horizontal
+ * @NMT_NEWT_BUTTON_BOX_VERTICAL: vertical
+ *
+ * The orientation of an #NmtNewtButtonBox
+ */
+
+/**
+ * nmt_newt_button_box_new:
+ * @orientation: the orientation
+ *
+ * Creates a new #NmtNewtButtonBox
+ *
+ * Returns: a new #NmtNewtButtonBox
+ */
+NmtNewtWidget *
+nmt_newt_button_box_new (NmtNewtButtonBoxOrientation orientation)
+{
+ return g_object_new (NMT_TYPE_NEWT_BUTTON_BOX,
+ "orientation", orientation,
+ NULL);
+}
+
+static void
+nmt_newt_button_box_init (NmtNewtButtonBox *bbox)
+{
+ NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox);
+
+ priv->start_buttons = g_ptr_array_new ();
+ priv->end_buttons = g_ptr_array_new ();
+}
+
+/**
+ * nmt_newt_button_box_add_start:
+ * @bbox: an #NmtNewtButtonBox
+ * @label: the label for the newt button
+ *
+ * Creates a new #NmtNewtButton with the given @label, adds it
+ * to the "start" section of @bbox, and returns the newly-created
+ * button.
+ *
+ * Returns: the newly-created button, already added to @bbox
+ */
+NmtNewtWidget *
+nmt_newt_button_box_add_start (NmtNewtButtonBox *bbox,
+ const char *label)
+{
+ NmtNewtWidget *button;
+
+ button = nmt_newt_button_new (label);
+ nmt_newt_button_box_add_widget_start (bbox, button);
+ return button;
+}
+
+/**
+ * nmt_newt_button_box_add_widget_start:
+ * @bbox: an #NmtNewtButtonBox
+ * @widget: the #NmtNewtWidget to add
+ *
+ * Adds the given widget to the "start" section of @bbox.
+ */
+void
+nmt_newt_button_box_add_widget_start (NmtNewtButtonBox *bbox,
+ NmtNewtWidget *widget)
+{
+ NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox);
+
+ NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)->
+ add (NMT_NEWT_CONTAINER (bbox), widget);
+ g_ptr_array_add (priv->start_buttons, widget);
+}
+
+/**
+ * nmt_newt_button_box_add_end:
+ * @bbox: an #NmtNewtButtonBox
+ * @label: the label for the newt button
+ *
+ * Creates a new #NmtNewtButton with the given @label, adds it
+ * to the "end" section of @bbox, and returns the newly-created
+ * button.
+ *
+ * Returns: the newly-created button, already added to @bbox
+ */
+NmtNewtWidget *
+nmt_newt_button_box_add_end (NmtNewtButtonBox *bbox,
+ const char *label)
+{
+ NmtNewtWidget *button;
+
+ button = nmt_newt_button_new (label);
+ nmt_newt_button_box_add_widget_end (bbox, button);
+ return button;
+}
+
+/**
+ * nmt_newt_button_box_add_widget_end:
+ * @bbox: an #NmtNewtButtonBox
+ * @widget: the #NmtNewtWidget to add
+ *
+ * Adds the given widget to the "end" section of @bbox.
+ */
+void
+nmt_newt_button_box_add_widget_end (NmtNewtButtonBox *bbox,
+ NmtNewtWidget *widget)
+{
+ NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox);
+
+ NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)->
+ add (NMT_NEWT_CONTAINER (bbox), widget);
+ g_ptr_array_add (priv->end_buttons, widget);
+}
+
+static void
+nmt_newt_button_box_remove (NmtNewtContainer *container,
+ NmtNewtWidget *child)
+{
+ NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (container);
+ int i;
+
+ NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)->
+ remove (container, child);
+
+ for (i = 0; i < priv->start_buttons->len; i++) {
+ if (priv->start_buttons->pdata[i] == (gpointer) child) {
+ g_ptr_array_remove_index (priv->start_buttons, i);
+ return;
+ }
+ }
+ for (i = 0; i < priv->end_buttons->len; i++) {
+ if (priv->end_buttons->pdata[i] == (gpointer) child) {
+ g_ptr_array_remove_index (priv->end_buttons, i);
+ return;
+ }
+ }
+}
+
+static void
+add_buttons (GPtrArray *buttons, GPtrArray *cos)
+{
+ NmtNewtWidget *child;
+ newtComponent *child_cos;
+ int i, c;
+
+ for (i = 0; i < buttons->len; i++) {
+ child = buttons->pdata[i];
+
+ if (!nmt_newt_widget_get_visible (child))
+ continue;
+
+ child_cos = nmt_newt_widget_get_components (child);
+ for (c = 0; child_cos[c]; c++)
+ g_ptr_array_add (cos, child_cos[c]);
+ g_free (child_cos);
+ }
+}
+
+static newtComponent *
+nmt_newt_button_box_get_components (NmtNewtWidget *widget)
+{
+ NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget);
+ GPtrArray *cos;
+
+ cos = g_ptr_array_new ();
+ add_buttons (priv->start_buttons, cos);
+ add_buttons (priv->end_buttons, cos);
+ g_ptr_array_add (cos, NULL);
+
+ return (newtComponent *) g_ptr_array_free (cos, FALSE);
+}
+
+static void
+size_request_buttons (NmtNewtButtonBox *bbox,
+ GPtrArray *buttons,
+ int *width,
+ int *height)
+{
+ NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox);
+ int child_width, child_height;
+ int i;
+
+ for (i = 0; i < buttons->len; i++) {
+ NmtNewtWidget *child = buttons->pdata[i];
+
+ nmt_newt_widget_size_request (child, &child_width, &child_height);
+ if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) {
+ *width += child_width;
+ if (i > 0)
+ *width += 1;
+ *height = MAX (*height, child_height);
+ } else {
+ *height += child_height;
+ if (i > 0)
+ *height += 1;
+ *width = MAX (*width, child_width);
+ }
+ }
+}
+
+static void
+nmt_newt_button_box_size_request (NmtNewtWidget *widget,
+ int *width,
+ int *height)
+{
+ NmtNewtButtonBox *bbox = NMT_NEWT_BUTTON_BOX (widget);
+ NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget);
+
+ *width = *height = 0;
+ size_request_buttons (bbox, priv->start_buttons, width, height);
+ size_request_buttons (bbox, priv->end_buttons, width, height);
+
+ if (priv->start_buttons && priv->end_buttons) {
+ if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL)
+ *width += 1;
+ else
+ *height += 1;
+ }
+}
+
+static void
+nmt_newt_button_box_size_allocate (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget);
+ NmtNewtWidget *child;
+ int child_x, child_y, child_width, child_height;
+ int i;
+
+ child_x = x;
+ child_y = y;
+ for (i = 0; i < priv->start_buttons->len; i++) {
+ child = priv->start_buttons->pdata[i];
+ nmt_newt_widget_size_request (child, &child_width, &child_height);
+
+ if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) {
+ nmt_newt_widget_size_allocate (child, child_x, child_y, child_width, child_height);
+ child_x += child_width + 1;
+ } else {
+ nmt_newt_widget_size_allocate (child, child_x, child_y, child_width, child_height);
+ child_y += child_height + 1;
+ }
+ }
+
+ if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL)
+ child_x = x + width;
+ else
+ child_y = y + height;
+
+ for (i = priv->end_buttons->len - 1; i >= 0; i--) {
+ child = priv->end_buttons->pdata[i];
+ nmt_newt_widget_size_request (child, &child_width, &child_height);
+
+ if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) {
+ nmt_newt_widget_size_allocate (child,
+ child_x - child_width, child_y,
+ child_width, child_height);
+ child_x -= child_width + 1;
+ } else {
+ nmt_newt_widget_size_allocate (child,
+ child_x, child_y - child_height,
+ child_width, child_height);
+ child_y -= child_height + 1;
+ }
+ }
+}
+
+static void
+nmt_newt_button_box_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_ORIENTATION:
+ priv->orientation = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_button_box_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_ORIENTATION:
+ g_value_set_int (value, priv->orientation);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_button_box_class_init (NmtNewtButtonBoxClass *bbox_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (bbox_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (bbox_class);
+ NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (bbox_class);
+
+ g_type_class_add_private (bbox_class, sizeof (NmtNewtButtonBoxPrivate));
+
+ object_class->get_property = nmt_newt_button_box_get_property;
+ object_class->set_property = nmt_newt_button_box_set_property;
+
+ widget_class->get_components = nmt_newt_button_box_get_components;
+ widget_class->size_request = nmt_newt_button_box_size_request;
+ widget_class->size_allocate = nmt_newt_button_box_size_allocate;
+
+ container_class->remove = nmt_newt_button_box_remove;
+
+ g_object_class_install_property
+ (object_class, PROP_ORIENTATION,
+ g_param_spec_int ("orientation", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-button-box.h b/clients/tui/newt/nmt-newt-button-box.h
new file mode 100644
index 0000000000..970588a4d0
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-button-box.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_BUTTON_BOX_H
+#define NMT_NEWT_BUTTON_BOX_H
+
+#include "nmt-newt-grid.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_BUTTON_BOX (nmt_newt_button_box_get_type ())
+#define NMT_NEWT_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBox))
+#define NMT_NEWT_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxClass))
+#define NMT_IS_NEWT_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_BUTTON_BOX))
+#define NMT_IS_NEWT_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_BUTTON_BOX))
+#define NMT_NEWT_BUTTON_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxClass))
+
+struct _NmtNewtButtonBox {
+ NmtNewtContainer parent;
+
+};
+
+typedef struct {
+ NmtNewtContainerClass parent;
+
+} NmtNewtButtonBoxClass;
+
+GType nmt_newt_button_box_get_type (void);
+
+typedef enum {
+ NMT_NEWT_BUTTON_BOX_HORIZONTAL,
+ NMT_NEWT_BUTTON_BOX_VERTICAL
+} NmtNewtButtonBoxOrientation;
+
+NmtNewtWidget *nmt_newt_button_box_new (NmtNewtButtonBoxOrientation orientation);
+
+NmtNewtWidget *nmt_newt_button_box_add_start (NmtNewtButtonBox *bbox,
+ const char *label);
+NmtNewtWidget *nmt_newt_button_box_add_end (NmtNewtButtonBox *bbox,
+ const char *label);
+
+void nmt_newt_button_box_add_widget_start (NmtNewtButtonBox *bbox,
+ NmtNewtWidget *widget);
+void nmt_newt_button_box_add_widget_end (NmtNewtButtonBox *bbox,
+ NmtNewtWidget *widget);
+
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_BUTTON_BOX_H */
diff --git a/clients/tui/newt/nmt-newt-button.c b/clients/tui/newt/nmt-newt-button.c
new file mode 100644
index 0000000000..00dbcd79a4
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-button.c
@@ -0,0 +1,261 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-button
+ * @short_description: Push buttons
+ *
+ * #NmtNewtButton implements a button widget.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-button.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtButton, nmt_newt_button, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonPrivate))
+
+typedef struct {
+ char *label;
+} NmtNewtButtonPrivate;
+
+enum {
+ PROP_0,
+ PROP_LABEL,
+
+ LAST_PROP
+};
+
+enum {
+ CLICKED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/**
+ * nmt_newt_button_new:
+ * @label: the (initial) button label
+ *
+ * Creates a new button.
+ *
+ * Returns: a new #NmtNewtButton
+ */
+NmtNewtWidget *
+nmt_newt_button_new (const char *label)
+{
+ return g_object_new (NMT_TYPE_NEWT_BUTTON,
+ "label", label,
+ NULL);
+}
+
+/**
+ * nmt_newt_button_set_label:
+ * @button: an #NmtNewtButton
+ * @label: the new label
+ *
+ * Updates @button's label.
+ */
+void
+nmt_newt_button_set_label (NmtNewtButton *button,
+ const char *label)
+{
+ NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (button);
+
+ if (!g_strcmp0 (priv->label, label))
+ return;
+
+ g_free (priv->label);
+ priv->label = g_strdup (label);
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (button));
+}
+
+/**
+ * nmt_newt_button_get_label:
+ * @button: an #NmtNewtButton
+ *
+ * Gets @button's label.
+ *
+ * Returns: @button's label.
+ */
+const char *
+nmt_newt_button_get_label (NmtNewtButton *button)
+{
+ NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (button);
+
+ return priv->label;
+}
+
+static void
+nmt_newt_button_init (NmtNewtButton *button)
+{
+}
+
+static void
+nmt_newt_button_finalize (GObject *object)
+{
+ NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (object);
+
+ g_free (priv->label);
+
+ G_OBJECT_CLASS (nmt_newt_button_parent_class)->finalize (object);
+}
+
+static newtComponent
+nmt_newt_button_build_component (NmtNewtComponent *component,
+ gboolean sensitive)
+{
+ NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (component);
+ newtComponent co;
+ char *label = NULL, *label_lc;
+
+ if (sensitive) {
+ label_lc = nmt_newt_locale_from_utf8 (priv->label);
+ co = newtCompactButton (-1, -1, label_lc);
+ g_free (label_lc);
+ } else {
+ label = g_strdup_printf (" <%s>", priv->label);
+ label_lc = nmt_newt_locale_from_utf8 (label);
+ co = newtLabel (-1, -1, label_lc);
+ g_free (label_lc);
+ newtLabelSetColors (co, NMT_NEWT_COLORSET_DISABLED_BUTTON);
+ }
+
+ return co;
+}
+
+static void
+nmt_newt_button_size_request (NmtNewtWidget *widget,
+ int *width,
+ int *height)
+{
+ NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->size_request (widget, width, height);
+
+ /* remove the automatically-added left padding */
+ (*width)--;
+}
+
+static void
+nmt_newt_button_size_allocate (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ /* account for the automatically-added left padding */
+ x--;
+ width++;
+
+ NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->size_allocate (widget, x, y, width, height);
+}
+
+static void
+nmt_newt_button_activated (NmtNewtWidget *widget)
+{
+ g_signal_emit (widget, signals[CLICKED], 0);
+
+ NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->activated (widget);
+}
+
+static void
+nmt_newt_button_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_LABEL:
+ nmt_newt_button_set_label (NMT_NEWT_BUTTON (object),
+ g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_button_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_LABEL:
+ g_value_set_string (value, priv->label);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_button_class_init (NmtNewtButtonClass *button_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (button_class);
+ NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (button_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (button_class);
+
+ g_type_class_add_private (button_class, sizeof (NmtNewtButtonPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_button_set_property;
+ object_class->get_property = nmt_newt_button_get_property;
+ object_class->finalize = nmt_newt_button_finalize;
+
+ widget_class->size_request = nmt_newt_button_size_request;
+ widget_class->size_allocate = nmt_newt_button_size_allocate;
+ widget_class->activated = nmt_newt_button_activated;
+
+ component_class->build_component = nmt_newt_button_build_component;
+
+ /* signals */
+
+ /**
+ * NmtNewtButton::clicked:
+ * @button: the #NmtNewtButton
+ *
+ * Emitted when the button is clicked.
+ */
+ signals[CLICKED] =
+ g_signal_new ("clicked",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /* properties */
+
+ /**
+ * NmtNewtButton:label:
+ *
+ * The button's label
+ */
+ g_object_class_install_property
+ (object_class, PROP_LABEL,
+ g_param_spec_string ("label", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-button.h b/clients/tui/newt/nmt-newt-button.h
new file mode 100644
index 0000000000..52e95c5d13
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-button.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_BUTTON_H
+#define NMT_NEWT_BUTTON_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_BUTTON (nmt_newt_button_get_type ())
+#define NMT_NEWT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButton))
+#define NMT_NEWT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonClass))
+#define NMT_IS_NEWT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_BUTTON))
+#define NMT_IS_NEWT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_BUTTON))
+#define NMT_NEWT_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonClass))
+
+struct _NmtNewtButton {
+ NmtNewtComponent parent;
+
+};
+
+typedef struct {
+ NmtNewtComponentClass parent;
+
+} NmtNewtButtonClass;
+
+GType nmt_newt_button_get_type (void);
+
+NmtNewtWidget *nmt_newt_button_new (const char *label);
+
+void nmt_newt_button_set_label (NmtNewtButton *button,
+ const char *label);
+const char *nmt_newt_button_get_label (NmtNewtButton *button);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_BUTTON_H */
diff --git a/clients/tui/newt/nmt-newt-checkbox.c b/clients/tui/newt/nmt-newt-checkbox.c
new file mode 100644
index 0000000000..69d9920a46
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-checkbox.c
@@ -0,0 +1,237 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-checkbox
+ * @short_description: Checkboxes
+ *
+ * #NmtNewtCheckbox implements a checkbox widget.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-checkbox.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtCheckbox, nmt_newt_checkbox, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_CHECKBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxPrivate))
+
+typedef struct {
+ char *label_lc;
+ gboolean active;
+} NmtNewtCheckboxPrivate;
+
+enum {
+ PROP_0,
+ PROP_LABEL,
+ PROP_ACTIVE,
+
+ LAST_PROP
+};
+
+#define CHECKBOX_INACTIVE ' '
+#define CHECKBOX_ACTIVE 'X'
+#define CHECKBOX_STATES " X"
+
+/**
+ * nmt_newt_checkbox_new:
+ * @label: the (initial) checkbox label
+ *
+ * Creates a new checkbox.
+ *
+ * Returns: a new #NmtNewtCheckbox
+ */
+NmtNewtWidget *
+nmt_newt_checkbox_new (const char *label)
+{
+ return g_object_new (NMT_TYPE_NEWT_CHECKBOX,
+ "label", label,
+ NULL);
+}
+
+/**
+ * nmt_newt_checkbox_set_active:
+ * @checkbox: an #NmtNewtCheckbox
+ * @active: whether @checkbox should be checked
+ *
+ * Updates @checkbox's checked state
+ */
+void
+nmt_newt_checkbox_set_active (NmtNewtCheckbox *checkbox,
+ gboolean active)
+{
+ NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox);
+ newtComponent co;
+
+ active = !!active;
+ if (active == priv->active)
+ return;
+
+ priv->active = active;
+
+ co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (checkbox));
+ if (co)
+ newtCheckboxSetValue (co, priv->active ? CHECKBOX_ACTIVE : CHECKBOX_INACTIVE);
+
+ g_object_notify (G_OBJECT (checkbox), "active");
+}
+
+/**
+ * nmt_newt_checkbox_get_active:
+ * @checkbox: an #NmtNewtCheckbox
+ *
+ * Gets @checkbox's checked state
+ *
+ * Returns: @checkbox's checked state
+ */
+gboolean
+nmt_newt_checkbox_get_active (NmtNewtCheckbox *checkbox)
+{
+ NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox);
+
+ return priv->active;
+}
+
+static void
+nmt_newt_checkbox_init (NmtNewtCheckbox *checkbox)
+{
+}
+
+static void
+nmt_newt_checkbox_finalize (GObject *object)
+{
+ NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object);
+
+ g_free (priv->label_lc);
+
+ G_OBJECT_CLASS (nmt_newt_checkbox_parent_class)->finalize (object);
+}
+
+static void
+checkbox_toggled_callback (newtComponent co,
+ void *checkbox)
+{
+ NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox);
+ gboolean active;
+
+ active = (newtCheckboxGetValue (co) == CHECKBOX_ACTIVE);
+ if (active != priv->active) {
+ priv->active = active;
+ g_object_notify (checkbox, "active");
+ }
+}
+
+static newtComponent
+nmt_newt_checkbox_build_component (NmtNewtComponent *component,
+ gboolean sensitive)
+{
+ NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (component);
+ newtComponent co;
+
+ co = newtCheckbox (-1, -1, priv->label_lc,
+ priv->active ? CHECKBOX_ACTIVE : CHECKBOX_INACTIVE,
+ CHECKBOX_STATES, NULL);
+ if (!sensitive)
+ newtCheckboxSetFlags (co, NEWT_FLAG_DISABLED, NEWT_FLAGS_SET);
+ newtComponentAddCallback (co, checkbox_toggled_callback, component);
+ return co;
+}
+
+static void
+nmt_newt_checkbox_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtCheckbox *checkbox = NMT_NEWT_CHECKBOX (object);
+ NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_LABEL:
+ g_free (priv->label_lc);
+ priv->label_lc = nmt_newt_locale_from_utf8 (g_value_get_string (value));
+ break;
+ case PROP_ACTIVE:
+ nmt_newt_checkbox_set_active (checkbox, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_checkbox_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_LABEL:
+ g_value_take_string (value, nmt_newt_locale_to_utf8 (priv->label_lc));
+ break;
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, priv->active);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_checkbox_class_init (NmtNewtCheckboxClass *checkbox_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (checkbox_class);
+ NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (checkbox_class);
+
+ g_type_class_add_private (checkbox_class, sizeof (NmtNewtCheckboxPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_checkbox_set_property;
+ object_class->get_property = nmt_newt_checkbox_get_property;
+ object_class->finalize = nmt_newt_checkbox_finalize;
+
+ component_class->build_component = nmt_newt_checkbox_build_component;
+
+ /**
+ * NmtNewtCheckbox:label:
+ *
+ * The checkbox's label
+ */
+ g_object_class_install_property
+ (object_class, PROP_LABEL,
+ g_param_spec_string ("label", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtCheckbox:active:
+ *
+ * The checkbox's checked state
+ */
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE,
+ g_param_spec_boolean ("active", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-checkbox.h b/clients/tui/newt/nmt-newt-checkbox.h
new file mode 100644
index 0000000000..c277386b18
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-checkbox.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_CHECKBOX_H
+#define NMT_NEWT_CHECKBOX_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_CHECKBOX (nmt_newt_checkbox_get_type ())
+#define NMT_NEWT_CHECKBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckbox))
+#define NMT_NEWT_CHECKBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxClass))
+#define NMT_IS_NEWT_CHECKBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_CHECKBOX))
+#define NMT_IS_NEWT_CHECKBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_CHECKBOX))
+#define NMT_NEWT_CHECKBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxClass))
+
+struct _NmtNewtCheckbox {
+ NmtNewtComponent parent;
+
+};
+
+typedef struct {
+ NmtNewtComponentClass parent;
+
+} NmtNewtCheckboxClass;
+
+GType nmt_newt_checkbox_get_type (void);
+
+NmtNewtWidget *nmt_newt_checkbox_new (const char *label);
+
+void nmt_newt_checkbox_set_active (NmtNewtCheckbox *checkbox,
+ gboolean active);
+gboolean nmt_newt_checkbox_get_active (NmtNewtCheckbox *checkbox);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_CHECKBOX_H */
diff --git a/clients/tui/newt/nmt-newt-component.c b/clients/tui/newt/nmt-newt-component.c
new file mode 100644
index 0000000000..98f6df63f0
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-component.c
@@ -0,0 +1,309 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-component
+ * @short_description: Base class for widgets that wrap #newtComponents
+ *
+ * #NmtNewtComponent is the abstract class for #NmtNewtWidgets that
+ * wrap a (single) #newtComponent.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-component.h"
+#include "nmt-newt-form.h"
+#include "nmt-newt-hacks.h"
+
+G_DEFINE_ABSTRACT_TYPE (NmtNewtComponent, nmt_newt_component, NMT_TYPE_NEWT_WIDGET)
+
+#define NMT_NEWT_COMPONENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentPrivate))
+
+typedef struct {
+ newtComponent co;
+ gboolean own_component;
+ gboolean sensitive;
+} NmtNewtComponentPrivate;
+
+enum {
+ PROP_0,
+
+ PROP_COMPONENT,
+ PROP_SENSITIVE,
+
+ LAST_PROP
+};
+
+static void
+nmt_newt_component_init (NmtNewtComponent *component)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component);
+
+ priv->sensitive = TRUE;
+}
+
+static void
+nmt_newt_component_unrealize (NmtNewtWidget *widget)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+
+ if (!priv->co)
+ return;
+
+ newtComponentAddCallback (priv->co, NULL, NULL);
+ newtComponentAddDestroyCallback (priv->co, NULL, NULL);
+
+ if (priv->own_component)
+ newtComponentDestroy (priv->co);
+ priv->co = NULL;
+}
+
+static void
+component_destroy_callback (newtComponent co,
+ void *component)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component);
+
+ priv->own_component = FALSE;
+ nmt_newt_widget_unrealize (component);
+ nmt_newt_widget_needs_rebuild (component);
+}
+
+static void
+nmt_newt_component_realize (NmtNewtWidget *widget)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+
+ nmt_newt_component_unrealize (widget);
+ priv->co = NMT_NEWT_COMPONENT_GET_CLASS (widget)->
+ build_component (NMT_NEWT_COMPONENT (widget), priv->sensitive);
+ priv->own_component = TRUE;
+ if (!priv->sensitive)
+ newtComponentTakesFocus (priv->co, FALSE);
+ newtComponentAddDestroyCallback (priv->co, component_destroy_callback, widget);
+}
+
+static newtComponent *
+nmt_newt_component_get_components (NmtNewtWidget *widget)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+ newtComponent *cos;
+
+ priv->own_component = FALSE;
+ cos = g_new0 (newtComponent, 2);
+ cos[0] = priv->co;
+ return cos;
+}
+
+static NmtNewtWidget *
+nmt_newt_component_find_component (NmtNewtWidget *widget,
+ newtComponent co)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+
+ if (co == priv->co)
+ return widget;
+ else
+ return NULL;
+}
+
+static void
+nmt_newt_component_size_request (NmtNewtWidget *widget,
+ int *width,
+ int *height)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+
+ newtComponentGetSize (priv->co, width, height);
+}
+
+static void
+nmt_newt_component_size_allocate (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+ newtGrid grid;
+
+ /* You can't directly place a newtComponent, so we create a newtGrid,
+ * position the component within that, and then place the grid.
+ */
+ grid = newtCreateGrid (1, 1);
+ newtGridSetField (grid, 0, 0,
+ NEWT_GRID_COMPONENT, priv->co,
+ x, y, 0, 0,
+ NEWT_ANCHOR_LEFT | NEWT_ANCHOR_TOP, 0);
+ newtGridPlace (grid, 0, 0);
+ newtGridFree (grid, FALSE);
+}
+
+static newtComponent
+nmt_newt_component_get_focus_component (NmtNewtWidget *widget)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+
+ return priv->co;
+}
+
+/**
+ * nmt_newt_component_get_component:
+ * @component: an #NmtNewtComponent
+ *
+ * A simpler version of nmt_newt_widget_get_components() for the
+ * single-component case. Also, unlike
+ * nmt_newt_widget_get_component(), this does not realize the widget
+ * if it isn't already realized. FIXME: why?
+ *
+ * Returns: @component's #newtComponent
+ */
+newtComponent
+nmt_newt_component_get_component (NmtNewtComponent *component)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component);
+
+ return priv->co;
+}
+
+/**
+ * nmt_newt_component_get_sensitive:
+ * @component: an #NmtNewtComponent
+ *
+ * Gets @component's #NmtNewtComponent:sensitive property, indicating
+ * whether the widget is available for manipulation. Insensitive
+ * components will be skipped over in the keyboard tab chain, and may
+ * be displayed differently.
+ *
+ * Returns: @component's #NmtNewtComponent:sensitive property
+ */
+gboolean
+nmt_newt_component_get_sensitive (NmtNewtComponent *component)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component);
+
+ return priv->sensitive;
+}
+
+/**
+ * nmt_newt_component_set_sensitive:
+ * @component: an #NmtNewtComponent
+ * @sensitive: whether @component should be sensitive
+ *
+ * Sets @component's #NmtNewtComponent:sensitive property.
+ */
+void
+nmt_newt_component_set_sensitive (NmtNewtComponent *component,
+ gboolean sensitive)
+{
+ NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component);
+
+ sensitive = !!sensitive;
+ if (priv->sensitive == sensitive)
+ return;
+
+ priv->sensitive = sensitive;
+ g_object_notify (G_OBJECT (component), "sensitive");
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (component));
+}
+
+static void
+nmt_newt_component_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtComponent *component = NMT_NEWT_COMPONENT (object);
+
+ switch (prop_id) {
+ case PROP_SENSITIVE:
+ nmt_newt_component_set_sensitive (component, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_component_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtComponent *component = NMT_NEWT_COMPONENT (object);
+
+ switch (prop_id) {
+ case PROP_COMPONENT:
+ g_value_set_pointer (value, nmt_newt_component_get_component (component));
+ break;
+ case PROP_SENSITIVE:
+ g_value_set_boolean (value, nmt_newt_component_get_sensitive (component));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_component_class_init (NmtNewtComponentClass *component_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (component_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (component_class);
+
+ g_type_class_add_private (component_class, sizeof (NmtNewtComponentPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_component_set_property;
+ object_class->get_property = nmt_newt_component_get_property;
+
+ widget_class->realize = nmt_newt_component_realize;
+ widget_class->unrealize = nmt_newt_component_unrealize;
+ widget_class->get_components = nmt_newt_component_get_components;
+ widget_class->find_component = nmt_newt_component_find_component;
+ widget_class->size_request = nmt_newt_component_size_request;
+ widget_class->size_allocate = nmt_newt_component_size_allocate;
+ widget_class->get_focus_component = nmt_newt_component_get_focus_component;
+
+ /* properties */
+
+ /**
+ * NmtNewtComponent:component:
+ *
+ * The component's #newtComponent
+ */
+ g_object_class_install_property
+ (object_class, PROP_COMPONENT,
+ g_param_spec_pointer ("component", "", "",
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtComponent:sensitive:
+ *
+ * Whether the component is sensitive. Insensitive components will
+ * be skipped over in the keyboard tab chain, and may be displayed
+ * differently.
+ */
+ g_object_class_install_property
+ (object_class, PROP_SENSITIVE,
+ g_param_spec_boolean ("sensitive", "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-component.h b/clients/tui/newt/nmt-newt-component.h
new file mode 100644
index 0000000000..2ffa018e1a
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-component.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_COMPONENT_H
+#define NMT_NEWT_COMPONENT_H
+
+#include "nmt-newt-widget.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_COMPONENT (nmt_newt_component_get_type ())
+#define NMT_NEWT_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponent))
+#define NMT_NEWT_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentClass))
+#define NMT_IS_NEWT_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_COMPONENT))
+#define NMT_IS_NEWT_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_COMPONENT))
+#define NMT_NEWT_COMPONENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentClass))
+
+struct _NmtNewtComponent {
+ NmtNewtWidget parent;
+
+};
+
+typedef struct {
+ NmtNewtWidgetClass parent;
+
+ /* methods */
+ newtComponent (*build_component) (NmtNewtComponent *component,
+ gboolean sensitive);
+
+} NmtNewtComponentClass;
+
+GType nmt_newt_component_get_type (void);
+
+newtComponent nmt_newt_component_get_component (NmtNewtComponent *component);
+
+gboolean nmt_newt_component_get_sensitive (NmtNewtComponent *component);
+void nmt_newt_component_set_sensitive (NmtNewtComponent *component,
+ gboolean sensitive);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_COMPONENT_H */
diff --git a/clients/tui/newt/nmt-newt-container.c b/clients/tui/newt/nmt-newt-container.c
new file mode 100644
index 0000000000..03f677fbdc
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-container.c
@@ -0,0 +1,252 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-container
+ * @short_description: Base class for containers
+ *
+ * #NmtNewtContainer is the base class for #NmtNewtWidgets that
+ * contain other widgets.
+ *
+ * #NmtNewtGrid is the most generic container type.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-container.h"
+#include "nmt-newt-component.h"
+
+G_DEFINE_ABSTRACT_TYPE (NmtNewtContainer, nmt_newt_container, NMT_TYPE_NEWT_WIDGET)
+
+#define NMT_NEWT_CONTAINER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerPrivate))
+
+typedef struct {
+ GPtrArray *children;
+
+} NmtNewtContainerPrivate;
+
+static void child_needs_rebuild (NmtNewtWidget *widget, gpointer user_data);
+
+static void
+nmt_newt_container_init (NmtNewtContainer *container)
+{
+ NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container);
+
+ priv->children = g_ptr_array_new ();
+}
+
+static void
+nmt_newt_container_finalize (GObject *object)
+{
+ NmtNewtContainer *container = NMT_NEWT_CONTAINER (object);
+ NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (object);
+
+ while (priv->children->len)
+ nmt_newt_container_remove (container, priv->children->pdata[0]);
+
+ G_OBJECT_CLASS (nmt_newt_container_parent_class)->finalize (object);
+}
+
+static void
+nmt_newt_container_realize (NmtNewtWidget *widget)
+{
+ NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget);
+ int i;
+
+ for (i = 0; i < priv->children->len; i++)
+ nmt_newt_widget_realize (priv->children->pdata[i]);
+}
+
+static void
+nmt_newt_container_unrealize (NmtNewtWidget *widget)
+{
+ NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget);
+ int i;
+
+ for (i = 0; i < priv->children->len; i++)
+ nmt_newt_widget_unrealize (priv->children->pdata[i]);
+}
+
+static void
+child_needs_rebuild (NmtNewtWidget *widget,
+ gpointer user_data)
+{
+ NmtNewtWidget *container = user_data;
+
+ nmt_newt_widget_needs_rebuild (container);
+}
+
+static void
+nmt_newt_container_real_child_validity_changed (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NmtNewtContainerPrivate *priv;
+ int i;
+
+ if (widget) {
+ if (!nmt_newt_widget_get_visible (widget))
+ return;
+ if (!nmt_newt_widget_get_valid (widget)) {
+ nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), FALSE);
+ return;
+ }
+ }
+
+ priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container);
+ for (i = 0; i < priv->children->len; i++) {
+ widget = priv->children->pdata[i];
+ if ( nmt_newt_widget_get_visible (widget)
+ && !nmt_newt_widget_get_valid (widget)) {
+ nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), FALSE);
+ return;
+ }
+ }
+
+ nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), TRUE);
+}
+
+static void
+nmt_newt_container_child_validity_changed (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NMT_NEWT_CONTAINER_GET_CLASS (container)->child_validity_changed (container, widget);
+}
+
+static void
+child_validity_notify (GObject *object,
+ GParamSpec *pspec,
+ gpointer container)
+{
+ nmt_newt_container_child_validity_changed (container, NMT_NEWT_WIDGET (object));
+}
+
+static void
+nmt_newt_container_real_add (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container);
+
+ g_signal_connect (widget, "needs-rebuild", G_CALLBACK (child_needs_rebuild), container);
+ g_signal_connect (widget, "notify::valid", G_CALLBACK (child_validity_notify), container);
+ g_ptr_array_add (priv->children, g_object_ref_sink (widget));
+ nmt_newt_widget_set_parent (widget, NMT_NEWT_WIDGET (container));
+
+ nmt_newt_container_child_validity_changed (container, widget);
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (container));
+}
+
+static void
+nmt_newt_container_real_remove (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container);
+ int i;
+
+ for (i = 0; i < priv->children->len; i++) {
+ if (widget == priv->children->pdata[i]) {
+ g_ptr_array_remove_index (priv->children, i);
+ g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (child_needs_rebuild), container);
+ g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (child_validity_notify), container);
+ nmt_newt_widget_set_parent (widget, NULL);
+ g_object_unref (widget);
+
+ nmt_newt_container_child_validity_changed (container, NULL);
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (container));
+ return;
+ }
+ }
+}
+
+/**
+ * nmt_newt_container_remove:
+ * @container: the #NmtNewtContainer
+ * @widget: the child to remove
+ *
+ * Removes @widget from @container.
+ *
+ * Note that there is not a corresponding
+ * <literal>nmt_newt_container_add ()</literal>; you must use
+ * container-type-specific methods to add widgets to containers.
+ */
+void
+nmt_newt_container_remove (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NMT_NEWT_CONTAINER_GET_CLASS (container)->remove (container, widget);
+}
+
+static NmtNewtWidget *
+nmt_newt_container_find_component (NmtNewtWidget *widget,
+ newtComponent co)
+{
+ NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget);
+ NmtNewtWidget *found, *child;
+ int i;
+
+ for (i = 0; i < priv->children->len; i++) {
+ child = priv->children->pdata[i];
+
+ found = nmt_newt_widget_find_component (child, co);
+ if (found)
+ return found;
+ }
+
+ return NULL;
+}
+
+/**
+ * nmt_newt_container_get_children:
+ * @container: an #NmtNewtContainer
+ *
+ * Gets a list of @container's children.
+ *
+ * Returns: (transfer full): a list of @container's children.
+ */
+GSList *
+nmt_newt_container_get_children (NmtNewtContainer *container)
+{
+ NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container);
+ GSList *ret;
+ int i;
+
+ for (i = 0, ret = NULL; i < priv->children->len; i++)
+ ret = g_slist_prepend (ret, g_object_ref (priv->children->pdata[i]));
+ return g_slist_reverse (ret);
+}
+
+static void
+nmt_newt_container_class_init (NmtNewtContainerClass *container_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (container_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (container_class);
+
+ g_type_class_add_private (container_class, sizeof (NmtNewtContainerPrivate));
+
+ /* virtual methods */
+ object_class->finalize = nmt_newt_container_finalize;
+
+ widget_class->realize = nmt_newt_container_realize;
+ widget_class->unrealize = nmt_newt_container_unrealize;
+ widget_class->find_component = nmt_newt_container_find_component;
+
+ container_class->add = nmt_newt_container_real_add;
+ container_class->remove = nmt_newt_container_real_remove;
+ container_class->child_validity_changed = nmt_newt_container_real_child_validity_changed;
+}
diff --git a/clients/tui/newt/nmt-newt-container.h b/clients/tui/newt/nmt-newt-container.h
new file mode 100644
index 0000000000..6f182925d5
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-container.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_CONTAINER_H
+#define NMT_NEWT_CONTAINER_H
+
+#include "nmt-newt-widget.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_CONTAINER (nmt_newt_container_get_type ())
+#define NMT_NEWT_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainer))
+#define NMT_NEWT_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerClass))
+#define NMT_IS_NEWT_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_CONTAINER))
+#define NMT_IS_NEWT_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_CONTAINER))
+#define NMT_NEWT_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerClass))
+
+struct _NmtNewtContainer {
+ NmtNewtWidget parent;
+
+};
+
+typedef struct {
+ NmtNewtWidgetClass parent;
+
+ /* methods */
+ void (*add) (NmtNewtContainer *container,
+ NmtNewtWidget *child);
+ void (*remove) (NmtNewtContainer *container,
+ NmtNewtWidget *child);
+
+ void (*child_validity_changed) (NmtNewtContainer *container,
+ NmtNewtWidget *child);
+
+} NmtNewtContainerClass;
+
+GType nmt_newt_container_get_type (void);
+
+void nmt_newt_container_remove (NmtNewtContainer *container,
+ NmtNewtWidget *widget);
+
+GSList *nmt_newt_container_get_children (NmtNewtContainer *container);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_CONTAINER_H */
diff --git a/clients/tui/newt/nmt-newt-entry-numeric.c b/clients/tui/newt/nmt-newt-entry-numeric.c
new file mode 100644
index 0000000000..416b4b79bb
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-entry-numeric.c
@@ -0,0 +1,215 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-entry-numeric
+ * @short_description: Numeric text entry
+ *
+ * #NmtNewtEntryNumeric implements a numeric-only #NmtNewtEntry.
+ *
+ * #NmtNewtEntryNumeric provides its own #NmtNewtEntryFilter and
+ * #NmtNewtEntryValidator functions, so you should not set your own.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "nmt-newt-entry-numeric.h"
+
+G_DEFINE_TYPE (NmtNewtEntryNumeric, nmt_newt_entry_numeric, NMT_TYPE_NEWT_ENTRY)
+
+#define NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericPrivate))
+
+typedef struct {
+ int min, max;
+} NmtNewtEntryNumericPrivate;
+
+enum {
+ PROP_0,
+ PROP_MINIMUM,
+ PROP_MAXIMUM,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_newt_entry_numeric_new:
+ * @width: the entry's width in characters
+ * @min: the minimum valid value
+ * @max: the maximum valid value
+ *
+ * Creates a new #NmtNewtEntryNumeric, accepting values in the
+ * indicated range.
+ *
+ * Returns: a new #NmtNewtEntryNumeric
+ */
+NmtNewtWidget *
+nmt_newt_entry_numeric_new (int width,
+ int min,
+ int max)
+{
+ return g_object_new (NMT_TYPE_NEWT_ENTRY_NUMERIC,
+ "width", width,
+ "minimum", min,
+ "maximum", max,
+ NULL);
+}
+
+static gboolean
+newt_entry_numeric_filter (NmtNewtEntry *entry,
+ const char *text,
+ int ch,
+ int position,
+ gpointer user_data)
+{
+ NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (entry);
+
+ if (g_ascii_isdigit (ch))
+ return TRUE;
+
+ if (ch == '-' && position == 0 && priv->min < 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+newt_entry_numeric_validate (NmtNewtEntry *entry,
+ const char *text,
+ gpointer user_data)
+{
+ NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (entry);
+ int val;
+ char *end;
+
+ if (!*text)
+ return FALSE;
+
+ val = strtoul (text, &end, 10);
+ if (*end)
+ return FALSE;
+ if (val < priv->min || val > priv->max)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+nmt_newt_entry_numeric_init (NmtNewtEntryNumeric *entry)
+{
+ nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), newt_entry_numeric_filter, NULL);
+ nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), newt_entry_numeric_validate, NULL);
+}
+
+static void
+nmt_newt_entry_numeric_constructed (GObject *object)
+{
+ NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object);
+
+ if (!*nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object))) {
+ char buf[32];
+
+ g_snprintf (buf, sizeof (buf), "%d", priv->min);
+ nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), buf);
+ }
+
+ G_OBJECT_CLASS (nmt_newt_entry_numeric_parent_class)->constructed (object);
+}
+
+static void
+nmt_newt_entry_numeric_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MINIMUM:
+ priv->min = g_value_get_int (value);
+ break;
+ case PROP_MAXIMUM:
+ priv->max = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_entry_numeric_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MINIMUM:
+ g_value_set_int (value, priv->min);
+ break;
+ case PROP_MAXIMUM:
+ g_value_set_int (value, priv->max);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_entry_numeric_class_init (NmtNewtEntryNumericClass *entry_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+ g_type_class_add_private (entry_class, sizeof (NmtNewtEntryNumericPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_newt_entry_numeric_constructed;
+ object_class->set_property = nmt_newt_entry_numeric_set_property;
+ object_class->get_property = nmt_newt_entry_numeric_get_property;
+
+ /**
+ * NmtNewtEntryNumeric:minimum:
+ *
+ * The minimum #NmtNewtWidget:valid value for the entry. If this
+ * is non-negative, then the entry will not allow negative numbers
+ * to be entered.
+ */
+ g_object_class_install_property
+ (object_class, PROP_MINIMUM,
+ g_param_spec_int ("minimum", "", "",
+ G_MININT, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtEntryNumeric:maximum:
+ *
+ * The maximum #NmtNewtWidget:valid value for the entry.
+ */
+ g_object_class_install_property
+ (object_class, PROP_MAXIMUM,
+ g_param_spec_int ("maximum", "", "",
+ G_MININT, G_MAXINT, G_MAXINT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-entry-numeric.h b/clients/tui/newt/nmt-newt-entry-numeric.h
new file mode 100644
index 0000000000..43ac34490a
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-entry-numeric.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_ENTRY_NUMERIC_H
+#define NMT_NEWT_ENTRY_NUMERIC_H
+
+#include "nmt-newt-entry.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_ENTRY_NUMERIC (nmt_newt_entry_numeric_get_type ())
+#define NMT_NEWT_ENTRY_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumeric))
+#define NMT_NEWT_ENTRY_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericClass))
+#define NMT_IS_NEWT_ENTRY_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC))
+#define NMT_IS_NEWT_ENTRY_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_ENTRY_NUMERIC))
+#define NMT_NEWT_ENTRY_NUMERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericClass))
+
+struct _NmtNewtEntryNumeric {
+ NmtNewtEntry parent;
+
+};
+
+typedef struct {
+ NmtNewtEntryClass parent;
+
+} NmtNewtEntryNumericClass;
+
+GType nmt_newt_entry_numeric_get_type (void);
+
+NmtNewtWidget *nmt_newt_entry_numeric_new (int width,
+ int min,
+ int max);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_ENTRY_NUMERIC_H */
diff --git a/clients/tui/newt/nmt-newt-entry.c b/clients/tui/newt/nmt-newt-entry.c
new file mode 100644
index 0000000000..7c92d00ada
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-entry.c
@@ -0,0 +1,540 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-entry
+ * @short_description: Text entries
+ *
+ * #NmtNewtEntry implements entry widgets, with optional filtering and
+ * validation.
+ *
+ * See also #NmtNewtEntryNumeric, for numeric-only entries.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-entry.h"
+#include "nmt-newt-form.h"
+#include "nmt-newt-hacks.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtEntry, nmt_newt_entry, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryPrivate))
+
+typedef struct {
+ int width;
+ NmtNewtEntryFlags flags;
+ char *text;
+ int last_cursor_pos;
+ guint idle_update;
+
+ NmtNewtEntryFilter filter;
+ gpointer filter_data;
+
+ NmtNewtEntryValidator validator;
+ gpointer validator_data;
+} NmtNewtEntryPrivate;
+
+enum {
+ PROP_0,
+ PROP_TEXT,
+ PROP_WIDTH,
+ PROP_FLAGS,
+ PROP_PASSWORD,
+
+ LAST_PROP
+};
+
+/**
+ * NmtNewtEntryFlags:
+ * @NMT_NEWT_ENTRY_NOSCROLL: the entry content should not scroll left
+ * and right
+ * @NMT_NEWT_ENTRY_PASSWORD: the entry should show '*'s instead of its
+ * actual contents
+ * @NMT_NEWT_ENTRY_NONEMPTY: the entry should be considered not
+ * #NmtNewtWidget:valid if it is empty.
+ *
+ * Flags describing an #NmtNewtEntry
+ */
+
+/**
+ * nmt_newt_entry_new:
+ * @width: the width in characters for the entry
+ * @flags: flags describing the entry
+ *
+ * Creates a new #NmtNewtEntry.
+ *
+ * Returns: a new #NmtNewtEntry
+ */
+NmtNewtWidget *
+nmt_newt_entry_new (int width,
+ NmtNewtEntryFlags flags)
+{
+ return g_object_new (NMT_TYPE_NEWT_ENTRY,
+ "width", width,
+ "flags", flags,
+ NULL);
+}
+
+/**
+ * NmtNewtEntryFilter:
+ * @entry: the #NmtNewtEntry
+ * @text: the current contents of @entry
+ * @ch: the character just typed
+ * @position: the position of the cursor in @entry
+ * @user_data: the data passed to nmt_newt_entry_set_filter()
+ *
+ * Callback function used to filter the contents of an entry.
+ *
+ * Returns: %TRUE if @ch should be accepted, %FALSE if not
+ */
+
+/**
+ * nmt_newt_entry_set_filter:
+ * @entry: an #NmtNewtEntry
+ * @filter: the function to use to filter the entry
+ * @user_data: data for @filter
+ *
+ * Sets a #NmtNewtEntryFilter on @entry, to allow filtering out
+ * certain characters from the entry.
+ *
+ * Note that @filter will only be called for printable characters (eg,
+ * not for cursor-control characters or the like), and that it will
+ * only be called for user input, not, eg, for
+ * nmt_newt_entry_set_text().
+ */
+void
+nmt_newt_entry_set_filter (NmtNewtEntry *entry,
+ NmtNewtEntryFilter filter,
+ gpointer user_data)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+ priv->filter = filter;
+ priv->filter_data = user_data;
+}
+
+static void
+nmt_newt_entry_check_valid (NmtNewtEntry *entry)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+ gboolean valid;
+
+ if ( (priv->flags & NMT_NEWT_ENTRY_NONEMPTY)
+ && *priv->text == '\0')
+ valid = FALSE;
+ else if (priv->validator)
+ valid = !!priv->validator (entry, priv->text, priv->validator_data);
+ else
+ valid = TRUE;
+
+ nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (entry), valid);
+}
+
+/**
+ * NmtNewtEntryValidator:
+ * @entry: the #NmtNewtEntry
+ * @text: the current contents of @entry
+ * @user_data: the data passed to nmt_newt_entry_set_validator()
+ *
+ * Callback function used to validate the contents of an entry.
+ *
+ * Returns: whether the entry is #NmtNewtWidget:valid
+ */
+
+/**
+ * nmt_newt_entry_set_validator:
+ * @entry: an #NmtNewtEntry
+ * @validator: the function to use to validate the entry
+ * @user_data: data for @validator
+ *
+ * Sets a #NmtNewtEntryValidator on @entry, to allow validation of
+ * the entry contents. If @validator returns %FALSE, then the entry
+ * will not be considered #NmtNewtWidget:valid.
+ */
+void
+nmt_newt_entry_set_validator (NmtNewtEntry *entry,
+ NmtNewtEntryValidator validator,
+ gpointer user_data)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+ priv->validator = validator;
+ priv->validator_data = user_data;
+
+ nmt_newt_entry_check_valid (entry);
+}
+
+static void
+nmt_newt_entry_set_text_internal (NmtNewtEntry *entry,
+ const char *text,
+ newtComponent co)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+ if (!text)
+ text = "";
+
+ if (!strcmp (priv->text, text))
+ return;
+
+ g_free (priv->text);
+ priv->text = g_strdup (text);
+
+ if (co) {
+ char *text_lc;
+
+ text_lc = priv->text ? nmt_newt_locale_from_utf8 (priv->text) : NULL;
+ newtEntrySet (co, text_lc, TRUE);
+ g_free (text_lc);
+ priv->last_cursor_pos = -1;
+ }
+
+ g_object_freeze_notify (G_OBJECT (entry));
+ nmt_newt_entry_check_valid (entry);
+ g_object_notify (G_OBJECT (entry), "text");
+ g_object_thaw_notify (G_OBJECT (entry));
+}
+
+/**
+ * nmt_newt_entry_set_text:
+ * @entry: an #NmtNewtEntry
+ * @text: the new text
+ *
+ * Updates @entry's text. Note that this skips the entry's
+ * #NmtNewtEntryFilter, but will cause its #NmtNewtEntryValidator to
+ * be re-run.
+ */
+void
+nmt_newt_entry_set_text (NmtNewtEntry *entry,
+ const char *text)
+{
+ newtComponent co;
+
+ co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (entry));
+ nmt_newt_entry_set_text_internal (entry, text, co);
+}
+
+/**
+ * nmt_newt_entry_get_text:
+ * @entry: an #NmtNewtEntry
+ *
+ * Gets @entry's text
+ *
+ * Returns: @entry's text
+ */
+const char *
+nmt_newt_entry_get_text (NmtNewtEntry *entry)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+ return priv->text;
+}
+
+/**
+ * nmt_newt_entry_set_width:
+ * @entry: an #NmtNewtEntpry
+ * @widget: the new width
+ *
+ * Updates @entry's width
+ */
+void
+nmt_newt_entry_set_width (NmtNewtEntry *entry,
+ int width)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+ if (priv->width == width)
+ return;
+
+ priv->width = width;
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry));
+
+ g_object_notify (G_OBJECT (entry), "width");
+}
+
+/**
+ * nmt_newt_entry_get_width:
+ * @entry: an #NmtNewtEntry
+ *
+ * Gets @entry's width
+ *
+ * Returns: @entry's width
+ */
+int
+nmt_newt_entry_get_width (NmtNewtEntry *entry)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+ return priv->width;
+}
+
+static void
+nmt_newt_entry_init (NmtNewtEntry *entry)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+ priv->text = g_strdup ("");
+ priv->last_cursor_pos = -1;
+}
+
+static void
+nmt_newt_entry_constructed (GObject *object)
+{
+ nmt_newt_entry_check_valid (NMT_NEWT_ENTRY (object));
+
+ G_OBJECT_CLASS (nmt_newt_entry_parent_class)->constructed (object);
+}
+
+static void
+nmt_newt_entry_finalize (GObject *object)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (object);
+
+ g_free (priv->text);
+ if (priv->idle_update)
+ g_source_remove (priv->idle_update);
+
+ G_OBJECT_CLASS (nmt_newt_entry_parent_class)->finalize (object);
+}
+
+static gboolean
+idle_update_entry (gpointer entry)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+ newtComponent co = nmt_newt_component_get_component (entry);
+ char *text;
+
+ priv->idle_update = 0;
+ if (!co)
+ return FALSE;
+
+ priv->last_cursor_pos = newtEntryGetCursorPosition (co);
+
+ text = nmt_newt_locale_to_utf8 (newtEntryGetValue (co));
+ nmt_newt_entry_set_text_internal (entry, text, NULL);
+ g_free (text);
+
+ return FALSE;
+}
+
+static int
+entry_filter (newtComponent entry,
+ void *self,
+ int ch,
+ int cursor)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (self);
+
+ if (g_ascii_isprint (ch)) {
+ if (priv->filter) {
+ char *text = nmt_newt_locale_to_utf8 (newtEntryGetValue (entry));
+
+ if (!priv->filter (self, text, ch, cursor, priv->filter_data)) {
+ g_free (text);
+ return 0;
+ }
+ g_free (text);
+ }
+ }
+
+ if (!priv->idle_update)
+ priv->idle_update = g_idle_add (idle_update_entry, self);
+ return ch;
+}
+
+static guint
+convert_flags (NmtNewtEntryFlags flags)
+{
+ guint newt_flags = NEWT_FLAG_RETURNEXIT;
+
+ if (!(flags & NMT_NEWT_ENTRY_NOSCROLL))
+ newt_flags |= NEWT_FLAG_SCROLL;
+ if (flags & NMT_NEWT_ENTRY_PASSWORD)
+ newt_flags |= NEWT_FLAG_PASSWORD;
+
+ return newt_flags;
+}
+
+static newtComponent
+nmt_newt_entry_build_component (NmtNewtComponent *component,
+ gboolean sensitive)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (component);
+ newtComponent co;
+ char *text_lc;
+ int flags;
+
+ flags = convert_flags (priv->flags);
+ if (!sensitive)
+ flags |= NEWT_FLAG_DISABLED;
+
+ text_lc = priv->text ? nmt_newt_locale_from_utf8 (priv->text) : NULL;
+ co = newtEntry (-1, -1, text_lc, priv->width, NULL, flags);
+ g_free (text_lc);
+
+ if (priv->last_cursor_pos != -1)
+ newtEntrySetCursorPosition (co, priv->last_cursor_pos);
+
+ newtEntrySetFilter (co, entry_filter, component);
+ return co;
+}
+
+static void
+nmt_newt_entry_activated (NmtNewtWidget *widget)
+{
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (widget);
+
+ if (priv->idle_update) {
+ g_source_remove (priv->idle_update);
+ idle_update_entry (widget);
+ }
+
+ NMT_NEWT_WIDGET_CLASS (nmt_newt_entry_parent_class)->activated (widget);
+}
+
+static void
+nmt_newt_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtEntry *entry = NMT_NEWT_ENTRY (object);
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+ switch (prop_id) {
+ case PROP_TEXT:
+ nmt_newt_entry_set_text (entry, g_value_get_string (value));
+ break;
+ case PROP_WIDTH:
+ nmt_newt_entry_set_width (entry, g_value_get_int (value));
+ break;
+ case PROP_FLAGS:
+ priv->flags = g_value_get_uint (value);
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry));
+ break;
+ case PROP_PASSWORD:
+ if (g_value_get_boolean (value))
+ priv->flags |= NMT_NEWT_ENTRY_PASSWORD;
+ else
+ priv->flags &= ~NMT_NEWT_ENTRY_PASSWORD;
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtEntry *entry = NMT_NEWT_ENTRY (object);
+ NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+ switch (prop_id) {
+ case PROP_TEXT:
+ g_value_set_string (value, nmt_newt_entry_get_text (entry));
+ break;
+ case PROP_WIDTH:
+ g_value_set_int (value, priv->width);
+ break;
+ case PROP_FLAGS:
+ g_value_set_uint (value, priv->flags);
+ break;
+ case PROP_PASSWORD:
+ g_value_set_boolean (value, (priv->flags & NMT_NEWT_ENTRY_PASSWORD) != 0);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_entry_class_init (NmtNewtEntryClass *entry_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (entry_class);
+ NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (entry_class);
+
+ g_type_class_add_private (entry_class, sizeof (NmtNewtEntryPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_newt_entry_constructed;
+ object_class->set_property = nmt_newt_entry_set_property;
+ object_class->get_property = nmt_newt_entry_get_property;
+ object_class->finalize = nmt_newt_entry_finalize;
+
+ widget_class->activated = nmt_newt_entry_activated;
+
+ component_class->build_component = nmt_newt_entry_build_component;
+
+ /**
+ * NmtNewtEntry:text
+ *
+ * The entry's text
+ */
+ g_object_class_install_property
+ (object_class, PROP_TEXT,
+ g_param_spec_string ("text", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtEntry:width
+ *
+ * The entry's width in characters
+ */
+ g_object_class_install_property
+ (object_class, PROP_WIDTH,
+ g_param_spec_int ("width", "", "",
+ -1, 80, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtEntry:flags
+ *
+ * The entry's #NmtNewtEntryFlags
+ */
+ g_object_class_install_property
+ (object_class, PROP_FLAGS,
+ g_param_spec_uint ("flags", "", "",
+ 0, 0xFFFF, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtEntry:password
+ *
+ * %TRUE if #NmtNewtEntry:flags contains %NMT_NEWT_ENTRY_PASSWORD,
+ * %FALSE if not.
+ */
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD,
+ g_param_spec_boolean ("password", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-entry.h b/clients/tui/newt/nmt-newt-entry.h
new file mode 100644
index 0000000000..8df0b13701
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-entry.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_ENTRY_H
+#define NMT_NEWT_ENTRY_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_ENTRY (nmt_newt_entry_get_type ())
+#define NMT_NEWT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntry))
+#define NMT_NEWT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryClass))
+#define NMT_IS_NEWT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_ENTRY))
+#define NMT_IS_NEWT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_ENTRY))
+#define NMT_NEWT_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryClass))
+
+struct _NmtNewtEntry {
+ NmtNewtComponent parent;
+
+};
+
+typedef struct {
+ NmtNewtComponentClass parent;
+
+} NmtNewtEntryClass;
+
+GType nmt_newt_entry_get_type (void);
+
+typedef gboolean (*NmtNewtEntryFilter) (NmtNewtEntry *, const char *text, int ch, int position, gpointer);
+typedef gboolean (*NmtNewtEntryValidator) (NmtNewtEntry *, const char *text, gpointer);
+
+typedef enum {
+ NMT_NEWT_ENTRY_NOSCROLL = (1 << 0),
+ NMT_NEWT_ENTRY_PASSWORD = (1 << 1),
+ NMT_NEWT_ENTRY_NONEMPTY = (1 << 2)
+} NmtNewtEntryFlags;
+
+NmtNewtWidget *nmt_newt_entry_new (int width,
+ NmtNewtEntryFlags flags);
+
+void nmt_newt_entry_set_filter (NmtNewtEntry *entry,
+ NmtNewtEntryFilter filter,
+ gpointer user_data);
+void nmt_newt_entry_set_validator (NmtNewtEntry *entry,
+ NmtNewtEntryValidator validator,
+ gpointer user_data);
+
+void nmt_newt_entry_set_text (NmtNewtEntry *entry,
+ const char *text);
+const char *nmt_newt_entry_get_text (NmtNewtEntry *entry);
+
+void nmt_newt_entry_set_width (NmtNewtEntry *entry,
+ int width);
+int nmt_newt_entry_get_width (NmtNewtEntry *entry);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_ENTRY_H */
diff --git a/clients/tui/newt/nmt-newt-form.c b/clients/tui/newt/nmt-newt-form.c
new file mode 100644
index 0000000000..8172b84368
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-form.c
@@ -0,0 +1,753 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-form
+ * @short_description: The top-level NmtNewt widget
+ *
+ * #NmtNewtForm is the top-level widget that contains and presents a
+ * "form" (aka dialog) to the user.
+ */
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-newt-form.h"
+#include "nmt-newt-button.h"
+#include "nmt-newt-grid.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtForm, nmt_newt_form, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_NEWT_FORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_FORM, NmtNewtFormPrivate))
+
+typedef struct {
+ newtComponent form;
+ NmtNewtWidget *content;
+
+ guint x, y, width, height;
+ guint padding;
+ gboolean fixed_x, fixed_y;
+ gboolean fixed_width, fixed_height;
+ char *title_lc;
+
+ gboolean dirty, escape_exits;
+ NmtNewtWidget *focus;
+#ifdef HAVE_NEWTFORMGETSCROLLPOSITION
+ int scroll_position = 0;
+#endif
+} NmtNewtFormPrivate;
+
+enum {
+ PROP_0,
+ PROP_TITLE,
+ PROP_FULLSCREEN,
+ PROP_FULLSCREEN_VERTICAL,
+ PROP_FULLSCREEN_HORIZONTAL,
+ PROP_X,
+ PROP_Y,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_PADDING,
+ PROP_ESCAPE_EXITS,
+
+ LAST_PROP
+};
+
+enum {
+ QUIT,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void nmt_newt_form_redraw (NmtNewtForm *form);
+
+/**
+ * nmt_newt_form_new:
+ * @title: (allow-none): the form title
+ *
+ * Creates a new form, which will be shown centered on the screen.
+ * Compare nmt_newt_form_new_fullscreen(). You can also position a
+ * form manually by setting its #NmtNewtForm:x and #NmtNewtForm:y
+ * properties at construct time, and/or by setting
+ * #NmtNewtForm:fullscreen, #NmtNewtform:fullscreen-horizontal, or
+ * #NmtNewtForm:fullscreen-vertical.
+ *
+ * If @title is NULL, the form will have no title.
+ *
+ * Returns: a new #NmtNewtForm
+ */
+NmtNewtForm *
+nmt_newt_form_new (const char *title)
+{
+ return g_object_new (NMT_TYPE_NEWT_FORM,
+ "title", title,
+ NULL);
+}
+
+/**
+ * nmt_newt_form_new_fullscreen:
+ * @title: (allow-none): the form title
+ *
+ * Creates a new fullscreen form. Compare nmt_newt_form_new().
+ *
+ * If @title is NULL, the form will have no title.
+ *
+ * Returns: a new #NmtNewtForm
+ */
+NmtNewtForm *
+nmt_newt_form_new_fullscreen (const char *title)
+{
+ return g_object_new (NMT_TYPE_NEWT_FORM,
+ "title", title,
+ "fullscreen", TRUE,
+ NULL);
+}
+
+static void
+nmt_newt_form_init (NmtNewtForm *form)
+{
+ g_object_ref_sink (form);
+}
+
+static void
+nmt_newt_form_finalize (GObject *object)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object);
+
+ g_free (priv->title_lc);
+ g_clear_object (&priv->focus);
+
+ G_OBJECT_CLASS (nmt_newt_form_parent_class)->finalize (object);
+}
+
+static void
+nmt_newt_form_needs_rebuild (NmtNewtWidget *widget)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (widget);
+
+ if (!priv->dirty) {
+ priv->dirty = TRUE;
+ nmt_newt_form_redraw (NMT_NEWT_FORM (widget));
+ }
+}
+
+static void
+nmt_newt_form_remove (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (container);
+ NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_form_parent_class);
+
+ g_return_if_fail (widget == priv->content);
+
+ parent_class->remove (container, widget);
+ priv->content = NULL;
+}
+
+/**
+ * nmt_newt_form_set_content:
+ * @form: the #NmtNewtForm
+ * @content: the form's content
+ *
+ * Sets @form's content to be @content.
+ */
+void
+nmt_newt_form_set_content (NmtNewtForm *form,
+ NmtNewtWidget *content)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+ NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_form_parent_class);
+
+ if (priv->content)
+ nmt_newt_form_remove (NMT_NEWT_CONTAINER (form), priv->content);
+
+ priv->content = content;
+
+ if (priv->content)
+ parent_class->add (NMT_NEWT_CONTAINER (form), content);
+}
+
+static void
+nmt_newt_form_build (NmtNewtForm *form)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+ int screen_height, screen_width, form_height, form_width;
+ newtComponent *cos;
+ int i;
+
+ priv->dirty = FALSE;
+ nmt_newt_widget_realize (NMT_NEWT_WIDGET (form));
+
+ nmt_newt_widget_size_request (priv->content, &form_width, &form_height);
+ newtGetScreenSize (&screen_width, &screen_height);
+
+ if (!priv->fixed_width)
+ priv->width = MIN (form_width + 2 * priv->padding, screen_width - 2);
+ if (!priv->fixed_height)
+ priv->height = MIN (form_height + 2 * priv->padding, screen_height - 2);
+
+ if (!priv->fixed_x)
+ priv->x = (screen_width - form_width) / 2;
+ if (!priv->fixed_y)
+ priv->y = (screen_height - form_height) / 2;
+
+ nmt_newt_widget_size_allocate (priv->content,
+ priv->padding,
+ priv->padding,
+ priv->width - 2 * priv->padding,
+ priv->height - 2 * priv->padding);
+
+ if (priv->height - 2 * priv->padding < form_height) {
+ newtComponent scroll_bar =
+ newtVerticalScrollbar (priv->width - 1, 0, priv->height,
+ NEWT_COLORSET_WINDOW,
+ NEWT_COLORSET_ACTCHECKBOX);
+
+ priv->form = newtForm (scroll_bar, NULL, NEWT_FLAG_NOF12);
+ newtFormAddComponent (priv->form, scroll_bar);
+ newtFormSetHeight (priv->form, priv->height - 2);
+ } else
+ priv->form = newtForm (NULL, NULL, NEWT_FLAG_NOF12);
+
+ if (priv->escape_exits)
+ newtFormAddHotKey (priv->form, NEWT_KEY_ESCAPE);
+
+ cos = nmt_newt_widget_get_components (priv->content);
+ for (i = 0; cos[i]; i++)
+ newtFormAddComponent (priv->form, cos[i]);
+ g_free (cos);
+
+ if (priv->focus) {
+ newtComponent fco;
+
+ fco = nmt_newt_widget_get_focus_component (priv->focus);
+ if (fco)
+ newtFormSetCurrent (priv->form, fco);
+ }
+#ifdef HAVE_NEWTFORMGETSCROLLPOSITION
+ if (priv->scroll_position)
+ newtFormSetScrollPosition (priv->form, priv->scroll_position);
+#endif
+
+ newtOpenWindow (priv->x, priv->y, priv->width, priv->height, priv->title_lc);
+}
+
+static void
+nmt_newt_form_destroy (NmtNewtForm *form)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+
+#ifdef HAVE_NEWTFORMGETSCROLLPOSITION
+ priv->scroll_position = newtFormGetScrollPosition (priv->form);
+#endif
+
+ newtFormDestroy (priv->form);
+ priv->form = NULL;
+ newtPopWindowNoRefresh ();
+
+ nmt_newt_widget_unrealize (NMT_NEWT_WIDGET (form));
+}
+
+/* A "normal" newt program would call newtFormRun() to run newt's main loop
+ * and process events. But we want to let GLib's main loop control the program
+ * (eg, so libnm-glib can process D-Bus notifications). So we call this function
+ * to run a single iteration of newt's main loop (or rather, to run newt's
+ * main loop for 1ms) whenever there are events for newt to process (redrawing
+ * or keypresses).
+ */
+static void
+nmt_newt_form_iterate (NmtNewtForm *form)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+ NmtNewtWidget *focus;
+ struct newtExitStruct es;
+
+ if (priv->dirty) {
+ nmt_newt_form_destroy (form);
+ nmt_newt_form_build (form);
+ }
+
+ newtFormSetTimer (priv->form, 1);
+ newtFormRun (priv->form, &es);
+
+ if ( es.reason == NEWT_EXIT_HOTKEY
+ || es.reason == NEWT_EXIT_ERROR) {
+ /* The user hit Esc or there was an error. */
+ g_clear_object (&priv->focus);
+ nmt_newt_form_quit (form);
+ return;
+ }
+
+ if (es.reason == NEWT_EXIT_COMPONENT) {
+ /* The user hit Return/Space on a component; update the form focus
+ * to point that that component, and activate it.
+ */
+ focus = nmt_newt_widget_find_component (priv->content, es.u.co);
+ if (focus) {
+ nmt_newt_form_set_focus (form, focus);
+ nmt_newt_widget_activated (focus);
+ }
+ } else {
+ /* The 1ms timer ran out. Update focus but don't do anything else. */
+ focus = nmt_newt_widget_find_component (priv->content,
+ newtFormGetCurrent (priv->form));
+ if (focus)
+ nmt_newt_form_set_focus (form, focus);
+ }
+}
+
+/* @form_stack keeps track of all currently-displayed forms, from top to bottom.
+ * @keypress_source is the global stdin-monitoring GSource. When it triggers,
+ * nmt_newt_form_keypress_callback() iterates the top-most form, so it can
+ * process the keypress.
+ */
+static GSList *form_stack;
+static GSource *keypress_source;
+
+static gboolean
+nmt_newt_form_keypress_callback (int fd,
+ GIOCondition condition,
+ gpointer user_data)
+{
+ g_return_val_if_fail (form_stack != NULL, FALSE);
+
+ nmt_newt_form_iterate (form_stack->data);
+ return TRUE;
+}
+
+static gboolean
+nmt_newt_form_timeout_callback (gpointer user_data)
+{
+ if (form_stack)
+ nmt_newt_form_iterate (form_stack->data);
+ return FALSE;
+}
+
+static void
+nmt_newt_form_redraw (NmtNewtForm *form)
+{
+ g_timeout_add (0, nmt_newt_form_timeout_callback, NULL);
+}
+
+static void
+nmt_newt_form_real_show (NmtNewtForm *form)
+{
+ if (!keypress_source) {
+ GIOChannel *io;
+
+ io = g_io_channel_unix_new (STDIN_FILENO);
+ keypress_source = g_io_create_watch (io, G_IO_IN);
+ g_source_set_can_recurse (keypress_source, TRUE);
+ g_source_set_callback (keypress_source,
+ (GSourceFunc) nmt_newt_form_keypress_callback,
+ NULL, NULL);
+ g_source_attach (keypress_source, NULL);
+ g_io_channel_unref (io);
+ }
+
+ nmt_newt_form_build (form);
+ form_stack = g_slist_prepend (form_stack, g_object_ref (form));
+ nmt_newt_form_redraw (form);
+}
+
+/**
+ * nmt_newt_form_show:
+ * @form: an #NmtNewtForm
+ *
+ * Displays @form and begins running it asynchronously in the default
+ * #GMainContext. If another form is currently running, it will remain
+ * visible in the background, but will not be able to receive keyboard
+ * input until @form exits.
+ *
+ * Call nmt_newt_form_quit() to quit the form.
+ */
+void
+nmt_newt_form_show (NmtNewtForm *form)
+{
+ NMT_NEWT_FORM_GET_CLASS (form)->show (form);
+}
+
+/**
+ * nmt_newt_form_run_sync:
+ * @form: an #NmtNewtForm
+ *
+ * Displays @form as with nmt_newt_form_show(), but then iterates the
+ * #GMainContext internally until @form exits.
+ *
+ * Returns: the widget whose activation caused @form to exit, or
+ * %NULL if it was not caused by a widget. FIXME: this exit value is
+ * sort of weird and may not be 100% accurate anyway.
+ */
+NmtNewtWidget *
+nmt_newt_form_run_sync (NmtNewtForm *form)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+
+ nmt_newt_form_show (form);
+ while (priv->form)
+ g_main_context_iteration (NULL, TRUE);
+
+ return priv->focus;
+}
+
+/**
+ * nmt_newt_form_quit:
+ * @form: an #NmtNewtForm
+ *
+ * Causes @form to exit.
+ */
+void
+nmt_newt_form_quit (NmtNewtForm *form)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+
+ g_return_if_fail (priv->form != NULL);
+
+ nmt_newt_form_destroy (form);
+
+ form_stack = g_slist_remove (form_stack, form);
+
+ if (form_stack)
+ nmt_newt_form_iterate (form_stack->data);
+ else if (keypress_source) {
+ g_source_destroy (keypress_source);
+ g_clear_pointer (&keypress_source, g_source_unref);
+ }
+
+ g_signal_emit (form, signals[QUIT], 0);
+ g_object_unref (form);
+}
+
+/**
+ * nmt_newt_form_set_focus:
+ * @form: an #NmtNewtForm
+ * @widget: the widget to focus
+ *
+ * Focuses @widget in @form.
+ */
+void
+nmt_newt_form_set_focus (NmtNewtForm *form,
+ NmtNewtWidget *widget)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+
+ g_return_if_fail (priv->form != NULL);
+
+ if (priv->focus == widget)
+ return;
+
+ if (priv->focus)
+ g_object_unref (priv->focus);
+ priv->focus = widget;
+ if (priv->focus)
+ g_object_ref (priv->focus);
+}
+
+static void
+nmt_newt_form_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object);
+ int screen_width, screen_height;
+
+ switch (prop_id) {
+ case PROP_TITLE:
+ if (g_value_get_string (value)) {
+ priv->title_lc = nmt_newt_locale_from_utf8 (g_value_get_string (value));
+ } else
+ priv->title_lc = NULL;
+ break;
+ case PROP_FULLSCREEN:
+ if (g_value_get_boolean (value)) {
+ newtGetScreenSize (&screen_width, &screen_height);
+ priv->x = priv->y = 2;
+ priv->fixed_x = priv->fixed_y = TRUE;
+ priv->width = screen_width - 4;
+ priv->height = screen_height - 4;
+ priv->fixed_width = priv->fixed_height = TRUE;
+ }
+ break;
+ case PROP_FULLSCREEN_VERTICAL:
+ if (g_value_get_boolean (value)) {
+ newtGetScreenSize (&screen_width, &screen_height);
+ priv->y = 2;
+ priv->fixed_y = TRUE;
+ priv->height = screen_height - 4;
+ priv->fixed_height = TRUE;
+ }
+ break;
+ case PROP_FULLSCREEN_HORIZONTAL:
+ if (g_value_get_boolean (value)) {
+ newtGetScreenSize (&screen_width, &screen_height);
+ priv->x = 2;
+ priv->fixed_x = TRUE;
+ priv->width = screen_width - 4;
+ priv->fixed_width = TRUE;
+ }
+ break;
+ case PROP_X:
+ if (g_value_get_uint (value)) {
+ priv->x = g_value_get_uint (value);
+ priv->fixed_x = TRUE;
+ }
+ break;
+ case PROP_Y:
+ if (g_value_get_uint (value)) {
+ priv->y = g_value_get_uint (value);
+ priv->fixed_y = TRUE;
+ }
+ break;
+ case PROP_WIDTH:
+ if (g_value_get_uint (value)) {
+ priv->width = g_value_get_uint (value);
+ priv->fixed_width = TRUE;
+ }
+ break;
+ case PROP_HEIGHT:
+ if (g_value_get_uint (value)) {
+ priv->height = g_value_get_uint (value);
+ priv->fixed_height = TRUE;
+ }
+ break;
+ case PROP_PADDING:
+ priv->padding = g_value_get_uint (value);
+ break;
+ case PROP_ESCAPE_EXITS:
+ priv->escape_exits = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_form_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_TITLE:
+ if (priv->title_lc) {
+ g_value_take_string (value, nmt_newt_locale_to_utf8 (priv->title_lc));
+ } else
+ g_value_set_string (value, NULL);
+ break;
+ case PROP_X:
+ g_value_set_uint (value, priv->x);
+ break;
+ case PROP_Y:
+ g_value_set_uint (value, priv->y);
+ break;
+ case PROP_WIDTH:
+ g_value_set_uint (value, priv->width);
+ break;
+ case PROP_HEIGHT:
+ g_value_set_uint (value, priv->height);
+ break;
+ case PROP_PADDING:
+ g_value_set_uint (value, priv->padding);
+ break;
+ case PROP_ESCAPE_EXITS:
+ g_value_set_boolean (value, priv->escape_exits);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_form_class_init (NmtNewtFormClass *form_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (form_class);
+ NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (form_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (form_class);
+
+ g_type_class_add_private (form_class, sizeof (NmtNewtFormPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_form_set_property;
+ object_class->get_property = nmt_newt_form_get_property;
+ object_class->finalize = nmt_newt_form_finalize;
+
+ widget_class->needs_rebuild = nmt_newt_form_needs_rebuild;
+
+ container_class->remove = nmt_newt_form_remove;
+
+ form_class->show = nmt_newt_form_real_show;
+
+ /* signals */
+
+ /**
+ * NmtNewtForm::quit:
+ * @form: the #NmtNewtForm
+ *
+ * Emitted when the form quits.
+ */
+ signals[QUIT] =
+ g_signal_new ("quit",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NmtNewtFormClass, quit),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * NmtNewtForm:title:
+ *
+ * The form's title. If non-%NULL, this will be displayed above
+ * the form in its border.
+ */
+ g_object_class_install_property
+ (object_class, PROP_TITLE,
+ g_param_spec_string ("title", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * NmtNewtForm:fullscreen:
+ *
+ * If %TRUE, the form will fill the entire "screen" (ie, terminal
+ * window).
+ */
+ g_object_class_install_property
+ (object_class, PROP_FULLSCREEN,
+ g_param_spec_boolean ("fullscreen", "", "",
+ FALSE,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * NmtNewtForm:fullscreen-vertical:
+ *
+ * If %TRUE, the form will fill the entire "screen" (ie, terminal
+ * window) vertically, but not necessarily horizontally.
+ */
+ g_object_class_install_property
+ (object_class, PROP_FULLSCREEN_VERTICAL,
+ g_param_spec_boolean ("fullscreen-vertical", "", "",
+ FALSE,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * NmtNewtForm:fullscreen-horizontal:
+ *
+ * If %TRUE, the form will fill the entire "screen" (ie, terminal
+ * window) horizontally, but not necessarily vertically.
+ */
+ g_object_class_install_property
+ (object_class, PROP_FULLSCREEN_HORIZONTAL,
+ g_param_spec_boolean ("fullscreen-horizontal", "", "",
+ FALSE,
+ G_PARAM_WRITABLE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * NmtNewtForm:x:
+ *
+ * The form's x coordinate. By default, the form will be centered
+ * on the screen.
+ */
+ g_object_class_install_property
+ (object_class, PROP_X,
+ g_param_spec_uint ("x", "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * NmtNewtForm:y:
+ *
+ * The form's y coordinate. By default, the form will be centered
+ * on the screen.
+ */
+ g_object_class_install_property
+ (object_class, PROP_Y,
+ g_param_spec_uint ("y", "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * NmtNewtForm:width:
+ *
+ * The form's width. By default, this will be determined by the
+ * width of the form's content.
+ */
+ g_object_class_install_property
+ (object_class, PROP_WIDTH,
+ g_param_spec_uint ("width", "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * NmtNewtForm:height:
+ *
+ * The form's height. By default, this will be determined by the
+ * height of the form's content.
+ */
+ g_object_class_install_property
+ (object_class, PROP_HEIGHT,
+ g_param_spec_uint ("height", "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * NmtNewtForm:padding:
+ *
+ * The padding between the form's content and its border.
+ */
+ g_object_class_install_property
+ (object_class, PROP_PADDING,
+ g_param_spec_uint ("padding", "", "",
+ 0, G_MAXUINT, 1,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+ /**
+ * NmtNewtForm:escape-exits:
+ *
+ * If %TRUE, then hitting the Escape key will cause the form to
+ * exit.
+ */
+ g_object_class_install_property
+ (object_class, PROP_ESCAPE_EXITS,
+ g_param_spec_boolean ("escape-exits", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+}
diff --git a/clients/tui/newt/nmt-newt-form.h b/clients/tui/newt/nmt-newt-form.h
new file mode 100644
index 0000000000..be95eb68ad
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-form.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_FORM_H
+#define NMT_NEWT_FORM_H
+
+#include "nmt-newt-container.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_FORM (nmt_newt_form_get_type ())
+#define NMT_NEWT_FORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_FORM, NmtNewtForm))
+#define NMT_NEWT_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_FORM, NmtNewtFormClass))
+#define NMT_IS_NEWT_FORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_FORM))
+#define NMT_IS_NEWT_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_FORM))
+#define NMT_NEWT_FORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_FORM, NmtNewtFormClass))
+
+struct _NmtNewtForm {
+ NmtNewtContainer parent;
+
+};
+
+typedef struct {
+ NmtNewtContainerClass parent;
+
+ /* signals */
+ void (*quit) (NmtNewtForm *form);
+
+ /* methods */
+ void (*show) (NmtNewtForm *form);
+
+} NmtNewtFormClass;
+
+GType nmt_newt_form_get_type (void);
+
+NmtNewtForm *nmt_newt_form_new (const char *title);
+NmtNewtForm *nmt_newt_form_new_fullscreen (const char *title);
+
+void nmt_newt_form_set_content (NmtNewtForm *form,
+ NmtNewtWidget *content);
+
+void nmt_newt_form_show (NmtNewtForm *form);
+NmtNewtWidget *nmt_newt_form_run_sync (NmtNewtForm *form);
+void nmt_newt_form_quit (NmtNewtForm *form);
+
+void nmt_newt_form_set_focus (NmtNewtForm *form,
+ NmtNewtWidget *widget);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_FORM_H */
diff --git a/clients/tui/newt/nmt-newt-grid.c b/clients/tui/newt/nmt-newt-grid.c
new file mode 100644
index 0000000000..ac96733452
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-grid.c
@@ -0,0 +1,472 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-grid
+ * @short_description: Grid container
+ *
+ * #NmtNewtGrid is the most general-purpose container widget in NmtNewt.
+ *
+ * An #NmtNewtGrid consists of a number of rows and columns. There is
+ * no pre-established maximum row or columns. Rather, rows and columns
+ * exist if and only if there are widgets in them.
+ *
+ * The width of each column is the width of the widest widget in that
+ * column, and the height of each row is the height of the tallest
+ * widget in that row. Empty rows and empty columns take up no space,
+ * so a grid with a single widget at 0,0 would look exactly the same
+ * if the widget was at 5,10 instead.
+ *
+ * If a widget's cell ends up being larger than the widget's requested
+ * size, then by default the widget will be centered in its cell.
+ * However, this can be modified by changing its #NmtNewtGridFlags.
+ * FIXME: the FILL/ANCHOR flags can be implemented in #NmtNewtWidget
+ * and so should move there. Less clear about the EXPAND flags, which
+ * must be implemented by the container...
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-grid.h"
+
+G_DEFINE_TYPE (NmtNewtGrid, nmt_newt_grid, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_NEWT_GRID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_GRID, NmtNewtGridPrivate))
+
+typedef struct {
+ NmtNewtWidget *widget;
+ int x, y;
+ NmtNewtGridFlags flags;
+ int req_height, req_width;
+} NmtNewtGridChild;
+
+typedef struct {
+ GArray *children;
+ int max_x, max_y;
+ int *row_heights, *col_widths;
+ gboolean *expand_rows, *expand_cols;
+ int n_expand_rows, n_expand_cols;
+ int req_height, req_width;
+} NmtNewtGridPrivate;
+
+/**
+ * nmt_newt_grid_new:
+ *
+ * Creates a new #NmtNewtGrid
+ *
+ * Returns: a new #NmtNewtGrid
+ */
+NmtNewtWidget *
+nmt_newt_grid_new (void)
+{
+ return g_object_new (NMT_TYPE_NEWT_GRID, NULL);
+}
+
+static void
+nmt_newt_grid_init (NmtNewtGrid *grid)
+{
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+
+ priv->children = g_array_new (FALSE, FALSE, sizeof (NmtNewtGridChild));
+}
+
+static void
+nmt_newt_grid_finalize (GObject *object)
+{
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (object);
+
+ g_array_unref (priv->children);
+ g_clear_pointer (&priv->row_heights, g_free);
+ g_clear_pointer (&priv->col_widths, g_free);
+ g_clear_pointer (&priv->expand_rows, g_free);
+ g_clear_pointer (&priv->expand_cols, g_free);
+
+ G_OBJECT_CLASS (nmt_newt_grid_parent_class)->finalize (object);
+}
+
+static int
+child_sort_func (gconstpointer a,
+ gconstpointer b)
+{
+ NmtNewtGridChild *child_a = (NmtNewtGridChild *)a;
+ NmtNewtGridChild *child_b = (NmtNewtGridChild *)b;
+
+ if (child_a->y != child_b->y)
+ return child_a->y - child_b->y;
+ else
+ return child_a->x - child_b->x;
+}
+
+static newtComponent *
+nmt_newt_grid_get_components (NmtNewtWidget *widget)
+{
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (widget);
+ NmtNewtGridChild *children;
+ GPtrArray *cos;
+ newtComponent *child_cos;
+ int i, c;
+
+ g_array_sort (priv->children, child_sort_func);
+ children = (NmtNewtGridChild *)priv->children->data;
+
+ cos = g_ptr_array_new ();
+
+ for (i = 0; i < priv->children->len; i++) {
+ if (!nmt_newt_widget_get_visible (children[i].widget))
+ continue;
+
+ child_cos = nmt_newt_widget_get_components (children[i].widget);
+ for (c = 0; child_cos[c]; c++)
+ g_ptr_array_add (cos, child_cos[c]);
+ g_free (child_cos);
+ }
+ g_ptr_array_add (cos, NULL);
+
+ return (newtComponent *) g_ptr_array_free (cos, FALSE);
+}
+
+static void
+nmt_newt_grid_size_request (NmtNewtWidget *widget,
+ int *width,
+ int *height)
+{
+ NmtNewtGrid *grid = NMT_NEWT_GRID (widget);
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+ NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
+ int row, col, i;
+
+ g_free (priv->row_heights);
+ g_free (priv->col_widths);
+ g_free (priv->expand_rows);
+ g_free (priv->expand_cols);
+
+ priv->row_heights = g_new0 (int, priv->max_y + 1);
+ priv->col_widths = g_new0 (int, priv->max_x + 1);
+ priv->expand_rows = g_new0 (gboolean, priv->max_y + 1);
+ priv->expand_cols = g_new0 (gboolean, priv->max_x + 1);
+ priv->n_expand_rows = priv->n_expand_cols = 0;
+
+ for (row = 0; row < priv->max_y + 1; row++) {
+ for (col = 0; col < priv->max_x + 1; col++) {
+ for (i = 0; i < priv->children->len; i++) {
+ if (children[i].x != col || children[i].y != row)
+ continue;
+ if (!nmt_newt_widget_get_visible (children[i].widget))
+ continue;
+
+ nmt_newt_widget_size_request (children[i].widget,
+ &children[i].req_width,
+ &children[i].req_height);
+ if (children[i].req_height > priv->row_heights[row])
+ priv->row_heights[row] = children[i].req_height;
+ if (children[i].req_width > priv->col_widths[col])
+ priv->col_widths[col] = children[i].req_width;
+
+ if ( (children[i].flags & NMT_NEWT_GRID_EXPAND_X)
+ && !priv->expand_cols[children[i].x]) {
+ priv->expand_cols[children[i].x] = TRUE;
+ priv->n_expand_cols++;
+ }
+ if ( (children[i].flags & NMT_NEWT_GRID_EXPAND_Y)
+ && !priv->expand_rows[children[i].y]) {
+ priv->expand_rows[children[i].y] = TRUE;
+ priv->n_expand_rows++;
+ }
+ }
+ }
+ }
+
+ priv->req_height = priv->req_width = 0;
+ for (row = 0; row < priv->max_y + 1; row++)
+ priv->req_height += priv->row_heights[row];
+ for (col = 0; col < priv->max_x + 1; col++)
+ priv->req_width += priv->col_widths[col];
+
+ *height = priv->req_height;
+ *width = priv->req_width;
+}
+
+static void
+nmt_newt_grid_size_allocate (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (widget);
+ NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data, *child;
+ int i, row, col;
+ int child_x, child_y, child_width, child_height;
+ int extra, extra_all, extra_some;
+
+ extra = width - priv->req_width;
+ if (extra > 0 && priv->n_expand_cols) {
+ extra_all = extra / priv->n_expand_cols;
+ extra_some = extra % priv->n_expand_cols;
+
+ for (col = 0; col < priv->max_x + 1; col++) {
+ if (!priv->expand_cols[col])
+ continue;
+ priv->col_widths[col] += extra_all;
+ if (extra_some) {
+ priv->col_widths[col]++;
+ extra_some--;
+ }
+ }
+ }
+
+ extra = height - priv->req_height;
+ if (extra > 0 && priv->n_expand_rows) {
+ extra_all = extra / priv->n_expand_rows;
+ extra_some = extra % priv->n_expand_rows;
+
+ for (row = 0; row < priv->max_y + 1; row++) {
+ if (!priv->expand_rows[row])
+ continue;
+ priv->row_heights[row] += extra_all;
+ if (extra_some) {
+ priv->row_heights[row]++;
+ extra_some--;
+ }
+ }
+ }
+
+ for (i = 0; i < priv->children->len; i++) {
+ child = &children[i];
+ if (!nmt_newt_widget_get_visible (child->widget))
+ continue;
+
+ child_x = x;
+ for (col = 0; col < child->x; col++)
+ child_x += priv->col_widths[col];
+
+ if ((child->flags & NMT_NEWT_GRID_FILL_X) == NMT_NEWT_GRID_FILL_X) {
+ child_width = priv->col_widths[child->x];
+ } else {
+ child_width = child->req_width;
+ if (child->flags & NMT_NEWT_GRID_ANCHOR_RIGHT)
+ child_x += priv->col_widths[child->x] - child->req_width;
+ else if (!(child->flags & NMT_NEWT_GRID_ANCHOR_LEFT))
+ child_x += (priv->col_widths[child->x] - child->req_width) / 2;
+ }
+
+ child_y = y;
+ for (row = 0; row < child->y; row++)
+ child_y += priv->row_heights[row];
+
+ if ((child->flags & NMT_NEWT_GRID_FILL_Y) == NMT_NEWT_GRID_FILL_Y) {
+ child_height = priv->row_heights[child->y];
+ } else {
+ child_height = child->req_height;
+ if (child->flags & NMT_NEWT_GRID_ANCHOR_BOTTOM)
+ child_y += priv->row_heights[child->y] - child->req_height;
+ else if (!(child->flags & NMT_NEWT_GRID_ANCHOR_TOP))
+ child_y += (priv->row_heights[child->y] - child->req_height) / 2;
+ }
+
+ nmt_newt_widget_size_allocate (child->widget,
+ child_x, child_y,
+ child_width, child_height);
+ }
+}
+
+static void
+nmt_newt_grid_find_size (NmtNewtGrid *grid)
+{
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+ NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
+ int i;
+
+ priv->max_x = priv->max_y = 0;
+ for (i = 0; i < priv->children->len; i++) {
+ if (children[i].x > priv->max_x)
+ priv->max_x = children[i].x;
+ if (children[i].y > priv->max_y)
+ priv->max_y = children[i].y;
+ }
+}
+
+/**
+ * nmt_newt_grid_add:
+ * @grid: an #NmtNewtGrid
+ * @widget: the widget to add
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * Adds @widget to @grid at @x, @y. See the discussion above for more
+ * details of exactly how this works.
+ */
+void
+nmt_newt_grid_add (NmtNewtGrid *grid,
+ NmtNewtWidget *widget,
+ int x,
+ int y)
+{
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+ NmtNewtGridChild child;
+
+ NMT_NEWT_CONTAINER_CLASS (nmt_newt_grid_parent_class)->add (NMT_NEWT_CONTAINER (grid), widget);
+
+ memset (&child, 0, sizeof (child));
+ child.widget = widget;
+ child.x = x;
+ child.y = y;
+ child.flags = NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y;
+ g_array_append_val (priv->children, child);
+
+ if (x > priv->max_x)
+ priv->max_x = x;
+ if (y > priv->max_y)
+ priv->max_y = y;
+}
+
+static int
+find_child (NmtNewtGrid *grid,
+ NmtNewtWidget *widget)
+{
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+ NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
+ int i;
+
+ for (i = 0; i < priv->children->len; i++) {
+ if (children[i].widget == widget)
+ return i;
+ }
+
+ return -1;
+}
+
+static void
+nmt_newt_grid_remove (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NmtNewtGrid *grid = NMT_NEWT_GRID (container);
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+ int i;
+
+ i = find_child (grid, widget);
+ if (i != -1) {
+ g_array_remove_index (priv->children, i);
+ nmt_newt_grid_find_size (grid);
+ }
+
+ NMT_NEWT_CONTAINER_CLASS (nmt_newt_grid_parent_class)->remove (container, widget);
+}
+
+/**
+ * nmt_newt_grid_move:
+ * @grid: an #NmtNewtGrid
+ * @widget: a child of @grid
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * Moves @widget to the given new coordinates.
+ */
+void
+nmt_newt_grid_move (NmtNewtGrid *grid,
+ NmtNewtWidget *widget,
+ int x,
+ int y)
+{
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+ NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
+ int i;
+
+ i = find_child (grid, widget);
+ if (i != -1 && (children[i].x != x || children[i].y != y)) {
+ children[i].x = x;
+ children[i].y = y;
+ nmt_newt_grid_find_size (grid);
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (grid));
+ }
+}
+
+/**
+ * NmtNewtGridFlags:
+ * @NMT_NEWT_GRID_EXPAND_X: The widget's cell should expand
+ * horizontally if the grid as a whole is given more width than
+ * it requested.
+ * @NMT_NEWT_GRID_EXPAND_Y: The widget's cell should expand
+ * vertically if the grid as a whole is given more height than
+ * it requested.
+ * @NMT_NEWT_GRID_ANCHOR_LEFT: If the widget's cell is wider than
+ * the widget requested, the widget should be anchored to the
+ * left of its cell rather than being centered.
+ * @NMT_NEWT_GRID_ANCHOR_RIGHT: If the widget's cell is wider than
+ * the widget requested, the widget should be anchored to the
+ * right of its cell rather than being centered.
+ * @NMT_NEWT_GRID_FILL_X: If the widget's cell is wider than
+ * the widget requested, the widget should be allocated the
+ * full width of the cell; this is equivalent to specifying
+ * both %NMT_NEWT_GRID_ANCHOR_LEFT and %NMT_NEWT_GRID_ANCHOR_RIGHT.
+ * @NMT_NEWT_GRID_ANCHOR_TOP: If the widget's cell is taller than
+ * the widget requested, the widget should be anchored to the
+ * top of its cell rather than being centered.
+ * @NMT_NEWT_GRID_ANCHOR_BOTTOM: If the widget's cell is taller than
+ * the widget requested, the widget should be anchored to the
+ * bottom of its cell rather than being centered.
+ * @NMT_NEWT_GRID_FILL_Y: If the widget's cell is taller than
+ * the widget requested, the widget should be allocated the
+ * full height of the cell; this is equivalent to specifying
+ * both %NMT_NEWT_GRID_ANCHOR_TOP and %NMT_NEWT_GRID_ANCHOR_BOTTOM.
+ *
+ * Flags describing how a widget is placed within its grid cell.
+ */
+
+/**
+ * nmt_newt_grid_set_flags:
+ * @grid: an #NmtNewtGrid
+ * @widget: a child of @grid
+ * @flags: #NmtNewtGridFlags for @widget
+ *
+ * Sets the #NmtNewtGridFlags on @widget
+ */
+void
+nmt_newt_grid_set_flags (NmtNewtGrid *grid,
+ NmtNewtWidget *widget,
+ NmtNewtGridFlags flags)
+{
+ NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+ NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
+ int i;
+
+ i = find_child (grid, widget);
+ if (i != -1)
+ children[i].flags = flags;
+}
+
+static void
+nmt_newt_grid_class_init (NmtNewtGridClass *grid_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (grid_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (grid_class);
+ NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (grid_class);
+
+ g_type_class_add_private (grid_class, sizeof (NmtNewtGridPrivate));
+
+ /* virtual methods */
+ object_class->finalize = nmt_newt_grid_finalize;
+
+ widget_class->get_components = nmt_newt_grid_get_components;
+ widget_class->size_request = nmt_newt_grid_size_request;
+ widget_class->size_allocate = nmt_newt_grid_size_allocate;
+
+ container_class->remove = nmt_newt_grid_remove;
+}
diff --git a/clients/tui/newt/nmt-newt-grid.h b/clients/tui/newt/nmt-newt-grid.h
new file mode 100644
index 0000000000..f36a38ee8f
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-grid.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_GRID_H
+#define NMT_NEWT_GRID_H
+
+#include "nmt-newt-container.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_GRID (nmt_newt_grid_get_type ())
+#define NMT_NEWT_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_GRID, NmtNewtGrid))
+#define NMT_NEWT_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_GRID, NmtNewtGridClass))
+#define NMT_IS_NEWT_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_GRID))
+#define NMT_IS_NEWT_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_GRID))
+#define NMT_NEWT_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_GRID, NmtNewtGridClass))
+
+struct _NmtNewtGrid {
+ NmtNewtContainer parent;
+
+};
+
+typedef struct {
+ NmtNewtContainerClass parent;
+
+} NmtNewtGridClass;
+
+GType nmt_newt_grid_get_type (void);
+
+typedef enum {
+ NMT_NEWT_GRID_EXPAND_X = (1 << 0),
+ NMT_NEWT_GRID_EXPAND_Y = (1 << 1),
+ NMT_NEWT_GRID_ANCHOR_LEFT = (1 << 2),
+ NMT_NEWT_GRID_ANCHOR_RIGHT = (1 << 3),
+ NMT_NEWT_GRID_FILL_X = NMT_NEWT_GRID_ANCHOR_LEFT | NMT_NEWT_GRID_ANCHOR_RIGHT,
+ NMT_NEWT_GRID_ANCHOR_TOP = (1 << 4),
+ NMT_NEWT_GRID_ANCHOR_BOTTOM = (1 << 5),
+ NMT_NEWT_GRID_FILL_Y = NMT_NEWT_GRID_ANCHOR_TOP | NMT_NEWT_GRID_ANCHOR_BOTTOM,
+} NmtNewtGridFlags;
+
+NmtNewtWidget *nmt_newt_grid_new (void);
+
+void nmt_newt_grid_add (NmtNewtGrid *grid,
+ NmtNewtWidget *widget,
+ int x,
+ int y);
+void nmt_newt_grid_move (NmtNewtGrid *grid,
+ NmtNewtWidget *widget,
+ int x,
+ int y);
+void nmt_newt_grid_set_flags (NmtNewtGrid *grid,
+ NmtNewtWidget *widget,
+ NmtNewtGridFlags flags);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_GRID_H */
diff --git a/clients/tui/newt/nmt-newt-hacks.c b/clients/tui/newt/nmt-newt-hacks.c
new file mode 100644
index 0000000000..2d9b1725ae
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-hacks.c
@@ -0,0 +1,103 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-hacks
+ * @short_description: Hacks!
+ *
+ * This contains hacky cheating implementations of certain newt
+ * functions that were added after 0.52.15.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-hacks.h"
+
+#if !defined (HAVE_NEWTCOMPONENTGETSIZE) || !defined (HAVE_NEWTENTRYGETCURSORPOSITION)
+struct newtComponent_0_52_15_struct_hack {
+ int height, width;
+ int top, left;
+ int takesFocus;
+ int isMapped;
+
+ struct componentOps *ops;
+
+ newtCallback callback;
+ void *callbackData;
+
+ newtCallback destroyCallback;
+ void *destroyCallbackData;
+
+ void *data;
+};
+#endif
+
+#ifndef HAVE_NEWTCOMPONENTGETSIZE
+void
+newtComponentGetSize (newtComponent component,
+ int *width,
+ int *height)
+{
+ struct newtComponent_0_52_15_struct_hack *hack = (void *) component;
+
+ *width = hack->width;
+ *height = hack->height;
+}
+
+void
+newtComponentGetPosition (newtComponent component,
+ int *left,
+ int *top)
+{
+ struct newtComponent_0_52_15_struct_hack *hack = (void *) component;
+
+ *left = hack->left;
+ *top = hack->top;
+}
+#endif
+
+#ifndef HAVE_NEWTENTRYGETCURSORPOSITION
+struct newtEntry_0_52_15_struct_hack {
+ int flags;
+ char *buf;
+ const char **resultPtr;
+ int bufAlloced;
+ int bufUsed;
+ int cursorPosition;
+ /* ... */
+};
+
+int
+newtEntryGetCursorPosition (newtComponent component)
+{
+ struct newtComponent_0_52_15_struct_hack *co_hack = (void *) component;
+ struct newtEntry_0_52_15_struct_hack *entry_hack = co_hack->data;
+
+ return entry_hack->cursorPosition;
+}
+
+void
+newtEntrySetCursorPosition (newtComponent component,
+ int position)
+{
+ struct newtComponent_0_52_15_struct_hack *co_hack = (void *) component;
+ struct newtEntry_0_52_15_struct_hack *entry_hack = co_hack->data;
+
+ entry_hack->cursorPosition = position;
+}
+#endif
diff --git a/clients/tui/newt/nmt-newt-hacks.h b/clients/tui/newt/nmt-newt-hacks.h
new file mode 100644
index 0000000000..ba8464af0d
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-hacks.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_HACKS_H
+#define NMT_NEWT_HACKS_H
+
+#include "config.h"
+
+#include <newt.h>
+
+#ifndef HAVE_NEWTCOMPONENTGETSIZE
+void newtComponentGetSize (newtComponent component,
+ int *width,
+ int *height);
+
+void newtComponentGetPosition (newtComponent component,
+ int *left,
+ int *top);
+#endif
+
+#ifndef HAVE_NEWTENTRYGETCURSORPOSITION
+int newtEntryGetCursorPosition (newtComponent component);
+void newtEntrySetCursorPosition (newtComponent component,
+ int position);
+#endif
+
+#endif /* NMT_NEWT_HACKS_H */
diff --git a/clients/tui/newt/nmt-newt-label.c b/clients/tui/newt/nmt-newt-label.c
new file mode 100644
index 0000000000..9c4df8ed4e
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-label.c
@@ -0,0 +1,326 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-label
+ * @short_description: Labels
+ *
+ * #NmtNewtLabel implements a single-line label.
+ *
+ * See also #NmtNewtTextbox, for multiline.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-label.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtLabel, nmt_newt_label, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_LABEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_LABEL, NmtNewtLabelPrivate))
+
+typedef struct {
+ char *text;
+ NmtNewtLabelStyle style;
+ gboolean highlight;
+} NmtNewtLabelPrivate;
+
+enum {
+ PROP_0,
+ PROP_TEXT,
+ PROP_STYLE,
+ PROP_HIGHLIGHT,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_newt_label_new:
+ * @text: the initial label text
+ *
+ * Creates a new #NmtNewtLabel
+ *
+ * Returns: a new #NmtNewtLabel
+ */
+NmtNewtWidget *
+nmt_newt_label_new (const char *text)
+{
+ return g_object_new (NMT_TYPE_NEWT_LABEL,
+ "text", text,
+ NULL);
+}
+
+/**
+ * nmt_newt_label_set_text:
+ * @label: an #NmtNewtLabel
+ * @text: the new text
+ *
+ * Updates @label's text.
+ */
+void
+nmt_newt_label_set_text (NmtNewtLabel *label,
+ const char *text)
+{
+ NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+ if (!g_strcmp0 (priv->text, text))
+ return;
+
+ g_free (priv->text);
+ priv->text = g_strdup (text);
+
+ g_object_notify (G_OBJECT (label), "text");
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label));
+}
+
+/**
+ * nmt_newt_label_get_text:
+ * @label: an #NmtNewtLabel
+ *
+ * Gets @label's text
+ *
+ * Returns: @label's text
+ */
+const char *
+nmt_newt_label_get_text (NmtNewtLabel *label)
+{
+ NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+ return priv->text;
+}
+
+/**
+ * NmtNewtLabelStyle:
+ * @NMT_NEWT_LABEL_NORMAL: a normal label
+ * @NMT_NEWT_LABEL_PLAIN: a "plain-looking" label
+ *
+ * The label style. Normal labels are blue. Plain labels are black,
+ * making them look more like they are text in their own right rather
+ * than just being a label for something else.
+ */
+
+/**
+ * nmt_newt_label_set_style:
+ * @label: an #NmtNewtLabel
+ * @style: the #NmtNewtLabelStyle
+ *
+ * Sets the style of @label
+ */
+void
+nmt_newt_label_set_style (NmtNewtLabel *label,
+ NmtNewtLabelStyle style)
+{
+ NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+ if (priv->style == style)
+ return;
+
+ priv->style = style;
+ g_object_notify (G_OBJECT (label), "style");
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label));
+}
+
+/**
+ * nmt_newt_label_get_style:
+ * @label: an #NmtNewtLabel
+ *
+ * Gets the style of @label
+ *
+ * Returns: the style of @label
+ */
+NmtNewtLabelStyle
+nmt_newt_label_get_style (NmtNewtLabel *label)
+{
+ NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+ return priv->style;
+}
+
+/**
+ * nmt_newt_label_set_highlight:
+ * @label: an #NmtNewtLabel
+ * @highlight: %TRUE if @label should be highlighted
+ *
+ * Sets whether @label is highlighted. Highlighted labels are red;
+ * this is generally used to highlight invalid widgets.
+ */
+void
+nmt_newt_label_set_highlight (NmtNewtLabel *label,
+ gboolean highlight)
+{
+ NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+ highlight = !!highlight;
+ if (priv->highlight == highlight)
+ return;
+
+ priv->highlight = highlight;
+ g_object_notify (G_OBJECT (label), "highlight");
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label));
+}
+
+/**
+ * nmt_newt_label_get_highlight:
+ * @label: an #NmtNewtLabel
+ *
+ * Gets whether @label is highlighted.
+ *
+ * Returns: whether @label is highlighted.
+ */
+gboolean
+nmt_newt_label_get_highlight (NmtNewtLabel *label)
+{
+ NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+ return priv->highlight;
+}
+
+static void
+nmt_newt_label_init (NmtNewtLabel *label)
+{
+}
+
+static void
+nmt_newt_label_finalize (GObject *object)
+{
+ NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (object);
+
+ g_free (priv->text);
+
+ G_OBJECT_CLASS (nmt_newt_label_parent_class)->finalize (object);
+}
+
+static newtComponent
+nmt_newt_label_build_component (NmtNewtComponent *component,
+ gboolean sensitive)
+{
+ NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (component);
+ newtComponent co;
+ char *text_lc;
+
+ text_lc = nmt_newt_locale_from_utf8 (priv->text);
+ co = newtLabel (-1, -1, text_lc);
+ g_free (text_lc);
+
+ if (priv->highlight)
+ newtLabelSetColors (co, NMT_NEWT_COLORSET_BAD_LABEL);
+ else if (priv->style == NMT_NEWT_LABEL_PLAIN)
+ newtLabelSetColors (co, NMT_NEWT_COLORSET_PLAIN_LABEL);
+
+ return co;
+}
+
+static void
+nmt_newt_label_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtLabel *label = NMT_NEWT_LABEL (object);
+
+ switch (prop_id) {
+ case PROP_TEXT:
+ nmt_newt_label_set_text (label, g_value_get_string (value));
+ break;
+ case PROP_STYLE:
+ nmt_newt_label_set_style (label, g_value_get_int (value));
+ break;
+ case PROP_HIGHLIGHT:
+ nmt_newt_label_set_highlight (label, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_label_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_TEXT:
+ g_value_set_string (value, priv->text);
+ break;
+ case PROP_STYLE:
+ g_value_set_int (value, priv->style);
+ break;
+ case PROP_HIGHLIGHT:
+ g_value_set_boolean (value, priv->highlight);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_label_class_init (NmtNewtLabelClass *label_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (label_class);
+ NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (label_class);
+
+ g_type_class_add_private (label_class, sizeof (NmtNewtLabelPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_label_set_property;
+ object_class->get_property = nmt_newt_label_get_property;
+ object_class->finalize = nmt_newt_label_finalize;
+
+ component_class->build_component = nmt_newt_label_build_component;
+
+ /**
+ * NmtNewtLabel:text:
+ *
+ * The label's text
+ */
+ g_object_class_install_property
+ (object_class, PROP_TEXT,
+ g_param_spec_string ("text", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtLabel:style:
+ *
+ * The label's #NmtNewtLabelStyle
+ */
+ g_object_class_install_property
+ (object_class, PROP_STYLE,
+ g_param_spec_int ("style", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtLabel:highlight:
+ *
+ * Whether the label is highlighted.
+ */
+ g_object_class_install_property
+ (object_class, PROP_HIGHLIGHT,
+ g_param_spec_boolean ("highlight", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-label.h b/clients/tui/newt/nmt-newt-label.h
new file mode 100644
index 0000000000..a4e0dcca5c
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-label.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_LABEL_H
+#define NMT_NEWT_LABEL_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_LABEL (nmt_newt_label_get_type ())
+#define NMT_NEWT_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabel))
+#define NMT_NEWT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_LABEL, NmtNewtLabelClass))
+#define NMT_IS_NEWT_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_LABEL))
+#define NMT_IS_NEWT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_LABEL))
+#define NMT_NEWT_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabelClass))
+
+struct _NmtNewtLabel {
+ NmtNewtComponent parent;
+
+};
+
+typedef struct {
+ NmtNewtComponentClass parent;
+
+} NmtNewtLabelClass;
+
+GType nmt_newt_label_get_type (void);
+
+typedef enum {
+ NMT_NEWT_LABEL_NORMAL,
+ NMT_NEWT_LABEL_PLAIN
+} NmtNewtLabelStyle;
+
+NmtNewtWidget *nmt_newt_label_new (const char *text);
+
+void nmt_newt_label_set_text (NmtNewtLabel *label,
+ const char *text);
+const char *nmt_newt_label_get_text (NmtNewtLabel *label);
+
+void nmt_newt_label_set_style (NmtNewtLabel *label,
+ NmtNewtLabelStyle style);
+NmtNewtLabelStyle nmt_newt_label_get_style (NmtNewtLabel *label);
+
+void nmt_newt_label_set_highlight (NmtNewtLabel *label,
+ gboolean highlight);
+gboolean nmt_newt_label_get_highlight (NmtNewtLabel *label);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_LABEL_H */
diff --git a/clients/tui/newt/nmt-newt-listbox.c b/clients/tui/newt/nmt-newt-listbox.c
new file mode 100644
index 0000000000..e450df9123
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-listbox.c
@@ -0,0 +1,548 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-listbox
+ * @short_description: Single-choice listboxes
+ *
+ * #NmtNewtListbox implements a single-choice listbox.
+ *
+ * A listbox has some number of rows, each associated with an
+ * arbitrary pointer value. The pointer values do not need to be
+ * unique, but some APIs will not be usable if they aren't. You
+ * can also cause rows with %NULL keys to be treated specially.
+ *
+ * The listbox will emit #NmtNewtWidget::activate when the user
+ * presses Return on a selection.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-listbox.h"
+#include "nmt-newt-form.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtListbox, nmt_newt_listbox, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_LISTBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxPrivate))
+
+typedef struct {
+ int height, alloc_height, width;
+ gboolean fixed_height;
+ NmtNewtListboxFlags flags;
+
+ GPtrArray *entries;
+ GPtrArray *keys;
+
+ int active;
+ gpointer active_key;
+ gboolean skip_null_keys;
+
+} NmtNewtListboxPrivate;
+
+enum {
+ PROP_0,
+ PROP_HEIGHT,
+ PROP_FLAGS,
+ PROP_ACTIVE,
+ PROP_ACTIVE_KEY,
+ PROP_SKIP_NULL_KEYS,
+
+ LAST_PROP
+};
+
+/**
+ * NmtNewtListboxFlags:
+ * @NMT_NEWT_LISTBOX_SCROLL: the listbox should have a scroll bar.
+ * @NMT_NEWT_LISTBOX_BORDER: the listbox should have a border around it.
+ *
+ * Flags describing an #NmtNewtListbox
+ */
+
+/**
+ * nmt_newt_listbox_new:
+ * @height: the height of the listbox, or -1 for no fixed height
+ * @flags: the listbox flags
+ *
+ * Creates a new #NmtNewtListbox
+ *
+ * Returns: a new #NmtNewtListbox
+ */
+NmtNewtWidget *
+nmt_newt_listbox_new (int height,
+ NmtNewtListboxFlags flags)
+{
+ return g_object_new (NMT_TYPE_NEWT_LISTBOX,
+ "height", height,
+ "flags", flags,
+ NULL);
+}
+
+/**
+ * nmt_newt_listbox_append:
+ * @listbox: an #NmtNewtListbox
+ * @entry: the text for the new row
+ * @key: (allow-none): the key associated with @entry
+ *
+ * Adds a row to @listbox.
+ */
+void
+nmt_newt_listbox_append (NmtNewtListbox *listbox,
+ const char *entry,
+ gpointer key)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+ g_ptr_array_add (priv->entries, nmt_newt_locale_from_utf8 (entry));
+ g_ptr_array_add (priv->keys, key);
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (listbox));
+}
+
+/**
+ * nmt_newt_listbox_clear:
+ * @listbox: an #NmtNewtListbox
+ *
+ * Clears the contents of @listbox.
+ */
+void
+nmt_newt_listbox_clear (NmtNewtListbox *listbox)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+ g_ptr_array_set_size (priv->entries, 0);
+ g_ptr_array_set_size (priv->keys, 0);
+
+ priv->active = -1;
+ priv->active_key = NULL;
+
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (listbox));
+}
+
+/**
+ * nmt_newt_listbox_set_active:
+ * @listbox: an #NmtNewtListbox
+ * @active: the row to make active
+ *
+ * Sets @active to be the currently-selected row in @listbox,
+ * scrolling it into view if needed.
+ */
+void
+nmt_newt_listbox_set_active (NmtNewtListbox *listbox,
+ int active)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+ if (active == priv->active)
+ return;
+
+ g_return_if_fail (active >= 0 && active < priv->entries->len);
+ g_return_if_fail (!priv->skip_null_keys || priv->keys->pdata[active]);
+
+ priv->active = active;
+ priv->active_key = priv->keys->pdata[active];
+
+ g_object_notify (G_OBJECT (listbox), "active");
+ g_object_notify (G_OBJECT (listbox), "active-key");
+}
+
+/**
+ * nmt_newt_listbox_set_active_key:
+ * @listbox: an #NmtNewtListbox
+ * @active_key: the key for the row to make active
+ *
+ * Selects the (first) row in @listbox with @active_key as its key,
+ * scrolling it into view if needed.
+ */
+void
+nmt_newt_listbox_set_active_key (NmtNewtListbox *listbox,
+ gpointer active_key)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+ int i;
+
+ if (active_key == priv->active_key)
+ return;
+
+ g_return_if_fail (!priv->skip_null_keys || active_key);
+
+ for (i = 0; i < priv->keys->len; i++) {
+ if (priv->keys->pdata[i] == active_key) {
+ priv->active = i;
+ priv->active_key = active_key;
+
+ g_object_notify (G_OBJECT (listbox), "active");
+ g_object_notify (G_OBJECT (listbox), "active-key");
+ return;
+ }
+ }
+}
+
+/**
+ * nmt_newt_listbox_get_active:
+ * @listbox: an #NmtNewtListbox
+ *
+ * Gets the currently-selected row in @listbox.
+ *
+ * Returns: the currently-selected row in @listbox.
+ */
+int
+nmt_newt_listbox_get_active (NmtNewtListbox *listbox)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+ return priv->active;
+}
+
+/**
+ * nmt_newt_listbox_get_active_key:
+ * @listbox: an #NmtNewtListbox
+ *
+ * Gets the key of the currently-selected row in @listbox.
+ *
+ * Returns: the key of the currently-selected row in @listbox.
+ */
+gpointer
+nmt_newt_listbox_get_active_key (NmtNewtListbox *listbox)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+ return priv->active_key;
+}
+
+/**
+ * nmt_newt_listbox_set_height:
+ * @listbox: an #NmtNewtListbox
+ * @height: the new height, or -1 for no fixed heigh
+ *
+ * Updates @listbox's height.
+ */
+void
+nmt_newt_listbox_set_height (NmtNewtListbox *listbox,
+ int height)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+ priv->height = height;
+ priv->fixed_height = priv->height != 0;
+ g_object_notify (G_OBJECT (listbox), "height");
+}
+
+static void
+nmt_newt_listbox_init (NmtNewtListbox *listbox)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+ priv->entries = g_ptr_array_new_with_free_func (g_free);
+ priv->keys = g_ptr_array_new ();
+
+ priv->active = -1;
+}
+
+static void
+nmt_newt_listbox_finalize (GObject *object)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object);
+
+ g_ptr_array_unref (priv->entries);
+ g_ptr_array_unref (priv->keys);
+
+ G_OBJECT_CLASS (nmt_newt_listbox_parent_class)->finalize (object);
+}
+
+static void
+nmt_newt_listbox_size_request (NmtNewtWidget *widget,
+ int *width,
+ int *height)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (widget);
+
+ NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)->
+ size_request (widget, width, height);
+
+ priv->alloc_height = -1;
+ if (!priv->fixed_height)
+ *height = 1;
+ priv->width = *width;
+}
+
+static void
+nmt_newt_listbox_size_allocate (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (widget);
+
+ if (width > priv->width) {
+ newtListboxSetWidth (nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget)),
+ width);
+ }
+
+ NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)->
+ size_allocate (widget, x, y, width, height);
+
+ priv->alloc_height = height;
+
+ if (!priv->fixed_height && height != priv->height) {
+ priv->height = height;
+ nmt_newt_widget_needs_rebuild (widget);
+ }
+}
+
+static void
+update_active_internal (NmtNewtListbox *listbox,
+ int new_active)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+ if (priv->active == new_active)
+ return;
+ if (new_active >= priv->keys->len)
+ return;
+
+ if (priv->skip_null_keys && !priv->keys->pdata[new_active]) {
+ if (new_active > priv->active) {
+ while ( new_active < priv->entries->len
+ && !priv->keys->pdata[new_active])
+ new_active++;
+ } else {
+ while ( new_active >= 0
+ && !priv->keys->pdata[new_active])
+ new_active--;
+ }
+
+ if ( new_active < 0
+ || new_active >= priv->entries->len
+ || !priv->keys->pdata[new_active]) {
+ g_assert (priv->active >= 0 && priv->active < priv->entries->len);
+ return;
+ }
+ }
+
+ nmt_newt_listbox_set_active (listbox, new_active);
+}
+
+static void
+selection_changed_callback (newtComponent co,
+ void *user_data)
+{
+ NmtNewtListbox *listbox = user_data;
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+ int new_active;
+
+ new_active = GPOINTER_TO_UINT (newtListboxGetCurrent (co));
+ update_active_internal (listbox, new_active);
+
+ if (priv->active != new_active)
+ newtListboxSetCurrent (co, priv->active);
+}
+
+static guint
+convert_flags (NmtNewtListboxFlags flags)
+{
+ guint newt_flags = NEWT_FLAG_RETURNEXIT;
+
+ if (flags & NMT_NEWT_LISTBOX_SCROLL)
+ newt_flags |= NEWT_FLAG_SCROLL;
+ if (flags & NMT_NEWT_LISTBOX_BORDER)
+ newt_flags |= NEWT_FLAG_BORDER;
+
+ return newt_flags;
+}
+
+static newtComponent
+nmt_newt_listbox_build_component (NmtNewtComponent *component,
+ gboolean sensitive)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (component);
+ newtComponent co;
+ int i, active;
+
+ if (priv->active == -1)
+ update_active_internal (NMT_NEWT_LISTBOX (component), 0);
+ active = priv->active;
+
+ co = newtListbox (-1, -1, priv->height, convert_flags (priv->flags));
+ newtComponentAddCallback (co, selection_changed_callback, component);
+
+ for (i = 0; i < priv->entries->len; i++) {
+ newtListboxAppendEntry (co, priv->entries->pdata[i], GUINT_TO_POINTER (i));
+ if (active == -1 && priv->keys->pdata[i] == priv->active_key)
+ active = i;
+ }
+
+ if (active != -1)
+ newtListboxSetCurrent (co, active);
+
+ return co;
+}
+
+static void
+nmt_newt_listbox_activated (NmtNewtWidget *widget)
+{
+ NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (widget);
+ newtComponent co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget));
+
+ nmt_newt_listbox_set_active (listbox, GPOINTER_TO_UINT (newtListboxGetCurrent (co)));
+
+ NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)->activated (widget);
+}
+
+static void
+nmt_newt_listbox_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (object);
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_HEIGHT:
+ priv->height = g_value_get_int (value);
+ priv->fixed_height = (priv->height != 0);
+ break;
+ case PROP_FLAGS:
+ priv->flags = g_value_get_uint (value);
+ break;
+ case PROP_ACTIVE:
+ nmt_newt_listbox_set_active (listbox, g_value_get_int (value));
+ break;
+ case PROP_ACTIVE_KEY:
+ nmt_newt_listbox_set_active_key (listbox, g_value_get_pointer (value));
+ break;
+ case PROP_SKIP_NULL_KEYS:
+ priv->skip_null_keys = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_listbox_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_HEIGHT:
+ g_value_set_int (value, priv->height);
+ break;
+ case PROP_FLAGS:
+ g_value_set_uint (value, priv->flags);
+ break;
+ case PROP_ACTIVE:
+ g_value_set_int (value, priv->active);
+ break;
+ case PROP_ACTIVE_KEY:
+ g_value_set_pointer (value, priv->active_key);
+ break;
+ case PROP_SKIP_NULL_KEYS:
+ g_value_set_boolean (value, priv->skip_null_keys);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_listbox_class_init (NmtNewtListboxClass *listbox_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (listbox_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (listbox_class);
+ NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (listbox_class);
+
+ g_type_class_add_private (listbox_class, sizeof (NmtNewtListboxPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_listbox_set_property;
+ object_class->get_property = nmt_newt_listbox_get_property;
+ object_class->finalize = nmt_newt_listbox_finalize;
+
+ widget_class->size_request = nmt_newt_listbox_size_request;
+ widget_class->size_allocate = nmt_newt_listbox_size_allocate;
+ widget_class->activated = nmt_newt_listbox_activated;
+
+ component_class->build_component = nmt_newt_listbox_build_component;
+
+ /* properties */
+
+ /**
+ * NmtNewtListbox:height:
+ *
+ * The listbox's height, or -1 if it has no fixed height.
+ */
+ g_object_class_install_property
+ (object_class, PROP_HEIGHT,
+ g_param_spec_int ("height", "", "",
+ -1, 255, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtListbox:flags:
+ *
+ * The listbox's #NmtNewtListboxFlags.
+ */
+ g_object_class_install_property
+ (object_class, PROP_FLAGS,
+ g_param_spec_uint ("flags", "", "",
+ 0, 0xFFFF, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtListbox:active:
+ *
+ * The currently-selected row.
+ */
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE,
+ g_param_spec_int ("active", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtListbox:active-key:
+ *
+ * The key of the currently-selected row.
+ */
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE_KEY,
+ g_param_spec_pointer ("active-key", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtListbox:skip-null-keys:
+ *
+ * If %TRUE, rows with %NULL key values will be skipped over when
+ * navigating the list with the arrow keys.
+ */
+ g_object_class_install_property
+ (object_class, PROP_SKIP_NULL_KEYS,
+ g_param_spec_boolean ("skip-null-keys", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-listbox.h b/clients/tui/newt/nmt-newt-listbox.h
new file mode 100644
index 0000000000..c18c9f8e65
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-listbox.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_LISTBOX_H
+#define NMT_NEWT_LISTBOX_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_LISTBOX (nmt_newt_listbox_get_type ())
+#define NMT_NEWT_LISTBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListbox))
+#define NMT_NEWT_LISTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass))
+#define NMT_IS_NEWT_LISTBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_LISTBOX))
+#define NMT_IS_NEWT_LISTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_LISTBOX))
+#define NMT_NEWT_LISTBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass))
+
+struct _NmtNewtListbox {
+ NmtNewtComponent parent;
+
+};
+
+typedef struct {
+ NmtNewtComponentClass parent;
+
+} NmtNewtListboxClass;
+
+GType nmt_newt_listbox_get_type (void);
+
+typedef enum {
+ NMT_NEWT_LISTBOX_SCROLL = (1 << 0),
+ NMT_NEWT_LISTBOX_BORDER = (1 << 1)
+} NmtNewtListboxFlags;
+
+NmtNewtWidget *nmt_newt_listbox_new (int height,
+ NmtNewtListboxFlags flags);
+
+void nmt_newt_listbox_set_height (NmtNewtListbox *listbox,
+ int height);
+
+void nmt_newt_listbox_append (NmtNewtListbox *listbox,
+ const char *entry,
+ gpointer key);
+void nmt_newt_listbox_clear (NmtNewtListbox *listbox);
+
+void nmt_newt_listbox_set_active (NmtNewtListbox *listbox,
+ int active);
+void nmt_newt_listbox_set_active_key (NmtNewtListbox *listbox,
+ gpointer active_key);
+
+int nmt_newt_listbox_get_active (NmtNewtListbox *listbox);
+gpointer nmt_newt_listbox_get_active_key (NmtNewtListbox *listbox);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_LISTBOX_H */
diff --git a/clients/tui/newt/nmt-newt-popup.c b/clients/tui/newt/nmt-newt-popup.c
new file mode 100644
index 0000000000..80fa0181c5
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-popup.c
@@ -0,0 +1,345 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-popup
+ * @short_description: Pop-up menus
+ *
+ * #NmtNewtPopup implements a pop-up menu. When inactive, they appear
+ * the same as #NmtNewtButtons, displaying the label from the
+ * #NmtNewtPopup:active entry. When activated, they pop up a temporary
+ * #NmtNewtForm containing an #NmtNewtListbox to select from.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-newt-popup.h"
+#include "nmt-newt-form.h"
+#include "nmt-newt-hacks.h"
+#include "nmt-newt-listbox.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtPopup, nmt_newt_popup, NMT_TYPE_NEWT_BUTTON)
+
+#define NMT_NEWT_POPUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_POPUP, NmtNewtPopupPrivate))
+
+typedef struct {
+ GArray *entries;
+ int active;
+} NmtNewtPopupPrivate;
+
+enum {
+ PROP_0,
+ PROP_ACTIVE,
+ PROP_ACTIVE_ID,
+
+ LAST_PROP
+};
+
+/**
+ * NmtNewtPopupEntry:
+ * @label: the user-visible label for the entry
+ * @id: the internal ID of the entry
+ *
+ * A single entry in a pop-up menu.
+ */
+
+/**
+ * nmt_newt_popup_new:
+ * @entries: an array of #NmtNewtPopupEntry, terminated by an
+ * entry with a %NULL label
+ *
+ * Creates a new #NmtNewtPopup with the given entries.
+ *
+ * Returns: a new #NmtNewtPopup
+ */
+NmtNewtWidget *
+nmt_newt_popup_new (NmtNewtPopupEntry *entries)
+{
+ NmtNewtWidget *widget;
+ NmtNewtPopupPrivate *priv;
+ int i;
+
+ widget = g_object_new (NMT_TYPE_NEWT_POPUP, NULL);
+ priv = NMT_NEWT_POPUP_GET_PRIVATE (widget);
+
+ for (i = 0; entries[i].label; i++) {
+ NmtNewtPopupEntry entry;
+
+ entry.label = nmt_newt_locale_from_utf8 (_(entries[i].label));
+ entry.id = g_strdup (entries[i].id);
+ g_array_append_val (priv->entries, entry);
+ }
+
+ return widget;
+}
+
+static void
+popup_entry_clear_func (NmtNewtPopupEntry *entry)
+{
+ g_free (entry->label);
+ g_free (entry->id);
+}
+
+static void
+nmt_newt_popup_init (NmtNewtPopup *popup)
+{
+ NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup);
+
+ priv->entries = g_array_sized_new (FALSE, FALSE, sizeof (NmtNewtPopupEntry), 10);
+ g_array_set_clear_func (priv->entries, (GDestroyNotify) popup_entry_clear_func);
+}
+
+static void
+nmt_newt_popup_finalize (GObject *object)
+{
+ NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (object);
+
+ g_array_unref (priv->entries);
+
+ G_OBJECT_CLASS (nmt_newt_popup_parent_class)->finalize (object);
+}
+
+static newtComponent
+nmt_newt_popup_build_component (NmtNewtComponent *component,
+ gboolean sensitive)
+{
+ NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (component);
+ NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data;
+
+ nmt_newt_button_set_label (NMT_NEWT_BUTTON (component),
+ entries[priv->active].label);
+ return NMT_NEWT_COMPONENT_CLASS (nmt_newt_popup_parent_class)->
+ build_component (component, sensitive);
+}
+
+static void
+nmt_newt_popup_activated (NmtNewtWidget *widget)
+{
+ NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (widget);
+ NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data;
+ NmtNewtForm *form;
+ NmtNewtWidget *listbox, *ret;
+ int button_x, button_y;
+ int window_x, window_y;
+ int list_w, list_h;
+ int i, active;
+
+ listbox = nmt_newt_listbox_new (priv->entries->len, 0);
+ nmt_newt_widget_set_exit_on_activate (listbox, TRUE);
+ for (i = 0; i < priv->entries->len; i++)
+ nmt_newt_listbox_append (NMT_NEWT_LISTBOX (listbox), entries[i].label, NULL);
+ nmt_newt_listbox_set_active (NMT_NEWT_LISTBOX (listbox), priv->active);
+
+ nmt_newt_widget_size_request (listbox, &list_w, &list_h);
+ newtComponentGetPosition (nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget)),
+ &button_x, &button_y);
+ window_x = button_x + 4;
+ window_y = button_y + 2 - priv->active;
+
+ form = g_object_new (NMT_TYPE_NEWT_FORM,
+ "x", window_x,
+ "y", window_y,
+ "width", list_w,
+ "height", list_h,
+ "padding", 0,
+ "escape-exits", TRUE,
+ NULL);
+ nmt_newt_form_set_content (form, listbox);
+
+ ret = nmt_newt_form_run_sync (form);
+ if (ret == listbox)
+ active = nmt_newt_listbox_get_active (NMT_NEWT_LISTBOX (listbox));
+ else
+ active = priv->active;
+
+ g_object_unref (form);
+
+ if (active != priv->active) {
+ priv->active = active;
+ g_object_notify (G_OBJECT (widget), "active");
+ g_object_notify (G_OBJECT (widget), "active-id");
+ nmt_newt_widget_needs_rebuild (widget);
+ }
+
+ NMT_NEWT_WIDGET_CLASS (nmt_newt_popup_parent_class)->activated (widget);
+}
+
+/**
+ * nmt_newt_popup_get_active:
+ * @popup: a #NmtNewtPopup
+ *
+ * Gets the index of the active entry in @popup.
+ *
+ * Returns: the index of the active entry in @popup.
+ */
+int
+nmt_newt_popup_get_active (NmtNewtPopup *popup)
+{
+ NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup);
+
+ return priv->active;
+}
+
+/**
+ * nmt_newt_popup_set_active:
+ * @popup: a #NmtNewtPopup
+ * @active: the index of the new active entry
+ *
+ * Sets the active entry in @popup.
+ */
+void
+nmt_newt_popup_set_active (NmtNewtPopup *popup,
+ int active)
+{
+ NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup);
+
+ active = CLAMP (active, 0, priv->entries->len - 1);
+
+ if (active != priv->active) {
+ priv->active = active;
+ g_object_notify (G_OBJECT (popup), "active");
+ g_object_notify (G_OBJECT (popup), "active-id");
+ }
+}
+
+/**
+ * nmt_newt_popup_get_active_id:
+ * @popup: a #NmtNewtPopup
+ *
+ * Gets the textual ID of the active entry in @popup.
+ *
+ * Returns: the ID of the active entry in @popup.
+ */
+const char *
+nmt_newt_popup_get_active_id (NmtNewtPopup *popup)
+{
+ NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup);
+ NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data;
+
+ return entries[priv->active].id;
+}
+
+/**
+ * nmt_newt_popup_set_active_id:
+ * @popup: a #NmtNewtPopup
+ * @active_id: the ID of the new active entry
+ *
+ * Sets the active entry in @popup.
+ */
+void
+nmt_newt_popup_set_active_id (NmtNewtPopup *popup,
+ const char *active_id)
+{
+ NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup);
+ NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data;
+ int i;
+
+ for (i = 0; i < priv->entries->len; i++) {
+ if (!g_strcmp0 (active_id, entries[i].id)) {
+ nmt_newt_popup_set_active (popup, i);
+ return;
+ }
+ }
+}
+
+static void
+nmt_newt_popup_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtPopup *popup = NMT_NEWT_POPUP (object);
+
+ switch (prop_id) {
+ case PROP_ACTIVE:
+ nmt_newt_popup_set_active (popup, g_value_get_uint (value));
+ break;
+ case PROP_ACTIVE_ID:
+ nmt_newt_popup_set_active_id (popup, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_popup_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtPopup *popup = NMT_NEWT_POPUP (object);
+
+ switch (prop_id) {
+ case PROP_ACTIVE:
+ g_value_set_uint (value, nmt_newt_popup_get_active (popup));
+ break;
+ case PROP_ACTIVE_ID:
+ g_value_set_string (value, nmt_newt_popup_get_active_id (popup));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_popup_class_init (NmtNewtPopupClass *popup_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (popup_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (popup_class);
+ NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (popup_class);
+
+ g_type_class_add_private (popup_class, sizeof (NmtNewtPopupPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_popup_set_property;
+ object_class->get_property = nmt_newt_popup_get_property;
+ object_class->finalize = nmt_newt_popup_finalize;
+
+ widget_class->activated = nmt_newt_popup_activated;
+
+ component_class->build_component = nmt_newt_popup_build_component;
+
+ /**
+ * NmtNewtPopup:active:
+ *
+ * The index of the currently-active entry.
+ */
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE,
+ g_param_spec_uint ("active", "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtPopup:active-id:
+ *
+ * The textual ID of the currently-active entry.
+ */
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE_ID,
+ g_param_spec_string ("active-id", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-popup.h b/clients/tui/newt/nmt-newt-popup.h
new file mode 100644
index 0000000000..643ea2d8c4
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-popup.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_POPUP_H
+#define NMT_NEWT_POPUP_H
+
+#include "nmt-newt-button.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_POPUP (nmt_newt_popup_get_type ())
+#define NMT_NEWT_POPUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopup))
+#define NMT_NEWT_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_POPUP, NmtNewtPopupClass))
+#define NMT_IS_NEWT_POPUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_POPUP))
+#define NMT_IS_NEWT_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_POPUP))
+#define NMT_NEWT_POPUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopupClass))
+
+struct _NmtNewtPopup {
+ NmtNewtButton parent;
+
+};
+
+typedef struct {
+ NmtNewtButtonClass parent;
+
+} NmtNewtPopupClass;
+
+GType nmt_newt_popup_get_type (void);
+
+typedef struct {
+ char *label;
+ char *id;
+} NmtNewtPopupEntry;
+
+NmtNewtWidget *nmt_newt_popup_new (NmtNewtPopupEntry *entries);
+
+int nmt_newt_popup_get_active (NmtNewtPopup *popup);
+void nmt_newt_popup_set_active (NmtNewtPopup *popup,
+ int active);
+
+const char *nmt_newt_popup_get_active_id (NmtNewtPopup *popup);
+void nmt_newt_popup_set_active_id (NmtNewtPopup *popup,
+ const char *active_id);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_POPUP_H */
diff --git a/clients/tui/newt/nmt-newt-section.c b/clients/tui/newt/nmt-newt-section.c
new file mode 100644
index 0000000000..b1d0b5ffa3
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-section.c
@@ -0,0 +1,409 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-section
+ * @short_description: A collapsible section
+ *
+ * #NmtNewtSection is a container with two children; the header and
+ * the body. The header is always visible, but the body is only
+ * visible when the container is #NmtNewtSection:open.
+ *
+ * Note that there is no default way to open and close an
+ * #NmtNewtSection. You need to implement this yourself. (Eg, by
+ * binding the #NmtToggleButton:active property of an #NmtToggleButton
+ * in the section's header to the section's #NmtNewtSection:open
+ * property.)
+ *
+ * In addition to the header and body, the #NmtNewtSection also draws
+ * a border along the left side, indicating the extent of the section.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-section.h"
+#include "nmt-newt-grid.h"
+#include "nmt-newt-label.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtSection, nmt_newt_section, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_NEWT_SECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_SECTION, NmtNewtSectionPrivate))
+
+typedef struct {
+ NmtNewtWidget *header;
+ int hheight_req, hwidth_req;
+
+ NmtNewtWidget *body;
+ int bheight_req, bwidth_req;
+
+ NmtNewtWidget *border_grid;
+ NmtNewtWidget *border_open_label;
+ NmtNewtWidget *border_closed_label;
+ NmtNewtWidget *border_end_label;
+ GPtrArray *border_line_labels;
+
+ gboolean open;
+} NmtNewtSectionPrivate;
+
+static char *closed_glyph, *open_glyph, *line_glyph, *end_glyph;
+
+enum {
+ PROP_0,
+
+ PROP_OPEN,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_newt_section_new:
+ *
+ * Creates a new #NmtNewtSection
+ *
+ * Returns: a new #NmtNewtSection
+ */
+NmtNewtWidget *
+nmt_newt_section_new (void)
+{
+ return g_object_new (NMT_TYPE_NEWT_SECTION,
+ NULL);
+}
+
+static void
+nmt_newt_section_init (NmtNewtSection *section)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
+ NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class);
+
+ priv->border_grid = nmt_newt_grid_new ();
+ parent_class->add (NMT_NEWT_CONTAINER (section), priv->border_grid);
+
+ priv->border_open_label = nmt_newt_label_new (open_glyph);
+ nmt_newt_widget_set_visible (priv->border_open_label, FALSE);
+ nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_open_label, 0, 0);
+
+ priv->border_closed_label = nmt_newt_label_new (closed_glyph);
+ nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_closed_label, 0, 0);
+
+ priv->border_end_label = nmt_newt_label_new (end_glyph);
+ nmt_newt_widget_set_visible (priv->border_open_label, FALSE);
+ nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_end_label, 0, 1);
+
+ priv->border_line_labels = g_ptr_array_new ();
+}
+
+static void
+nmt_newt_section_finalize (GObject *object)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object);
+
+ g_ptr_array_unref (priv->border_line_labels);
+
+ G_OBJECT_CLASS (nmt_newt_section_parent_class)->finalize (object);
+}
+
+/**
+ * nmt_newt_section_set_header:
+ * @section: an #NmtNewtSection
+ * @header: the header widget
+ *
+ * Sets @section's header widget.
+ */
+void
+nmt_newt_section_set_header (NmtNewtSection *section,
+ NmtNewtWidget *header)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
+ NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class);
+ NmtNewtContainer *container = NMT_NEWT_CONTAINER (section);
+
+ if (priv->header)
+ parent_class->remove (container, priv->header);
+ priv->header = header;
+ parent_class->add (container, header);
+}
+
+/**
+ * nmt_newt_section_get_header:
+ * @section: an #NmtNewtSection
+ *
+ * Gets @section's header widget.
+ *
+ * Returns: @section's header widget.
+ */
+NmtNewtWidget *
+nmt_newt_section_get_header (NmtNewtSection *section)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
+
+ return priv->header;
+}
+
+/**
+ * nmt_newt_section_set_body:
+ * @section: an #NmtNewtSection
+ * @body: the body widget
+ *
+ * Sets @section's body widget.
+ */
+void
+nmt_newt_section_set_body (NmtNewtSection *section,
+ NmtNewtWidget *body)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
+ NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class);
+ NmtNewtContainer *container = NMT_NEWT_CONTAINER (section);
+
+ if (priv->body)
+ parent_class->remove (container, priv->body);
+ priv->body = body;
+ parent_class->add (container, body);
+}
+
+/**
+ * nmt_newt_section_get_body:
+ * @section: an #NmtNewtSection
+ *
+ * Gets @section's body widget.
+ *
+ * Returns: @section's body widget.
+ */
+NmtNewtWidget *
+nmt_newt_section_get_body (NmtNewtSection *section)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
+
+ return priv->body;
+}
+
+static void
+nmt_newt_section_remove (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NmtNewtSection *section = NMT_NEWT_SECTION (container);
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section);
+ NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class);
+
+ if (widget == priv->header)
+ priv->header = NULL;
+ else if (widget == priv->body)
+ priv->body = NULL;
+ else if (widget == priv->border_grid)
+ priv->border_grid = NULL;
+
+ parent_class->remove (container, widget);
+}
+
+static newtComponent *
+nmt_newt_section_get_components (NmtNewtWidget *widget)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget);
+ newtComponent *child_cos;
+ GPtrArray *cos;
+ int i;
+
+ g_return_val_if_fail (priv->header != NULL && priv->body != NULL, NULL);
+
+ cos = g_ptr_array_new ();
+
+ child_cos = nmt_newt_widget_get_components (priv->border_grid);
+ for (i = 0; child_cos[i]; i++)
+ g_ptr_array_add (cos, child_cos[i]);
+ g_free (child_cos);
+
+ child_cos = nmt_newt_widget_get_components (priv->header);
+ for (i = 0; child_cos[i]; i++)
+ g_ptr_array_add (cos, child_cos[i]);
+ g_free (child_cos);
+
+ if (priv->open) {
+ child_cos = nmt_newt_widget_get_components (priv->body);
+ for (i = 0; child_cos[i]; i++)
+ g_ptr_array_add (cos, child_cos[i]);
+ g_free (child_cos);
+ }
+
+ g_ptr_array_add (cos, NULL);
+ return (newtComponent *) g_ptr_array_free (cos, FALSE);
+}
+
+static void
+nmt_newt_section_size_request (NmtNewtWidget *widget,
+ int *width,
+ int *height)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget);
+ int border_width, border_height;
+
+ g_return_if_fail (priv->header != NULL && priv->body != NULL);
+
+ nmt_newt_widget_size_request (priv->border_grid, &border_width, &border_height);
+ nmt_newt_widget_size_request (priv->header, &priv->hwidth_req, &priv->hheight_req);
+ nmt_newt_widget_size_request (priv->body, &priv->bwidth_req, &priv->bheight_req);
+
+ *width = MAX (priv->hwidth_req, priv->bwidth_req) + 2;
+ *height = priv->open ? priv->hheight_req + priv->bheight_req + 1 : priv->hheight_req;
+}
+
+static void
+adjust_border_for_allocation (NmtNewtSectionPrivate *priv,
+ int height)
+{
+ int i;
+
+ /* We have to use a series of one-line labels rather than a multi-line
+ * textbox, because newt will hide any component that's partially offscreen,
+ * but we want the on-screen portion of the border to show even if part of
+ * it is offscreen.
+ */
+
+ if (height == 1) {
+ nmt_newt_widget_set_visible (priv->border_closed_label, TRUE);
+ nmt_newt_widget_set_visible (priv->border_open_label, FALSE);
+ for (i = 0; i < priv->border_line_labels->len; i++)
+ nmt_newt_widget_set_visible (priv->border_line_labels->pdata[i], FALSE);
+ nmt_newt_widget_set_visible (priv->border_end_label, FALSE);
+ } else {
+ nmt_newt_widget_set_visible (priv->border_closed_label, FALSE);
+ nmt_newt_widget_set_visible (priv->border_open_label, TRUE);
+ for (i = 0; i < height - 2; i++) {
+ if (i >= priv->border_line_labels->len) {
+ NmtNewtWidget *label;
+
+ label = nmt_newt_label_new (line_glyph);
+ g_ptr_array_add (priv->border_line_labels, label);
+ nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), label, 0, i + 1);
+ } else
+ nmt_newt_widget_set_visible (priv->border_line_labels->pdata[i], TRUE);
+ }
+ nmt_newt_widget_set_visible (priv->border_end_label, TRUE);
+ nmt_newt_grid_move (NMT_NEWT_GRID (priv->border_grid), priv->border_end_label, 0, height - 1);
+ }
+}
+
+static void
+nmt_newt_section_size_allocate (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget);
+
+ adjust_border_for_allocation (priv, height);
+
+ nmt_newt_widget_size_allocate (priv->border_grid, x, y, 1, height);
+ nmt_newt_widget_size_allocate (priv->header, x + 2, y, width, priv->hheight_req);
+ if (priv->open) {
+ nmt_newt_widget_size_allocate (priv->body, x + 2, y + priv->hheight_req,
+ width, height - priv->hheight_req);
+ }
+}
+
+static void
+nmt_newt_section_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_OPEN:
+ priv->open = g_value_get_boolean (value);
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (object));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_section_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_OPEN:
+ g_value_set_boolean (value, priv->open);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_section_class_init (NmtNewtSectionClass *section_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (section_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (section_class);
+ NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (section_class);
+
+ g_type_class_add_private (section_class, sizeof (NmtNewtSectionPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_section_set_property;
+ object_class->get_property = nmt_newt_section_get_property;
+ object_class->finalize = nmt_newt_section_finalize;
+
+ widget_class->get_components = nmt_newt_section_get_components;
+ widget_class->size_request = nmt_newt_section_size_request;
+ widget_class->size_allocate = nmt_newt_section_size_allocate;
+
+ container_class->remove = nmt_newt_section_remove;
+
+ /* properties */
+
+ /**
+ * NmtNewtSection:open:
+ *
+ * %TRUE if the section is open (ie, its body is visible), %FALSE
+ * if not.
+ */
+ g_object_class_install_property
+ (object_class, PROP_OPEN,
+ g_param_spec_boolean ("open", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+
+ /* globals */
+ closed_glyph = nmt_newt_locale_from_utf8 ("\342\225\220"); /* ═ */
+ open_glyph = nmt_newt_locale_from_utf8 ("\342\225\244"); /* ╤ */
+ line_glyph = nmt_newt_locale_from_utf8 ("\342\224\202"); /* │ */
+ end_glyph = nmt_newt_locale_from_utf8 ("\342\224\224"); /* └ */
+ if (!*closed_glyph || !*open_glyph || !*line_glyph || !*end_glyph) {
+ g_free (closed_glyph);
+ g_free (open_glyph);
+ g_free (line_glyph);
+ g_free (end_glyph);
+
+ closed_glyph = g_strdup ("-");
+ open_glyph = g_strdup ("+");
+ line_glyph = g_strdup ("|");
+ end_glyph = g_strdup ("\\");
+ }
+}
diff --git a/clients/tui/newt/nmt-newt-section.h b/clients/tui/newt/nmt-newt-section.h
new file mode 100644
index 0000000000..a943ba2000
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-section.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_SECTION_H
+#define NMT_NEWT_SECTION_H
+
+#include "nmt-newt-container.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_SECTION (nmt_newt_section_get_type ())
+#define NMT_NEWT_SECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSection))
+#define NMT_NEWT_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass))
+#define NMT_IS_NEWT_SECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_SECTION))
+#define NMT_IS_NEWT_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_SECTION))
+#define NMT_NEWT_SECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass))
+
+struct _NmtNewtSection {
+ NmtNewtContainer parent;
+
+};
+
+typedef struct {
+ NmtNewtContainerClass parent;
+
+} NmtNewtSectionClass;
+
+GType nmt_newt_section_get_type (void);
+
+NmtNewtWidget *nmt_newt_section_new (void);
+
+void nmt_newt_section_set_header (NmtNewtSection *section,
+ NmtNewtWidget *header);
+NmtNewtWidget *nmt_newt_section_get_header (NmtNewtSection *section);
+
+void nmt_newt_section_set_body (NmtNewtSection *section,
+ NmtNewtWidget *body);
+NmtNewtWidget *nmt_newt_section_get_body (NmtNewtSection *section);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_SECTION_H */
diff --git a/clients/tui/newt/nmt-newt-separator.c b/clients/tui/newt/nmt-newt-separator.c
new file mode 100644
index 0000000000..07deb1af90
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-separator.c
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-separator
+ * @short_description: Separator
+ *
+ * #NmtNewtSeparator is just a blank label, which is used in a few places
+ * where a widget is needed but none is desired, or to add blank space
+ * between widgets in containers that don't implement padding.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-separator.h"
+
+G_DEFINE_TYPE (NmtNewtSeparator, nmt_newt_separator, NMT_TYPE_NEWT_COMPONENT)
+
+/**
+ * nmt_newt_separator_new:
+ *
+ * Creates a new #NmtNewtSeparator.
+ *
+ * Returns: a new #NmtNewtSeparator
+ */
+NmtNewtWidget *
+nmt_newt_separator_new (void)
+{
+ return g_object_new (NMT_TYPE_NEWT_SEPARATOR, NULL);
+}
+
+static void
+nmt_newt_separator_init (NmtNewtSeparator *separator)
+{
+}
+
+static newtComponent
+nmt_newt_separator_build_component (NmtNewtComponent *component,
+ gboolean sensitive)
+{
+ return newtLabel (-1, -1, " ");
+}
+
+static void
+nmt_newt_separator_class_init (NmtNewtSeparatorClass *separator_class)
+{
+ NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (separator_class);
+
+ /* virtual methods */
+ component_class->build_component = nmt_newt_separator_build_component;
+}
diff --git a/clients/tui/newt/nmt-newt-separator.h b/clients/tui/newt/nmt-newt-separator.h
new file mode 100644
index 0000000000..3f4183d588
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-separator.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_SEPARATOR_H
+#define NMT_NEWT_SEPARATOR_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_SEPARATOR (nmt_newt_separator_get_type ())
+#define NMT_NEWT_SEPARATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparator))
+#define NMT_NEWT_SEPARATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparatorClass))
+#define NMT_IS_NEWT_SEPARATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_SEPARATOR))
+#define NMT_IS_NEWT_SEPARATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_SEPARATOR))
+#define NMT_NEWT_SEPARATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparatorClass))
+
+struct _NmtNewtSeparator {
+ NmtNewtComponent parent;
+
+};
+
+typedef struct {
+ NmtNewtComponentClass parent;
+
+} NmtNewtSeparatorClass;
+
+GType nmt_newt_separator_get_type (void);
+
+NmtNewtWidget *nmt_newt_separator_new (void);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_SEPARATOR_H */
diff --git a/clients/tui/newt/nmt-newt-stack.c b/clients/tui/newt/nmt-newt-stack.c
new file mode 100644
index 0000000000..d94bdebffc
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-stack.c
@@ -0,0 +1,368 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-stack
+ * @short_description: A stack of alternative widgets
+ *
+ * #NmtNewtStack implements a stack of widgets, only one of which is
+ * visible at any time.
+ *
+ * The height and width of the widget is determined only by its
+ * visible child. Likewise, the widget's #NmtNewtWidget:valid is
+ * determined only by the validity of its visible child, not its other
+ * children.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-stack.h"
+
+G_DEFINE_TYPE (NmtNewtStack, nmt_newt_stack, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_NEWT_STACK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_STACK, NmtNewtStackPrivate))
+
+typedef struct {
+ GPtrArray *children;
+ GPtrArray *ids;
+
+ guint active;
+} NmtNewtStackPrivate;
+
+enum {
+ PROP_0,
+ PROP_ACTIVE,
+ PROP_ACTIVE_ID,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_newt_stack_new:
+ *
+ * Creates a new #NmtNewtStack
+ *
+ * Returns: a new #NmtNewtStack
+ */
+NmtNewtWidget *
+nmt_newt_stack_new (void)
+{
+ return g_object_new (NMT_TYPE_NEWT_STACK, NULL);
+}
+
+static void
+nmt_newt_stack_init (NmtNewtStack *stack)
+{
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+
+ priv->children = g_ptr_array_new ();
+ priv->ids = g_ptr_array_new_with_free_func (g_free);
+}
+
+static void
+nmt_newt_stack_finalize (GObject *object)
+{
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (object);
+
+ g_ptr_array_unref (priv->children);
+ g_ptr_array_unref (priv->ids);
+
+ G_OBJECT_CLASS (nmt_newt_stack_parent_class)->finalize (object);
+}
+
+static newtComponent *
+nmt_newt_stack_get_components (NmtNewtWidget *widget)
+{
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (widget);
+
+ if (priv->active > priv->children->len)
+ return NULL;
+
+ return nmt_newt_widget_get_components (priv->children->pdata[priv->active]);
+}
+
+static void
+nmt_newt_stack_size_request (NmtNewtWidget *widget,
+ int *width,
+ int *height)
+{
+ NmtNewtStack *stack = NMT_NEWT_STACK (widget);
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+ int i, child_width, child_height;
+
+ if (priv->active > priv->children->len) {
+ *width = *height = 0;
+ return;
+ }
+
+ /* We size-request all pages so that embedded NmtPageGrids will
+ * participate in their size-grouping (so that switching pages
+ * won't result in the column widths changing).
+ */
+ for (i = 0; i < priv->children->len; i++) {
+ nmt_newt_widget_size_request (priv->children->pdata[i], &child_width, &child_height);
+ if (i == priv->active) {
+ *width = child_width;
+ *height = child_height;
+ }
+ }
+}
+
+static void
+nmt_newt_stack_size_allocate (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (widget);
+
+ if (priv->active > priv->children->len)
+ return;
+
+ nmt_newt_widget_size_allocate (priv->children->pdata[priv->active], x, y, width, height);
+}
+
+/**
+ * nmt_newt_stack_add:
+ * @stack: an #NmtNewtStack
+ * @id: the ID for the new page
+ * @widget: the widget to add
+ *
+ * Adds @widget to @stack with the given @id.
+ */
+void
+nmt_newt_stack_add (NmtNewtStack *stack,
+ const char *id,
+ NmtNewtWidget *widget)
+{
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+
+ g_ptr_array_add (priv->children, widget);
+ g_ptr_array_add (priv->ids, g_strdup (id));
+
+ NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)->add (NMT_NEWT_CONTAINER (stack), widget);
+}
+
+static void
+nmt_newt_stack_remove (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NmtNewtStack *stack = NMT_NEWT_STACK (container);
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+ int i;
+
+ NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)->remove (container, widget);
+
+ for (i = 0; i < priv->children->len; i++) {
+ if (priv->children->pdata[i] == widget) {
+ g_ptr_array_remove_index (priv->children, i);
+ g_ptr_array_remove_index (priv->ids, i);
+ return;
+ }
+ }
+}
+
+static void
+nmt_newt_stack_child_validity_changed (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (container);
+
+ if (priv->active > priv->children->len)
+ return;
+
+ if (priv->children->pdata[priv->active] == (gpointer) widget) {
+ NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)->
+ child_validity_changed (container, widget);
+ }
+}
+
+/**
+ * nmt_newt_stack_set_active:
+ * @stack: an #NmtNewtStack
+ * @active: the index of the new active page
+ *
+ * Sets the active page on @stack to @active.
+ */
+void
+nmt_newt_stack_set_active (NmtNewtStack *stack,
+ guint active)
+{
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+
+ if (priv->active == active)
+ return;
+
+ priv->active = active;
+ g_object_notify (G_OBJECT (stack), "active");
+ g_object_notify (G_OBJECT (stack), "active-id");
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (stack));
+}
+
+/**
+ * nmt_newt_stack_get_active:
+ * @stack: an #NmtNewtStack
+ *
+ * Gets the index of the active page on @stack
+ *
+ * Returns: the index of the active page on @stack
+ */
+guint
+nmt_newt_stack_get_active (NmtNewtStack *stack)
+{
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+
+ return priv->active;
+}
+
+/**
+ * nmt_newt_stack_set_active_id:
+ * @stack: an #NmtNewtStack
+ * @active_id: the ID of the new active page
+ *
+ * Sets the active page on @stack to @active_id.
+ */
+void
+nmt_newt_stack_set_active_id (NmtNewtStack *stack,
+ const char *id)
+{
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+ int i;
+
+ if (!g_strcmp0 (priv->ids->pdata[priv->active], id))
+ return;
+
+ for (i = 0; i < priv->ids->len; i++) {
+ if (!g_strcmp0 (priv->ids->pdata[i], id)) {
+ priv->active = i;
+ g_object_notify (G_OBJECT (stack), "active");
+ g_object_notify (G_OBJECT (stack), "active-id");
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (stack));
+ return;
+ }
+ }
+}
+
+/**
+ * nmt_newt_stack_get_active_id:
+ * @stack: an #NmtNewtStack
+ *
+ * Gets the ID of the active page on @stack
+ *
+ * Returns: the ID of the active page on @stack
+ */
+const char *
+nmt_newt_stack_get_active_id (NmtNewtStack *stack)
+{
+ NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+
+ if (priv->active > priv->children->len)
+ return NULL;
+
+ return priv->ids->pdata[priv->active];
+}
+
+static void
+nmt_newt_stack_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtStack *stack = NMT_NEWT_STACK (object);
+
+ switch (prop_id) {
+ case PROP_ACTIVE:
+ nmt_newt_stack_set_active (stack, g_value_get_uint (value));
+ break;
+ case PROP_ACTIVE_ID:
+ nmt_newt_stack_set_active_id (stack, g_value_get_string (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_stack_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtStack *stack = NMT_NEWT_STACK (object);
+
+ switch (prop_id) {
+ case PROP_ACTIVE:
+ g_value_set_uint (value, nmt_newt_stack_get_active (stack));
+ break;
+ case PROP_ACTIVE_ID:
+ g_value_set_string (value, nmt_newt_stack_get_active_id (stack));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_stack_class_init (NmtNewtStackClass *stack_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (stack_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (stack_class);
+ NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (stack_class);
+
+ g_type_class_add_private (stack_class, sizeof (NmtNewtStackPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_stack_set_property;
+ object_class->get_property = nmt_newt_stack_get_property;
+ object_class->finalize = nmt_newt_stack_finalize;
+
+ widget_class->get_components = nmt_newt_stack_get_components;
+ widget_class->size_request = nmt_newt_stack_size_request;
+ widget_class->size_allocate = nmt_newt_stack_size_allocate;
+
+ container_class->remove = nmt_newt_stack_remove;
+ container_class->child_validity_changed = nmt_newt_stack_child_validity_changed;
+
+ /**
+ * NmtNewtStack:active:
+ *
+ * The index of the active page
+ */
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE,
+ g_param_spec_uint ("active", "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtStack:active-id:
+ *
+ * The ID of the active page
+ */
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE_ID,
+ g_param_spec_string ("active-id", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-stack.h b/clients/tui/newt/nmt-newt-stack.h
new file mode 100644
index 0000000000..09afe4baf6
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-stack.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_STACK_H
+#define NMT_NEWT_STACK_H
+
+#include "nmt-newt-container.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_STACK (nmt_newt_stack_get_type ())
+#define NMT_NEWT_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_STACK, NmtNewtStack))
+#define NMT_NEWT_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_STACK, NmtNewtStackClass))
+#define NMT_IS_NEWT_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_STACK))
+#define NMT_IS_NEWT_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_STACK))
+#define NMT_NEWT_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_STACK, NmtNewtStackClass))
+
+struct _NmtNewtStack {
+ NmtNewtContainer parent;
+
+};
+
+typedef struct {
+ NmtNewtContainerClass parent;
+
+} NmtNewtStackClass;
+
+GType nmt_newt_stack_get_type (void);
+
+NmtNewtWidget *nmt_newt_stack_new (void);
+
+void nmt_newt_stack_add (NmtNewtStack *stack,
+ const char *id,
+ NmtNewtWidget *widget);
+
+void nmt_newt_stack_set_active (NmtNewtStack *stack,
+ guint active);
+guint nmt_newt_stack_get_active (NmtNewtStack *stack);
+
+void nmt_newt_stack_set_active_id (NmtNewtStack *stack,
+ const char *id);
+const char * nmt_newt_stack_get_active_id (NmtNewtStack *stack);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_STACK_H */
diff --git a/clients/tui/newt/nmt-newt-textbox.c b/clients/tui/newt/nmt-newt-textbox.c
new file mode 100644
index 0000000000..3d297160d3
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-textbox.c
@@ -0,0 +1,295 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-textbox
+ * @short_description: Multi-line text box
+ *
+ * #NmtNewtTextbox implements a multi-line text, optionally with
+ * word-wrapping.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-textbox.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtTextbox, nmt_newt_textbox, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_TEXTBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxPrivate))
+
+typedef struct {
+ int wrap_width;
+ NmtNewtTextboxFlags flags;
+
+ char *text;
+ int width, height;
+} NmtNewtTextboxPrivate;
+
+enum {
+ PROP_0,
+ PROP_TEXT,
+ PROP_FLAGS,
+ PROP_WRAP_WIDTH,
+
+ LAST_PROP
+};
+
+/**
+ * NmtNewtTextboxFlags:
+ * @NMT_NEWT_TEXTBOX_SCROLLABLE: the textbox should be scollable.
+ * @NMT_NEWT_TEXTBOX_SET_BACKGROUND: the textbox should have a
+ * white background
+ *
+ * Flags for an #NmtNewtTextbox
+ */
+
+/**
+ * nmt_newt_textbox_new:
+ * @flags: the textbox's flags
+ * @wrap_width: width in characters at which to word-wrap, or
+ * 0 to not wrap.
+ *
+ * Creates a new #NmtNewtTextbox.
+ *
+ * Returns: a new #NmtNewtTextbox
+ */
+NmtNewtWidget *
+nmt_newt_textbox_new (NmtNewtTextboxFlags flags,
+ int wrap_width)
+{
+ return g_object_new (NMT_TYPE_NEWT_TEXTBOX,
+ "flags", flags,
+ "wrap-width", wrap_width,
+ NULL);
+}
+
+/**
+ * nmt_newt_textbox_get_text:
+ * @textbox: an #NmtNewtTextbox
+ *
+ * Gets @textbox's text
+ *
+ * Returns: @textbox's text
+ */
+void
+nmt_newt_textbox_set_text (NmtNewtTextbox *textbox,
+ const char *text)
+{
+ NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox);
+ char **lines;
+ int i, width;
+
+ if (!text)
+ text = "";
+ if (!strcmp (priv->text, text))
+ return;
+
+ g_free (priv->text);
+ priv->text = g_strdup (text);
+
+ priv->width = priv->height = 0;
+ lines = g_strsplit (priv->text, "\n", -1);
+ for (i = 0; lines[i]; i++) {
+ width = nmt_newt_text_width (lines[i]);
+ if (width > priv->width)
+ priv->width = width;
+ }
+ g_free (lines);
+ priv->height = MIN (i, 1);
+
+ g_object_notify (G_OBJECT (textbox), "text");
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (textbox));
+}
+
+/**
+ * nmt_newt_textbox_get_text:
+ * @textbox: an #NmtNewtTextbox
+ *
+ * Gets @textbox's text
+ *
+ * Returns: @textbox's text
+ */
+const char *
+nmt_newt_textbox_get_text (NmtNewtTextbox *textbox)
+{
+ NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox);
+
+ return priv->text;
+}
+
+static void
+nmt_newt_textbox_init (NmtNewtTextbox *textbox)
+{
+ NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox);
+
+ priv->text = g_strdup ("");
+}
+
+static void
+nmt_newt_textbox_finalize (GObject *object)
+{
+ NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (object);
+
+ g_free (priv->text);
+
+ G_OBJECT_CLASS (nmt_newt_textbox_parent_class)->finalize (object);
+}
+
+static guint
+convert_flags (NmtNewtTextboxFlags flags)
+{
+ guint newt_flags = 0;
+
+ if (flags & NMT_NEWT_TEXTBOX_SCROLLABLE)
+ newt_flags |= NEWT_FLAG_SCROLL;
+
+ return newt_flags;
+}
+
+static newtComponent
+nmt_newt_textbox_build_component (NmtNewtComponent *component,
+ gboolean sensitive)
+{
+ NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (component);
+ newtComponent co;
+ const char *text;
+ char *text_lc;
+
+ text = priv->text;
+ if (!*text)
+ text = "\n";
+
+ text_lc = nmt_newt_locale_from_utf8 (text);
+ if (priv->wrap_width > 0) {
+ co = newtTextboxReflowed (-1, -1, text_lc, priv->wrap_width, 0, 0, 0);
+ } else {
+ co = newtTextbox (-1, -1, priv->width, priv->height, convert_flags (priv->flags));
+ newtTextboxSetText (co, text_lc);
+ }
+ g_free (text_lc);
+
+ if (priv->flags & NMT_NEWT_TEXTBOX_SET_BACKGROUND)
+ newtTextboxSetColors (co, NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, NEWT_COLORSET_ACTTEXTBOX);
+
+ return co;
+}
+
+static void
+nmt_newt_textbox_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtTextbox *textbox = NMT_NEWT_TEXTBOX (object);
+ NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox);
+
+ switch (prop_id) {
+ case PROP_TEXT:
+ nmt_newt_textbox_set_text (textbox, g_value_get_string (value));
+ break;
+ case PROP_FLAGS:
+ priv->flags = g_value_get_uint (value);
+ break;
+ case PROP_WRAP_WIDTH:
+ priv->wrap_width = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_textbox_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_TEXT:
+ g_value_set_string (value, priv->text);
+ break;
+ case PROP_FLAGS:
+ g_value_set_uint (value, priv->flags);
+ break;
+ case PROP_WRAP_WIDTH:
+ g_value_set_int (value, priv->wrap_width);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_textbox_class_init (NmtNewtTextboxClass *textbox_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (textbox_class);
+ NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (textbox_class);
+
+ g_type_class_add_private (textbox_class, sizeof (NmtNewtTextboxPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_textbox_set_property;
+ object_class->get_property = nmt_newt_textbox_get_property;
+ object_class->finalize = nmt_newt_textbox_finalize;
+
+ component_class->build_component = nmt_newt_textbox_build_component;
+
+ /**
+ * NmtNewtTextbox:text:
+ *
+ * The textbox's text
+ */
+ g_object_class_install_property
+ (object_class, PROP_TEXT,
+ g_param_spec_string ("text", "", "",
+ "",
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtTextbox:flags:
+ *
+ * The textbox's flags
+ */
+ g_object_class_install_property
+ (object_class, PROP_FLAGS,
+ g_param_spec_uint ("flags", "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtTextbox:wrap-width:
+ *
+ * The width in characters at which the textbox's text
+ * will wrap, or 0 if it does not wrap.
+ */
+ g_object_class_install_property
+ (object_class, PROP_WRAP_WIDTH,
+ g_param_spec_int ("wrap-width", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-textbox.h b/clients/tui/newt/nmt-newt-textbox.h
new file mode 100644
index 0000000000..b3743aea05
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-textbox.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_TEXTBOX_H
+#define NMT_NEWT_TEXTBOX_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_TEXTBOX (nmt_newt_textbox_get_type ())
+#define NMT_NEWT_TEXTBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextbox))
+#define NMT_NEWT_TEXTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxClass))
+#define NMT_IS_NEWT_TEXTBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_TEXTBOX))
+#define NMT_IS_NEWT_TEXTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_TEXTBOX))
+#define NMT_NEWT_TEXTBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxClass))
+
+struct _NmtNewtTextbox {
+ NmtNewtComponent parent;
+
+};
+
+typedef struct {
+ NmtNewtComponentClass parent;
+
+} NmtNewtTextboxClass;
+
+GType nmt_newt_textbox_get_type (void);
+
+typedef enum {
+ NMT_NEWT_TEXTBOX_SCROLLABLE = (1 << 0),
+ NMT_NEWT_TEXTBOX_SET_BACKGROUND = (1 << 1)
+} NmtNewtTextboxFlags;
+
+NmtNewtWidget *nmt_newt_textbox_new (NmtNewtTextboxFlags flags,
+ int wrap_width);
+
+void nmt_newt_textbox_set_text (NmtNewtTextbox *textbox,
+ const char *text);
+const char *nmt_newt_textbox_get_text (NmtNewtTextbox *textbox);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_TEXTBOX_H */
diff --git a/clients/tui/newt/nmt-newt-toggle-button.c b/clients/tui/newt/nmt-newt-toggle-button.c
new file mode 100644
index 0000000000..ec4d9aca51
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-toggle-button.c
@@ -0,0 +1,237 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-toggle-button
+ * @short_description: Toggle buttons
+ *
+ * #NmtNewtToggleButton implements a two-state toggle button.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-toggle-button.h"
+
+G_DEFINE_TYPE (NmtNewtToggleButton, nmt_newt_toggle_button, NMT_TYPE_NEWT_BUTTON)
+
+#define NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonPrivate))
+
+typedef struct {
+ char *on_label, *off_label;
+ gboolean active;
+} NmtNewtToggleButtonPrivate;
+
+enum {
+ PROP_0,
+ PROP_ON_LABEL,
+ PROP_OFF_LABEL,
+ PROP_ACTIVE,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_newt_toggle_button_new:
+ * @on_label: the button's label when it is in the "on" state
+ * @off_label: the button's label when it is in the "off" state
+ *
+ * Creates a new #NmtNewtToggleButton
+ *
+ * Returns: a new #NmtNewtToggleButton
+ */
+NmtNewtWidget *
+nmt_newt_toggle_button_new (const char *on_label,
+ const char *off_label)
+{
+ return g_object_new (NMT_TYPE_NEWT_TOGGLE_BUTTON,
+ "on-label", on_label,
+ "off-label", off_label,
+ NULL);
+}
+
+/**
+ * nmt_newt_toggle_button_get_active:
+ * @button: an #NmtNewtToggleButton
+ *
+ * Gets whether @button is currently "on" or "off"
+ *
+ * Returns: whether @button is currently "on" (%TRUE) or "off" (%FALSE)
+ */
+gboolean
+nmt_newt_toggle_button_get_active (NmtNewtToggleButton *button)
+{
+ NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (button);
+
+ return priv->active;
+}
+
+/**
+ * nmt_newt_toggle_button_set_active:
+ * @button: an #NmtNewtToggleButton
+ * @active: whether @button should be "on" or "off"
+ *
+ * Sets whether @button is currently "on" or "off"
+ */
+void
+nmt_newt_toggle_button_set_active (NmtNewtToggleButton *button,
+ gboolean active)
+{
+ NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (button);
+
+ if (priv->active == active)
+ return;
+
+ priv->active = active;
+ g_object_set (G_OBJECT (button),
+ "label", active ? priv->on_label : priv->off_label,
+ NULL);
+ g_object_notify (G_OBJECT (button), "active");
+}
+
+static void
+nmt_newt_toggle_button_init (NmtNewtToggleButton *button)
+{
+}
+
+static void
+nmt_newt_toggle_button_finalize (GObject *object)
+{
+ NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object);
+
+ g_free (priv->on_label);
+ g_free (priv->off_label);
+
+ G_OBJECT_CLASS (nmt_newt_toggle_button_parent_class)->finalize (object);
+}
+
+static void
+nmt_newt_toggle_button_activated (NmtNewtWidget *widget)
+{
+ NmtNewtToggleButton *button = NMT_NEWT_TOGGLE_BUTTON (widget);
+
+ nmt_newt_toggle_button_set_active (button, !nmt_newt_toggle_button_get_active (button));
+
+ NMT_NEWT_WIDGET_CLASS (nmt_newt_toggle_button_parent_class)->activated (widget);
+}
+
+static void
+nmt_newt_toggle_button_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_ON_LABEL:
+ g_free (priv->on_label);
+ priv->on_label = g_value_dup_string (value);
+ if (priv->active)
+ g_object_set (object, "label", priv->on_label, NULL);
+ break;
+ case PROP_OFF_LABEL:
+ g_free (priv->off_label);
+ priv->off_label = g_value_dup_string (value);
+ if (!priv->active)
+ g_object_set (object, "label", priv->off_label, NULL);
+ break;
+ case PROP_ACTIVE:
+ priv->active = g_value_get_boolean (value);
+ g_object_set (object,
+ "label", priv->active ? priv->on_label : priv->off_label,
+ NULL);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_toggle_button_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_ON_LABEL:
+ g_value_set_string (value, priv->on_label);
+ break;
+ case PROP_OFF_LABEL:
+ g_value_set_string (value, priv->off_label);
+ break;
+ case PROP_ACTIVE:
+ g_value_set_boolean (value, priv->active);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_toggle_button_class_init (NmtNewtToggleButtonClass *button_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (button_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (button_class);
+
+ g_type_class_add_private (button_class, sizeof (NmtNewtToggleButtonPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_toggle_button_set_property;
+ object_class->get_property = nmt_newt_toggle_button_get_property;
+ object_class->finalize = nmt_newt_toggle_button_finalize;
+
+ widget_class->activated = nmt_newt_toggle_button_activated;
+
+ /**
+ * NmtNewtToggleButton:on-label:
+ *
+ * The label the button displays when it is "on".
+ */
+ g_object_class_install_property
+ (object_class, PROP_ON_LABEL,
+ g_param_spec_string ("on-label", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtToggleButton:off-label:
+ *
+ * The label the button displays when it is "off".
+ */
+ g_object_class_install_property
+ (object_class, PROP_OFF_LABEL,
+ g_param_spec_string ("off-label", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtToggleButton:active:
+ *
+ * Whether the button is currently "on" (%TRUE) or "off" (%FALSE)
+ */
+ g_object_class_install_property
+ (object_class, PROP_ACTIVE,
+ g_param_spec_boolean ("active", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-toggle-button.h b/clients/tui/newt/nmt-newt-toggle-button.h
new file mode 100644
index 0000000000..e54c6d3fa1
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-toggle-button.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_TOGGLE_BUTTON_H
+#define NMT_NEWT_TOGGLE_BUTTON_H
+
+#include "nmt-newt-button.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_TOGGLE_BUTTON (nmt_newt_toggle_button_get_type ())
+#define NMT_NEWT_TOGGLE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButton))
+#define NMT_NEWT_TOGGLE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonClass))
+#define NMT_IS_NEWT_TOGGLE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON))
+#define NMT_IS_NEWT_TOGGLE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_TOGGLE_BUTTON))
+#define NMT_NEWT_TOGGLE_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonClass))
+
+struct _NmtNewtToggleButton {
+ NmtNewtButton parent;
+
+};
+
+typedef struct {
+ NmtNewtButtonClass parent;
+
+} NmtNewtToggleButtonClass;
+
+GType nmt_newt_toggle_button_get_type (void);
+
+NmtNewtWidget *nmt_newt_toggle_button_new (const char *on_label,
+ const char *off_label);
+
+gboolean nmt_newt_toggle_button_get_active (NmtNewtToggleButton *button);
+void nmt_newt_toggle_button_set_active (NmtNewtToggleButton *button,
+ gboolean active);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_TOGGLE_BUTTON_H */
diff --git a/clients/tui/newt/nmt-newt-types.h b/clients/tui/newt/nmt-newt-types.h
new file mode 100644
index 0000000000..608f7ff6fa
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-types.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_TYPES_H
+#define NMT_NEWT_TYPES_H
+
+#include <glib-object.h>
+#include <newt.h>
+#include "nm-glib-compat.h"
+
+G_BEGIN_DECLS
+
+typedef struct _NmtNewtButton NmtNewtButton;
+typedef struct _NmtNewtButtonBox NmtNewtButtonBox;
+typedef struct _NmtNewtCheckbox NmtNewtCheckbox;
+typedef struct _NmtNewtComponent NmtNewtComponent;
+typedef struct _NmtNewtContainer NmtNewtContainer;
+typedef struct _NmtNewtEntry NmtNewtEntry;
+typedef struct _NmtNewtEntryNumeric NmtNewtEntryNumeric;
+typedef struct _NmtNewtForm NmtNewtForm;
+typedef struct _NmtNewtGrid NmtNewtGrid;
+typedef struct _NmtNewtLabel NmtNewtLabel;
+typedef struct _NmtNewtListbox NmtNewtListbox;
+typedef struct _NmtNewtPopup NmtNewtPopup;
+typedef struct _NmtNewtSection NmtNewtSection;
+typedef struct _NmtNewtSectionBorder NmtNewtSectionBorder;
+typedef struct _NmtNewtSeparator NmtNewtSeparator;
+typedef struct _NmtNewtStack NmtNewtStack;
+typedef struct _NmtNewtTextbox NmtNewtTextbox;
+typedef struct _NmtNewtToggleButton NmtNewtToggleButton;
+typedef struct _NmtNewtWidget NmtNewtWidget;
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_COMPONENT_H */
diff --git a/clients/tui/newt/nmt-newt-utils.c b/clients/tui/newt/nmt-newt-utils.c
new file mode 100644
index 0000000000..68d8c449e5
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-utils.c
@@ -0,0 +1,392 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-utils
+ * @short_description: Utility functions
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-newt-utils.h"
+
+static void
+nmt_newt_dialog_g_log_handler (const char *log_domain,
+ GLogLevelFlags log_level,
+ const char *message,
+ gpointer user_data)
+{
+ const char *level_name;
+ char *full_message;
+ int screen_width, screen_height;
+ newtComponent text, ok, form;
+ newtGrid grid;
+
+ g_assert (!(log_level & G_LOG_FLAG_RECURSION));
+
+ if (log_level & G_LOG_LEVEL_DEBUG)
+ return;
+
+ switch (log_level & G_LOG_LEVEL_MASK) {
+ case G_LOG_LEVEL_ERROR:
+ level_name = "ERROR";
+ break;
+ case G_LOG_LEVEL_CRITICAL:
+ level_name = "CRITICAL";
+ break;
+ case G_LOG_LEVEL_WARNING:
+ level_name = "WARNING";
+ break;
+ case G_LOG_LEVEL_MESSAGE:
+ level_name = "Message";
+ break;
+ default:
+ level_name = NULL;
+ }
+
+ full_message = g_strdup_printf ("%s%s%s%s%s",
+ log_domain ? log_domain : "",
+ log_domain && level_name ? " " : "",
+ level_name ? level_name : "",
+ log_domain || level_name ? ": " : "",
+ message);
+
+ /* newtWinMessage() wraps the window too narrowly by default, so
+ * we don't want to use that. But we intentionally avoid using any
+ * NmtNewt classes, to avoid possible error recursion.
+ */
+
+ newtGetScreenSize (&screen_width, &screen_height);
+ text = newtTextboxReflowed (-1, -1, full_message, MAX (70, screen_width - 10), 0, 0, 0);
+ g_free (full_message);
+
+ ok = newtButton (-1, -1, "OK");
+
+ grid = newtCreateGrid (1, 2);
+ newtGridSetField (grid, 0, 0, NEWT_GRID_COMPONENT, text, 0, 0, 0, 0, 0, 0);
+ newtGridSetField (grid, 0, 1, NEWT_GRID_COMPONENT, ok, 0, 1, 0, 0,
+ NEWT_ANCHOR_RIGHT, 0);
+
+ newtGridWrappedWindow (grid, (char *) (level_name ? level_name : ""));
+ newtGridFree (grid, TRUE);
+
+ form = newtForm (NULL, NULL, 0);
+ newtFormAddComponents (form, text, ok, NULL);
+ newtRunForm (form);
+ newtFormDestroy (form);
+ newtPopWindow ();
+}
+
+static void
+nmt_newt_basic_g_log_handler (const char *log_domain,
+ GLogLevelFlags log_level,
+ const char *message,
+ gpointer user_data)
+{
+ newtSuspend ();
+ g_log_default_handler (log_domain, log_level, message, NULL);
+ newtResume ();
+}
+
+static void
+nmt_newt_suspend_callback (gpointer user_data)
+{
+ newtSuspend ();
+ kill (getpid (), SIGTSTP);
+ newtResume ();
+}
+
+/**
+ * nmt_newt_init:
+ *
+ * Wrapper for newtInit() that also does some nmt-newt-internal setup.
+ * This should be called once, before any other nmt-newt functions.
+ */
+void
+nmt_newt_init (void)
+{
+ newtInit ();
+ newtCls ();
+
+ newtSetColor (NEWT_COLORSET_CHECKBOX, "black", "lightgray");
+ newtSetColor (NMT_NEWT_COLORSET_BAD_LABEL, "red", "lightgray");
+ newtSetColor (NMT_NEWT_COLORSET_PLAIN_LABEL, "black", "lightgray");
+ newtSetColor (NMT_NEWT_COLORSET_DISABLED_BUTTON, "blue", "lightgray");
+ newtSetColor (NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, "black", "white");
+
+ if (g_getenv ("NMTUI_DEBUG"))
+ g_log_set_default_handler (nmt_newt_dialog_g_log_handler, NULL);
+ else
+ g_log_set_default_handler (nmt_newt_basic_g_log_handler, NULL);
+
+ newtSetSuspendCallback (nmt_newt_suspend_callback, NULL);
+}
+
+/**
+ * nmt_newt_finished:
+ *
+ * Wrapper for newtFinished(). Should be called at the end of the program.
+ */
+void
+nmt_newt_finished (void)
+{
+ newtFinished ();
+ g_log_set_default_handler (g_log_default_handler, NULL);
+}
+
+/**
+ * nmt_newt_message_dialog:
+ * @message: a printf()-style message format
+ * @...: arguments
+ *
+ * Displays the given message in a dialog box with a single "OK"
+ * button, and returns after the user clicks "OK".
+ */
+void
+nmt_newt_message_dialog (const char *message,
+ ...)
+{
+ va_list ap;
+ char *msg, *msg_lc, *ok_lc;
+
+ va_start (ap, message);
+ msg = g_strdup_vprintf (message, ap);
+ va_end (ap);
+
+ msg_lc = nmt_newt_locale_from_utf8 (msg);
+ ok_lc = nmt_newt_locale_from_utf8 (_("OK"));
+ newtWinMessage (NULL, ok_lc, "%s", msg_lc);
+
+ g_free (ok_lc);
+ g_free (msg_lc);
+ g_free (msg);
+}
+
+/**
+ * nmt_newt_choice_dialog:
+ * @button1: the label for the first button
+ * @button2: the label for the second button
+ * @message: a printf()-style message format
+ * @...: arguments
+ *
+ * Displays the given message in a dialog box with two buttons with
+ * the indicated labels, and waits for the user to click one.
+ *
+ * Returns: which button was clicked: 0 for @button1 or 1 for @button2
+ */
+int
+nmt_newt_choice_dialog (const char *button1,
+ const char *button2,
+ const char *message,
+ ...)
+{
+ va_list ap;
+ char *msg, *msg_lc, *button1_lc, *button2_lc;
+ int choice;
+
+ va_start (ap, message);
+ msg = g_strdup_vprintf (message, ap);
+ va_end (ap);
+
+ msg_lc = nmt_newt_locale_from_utf8 (msg);
+ button1_lc = nmt_newt_locale_from_utf8 (button1);
+ button2_lc = nmt_newt_locale_from_utf8 (button2);
+ choice = newtWinChoice (NULL, button1_lc, button2_lc, "%s", msg_lc);
+
+ g_free (button1_lc);
+ g_free (button2_lc);
+ g_free (msg_lc);
+ g_free (msg);
+
+ return choice;
+}
+
+/**
+ * nmt_newt_locale_to_utf8:
+ * @str_lc: a string in the user's locale encoding
+ *
+ * Convenience wrapper around g_locale_to_utf8().
+ *
+ * Note that libnewt works in terms of the user's locale character
+ * set, NOT UTF-8, so all strings received from libnewt must be
+ * converted back to UTF-8 before being returned to the caller or used
+ * in other APIs.
+ *
+ * Returns: @str_lc, converted to UTF-8.
+ */
+char *
+nmt_newt_locale_to_utf8 (const char *str_lc)
+{
+ char *str_utf8;
+
+ str_utf8 = g_locale_to_utf8 (str_lc, -1, NULL, NULL, NULL);
+ if (!str_utf8)
+ str_utf8 = g_strdup ("");
+ return str_utf8;
+}
+
+/**
+ * nmt_newt_locale_from_utf8:
+ * @str_utf8: a UTF-8 string
+ *
+ * Convenience wrapper around g_locale_from_utf8().
+ *
+ * Note that libnewt works in terms of the user's locale character
+ * set, NOT UTF-8, so all strings from nmt-newt must be converted to
+ * locale encoding before being passed to libnewt.
+ *
+ * Returns: @str_utf8, converted to the user's locale encoding.
+ */
+char *
+nmt_newt_locale_from_utf8 (const char *str_utf8)
+{
+ char *str_lc;
+
+ str_lc = g_locale_from_utf8 (str_utf8, -1, NULL, NULL, NULL);
+ if (!str_lc)
+ str_lc = g_strdup ("");
+ return str_lc;
+}
+
+/**
+ * nmt_newt_text_width
+ * @str: a UTF-8 string
+ *
+ * Computes the width (in terminal columns) of @str.
+ *
+ * Returns: the width of @str
+ */
+int
+nmt_newt_text_width (const char *str)
+{
+ int width;
+ gunichar ch;
+
+ for (width = 0; *str; str = g_utf8_next_char (str)) {
+ ch = g_utf8_get_char (str);
+
+ /* Based on _vte_iso2022_unichar_width */
+ if (G_LIKELY (ch < 0x80))
+ width += 1;
+ else if (G_UNLIKELY (g_unichar_iszerowidth (ch)))
+ width += 0;
+ else if (G_UNLIKELY (g_unichar_iswide (ch)))
+ width += 2;
+ else
+ width += 1;
+ }
+
+ return width;
+}
+
+/**
+ * nmt_newt_edit_string:
+ * @data: data to edit
+ *
+ * libnewt does not have a multi-line editable text component, so
+ * nmt-newt provides this function instead, which will open the user's
+ * editor to edit a file containing the given @data (ensuring that the
+ * current screen state is saved before starting the editor and
+ * restored after it returns).
+ *
+ * Returns: the edited data, or %NULL if an error occurred.
+ */
+char *
+nmt_newt_edit_string (const char *data)
+{
+ gssize len, nwrote;
+ char *filename, *argv[3];
+ GError *error = NULL;
+ int fd, status;
+ char *new_data = NULL;
+
+ fd = g_file_open_tmp ("XXXXXX.json", &filename, &error);
+ if (fd == -1) {
+ nmt_newt_message_dialog (_("Could not create temporary file: %s"), error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ len = data ? strlen (data) : 0;
+ while (len) {
+ do
+ nwrote = write (fd, data, len);
+ while (nwrote == -1 && errno == EINTR);
+
+ len -= nwrote;
+ data += nwrote;
+ }
+ close (fd);
+
+ argv[0] = (char *) g_getenv ("VISUAL");
+ if (!argv[0])
+ argv[0] = (char *) g_getenv ("EDITOR");
+ if (!argv[0])
+ argv[0] = (char *) "vi";
+ argv[1] = filename;
+ argv[2] = NULL;
+
+ newtSuspend ();
+ g_spawn_sync (NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN,
+ NULL, NULL, NULL, NULL,
+ &status, &error);
+ newtResume ();
+
+ if (error) {
+ nmt_newt_message_dialog (_("Could not create temporary file: %s"), error->message);
+ g_error_free (error);
+ goto done;
+ }
+
+#if GLIB_CHECK_VERSION (2, 34, 0)
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ if (!g_spawn_check_exit_status (status, &error)) {
+ nmt_newt_message_dialog (_("Editor failed: %s"), error->message);
+ g_error_free (error);
+ goto done;
+ }
+ G_GNUC_END_IGNORE_DEPRECATIONS
+#else
+ if (WIFEXITED (status)) {
+ if (WEXITSTATUS (status) != 0)
+ nmt_newt_message_dialog (_("Editor failed with status %d"), WEXITSTATUS (status));
+ } else if (WIFSIGNALED (status))
+ nmt_newt_message_dialog (_("Editor failed with signal %d"), WTERMSIG (status));
+#endif
+
+ if (!g_file_get_contents (filename, &new_data, NULL, &error)) {
+ nmt_newt_message_dialog (_("Could not re-read file: %s"), error->message);
+ g_error_free (error);
+ goto done;
+ }
+
+ done:
+ unlink (filename);
+ g_free (filename);
+
+ return new_data;
+}
+
diff --git a/clients/tui/newt/nmt-newt-utils.h b/clients/tui/newt/nmt-newt-utils.h
new file mode 100644
index 0000000000..3b37868a0d
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-utils.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_UTILS_H
+#define NMT_NEWT_UTILS_H
+
+#include <newt.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void nmt_newt_init (void);
+void nmt_newt_finished (void);
+
+typedef enum {
+ NMT_NEWT_COLORSET_BAD_LABEL = NEWT_COLORSET_CUSTOM (0),
+ NMT_NEWT_COLORSET_PLAIN_LABEL,
+ NMT_NEWT_COLORSET_DISABLED_BUTTON,
+ NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND
+} NmtNewtColorsets;
+
+char *nmt_newt_locale_to_utf8 (const char *str_lc);
+char *nmt_newt_locale_from_utf8 (const char *str_utf8);
+
+int nmt_newt_text_width (const char *str);
+
+void nmt_newt_message_dialog (const char *message,
+ ...);
+int nmt_newt_choice_dialog (const char *button1,
+ const char *button2,
+ const char *message,
+ ...);
+
+char *nmt_newt_edit_string (const char *data);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_UTILS_H */
diff --git a/clients/tui/newt/nmt-newt-widget.c b/clients/tui/newt/nmt-newt-widget.c
new file mode 100644
index 0000000000..24c94db940
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-widget.c
@@ -0,0 +1,668 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-widget
+ * @short_description: Base TUI Widget class
+ *
+ * #NmtNewtWidget is the abstract base class for nmt-newt. All widgets
+ * inherit from one of its two subclasses: #NmtNewtComponent, for
+ * widgets that wrap a (single) #newtComponent, and #NmtNewtContainer,
+ * for widgets consisting of multiple components. See those classes
+ * for more details.
+ *
+ * With the exception of #NmtNewtForm, all widgets start out with a
+ * floating reference, which will be sunk by the container they are
+ * added to. #NmtNewtForm is the "top-level" widget type, and so does
+ * not have a floating reference.
+ *
+ * FIXME: need RTL support
+ */
+
+#include "config.h"
+
+#include "nmt-newt-widget.h"
+#include "nmt-newt-form.h"
+
+G_DEFINE_ABSTRACT_TYPE (NmtNewtWidget, nmt_newt_widget, G_TYPE_INITIALLY_UNOWNED)
+
+#define NMT_NEWT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetPrivate))
+
+typedef struct {
+ NmtNewtWidget *parent;
+ gboolean visible, realized, valid;
+ gboolean exit_on_activate;
+
+ int pad_left, pad_top, pad_right, pad_bottom;
+} NmtNewtWidgetPrivate;
+
+enum {
+ PROP_0,
+
+ PROP_PARENT,
+ PROP_VISIBLE,
+ PROP_VALID,
+ PROP_EXIT_ON_ACTIVATE,
+
+ LAST_PROP
+};
+
+enum {
+ NEEDS_REBUILD,
+ ACTIVATED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+nmt_newt_widget_init (NmtNewtWidget *widget)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ priv->visible = TRUE;
+ priv->valid = TRUE;
+}
+
+static void
+nmt_newt_widget_finalize (GObject *object)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (object);
+
+ nmt_newt_widget_unrealize (NMT_NEWT_WIDGET (object));
+ g_clear_object (&priv->parent);
+
+ G_OBJECT_CLASS (nmt_newt_widget_parent_class)->finalize (object);
+}
+
+/**
+ * nmt_newt_widget_realize:
+ * @widget: an #NmtNewtWidget
+ *
+ * "Realizes" @widget. That is, creates #newtComponents corresponding
+ * to @widget and its children.
+ *
+ * You should not need to call this yourself; an #NmtNewtForm will
+ * cause its children to be realized and unrealized as needed.
+ */
+void
+nmt_newt_widget_realize (NmtNewtWidget *widget)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ if (!priv->realized) {
+ NMT_NEWT_WIDGET_GET_CLASS (widget)->realize (widget);
+ priv->realized = TRUE;
+ }
+}
+
+/**
+ * nmt_newt_widget_unrealize:
+ * @widget: an #NmtNewtWidget
+ *
+ * "Unrealizes" @widget, destroying its #newtComponents.
+ *
+ * You should not need to call this yourself; an #NmtNewtForm will
+ * cause its children to be realized and unrealized as needed.
+ */
+void
+nmt_newt_widget_unrealize (NmtNewtWidget *widget)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ if (priv->realized) {
+ NMT_NEWT_WIDGET_GET_CLASS (widget)->unrealize (widget);
+ priv->realized = FALSE;
+ }
+}
+
+/**
+ * nmt_newt_widget_get_realized:
+ * @widget: an #NmtNewtWidget
+ *
+ * Checks if @widget is realized or not.
+ *
+ * Returns: whether @widget is realized.
+ */
+gboolean
+nmt_newt_widget_get_realized (NmtNewtWidget *widget)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ return priv->realized;
+}
+
+/**
+ * nmt_newt_widget_get_components:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets the #newtComponents that make up @widget, if @widget is
+ * visible. If @widget has not yet been realized, it will be realized
+ * first.
+ *
+ * If this function is called on a widget, then the widget will assume
+ * that someone else is now responsible for destroying the components,
+ * and so it will not destroy them itself when the widget is
+ * destroyed. Normally, components will end up being destroyed by the
+ * #NmtNewtForm they are added to.
+ *
+ * Returns: a %NULL-terminated array of components, in focus-chain
+ * order. You must free the array with g_free() when you are done
+ * with it.
+ */
+newtComponent *
+nmt_newt_widget_get_components (NmtNewtWidget *widget)
+{
+ if (nmt_newt_widget_get_visible (widget)) {
+ nmt_newt_widget_realize (widget);
+ return NMT_NEWT_WIDGET_GET_CLASS (widget)->get_components (widget);
+ } else
+ return NULL;
+}
+
+/**
+ * nmt_newt_widget_find_component:
+ * @widget: an #NmtNewtWidget
+ * @co: a #newtComponent
+ *
+ * Finds the widget inside @widget that owns @co.
+ *
+ * Return value: @co's owner, or %NULL if it was not found.
+ */
+NmtNewtWidget *
+nmt_newt_widget_find_component (NmtNewtWidget *widget,
+ newtComponent co)
+{
+ return NMT_NEWT_WIDGET_GET_CLASS (widget)->find_component (widget, co);
+}
+
+/**
+ * nmt_newt_widget_set_padding:
+ * @widget: an #NmtNewtWidget
+ * @pad_left: padding on the left of @widget
+ * @pad_top: padding on the top of @widget
+ * @pad_right: padding on the right of @widget
+ * @pad_bottom: padding on the bottom of @widget
+ *
+ * Sets the padding on @widget.
+ */
+void
+nmt_newt_widget_set_padding (NmtNewtWidget *widget,
+ int pad_left,
+ int pad_top,
+ int pad_right,
+ int pad_bottom)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ priv->pad_left = pad_left;
+ priv->pad_top = pad_top;
+ priv->pad_right = pad_right;
+ priv->pad_bottom = pad_bottom;
+}
+
+/**
+ * nmt_newt_widget_size_request:
+ * @widget: an #NmtNewtWidget
+ * @width: (out): on output, the widget's requested width
+ * @height: (out): on output, the widget's requested height
+ *
+ * Asks @widget for its requested size. If @widget is not visible,
+ * this will return 0, 0. If @widget has not yet been realized, it
+ * will be realized first.
+ */
+void
+nmt_newt_widget_size_request (NmtNewtWidget *widget,
+ int *width,
+ int *height)
+{
+ if (nmt_newt_widget_get_visible (widget)) {
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ nmt_newt_widget_realize (widget);
+ NMT_NEWT_WIDGET_GET_CLASS (widget)->size_request (widget, width, height);
+
+ *width += priv->pad_left + priv->pad_right;
+ *height += priv->pad_top + priv->pad_bottom;
+ } else
+ *width = *height = 0;
+}
+
+/**
+ * nmt_newt_widget_size_allocate:
+ * @widget: an #NmtNewtWidget
+ * @x: the widget's (absolute) X coordinate
+ * @y: the widget's (absolute) Y coordinate
+ * @width: the widget's allocated width
+ * @height: the widget's allocated height
+ *
+ * Positions @widget at the given coordinates, with the given size. If
+ * @widget is not visible, this has no effect. If @widget has not yet
+ * been realized, it will be realized first.
+ *
+ * @x and @y are absolute coordinates (ie, relative to the screen /
+ * terminal window, not relative to @widget's parent).
+ *
+ * In general, the results are undefined if @width or @height is less
+ * than the widget's requested size. If @width or @height is larger
+ * than the requested size, most #NmtNewtComponents will ignore the
+ * extra space, but some components and most containers will expand to
+ * fit.
+ */
+void
+nmt_newt_widget_size_allocate (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ if (nmt_newt_widget_get_visible (widget)) {
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ nmt_newt_widget_realize (widget);
+ x += priv->pad_left;
+ y += priv->pad_top;
+ width -= priv->pad_left + priv->pad_right;
+ height -= priv->pad_top + priv->pad_bottom;
+
+ NMT_NEWT_WIDGET_GET_CLASS (widget)->size_allocate (widget, x, y, width, height);
+ }
+}
+
+/**
+ * nmt_newt_widget_get_focus_component:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets the #newtComponent that should be given the keyboard focus when
+ * @widget is focused.
+ *
+ * Returns: the #newtComponent to focus, or %NULL if @widget can't
+ * take the focus.
+ */
+newtComponent
+nmt_newt_widget_get_focus_component (NmtNewtWidget *widget)
+{
+ if (!NMT_NEWT_WIDGET_GET_CLASS (widget)->get_focus_component)
+ return NULL;
+
+ return NMT_NEWT_WIDGET_GET_CLASS (widget)->get_focus_component (widget);
+}
+
+static void
+nmt_newt_widget_real_activated (NmtNewtWidget *widget)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ if (priv->exit_on_activate)
+ nmt_newt_form_quit (nmt_newt_widget_get_form (widget));
+}
+
+/**
+ * nmt_newt_widget_activated:
+ * @widget: an #NmtNewtWidget
+ *
+ * Tells @widget that its #newtComponent has been activated (ie, the
+ * user hit "Return" on it) and emits #NmtNewtWidget::activated.
+ *
+ * If #NmtNewtWidget:exit-on-activate is set on @widget, then this
+ * will call nmt_newt_form_quit() on the widget's form.
+ */
+void
+nmt_newt_widget_activated (NmtNewtWidget *widget)
+{
+ g_signal_emit (widget, signals[ACTIVATED], 0);
+}
+
+/**
+ * nmt_newt_widget_get_exit_on_activate:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets @widget's #NmtNewtWidget:exit-on-activate flag, qv.
+ *
+ * Returns: @widget's #NmtNewtWidget:exit-on-activate flag
+ */
+gboolean
+nmt_newt_widget_get_exit_on_activate (NmtNewtWidget *widget)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ return priv->exit_on_activate;
+}
+
+/**
+ * nmt_newt_widget_set_exit_on_activate:
+ * @widget: an #NmtNewtWidget
+ * @exit_on_activate: whether @widget should exit on activate.
+ *
+ * Sets @widget's #NmtNewtWidget:exit-on-activate flag, qv.
+ */
+void
+nmt_newt_widget_set_exit_on_activate (NmtNewtWidget *widget,
+ gboolean exit_on_activate)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ exit_on_activate = !!exit_on_activate;
+ if (priv->exit_on_activate != exit_on_activate) {
+ priv->exit_on_activate = exit_on_activate;
+ g_object_notify (G_OBJECT (widget), "exit-on-activate");
+ }
+}
+
+/**
+ * nmt_newt_widget_get_visible:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets @widget's #NmtNewtWidget:visible flag, qv.
+ *
+ * Returns: @widget's #NmtNewtWidget:visible flag
+ */
+gboolean
+nmt_newt_widget_get_visible (NmtNewtWidget *widget)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ return priv->visible;
+}
+
+/**
+ * nmt_newt_widget_set_visible:
+ * @widget: an #NmtNewtWidget
+ * @visible: whether @widget should be visible
+ *
+ * Sets @widget's #NmtNewtWidget:visible flag, qv.
+ */
+void
+nmt_newt_widget_set_visible (NmtNewtWidget *widget,
+ gboolean visible)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ visible = !!visible;
+ if (priv->visible != visible) {
+ priv->visible = visible;
+ g_object_notify (G_OBJECT (widget), "visible");
+ nmt_newt_widget_needs_rebuild (widget);
+ }
+}
+
+/**
+ * nmt_newt_widget_set_parent:
+ * @widget: an #NmtNewtWidget
+ * @parent: @widget's parent
+ *
+ * Sets @widget's parent to @parent. This is used internally by
+ * #NmtNewtContainer implementations; you must use an appropriate
+ * container-specific method to actually add a widget to a container.
+ */
+void
+nmt_newt_widget_set_parent (NmtNewtWidget *widget,
+ NmtNewtWidget *parent)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ g_clear_object (&priv->parent);
+ priv->parent = parent ? g_object_ref (parent) : NULL;
+ g_object_notify (G_OBJECT (widget), "parent");
+}
+
+/**
+ * nmt_newt_widget_get_parent:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets @widget's parent
+ *
+ * Returns: @widget's parent
+ */
+NmtNewtWidget *
+nmt_newt_widget_get_parent (NmtNewtWidget *widget)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ return priv->parent;
+}
+
+/**
+ * nmt_newt_widget_get_form:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets @widget's top-level form.
+ *
+ * Returns: @widget's #NmtNewtForm
+ */
+NmtNewtForm *
+nmt_newt_widget_get_form (NmtNewtWidget *widget)
+{
+ while (widget) {
+ if (NMT_IS_NEWT_FORM (widget))
+ return NMT_NEWT_FORM (widget);
+ widget = nmt_newt_widget_get_parent (widget);
+ }
+
+ return NULL;
+}
+
+/**
+ * nmt_newt_widget_get_valid:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets @widget's #NmtNewtWidget:valid flag, indicating whether its
+ * content is valid.
+ *
+ * Returns: @widget's #NmtNewtWidget:valid flag
+ */
+gboolean
+nmt_newt_widget_get_valid (NmtNewtWidget *widget)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ return priv->valid;
+}
+
+/**
+ * nmt_newt_widget_set_valid:
+ * @widget: an #NmtNewtWidget
+ * @valid: whether @widget is valid
+ *
+ * Sets @widget's #NmtNewtWidget:valid flag, indicating whether its
+ * content is valid.
+ *
+ * This method should be considered "protected"; if you change it, the
+ * widget implementation will likely just change it back at some
+ * point.
+ */
+void
+nmt_newt_widget_set_valid (NmtNewtWidget *widget,
+ gboolean valid)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+ valid = !!valid;
+ if (priv->valid == valid)
+ return;
+
+ priv->valid = valid;
+ g_object_notify (G_OBJECT (widget), "valid");
+}
+
+/**
+ * nmt_newt_widget_needs_rebuilds:
+ * @widget: an #NmtNewtWidget
+ *
+ * Marks @widget as needing to be "rebuilt" (ie, re-realized). This is
+ * called automatically in some cases (such as when adding a widget to
+ * or removing it from a container). #NmtNewtComponent implementations
+ * should also call this if they need to make some change that can
+ * only be done by destroying their current #newtComponent and
+ * creating a new one.
+ */
+void
+nmt_newt_widget_needs_rebuild (NmtNewtWidget *widget)
+{
+ g_signal_emit (widget, signals[NEEDS_REBUILD], 0);
+}
+
+static void
+nmt_newt_widget_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtWidget *widget = NMT_NEWT_WIDGET (object);
+
+ switch (prop_id) {
+ case PROP_PARENT:
+ nmt_newt_widget_set_parent (widget, g_value_get_object (value));
+ break;
+ case PROP_VISIBLE:
+ nmt_newt_widget_set_visible (widget, g_value_get_boolean (value));
+ break;
+ case PROP_EXIT_ON_ACTIVATE:
+ nmt_newt_widget_set_exit_on_activate (widget, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_widget_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_PARENT:
+ g_value_set_object (value, priv->parent);
+ break;
+ case PROP_VISIBLE:
+ g_value_set_boolean (value, priv->visible);
+ break;
+ case PROP_VALID:
+ g_value_set_boolean (value, priv->valid);
+ break;
+ case PROP_EXIT_ON_ACTIVATE:
+ g_value_set_boolean (value, priv->exit_on_activate);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_newt_widget_class_init (NmtNewtWidgetClass *widget_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (widget_class);
+
+ g_type_class_add_private (widget_class, sizeof (NmtNewtWidgetPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_newt_widget_set_property;
+ object_class->get_property = nmt_newt_widget_get_property;
+ object_class->finalize = nmt_newt_widget_finalize;
+
+ widget_class->activated = nmt_newt_widget_real_activated;
+
+ /* signals */
+
+ /**
+ * NmtNewtWidget::needs-rebuild:
+ * @widget: the #NmtNewtWidget
+ *
+ * Emitted when nmt_newt_widget_need_rebuild() is called on @widget
+ * or any of its children. This signal propagates up the container
+ * hierarchy, eventually reaching the top-level #NmtNewtForm.
+ */
+ signals[NEEDS_REBUILD] =
+ g_signal_new ("needs-rebuild",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NmtNewtWidgetClass, needs_rebuild),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * NmtNewtWidget::activated:
+ * @widget: the #NmtNewtWidget
+ *
+ * Emitted when the widget's #newtComponent is activated.
+ */
+ signals[ACTIVATED] =
+ g_signal_new ("activated",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NmtNewtWidgetClass, activated),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /* properties */
+
+ /**
+ * NmtNewtWidget:parent:
+ *
+ * The widget's parent widget, or %NULL if it has no parent.
+ */
+ g_object_class_install_property
+ (object_class, PROP_PARENT,
+ g_param_spec_object ("parent", "", "",
+ NMT_TYPE_NEWT_WIDGET,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtWidget:visible:
+ *
+ * Whether the widget is visible. Invisible widgets do not get
+ * realized or sized.
+ */
+ g_object_class_install_property
+ (object_class, PROP_VISIBLE,
+ g_param_spec_boolean ("visible", "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtWidget:valid:
+ *
+ * Whether the widget's content is considered valid. Components
+ * determine their own validity. A container, by default, is
+ * considered valid if all of its children are valid.
+ */
+ g_object_class_install_property
+ (object_class, PROP_VALID,
+ g_param_spec_boolean ("valid", "", "",
+ TRUE,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtNewtWidget:exit-on-activate:
+ *
+ * If %TRUE, the widget will call nmt_newt_form_quit() on its form
+ * when it is activated.
+ */
+ g_object_class_install_property
+ (object_class, PROP_EXIT_ON_ACTIVATE,
+ g_param_spec_boolean ("exit-on-activate", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-widget.h b/clients/tui/newt/nmt-newt-widget.h
new file mode 100644
index 0000000000..a526a674c3
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-widget.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_WIDGET_H
+#define NMT_NEWT_WIDGET_H
+
+#include "nmt-newt-types.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_WIDGET (nmt_newt_widget_get_type ())
+#define NMT_NEWT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidget))
+#define NMT_NEWT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass))
+#define NMT_IS_NEWT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_WIDGET))
+#define NMT_IS_NEWT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_WIDGET))
+#define NMT_NEWT_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass))
+
+struct _NmtNewtWidget {
+ GInitiallyUnowned parent;
+
+};
+
+typedef struct {
+ GInitiallyUnownedClass parent;
+
+ /* signals */
+ void (*needs_rebuild) (NmtNewtWidget *widget);
+ void (*activated) (NmtNewtWidget *widget);
+
+ /* methods */
+ void (*realize) (NmtNewtWidget *widget);
+ void (*unrealize) (NmtNewtWidget *widget);
+
+ newtComponent * (*get_components) (NmtNewtWidget *widget);
+ NmtNewtWidget * (*find_component) (NmtNewtWidget *widget,
+ newtComponent co);
+
+ void (*size_request) (NmtNewtWidget *widget,
+ int *width,
+ int *height);
+ void (*size_allocate) (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height);
+
+ newtComponent (*get_focus_component) (NmtNewtWidget *widget);
+
+} NmtNewtWidgetClass;
+
+GType nmt_newt_widget_get_type (void);
+
+void nmt_newt_widget_realize (NmtNewtWidget *widget);
+void nmt_newt_widget_unrealize (NmtNewtWidget *widget);
+gboolean nmt_newt_widget_get_realized (NmtNewtWidget *widget);
+
+newtComponent *nmt_newt_widget_get_components (NmtNewtWidget *widget);
+
+void nmt_newt_widget_set_padding (NmtNewtWidget *widget,
+ int pad_left,
+ int pad_top,
+ int pad_right,
+ int pad_bottom);
+
+void nmt_newt_widget_size_request (NmtNewtWidget *widget,
+ int *width,
+ int *height);
+void nmt_newt_widget_size_allocate (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height);
+
+void nmt_newt_widget_set_parent (NmtNewtWidget *widget,
+ NmtNewtWidget *parent);
+NmtNewtWidget *nmt_newt_widget_get_parent (NmtNewtWidget *widget);
+
+NmtNewtForm *nmt_newt_widget_get_form (NmtNewtWidget *widget);
+
+gboolean nmt_newt_widget_get_visible (NmtNewtWidget *widget);
+void nmt_newt_widget_set_visible (NmtNewtWidget *widget,
+ gboolean visible);
+
+newtComponent nmt_newt_widget_get_focus_component (NmtNewtWidget *widget);
+
+void nmt_newt_widget_activated (NmtNewtWidget *widget);
+gboolean nmt_newt_widget_get_exit_on_activate (NmtNewtWidget *widget);
+void nmt_newt_widget_set_exit_on_activate (NmtNewtWidget *widget,
+ gboolean exit_on_activate);
+
+gboolean nmt_newt_widget_get_valid (NmtNewtWidget *widget);
+
+NmtNewtWidget *nmt_newt_widget_find_component (NmtNewtWidget *widget,
+ newtComponent co);
+
+/* protected */
+void nmt_newt_widget_needs_rebuild (NmtNewtWidget *widget);
+void nmt_newt_widget_set_valid (NmtNewtWidget *widget,
+ gboolean valid);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_WIDGET_H */
diff --git a/clients/tui/newt/nmt-newt.h b/clients/tui/newt/nmt-newt.h
new file mode 100644
index 0000000000..6a9c8d9fb5
--- /dev/null
+++ b/clients/tui/newt/nmt-newt.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_H
+#define NMT_NEWT_H
+
+#include "nmt-newt-button.h"
+#include "nmt-newt-button-box.h"
+#include "nmt-newt-checkbox.h"
+#include "nmt-newt-entry.h"
+#include "nmt-newt-entry-numeric.h"
+#include "nmt-newt-form.h"
+#include "nmt-newt-grid.h"
+#include "nmt-newt-label.h"
+#include "nmt-newt-listbox.h"
+#include "nmt-newt-popup.h"
+#include "nmt-newt-section.h"
+#include "nmt-newt-separator.h"
+#include "nmt-newt-stack.h"
+#include "nmt-newt-textbox.h"
+#include "nmt-newt-toggle-button.h"
+#include "nmt-newt-utils.h"
+
+#endif /* NMT_NEWT_H */
diff --git a/clients/tui/nm-editor-bindings.c b/clients/tui/nm-editor-bindings.c
new file mode 100644
index 0000000000..5be13588ee
--- /dev/null
+++ b/clients/tui/nm-editor-bindings.c
@@ -0,0 +1,1676 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nm-editor-bindings
+ * @short_description: #GBinding-based NM connection editor helpers
+ *
+ * nm-editor-bindings contains helper functions to bind NMSettings objects
+ * to connection editing widgets. The goal is that this should eventually be
+ * shared between nmtui, nm-connection-editor, and gnome-control-center.
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dbus/dbus-glib.h>
+
+#include "nm-editor-bindings.h"
+#include "nm-gvaluearray-compat.h"
+
+static void
+value_transform_string_int (const GValue *src_value,
+ GValue *dest_value)
+{
+ long val;
+ char *end;
+
+ val = strtol (g_value_get_string (src_value), &end, 10);
+ if (val < G_MININT || val > G_MAXINT || *end)
+ return;
+
+ g_value_set_int (dest_value, (int) val);
+}
+
+static void
+value_transform_string_uint (const GValue *src_value,
+ GValue *dest_value)
+{
+ long val;
+ char *end;
+
+ val = strtol (g_value_get_string (src_value), &end, 10);
+ if (val < 0 || val > G_MAXUINT || *end)
+ return;
+
+ g_value_set_uint (dest_value, (gint) val);
+}
+
+void
+nm_editor_bindings_init (void)
+{
+ /* glib registers number -> string, but not string -> number */
+ g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, value_transform_string_int);
+ g_value_register_transform_func (G_TYPE_STRING, G_TYPE_UINT, value_transform_string_uint);
+}
+
+static gboolean
+ip_string_parse (const char *text,
+ int family,
+ gpointer addr,
+ guint32 *prefix)
+{
+ const char *slash;
+ char *addrstr, *end;
+ gboolean valid;
+
+ slash = strchr (text, '/');
+
+ if (slash) {
+ if (!prefix)
+ return FALSE;
+ addrstr = g_strndup (text, slash - text);
+ } else
+ addrstr = g_strdup (text);
+ valid = (inet_pton (family, addrstr, addr) == 1);
+ g_free (addrstr);
+
+ if (!valid)
+ return FALSE;
+
+ if (slash) {
+ *prefix = strtoul (slash + 1, &end, 10);
+ if ( *end
+ || *prefix == 0
+ || (family == AF_INET && *prefix > 32)
+ || (family == AF_INET6 && *prefix > 128))
+ valid = FALSE;
+ } else if (prefix) {
+ if (family == AF_INET)
+ *prefix = 32;
+ else
+ *prefix = 128;
+ }
+
+ return valid;
+}
+
+static gboolean
+ip4_addresses_with_prefix_to_strv (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GPtrArray *addrs;
+ GArray *addr;
+ guint32 addrbytes, prefix;
+ char buf[INET_ADDRSTRLEN], **strings;
+ int i;
+
+ addrs = g_value_get_boxed (source_value);
+ strings = g_new0 (char *, addrs->len + 1);
+
+ for (i = 0; i < addrs->len; i++) {
+ addr = addrs->pdata[i];
+ addrbytes = g_array_index (addr, guint32, 0);
+ prefix = g_array_index (addr, guint32, 1);
+
+ if (addrbytes) {
+ strings[i] = g_strdup_printf ("%s/%d",
+ inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)),
+ (int) prefix);
+ } else
+ strings[i] = g_strdup ("");
+ }
+
+ g_value_take_boxed (target_value, strings);
+ return TRUE;
+}
+
+static gboolean
+ip4_addresses_with_prefix_from_strv (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ char **strings;
+ GPtrArray *addrs;
+ GArray *addr;
+ guint32 *addrvals;
+ int i;
+
+ strings = g_value_get_boxed (source_value);
+ /* Fetch the original property value, so as to preserve the gateway elements */
+ g_object_get (g_binding_get_source (binding),
+ g_binding_get_source_property (binding), &addrs,
+ NULL);
+
+ for (i = 0; strings[i]; i++) {
+ if (i >= addrs->len) {
+ guint32 val;
+
+ addr = g_array_sized_new (FALSE, FALSE, sizeof (guint32), 3);
+ val = 0;
+ g_array_append_val (addr, val);
+ val = 32;
+ g_array_append_val (addr, val);
+ val = 0;
+ g_array_append_val (addr, val);
+ g_ptr_array_add (addrs, addr);
+ } else
+ addr = addrs->pdata[i];
+ addrvals = (guint32 *)addr->data;
+
+ if (!ip_string_parse (strings[i], AF_INET, &addrvals[0], &addrvals[1])) {
+ g_ptr_array_unref (addrs);
+ return FALSE;
+ }
+ }
+
+ g_ptr_array_set_size (addrs, i);
+ g_value_take_boxed (target_value, addrs);
+ return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip4_addresses_with_prefix_to_strv:
+ * @source: the source object (eg, an #NMSettingIP4Config)
+ * @source_property: the property on @source to bind (eg,
+ * %NM_SETTING_IP4_CONFIG_ADDRESSES)
+ * @target: the target object (eg, an #NmtAddressList)
+ * @target_property: the property on @target to bind
+ * (eg, "strings")
+ * @flags: %GBindingFlags
+ *
+ * Binds the %DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT property
+ * @source_property on @source to the %G_TYPE_STRV property
+ * @target_property on @target.
+ *
+ * Each address/prefix/gateway triplet in @source_property will be
+ * converted to a string of the form "ip.ad.dr.ess/prefix" in
+ * @target_property (and vice versa if %G_BINDING_BIDIRECTIONAL) is
+ * specified. The "gateway" fields in @source_property are ignored
+ * when converting to strings, and unmodified when converting from
+ * strings.
+ */
+void
+nm_editor_bind_ip4_addresses_with_prefix_to_strv (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags)
+{
+ g_object_bind_property_full (source, source_property,
+ target, target_property,
+ flags,
+ ip4_addresses_with_prefix_to_strv,
+ ip4_addresses_with_prefix_from_strv,
+ NULL, NULL);
+}
+
+static gboolean
+ip4_addresses_to_strv (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GArray *addrs;
+ guint32 addrbytes;
+ char buf[INET_ADDRSTRLEN], **strings;
+ int i;
+
+ addrs = g_value_get_boxed (source_value);
+ strings = g_new0 (char *, addrs->len + 1);
+
+ for (i = 0; i < addrs->len; i++) {
+ addrbytes = g_array_index (addrs, guint32, i);
+ if (addrbytes)
+ inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf));
+ else
+ buf[0] = '\0';
+ strings[i] = g_strdup (buf);
+ }
+
+ g_value_take_boxed (target_value, strings);
+ return TRUE;
+}
+
+static gboolean
+ip4_addresses_from_strv (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ char **strings;
+ GArray *addrs;
+ guint32 addr;
+ int i;
+
+ strings = g_value_get_boxed (source_value);
+ addrs = g_array_new (FALSE, FALSE, sizeof (guint32));
+
+ for (i = 0; strings[i]; i++) {
+ if (!ip_string_parse (strings[i], AF_INET, &addr, NULL)) {
+ g_array_unref (addrs);
+ return FALSE;
+ }
+ g_array_append_val (addrs, addr);
+ }
+
+ g_value_take_boxed (target_value, addrs);
+ return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip4_addresses_to_strv:
+ * @source: the source object (eg, an #NMSettingIP4Config)
+ * @source_property: the property on @source to bind (eg,
+ * %NM_SETTING_IP4_CONFIG_DNS)
+ * @target: the target object (eg, an #NmtAddressList)
+ * @target_property: the property on @target to bind
+ * (eg, "strings")
+ * @flags: %GBindingFlags
+ *
+ * Binds the %DBUS_TYPE_G_UINT_ARRAY property @source_property on
+ * @source to the %G_TYPE_STRV property @target_property on @target.
+ *
+ * Each address in @source_property will be converted to a string of
+ * the form "ip.ad.dr.ess" in @target_property (and vice versa if
+ * %G_BINDING_BIDIRECTIONAL) is specified.
+ */
+void
+nm_editor_bind_ip4_addresses_to_strv (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags)
+{
+ g_object_bind_property_full (source, source_property,
+ target, target_property,
+ flags,
+ ip4_addresses_to_strv,
+ ip4_addresses_from_strv,
+ NULL, NULL);
+}
+
+static gboolean
+ip4_gateway_to_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GPtrArray *addrs;
+ GArray *addr;
+ guint32 gateway = 0;
+ const char *str;
+ char buf[INET_ADDRSTRLEN];
+ int i;
+
+ addrs = g_value_get_boxed (source_value);
+ for (i = 0; i < addrs->len; i++) {
+ addr = addrs->pdata[i];
+ gateway = g_array_index (addr, guint32, 2);
+ if (gateway)
+ break;
+ }
+
+ if (gateway)
+ str = inet_ntop (AF_INET, &gateway, buf, sizeof (buf));
+ else
+ str = "";
+ g_value_set_string (target_value, str);
+ return TRUE;
+}
+
+static gboolean
+ip4_gateway_from_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ const char *text;
+ GPtrArray *addrs;
+ GArray *addr;
+ guint32 addrbytes, *addrvals;
+ int i;
+
+ text = g_value_get_string (source_value);
+ if (!ip_string_parse (text, AF_INET, &addrbytes, NULL))
+ return FALSE;
+
+ /* Fetch the original property value, so as to preserve the IP address elements */
+ g_object_get (g_binding_get_source (binding),
+ g_binding_get_source_property (binding), &addrs,
+ NULL);
+ if (!addrs->len) {
+ g_ptr_array_unref (addrs);
+ return FALSE;
+ }
+ addr = addrs->pdata[0];
+ addrvals = (guint32 *)addr->data;
+ if (addrbytes == addrvals[2]) {
+ g_ptr_array_unref (addrs);
+ return FALSE;
+ }
+ addrvals[2] = addrbytes;
+
+ for (i = 1; i < addrs->len; i++) {
+ addr = addrs->pdata[i];
+ addrvals = (guint32 *)addr->data;
+ addrvals[2] = 0;
+ }
+
+ g_value_take_boxed (target_value, addrs);
+ return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip4_gateway_to_string:
+ * @source: the source object (eg, an #NMSettingIP4Config)
+ * @source_property: the property on @source to bind (eg,
+ * %NM_SETTING_IP4_CONFIG_ADDRESSES)
+ * @target: the target object (eg, an #NmtNewtEntry)
+ * @target_property: the property on @target to bind
+ * (eg, "text")
+ * @flags: %GBindingFlags
+ *
+ * Binds the %DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT property
+ * @source_property on @source to the %G_TYPE_STRING property
+ * @target_property on @target.
+ *
+ * Specifically, this binds the "gateway" field of the first address
+ * in @source_property; all other addresses in @source_property are
+ * ignored, and its "address" and "prefix" fields are unmodified.
+ */
+void
+nm_editor_bind_ip4_gateway_to_string (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags)
+{
+ g_object_bind_property_full (source, source_property,
+ target, target_property,
+ flags,
+ ip4_gateway_to_string,
+ ip4_gateway_from_string,
+ NULL, NULL);
+}
+
+static gboolean
+ip4_route_transform_to_dest_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP4Route *route;
+ char buf[INET_ADDRSTRLEN], *string;
+ guint32 addrbytes;
+
+ route = g_value_get_boxed (source_value);
+ if (route)
+ addrbytes = nm_ip4_route_get_dest (route);
+ else
+ addrbytes = 0;
+
+ if (addrbytes) {
+ string = g_strdup_printf ("%s/%d",
+ inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)),
+ (int) nm_ip4_route_get_prefix (route));
+ g_value_take_string (target_value, string);
+ } else
+ g_value_set_string (target_value, "");
+ return TRUE;
+}
+
+static gboolean
+ip4_route_transform_to_next_hop_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP4Route *route;
+ char buf[INET_ADDRSTRLEN];
+ guint32 addrbytes;
+
+ route = g_value_get_boxed (source_value);
+ if (route)
+ addrbytes = nm_ip4_route_get_next_hop (route);
+ else
+ addrbytes = 0;
+
+ if (addrbytes)
+ inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf));
+ else
+ buf[0] = '\0';
+ g_value_set_string (target_value, buf);
+ return TRUE;
+}
+
+static gboolean
+ip4_route_transform_to_metric_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP4Route *route;
+ char *string;
+
+ route = g_value_get_boxed (source_value);
+ if (route && nm_ip4_route_get_dest (route)) {
+ string = g_strdup_printf ("%lu", (gulong) nm_ip4_route_get_metric (route));
+ g_value_take_string (target_value, string);
+ } else
+ g_value_set_string (target_value, "");
+ return TRUE;
+}
+
+static gboolean
+ip4_route_transform_from_dest_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP4Route *route;
+ const char *text;
+ guint32 addrbytes, prefix;
+
+ text = g_value_get_string (source_value);
+ if (!ip_string_parse (text, AF_INET, &addrbytes, &prefix))
+ return FALSE;
+
+ /* Fetch the original property value */
+ g_object_get (g_binding_get_source (binding),
+ g_binding_get_source_property (binding), &route,
+ NULL);
+
+ nm_ip4_route_set_dest (route, addrbytes);
+ nm_ip4_route_set_prefix (route, prefix);
+
+ g_value_take_boxed (target_value, route);
+ return TRUE;
+}
+
+static gboolean
+ip4_route_transform_from_next_hop_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP4Route *route;
+ const char *text;
+ guint32 addrbytes;
+
+ text = g_value_get_string (source_value);
+ if (*text) {
+ if (!ip_string_parse (text, AF_INET, &addrbytes, NULL))
+ return FALSE;
+ } else
+ addrbytes = 0;
+
+ /* Fetch the original property value */
+ g_object_get (g_binding_get_source (binding),
+ g_binding_get_source_property (binding), &route,
+ NULL);
+
+ nm_ip4_route_set_next_hop (route, addrbytes);
+
+ g_value_take_boxed (target_value, route);
+ return TRUE;
+}
+
+static gboolean
+ip4_route_transform_from_metric_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP4Route *route;
+ const char *text;
+ guint32 metric;
+
+ text = g_value_get_string (source_value);
+ metric = strtoul (text, NULL, 10);
+
+ /* Fetch the original property value */
+ g_object_get (g_binding_get_source (binding),
+ g_binding_get_source_property (binding), &route,
+ NULL);
+
+ nm_ip4_route_set_metric (route, metric);
+
+ g_value_take_boxed (target_value, route);
+ return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip4_route_to_strings:
+ * @source: the source object
+ * @source_property: the source property
+ * @dest_target: the target object for the route's destionation
+ * @dest_target_property: the property on @dest_target
+ * @next_hop_target: the target object for the route's next hop
+ * @next_hop_target_property: the property on @next_hop_target
+ * @metric_target: the target object for the route's metric
+ * @metric_target_property: the property on @metric_target
+ * @flags: %GBindingFlags
+ *
+ * Binds the #NMIP4Route-valued property @source_property on @source
+ * to the three indicated string-valued target properties (and vice
+ * versa if %G_BINDING_BIDIRECTIONAL is specified).
+ *
+ * @dest_target_property should be an "address/prefix" string, as with
+ * nm_editor_bind_ip4_addresses_with_prefix_to_strv(). @next_hop_target
+ * is a plain IP address, and @metric_target is a number.
+ */
+void
+nm_editor_bind_ip4_route_to_strings (gpointer source,
+ const gchar *source_property,
+ gpointer dest_target,
+ const gchar *dest_target_property,
+ gpointer next_hop_target,
+ const gchar *next_hop_target_property,
+ gpointer metric_target,
+ const gchar *metric_target_property,
+ GBindingFlags flags)
+{
+ g_object_bind_property_full (source, source_property,
+ dest_target, dest_target_property,
+ flags,
+ ip4_route_transform_to_dest_string,
+ ip4_route_transform_from_dest_string,
+ NULL, NULL);
+ g_object_bind_property_full (source, source_property,
+ next_hop_target, next_hop_target_property,
+ flags,
+ ip4_route_transform_to_next_hop_string,
+ ip4_route_transform_from_next_hop_string,
+ NULL, NULL);
+ g_object_bind_property_full (source, source_property,
+ metric_target, metric_target_property,
+ flags,
+ ip4_route_transform_to_metric_string,
+ ip4_route_transform_from_metric_string,
+ NULL, NULL);
+}
+
+#define IP6_ADDRESS_SET(addr) ( addr \
+ && addr->len == sizeof (struct in6_addr) \
+ && memcmp (addr->data, &in6addr_any, addr->len) != 0)
+
+static gboolean
+ip6_addresses_with_prefix_to_strv (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GPtrArray *addrs;
+ GValueArray *addr;
+ GValue *val;
+ GByteArray *addrbytes;
+ guint prefix;
+ char **strings, buf[INET6_ADDRSTRLEN];
+ int i;
+
+ addrs = g_value_get_boxed (source_value);
+ strings = g_new0 (char *, addrs->len + 1);
+
+ for (i = 0; i < addrs->len; i++) {
+ addr = addrs->pdata[i];
+ val = g_value_array_get_nth (addr, 0);
+ addrbytes = g_value_get_boxed (val);
+ val = g_value_array_get_nth (addr, 1);
+ prefix = g_value_get_uint (val);
+
+ if (IP6_ADDRESS_SET (addrbytes)) {
+ strings[i] = g_strdup_printf ("%s/%d",
+ inet_ntop (AF_INET6, addrbytes->data, buf, sizeof (buf)),
+ prefix);
+ } else
+ strings[i] = g_strdup ("");
+ }
+
+ g_value_take_boxed (target_value, strings);
+ return TRUE;
+}
+
+static gboolean
+ip6_addresses_with_prefix_from_strv (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ char **strings;
+ GPtrArray *addrs;
+ GValueArray *addr;
+ guint32 prefix;
+ GValue val = G_VALUE_INIT, *valp;
+ GByteArray *ba;
+ int i;
+
+ strings = g_value_get_boxed (source_value);
+
+ /* Fetch the original property value, so as to preserve the gateway elements */
+ g_object_get (g_binding_get_source (binding),
+ g_binding_get_source_property (binding), &addrs,
+ NULL);
+
+ for (i = 0; strings[i]; i++) {
+ if (i >= addrs->len) {
+ addr = g_value_array_new (3);
+
+ g_value_init (&val, DBUS_TYPE_G_UCHAR_ARRAY);
+ ba = g_byte_array_sized_new (sizeof (struct in6_addr));
+ g_byte_array_append (ba, (guint8 *) &in6addr_any, sizeof (struct in6_addr));
+ g_value_take_boxed (&val, ba);
+ g_value_array_append (addr, &val);
+ g_value_unset (&val);
+
+ g_value_init (&val, G_TYPE_UINT);
+ g_value_set_uint (&val, 128);
+ g_value_array_append (addr, &val);
+ g_value_unset (&val);
+
+ g_value_init (&val, DBUS_TYPE_G_UCHAR_ARRAY);
+ ba = g_byte_array_sized_new (sizeof (struct in6_addr));
+ g_byte_array_append (ba, (guint8 *) &in6addr_any, sizeof (struct in6_addr));
+ g_value_take_boxed (&val, ba);
+ g_value_array_append (addr, &val);
+ g_value_unset (&val);
+
+ g_ptr_array_add (addrs, addr);
+ } else
+ addr = addrs->pdata[i];
+
+ valp = g_value_array_get_nth (addr, 0);
+ ba = g_value_get_boxed (valp);
+ g_assert (ba->len == sizeof (struct in6_addr));
+
+ if (!ip_string_parse (strings[i], AF_INET6, ba->data, &prefix)) {
+ g_ptr_array_unref (addrs);
+ return FALSE;
+ }
+
+ valp = g_value_array_get_nth (addr, 1);
+ g_value_set_uint (valp, prefix);
+ }
+
+ g_ptr_array_set_size (addrs, i);
+ g_value_set_boxed (target_value, addrs);
+ return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip6_addresses_with_prefix_to_strv:
+ * @source: the source object (eg, an #NMSettingIP6Config)
+ * @source_property: the property on @source to bind (eg,
+ * %NM_SETTING_IP6_CONFIG_ADDRESSES)
+ * @target: the target object (eg, an #NmtAddressList)
+ * @target_property: the property on @target to bind
+ * (eg, "strings")
+ * @flags: %GBindingFlags
+ *
+ * Binds the %DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS property
+ * @source_property on @source to the %G_TYPE_STRV property
+ * @target_property on @target.
+ *
+ * Each address/prefix/gateway triplet in @source_property will be
+ * converted to a string of the form "ip::ad:dr:ess/prefix" in
+ * @target_property (and vice versa if %G_BINDING_BIDIRECTIONAL) is
+ * specified. The "gateway" fields in @source_property are ignored
+ * when converting to strings, and unmodified when converting from
+ * strings.
+ */
+void
+nm_editor_bind_ip6_addresses_with_prefix_to_strv (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags)
+{
+ g_object_bind_property_full (source, source_property,
+ target, target_property,
+ flags,
+ ip6_addresses_with_prefix_to_strv,
+ ip6_addresses_with_prefix_from_strv,
+ NULL, NULL);
+}
+
+static gboolean
+ip6_addresses_to_strv (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GPtrArray *addrs;
+ GByteArray *addrbytes;
+ char buf[INET6_ADDRSTRLEN], **strings;
+ int i;
+
+ addrs = g_value_get_boxed (source_value);
+ strings = g_new0 (char *, addrs->len + 1);
+
+ for (i = 0; i < addrs->len; i++) {
+ addrbytes = addrs->pdata[i];
+ if (IP6_ADDRESS_SET (addrbytes))
+ inet_ntop (AF_INET6, addrbytes->data, buf, sizeof (buf));
+ else
+ buf[0] = '\0';
+ strings[i] = g_strdup (buf);
+ }
+
+ g_value_take_boxed (target_value, strings);
+ return TRUE;
+}
+
+static gboolean
+ip6_addresses_from_strv (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ char **strings;
+ GPtrArray *addrs;
+ GByteArray *addr;
+ struct in6_addr addrbytes;
+ int i;
+
+ strings = g_value_get_boxed (source_value);
+ addrs = g_ptr_array_new ();
+
+ for (i = 0; strings[i]; i++) {
+ if (!ip_string_parse (strings[i], AF_INET6, &addrbytes, NULL)) {
+ while (i--)
+ g_byte_array_unref (addrs->pdata[i]);
+ g_ptr_array_unref (addrs);
+ return FALSE;
+ }
+
+ addr = g_byte_array_sized_new (sizeof (addrbytes));
+ g_byte_array_append (addr, (guint8 *)&addrbytes, sizeof (addrbytes));
+ g_ptr_array_add (addrs, addr);
+ }
+
+ g_value_take_boxed (target_value, addrs);
+ return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip6_addresses_to_strv:
+ * @source: the source object (eg, an #NMSettingIP6Config)
+ * @source_property: the property on @source to bind (eg,
+ * %NM_SETTING_IP6_CONFIG_DNS)
+ * @target: the target object (eg, an #NmtAddressList)
+ * @target_property: the property on @target to bind
+ * (eg, "strings")
+ * @flags: %GBindingFlags
+ *
+ * Binds the %DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR property
+ * @source_property on @source to the %G_TYPE_STRV property
+ * @target_property on @target.
+ *
+ * Each address in @source_property will be converted to a string of
+ * the form "ip::ad:dr:ess" in @target_property (and vice versa if
+ * %G_BINDING_BIDIRECTIONAL) is specified.
+ */
+void
+nm_editor_bind_ip6_addresses_to_strv (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags)
+{
+ g_object_bind_property_full (source, source_property,
+ target, target_property,
+ flags,
+ ip6_addresses_to_strv,
+ ip6_addresses_from_strv,
+ NULL, NULL);
+}
+
+static gboolean
+ip6_gateway_to_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GPtrArray *addrs;
+ GValueArray *addr;
+ GValue *val;
+ GByteArray *gateway;
+ char buf[INET6_ADDRSTRLEN];
+ const char *str;
+
+ addrs = g_value_get_boxed (source_value);
+ if (addrs->len == 0)
+ return FALSE;
+
+ addr = addrs->pdata[0];
+ val = g_value_array_get_nth (addr, 2);
+ gateway = g_value_get_boxed (val);
+
+ if (IP6_ADDRESS_SET (gateway))
+ str = inet_ntop (AF_INET6, gateway->data, buf, sizeof (buf));
+ else
+ str = "";
+ g_value_set_string (target_value, str);
+ return TRUE;
+}
+
+static gboolean
+ip6_gateway_from_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GPtrArray *addrs;
+ const char *text;
+ GValueArray *addr;
+ struct in6_addr gateway;
+ GValue *val;
+ GByteArray *ba;
+ int i;
+
+ text = g_value_get_string (source_value);
+ if (!ip_string_parse (text, AF_INET6, &gateway, NULL))
+ return FALSE;
+
+ /* Fetch the original property value, so as to preserve the IP address elements */
+ g_object_get (g_binding_get_source (binding),
+ g_binding_get_source_property (binding), &addrs,
+ NULL);
+ if (!addrs->len) {
+ g_ptr_array_unref (addrs);
+ return FALSE;
+ }
+
+ addr = addrs->pdata[0];
+
+ ba = g_byte_array_sized_new (sizeof (gateway));
+ g_byte_array_append (ba, (guint8 *) &gateway, sizeof (gateway));
+
+ val = g_value_array_get_nth (addr, 2);
+ g_value_take_boxed (val, ba);
+
+ for (i = 1; i < addrs->len; i++) {
+ addr = addrs->pdata[i];
+ val = g_value_array_get_nth (addr, 2);
+ ba = g_value_get_boxed (val);
+
+ if (ba)
+ memset (ba->data, 0, ba->len);
+ }
+
+ g_value_take_boxed (target_value, addrs);
+ return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip6_gateway_to_string:
+ * @source: the source object (eg, an #NMSettingIP6Config)
+ * @source_property: the property on @source to bind (eg,
+ * %NM_SETTING_IP6_CONFIG_ADDRESSES)
+ * @target: the target object (eg, an #NmtNewtEntry)
+ * @target_property: the property on @target to bind
+ * (eg, "text")
+ * @flags: %GBindingFlags
+ *
+ * Binds the %DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS property
+ * @source_property on @source to the %G_TYPE_STRING property
+ * @target_property on @target.
+ *
+ * Specifically, this binds the "gateway" field of the first address
+ * in @source_property; all other addresses in @source_property are
+ * ignored, and its "address" and "prefix" fields are unmodified.
+ */
+void
+nm_editor_bind_ip6_gateway_to_string (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags)
+{
+ g_object_bind_property_full (source, source_property,
+ target, target_property,
+ flags,
+ ip6_gateway_to_string,
+ ip6_gateway_from_string,
+ NULL, NULL);
+}
+
+#define IN6_ADDR_SET(bytes) (memcmp (bytes, &in6addr_any, sizeof (struct in6_addr)) != 0)
+
+static gboolean
+ip6_route_transform_to_dest_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP6Route *route;
+ char buf[INET6_ADDRSTRLEN], *string;
+ const struct in6_addr *addrbytes;
+
+ route = g_value_get_boxed (source_value);
+ if (route)
+ addrbytes = nm_ip6_route_get_dest (route);
+ else
+ addrbytes = &in6addr_any;
+
+ if (IN6_ADDR_SET (addrbytes)) {
+ string = g_strdup_printf ("%s/%d",
+ inet_ntop (AF_INET6, addrbytes, buf, sizeof (buf)),
+ (int) nm_ip6_route_get_prefix (route));
+ g_value_take_string (target_value, string);
+ } else
+ g_value_set_string (target_value, "");
+ return TRUE;
+}
+
+static gboolean
+ip6_route_transform_to_next_hop_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP6Route *route;
+ char buf[INET6_ADDRSTRLEN];
+ const struct in6_addr *addrbytes;
+
+ route = g_value_get_boxed (source_value);
+ if (route)
+ addrbytes = nm_ip6_route_get_next_hop (route);
+ else
+ addrbytes = &in6addr_any;
+
+ if (IN6_ADDR_SET (addrbytes))
+ inet_ntop (AF_INET6, addrbytes, buf, sizeof (buf));
+ else
+ buf[0] = '\0';
+ g_value_set_string (target_value, buf);
+ return TRUE;
+}
+
+static gboolean
+ip6_route_transform_to_metric_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP6Route *route;
+ char *string;
+
+ route = g_value_get_boxed (source_value);
+ if (route && IN6_ADDR_SET (nm_ip6_route_get_dest (route))) {
+ string = g_strdup_printf ("%lu", (gulong) nm_ip6_route_get_metric (route));
+ g_value_take_string (target_value, string);
+ } else
+ g_value_set_string (target_value, "");
+ return TRUE;
+}
+
+static gboolean
+ip6_route_transform_from_dest_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP6Route *route;
+ const char *text;
+ struct in6_addr addrbytes;
+ guint32 prefix;
+
+ text = g_value_get_string (source_value);
+ if (!ip_string_parse (text, AF_INET6, &addrbytes, &prefix))
+ return FALSE;
+
+ /* Fetch the original property value */
+ g_object_get (g_binding_get_source (binding),
+ g_binding_get_source_property (binding), &route,
+ NULL);
+
+ nm_ip6_route_set_dest (route, &addrbytes);
+ nm_ip6_route_set_prefix (route, prefix);
+
+ g_value_take_boxed (target_value, route);
+ return TRUE;
+}
+
+static gboolean
+ip6_route_transform_from_next_hop_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP6Route *route;
+ const char *text;
+ struct in6_addr addrbytes;
+
+ text = g_value_get_string (source_value);
+ if (*text) {
+ if (!ip_string_parse (text, AF_INET6, &addrbytes, NULL))
+ return FALSE;
+ } else
+ addrbytes = in6addr_any;
+
+ /* Fetch the original property value */
+ g_object_get (g_binding_get_source (binding),
+ g_binding_get_source_property (binding), &route,
+ NULL);
+
+ nm_ip6_route_set_next_hop (route, &addrbytes);
+
+ g_value_take_boxed (target_value, route);
+ return TRUE;
+}
+
+static gboolean
+ip6_route_transform_from_metric_string (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMIP6Route *route;
+ const char *text;
+ guint32 metric;
+
+ text = g_value_get_string (source_value);
+ metric = strtoul (text, NULL, 10);
+
+ /* Fetch the original property value */
+ g_object_get (g_binding_get_source (binding),
+ g_binding_get_source_property (binding), &route,
+ NULL);
+
+ nm_ip6_route_set_metric (route, metric);
+
+ g_value_take_boxed (target_value, route);
+ return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip6_route_to_strings:
+ * @source: the source object
+ * @source_property: the source property
+ * @dest_target: the target object for the route's destionation
+ * @dest_target_property: the property on @dest_target
+ * @next_hop_target: the target object for the route's next hop
+ * @next_hop_target_property: the property on @next_hop_target
+ * @metric_target: the target object for the route's metric
+ * @metric_target_property: the property on @metric_target
+ * @flags: %GBindingFlags
+ *
+ * Binds the #NMIP6Route-valued property @source_property on @source
+ * to the three indicated string-valued target properties (and vice
+ * versa if %G_BINDING_BIDIRECTIONAL is specified).
+ *
+ * @dest_target_property should be an "address/prefix" string, as with
+ * nm_editor_bind_ip6_addresses_with_prefix_to_strv(). @next_hop_target
+ * is a plain IP address, and @metric_target is a number.
+ */
+void
+nm_editor_bind_ip6_route_to_strings (gpointer source,
+ const gchar *source_property,
+ gpointer dest_target,
+ const gchar *dest_target_property,
+ gpointer next_hop_target,
+ const gchar *next_hop_target_property,
+ gpointer metric_target,
+ const gchar *metric_target_property,
+ GBindingFlags flags)
+{
+ g_object_bind_property_full (source, source_property,
+ dest_target, dest_target_property,
+ flags,
+ ip6_route_transform_to_dest_string,
+ ip6_route_transform_from_dest_string,
+ NULL, NULL);
+ g_object_bind_property_full (source, source_property,
+ next_hop_target, next_hop_target_property,
+ flags,
+ ip6_route_transform_to_next_hop_string,
+ ip6_route_transform_from_next_hop_string,
+ NULL, NULL);
+ g_object_bind_property_full (source, source_property,
+ metric_target, metric_target_property,
+ flags,
+ ip6_route_transform_to_metric_string,
+ ip6_route_transform_from_metric_string,
+ NULL, NULL);
+}
+
+/* Wireless security method binding */
+typedef struct {
+ NMConnection *connection;
+ NMSettingWirelessSecurity *s_wsec;
+ gboolean s_wsec_in_use;
+
+ GObject *target;
+ char *target_property;
+
+ gboolean updating;
+} NMEditorWirelessSecurityMethodBinding;
+
+static const char *
+get_security_type (NMEditorWirelessSecurityMethodBinding *binding)
+{
+ const char *key_mgmt, *auth_alg;
+
+ if (!binding->s_wsec_in_use)
+ return "none";
+
+ key_mgmt = nm_setting_wireless_security_get_key_mgmt (binding->s_wsec);
+ auth_alg = nm_setting_wireless_security_get_auth_alg (binding->s_wsec);
+
+ /* No IEEE 802.1x */
+ if (!strcmp (key_mgmt, "none")) {
+ NMWepKeyType wep_type = nm_setting_wireless_security_get_wep_key_type (binding->s_wsec);
+
+ if (wep_type == NM_WEP_KEY_TYPE_KEY)
+ return "wep-key";
+ else
+ return "wep-passphrase";
+ }
+
+ if (!strcmp (key_mgmt, "ieee8021x")) {
+ if (auth_alg && !strcmp (auth_alg, "leap"))
+ return "leap";
+ return "dynamic-wep";
+ }
+
+ if ( !strcmp (key_mgmt, "wpa-none")
+ || !strcmp (key_mgmt, "wpa-psk"))
+ return "wpa-personal";
+
+ if (!strcmp (key_mgmt, "wpa-eap"))
+ return "wpa-enterprise";
+
+ return NULL;
+}
+
+static void
+wireless_security_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NMEditorWirelessSecurityMethodBinding *binding = user_data;
+
+ if (binding->updating)
+ return;
+
+ binding->updating = TRUE;
+ g_object_set (binding->target,
+ binding->target_property, get_security_type (binding),
+ NULL);
+ binding->updating = FALSE;
+}
+
+static void
+wireless_connection_changed (NMConnection *connection,
+ gpointer user_data)
+{
+ NMEditorWirelessSecurityMethodBinding *binding = user_data;
+ NMSettingWirelessSecurity *s_wsec;
+
+ if (binding->updating)
+ return;
+
+ s_wsec = nm_connection_get_setting_wireless_security (connection);
+ if ( (s_wsec && binding->s_wsec_in_use)
+ || (!s_wsec && !binding->s_wsec_in_use))
+ return;
+
+ binding->s_wsec_in_use = !binding->s_wsec_in_use;
+ wireless_security_changed (NULL, NULL, binding);
+}
+
+static void
+wireless_security_target_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NMEditorWirelessSecurityMethodBinding *binding = user_data;
+ char *method;
+
+ if (binding->updating)
+ return;
+
+ g_object_get (binding->target,
+ binding->target_property, &method,
+ NULL);
+
+ binding->updating = TRUE;
+
+ if (!strcmp (method, "none")) {
+ if (!binding->s_wsec_in_use)
+ return;
+ binding->s_wsec_in_use = FALSE;
+ nm_connection_remove_setting (binding->connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
+
+ binding->updating = FALSE;
+ return;
+ }
+
+ if (!binding->s_wsec_in_use) {
+ binding->s_wsec_in_use = TRUE;
+ nm_connection_add_setting (binding->connection, NM_SETTING (binding->s_wsec));
+ }
+
+ if (!strcmp (method, "wep-key")) {
+ g_object_set (binding->s_wsec,
+ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none",
+ NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_KEY,
+ NULL);
+ } else if (!strcmp (method, "wep-passphrase")) {
+ g_object_set (binding->s_wsec,
+ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none",
+ NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_PASSPHRASE,
+ NULL);
+ } else if (!strcmp (method, "leap")) {
+ g_object_set (binding->s_wsec,
+ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x",
+ NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap",
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN,
+ NULL);
+ } else if (!strcmp (method, "dynamic-wep")) {
+ g_object_set (binding->s_wsec,
+ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x",
+ NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open",
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN,
+ NULL);
+ } else if (!strcmp (method, "wpa-personal")) {
+ g_object_set (binding->s_wsec,
+ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk",
+ NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, NULL,
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN,
+ NULL);
+ } else if (!strcmp (method, "wpa-enterprise")) {
+ g_object_set (binding->s_wsec,
+ NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap",
+ NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, NULL,
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN,
+ NULL);
+ } else
+ g_warn_if_reached ();
+
+ binding->updating = FALSE;
+}
+
+static void
+wireless_security_target_destroyed (gpointer user_data,
+ GObject *ex_target)
+{
+ NMEditorWirelessSecurityMethodBinding *binding = user_data;
+
+ g_signal_handlers_disconnect_by_func (binding->s_wsec, G_CALLBACK (wireless_security_changed), binding);
+ g_object_unref (binding->s_wsec);
+ g_object_unref (binding->connection);
+
+ g_free (binding->target_property);
+
+ g_slice_free (NMEditorWirelessSecurityMethodBinding, binding);
+}
+
+/**
+ * nm_editor_bind_wireless_security_method:
+ * @connection: an #NMConnection
+ * @s_wsec: an #NMSettingWirelessSecurity
+ * @target: the target widget
+ * @target_property: the string-valued property on @target to bind
+ * @flags: %GBindingFlags
+ *
+ * Binds the wireless security method on @connection to
+ * @target_property on @target (and vice versa if
+ * %G_BINDING_BIDIRECTIONAL).
+ *
+ * @target_property will be of the values "none", "wpa-personal",
+ * "wpa-enterprise", "wep-key", "wep-passphrase", "dynamic-wep", or
+ * "leap".
+ *
+ * If binding bidirectionally, @s_wsec will be automatically added to
+ * or removed from @connection as needed when @target_property
+ * changes.
+ */
+void
+nm_editor_bind_wireless_security_method (NMConnection *connection,
+ NMSettingWirelessSecurity *s_wsec,
+ gpointer target,
+ const char *target_property,
+ GBindingFlags flags)
+{
+ NMEditorWirelessSecurityMethodBinding *binding;
+ char *notify;
+
+ binding = g_slice_new0 (NMEditorWirelessSecurityMethodBinding);
+
+ binding->target = target;
+ binding->target_property = g_strdup (target_property);
+ if (flags & G_BINDING_BIDIRECTIONAL) {
+ notify = g_strdup_printf ("notify::%s", target_property);
+ g_signal_connect (target, notify, G_CALLBACK (wireless_security_target_changed), binding);
+ g_free (notify);
+ }
+ g_object_weak_ref (target, wireless_security_target_destroyed, binding);
+
+ binding->connection = g_object_ref (connection);
+ g_signal_connect (connection, NM_CONNECTION_CHANGED,
+ G_CALLBACK (wireless_connection_changed), binding);
+ binding->s_wsec_in_use = (nm_connection_get_setting_wireless_security (connection) != NULL);
+
+ binding->s_wsec = g_object_ref (s_wsec);
+ g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_KEY_MGMT,
+ G_CALLBACK (wireless_security_changed), binding);
+ g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_AUTH_ALG,
+ G_CALLBACK (wireless_security_changed), binding);
+ g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE,
+ G_CALLBACK (wireless_security_changed), binding);
+
+ if (flags & G_BINDING_SYNC_CREATE)
+ wireless_security_changed (NULL, NULL, binding);
+}
+
+/* WEP key binding */
+
+typedef struct {
+ NMSettingWirelessSecurity *s_wsec;
+ GObject *entry, *key_selector;
+ char *entry_property, *key_selector_property;
+
+ gboolean updating;
+} NMEditorWepKeyBinding;
+
+static void
+wep_key_setting_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NMEditorWepKeyBinding *binding = user_data;
+ const char *key;
+ int index;
+
+ if (binding->updating)
+ return;
+
+ index = nm_setting_wireless_security_get_wep_tx_keyidx (binding->s_wsec);
+ key = nm_setting_wireless_security_get_wep_key (binding->s_wsec, index);
+
+ binding->updating = TRUE;
+ g_object_set (binding->key_selector,
+ binding->key_selector_property, index,
+ NULL);
+ g_object_set (binding->entry,
+ binding->entry_property, key,
+ NULL);
+ binding->updating = FALSE;
+}
+
+static void
+wep_key_ui_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NMEditorWepKeyBinding *binding = user_data;
+ char *key;
+ int index;
+
+ if (binding->updating)
+ return;
+
+ g_object_get (binding->key_selector,
+ binding->key_selector_property, &index,
+ NULL);
+ g_object_get (binding->entry,
+ binding->entry_property, &key,
+ NULL);
+
+ binding->updating = TRUE;
+ g_object_set (binding->s_wsec,
+ NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, index,
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, index == 0 ? key : NULL,
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, index == 1 ? key : NULL,
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, index == 2 ? key : NULL,
+ NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, index == 3 ? key : NULL,
+ NULL);
+ binding->updating = FALSE;
+
+ g_free (key);
+}
+
+static void
+wep_key_target_destroyed (gpointer user_data,
+ GObject *ex_target)
+{
+ NMEditorWepKeyBinding *binding = user_data;
+
+ g_signal_handlers_disconnect_by_func (binding->s_wsec, G_CALLBACK (wep_key_setting_changed), binding);
+
+ if (ex_target != binding->entry) {
+ g_signal_handlers_disconnect_by_func (binding->entry, G_CALLBACK (wep_key_ui_changed), binding);
+ g_object_weak_unref (binding->entry, wep_key_target_destroyed, binding);
+ } else {
+ g_signal_handlers_disconnect_by_func (binding->key_selector, G_CALLBACK (wep_key_ui_changed), binding);
+ g_object_weak_unref (binding->key_selector, wep_key_target_destroyed, binding);
+ }
+
+ g_object_unref (binding->s_wsec);
+ g_free (binding->entry_property);
+ g_free (binding->key_selector_property);
+
+ g_slice_free (NMEditorWepKeyBinding, binding);
+}
+
+/**
+ * nm_editor_bind_wireless_security_wep_key:
+ * @s_wsec: an #NMSettingWirelessSecurity
+ * @entry: an entry widget
+ * @entry_property: the string-valued property on @entry to bind
+ * @key_selector: a pop-up widget of some sort
+ * @key_selector_property: the integer-valued property on
+ * @key_selector to bind
+ * @flags: %GBindingFlags
+ *
+ * Binds the "wep-tx-keyidx" property on @s_wsec to
+ * @key_selector_property on @key_selector, and the corresponding
+ * "wep-keyN" property to @entry_property on @entry (and vice versa if
+ * %G_BINDING_BIDIRECTIONAL).
+ */
+void
+nm_editor_bind_wireless_security_wep_key (NMSettingWirelessSecurity *s_wsec,
+ gpointer entry,
+ const char *entry_property,
+ gpointer key_selector,
+ const char *key_selector_property,
+ GBindingFlags flags)
+{
+ NMEditorWepKeyBinding *binding;
+ char *notify;
+
+ binding = g_slice_new0 (NMEditorWepKeyBinding);
+ binding->s_wsec = g_object_ref (s_wsec);
+ binding->entry = entry;
+ binding->entry_property = g_strdup (entry_property);
+ binding->key_selector = key_selector;
+ binding->key_selector_property = g_strdup (key_selector_property);
+
+ g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY0,
+ G_CALLBACK (wep_key_setting_changed), binding);
+ g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY1,
+ G_CALLBACK (wep_key_setting_changed), binding);
+ g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY2,
+ G_CALLBACK (wep_key_setting_changed), binding);
+ g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY3,
+ G_CALLBACK (wep_key_setting_changed), binding);
+
+ g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX,
+ G_CALLBACK (wep_key_setting_changed), binding);
+
+ if (flags & G_BINDING_BIDIRECTIONAL) {
+ notify = g_strdup_printf ("notify::%s", entry_property);
+ g_signal_connect (entry, notify, G_CALLBACK (wep_key_ui_changed), binding);
+ g_free (notify);
+
+ notify = g_strdup_printf ("notify::%s", key_selector_property);
+ g_signal_connect (key_selector, notify, G_CALLBACK (wep_key_ui_changed), binding);
+ g_free (notify);
+ }
+
+ g_object_weak_ref (entry, wep_key_target_destroyed, binding);
+ g_object_weak_ref (key_selector, wep_key_target_destroyed, binding);
+
+ if (flags & G_BINDING_SYNC_CREATE)
+ wep_key_setting_changed (NULL, NULL, binding);
+}
+
+/* VLAN binding */
+
+typedef struct {
+ NMSettingVlan *s_vlan;
+
+ char *last_ifname_parent;
+ int last_ifname_id;
+
+ gboolean updating;
+} NMEditorVlanWidgetBinding;
+
+static gboolean
+parse_interface_name (const char *ifname,
+ char **parent_ifname,
+ int *id)
+{
+ const char *ifname_end;
+ char *end;
+
+ if (!ifname || !*ifname)
+ return FALSE;
+
+ if (g_str_has_prefix (ifname, "vlan")) {
+ ifname_end = ifname + 4;
+ *id = strtoul (ifname_end, &end, 10);
+ if (*end || end == (char *)ifname_end || *id < 0)
+ return FALSE;
+ *parent_ifname = NULL;
+ return TRUE;
+ }
+
+ ifname_end = strchr (ifname, '.');
+ if (ifname_end) {
+ *id = strtoul (ifname_end + 1, &end, 10);
+ if (*end || end == (char *)ifname_end + 1 || *id < 0)
+ return FALSE;
+ *parent_ifname = g_strndup (ifname, ifname_end - ifname);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+vlan_settings_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NMEditorVlanWidgetBinding *binding = user_data;
+ const char *ifname, *parent;
+ char *ifname_parent;
+ int ifname_id, id;
+
+ if (binding->updating)
+ return;
+
+ ifname = nm_setting_vlan_get_interface_name (binding->s_vlan);
+ parent = nm_setting_vlan_get_parent (binding->s_vlan);
+ id = nm_setting_vlan_get_id (binding->s_vlan);
+
+ if (!parse_interface_name (ifname, &ifname_parent, &ifname_id))
+ return;
+
+ /* If the id in INTERFACE_NAME changed, and ID is either unset, or was previously
+ * in sync with INTERFACE_NAME, then update ID.
+ */
+ if ( id != ifname_id
+ && (id == binding->last_ifname_id || id == 0)) {
+ binding->updating = TRUE;
+ g_object_set (G_OBJECT (binding->s_vlan),
+ NM_SETTING_VLAN_ID, ifname_id,
+ NULL);
+ binding->updating = FALSE;
+ }
+
+ /* If the PARENT in INTERFACE_NAME changed, and PARENT is either unset, or was
+ * previously in sync with INTERFACE_NAME, then update PARENT.
+ */
+ if ( g_strcmp0 (parent, ifname_parent) != 0
+ && ( g_strcmp0 (parent, binding->last_ifname_parent) == 0
+ || !parent || !*parent)) {
+ binding->updating = TRUE;
+ g_object_set (G_OBJECT (binding->s_vlan),
+ NM_SETTING_VLAN_PARENT, ifname_parent,
+ NULL);
+ binding->updating = FALSE;
+ }
+
+ g_free (binding->last_ifname_parent);
+ binding->last_ifname_parent = ifname_parent;
+ binding->last_ifname_id = ifname_id;
+}
+
+static void
+vlan_target_destroyed (gpointer user_data,
+ GObject *ex_target)
+{
+ NMEditorVlanWidgetBinding *binding = user_data;
+
+ g_free (binding->last_ifname_parent);
+ g_slice_free (NMEditorVlanWidgetBinding, binding);
+}
+
+/**
+ * nm_editor_bind_vlan_name:
+ * @s_vlan: an #NMSettingVlan
+ *
+ * Binds together several properties on @s_vlan, so that if the
+ * %NM_SETTING_VLAN_INTERFACE_NAME matches %NM_SETTING_VLAN_PARENT
+ * and %NM_SETTING_VLAN_ID in the obvious way, then changes to
+ * %NM_SETTING_VLAN_INTERFACE_NAME will propagate to the other
+ * two properties automatically.
+ */
+void
+nm_editor_bind_vlan_name (NMSettingVlan *s_vlan)
+{
+ NMEditorVlanWidgetBinding *binding;
+ const char *ifname;
+
+ binding = g_slice_new0 (NMEditorVlanWidgetBinding);
+ binding->s_vlan = s_vlan;
+
+ g_signal_connect (s_vlan, "notify::" NM_SETTING_VLAN_INTERFACE_NAME,
+ G_CALLBACK (vlan_settings_changed), binding);
+
+ g_object_weak_ref (G_OBJECT (s_vlan), vlan_target_destroyed, binding);
+
+ ifname = nm_setting_vlan_get_interface_name (s_vlan);
+ if (!parse_interface_name (ifname, &binding->last_ifname_parent, &binding->last_ifname_id)) {
+ binding->last_ifname_parent = NULL;
+ binding->last_ifname_id = 0;
+ }
+}
diff --git a/clients/tui/nm-editor-bindings.h b/clients/tui/nm-editor-bindings.h
new file mode 100644
index 0000000000..a7a32dd142
--- /dev/null
+++ b/clients/tui/nm-editor-bindings.h
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_EDITOR_BINDINGS_H
+#define NM_EDITOR_BINDINGS_H
+
+#include <glib-object.h>
+#include <nm-connection.h>
+#include <nm-setting-wireless-security.h>
+#include <nm-setting-vlan.h>
+
+G_BEGIN_DECLS
+
+void nm_editor_bindings_init (void);
+
+void nm_editor_bind_ip4_addresses_with_prefix_to_strv (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags);
+void nm_editor_bind_ip4_addresses_to_strv (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags);
+void nm_editor_bind_ip4_gateway_to_string (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags);
+
+void nm_editor_bind_ip4_route_to_strings (gpointer source,
+ const gchar *source_property,
+ gpointer dest_target,
+ const gchar *dest_target_property,
+ gpointer next_hop_target,
+ const gchar *next_hop_target_property,
+ gpointer metric_target,
+ const gchar *metric_target_property,
+ GBindingFlags flags);
+
+void nm_editor_bind_ip6_addresses_with_prefix_to_strv (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags);
+void nm_editor_bind_ip6_addresses_to_strv (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags);
+void nm_editor_bind_ip6_gateway_to_string (gpointer source,
+ const gchar *source_property,
+ gpointer target,
+ const gchar *target_property,
+ GBindingFlags flags);
+
+void nm_editor_bind_ip6_route_to_strings (gpointer source,
+ const gchar *source_property,
+ gpointer dest_target,
+ const gchar *dest_target_property,
+ gpointer next_hop_target,
+ const gchar *next_hop_target_property,
+ gpointer metric_target,
+ const gchar *metric_target_property,
+ GBindingFlags flags);
+
+void nm_editor_bind_wireless_security_method (NMConnection *connection,
+ NMSettingWirelessSecurity *s_wsec,
+ gpointer target,
+ const char *target_property,
+ GBindingFlags flags);
+void nm_editor_bind_wireless_security_wep_key (NMSettingWirelessSecurity *s_wsec,
+ gpointer entry,
+ const char *entry_property,
+ gpointer key_selector,
+ const char *key_selector_property,
+ GBindingFlags flags);
+
+void nm_editor_bind_vlan_name (NMSettingVlan *s_vlan);
+
+G_END_DECLS
+
+#endif /* NM_EDITOR_BINDINGS_H */
diff --git a/clients/tui/nm-editor-utils.c b/clients/tui/nm-editor-utils.c
new file mode 100644
index 0000000000..4ac0eb6413
--- /dev/null
+++ b/clients/tui/nm-editor-utils.c
@@ -0,0 +1,423 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2012, 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nm-editor-utils
+ * @short_description: Miscellaneous connection editor utilities
+ *
+ * nm-editor-utils contains helper functions for connection editors.
+ * The goal is that this should eventually be shared between nmtui,
+ * nm-connection-editor, and gnome-control-center.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <nm-utils.h>
+
+#include <nm-device-bond.h>
+#include <nm-device-bridge.h>
+#include <nm-device-team.h>
+#include <nm-device-ethernet.h>
+#include <nm-device-infiniband.h>
+#include <nm-device-team.h>
+#include <nm-device-vlan.h>
+#include <nm-device-wifi.h>
+
+#include "nm-editor-utils.h"
+#if 0
+#include "vpn-helpers.h"
+
+static GSList *vpn_plugins;
+
+static gint
+sort_vpn_plugins (gconstpointer a, gconstpointer b)
+{
+ NMVpnPluginUiInterface *aa = NM_VPN_PLUGIN_UI_INTERFACE (a);
+ NMVpnPluginUiInterface *bb = NM_VPN_PLUGIN_UI_INTERFACE (b);
+ char *aa_desc = NULL, *bb_desc = NULL;
+ int ret;
+
+ g_object_get (aa, NM_VPN_PLUGIN_UI_INTERFACE_NAME, &aa_desc, NULL);
+ g_object_get (bb, NM_VPN_PLUGIN_UI_INTERFACE_NAME, &bb_desc, NULL);
+
+ ret = g_strcmp0 (aa_desc, bb_desc);
+
+ g_free (aa_desc);
+ g_free (bb_desc);
+
+ return ret;
+}
+#endif
+
+static void
+wifi_connection_setup_func (NMConnection *connection,
+ NMSettingConnection *s_con,
+ NMSetting *s_hw)
+{
+ g_object_set (G_OBJECT (s_hw),
+ NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA,
+ NULL);
+}
+
+static void
+bond_connection_setup_func (NMConnection *connection,
+ NMSettingConnection *s_con,
+ NMSetting *s_hw)
+{
+ NMSettingBond *s_bond = NM_SETTING_BOND (s_hw);
+ const char **options, *def, *cur;
+ int i;
+
+ options = nm_setting_bond_get_valid_options (s_bond);
+ for (i = 0; options[i]; i++) {
+ def = nm_setting_bond_get_option_default (s_bond, options[i]);
+ cur = nm_setting_bond_get_option_by_name (s_bond, options[i]);
+ if (g_strcmp0 (def, cur) != 0)
+ nm_setting_bond_add_option (s_bond, options[i], def);
+ }
+}
+
+typedef void (*NMEditorNewConnectionSetupFunc) (NMConnection *connection,
+ NMSettingConnection *s_con,
+ NMSetting *s_hw);
+
+typedef struct {
+ NMEditorConnectionTypeData data;
+
+ const char *id_format;
+ NMEditorNewConnectionSetupFunc connection_setup_func;
+ gboolean no_autoconnect;
+} NMEditorConnectionTypeDataReal;
+
+static gint
+sort_types (gconstpointer a, gconstpointer b)
+{
+ NMEditorConnectionTypeData *typea = *(NMEditorConnectionTypeData **)a;
+ NMEditorConnectionTypeData *typeb = *(NMEditorConnectionTypeData **)b;
+
+ if (typea->virtual && !typeb->virtual)
+ return 1;
+ else if (typeb->virtual && !typea->virtual)
+ return -1;
+
+ if (typea->setting_type == NM_TYPE_SETTING_VPN &&
+ typeb->setting_type != NM_TYPE_SETTING_VPN)
+ return 1;
+ else if (typeb->setting_type == NM_TYPE_SETTING_VPN &&
+ typea->setting_type != NM_TYPE_SETTING_VPN)
+ return -1;
+
+ return g_utf8_collate (typea->name, typeb->name);
+}
+
+/**
+ * nm_editor_utils_get_connection_type_list:
+ *
+ * Gets an array of information about supported connection types. The
+ * array is sorted in a standard presentation order (hardware types
+ * first, alphabetized, then virtual types, alphabetized, then VPN
+ * types, alphabetized).
+ *
+ * Returns: the array of connection type information
+ */
+NMEditorConnectionTypeData **
+nm_editor_utils_get_connection_type_list (void)
+{
+ GPtrArray *array;
+ NMEditorConnectionTypeDataReal *item;
+ static NMEditorConnectionTypeData **list;
+#if 0
+ GHashTable *vpn_plugins_hash;
+ gboolean have_vpn_plugins;
+#endif
+
+ if (list)
+ return list;
+
+ array = g_ptr_array_new ();
+
+ item = g_new0 (NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("Ethernet");
+ item->data.setting_type = NM_TYPE_SETTING_WIRED;
+ item->data.device_type = NM_TYPE_DEVICE_ETHERNET;
+ item->data.virtual = FALSE;
+ item->id_format = _("Ethernet connection %d");
+ g_ptr_array_add (array, item);
+
+ item = g_new0 (NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("Wi-Fi");
+ item->data.setting_type = NM_TYPE_SETTING_WIRELESS;
+ item->data.device_type = NM_TYPE_DEVICE_WIFI;
+ item->data.virtual = FALSE;
+ item->id_format = _("Wi-Fi connection %d");
+ item->connection_setup_func = wifi_connection_setup_func;
+ g_ptr_array_add (array, item);
+
+ item = g_new0 (NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("InfiniBand");
+ item->data.setting_type = NM_TYPE_SETTING_INFINIBAND;
+ item->data.device_type = NM_TYPE_DEVICE_INFINIBAND;
+ item->data.virtual = FALSE;
+ item->id_format = _("InfiniBand connection %d");
+ g_ptr_array_add (array, item);
+
+#if 0
+ item = g_new0 (NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("Mobile Broadband");
+ item->data.setting_type = NM_TYPE_SETTING_GSM;
+ item->data.virtual = FALSE;
+ item->id_format = _("Mobile broadband connection %d");
+ item->no_autoconnect = TRUE;
+ g_ptr_array_add (array, item);
+
+ item = g_new0 (NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("DSL");
+ item->data.setting_type = NM_TYPE_SETTING_PPPOE;
+ item->data.device_type = NM_TYPE_DEVICE_ETHERNET;
+ item->data.virtual = FALSE;
+ item->id_format = _("DSL connection %d");
+ item->no_autoconnect = TRUE;
+ g_ptr_array_add (array, item);
+#endif
+
+ item = g_new0 (NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("Bond");
+ item->data.setting_type = NM_TYPE_SETTING_BOND;
+ item->data.device_type = NM_TYPE_DEVICE_BOND;
+ item->data.virtual = TRUE;
+ item->id_format = _("Bond connection %d");
+ item->connection_setup_func = bond_connection_setup_func;
+ g_ptr_array_add (array, item);
+
+ item = g_new0 (NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("Bridge");
+ item->data.setting_type = NM_TYPE_SETTING_BRIDGE;
+ item->data.slave_setting_type = NM_TYPE_SETTING_BRIDGE_PORT;
+ item->data.device_type = NM_TYPE_DEVICE_BRIDGE;
+ item->data.virtual = TRUE;
+ item->id_format = _("Bridge connection %d");
+ g_ptr_array_add (array, item);
+
+ item = g_new0 (NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("Team");
+ item->data.setting_type = NM_TYPE_SETTING_TEAM;
+ item->data.slave_setting_type = NM_TYPE_SETTING_TEAM_PORT;
+ item->data.device_type = NM_TYPE_DEVICE_TEAM;
+ item->data.virtual = TRUE;
+ item->id_format = _("Team connection %d");
+ g_ptr_array_add (array, item);
+
+ item = g_new0 (NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("VLAN");
+ item->data.setting_type = NM_TYPE_SETTING_VLAN;
+ item->data.device_type = NM_TYPE_DEVICE_VLAN;
+ item->data.virtual = TRUE;
+ item->id_format = _("VLAN connection %d");
+ g_ptr_array_add (array, item);
+
+#if 0
+ /* 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);
+ if (have_vpn_plugins) {
+ GHashTableIter iter;
+ gpointer name, plugin;
+
+ item = g_new0 (NMEditorConnectionTypeDataReal, 1);
+ item->data.name = _("VPN");
+ item->data.setting_type = NM_TYPE_SETTING_VPN;
+ item->data.virtual = TRUE;
+ item->id_format = _("VPN connection %d");
+ item->no_autoconnect = TRUE;
+ g_ptr_array_add (array, item);
+
+ vpn_plugins = NULL;
+ g_hash_table_iter_init (&iter, vpn_plugins_hash);
+ while (g_hash_table_iter_next (&iter, &name, &plugin))
+ vpn_plugins = g_slist_prepend (vpn_plugins, plugin);
+ vpn_plugins = g_slist_sort (vpn_plugins, sort_vpn_plugins);
+ }
+#endif
+
+ g_ptr_array_sort (array, sort_types);
+ g_ptr_array_add (array, NULL);
+
+ list = (NMEditorConnectionTypeData **)g_ptr_array_free (array, FALSE);
+ return list;
+}
+
+static char *
+get_available_connection_name (const char *format,
+ NMRemoteSettings *settings)
+{
+ GSList *connections, *iter, *names = NULL;
+ char *cname = NULL;
+ int i = 0;
+
+ connections = nm_remote_settings_list_connections (settings);
+ for (iter = connections; iter; iter = iter->next) {
+ const char *id;
+
+ id = nm_connection_get_id (NM_CONNECTION (iter->data));
+ g_assert (id);
+ names = g_slist_append (names, (gpointer) id);
+ }
+ g_slist_free (connections);
+
+ /* Find the next available unique connection name */
+ while (!cname && (i++ < 10000)) {
+ char *temp;
+ gboolean found = FALSE;
+
+ temp = g_strdup_printf (format, i);
+ for (iter = names; iter; iter = g_slist_next (iter)) {
+ if (!strcmp (iter->data, temp)) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (!found)
+ cname = temp;
+ else
+ g_free (temp);
+ }
+
+ g_slist_free (names);
+ return cname;
+}
+
+/**
+ * nm_editor_utils_create_connection:
+ * @type: the type of the connection's primary #NMSetting
+ * @master: (allow-none): the connection's master, if any
+ * @settings: an #NMRemoteSettings
+ *
+ * Creates a new #NMConnection of the given type, automatically
+ * creating a UUID and an appropriate not-currently-in-use connection
+ * name, setting #NMSettingConnection:autoconnect appropriately for
+ * the connection type, filling in slave-related information if
+ * @master is not %NULL, and initializing any other mandatory-to-set
+ * properties to reasonable initial values.
+ *
+ * Returns: a new #NMConnection
+ */
+NMConnection *
+nm_editor_utils_create_connection (GType type,
+ NMConnection *master,
+ NMRemoteSettings *settings)
+{
+ NMEditorConnectionTypeData **types;
+ NMEditorConnectionTypeDataReal *type_data = NULL;
+ const char *master_setting_type = NULL, *master_uuid = NULL;
+ GType master_type = G_TYPE_INVALID, slave_setting_type = G_TYPE_INVALID;
+ NMConnection *connection;
+ NMSettingConnection *s_con;
+ NMSetting *s_hw, *s_slave;
+ char *uuid, *id;
+ int i;
+
+ if (master) {
+ NMSettingConnection *master_s_con;
+
+ master_s_con = nm_connection_get_setting_connection (master);
+ master_setting_type = nm_setting_connection_get_connection_type (master_s_con);
+ master_uuid = nm_setting_connection_get_uuid (master_s_con);
+ master_type = nm_connection_lookup_setting_type (master_setting_type);
+ }
+
+ types = nm_editor_utils_get_connection_type_list ();
+ for (i = 0; types[i]; i++) {
+ if (types[i]->setting_type == type)
+ type_data = (NMEditorConnectionTypeDataReal *)types[i];
+ if (types[i]->setting_type == master_type)
+ slave_setting_type = types[i]->slave_setting_type;
+
+ }
+ if (!type_data) {
+ g_return_val_if_reached (NULL);
+ return NULL;
+ }
+
+ connection = nm_connection_new ();
+
+ s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ());
+ nm_connection_add_setting (connection, NM_SETTING (s_con));
+
+ s_hw = g_object_new (type, NULL);
+ nm_connection_add_setting (connection, s_hw);
+
+ if (slave_setting_type != G_TYPE_INVALID) {
+ s_slave = g_object_new (slave_setting_type, NULL);
+ nm_connection_add_setting (connection, s_slave);
+ }
+
+ uuid = nm_utils_uuid_generate ();
+ id = get_available_connection_name (type_data->id_format, settings);
+
+ g_object_set (s_con,
+ NM_SETTING_CONNECTION_UUID, uuid,
+ NM_SETTING_CONNECTION_ID, id,
+ NM_SETTING_CONNECTION_TYPE, nm_setting_get_name (s_hw),
+ NM_SETTING_CONNECTION_AUTOCONNECT, !type_data->no_autoconnect,
+ NM_SETTING_CONNECTION_MASTER, master_uuid,
+ NM_SETTING_CONNECTION_SLAVE_TYPE, master_setting_type,
+ NULL);
+
+ g_free (uuid);
+ g_free (id);
+
+ if (type_data->connection_setup_func)
+ type_data->connection_setup_func (connection, s_con, s_hw);
+
+ return connection;
+}
+
+/**
+ * nm_editor_utils_get_connection_type_data:
+ * @conn: an #NMConnection
+ *
+ * Gets the #NMEditorConnectionTypeData corresponding to
+ * @conn's connection type.
+ *
+ * Returns: the #NMEditorConnectionTypeData
+ */
+NMEditorConnectionTypeData *
+nm_editor_utils_get_connection_type_data (NMConnection *conn)
+{
+ NMSettingConnection *s_con;
+ const char *conn_type;
+ GType conn_gtype;
+ NMEditorConnectionTypeData **types;
+ int i;
+
+ s_con = nm_connection_get_setting_connection (conn);
+ g_return_val_if_fail (s_con != NULL, NULL);
+
+ conn_type = nm_setting_connection_get_connection_type (s_con);
+ conn_gtype = nm_connection_lookup_setting_type (conn_type);
+ g_return_val_if_fail (conn_gtype != G_TYPE_INVALID, NULL);
+
+ types = nm_editor_utils_get_connection_type_list ();
+ for (i = 0; types[i]; i++) {
+ if (types[i]->setting_type == conn_gtype)
+ return types[i];
+ }
+
+ return NULL;
+}
diff --git a/clients/tui/nm-editor-utils.h b/clients/tui/nm-editor-utils.h
new file mode 100644
index 0000000000..4b9ad5edbb
--- /dev/null
+++ b/clients/tui/nm-editor-utils.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2012, 2013 Red Hat, Inc.
+ */
+
+#ifndef NM_EDITOR_UTILS_H
+#define NM_EDITOR_UTILS_H
+
+#include <glib-object.h>
+#include <nm-remote-settings.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ const char *name;
+ GType setting_type;
+ GType slave_setting_type;
+ GType device_type;
+ gboolean virtual;
+} NMEditorConnectionTypeData;
+
+NMEditorConnectionTypeData **nm_editor_utils_get_connection_type_list (void);
+NMEditorConnectionTypeData *nm_editor_utils_get_connection_type_data (NMConnection *conn);
+
+NMConnection *nm_editor_utils_create_connection (GType type,
+ NMConnection *master,
+ NMRemoteSettings *settings);
+
+G_END_DECLS
+
+#endif /* NM_EDITOR_UTILS_H */
diff --git a/clients/tui/nmt-address-list.c b/clients/tui/nmt-address-list.c
new file mode 100644
index 0000000000..b822243c32
--- /dev/null
+++ b/clients/tui/nmt-address-list.c
@@ -0,0 +1,286 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-address-list
+ * @short_description: An editable list of IP addresses or hostnames
+ *
+ * #NmtAddressList is a subclass of #NmtWidgetList that contains
+ * entries displaying IP addresses, address/prefix strings, or
+ * hostnames. This is designed for binding its #NmtAddressList:strings
+ * property to an appropriate #NMSettingIP4Config or
+ * #NMSettingIP6Config property via one of the nm-editor-bindings
+ * functions.
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-address-list.h"
+#include "nmt-ip-entry.h"
+
+G_DEFINE_TYPE (NmtAddressList, nmt_address_list, NMT_TYPE_WIDGET_LIST)
+
+#define NMT_ADDRESS_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ADDRESS_LIST, NmtAddressListPrivate))
+
+typedef struct {
+ NmtAddressListType list_type;
+ char **strings;
+} NmtAddressListPrivate;
+
+enum {
+ PROP_0,
+ PROP_LIST_TYPE,
+ PROP_STRINGS,
+
+ LAST_PROP
+};
+
+/**
+ * NmtAddressListType:
+ * @NMT_ADDRESS_LIST_IP4_WITH_PREFIX: IPv4 address/prefix strings
+ * @NMT_ADDRESS_LIST_IP4: IPv4 addresses
+ * @NMT_ADDRESS_LIST_IP6_WITH_PREFIX: IPv6 address/prefix strings
+ * @NMT_ADDRESS_LIST_IP6: IPv6 addresses
+ * @NMT_ADDRESS_LIST_HOSTNAME: hostnames
+ *
+ * The type of address in an #NmtAddressList
+ */
+
+/**
+ * nmt_address_list_new:
+ * @list_type: the type of address the list will contain
+ *
+ * Creates a new #NmtAddressList
+ *
+ * Returns: a new #NmtAddressList
+ */
+NmtNewtWidget *
+nmt_address_list_new (NmtAddressListType list_type)
+{
+ return g_object_new (NMT_TYPE_ADDRESS_LIST,
+ "list-type", list_type,
+ NULL);
+}
+
+static void
+nmt_address_list_init (NmtAddressList *list)
+{
+}
+
+static gboolean
+strings_transform_to_entry (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ int n = GPOINTER_TO_INT (user_data);
+ char **strings;
+
+ strings = g_value_get_boxed (source_value);
+ if (n >= g_strv_length (strings))
+ return FALSE;
+
+ g_value_set_string (target_value, strings[n]);
+ return TRUE;
+}
+
+static gboolean
+strings_transform_from_entry (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NmtAddressList *list = NMT_ADDRESS_LIST (g_binding_get_source (binding));
+ NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list);
+ int n = GPOINTER_TO_INT (user_data);
+
+ if (n >= g_strv_length (priv->strings))
+ return FALSE;
+
+ g_free (priv->strings[n]);
+ priv->strings[n] = g_value_dup_string (source_value);
+
+ g_value_set_boxed (target_value, priv->strings);
+ return TRUE;
+}
+
+static gboolean
+hostname_filter (NmtNewtEntry *entry,
+ const char *text,
+ int ch,
+ int position,
+ gpointer user_data)
+{
+ return g_ascii_isalnum (ch) || ch == '.' || ch == '-';
+}
+
+static NmtNewtWidget *
+nmt_address_list_create_widget (NmtWidgetList *list,
+ int num)
+{
+ NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list);
+ NmtNewtWidget *entry;
+
+ if (priv->list_type == NMT_ADDRESS_LIST_IP4_WITH_PREFIX)
+ entry = nmt_ip_entry_new (25, AF_INET, TRUE, FALSE);
+ else if (priv->list_type == NMT_ADDRESS_LIST_IP4)
+ entry = nmt_ip_entry_new (25, AF_INET, FALSE, FALSE);
+ else if (priv->list_type == NMT_ADDRESS_LIST_IP6_WITH_PREFIX)
+ entry = nmt_ip_entry_new (25, AF_INET6, TRUE, FALSE);
+ else if (priv->list_type == NMT_ADDRESS_LIST_IP6)
+ entry = nmt_ip_entry_new (25, AF_INET6, FALSE, FALSE);
+ else if (priv->list_type == NMT_ADDRESS_LIST_HOSTNAME) {
+ entry = nmt_newt_entry_new (25, NMT_NEWT_ENTRY_NONEMPTY);
+ nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), hostname_filter, list);
+ } else
+ g_assert_not_reached ();
+
+ g_object_bind_property_full (list, "strings", entry, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
+ strings_transform_to_entry,
+ strings_transform_from_entry,
+ GINT_TO_POINTER (num), NULL);
+
+ return entry;
+}
+
+static void
+nmt_address_list_add_clicked (NmtWidgetList *list)
+{
+ NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list);
+ int len;
+
+ len = g_strv_length (priv->strings);
+ priv->strings = g_renew (char *, priv->strings, len + 2);
+ priv->strings[len] = g_strdup ("");
+ priv->strings[len + 1] = NULL;
+
+ nmt_widget_list_set_length (list, len + 1);
+ g_object_notify (G_OBJECT (list), "strings");
+}
+
+static void
+nmt_address_list_remove_clicked (NmtWidgetList *list,
+ int num)
+{
+ NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list);
+ int len;
+
+ len = g_strv_length (priv->strings);
+ g_free (priv->strings[num]);
+ memmove (priv->strings + num, priv->strings + num + 1, (len - num) * sizeof (char *));
+
+ nmt_widget_list_set_length (list, len - 1);
+ g_object_notify (G_OBJECT (list), "strings");
+}
+
+static void
+nmt_address_list_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_LIST_TYPE:
+ priv->list_type = g_value_get_uint (value);
+ break;
+ case PROP_STRINGS:
+ g_strfreev (priv->strings);
+ priv->strings = g_value_dup_boxed (value);
+ if (!priv->strings)
+ priv->strings = g_new0 (char *, 1);
+ nmt_widget_list_set_length (NMT_WIDGET_LIST (object),
+ g_strv_length (priv->strings));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_address_list_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_LIST_TYPE:
+ g_value_set_uint (value, priv->list_type);
+ break;
+ case PROP_STRINGS:
+ g_value_set_boxed (value, priv->strings);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_address_list_class_init (NmtAddressListClass *list_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+ NmtWidgetListClass *widget_list_class = NMT_WIDGET_LIST_CLASS (list_class);
+
+ g_type_class_add_private (list_class, sizeof (NmtAddressListPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_address_list_set_property;
+ object_class->get_property = nmt_address_list_get_property;
+
+ widget_list_class->create_widget = nmt_address_list_create_widget;
+ widget_list_class->add_clicked = nmt_address_list_add_clicked;
+ widget_list_class->remove_clicked = nmt_address_list_remove_clicked;
+
+ /**
+ * NmtAddressList:list-type:
+ *
+ * The type of address the list holds.
+ */
+ g_object_class_install_property
+ (object_class, PROP_LIST_TYPE,
+ g_param_spec_uint ("list-type", "", "",
+ 0, G_MAXUINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtAddressList:strings:
+ *
+ * The strings in the list's entries.
+ */
+ g_object_class_install_property
+ (object_class, PROP_STRINGS,
+ g_param_spec_boxed ("strings", "", "",
+ G_TYPE_STRV,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-address-list.h b/clients/tui/nmt-address-list.h
new file mode 100644
index 0000000000..df7a4f7962
--- /dev/null
+++ b/clients/tui/nmt-address-list.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_ADDRESS_LIST_H
+#define NMT_ADDRESS_LIST_H
+
+#include "nmt-widget-list.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_ADDRESS_LIST (nmt_address_list_get_type ())
+#define NMT_ADDRESS_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressList))
+#define NMT_ADDRESS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass))
+#define NMT_IS_ADDRESS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ADDRESS_LIST))
+#define NMT_IS_ADDRESS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ADDRESS_LIST))
+#define NMT_ADDRESS_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass))
+
+typedef struct {
+ NmtWidgetList parent;
+
+} NmtAddressList;
+
+typedef struct {
+ NmtWidgetListClass parent;
+
+} NmtAddressListClass;
+
+GType nmt_address_list_get_type (void);
+
+typedef enum {
+ NMT_ADDRESS_LIST_IP4_WITH_PREFIX,
+ NMT_ADDRESS_LIST_IP4,
+ NMT_ADDRESS_LIST_IP6_WITH_PREFIX,
+ NMT_ADDRESS_LIST_IP6,
+ NMT_ADDRESS_LIST_HOSTNAME
+} NmtAddressListType;
+
+NmtNewtWidget *nmt_address_list_new (NmtAddressListType list_type);
+
+G_END_DECLS
+
+#endif /* NMT_ADDRESS_LIST_H */
diff --git a/clients/tui/nmt-connect-connection-list.c b/clients/tui/nmt-connect-connection-list.c
new file mode 100644
index 0000000000..fffdc8ea4e
--- /dev/null
+++ b/clients/tui/nmt-connect-connection-list.c
@@ -0,0 +1,741 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-connect-connection-list
+ * @short_description: Connection list for "nmtui connect"
+ *
+ * #NmtConnectConnectionList is the list of devices, connections, and
+ * access points displayed by "nmtui connect".
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <glib/gi18n-lib.h>
+
+#include <nm-access-point.h>
+#include <nm-device-wifi.h>
+#include <nm-utils.h>
+
+#include "nmtui.h"
+#include "nmt-connect-connection-list.h"
+
+G_DEFINE_TYPE (NmtConnectConnectionList, nmt_connect_connection_list, NMT_TYPE_NEWT_LISTBOX)
+
+#define NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListPrivate))
+
+typedef struct {
+ char *name;
+ NMDevice *device;
+
+ int sort_order;
+
+ GSList *conns;
+} NmtConnectDevice;
+
+typedef struct {
+ const char *name;
+ char *ssid;
+
+ NMConnection *conn;
+ NMAccessPoint *ap;
+ NMDevice *device;
+ NMActiveConnection *active;
+} NmtConnectConnection;
+
+typedef struct {
+ GSList *nmt_devices;
+} NmtConnectConnectionListPrivate;
+
+static const char *strength_full, *strength_high, *strength_med, *strength_low, *strength_none;
+
+/**
+ * nmt_connect_connection_list_new:
+ *
+ * Creates a new #NmtConnectConnectionList
+ *
+ * Returns: a new #NmtConnectConnectionList
+ */
+NmtNewtWidget *
+nmt_connect_connection_list_new (void)
+{
+ return g_object_new (NMT_TYPE_CONNECT_CONNECTION_LIST,
+ "flags", NMT_NEWT_LISTBOX_SCROLL | NMT_NEWT_LISTBOX_BORDER,
+ "skip-null-keys", TRUE,
+ NULL);
+}
+
+static void
+nmt_connect_connection_list_init (NmtConnectConnectionList *list)
+{
+}
+
+static void
+nmt_connect_connection_free (NmtConnectConnection *nmtconn)
+{
+ g_clear_object (&nmtconn->conn);
+ g_clear_object (&nmtconn->ap);
+ g_clear_object (&nmtconn->active);
+ g_free (nmtconn->ssid);
+}
+
+static void
+nmt_connect_device_free (NmtConnectDevice *nmtdev)
+{
+ g_clear_pointer (&nmtdev->name, g_free);
+ g_clear_object (&nmtdev->device);
+
+ g_slist_free_full (nmtdev->conns, (GDestroyNotify) nmt_connect_connection_free);
+}
+
+static const char *device_sort_order[] = {
+ "NMDeviceEthernet",
+ "NMDeviceInfiniband",
+ "NMDeviceWifi",
+ NM_SETTING_VLAN_SETTING_NAME,
+ NM_SETTING_BOND_SETTING_NAME,
+ NM_SETTING_TEAM_SETTING_NAME,
+ NM_SETTING_BRIDGE_SETTING_NAME,
+ "NMDeviceModem",
+ "NMDeviceBt"
+};
+static const int device_sort_order_len = G_N_ELEMENTS (device_sort_order);
+
+static int
+get_sort_order_for_device (NMDevice *device)
+{
+ const char *type;
+ int i;
+
+ type = G_OBJECT_TYPE_NAME (device);
+ for (i = 0; i < device_sort_order_len; i++) {
+ if (!strcmp (type, device_sort_order[i]))
+ return i;
+ }
+
+ return -1;
+}
+
+static int
+get_sort_order_for_connection (NMConnection *conn)
+{
+ NMSettingConnection *s_con;
+ const char *type;
+ int i;
+
+ s_con = nm_connection_get_setting_connection (conn);
+ type = nm_setting_connection_get_connection_type (s_con);
+
+ for (i = 0; i < device_sort_order_len; i++) {
+ if (!strcmp (type, device_sort_order[i]))
+ return i;
+ }
+
+ return -1;
+}
+
+static int
+sort_connections (gconstpointer a,
+ gconstpointer b)
+{
+ NmtConnectConnection *nmta = (NmtConnectConnection *)a;
+ NmtConnectConnection *nmtb = (NmtConnectConnection *)b;
+
+ /* If nmta and nmtb both have NMConnections, sort them by timestamp */
+ if (nmta->conn && nmtb->conn) {
+ NMSettingConnection *s_con_a, *s_con_b;
+ guint64 time_a, time_b;
+
+ s_con_a = nm_connection_get_setting_connection (nmta->conn);
+ s_con_b = nm_connection_get_setting_connection (nmtb->conn);
+
+ time_a = nm_setting_connection_get_timestamp (s_con_a);
+ time_b = nm_setting_connection_get_timestamp (s_con_b);
+
+ return (int) (time_b - time_a);
+ }
+
+ /* If one is an NMConnection and the other is an NMAccessPoint, the
+ * connection comes first.
+ */
+ if (nmta->conn)
+ return -1;
+ else if (nmtb->conn)
+ return 1;
+
+ g_return_val_if_fail (nmta->ap && nmtb->ap, 0);
+
+ /* If both are access points, then sort by strength */
+ return nm_access_point_get_strength (nmtb->ap) - nm_access_point_get_strength (nmta->ap);
+}
+
+static void
+add_connections_for_device (NmtConnectDevice *nmtdev,
+ GSList *connections)
+{
+ GSList *iter;
+
+ for (iter = connections; iter; iter = iter->next) {
+ NMConnection *conn = iter->data;
+ NMSettingConnection *s_con;
+
+ s_con = nm_connection_get_setting_connection (conn);
+ if (nm_setting_connection_get_master (s_con))
+ continue;
+
+ if (nm_device_connection_valid (nmtdev->device, conn)) {
+ NmtConnectConnection *nmtconn = g_slice_new0 (NmtConnectConnection);
+
+ nmtconn->name = nm_connection_get_id (conn);
+ nmtconn->device = nmtdev->device;
+ nmtconn->conn = g_object_ref (conn);
+ nmtdev->conns = g_slist_prepend (nmtdev->conns, nmtconn);
+ }
+ }
+}
+
+/* stolen from nm-applet */
+static char *
+hash_ap (NMAccessPoint *ap)
+{
+ unsigned char input[66];
+ const GByteArray *ssid;
+ NM80211Mode mode;
+ guint32 flags;
+ guint32 wpa_flags;
+ guint32 rsn_flags;
+
+ memset (&input[0], 0, sizeof (input));
+
+ ssid = nm_access_point_get_ssid (ap);
+ if (ssid)
+ memcpy (input, ssid->data, ssid->len);
+
+ mode = nm_access_point_get_mode (ap);
+ if (mode == NM_802_11_MODE_INFRA)
+ input[32] |= (1 << 0);
+ else if (mode == NM_802_11_MODE_ADHOC)
+ input[32] |= (1 << 1);
+ else
+ input[32] |= (1 << 2);
+
+ /* Separate out no encryption, WEP-only, and WPA-capable */
+ flags = nm_access_point_get_flags (ap);
+ wpa_flags = nm_access_point_get_wpa_flags (ap);
+ rsn_flags = nm_access_point_get_rsn_flags (ap);
+ if ( !(flags & NM_802_11_AP_FLAGS_PRIVACY)
+ && (wpa_flags == NM_802_11_AP_SEC_NONE)
+ && (rsn_flags == NM_802_11_AP_SEC_NONE))
+ input[32] |= (1 << 3);
+ else if ( (flags & NM_802_11_AP_FLAGS_PRIVACY)
+ && (wpa_flags == NM_802_11_AP_SEC_NONE)
+ && (rsn_flags == NM_802_11_AP_SEC_NONE))
+ input[32] |= (1 << 4);
+ else if ( !(flags & NM_802_11_AP_FLAGS_PRIVACY)
+ && (wpa_flags != NM_802_11_AP_SEC_NONE)
+ && (rsn_flags != NM_802_11_AP_SEC_NONE))
+ input[32] |= (1 << 5);
+ else
+ input[32] |= (1 << 6);
+
+ /* duplicate it */
+ memcpy (&input[33], &input[0], 32);
+ return g_compute_checksum_for_data (G_CHECKSUM_MD5, input, sizeof (input));
+}
+
+static void
+add_connections_for_aps (NmtConnectDevice *nmtdev,
+ GSList *connections)
+{
+ NmtConnectConnection *nmtconn;
+ NMConnection *conn;
+ NMAccessPoint *ap;
+ const GPtrArray *aps;
+ GHashTable *seen_ssids;
+ char *ap_hash;
+ GSList *iter;
+ int i;
+
+ aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (nmtdev->device));
+ if (!aps)
+ return;
+
+ seen_ssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ for (i = 0; i < aps->len; i++) {
+ ap = aps->pdata[i];
+
+ if (!nm_access_point_get_ssid (ap))
+ continue;
+
+ ap_hash = hash_ap (ap);
+ if (g_hash_table_contains (seen_ssids, ap_hash)) {
+ g_free (ap_hash);
+ continue;
+ }
+ g_hash_table_add (seen_ssids, ap_hash);
+
+ nmtconn = g_slice_new0 (NmtConnectConnection);
+ nmtconn->device = nmtdev->device;
+ nmtconn->ap = g_object_ref (ap);
+ nmtconn->ssid = nm_utils_ssid_to_utf8 (nm_access_point_get_ssid (ap));
+
+ for (iter = connections; iter; iter = iter->next) {
+ conn = iter->data;
+ if ( nm_device_connection_valid (nmtdev->device, conn)
+ && nm_access_point_connection_valid (ap, conn)) {
+ nmtconn->name = nm_connection_get_id (conn);
+ nmtconn->conn = g_object_ref (conn);
+ break;
+ }
+ }
+
+ if (!iter)
+ nmtconn->name = nmtconn->ssid;
+
+ nmtdev->conns = g_slist_prepend (nmtdev->conns, nmtconn);
+ }
+
+ g_hash_table_unref (seen_ssids);
+}
+
+static GSList *
+append_nmt_devices_for_devices (GSList *nmt_devices,
+ const GPtrArray *devices,
+ char **names,
+ GSList *connections)
+{
+ NmtConnectDevice *nmtdev;
+ NMDevice *device;
+ int i, sort_order;
+
+ for (i = 0; i < devices->len; i++) {
+ device = devices->pdata[i];
+
+ sort_order = get_sort_order_for_device (device);
+ if (sort_order == -1)
+ continue;
+
+ nmtdev = g_slice_new0 (NmtConnectDevice);
+ nmtdev->name = g_strdup (names[i]);
+ nmtdev->device = g_object_ref (device);
+ nmtdev->sort_order = sort_order;
+
+ if (NM_IS_DEVICE_WIFI (device))
+ add_connections_for_aps (nmtdev, connections);
+ else
+ add_connections_for_device (nmtdev, connections);
+ nmtdev->conns = g_slist_sort (nmtdev->conns, sort_connections);
+
+ nmt_devices = g_slist_prepend (nmt_devices, nmtdev);
+ }
+
+ return nmt_devices;
+}
+
+static GSList *
+append_nmt_devices_for_virtual_devices (GSList *nmt_devices,
+ GSList *connections)
+{
+ NmtConnectDevice *nmtdev = NULL;
+ GSList *iter;
+ GHashTable *devices_by_name;
+ char *name;
+ NMConnection *conn;
+ NmtConnectConnection *nmtconn;
+ int sort_order;
+
+ devices_by_name = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (iter = connections; iter; iter = iter->next) {
+ conn = iter->data;
+ sort_order = get_sort_order_for_connection (conn);
+ if (sort_order == -1)
+ continue;
+
+ name = nm_connection_get_virtual_device_description (conn);
+ if (name)
+ nmtdev = g_hash_table_lookup (devices_by_name, name);
+ if (nmtdev)
+ g_free (name);
+ else {
+ nmtdev = g_slice_new0 (NmtConnectDevice);
+ nmtdev->name = name ? name : g_strdup ("Unknown");
+ nmtdev->sort_order = sort_order;
+
+ g_hash_table_insert (devices_by_name, nmtdev->name, nmtdev);
+ nmt_devices = g_slist_prepend (nmt_devices, nmtdev);
+ }
+
+ nmtconn = g_slice_new0 (NmtConnectConnection);
+ nmtconn->name = nm_connection_get_id (conn);
+ nmtconn->conn = g_object_ref (conn);
+
+ nmtdev->conns = g_slist_insert_sorted (nmtdev->conns, nmtconn, sort_connections);
+ }
+
+ g_hash_table_destroy (devices_by_name);
+ return nmt_devices;
+}
+
+static GSList *
+append_nmt_devices_for_vpns (GSList *nmt_devices,
+ GSList *connections)
+{
+ NmtConnectDevice *nmtdev;
+ GSList *iter;
+ NMConnection *conn;
+ NmtConnectConnection *nmtconn;
+
+ nmtdev = g_slice_new0 (NmtConnectDevice);
+ nmtdev->name = g_strdup (_("VPN"));
+ nmtdev->sort_order = 100;
+
+ for (iter = connections; iter; iter = iter->next) {
+ conn = iter->data;
+ if (!nm_connection_is_type (conn, NM_SETTING_VPN_SETTING_NAME))
+ continue;
+
+ nmtconn = g_slice_new0 (NmtConnectConnection);
+ nmtconn->name = nm_connection_get_id (conn);
+ nmtconn->conn = g_object_ref (conn);
+
+ nmtdev->conns = g_slist_insert_sorted (nmtdev->conns, nmtconn, sort_connections);
+ }
+
+ if (nmtdev->conns)
+ nmt_devices = g_slist_prepend (nmt_devices, nmtdev);
+ else
+ nmt_connect_device_free (nmtdev);
+
+ return nmt_devices;
+}
+
+static int
+sort_nmt_devices (gconstpointer a,
+ gconstpointer b)
+{
+ NmtConnectDevice *nmta = (NmtConnectDevice *)a;
+ NmtConnectDevice *nmtb = (NmtConnectDevice *)b;
+
+ if (nmta->sort_order != nmtb->sort_order)
+ return nmta->sort_order - nmtb->sort_order;
+
+ return strcmp (nmta->name, nmtb->name);
+}
+
+static NMActiveConnection *
+connection_find_ac (NMConnection *conn,
+ const GPtrArray *acs)
+{
+ NMActiveConnection *ac;
+ const char *path, *ac_path;
+ int i;
+
+ path = nm_connection_get_path (conn);
+ for (i = 0; acs && i < acs->len; i++) {
+ ac = acs->pdata[i];
+ ac_path = nm_active_connection_get_connection (ac);
+
+ if (!g_strcmp0 (path, ac_path))
+ return ac;
+ }
+
+ return NULL;
+}
+
+static void
+nmt_connect_connection_list_rebuild (NmtConnectConnectionList *list)
+{
+ NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (list);
+ NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (list);
+ const GPtrArray *devices, *acs;
+ int max_width;
+ char **names, *row, active_col;
+ const char *strength_col;
+ GSList *connections;
+ GSList *nmt_devices, *diter, *citer;
+ NmtConnectDevice *nmtdev;
+ NmtConnectConnection *nmtconn;
+
+ g_slist_free_full (priv->nmt_devices, (GDestroyNotify) nmt_connect_device_free);
+ priv->nmt_devices = NULL;
+ nmt_newt_listbox_clear (listbox);
+
+ devices = nm_client_get_devices (nm_client);
+ acs = nm_client_get_active_connections (nm_client);
+ connections = nm_remote_settings_list_connections (nm_settings);
+
+ nmt_devices = NULL;
+ if (devices) {
+ names = nm_device_disambiguate_names ((NMDevice **) devices->pdata, devices->len);
+ nmt_devices = append_nmt_devices_for_devices (nmt_devices, devices, names, connections);
+ g_strfreev (names);
+ }
+ nmt_devices = append_nmt_devices_for_virtual_devices (nmt_devices, connections);
+ nmt_devices = append_nmt_devices_for_vpns (nmt_devices, connections);
+
+ nmt_devices = g_slist_sort (nmt_devices, sort_nmt_devices);
+ g_slist_free (connections);
+
+ max_width = 0;
+ for (diter = nmt_devices; diter; diter = diter->next) {
+ nmtdev = diter->data;
+ for (citer = nmtdev->conns; citer; citer = citer->next) {
+ nmtconn = citer->data;
+
+ max_width = MAX (max_width, nmt_newt_text_width (nmtconn->name));
+ }
+ }
+
+ for (diter = nmt_devices; diter; diter = diter->next) {
+ nmtdev = diter->data;
+
+ if (diter != nmt_devices)
+ nmt_newt_listbox_append (listbox, "", NULL);
+ nmt_newt_listbox_append (listbox, nmtdev->name, NULL);
+
+ for (citer = nmtdev->conns; citer; citer = citer->next) {
+ nmtconn = citer->data;
+
+ if (nmtconn->conn)
+ nmtconn->active = connection_find_ac (nmtconn->conn, acs);
+ if (nmtconn->active) {
+ g_object_ref (nmtconn->active);
+ active_col = '*';
+ } else
+ active_col = ' ';
+
+ if (nmtconn->ap) {
+ guint8 strength = nm_access_point_get_strength (nmtconn->ap);
+
+ if (strength > 80)
+ strength_col = strength_full;
+ else if (strength > 55)
+ strength_col = strength_high;
+ else if (strength > 30)
+ strength_col = strength_med;
+ else if (strength > 5)
+ strength_col = strength_low;
+ else
+ strength_col = strength_none;
+ } else
+ strength_col = NULL;
+
+ row = g_strdup_printf ("%c %s%-*s%s%s",
+ active_col,
+ nmtconn->name,
+ (int)(max_width - nmt_newt_text_width (nmtconn->name)), "",
+ strength_col ? " " : "",
+ strength_col ? strength_col : "");
+
+ nmt_newt_listbox_append (listbox, row, nmtconn);
+ g_free (row);
+ }
+ }
+
+ priv->nmt_devices = nmt_devices;
+
+ g_object_notify (G_OBJECT (listbox), "active");
+ g_object_notify (G_OBJECT (listbox), "active-key");
+}
+
+static void
+rebuild_on_acs_changed (GObject *object,
+ GParamSpec *spec,
+ gpointer list)
+{
+ nmt_connect_connection_list_rebuild (list);
+}
+
+static void
+rebuild_on_devices_changed (NMClient *client,
+ NMDevice *device,
+ gpointer list)
+{
+ nmt_connect_connection_list_rebuild (list);
+}
+
+static void
+nmt_connect_connection_list_constructed (GObject *object)
+{
+ NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST (object);
+
+ g_signal_connect (nm_client, "notify::" NM_CLIENT_ACTIVE_CONNECTIONS,
+ G_CALLBACK (rebuild_on_acs_changed), list);
+ g_signal_connect (nm_client, "device-added",
+ G_CALLBACK (rebuild_on_devices_changed), list);
+ g_signal_connect (nm_client, "device-removed",
+ G_CALLBACK (rebuild_on_devices_changed), list);
+
+ nmt_connect_connection_list_rebuild (list);
+
+ G_OBJECT_CLASS (nmt_connect_connection_list_parent_class)->constructed (object);
+}
+
+static void
+nmt_connect_connection_list_finalize (GObject *object)
+{
+ NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (object);
+
+ g_slist_free_full (priv->nmt_devices, (GDestroyNotify) nmt_connect_device_free);
+
+ g_signal_handlers_disconnect_by_func (nm_client, G_CALLBACK (rebuild_on_acs_changed), object);
+ g_signal_handlers_disconnect_by_func (nm_client, G_CALLBACK (rebuild_on_devices_changed), object);
+
+ G_OBJECT_CLASS (nmt_connect_connection_list_parent_class)->finalize (object);
+}
+
+static void
+nmt_connect_connection_list_class_init (NmtConnectConnectionListClass *list_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+ char *tmp;
+
+ g_type_class_add_private (list_class, sizeof (NmtConnectConnectionListPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_connect_connection_list_constructed;
+ object_class->finalize = nmt_connect_connection_list_finalize;
+
+ /* globals */
+ tmp = nmt_newt_locale_from_utf8 ("\342\226\202\342\226\204\342\226\206\342\226\210");
+ if (*tmp) {
+ strength_full = /* ▂▄▆█ */ "\342\226\202\342\226\204\342\226\206\342\226\210";
+ strength_high = /* ▂▄▆_ */ "\342\226\202\342\226\204\342\226\206_";
+ strength_med = /* ▂▄__ */ "\342\226\202\342\226\204__";
+ strength_low = /* ▂___ */ "\342\226\202___";
+ strength_none = /* ____ */ "____";
+ } else {
+ strength_full = "****";
+ strength_high = "*** ";
+ strength_med = "** ";
+ strength_low = "* ";
+ strength_none = " ";
+ }
+ g_free (tmp);
+}
+
+/**
+ * nmt_connect_connection_list_get_connection:
+ * @list: an #NmtConnectConnectionList
+ * @identifier: a connection ID or UUID, or device name
+ * @connection: (out) (transfer none): the #NMConnection to be activated
+ * @device: (out) (transfer none): the #NMDevice to activate @connection on
+ * @specific_object: (out) (transfer none): the "specific object" to connect to
+ * @active: (out) (transfer none): the #NMActiveConnection corresponding
+ * to the selection, if any.
+ *
+ * Gets information about the indicated connection.
+ *
+ * Returns: %TRUE if there was a match, %FALSE if not.
+ */
+gboolean
+nmt_connect_connection_list_get_connection (NmtConnectConnectionList *list,
+ const char *identifier,
+ NMConnection **connection,
+ NMDevice **device,
+ NMObject **specific_object,
+ NMActiveConnection **active)
+{
+ NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (list);
+ GSList *diter, *citer;
+ NmtConnectDevice *nmtdev;
+ NmtConnectConnection *nmtconn = NULL;
+ NMConnection *conn = NULL;
+
+ g_return_val_if_fail (identifier, FALSE);
+
+ if (nm_utils_is_uuid (identifier))
+ conn = NM_CONNECTION (nm_remote_settings_get_connection_by_uuid (nm_settings, identifier));
+ if (!conn)
+ conn = NM_CONNECTION (nm_remote_settings_get_connection_by_id (nm_settings, identifier));
+
+ for (diter = priv->nmt_devices; diter; diter = diter->next) {
+ nmtdev = diter->data;
+ if (!nmtdev->conns)
+ continue;
+
+ for (citer = nmtdev->conns; citer; citer = citer->next) {
+ nmtconn = citer->data;
+ if (conn) {
+ if (conn == nmtconn->conn)
+ goto found;
+ } else if (nmtconn->ssid && !strcmp (identifier, nmtconn->ssid))
+ goto found;
+ }
+
+ if (!conn && nmtdev->device && !strcmp (identifier, nm_device_get_ip_iface (nmtdev->device))) {
+ nmtconn = nmtdev->conns->data;
+ goto found;
+ }
+ }
+
+ return FALSE;
+
+ found:
+ if (connection)
+ *connection = nmtconn->conn;
+ if (device)
+ *device = nmtconn->device;
+ if (specific_object)
+ *specific_object = NM_OBJECT (nmtconn->ap);
+ if (active)
+ *active = nmtconn->active;
+
+ return TRUE;
+}
+
+/**
+ * nmt_connect_connection_list_get_selection:
+ * @list: an #NmtConnectConnectionList
+ * @connection: (out) (transfer none): the #NMConnection to be activated
+ * @device: (out) (transfer none): the #NMDevice to activate @connection on
+ * @specific_object: (out) (transfer none): the "specific object" to connect to
+ * @active: (out) (transfer none): the #NMActiveConnection corresponding
+ * to the selection, if any.
+ *
+ * Gets information about the selected row.
+ *
+ * Returns: %TRUE if there is a selection, %FALSE if not.
+ */
+gboolean
+nmt_connect_connection_list_get_selection (NmtConnectConnectionList *list,
+ NMConnection **connection,
+ NMDevice **device,
+ NMObject **specific_object,
+ NMActiveConnection **active)
+{
+ NmtConnectConnection *nmtconn;
+
+ nmtconn = nmt_newt_listbox_get_active_key (NMT_NEWT_LISTBOX (list));
+ if (!nmtconn)
+ return FALSE;
+
+ if (connection)
+ *connection = nmtconn->conn;
+ if (device)
+ *device = nmtconn->device;
+ if (specific_object)
+ *specific_object = NM_OBJECT (nmtconn->ap);
+ if (active)
+ *active = nmtconn->active;
+
+ return TRUE;
+}
diff --git a/clients/tui/nmt-connect-connection-list.h b/clients/tui/nmt-connect-connection-list.h
new file mode 100644
index 0000000000..d47900437d
--- /dev/null
+++ b/clients/tui/nmt-connect-connection-list.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_CONNECT_CONNECTION_LIST_H
+#define NMT_CONNECT_CONNECTION_LIST_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_CONNECT_CONNECTION_LIST (nmt_connect_connection_list_get_type ())
+#define NMT_CONNECT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionList))
+#define NMT_CONNECT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListClass))
+#define NMT_IS_CONNECT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST))
+#define NMT_IS_CONNECT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_CONNECT_CONNECTION_LIST))
+#define NMT_CONNECT_CONNECTION_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListClass))
+
+typedef struct {
+ NmtNewtListbox parent;
+
+} NmtConnectConnectionList;
+
+typedef struct {
+ NmtNewtListboxClass parent;
+
+} NmtConnectConnectionListClass;
+
+GType nmt_connect_connection_list_get_type (void);
+
+NmtNewtWidget *nmt_connect_connection_list_new (void);
+
+gboolean nmt_connect_connection_list_get_connection (NmtConnectConnectionList *list,
+ const char *identifier,
+ NMConnection **connection,
+ NMDevice **device,
+ NMObject **specific_object,
+ NMActiveConnection **active);
+gboolean nmt_connect_connection_list_get_selection (NmtConnectConnectionList *list,
+ NMConnection **connection,
+ NMDevice **device,
+ NMObject **specific_object,
+ NMActiveConnection **active);
+
+G_END_DECLS
+
+#endif /* NMT_CONNECT_CONNECTION_LIST_H */
diff --git a/clients/tui/nmt-device-entry.c b/clients/tui/nmt-device-entry.c
new file mode 100644
index 0000000000..de8b630554
--- /dev/null
+++ b/clients/tui/nmt-device-entry.c
@@ -0,0 +1,594 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-device-entry
+ * @short_description: #NmtNewtEntry for identifying a device
+ *
+ * #NmtDeviceEntry provides a widget for identifying a device, either
+ * by interface name or by hardware address. The user can enter either
+ * value, and the entry's #NmtDeviceEntry:interface-name or
+ * #NmtDeviceEntry:mac-address property will be set accordingly. If
+ * the entry recognizes the interface name or mac address typed in as
+ * matching a known #NMDevice, then it will also display the other
+ * property in parentheses.
+ *
+ * FIXME: #NmtDeviceEntry is currently an #NmtPageGrid object, so that
+ * we can possibly eventually add a button to its "extra" field, that
+ * would pop up a form for selecting a device. But if we're not going
+ * to implement that then we should make it just an #NmtNewtEntry.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <sys/socket.h>
+#include <linux/if_arp.h>
+
+#include <glib/gi18n-lib.h>
+#include <nm-device.h>
+#include <nm-device-infiniband.h>
+#include <nm-utils.h>
+
+#include "nmtui.h"
+#include "nmt-device-entry.h"
+
+G_DEFINE_TYPE (NmtDeviceEntry, nmt_device_entry, NMT_TYPE_PAGE_GRID)
+
+#define NMT_DEVICE_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryPrivate))
+
+typedef struct {
+ GType hardware_type;
+ NmtDeviceEntryDeviceFilter device_filter;
+ gpointer device_filter_data;
+ int arptype;
+
+ char *interface_name;
+ GByteArray *mac_address;
+
+ char *label;
+ NmtNewtEntry *entry;
+ NmtNewtWidget *button;
+
+ gboolean updating;
+} NmtDeviceEntryPrivate;
+
+enum {
+ PROP_0,
+ PROP_LABEL,
+ PROP_WIDTH,
+ PROP_HARDWARE_TYPE,
+ PROP_INTERFACE_NAME,
+ PROP_MAC_ADDRESS,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_device_entry_new:
+ * @label: the label for the entry
+ * @width: the width of the entry
+ * @hardware_type: the type of #NMDevice to be selected, or
+ * %G_TYPE_NONE if this is for a virtual device type.
+ *
+ * Creates a new #NmtDeviceEntry, for identifying a device of type
+ * @hardware_type. If @hardware_type is %G_TYPE_NONE (and you do not
+ * set a #NmtDeviceEntryDeviceFilter), then this will only allow
+ * specifying an interface name, not a hardware address.
+ *
+ * Returns: a new #NmtDeviceEntry.
+ */
+NmtNewtWidget *
+nmt_device_entry_new (const char *label,
+ int width,
+ GType hardware_type)
+{
+ return g_object_new (NMT_TYPE_DEVICE_ENTRY,
+ "label", label,
+ "width", width,
+ "hardware-type", hardware_type,
+ NULL);
+}
+
+static gboolean
+device_entry_parse (NmtDeviceEntry *deventry,
+ const char *text,
+ char **interface_name,
+ char **mac_address)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+ guint8 buf[NM_UTILS_HWADDR_LEN_MAX];
+ char **words;
+ int len;
+
+ *interface_name = *mac_address = NULL;
+ if (!*text)
+ return TRUE;
+
+ if (priv->hardware_type == G_TYPE_NONE && !priv->device_filter) {
+ if (nm_utils_iface_valid_name (text)) {
+ *interface_name = g_strdup (text);
+ return TRUE;
+ } else
+ return FALSE;
+ }
+
+ words = g_strsplit (text, " ", -1);
+ if (g_strv_length (words) > 2) {
+ g_strfreev (words);
+ return FALSE;
+ }
+
+ if (words[1]) {
+ len = strlen (words[1]);
+ if (len < 3 || words[1][0] != '(' || words[1][len - 1] != ')')
+ goto fail;
+
+ memmove (words[1], words[1] + 1, len - 2);
+ words[1][len - 2] = '\0';
+ }
+
+ if ( nm_utils_hwaddr_aton (words[0], priv->arptype, buf)
+ && (!words[1] || nm_utils_iface_valid_name (words[1]))) {
+ *mac_address = words[0];
+ *interface_name = NULL;
+ g_free (words);
+ return TRUE;
+ } else if ( nm_utils_iface_valid_name (words[0])
+ && (!words[1] || nm_utils_hwaddr_aton (words[1], priv->arptype, buf))) {
+ *interface_name = words[0];
+ *mac_address = NULL;
+ g_free (words);
+ return TRUE;
+ }
+
+ fail:
+ g_strfreev (words);
+ return FALSE;
+}
+
+static gboolean
+device_entry_validate (NmtNewtEntry *entry,
+ const char *text,
+ gpointer user_data)
+{
+ NmtDeviceEntry *deventry = user_data;
+ char *ifname, *mac;
+
+ if (!device_entry_parse (deventry, text, &ifname, &mac))
+ return FALSE;
+
+ g_free (ifname);
+ g_free (mac);
+ return TRUE;
+}
+
+static NMDevice *
+find_device_by_interface_name (NmtDeviceEntry *deventry,
+ const char *interface_name)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+ const GPtrArray *devices;
+ NMDevice *device = NULL;
+ int i;
+
+ devices = nm_client_get_devices (nm_client);
+ if (!devices)
+ return NULL;
+
+ for (i = 0; i < devices->len && !device; i++) {
+ NMDevice *candidate = devices->pdata[i];
+
+ if ( priv->hardware_type != G_TYPE_NONE
+ && !G_TYPE_CHECK_INSTANCE_TYPE (candidate, priv->hardware_type))
+ continue;
+
+ if ( priv->device_filter
+ && !priv->device_filter (deventry, candidate, priv->device_filter_data))
+ continue;
+
+ if (!g_strcmp0 (interface_name, nm_device_get_iface (candidate)))
+ device = candidate;
+ }
+
+ return device;
+}
+
+static NMDevice *
+find_device_by_mac_address (NmtDeviceEntry *deventry,
+ const char *mac_address)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+ const GPtrArray *devices;
+ NMDevice *device = NULL;
+ int i;
+
+ devices = nm_client_get_devices (nm_client);
+ if (!devices)
+ return NULL;
+
+ for (i = 0; i < devices->len && !device; i++) {
+ NMDevice *candidate = devices->pdata[i];
+ char *hwaddr;
+
+ if ( priv->hardware_type != G_TYPE_NONE
+ && !G_TYPE_CHECK_INSTANCE_TYPE (candidate, priv->hardware_type))
+ continue;
+
+ if ( priv->device_filter
+ && !priv->device_filter (deventry, candidate, priv->device_filter_data))
+ continue;
+
+ g_object_get (G_OBJECT (candidate), "hw-address", &hwaddr, NULL);
+ if (hwaddr && !g_ascii_strcasecmp (mac_address, hwaddr))
+ device = candidate;
+ g_free (hwaddr);
+ }
+
+ return device;
+}
+
+static void
+update_entry (NmtDeviceEntry *deventry)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+ const char *ifname;
+ char *mac, *text;
+ NMDevice *ifname_device, *mac_device;
+
+ if (priv->interface_name) {
+ ifname = priv->interface_name;
+ ifname_device = find_device_by_interface_name (deventry, priv->interface_name);
+ } else {
+ ifname = NULL;
+ ifname_device = NULL;
+ }
+
+ if (priv->mac_address) {
+ mac = nm_utils_hwaddr_ntoa (priv->mac_address->data, priv->arptype);
+ mac_device = find_device_by_mac_address (deventry, mac);
+ } else {
+ mac = NULL;
+ mac_device = NULL;
+ }
+
+ if (!ifname && mac_device)
+ ifname = nm_device_get_iface (mac_device);
+ if (!mac && ifname_device && (priv->hardware_type != G_TYPE_NONE))
+ g_object_get (G_OBJECT (ifname_device), "hw-address", &mac, NULL);
+
+ if (ifname_device && mac_device && ifname_device != mac_device) {
+ /* Mismatch! */
+ text = g_strdup_printf ("%s != %s", priv->interface_name, mac);
+ } else if (ifname && mac) {
+ if (ifname_device)
+ text = g_strdup_printf ("%s (%s)", ifname, mac);
+ else
+ text = g_strdup_printf ("%s (%s)", mac, ifname);
+ } else if (ifname)
+ text = g_strdup (ifname);
+ else if (mac)
+ text = g_strdup (mac);
+ else
+ text = g_strdup ("");
+
+ priv->updating = TRUE;
+ g_object_set (G_OBJECT (priv->entry), "text", text, NULL);
+ priv->updating = FALSE;
+ g_free (text);
+
+ g_free (mac);
+}
+
+static gboolean
+nmt_device_entry_set_interface_name (NmtDeviceEntry *deventry,
+ const char *interface_name)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+
+ if (g_strcmp0 (interface_name, priv->interface_name) != 0) {
+ g_free (priv->interface_name);
+ priv->interface_name = g_strdup (interface_name);
+
+ g_object_notify (G_OBJECT (deventry), "interface-name");
+ return TRUE;
+ } else
+ return FALSE;
+}
+
+static gboolean
+nmt_device_entry_set_mac_address (NmtDeviceEntry *deventry,
+ GByteArray *mac_address)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+ gboolean changed;
+
+ if (mac_address)
+ g_return_val_if_fail (mac_address->len == nm_utils_hwaddr_len (priv->arptype), FALSE);
+
+ if (mac_address && !priv->mac_address) {
+ priv->mac_address = g_boxed_copy (DBUS_TYPE_G_UCHAR_ARRAY, mac_address);
+ changed = TRUE;
+ } else if (!mac_address && priv->mac_address) {
+ g_clear_pointer (&priv->mac_address, g_byte_array_unref);
+ changed = TRUE;
+ } else if ( mac_address && priv->mac_address
+ && memcmp (mac_address->data, priv->mac_address->data, mac_address->len) != 0) {
+ g_byte_array_unref (priv->mac_address);
+ priv->mac_address = g_boxed_copy (DBUS_TYPE_G_UCHAR_ARRAY, mac_address);
+ changed = TRUE;
+ } else
+ changed = FALSE;
+
+ if (changed)
+ g_object_notify (G_OBJECT (deventry), "mac-address");
+ return changed;
+}
+
+static void
+entry_text_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer deventry)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+ const char *text;
+ char *ifname, *mac;
+
+ if (priv->updating)
+ return;
+
+ text = nmt_newt_entry_get_text (priv->entry);
+ if (!device_entry_parse (deventry, text, &ifname, &mac))
+ return;
+
+ if (ifname) {
+ nmt_device_entry_set_interface_name (deventry, ifname);
+ g_free (ifname);
+ } else
+ nmt_device_entry_set_interface_name (deventry, NULL);
+
+ if (mac) {
+ GByteArray *mac_address;
+
+ mac_address = nm_utils_hwaddr_atoba (mac, priv->arptype);
+ nmt_device_entry_set_mac_address (deventry, mac_address);
+ g_byte_array_unref (mac_address);
+ g_free (mac);
+ } else
+ nmt_device_entry_set_mac_address (deventry, NULL);
+}
+
+static void
+nmt_device_entry_init (NmtDeviceEntry *deventry)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+ NmtNewtWidget *entry;
+
+ priv->hardware_type = G_TYPE_NONE;
+
+ entry = nmt_newt_entry_new (-1, 0);
+ priv->entry = NMT_NEWT_ENTRY (entry);
+ nmt_newt_entry_set_validator (priv->entry, device_entry_validate, deventry);
+ g_signal_connect (priv->entry, "notify::text",
+ G_CALLBACK (entry_text_changed), deventry);
+
+#if 0
+ priv->button = nmt_newt_button_new (_("Select..."));
+ g_signal_connect (priv->button, "clicked",
+ G_CALLBACK (do_select_dialog), deventry);
+#endif
+}
+
+static void
+nmt_device_entry_constructed (GObject *object)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object);
+
+ nmt_page_grid_append (NMT_PAGE_GRID (object), priv->label, NMT_NEWT_WIDGET (priv->entry), NULL);
+
+ G_OBJECT_CLASS (nmt_device_entry_parent_class)->constructed (object);
+}
+
+static void
+nmt_device_entry_finalize (GObject *object)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object);
+
+ g_free (priv->interface_name);
+ if (priv->mac_address)
+ g_byte_array_unref (priv->mac_address);
+
+ G_OBJECT_CLASS (nmt_device_entry_parent_class)->finalize (object);
+}
+
+/**
+ * NmtDeviceEntryDeviceFilter:
+ * @deventry: the #NmtDeviceEntry
+ * @device: an #NMDevice
+ * @user_data: user data
+ *
+ * Filter function for determining which devices can be specified
+ * on an entry.
+ *
+ * Returns: %TRUE if @device is acceptable for @deventry
+ */
+
+/**
+ * nmt_device_entry_set_device_filter:
+ * @deventry: the #NmtDeviceEntry
+ * @filter: the filter
+ * @user_data: data for @filter
+ *
+ * Sets a device filter on @deventry. Only devices that pass @filter
+ * will be recognized by @deventry.
+ *
+ * If the entry's #NmtDeviceEntry:hardware-type is not %G_TYPE_NONE,
+ * then only devices that both match the hardware type and are
+ * accepted by the filter will be allowed.
+ */
+void
+nmt_device_entry_set_device_filter (NmtDeviceEntry *deventry,
+ NmtDeviceEntryDeviceFilter filter,
+ gpointer user_data)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+
+ priv->device_filter = filter;
+ priv->device_filter_data = user_data;
+}
+
+static void
+nmt_device_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtDeviceEntry *deventry = NMT_DEVICE_ENTRY (object);
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+ const char *interface_name;
+ GByteArray *mac_address;
+
+ switch (prop_id) {
+ case PROP_LABEL:
+ priv->label = g_value_dup_string (value);
+ break;
+ case PROP_WIDTH:
+ nmt_newt_entry_set_width (priv->entry, g_value_get_int (value));
+ break;
+ case PROP_HARDWARE_TYPE:
+ priv->hardware_type = g_value_get_gtype (value);
+ priv->arptype = (priv->hardware_type == NM_TYPE_DEVICE_INFINIBAND) ? ARPHRD_INFINIBAND : ARPHRD_ETHER;
+ break;
+ case PROP_INTERFACE_NAME:
+ interface_name = g_value_get_string (value);
+ if (nmt_device_entry_set_interface_name (deventry, interface_name))
+ update_entry (deventry);
+ break;
+ case PROP_MAC_ADDRESS:
+ mac_address = g_value_get_boxed (value);
+ if (nmt_device_entry_set_mac_address (deventry, mac_address))
+ update_entry (deventry);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_device_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_LABEL:
+ g_value_set_string (value, priv->label);
+ break;
+ case PROP_WIDTH:
+ g_value_set_int (value, nmt_newt_entry_get_width (priv->entry));
+ break;
+ case PROP_HARDWARE_TYPE:
+ g_value_set_gtype (value, priv->hardware_type);
+ break;
+ case PROP_INTERFACE_NAME:
+ g_value_set_string (value, priv->interface_name);
+ break;
+ case PROP_MAC_ADDRESS:
+ g_value_set_boxed (value, priv->mac_address);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_device_entry_class_init (NmtDeviceEntryClass *deventry_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (deventry_class);
+
+ g_type_class_add_private (deventry_class, sizeof (NmtDeviceEntryPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_device_entry_constructed;
+ object_class->set_property = nmt_device_entry_set_property;
+ object_class->get_property = nmt_device_entry_get_property;
+ object_class->finalize = nmt_device_entry_finalize;
+
+ /**
+ * NmtDeviceEntry:label:
+ *
+ * The entry's label
+ */
+ g_object_class_install_property
+ (object_class, PROP_LABEL,
+ g_param_spec_string ("label", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtDeviceEntry:width:
+ *
+ * The entry's width in characters
+ */
+ g_object_class_install_property
+ (object_class, PROP_WIDTH,
+ g_param_spec_int ("width", "", "",
+ -1, 80, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtDeviceEntry:hardware-type:
+ *
+ * The type of #NMDevice to limit the entry to, or %G_TYPE_NONE
+ * if the entry is for a virtual device and should not accept
+ * hardware addresses.
+ */
+ g_object_class_install_property
+ (object_class, PROP_HARDWARE_TYPE,
+ g_param_spec_gtype ("hardware-type", "", "",
+ G_TYPE_NONE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtDeviceEntry:interface-name:
+ *
+ * The interface name of the device identified by the entry.
+ */
+ g_object_class_install_property
+ (object_class, PROP_INTERFACE_NAME,
+ g_param_spec_string ("interface-name", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtDeviceEntry:mac-address:
+ *
+ * The hardware address of the device identified by the entry.
+ */
+ g_object_class_install_property
+ (object_class, PROP_MAC_ADDRESS,
+ g_param_spec_boxed ("mac-address", "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-device-entry.h b/clients/tui/nmt-device-entry.h
new file mode 100644
index 0000000000..2b959aaa85
--- /dev/null
+++ b/clients/tui/nmt-device-entry.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_DEVICE_ENTRY_H
+#define NMT_DEVICE_ENTRY_H
+
+#include "nmt-page-grid.h"
+
+#include <nm-connection.h>
+#include <nm-device.h>
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_DEVICE_ENTRY (nmt_device_entry_get_type ())
+#define NMT_DEVICE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntry))
+#define NMT_DEVICE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryClass))
+#define NMT_IS_DEVICE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_DEVICE_ENTRY))
+#define NMT_IS_DEVICE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_DEVICE_ENTRY))
+#define NMT_DEVICE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryClass))
+
+typedef struct {
+ NmtPageGrid parent;
+
+} NmtDeviceEntry;
+
+typedef struct {
+ NmtPageGridClass parent;
+
+} NmtDeviceEntryClass;
+
+GType nmt_device_entry_get_type (void);
+
+NmtNewtWidget *nmt_device_entry_new (const char *label,
+ int width,
+ GType hardware_type);
+
+typedef gboolean (*NmtDeviceEntryDeviceFilter) (NmtDeviceEntry *deventry,
+ NMDevice *device,
+ gpointer user_data);
+void nmt_device_entry_set_device_filter (NmtDeviceEntry *deventry,
+ NmtDeviceEntryDeviceFilter filter,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* NMT_DEVICE_ENTRY_H */
diff --git a/clients/tui/nmt-edit-connection-list.c b/clients/tui/nmt-edit-connection-list.c
new file mode 100644
index 0000000000..2c2c084646
--- /dev/null
+++ b/clients/tui/nmt-edit-connection-list.c
@@ -0,0 +1,574 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-edit-connection-list
+ * @short_description: Connection list for "nmtui edit"
+ *
+ * #NmtEditConnectionList is the list of connections displayed by
+ * "nmtui edit".
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "nmtui.h"
+#include "nmtui-edit.h"
+#include "nmt-edit-connection-list.h"
+#include "nmt-editor.h"
+
+#include "nm-editor-utils.h"
+
+G_DEFINE_TYPE (NmtEditConnectionList, nmt_edit_connection_list, NMT_TYPE_NEWT_GRID)
+
+#define NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListPrivate))
+
+typedef struct {
+ GSList *connections;
+
+ gboolean grouped;
+ NmtEditConnectionListFilter connection_filter;
+ gpointer connection_filter_data;
+
+ NmtNewtListbox *listbox;
+ NmtNewtButtonBox *buttons;
+
+ NmtNewtWidget *add;
+ NmtNewtWidget *edit;
+ NmtNewtWidget *delete;
+ NmtNewtWidget *extra;
+} NmtEditConnectionListPrivate;
+
+enum {
+ PROP_0,
+
+ PROP_GROUPED,
+ PROP_CONNECTION_FILTER,
+ PROP_CONNECTION_FILTER_DATA,
+ PROP_EXTRA_WIDGET,
+ PROP_CONNECTIONS,
+ PROP_NUM_CONNECTIONS,
+
+ LAST_PROP
+};
+
+enum {
+ ADD_CONNECTION,
+ EDIT_CONNECTION,
+ REMOVE_CONNECTION,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void add_clicked (NmtNewtButton *button, gpointer list);
+static void edit_clicked (NmtNewtButton *button, gpointer list);
+static void delete_clicked (NmtNewtButton *button, gpointer list);
+static void listbox_activated (NmtNewtWidget *listbox, gpointer list);
+
+/**
+ * nmt_edit_connection_list_get_connections:
+ * @list: an #NmtEditConnectionList
+ *
+ * Gets the list's list of connections
+ *
+ * Returns: (transfer none) (element-type #NMConnection): the
+ * list of connections.
+ */
+GSList *
+nmt_edit_connection_list_get_connections (NmtEditConnectionList *list)
+{
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+
+ return priv->connections;
+}
+
+static void
+nmt_edit_connection_list_init (NmtEditConnectionList *list)
+{
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+ NmtNewtWidget *listbox, *buttons;
+ NmtNewtGrid *grid = NMT_NEWT_GRID (list);
+
+ listbox = g_object_new (NMT_TYPE_NEWT_LISTBOX,
+ "flags", NMT_NEWT_LISTBOX_SCROLL | NMT_NEWT_LISTBOX_BORDER,
+ "skip-null-keys", TRUE,
+ NULL);
+ priv->listbox = NMT_NEWT_LISTBOX (listbox);
+ nmt_newt_grid_add (grid, listbox, 0, 0);
+ nmt_newt_grid_set_flags (grid, listbox,
+ NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y |
+ NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_EXPAND_Y);
+ g_signal_connect (priv->listbox, "activated", G_CALLBACK (listbox_activated), list);
+
+ buttons = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_VERTICAL);
+ priv->buttons = NMT_NEWT_BUTTON_BOX (buttons);
+ nmt_newt_grid_add (grid, buttons, 1, 0);
+ nmt_newt_widget_set_padding (buttons, 1, 1, 0, 1);
+ nmt_newt_grid_set_flags (grid, buttons,
+ NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y |
+ NMT_NEWT_GRID_EXPAND_Y);
+
+ priv->add = nmt_newt_button_box_add_start (priv->buttons, _("Add"));
+ g_signal_connect (priv->add, "clicked", G_CALLBACK (add_clicked), list);
+
+ priv->edit = nmt_newt_button_box_add_start (priv->buttons, _("Edit..."));
+ g_signal_connect (priv->edit, "clicked", G_CALLBACK (edit_clicked), list);
+
+ priv->delete = nmt_newt_button_box_add_start (priv->buttons, _("Delete"));
+ g_signal_connect (priv->delete, "clicked", G_CALLBACK (delete_clicked), list);
+}
+
+static int
+sort_by_timestamp (gconstpointer a,
+ gconstpointer b)
+{
+ NMSettingConnection *s_con_a, *s_con_b;
+ guint64 time_a, time_b;
+
+ s_con_a = nm_connection_get_setting_connection ((NMConnection *) a);
+ s_con_b = nm_connection_get_setting_connection ((NMConnection *) b);
+
+ time_a = nm_setting_connection_get_timestamp (s_con_a);
+ time_b = nm_setting_connection_get_timestamp (s_con_b);
+
+ return (int) (time_b - time_a);
+}
+
+static void nmt_edit_connection_list_rebuild (NmtEditConnectionList *list);
+
+static void
+rebuild_on_connection_updated (NMRemoteConnection *connection,
+ gpointer list)
+{
+ nmt_edit_connection_list_rebuild (list);
+}
+
+static void
+free_connections (NmtEditConnectionList *list)
+{
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+ NMConnection *conn;
+ GSList *iter;
+
+ for (iter = priv->connections; iter; iter = iter->next) {
+ conn = iter->data;
+
+ g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (rebuild_on_connection_updated), list);
+ g_object_unref (conn);
+ }
+ g_slist_free (priv->connections);
+}
+
+static void
+nmt_edit_connection_list_rebuild (NmtEditConnectionList *list)
+{
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+ GSList *iter, *next;
+ gboolean did_header = FALSE, did_vpn = FALSE;
+ NMEditorConnectionTypeData **types;
+ NMConnection *conn, *selected_conn;
+ int i, row, selected_row;
+
+ selected_row = nmt_newt_listbox_get_active (priv->listbox);
+ selected_conn = nmt_newt_listbox_get_active_key (priv->listbox);
+
+ free_connections (list);
+ priv->connections = nm_remote_settings_list_connections (nm_settings);
+ for (iter = priv->connections; iter; iter = next) {
+ conn = iter->data;
+ next = iter->next;
+
+ if ( priv->connection_filter
+ && !priv->connection_filter (list, conn, priv->connection_filter_data)) {
+ priv->connections = g_slist_delete_link (priv->connections, iter);
+ continue;
+ }
+
+ g_signal_connect (conn, NM_REMOTE_CONNECTION_UPDATED,
+ G_CALLBACK (rebuild_on_connection_updated), list);
+ g_signal_connect (conn, NM_REMOTE_CONNECTION_REMOVED,
+ G_CALLBACK (rebuild_on_connection_updated), list);
+ g_object_ref (iter->data);
+ }
+ priv->connections = g_slist_sort (priv->connections, sort_by_timestamp);
+ g_object_notify (G_OBJECT (list), "connections");
+ g_object_notify (G_OBJECT (list), "num-connections");
+
+ nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->edit),
+ priv->connections != NULL);
+ nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->delete),
+ priv->connections != NULL);
+
+ nmt_newt_listbox_clear (priv->listbox);
+
+ if (!priv->grouped) {
+ /* Just add the connections in order */
+ for (iter = priv->connections, row = 0; iter; iter = iter->next, row++) {
+ conn = iter->data;
+ nmt_newt_listbox_append (priv->listbox, nm_connection_get_id (conn), conn);
+ if (conn == selected_conn)
+ selected_row = row;
+ }
+ if (selected_row >= row)
+ selected_row = row - 1;
+ nmt_newt_listbox_set_active (priv->listbox, selected_row);
+
+ return;
+ }
+
+ types = nm_editor_utils_get_connection_type_list ();
+ for (i = row = 0; types[i]; i++) {
+ if (types[i]->setting_type == NM_TYPE_SETTING_VPN) {
+ if (did_vpn)
+ continue;
+ did_vpn = TRUE;
+ }
+
+ did_header = FALSE;
+
+ for (iter = priv->connections; iter; iter = iter->next) {
+ NMSetting *setting;
+ char *indented;
+
+ conn = iter->data;
+ setting = nm_connection_get_setting (conn, types[i]->setting_type);
+ if (!setting)
+ continue;
+ if (!nm_connection_is_type (conn, nm_setting_get_name (setting)))
+ continue;
+
+ if (!did_header) {
+ nmt_newt_listbox_append (priv->listbox, types[i]->name, NULL);
+ if (row == selected_row)
+ selected_row++;
+ row++;
+ did_header = TRUE;
+ }
+
+ indented = g_strdup_printf (" %s", nm_connection_get_id (conn));
+ nmt_newt_listbox_append (priv->listbox, indented, conn);
+ g_free (indented);
+
+ if (conn == selected_conn)
+ selected_row = row;
+ row++;
+ }
+ }
+
+ if (selected_row >= row)
+ selected_row = row - 1;
+ nmt_newt_listbox_set_active (priv->listbox, selected_row);
+}
+
+static void
+rebuild_on_new_connection (NMRemoteSettings *settings,
+ NMRemoteConnection *connection,
+ gpointer list)
+{
+ nmt_edit_connection_list_rebuild (list);
+}
+
+static void
+nmt_edit_connection_list_constructed (GObject *object)
+{
+ NmtEditConnectionList *list = NMT_EDIT_CONNECTION_LIST (object);
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+
+ if (priv->extra)
+ nmt_newt_button_box_add_widget_end (priv->buttons, priv->extra);
+
+ g_signal_connect (nm_settings, NM_REMOTE_SETTINGS_NEW_CONNECTION,
+ G_CALLBACK (rebuild_on_new_connection), list);
+
+ nmt_edit_connection_list_rebuild (list);
+
+ G_OBJECT_CLASS (nmt_edit_connection_list_parent_class)->constructed (object);
+}
+
+static void
+add_clicked (NmtNewtButton *button, gpointer list)
+{
+ g_signal_emit (list, signals[ADD_CONNECTION], 0);
+}
+
+static void
+edit_clicked (NmtNewtButton *button, gpointer list)
+{
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+ NMConnection *connection;
+
+ connection = nmt_newt_listbox_get_active_key (priv->listbox);
+ g_return_if_fail (connection != NULL);
+
+ g_signal_emit (list, signals[EDIT_CONNECTION], 0, connection);
+}
+
+static void
+delete_clicked (NmtNewtButton *button, gpointer list)
+{
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+ NMRemoteConnection *connection;
+
+ connection = nmt_newt_listbox_get_active_key (priv->listbox);
+ g_return_if_fail (connection != NULL);
+
+ g_signal_emit (list, signals[REMOVE_CONNECTION], 0, connection);
+}
+
+static void
+listbox_activated (NmtNewtWidget *listbox, gpointer list)
+{
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+
+ edit_clicked (NMT_NEWT_BUTTON (priv->edit), list);
+}
+
+static void
+nmt_edit_connection_list_finalize (GObject *object)
+{
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object);
+
+ free_connections (NMT_EDIT_CONNECTION_LIST (object));
+ g_clear_object (&priv->extra);
+
+ G_OBJECT_CLASS (nmt_edit_connection_list_parent_class)->finalize (object);
+}
+
+static void
+nmt_edit_connection_list_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_GROUPED:
+ priv->grouped = g_value_get_boolean (value);
+ break;
+ case PROP_CONNECTION_FILTER:
+ priv->connection_filter = g_value_get_pointer (value);
+ break;
+ case PROP_CONNECTION_FILTER_DATA:
+ priv->connection_filter_data = g_value_get_pointer (value);
+ break;
+ case PROP_EXTRA_WIDGET:
+ priv->extra = g_value_get_object (value);
+ if (priv->extra)
+ g_object_ref_sink (priv->extra);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_edit_connection_list_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object);
+ GPtrArray *connections;
+ GSList *iter;
+
+ switch (prop_id) {
+ case PROP_GROUPED:
+ g_value_set_boolean (value, priv->grouped);
+ break;
+ case PROP_CONNECTION_FILTER:
+ g_value_set_pointer (value, priv->connection_filter);
+ break;
+ case PROP_CONNECTION_FILTER_DATA:
+ g_value_set_pointer (value, priv->connection_filter_data);
+ break;
+ case PROP_EXTRA_WIDGET:
+ g_value_set_object (value, priv->extra);
+ break;
+ case PROP_CONNECTIONS:
+ connections = g_ptr_array_new_with_free_func (g_object_unref);
+ for (iter = priv->connections; iter; iter = iter->next)
+ g_ptr_array_add (connections, g_object_ref (iter->data));
+ g_value_take_boxed (value, connections);
+ break;
+ case PROP_NUM_CONNECTIONS:
+ g_value_set_int (value, g_slist_length (priv->connections));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_edit_connection_list_class_init (NmtEditConnectionListClass *list_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+
+ g_type_class_add_private (list_class, sizeof (NmtEditConnectionListPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_edit_connection_list_constructed;
+ object_class->set_property = nmt_edit_connection_list_set_property;
+ object_class->get_property = nmt_edit_connection_list_get_property;
+ object_class->finalize = nmt_edit_connection_list_finalize;
+
+ /* signals */
+
+ /**
+ * NmtEditConnectionList::add-connection:
+ * @list: the #NmtEditConnectionList
+ *
+ * Emitted when the user clicks the list's "Add" button.
+ */
+ signals[ADD_CONNECTION] =
+ g_signal_new ("add-connection",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NmtEditConnectionListClass, add_connection),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
+ /**
+ * NmtEditConnectionList::edit-connection:
+ * @list: the #NmtEditConnectionList
+ * @connection: the connection to edit
+ *
+ * Emitted when the user clicks the list's "Edit" button, or
+ * hits "Return" on the listbox.
+ */
+ signals[EDIT_CONNECTION] =
+ g_signal_new ("edit-connection",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NmtEditConnectionListClass, edit_connection),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ NM_TYPE_CONNECTION);
+
+ /**
+ * NmtEditConnectionList::remove-connection:
+ * @list: the #NmtEditConnectionList
+ * @connection: the connection to remove
+ *
+ * Emitted when the user clicks the list's "Delete" button.
+ */
+ signals[REMOVE_CONNECTION] =
+ g_signal_new ("remove-connection",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NmtEditConnectionListClass, remove_connection),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1,
+ NM_TYPE_CONNECTION);
+
+ /* properties */
+
+ /**
+ * NmtEditConnectionList:grouped:
+ *
+ * If %TRUE, connections should be grouped by type, with headers
+ * indicating the types (as in the main connection list). If %FALSE,
+ * they will not be grouped (as in slave connection lists).
+ */
+ g_object_class_install_property
+ (object_class, PROP_GROUPED,
+ g_param_spec_boolean ("grouped", "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NmtEditConnectionListFilter:
+ * @list: the #NmtEditConnectionList
+ * @connection: an #NMConnection
+ * @user_data: the user data
+ *
+ * Decides whether @connection should be displayed in @list.
+ *
+ * Returns: %TRUE or %FALSE
+ */
+ /**
+ * NmtEditConnectionList:connection-filter:
+ *
+ * A callback function for filtering which connections appear in
+ * the list.
+ */
+ g_object_class_install_property
+ (object_class, PROP_CONNECTION_FILTER,
+ g_param_spec_pointer ("connection-filter", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtEditConnectionList:connection-filter-data:
+ *
+ * Data for the #NmtEditConnectionList:connection-filter.
+ */
+ g_object_class_install_property
+ (object_class, PROP_CONNECTION_FILTER_DATA,
+ g_param_spec_pointer ("connection-filter-data", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NmtEditConnectionList:extra-widget:
+ *
+ * An extra button widget to display at the bottom of the button
+ * box.
+ */
+ g_object_class_install_property
+ (object_class, PROP_EXTRA_WIDGET,
+ g_param_spec_object ("extra-widget", "", "",
+ NMT_TYPE_NEWT_WIDGET,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NmtEditConnectionList:connections:
+ *
+ * The list of connections in the widget.
+ *
+ * Element-Type: #NMConnection
+ */
+ g_object_class_install_property
+ (object_class, PROP_CONNECTIONS,
+ g_param_spec_boxed ("connections", "", "",
+ G_TYPE_PTR_ARRAY,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * NmtEditConnectionList:num-connections:
+ *
+ * The number of connections in the widget.
+ */
+ g_object_class_install_property
+ (object_class, PROP_NUM_CONNECTIONS,
+ g_param_spec_int ("num-connections", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-edit-connection-list.h b/clients/tui/nmt-edit-connection-list.h
new file mode 100644
index 0000000000..35157e94dc
--- /dev/null
+++ b/clients/tui/nmt-edit-connection-list.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_EDIT_CONNECTION_LIST_H
+#define NMT_EDIT_CONNECTION_LIST_H
+
+#include "nmt-newt.h"
+
+#include <nm-remote-connection.h>
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_EDIT_CONNECTION_LIST (nmt_edit_connection_list_get_type ())
+#define NMT_EDIT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionList))
+#define NMT_EDIT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListClass))
+#define NMT_IS_EDIT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDIT_CONNECTION_LIST))
+#define NMT_IS_EDIT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDIT_CONNECTION_LIST))
+#define NMT_EDIT_CONNECTION_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListClass))
+
+typedef struct {
+ NmtNewtGrid parent;
+
+} NmtEditConnectionList;
+
+typedef struct {
+ NmtNewtGridClass parent;
+
+ /* signals */
+ void (*add_connection) (NmtEditConnectionList *list);
+ void (*edit_connection) (NmtEditConnectionList *list,
+ NMConnection *connection);
+ void (*remove_connection) (NmtEditConnectionList *list,
+ NMRemoteConnection *connection);
+} NmtEditConnectionListClass;
+
+GType nmt_edit_connection_list_get_type (void);
+
+typedef gboolean (*NmtEditConnectionListFilter) (NmtEditConnectionList *list,
+ NMConnection *connection,
+ gpointer user_data);
+
+GSList *nmt_edit_connection_list_get_connections (NmtEditConnectionList *list);
+
+G_END_DECLS
+
+#endif /* NMT_EDIT_CONNECTION_LIST_H */
diff --git a/clients/tui/nmt-editor-page.c b/clients/tui/nmt-editor-page.c
new file mode 100644
index 0000000000..80fb163bb4
--- /dev/null
+++ b/clients/tui/nmt-editor-page.c
@@ -0,0 +1,229 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-editor-page:
+ * @short_description: An #NmtEditor "page"
+ *
+ * #NmtEditorPage is the abstract base class for #NmtEditor "pages".
+ * Note that despite the name, currently all "page" types except
+ * #NmtPageMain are actually displayed as collapsible sections, not
+ * separate tabs/forms.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-editor-page.h"
+
+G_DEFINE_ABSTRACT_TYPE (NmtEditorPage, nmt_editor_page, NMT_TYPE_PAGE_GRID)
+
+#define NMT_EDITOR_PAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR_PAGE, NmtEditorPagePrivate))
+
+typedef struct {
+ char *title;
+ NmtNewtWidget *header_widget;
+ NMConnection *connection;
+
+} NmtEditorPagePrivate;
+
+enum {
+ PROP_0,
+
+ PROP_CONNECTION,
+ PROP_TITLE,
+
+ LAST_PROP
+};
+
+static void
+nmt_editor_page_init (NmtEditorPage *page)
+{
+ NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page);
+
+ priv->header_widget = g_object_ref_sink (nmt_newt_separator_new ());
+}
+
+static void
+nmt_editor_page_finalize (GObject *object)
+{
+ NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (object);
+
+ g_free (priv->title);
+ g_clear_object (&priv->header_widget);
+ g_clear_object (&priv->connection);
+
+ G_OBJECT_CLASS (nmt_editor_page_parent_class)->finalize (object);
+}
+
+/**
+ * nmt_editor_page_get_connection:
+ * @page: the #NmtEditorPage
+ *
+ * Gets the page's #NMConnection.
+ *
+ * Returns: (transfer none): the page's #NMConnection.
+ */
+NMConnection *
+nmt_editor_page_get_connection (NmtEditorPage *page)
+{
+ NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page);
+
+ return priv->connection;
+}
+
+/**
+ * nmt_editor_page_set_header_widget:
+ * @page: the #NmtEditorPage
+ * @widget: an #NmtNewtWidget
+ *
+ * Sets the page's header widget. When displayed as a subpage of
+ * #NmtPageMain, this widget will be put into the corresponding
+ * #NmtNewtSection's header.
+ *
+ * FIXME: for consistency, this should be a property as well.
+ */
+void
+nmt_editor_page_set_header_widget (NmtEditorPage *page,
+ NmtNewtWidget *widget)
+{
+ NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page);
+
+ g_clear_object (&priv->header_widget);
+
+ if (!widget)
+ widget = nmt_newt_separator_new ();
+ priv->header_widget = g_object_ref_sink (widget);
+}
+
+/**
+ * nmt_editor_page_get_header_widget:
+ * @page: the #NmtEditorPage
+ *
+ * Gets the page's header widget. When displayed as a subpage of
+ * #NmtPageMain, this widget will be put into the corresponding
+ * #NmtNewtSection's header.
+ *
+ * Returns: (transfer none): the page's header widget.
+ */
+NmtNewtWidget *
+nmt_editor_page_get_header_widget (NmtEditorPage *page)
+{
+ NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page);
+
+ return priv->header_widget;
+}
+
+/**
+ * nmt_editor_page_get_title:
+ * @page: the #NmtEditorPage
+ *
+ * Gets the page's title.
+ *
+ * Returns: the page's title
+ */
+const char *
+nmt_editor_page_get_title (NmtEditorPage *page)
+{
+ NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page);
+
+ return priv->title;
+}
+
+static void
+nmt_editor_page_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_CONNECTION:
+ priv->connection = g_value_dup_object (value);
+ break;
+ case PROP_TITLE:
+ priv->title = g_value_dup_string (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_editor_page_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->connection);
+ break;
+ case PROP_TITLE:
+ g_value_set_string (value, priv->title);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_editor_page_class_init (NmtEditorPageClass *page_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (page_class);
+
+ g_type_class_add_private (page_class, sizeof (NmtEditorPagePrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_editor_page_set_property;
+ object_class->get_property = nmt_editor_page_get_property;
+ object_class->finalize = nmt_editor_page_finalize;
+
+ /* properties */
+
+ /**
+ * NmtEditorPage:connection:
+ *
+ * The page's #NMConnection.
+ */
+ g_object_class_install_property
+ (object_class, PROP_CONNECTION,
+ g_param_spec_object ("connection", "", "",
+ NM_TYPE_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtEditorPage:title:
+ *
+ * The page's title.
+ */
+ g_object_class_install_property
+ (object_class, PROP_TITLE,
+ g_param_spec_string ("title", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-editor-page.h b/clients/tui/nmt-editor-page.h
new file mode 100644
index 0000000000..d9a97102ee
--- /dev/null
+++ b/clients/tui/nmt-editor-page.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_EDITOR_PAGE_H
+#define NMT_EDITOR_PAGE_H
+
+#include <nm-connection.h>
+
+#include "nmt-page-grid.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_EDITOR_PAGE (nmt_editor_page_get_type ())
+#define NMT_EDITOR_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_PAGE, NmtEditorPage))
+#define NMT_EDITOR_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_PAGE, NmtEditorPageClass))
+#define NMT_IS_EDITOR_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR_PAGE))
+#define NMT_IS_EDITOR_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR_PAGE))
+#define NMT_EDITOR_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR_PAGE, NmtEditorPageClass))
+
+typedef struct {
+ NmtPageGrid parent;
+
+} NmtEditorPage;
+
+typedef struct {
+ NmtPageGridClass parent;
+
+} NmtEditorPageClass;
+
+GType nmt_editor_page_get_type (void);
+
+NMConnection *nmt_editor_page_get_connection (NmtEditorPage *page);
+
+void nmt_editor_page_set_header_widget (NmtEditorPage *page,
+ NmtNewtWidget *widget);
+NmtNewtWidget *nmt_editor_page_get_header_widget (NmtEditorPage *page);
+
+const char *nmt_editor_page_get_title (NmtEditorPage *page);
+
+G_END_DECLS
+
+#endif /* NMT_EDITOR_PAGE_H */
diff --git a/clients/tui/nmt-editor.c b/clients/tui/nmt-editor.c
new file mode 100644
index 0000000000..c200045ab9
--- /dev/null
+++ b/clients/tui/nmt-editor.c
@@ -0,0 +1,332 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-editor
+ * @short_description: Connection editing form
+ *
+ * #NmtEditor is the top-level form for editing a connection.
+ */
+
+#include "config.h"
+
+#include "nmt-editor.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <nm-utils.h>
+
+#include "nmtui.h"
+
+#include "nm-editor-utils.h"
+#include "nmt-page-main.h"
+#include "nmt-utils.h"
+
+G_DEFINE_TYPE (NmtEditor, nmt_editor, NMT_TYPE_NEWT_FORM)
+
+#define NMT_EDITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR, NmtEditorPrivate))
+
+typedef struct {
+ NMConnection *orig_connection;
+ NMConnection *edit_connection;
+
+ NMEditorConnectionTypeData *type_data;
+
+ NmtNewtWidget *ok, *cancel;
+ gboolean running;
+} NmtEditorPrivate;
+
+enum {
+ PROP_0,
+ PROP_CONNECTION,
+ PROP_TYPE_DATA,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_editor_new:
+ * @connection: the #NMConnection to edit
+ *
+ * Creates a new #NmtEditor to edit @connection.
+ *
+ * Returns: a new #NmtEditor
+ */
+NmtNewtForm *
+nmt_editor_new (NMConnection *connection)
+{
+ NMEditorConnectionTypeData *type_data;
+
+ type_data = nm_editor_utils_get_connection_type_data (connection);
+ if (!type_data) {
+ NMSettingConnection *s_con;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ if (s_con) {
+ nmt_newt_message_dialog (_("Could not create editor for connection '%s' of type '%s'."),
+ nm_connection_get_id (connection),
+ nm_setting_connection_get_connection_type (s_con));
+ } else {
+ nmt_newt_message_dialog (_("Could not create editor for invalid connection '%s'."),
+ nm_connection_get_id (connection));
+ }
+
+ return NULL;
+ }
+
+ return g_object_new (NMT_TYPE_EDITOR,
+ "connection", connection,
+ "type-data", type_data,
+ "title", _("Edit Connection"),
+ "fullscreen-vertical", TRUE,
+ NULL);
+}
+
+static void
+nmt_editor_init (NmtEditor *entry)
+{
+}
+
+static void
+connection_updated (NMRemoteConnection *connection,
+ GError *error,
+ gpointer op)
+{
+ nmt_sync_op_complete_boolean (op, error == NULL, error);
+}
+
+static void
+connection_added (NMRemoteSettings *settings,
+ NMRemoteConnection *connection,
+ GError *error,
+ gpointer op)
+{
+ nmt_sync_op_complete_boolean (op, error == NULL, error);
+}
+
+static void
+save_connection_and_exit (NmtNewtButton *button,
+ gpointer user_data)
+{
+ NmtEditor *editor = user_data;
+ NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (editor);
+ NmtSyncOp op;
+ GError *error = NULL;
+
+ if (!nm_connection_replace_settings_from_connection (priv->orig_connection,
+ priv->edit_connection,
+ &error)) {
+ nmt_newt_message_dialog (_("Error saving connection: %s"), error->message);
+ g_error_free (error);
+ return;
+ }
+
+ nmt_sync_op_init (&op);
+ if (NM_IS_REMOTE_CONNECTION (priv->orig_connection)) {
+ nm_remote_connection_commit_changes (NM_REMOTE_CONNECTION (priv->orig_connection),
+ connection_updated, &op);
+ if (!nmt_sync_op_wait_boolean (&op, &error)) {
+ nmt_newt_message_dialog (_("Unable to save connection: %s"),
+ error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* Clear secrets so they don't lay around in memory; they'll get
+ * requested again anyway next time the connection is edited.
+ */
+ nm_connection_clear_secrets (priv->orig_connection);
+ } else {
+ nm_remote_settings_add_connection (nm_settings, priv->orig_connection,
+ connection_added, &op);
+ if (!nmt_sync_op_wait_boolean (&op, &error)) {
+ nmt_newt_message_dialog (_("Unable to add new connection: %s"),
+ error->message);
+ g_error_free (error);
+ return;
+ }
+ }
+
+ nmt_newt_form_quit (NMT_NEWT_FORM (editor));
+}
+
+static void
+got_secrets (NMRemoteConnection *connection,
+ GHashTable *secrets,
+ GError *error,
+ gpointer op)
+{
+ nmt_sync_op_complete_pointer (op, secrets, error);
+}
+
+static NMConnection *
+build_edit_connection (NMConnection *orig_connection)
+{
+ NMConnection *edit_connection;
+ GHashTable *settings, *secrets;
+ GHashTableIter iter;
+ const char *setting_name;
+ NmtSyncOp op;
+
+ edit_connection = nm_connection_duplicate (orig_connection);
+
+ if (!NM_IS_REMOTE_CONNECTION (orig_connection))
+ return edit_connection;
+
+ settings = nm_connection_to_hash (orig_connection, NM_SETTING_HASH_FLAG_NO_SECRETS);
+ g_hash_table_iter_init (&iter, settings);
+ while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, NULL)) {
+ nmt_sync_op_init (&op);
+ nm_remote_connection_get_secrets (NM_REMOTE_CONNECTION (orig_connection),
+ setting_name, got_secrets, &op);
+ /* FIXME: error handling */
+ secrets = nmt_sync_op_wait_pointer (&op, NULL);
+ if (secrets)
+ (void) nm_connection_update_secrets (edit_connection, setting_name, secrets, NULL);
+ }
+ g_hash_table_unref (settings);
+
+ return edit_connection;
+}
+
+static void
+nmt_editor_constructed (GObject *object)
+{
+ NmtEditor *editor = NMT_EDITOR (object);
+ NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (editor);
+ NmtNewtWidget *vbox, *buttons, *page;
+
+ if (G_OBJECT_CLASS (nmt_editor_parent_class)->constructed)
+ G_OBJECT_CLASS (nmt_editor_parent_class)->constructed (object);
+
+ priv->edit_connection = build_edit_connection (priv->orig_connection);
+
+ vbox = nmt_newt_grid_new ();
+
+ page = nmt_page_main_new (priv->edit_connection, priv->type_data);
+ nmt_newt_grid_add (NMT_NEWT_GRID (vbox), page, 0, 0);
+
+ buttons = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL);
+ nmt_newt_grid_add (NMT_NEWT_GRID (vbox), buttons, 0, 1);
+ nmt_newt_widget_set_padding (buttons, 0, 1, 0, 0);
+
+ priv->cancel = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (buttons), _("Cancel"));
+ nmt_newt_widget_set_exit_on_activate (priv->cancel, TRUE);
+
+ priv->ok = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (buttons), _("OK"));
+ g_signal_connect (priv->ok, "clicked", G_CALLBACK (save_connection_and_exit), editor);
+ g_object_bind_property (page, "valid",
+ priv->ok, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ nmt_newt_form_set_content (NMT_NEWT_FORM (editor), vbox);
+}
+
+static void
+nmt_editor_finalize (GObject *object)
+{
+ NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object);
+
+ g_clear_object (&priv->orig_connection);
+ g_clear_object (&priv->edit_connection);
+
+ g_clear_object (&priv->ok);
+ g_clear_object (&priv->cancel);
+
+ G_OBJECT_CLASS (nmt_editor_parent_class)->finalize (object);
+}
+
+static void
+nmt_editor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_CONNECTION:
+ priv->orig_connection = g_value_dup_object (value);
+ break;
+ case PROP_TYPE_DATA:
+ priv->type_data = g_value_get_pointer (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_editor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_CONNECTION:
+ g_value_set_object (value, priv->orig_connection);
+ break;
+ case PROP_TYPE_DATA:
+ g_value_set_pointer (value, priv->type_data);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_editor_class_init (NmtEditorClass *entry_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+ g_type_class_add_private (entry_class, sizeof (NmtEditorPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_editor_constructed;
+ object_class->set_property = nmt_editor_set_property;
+ object_class->get_property = nmt_editor_get_property;
+ object_class->finalize = nmt_editor_finalize;
+
+ /**
+ * NmtEditor:connection:
+ *
+ * The connection being edited.
+ */
+ g_object_class_install_property
+ (object_class, PROP_CONNECTION,
+ g_param_spec_object ("connection", "", "",
+ NM_TYPE_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtEditor:type-data:
+ *
+ * The #NmEditorConnectionTypeData for #NmtEditor:connection.
+ */
+ g_object_class_install_property
+ (object_class, PROP_TYPE_DATA,
+ g_param_spec_pointer ("type-data", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-editor.h b/clients/tui/nmt-editor.h
new file mode 100644
index 0000000000..a991a7666a
--- /dev/null
+++ b/clients/tui/nmt-editor.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_EDITOR_H
+#define NMT_EDITOR_H
+
+#include <nm-connection.h>
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_EDITOR (nmt_editor_get_type ())
+#define NMT_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR, NmtEditor))
+#define NMT_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR, NmtEditorClass))
+#define NMT_IS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR))
+#define NMT_IS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR))
+#define NMT_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR, NmtEditorClass))
+
+typedef struct {
+ NmtNewtForm parent;
+
+} NmtEditor;
+
+typedef struct {
+ NmtNewtFormClass parent;
+
+} NmtEditorClass;
+
+GType nmt_editor_get_type (void);
+
+NmtNewtForm *nmt_editor_new (NMConnection *connection);
+
+G_END_DECLS
+
+#endif /* NMT_EDITOR_H */
diff --git a/clients/tui/nmt-ip-entry.c b/clients/tui/nmt-ip-entry.c
new file mode 100644
index 0000000000..30c7e2e48f
--- /dev/null
+++ b/clients/tui/nmt-ip-entry.c
@@ -0,0 +1,268 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-ip-entry
+ * @short_description: #NmtNewtEntry for IP address entry
+ *
+ * #NmtIPEntry is an #NmtNewtEntry for entering IP addresses, or IP
+ * address/prefix combination. It will only allow typing characters
+ * that are valid in an IP address, and will set its
+ * #NmtNewtWidget:valid property depending on whether it currently
+ * contains a valid IP address.
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-ip-entry.h"
+
+G_DEFINE_TYPE (NmtIPEntry, nmt_ip_entry, NMT_TYPE_NEWT_ENTRY)
+
+#define NMT_IP_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_IP_ENTRY, NmtIPEntryPrivate))
+
+typedef struct {
+ int family;
+ gboolean prefix;
+ gboolean optional;
+
+} NmtIPEntryPrivate;
+
+enum {
+ PROP_0,
+ PROP_FAMILY,
+ PROP_PREFIX,
+ PROP_OPTIONAL,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_ip_entry_new:
+ * @width: the width of the entry
+ * @family: the IP address family. Eg, %AF_INET
+ * @prefix: whether to require a trailing "/prefix"
+ * @optional: whether the address is optional
+ *
+ * Creates a new #NmtIPEntry, to accept IP addresses in the indicated
+ * @family, or (if @prefix is %TRUE), to accept IP address/prefix combos.
+ *
+ * If @optional is %TRUE then the address is considered optional, and
+ * so will still be #NmtNewtWidget:valid even when it is empty. If
+ * @optional is %FALSE, the entry will be invalid when it is empty.
+ */
+NmtNewtWidget *
+nmt_ip_entry_new (int width,
+ int family,
+ gboolean prefix,
+ gboolean optional)
+{
+ return g_object_new (NMT_TYPE_IP_ENTRY,
+ "width", width,
+ "family", family,
+ "prefix", prefix,
+ "optional", optional,
+ NULL);
+}
+
+static gboolean
+ip_entry_filter (NmtNewtEntry *entry,
+ const char *text,
+ int ch,
+ int position,
+ gpointer user_data)
+{
+ NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (entry);
+ const char *slash;
+ gboolean inaddr;
+
+ if (g_ascii_isdigit (ch))
+ return TRUE;
+
+ slash = strchr (text, '/');
+ if (ch == '/')
+ return priv->prefix && slash == NULL;
+
+ inaddr = !slash || (position <= (slash - text));
+
+ if (priv->family == AF_INET) {
+ if (ch == '.')
+ return inaddr;
+ else
+ return FALSE;
+ } else if (priv->family == AF_INET6) {
+ if (g_ascii_isxdigit (ch) || ch == ':')
+ return inaddr;
+ else
+ return FALSE;
+ } else
+ g_return_val_if_reached (FALSE);
+}
+
+static gboolean
+ip_entry_validate (NmtNewtEntry *entry,
+ const char *text,
+ gpointer user_data)
+{
+ NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (entry);
+ guchar buf[16];
+ guint32 prefix;
+ const char *slash;
+ char *addrstr, *end;
+ gboolean valid;
+
+ if (!*text)
+ return priv->optional;
+
+ slash = strchr (text, '/');
+
+ if (slash) {
+ if (!priv->prefix)
+ return FALSE;
+ addrstr = g_strndup (text, slash - text);
+ } else
+ addrstr = g_strdup (text);
+ valid = (inet_pton (priv->family, addrstr, buf) == 1);
+ g_free (addrstr);
+
+ if (!valid)
+ return FALSE;
+
+ if (slash) {
+ prefix = strtoul (slash + 1, &end, 10);
+ if ( *end
+ || prefix == 0
+ || (priv->family == AF_INET && prefix > 32)
+ || (priv->family == AF_INET6 && prefix > 128))
+ valid = FALSE;
+ }
+
+ return valid;
+}
+
+static void
+nmt_ip_entry_init (NmtIPEntry *entry)
+{
+ nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), ip_entry_filter, NULL);
+ nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), ip_entry_validate, NULL);
+}
+
+static void
+nmt_ip_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FAMILY:
+ priv->family = g_value_get_int (value);
+ break;
+ case PROP_PREFIX:
+ priv->prefix = g_value_get_boolean (value);
+ break;
+ case PROP_OPTIONAL:
+ priv->optional = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_ip_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FAMILY:
+ g_value_set_int (value, priv->family);
+ break;
+ case PROP_PREFIX:
+ g_value_set_boolean (value, priv->prefix);
+ break;
+ case PROP_OPTIONAL:
+ g_value_set_boolean (value, priv->optional);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_ip_entry_class_init (NmtIPEntryClass *entry_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+ g_type_class_add_private (entry_class, sizeof (NmtIPEntryPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_ip_entry_set_property;
+ object_class->get_property = nmt_ip_entry_get_property;
+
+ /**
+ * NmtIPEntry:family:
+ *
+ * The address family. Eg, %AF_INET
+ */
+ g_object_class_install_property
+ (object_class, PROP_FAMILY,
+ g_param_spec_int ("family", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtIPEntry:prefix:
+ *
+ * If %TRUE, the entry accepts address/prefix combinations. If
+ * %FALSE it accepts just addresses.
+ */
+ g_object_class_install_property
+ (object_class, PROP_PREFIX,
+ g_param_spec_boolean ("prefix", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtIPEntry:optional:
+ *
+ * If %TRUE, the entry will be #NmtNewtWidget:valid when it is
+ * empty. If %FALSE, it will only be valid when it contains a
+ * valid address or address/prefix.
+ */
+ g_object_class_install_property
+ (object_class, PROP_OPTIONAL,
+ g_param_spec_boolean ("optional", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-ip-entry.h b/clients/tui/nmt-ip-entry.h
new file mode 100644
index 0000000000..ceb355efcf
--- /dev/null
+++ b/clients/tui/nmt-ip-entry.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_IP_ENTRY_H
+#define NMT_IP_ENTRY_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_IP_ENTRY (nmt_ip_entry_get_type ())
+#define NMT_IP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_IP_ENTRY, NmtIPEntry))
+#define NMT_IP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_IP_ENTRY, NmtIPEntryClass))
+#define NMT_IS_IP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_IP_ENTRY))
+#define NMT_IS_IP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_IP_ENTRY))
+#define NMT_IP_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_IP_ENTRY, NmtIPEntryClass))
+
+typedef struct {
+ NmtNewtEntry parent;
+
+} NmtIPEntry;
+
+typedef struct {
+ NmtNewtEntryClass parent;
+
+} NmtIPEntryClass;
+
+GType nmt_ip_entry_get_type (void);
+
+NmtNewtWidget *nmt_ip_entry_new (int width,
+ int family,
+ gboolean prefix,
+ gboolean optional);
+
+G_END_DECLS
+
+#endif /* NMT_IP_ENTRY_H */
diff --git a/clients/tui/nmt-mac-entry.c b/clients/tui/nmt-mac-entry.c
new file mode 100644
index 0000000000..0dad12363b
--- /dev/null
+++ b/clients/tui/nmt-mac-entry.c
@@ -0,0 +1,238 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-mac-entry
+ * @short_description: #NmtNewtEntry for hardware address entry
+ *
+ * #NmtMacEntry is an #NmtNewtEntry for entering hardware addresses.
+ * It will only allow typing characters that are valid in a hardware
+ * address, and will set its #NmtNewtWidget:valid property depending
+ * on whether it currently contains a valid hardware address.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <dbus/dbus-glib.h>
+#include <nm-utils.h>
+
+#include "nmt-mac-entry.h"
+
+G_DEFINE_TYPE (NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY)
+
+#define NMT_MAC_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_MAC_ENTRY, NmtMacEntryPrivate))
+
+typedef struct {
+ int mac_length;
+ int mac_str_length;
+
+} NmtMacEntryPrivate;
+
+enum {
+ PROP_0,
+ PROP_MAC_LENGTH,
+ PROP_MAC_ADDRESS,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_mac_entry_new:
+ * @width: the width in characters of the entry
+ * @mac_length: the length in bytes of the hardware address
+ * (either %ETH_ALEN or %INFINIBAND_ALEN)
+ *
+ * Creates a new #NmtMacEntry.
+ *
+ * Returns: a new #NmtMacEntry.
+ */
+NmtNewtWidget *
+nmt_mac_entry_new (int width,
+ int mac_length)
+{
+ return g_object_new (NMT_TYPE_MAC_ENTRY,
+ "width", width,
+ "mac-length", mac_length,
+ NULL);
+}
+
+static gboolean
+mac_filter (NmtNewtEntry *entry,
+ const char *text,
+ int ch,
+ int position,
+ gpointer user_data)
+{
+ NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry);
+
+ if (position >= priv->mac_str_length)
+ return FALSE;
+
+ return g_ascii_isxdigit (ch) || ch == ':';
+}
+
+static gboolean
+mac_validator (NmtNewtEntry *entry,
+ const char *text,
+ gpointer user_data)
+{
+ NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry);
+ const char *p;
+
+ if (!*text)
+ return TRUE;
+
+ p = text;
+ while ( g_ascii_isxdigit (p[0])
+ && g_ascii_isxdigit (p[1])
+ && p[2] == ':')
+ p += 3;
+
+ if ( !g_ascii_isxdigit (p[0])
+ || !g_ascii_isxdigit (p[1]))
+ return FALSE;
+ p += 2;
+
+ if (!*p)
+ return (p - text == priv->mac_str_length);
+
+ if (g_ascii_isxdigit (p[0]) && !p[1]) {
+ char *fixed = g_strdup_printf ("%.*s:%c", (int)(p - text), text, *p);
+
+ nmt_newt_entry_set_text (entry, fixed);
+ g_free (fixed);
+
+ /* FIXME: NmtNewtEntry doesn't correctly deal with us calling set_text()
+ * from inside the validator.
+ */
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry));
+ }
+
+ return FALSE;
+}
+
+static void
+nmt_mac_entry_init (NmtMacEntry *entry)
+{
+ nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), mac_filter, NULL);
+ nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), mac_validator, NULL);
+}
+
+static void
+nmt_mac_entry_notify (GObject *object,
+ GParamSpec *pspec)
+{
+ if (G_OBJECT_CLASS (nmt_mac_entry_parent_class)->notify)
+ G_OBJECT_CLASS (nmt_mac_entry_parent_class)->notify (object, pspec);
+
+ if (pspec->owner_type == NMT_TYPE_NEWT_ENTRY && !strcmp (pspec->name, "text"))
+ g_object_notify (object, "mac-address");
+}
+
+static void
+nmt_mac_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (object);
+ GByteArray *addr;
+ char *addr_str;
+
+ switch (prop_id) {
+ case PROP_MAC_LENGTH:
+ priv->mac_length = g_value_get_int (value);
+ priv->mac_str_length = priv->mac_length * 3 - 1;
+ break;
+ case PROP_MAC_ADDRESS:
+ addr = g_value_get_boxed (value);
+ if (addr) {
+ addr_str = nm_utils_hwaddr_ntoa_len (addr->data, addr->len);
+ nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), addr_str);
+ g_free (addr_str);
+ } else
+ nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), "");
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_mac_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (object);
+ GByteArray *addr;
+
+ switch (prop_id) {
+ case PROP_MAC_LENGTH:
+ g_value_set_int (value, priv->mac_length);
+ break;
+ case PROP_MAC_ADDRESS:
+ addr = nm_utils_hwaddr_atoba (nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object)), ARPHRD_ETHER);
+ g_value_take_boxed (value, addr);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_mac_entry_class_init (NmtMacEntryClass *entry_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+ g_type_class_add_private (entry_class, sizeof (NmtMacEntryPrivate));
+
+ /* virtual methods */
+ object_class->notify = nmt_mac_entry_notify;
+ object_class->set_property = nmt_mac_entry_set_property;
+ object_class->get_property = nmt_mac_entry_get_property;
+
+ /**
+ * NmtMacEntry:mac-length:
+ *
+ * The length in bytes of the hardware address type the entry
+ * accepts: either %ETH_ALEN or %INFINIBAND_ALEN.
+ */
+ g_object_class_install_property
+ (object_class, PROP_MAC_LENGTH,
+ g_param_spec_int ("mac-length", "", "",
+ 0, INFINIBAND_ALEN, ETH_ALEN,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtMacEntry:mac-address:
+ *
+ * The MAC address, in binary (in the same format used by the various
+ * #NMSetting "mac-address" properties).
+ */
+ g_object_class_install_property
+ (object_class, PROP_MAC_ADDRESS,
+ g_param_spec_boxed ("mac-address", "", "",
+ DBUS_TYPE_G_UCHAR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-mac-entry.h b/clients/tui/nmt-mac-entry.h
new file mode 100644
index 0000000000..33a3232986
--- /dev/null
+++ b/clients/tui/nmt-mac-entry.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_MAC_ENTRY_H
+#define NMT_MAC_ENTRY_H
+
+#include <linux/if_ether.h>
+#include <linux/if_infiniband.h>
+#include <linux/if_arp.h>
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_MAC_ENTRY (nmt_mac_entry_get_type ())
+#define NMT_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntry))
+#define NMT_MAC_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass))
+#define NMT_IS_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_MAC_ENTRY))
+#define NMT_IS_MAC_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_MAC_ENTRY))
+#define NMT_MAC_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass))
+
+typedef struct {
+ NmtNewtEntry parent;
+
+} NmtMacEntry;
+
+typedef struct {
+ NmtNewtEntryClass parent;
+
+} NmtMacEntryClass;
+
+GType nmt_mac_entry_get_type (void);
+
+NmtNewtWidget *nmt_mac_entry_new (int width,
+ int mac_length);
+
+G_END_DECLS
+
+#endif /* NMT_MAC_ENTRY_H */
diff --git a/clients/tui/nmt-mtu-entry.c b/clients/tui/nmt-mtu-entry.c
new file mode 100644
index 0000000000..f599557aea
--- /dev/null
+++ b/clients/tui/nmt-mtu-entry.c
@@ -0,0 +1,192 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-mtu-entry
+ * @short_description: #NmtNewtEntry for MTU entry
+ *
+ * #NmtMtuEntry is an #NmtNewtEntry for entering MTU values. It will
+ * only allow typing numeric characters, and will set its
+ * #NmtNewtWidget:valid property depending on whether it currently
+ * contains a valid MTU.
+ *
+ * The entry also has an attached #NmtNewtLabel. When the entry value
+ * is "0", the label will read "(default)". Otherwise it reads "bytes",
+ * indicating the units used by the entry.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-mtu-entry.h"
+
+G_DEFINE_TYPE (NmtMtuEntry, nmt_mtu_entry, NMT_TYPE_NEWT_GRID)
+
+#define NMT_MTU_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_MTU_ENTRY, NmtMtuEntryPrivate))
+
+typedef struct {
+ int mtu;
+
+ NmtNewtEntry *entry;
+ NmtNewtLabel *label;
+
+} NmtMtuEntryPrivate;
+
+enum {
+ PROP_0,
+ PROP_MTU,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_mtu_entry_new:
+ *
+ * Creates a new #NmtMtuEntry
+ *
+ * Returns: a new #NmtMtuEntry
+ */
+NmtNewtWidget *
+nmt_mtu_entry_new (void)
+{
+ return g_object_new (NMT_TYPE_MTU_ENTRY, NULL);
+}
+
+static gboolean
+mtu_validator (NmtNewtEntry *entry,
+ const char *text,
+ gpointer user_data)
+{
+ NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (user_data);
+
+ if (*text && !atoi (text)) {
+ nmt_newt_entry_set_text (entry, "");
+ text = "";
+ }
+
+ if (!*text)
+ nmt_newt_label_set_text (priv->label, _("(default)"));
+ else
+ nmt_newt_label_set_text (priv->label, _("bytes"));
+
+ return TRUE;
+}
+
+static gboolean
+mtu_transform_to_text (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ int mtu = g_value_get_int (source_value);
+
+ if (mtu)
+ g_value_transform (source_value, target_value);
+ else
+ g_value_set_string (target_value, "");
+ return TRUE;
+}
+
+static void
+nmt_mtu_entry_init (NmtMtuEntry *entry)
+{
+
+ NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (entry);
+ NmtNewtGrid *grid = NMT_NEWT_GRID (entry);
+ NmtNewtWidget *real_entry, *label;
+
+ real_entry = nmt_newt_entry_numeric_new (10, 0, 65535);
+ priv->entry = NMT_NEWT_ENTRY (real_entry);
+
+ label = nmt_newt_label_new (_("bytes"));
+ priv->label = NMT_NEWT_LABEL (label);
+
+ nmt_newt_grid_add (grid, real_entry, 0, 0);
+ nmt_newt_grid_add (grid, label, 1, 0);
+ nmt_newt_widget_set_padding (label, 1, 0, 0, 0);
+
+ nmt_newt_entry_set_validator (priv->entry, mtu_validator, entry);
+ g_object_bind_property_full (entry, "mtu", real_entry, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
+ mtu_transform_to_text,
+ NULL,
+ NULL, NULL);
+}
+
+static void
+nmt_mtu_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MTU:
+ priv->mtu = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_mtu_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MTU:
+ g_value_set_int (value, priv->mtu);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_mtu_entry_class_init (NmtMtuEntryClass *entry_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+ g_type_class_add_private (entry_class, sizeof (NmtMtuEntryPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_mtu_entry_set_property;
+ object_class->get_property = nmt_mtu_entry_get_property;
+
+ /**
+ * NmtMtuEntry:mtu:
+ *
+ * The contents of the entry, as a number.
+ */
+ g_object_class_install_property
+ (object_class, PROP_MTU,
+ g_param_spec_int ("mtu", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-mtu-entry.h b/clients/tui/nmt-mtu-entry.h
new file mode 100644
index 0000000000..dfe416d0e7
--- /dev/null
+++ b/clients/tui/nmt-mtu-entry.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_MTU_ENTRY_H
+#define NMT_MTU_ENTRY_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_MTU_ENTRY (nmt_mtu_entry_get_type ())
+#define NMT_MTU_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntry))
+#define NMT_MTU_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass))
+#define NMT_IS_MTU_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_MTU_ENTRY))
+#define NMT_IS_MTU_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_MTU_ENTRY))
+#define NMT_MTU_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass))
+
+typedef struct {
+ NmtNewtGrid parent;
+
+} NmtMtuEntry;
+
+typedef struct {
+ NmtNewtGridClass parent;
+
+} NmtMtuEntryClass;
+
+GType nmt_mtu_entry_get_type (void);
+
+NmtNewtWidget *nmt_mtu_entry_new (void);
+
+G_END_DECLS
+
+#endif /* NMT_MTU_ENTRY_H */
diff --git a/clients/tui/nmt-page-bond.c b/clients/tui/nmt-page-bond.c
new file mode 100644
index 0000000000..55a6ffeebb
--- /dev/null
+++ b/clients/tui/nmt-page-bond.c
@@ -0,0 +1,436 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-bond
+ * @short_description: The editor page for Bond connections
+ *
+ * Note that this is fairly different from most of the other editor
+ * pages, because #NMSettingBond doesn't have properties, so we
+ * can't just use #GBinding.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-bond.h"
+
+#include "nmt-address-list.h"
+#include "nmt-slave-list.h"
+
+G_DEFINE_TYPE (NmtPageBond, nmt_page_bond, NMT_TYPE_PAGE_DEVICE)
+
+#define NMT_PAGE_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_BOND, NmtPageBondPrivate))
+
+typedef enum {
+ NMT_PAGE_BOND_MONITORING_UNKNOWN = -1,
+ NMT_PAGE_BOND_MONITORING_MII = 0,
+ NMT_PAGE_BOND_MONITORING_ARP = 1,
+} NmtPageBondMonitoringMode;
+
+typedef struct {
+ NmtSlaveList *slaves;
+
+ NmtNewtPopup *mode;
+ NmtNewtEntry *primary;
+ NmtNewtPopup *monitoring;
+ NmtNewtEntry *miimon;
+ NmtNewtEntry *updelay;
+ NmtNewtEntry *downdelay;
+ NmtNewtEntry *arp_interval;
+ NmtAddressList *arp_ip_target;
+
+ NmtPageBondMonitoringMode monitoring_mode;
+
+ NMSettingBond *s_bond;
+ GType slave_type;
+ gboolean updating;
+} NmtPageBondPrivate;
+
+NmtNewtWidget *
+nmt_page_bond_new (NMConnection *conn,
+ NmtDeviceEntry *deventry)
+{
+ return g_object_new (NMT_TYPE_PAGE_BOND,
+ "connection", conn,
+ "title", _("BOND"),
+ "device-entry", deventry,
+ NULL);
+}
+
+static void
+nmt_page_bond_init (NmtPageBond *bond)
+{
+ NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond);
+
+ priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_UNKNOWN;
+ priv->slave_type = G_TYPE_NONE;
+}
+
+static NmtNewtPopupEntry bond_mode[] = {
+ { N_("Round-robin"), "balance-rr" },
+ { N_("Active Backup"), "active-backup" },
+ { N_("XOR"), "balance-xor" },
+ { N_("Broadcast"), "broadcast" },
+ { N_("802.3ad"), "802.3ad" },
+ { N_("Adaptive Transmit Load Balancing (tlb)"), "balance-tlb" },
+ { N_("Adaptive Load Balancing (alb)"), "balance-alb" },
+ { NULL, NULL }
+};
+
+/* NB: the ordering/numbering here corresponds to NmtPageBondMonitoringMode */
+static NmtNewtPopupEntry bond_monitoring[] = {
+ { N_("MII (recommended)"), "mii" },
+ { N_("ARP"), "arp" },
+ { NULL, NULL }
+};
+
+static void
+bond_options_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NMSettingBond *s_bond = NM_SETTING_BOND (object);
+ NmtPageBond *bond = NMT_PAGE_BOND (user_data);
+ NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond);
+ const char *val;
+ char **ips;
+
+ if (priv->updating)
+ return;
+
+ priv->updating = TRUE;
+
+ val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE);
+ nmt_newt_popup_set_active_id (priv->mode, val);
+
+ if (!strcmp (val, "active-backup")) {
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), TRUE);
+ val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
+ nmt_newt_entry_set_text (priv->primary, val);
+ } else
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), FALSE);
+
+ if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_UNKNOWN) {
+ val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
+ if (val && strcmp (val, "0") != 0)
+ priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_ARP;
+ else
+ priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_MII;
+ }
+ nmt_newt_popup_set_active (priv->monitoring, priv->monitoring_mode);
+
+ if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_MII) {
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), TRUE);
+ val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MIIMON);
+ nmt_newt_entry_set_text (priv->miimon, val);
+
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), TRUE);
+ val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_UPDELAY);
+ nmt_newt_entry_set_text (priv->updelay, val);
+
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), TRUE);
+ val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY);
+ nmt_newt_entry_set_text (priv->downdelay, val);
+
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), FALSE);
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), FALSE);
+ } else {
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), TRUE);
+ val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL);
+ nmt_newt_entry_set_text (priv->arp_interval, val);
+
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), TRUE);
+ val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET);
+ ips = g_strsplit (val, ",", -1);
+ g_object_set (G_OBJECT (priv->arp_ip_target),
+ "string", ips,
+ NULL);
+ g_strfreev (ips);
+
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), FALSE);
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), FALSE);
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), FALSE);
+ }
+
+ priv->updating = FALSE;
+}
+
+static void
+slaves_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NmtPageBond *bond = NMT_PAGE_BOND (user_data);
+ NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond);
+ GPtrArray *slaves;
+
+ g_object_get (object, "connections", &slaves, NULL);
+ if (slaves->len == 0) {
+ if (priv->slave_type == G_TYPE_NONE)
+ return;
+ priv->slave_type = G_TYPE_NONE;
+ } else {
+ NMConnection *slave = slaves->pdata[0];
+
+ if (priv->slave_type != G_TYPE_NONE)
+ return;
+
+ if (nm_connection_is_type (slave, NM_SETTING_INFINIBAND_SETTING_NAME))
+ priv->slave_type = NM_TYPE_SETTING_INFINIBAND;
+ else
+ priv->slave_type = NM_TYPE_SETTING_WIRED;
+ }
+
+ if (priv->slave_type == NM_TYPE_SETTING_INFINIBAND) {
+ nmt_newt_popup_set_active_id (priv->mode, "active-backup");
+ nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->mode), FALSE);
+ } else
+ nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->mode), TRUE);
+}
+
+#define WIDGET_CHANGED_FUNC(widget, func, option) \
+static void \
+widget ## _widget_changed (GObject *object, \
+ GParamSpec *pspec, \
+ gpointer user_data) \
+{ \
+ NmtPageBond *bond = NMT_PAGE_BOND (user_data); \
+ NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); \
+ \
+ if (priv->updating) \
+ return; \
+ \
+ priv->updating = TRUE; \
+ nm_setting_bond_add_option (priv->s_bond, option, func (priv->widget)); \
+ priv->updating = FALSE; \
+}
+
+WIDGET_CHANGED_FUNC (primary, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_PRIMARY)
+WIDGET_CHANGED_FUNC (miimon, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_MIIMON)
+WIDGET_CHANGED_FUNC (updelay, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_UPDELAY)
+WIDGET_CHANGED_FUNC (downdelay, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_DOWNDELAY)
+WIDGET_CHANGED_FUNC (arp_interval, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_ARP_INTERVAL)
+
+static void
+mode_widget_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NmtPageBond *bond = NMT_PAGE_BOND (user_data);
+ NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond);
+ const char *mode;
+
+ if (priv->updating)
+ return;
+
+ mode = nmt_newt_popup_get_active_id (priv->mode);
+ priv->updating = TRUE;
+ nm_setting_bond_add_option (priv->s_bond, NM_SETTING_BOND_OPTION_MODE, mode);
+ priv->updating = FALSE;
+
+ if (!strcmp (mode, "balance-tlb") || !strcmp (mode, "balance-alb")) {
+ nmt_newt_popup_set_active (priv->monitoring, NMT_PAGE_BOND_MONITORING_MII);
+ nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->monitoring), FALSE);
+ } else
+ nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->monitoring), TRUE);
+
+ if (!strcmp (mode, "active-backup"))
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), TRUE);
+ else
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), FALSE);
+}
+
+static void
+monitoring_widget_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NmtPageBond *bond = NMT_PAGE_BOND (user_data);
+ NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond);
+
+ if (priv->updating)
+ return;
+
+ priv->monitoring_mode = nmt_newt_popup_get_active (priv->monitoring);
+ if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_MII) {
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), TRUE);
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), TRUE);
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), TRUE);
+
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), FALSE);
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), FALSE);
+ } else {
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), TRUE);
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), TRUE);
+
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), FALSE);
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), FALSE);
+ nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), FALSE);
+ }
+}
+
+static void
+arp_ip_target_widget_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NmtPageBond *bond = NMT_PAGE_BOND (user_data);
+ NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond);
+ char **ips, *target;
+
+ if (priv->updating)
+ return;
+
+ g_object_get (G_OBJECT (priv->arp_ip_target),
+ "strings", &ips,
+ NULL);
+ target = g_strjoinv (",", ips);
+
+ priv->updating = TRUE;
+ nm_setting_bond_add_option (priv->s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, target);
+ priv->updating = FALSE;
+
+ g_free (target);
+ g_strfreev (ips);
+}
+
+static gboolean
+bond_connection_type_filter (GType connection_type,
+ gpointer user_data)
+{
+ NmtPageBond *bond = user_data;
+ NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond);
+
+ if ( priv->slave_type != NM_TYPE_SETTING_WIRED
+ && connection_type == NM_TYPE_SETTING_INFINIBAND)
+ return TRUE;
+ if ( priv->slave_type != NM_TYPE_SETTING_INFINIBAND
+ && connection_type == NM_TYPE_SETTING_WIRED)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void
+nmt_page_bond_constructed (GObject *object)
+{
+ NmtPageBond *bond = NMT_PAGE_BOND (object);
+ NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond);
+ NmtDeviceEntry *deventry;
+ NmtPageGrid *grid;
+ NMSettingBond *s_bond;
+ NmtNewtWidget *widget, *label;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (bond));
+ s_bond = nm_connection_get_setting_bond (conn);
+ if (!s_bond) {
+ nm_connection_add_setting (conn, nm_setting_bond_new ());
+ s_bond = nm_connection_get_setting_bond (conn);
+ }
+ priv->s_bond = s_bond;
+
+ deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object));
+ g_object_bind_property (s_bond, NM_SETTING_BOND_INTERFACE_NAME,
+ deventry, "interface-name",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ grid = NMT_PAGE_GRID (bond);
+
+ widget = nmt_newt_separator_new ();
+ nmt_page_grid_append (grid, _("Slaves"), widget, NULL);
+ nmt_page_grid_set_row_flags (grid, widget, NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT);
+
+ widget = nmt_slave_list_new (conn, bond_connection_type_filter, bond);
+ g_signal_connect (widget, "notify::connections",
+ G_CALLBACK (slaves_changed), bond);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+ priv->slaves = NMT_SLAVE_LIST (widget);
+
+ widget = nmt_newt_popup_new (bond_mode);
+ g_signal_connect (widget, "notify::active-id",
+ G_CALLBACK (mode_widget_changed), bond);
+ nmt_page_grid_append (grid, _("Mode"), widget, NULL);
+ priv->mode = NMT_NEWT_POPUP (widget);
+
+ widget = nmt_newt_entry_new (40, 0);
+ g_signal_connect (widget, "notify::text",
+ G_CALLBACK (primary_widget_changed), bond);
+ nmt_page_grid_append (grid, _("Primary"), widget, NULL);
+ priv->primary = NMT_NEWT_ENTRY (widget);
+
+ widget = nmt_newt_popup_new (bond_monitoring);
+ g_signal_connect (widget, "notify::active",
+ G_CALLBACK (monitoring_widget_changed), bond);
+ nmt_page_grid_append (grid, _("Link monitoring"), widget, NULL);
+ priv->monitoring = NMT_NEWT_POPUP (widget);
+
+ widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT);
+ g_signal_connect (widget, "notify::text",
+ G_CALLBACK (miimon_widget_changed), bond);
+ label = nmt_newt_label_new (C_("milliseconds", "ms"));
+ nmt_page_grid_append (grid, _("Monitoring frequency"), widget, label);
+ priv->miimon = NMT_NEWT_ENTRY (widget);
+
+ widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT);
+ g_signal_connect (widget, "notify::text",
+ G_CALLBACK (updelay_widget_changed), bond);
+ label = nmt_newt_label_new (C_("milliseconds", "ms"));
+ nmt_page_grid_append (grid, _("Link up delay"), widget, label);
+ priv->updelay = NMT_NEWT_ENTRY (widget);
+
+ widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT);
+ g_signal_connect (widget, "notify::text",
+ G_CALLBACK (downdelay_widget_changed), bond);
+ label = nmt_newt_label_new (C_("milliseconds", "ms"));
+ nmt_page_grid_append (grid, _("Link down delay"), widget, label);
+ priv->downdelay = NMT_NEWT_ENTRY (widget);
+
+ widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT);
+ g_signal_connect (widget, "notify::text",
+ G_CALLBACK (arp_interval_widget_changed), bond);
+ label = nmt_newt_label_new (C_("milliseconds", "ms"));
+ nmt_page_grid_append (grid, _("Monitoring frequency"), widget, label);
+ priv->arp_interval = NMT_NEWT_ENTRY (widget);
+
+ widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4);
+ g_signal_connect (widget, "notify::strings",
+ G_CALLBACK (arp_ip_target_widget_changed), bond);
+ nmt_page_grid_append (grid, _("ARP targets"), widget, NULL);
+ priv->arp_ip_target = NMT_ADDRESS_LIST (widget);
+
+ g_signal_connect (s_bond, "notify::" NM_SETTING_BOND_OPTIONS,
+ G_CALLBACK (bond_options_changed), bond);
+ bond_options_changed (G_OBJECT (s_bond), NULL, bond);
+ slaves_changed (G_OBJECT (priv->slaves), NULL, bond);
+
+ G_OBJECT_CLASS (nmt_page_bond_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_bond_class_init (NmtPageBondClass *bond_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (bond_class);
+
+ g_type_class_add_private (bond_class, sizeof (NmtPageBondPrivate));
+
+ object_class->constructed = nmt_page_bond_constructed;
+}
diff --git a/clients/tui/nmt-page-bond.h b/clients/tui/nmt-page-bond.h
new file mode 100644
index 0000000000..c8e69dd5ee
--- /dev/null
+++ b/clients/tui/nmt-page-bond.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_BOND_H
+#define NMT_PAGE_BOND_H
+
+#include "nmt-page-device.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_BOND (nmt_page_bond_get_type ())
+#define NMT_PAGE_BOND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BOND, NmtPageBond))
+#define NMT_PAGE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BOND, NmtPageBondClass))
+#define NMT_IS_PAGE_BOND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_BOND))
+#define NMT_IS_PAGE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_BOND))
+#define NMT_PAGE_BOND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_BOND, NmtPageBondClass))
+
+typedef struct {
+ NmtPageDevice parent;
+
+} NmtPageBond;
+
+typedef struct {
+ NmtPageDeviceClass parent;
+
+} NmtPageBondClass;
+
+GType nmt_page_bond_get_type (void);
+
+NmtNewtWidget *nmt_page_bond_new (NMConnection *conn,
+ NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_BOND_H */
diff --git a/clients/tui/nmt-page-bridge-port.c b/clients/tui/nmt-page-bridge-port.c
new file mode 100644
index 0000000000..ab7a78887f
--- /dev/null
+++ b/clients/tui/nmt-page-bridge-port.c
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-bridge-port
+ * @short_description: The editor page for Bridge ports
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-bridge-port.h"
+
+G_DEFINE_TYPE (NmtPageBridgePort, nmt_page_bridge_port, NMT_TYPE_EDITOR_PAGE)
+
+NmtNewtWidget *
+nmt_page_bridge_port_new (NMConnection *conn)
+{
+ return g_object_new (NMT_TYPE_PAGE_BRIDGE_PORT,
+ "connection", conn,
+ "title", _("BRIDGE PORT"),
+ NULL);
+}
+
+static void
+nmt_page_bridge_port_init (NmtPageBridgePort *bridge)
+{
+}
+
+static void
+nmt_page_bridge_port_constructed (GObject *object)
+{
+ NmtPageBridgePort *bridge = NMT_PAGE_BRIDGE_PORT (object);
+ NmtPageGrid *grid;
+ NMSettingBridgePort *s_port;
+ NmtNewtWidget *widget;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (bridge));
+ s_port = nm_connection_get_setting_bridge_port (conn);
+ if (!s_port) {
+ nm_connection_add_setting (conn, nm_setting_bridge_port_new ());
+ s_port = nm_connection_get_setting_bridge_port (conn);
+ }
+
+ grid = NMT_PAGE_GRID (bridge);
+
+ widget = nmt_newt_entry_numeric_new (10, 0, 63);
+ g_object_bind_property (s_port, NM_SETTING_BRIDGE_PORT_PRIORITY,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Priority"), widget, NULL);
+
+ widget = nmt_newt_entry_numeric_new (10, 1, 65535);
+ g_object_bind_property (s_port, NM_SETTING_BRIDGE_PORT_PATH_COST,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Path cost"), widget, NULL);
+
+ widget = nmt_newt_checkbox_new (_("Hairpin mode"));
+ g_object_bind_property (s_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE,
+ widget, "active",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+
+ G_OBJECT_CLASS (nmt_page_bridge_port_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_bridge_port_class_init (NmtPageBridgePortClass *bridge_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (bridge_class);
+
+ object_class->constructed = nmt_page_bridge_port_constructed;
+}
diff --git a/clients/tui/nmt-page-bridge-port.h b/clients/tui/nmt-page-bridge-port.h
new file mode 100644
index 0000000000..7fe3a92c43
--- /dev/null
+++ b/clients/tui/nmt-page-bridge-port.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_BRIDGE_PORT_H
+#define NMT_PAGE_BRIDGE_PORT_H
+
+#include "nmt-editor-page.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_BRIDGE_PORT (nmt_page_bridge_port_get_type ())
+#define NMT_PAGE_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePort))
+#define NMT_PAGE_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass))
+#define NMT_IS_PAGE_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_BRIDGE_PORT))
+#define NMT_IS_PAGE_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_BRIDGE_PORT))
+#define NMT_PAGE_BRIDGE_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass))
+
+typedef struct {
+ NmtEditorPage parent;
+
+} NmtPageBridgePort;
+
+typedef struct {
+ NmtEditorPageClass parent;
+
+} NmtPageBridgePortClass;
+
+GType nmt_page_bridge_port_get_type (void);
+
+NmtNewtWidget *nmt_page_bridge_port_new (NMConnection *conn);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_BRIDGE_PORT_H */
diff --git a/clients/tui/nmt-page-bridge.c b/clients/tui/nmt-page-bridge.c
new file mode 100644
index 0000000000..713d83507e
--- /dev/null
+++ b/clients/tui/nmt-page-bridge.c
@@ -0,0 +1,153 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-bridge
+ * @short_description: The editor page for Bridge connections
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-bridge.h"
+
+#include "nmt-address-list.h"
+#include "nmt-slave-list.h"
+
+G_DEFINE_TYPE (NmtPageBridge, nmt_page_bridge, NMT_TYPE_PAGE_DEVICE)
+
+NmtNewtWidget *
+nmt_page_bridge_new (NMConnection *conn,
+ NmtDeviceEntry *deventry)
+{
+ return g_object_new (NMT_TYPE_PAGE_BRIDGE,
+ "connection", conn,
+ "title", _("BRIDGE"),
+ "device-entry", deventry,
+ NULL);
+}
+
+static void
+nmt_page_bridge_init (NmtPageBridge *bridge)
+{
+}
+
+static gboolean
+bridge_connection_type_filter (GType connection_type,
+ gpointer user_data)
+{
+ return ( connection_type == NM_TYPE_SETTING_WIRED
+ || connection_type == NM_TYPE_SETTING_WIRELESS
+ || connection_type == NM_TYPE_SETTING_VLAN);
+}
+
+static void
+nmt_page_bridge_constructed (GObject *object)
+{
+ NmtPageBridge *bridge = NMT_PAGE_BRIDGE (object);
+ NmtDeviceEntry *deventry;
+ NmtPageGrid *grid;
+ NMSettingBridge *s_bridge;
+ NmtNewtWidget *widget, *label, *stp;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (bridge));
+ s_bridge = nm_connection_get_setting_bridge (conn);
+ if (!s_bridge) {
+ nm_connection_add_setting (conn, nm_setting_bridge_new ());
+ s_bridge = nm_connection_get_setting_bridge (conn);
+ }
+
+ deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object));
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME,
+ deventry, "interface-name",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ grid = NMT_PAGE_GRID (bridge);
+
+ widget = nmt_newt_separator_new ();
+ nmt_page_grid_append (grid, _("Slaves"), widget, NULL);
+ nmt_page_grid_set_row_flags (grid, widget, NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT);
+
+ widget = nmt_slave_list_new (conn, bridge_connection_type_filter, bridge);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+
+ widget = nmt_newt_entry_numeric_new (10, 0, 1000000);
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ label = nmt_newt_label_new (_("seconds"));
+ nmt_page_grid_append (grid, _("Aging time"), widget, label);
+
+ widget = stp = nmt_newt_checkbox_new (_("Enable STP (Spanning Tree Protocol)"));
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP,
+ widget, "active",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+
+ widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT);
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_PRIORITY,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP,
+ widget, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Priority"), widget, NULL);
+
+ widget = nmt_newt_entry_numeric_new (10, 2, 30);
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP,
+ widget, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ label = nmt_newt_label_new (_("seconds"));
+ nmt_page_grid_append (grid, _("Forward delay"), widget, label);
+
+ widget = nmt_newt_entry_numeric_new (10, 1, 10);
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP,
+ widget, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ label = nmt_newt_label_new (_("seconds"));
+ nmt_page_grid_append (grid, _("Hello time"), widget, label);
+
+ widget = nmt_newt_entry_numeric_new (10, 6, 40);
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_MAX_AGE,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP,
+ widget, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ label = nmt_newt_label_new (_("seconds"));
+ nmt_page_grid_append (grid, _("Max age"), widget, label);
+
+ G_OBJECT_CLASS (nmt_page_bridge_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_bridge_class_init (NmtPageBridgeClass *bridge_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (bridge_class);
+
+ object_class->constructed = nmt_page_bridge_constructed;
+}
diff --git a/clients/tui/nmt-page-bridge.h b/clients/tui/nmt-page-bridge.h
new file mode 100644
index 0000000000..b11d350291
--- /dev/null
+++ b/clients/tui/nmt-page-bridge.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_BRIDGE_H
+#define NMT_PAGE_BRIDGE_H
+
+#include "nmt-page-device.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_BRIDGE (nmt_page_bridge_get_type ())
+#define NMT_PAGE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BRIDGE, NmtPageBridge))
+#define NMT_PAGE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BRIDGE, NmtPageBridgeClass))
+#define NMT_IS_PAGE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_BRIDGE))
+#define NMT_IS_PAGE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_BRIDGE))
+#define NMT_PAGE_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_BRIDGE, NmtPageBridgeClass))
+
+typedef struct {
+ NmtPageDevice parent;
+
+} NmtPageBridge;
+
+typedef struct {
+ NmtPageDeviceClass parent;
+
+} NmtPageBridgeClass;
+
+GType nmt_page_bridge_get_type (void);
+
+NmtNewtWidget *nmt_page_bridge_new (NMConnection *conn,
+ NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_BRIDGE_H */
diff --git a/clients/tui/nmt-page-device.c b/clients/tui/nmt-page-device.c
new file mode 100644
index 0000000000..811b5a7db8
--- /dev/null
+++ b/clients/tui/nmt-page-device.c
@@ -0,0 +1,153 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-device
+ * @short_description: Abstract base class for "device" editor pages
+ *
+ * #NmtPageDevice is the base class for #NmtEditorPage subclasses
+ * representing device-type-specific data. (Eg, #NmtPageEthernet,
+ * #NmtPageVlan, etc).
+ *
+ * FIXME: rename to NmtEditorPageDevice, so it doesn't sound like it's
+ * an actual page type.
+ */
+
+#include "config.h"
+
+#include "nmt-page-device.h"
+
+G_DEFINE_TYPE (NmtPageDevice, nmt_page_device, NMT_TYPE_EDITOR_PAGE)
+
+#define NMT_PAGE_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_DEVICE, NmtPageDevicePrivate))
+
+typedef struct {
+ NmtDeviceEntry *device_entry;
+ gboolean show_by_default;
+} NmtPageDevicePrivate;
+
+enum {
+ PROP_0,
+
+ PROP_DEVICE_ENTRY,
+ PROP_SHOW_BY_DEFAULT,
+
+ LAST_PROP
+};
+
+static void
+nmt_page_device_init (NmtPageDevice *device)
+{
+}
+
+static void
+nmt_page_device_finalize (GObject *object)
+{
+ NmtPageDevicePrivate *priv = NMT_PAGE_DEVICE_GET_PRIVATE (object);
+
+ g_clear_object (&priv->device_entry);
+
+ G_OBJECT_CLASS (nmt_page_device_parent_class)->finalize (object);
+}
+
+NmtDeviceEntry *
+nmt_page_device_get_device_entry (NmtPageDevice *page)
+{
+ NmtPageDevicePrivate *priv = NMT_PAGE_DEVICE_GET_PRIVATE (page);
+
+ return priv->device_entry;
+}
+
+gboolean
+nmt_page_device_get_show_by_default (NmtPageDevice *page)
+{
+ NmtPageDevicePrivate *priv = NMT_PAGE_DEVICE_GET_PRIVATE (page);
+
+ return priv->show_by_default;
+}
+
+static void
+nmt_page_device_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtPageDevicePrivate *priv = NMT_PAGE_DEVICE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DEVICE_ENTRY:
+ priv->device_entry = g_value_dup_object (value);
+ break;
+ case PROP_SHOW_BY_DEFAULT:
+ priv->show_by_default = g_value_get_boolean (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_page_device_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtPageDevicePrivate *priv = NMT_PAGE_DEVICE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_DEVICE_ENTRY:
+ g_value_set_object (value, priv->device_entry);
+ break;
+ case PROP_SHOW_BY_DEFAULT:
+ g_value_set_boolean (value, priv->show_by_default);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_page_device_class_init (NmtPageDeviceClass *page_device_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (page_device_class);
+
+ g_type_class_add_private (page_device_class, sizeof (NmtPageDevicePrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_page_device_set_property;
+ object_class->get_property = nmt_page_device_get_property;
+ object_class->finalize = nmt_page_device_finalize;
+
+ /* properties */
+ g_object_class_install_property
+ (object_class, PROP_DEVICE_ENTRY,
+ g_param_spec_object ("device-entry", "", "",
+ NMT_TYPE_DEVICE_ENTRY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property
+ (object_class, PROP_SHOW_BY_DEFAULT,
+ g_param_spec_boolean ("show-by-default", "", "",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-page-device.h b/clients/tui/nmt-page-device.h
new file mode 100644
index 0000000000..8c90a67a08
--- /dev/null
+++ b/clients/tui/nmt-page-device.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_DEVICE_H
+#define NMT_PAGE_DEVICE_H
+
+#include "nmt-editor-page.h"
+#include "nmt-device-entry.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_DEVICE (nmt_page_device_get_type ())
+#define NMT_PAGE_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_DEVICE, NmtPageDevice))
+#define NMT_PAGE_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_DEVICE, NmtPageDeviceClass))
+#define NMT_IS_PAGE_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_DEVICE))
+#define NMT_IS_PAGE_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_DEVICE))
+#define NMT_PAGE_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_DEVICE, NmtPageDeviceClass))
+
+typedef struct {
+ NmtEditorPage parent;
+
+} NmtPageDevice;
+
+typedef struct {
+ NmtEditorPageClass parent;
+
+} NmtPageDeviceClass;
+
+GType nmt_page_device_get_type (void);
+
+NmtDeviceEntry *nmt_page_device_get_device_entry (NmtPageDevice *page);
+gboolean nmt_page_device_get_show_by_default (NmtPageDevice *page);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_DEVICE_H */
diff --git a/clients/tui/nmt-page-ethernet.c b/clients/tui/nmt-page-ethernet.c
new file mode 100644
index 0000000000..9eb08fe5b3
--- /dev/null
+++ b/clients/tui/nmt-page-ethernet.c
@@ -0,0 +1,97 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-ethernet
+ * @short_description: The editor page for Ethernet connections
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-ethernet.h"
+#include "nmt-mac-entry.h"
+#include "nmt-mtu-entry.h"
+
+G_DEFINE_TYPE (NmtPageEthernet, nmt_page_ethernet, NMT_TYPE_PAGE_DEVICE)
+
+NmtNewtWidget *
+nmt_page_ethernet_new (NMConnection *conn,
+ NmtDeviceEntry *deventry)
+{
+ return g_object_new (NMT_TYPE_PAGE_ETHERNET,
+ "connection", conn,
+ "title", _("ETHERNET"),
+ "device-entry", deventry,
+ "show-by-default", FALSE,
+ NULL);
+}
+
+static void
+nmt_page_ethernet_init (NmtPageEthernet *ethernet)
+{
+}
+
+static void
+nmt_page_ethernet_constructed (GObject *object)
+{
+ NmtPageEthernet *ethernet = NMT_PAGE_ETHERNET (object);
+ NmtDeviceEntry *deventry;
+ NmtPageGrid *grid;
+ NMSettingWired *s_wired;
+ NmtNewtWidget *widget;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ethernet));
+ s_wired = nm_connection_get_setting_wired (conn);
+ if (!s_wired) {
+ nm_connection_add_setting (conn, nm_setting_wired_new ());
+ s_wired = nm_connection_get_setting_wired (conn);
+ }
+
+ deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object));
+ g_object_bind_property (s_wired, NM_SETTING_WIRED_MAC_ADDRESS,
+ deventry, "mac-address",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ grid = NMT_PAGE_GRID (ethernet);
+
+ widget = nmt_mac_entry_new (40, ETH_ALEN);
+ g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
+ widget, "mac-address",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Cloned MAC address"), widget, NULL);
+
+ widget = nmt_mtu_entry_new ();
+ g_object_bind_property (s_wired, NM_SETTING_WIRED_MTU,
+ widget, "mtu",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("MTU"), widget, NULL);
+
+ G_OBJECT_CLASS (nmt_page_ethernet_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_ethernet_class_init (NmtPageEthernetClass *ethernet_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (ethernet_class);
+
+ object_class->constructed = nmt_page_ethernet_constructed;
+}
diff --git a/clients/tui/nmt-page-ethernet.h b/clients/tui/nmt-page-ethernet.h
new file mode 100644
index 0000000000..5e001562f7
--- /dev/null
+++ b/clients/tui/nmt-page-ethernet.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_ETHERNET_H
+#define NMT_PAGE_ETHERNET_H
+
+#include "nmt-page-device.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_ETHERNET (nmt_page_ethernet_get_type ())
+#define NMT_PAGE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernet))
+#define NMT_PAGE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernetClass))
+#define NMT_IS_PAGE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_ETHERNET))
+#define NMT_IS_PAGE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_ETHERNET))
+#define NMT_PAGE_ETHERNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernetClass))
+
+typedef struct {
+ NmtPageDevice parent;
+
+} NmtPageEthernet;
+
+typedef struct {
+ NmtPageDeviceClass parent;
+
+} NmtPageEthernetClass;
+
+GType nmt_page_ethernet_get_type (void);
+
+NmtNewtWidget *nmt_page_ethernet_new (NMConnection *conn,
+ NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_ETHERNET_H */
diff --git a/clients/tui/nmt-page-grid.c b/clients/tui/nmt-page-grid.c
new file mode 100644
index 0000000000..89ba9330f4
--- /dev/null
+++ b/clients/tui/nmt-page-grid.c
@@ -0,0 +1,458 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-grid
+ * @short_description: Grid widget for #NmtEditorPages
+ *
+ * #NmtPageGrid is the layout grid used by #NmtEditorPages. It
+ * consists of a number of rows, each containing either a single
+ * widget that spans the entire width of the row, or else containing a
+ * label, a widget, and an optional extra widget.
+ *
+ * Each row of the grid can take up multiple on-screen rows, if
+ * its main widget is multiple rows high. The label and extra widgets
+ * will be top-aligned if the row is taller than they are.
+ *
+ * The #NmtPageGrids in a form behave as though they are all in a
+ * "size group" together; they will all use the same column widths,
+ * which will be wide enough for the widest labels/widgets in any of
+ * the grids. #NmtPageGrid is also specially aware of #NmtNewtSection,
+ * and grids inside sections will automatically take the size of the
+ * section border into account as well.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-page-grid.h"
+
+G_DEFINE_TYPE (NmtPageGrid, nmt_page_grid, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_PAGE_GRID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_GRID, NmtPageGridPrivate))
+
+typedef struct {
+ GArray *rows;
+ int *row_heights;
+ int indent;
+} NmtPageGridPrivate;
+
+typedef struct {
+ NmtNewtWidget *label;
+ NmtNewtWidget *widget;
+ NmtNewtWidget *extra;
+ NmtPageGridRowFlags flags;
+} NmtPageGridRow;
+
+typedef struct {
+ int col_widths[3];
+} NmtPageGridFormState;
+
+/**
+ * nmt_page_grid_new:
+ *
+ * Creates a new #NmtPageGrid
+ *
+ * Returns: a new #NmtPageGrid
+ */
+NmtNewtWidget *
+nmt_page_grid_new (void)
+{
+ return g_object_new (NMT_TYPE_PAGE_GRID,
+ NULL);
+}
+
+static void
+nmt_page_grid_init (NmtPageGrid *grid)
+{
+ NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (grid);
+
+ priv->rows = g_array_new (FALSE, TRUE, sizeof (NmtPageGridRow));
+}
+
+static void
+nmt_page_grid_finalize (GObject *object)
+{
+ NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (object);
+
+ g_array_unref (priv->rows);
+ g_clear_pointer (&priv->row_heights, g_free);
+
+ G_OBJECT_CLASS (nmt_page_grid_parent_class)->finalize (object);
+}
+
+/**
+ * nmt_page_grid_append:
+ * @grid: the #NmtPageGrid
+ * @label: (allow-none): the label text for @widget, or %NULL
+ * @widget: the (main) widget
+ * @extra: (allow-none): optional extra widget
+ *
+ * Adds a row to @grid.
+ *
+ * If @label is non-%NULL, this will add a three-column row,
+ * containing a right-aligned #NmtNewtLabel in the first column,
+ * @widget in the second column, and @extra (if non-%NULL) in
+ * the third column.
+ *
+ * If @label is %NULL, then this will add a row with a single
+ * grid-spanning column, containing @widget.
+ *
+ * FIXME: That's sort of weird.
+ *
+ * See also nmt_page_grid_set_row_flags().
+ */
+void
+nmt_page_grid_append (NmtPageGrid *grid,
+ const char *label,
+ NmtNewtWidget *widget,
+ NmtNewtWidget *extra)
+{
+ NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (grid);
+ NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_page_grid_parent_class);
+ NmtNewtContainer *container = NMT_NEWT_CONTAINER (grid);
+ NmtPageGridRow row;
+
+ memset (&row, 0, sizeof (row));
+
+ if (label) {
+ row.label = nmt_newt_label_new (label);
+ parent_class->add (container, row.label);
+ }
+
+ row.widget = widget;
+ parent_class->add (container, widget);
+ if (row.label) {
+ g_object_bind_property (row.widget, "valid",
+ row.label, "highlight",
+ G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
+ }
+
+ if (extra) {
+ row.extra = extra;
+ parent_class->add (container, extra);
+ }
+
+ g_array_append_val (priv->rows, row);
+}
+
+static int
+nmt_page_grid_find_widget (NmtPageGrid *grid,
+ NmtNewtWidget *widget)
+{
+ NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (grid);
+ NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data;
+ int i;
+
+ for (i = 0; i < priv->rows->len; i++) {
+ if (rows[i].label == widget || rows[i].widget == widget || rows[i].extra == widget)
+ return i;
+ }
+
+ return -1;
+}
+
+/**
+ * NmtPageGridRowFlags:
+ * @NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT: the row's label should be
+ * aligned left instead of right.
+ * @NMT_PAGE_GRID_ROW_EXTRA_ALIGN_RIGHT: the row's extra widget
+ * should be aligned right instead of left.
+ *
+ * Flags to alter an #NmtPageGrid row's layout.
+ */
+
+/**
+ * nmt_page_grid_set_row_flags:
+ * @grid: an #NmtPageGrid
+ * @widget: the widget whose row you want to adjust
+ * @flags: the flags to set
+ *
+ * Sets flags to adjust the layout of @widget's row in @grid.
+ */
+void
+nmt_page_grid_set_row_flags (NmtPageGrid *grid,
+ NmtNewtWidget *widget,
+ NmtPageGridRowFlags flags)
+{
+ NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (grid);
+ NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data;
+ int i;
+
+ i = nmt_page_grid_find_widget (grid, widget);
+ if (i != -1)
+ rows[i].flags = flags;
+}
+
+static void
+nmt_page_grid_remove (NmtNewtContainer *container,
+ NmtNewtWidget *widget)
+{
+ NmtPageGrid *grid = NMT_PAGE_GRID (container);
+ NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (grid);
+ NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_page_grid_parent_class);
+ NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data;
+ int i;
+
+ i = nmt_page_grid_find_widget (grid, widget);
+ if (i != -1) {
+ if (rows[i].label)
+ parent_class->remove (container, rows[i].label);
+ parent_class->remove (container, rows[i].widget);
+ if (rows[i].extra)
+ parent_class->remove (container, rows[i].extra);
+
+ g_array_remove_index (priv->rows, i);
+ return;
+ }
+
+ // FIXME: shouldn't happen
+ parent_class->remove (container, widget);
+}
+
+static newtComponent *
+nmt_page_grid_get_components (NmtNewtWidget *widget)
+{
+ NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (widget);
+ NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data;
+ newtComponent *child_cos;
+ GPtrArray *cos;
+ int i, c;
+
+ cos = g_ptr_array_new ();
+
+ for (i = 0; i < priv->rows->len; i++) {
+ if (!nmt_newt_widget_get_visible (rows[i].widget))
+ continue;
+
+ if (rows[i].label) {
+ child_cos = nmt_newt_widget_get_components (rows[i].label);
+ g_assert (child_cos[0] && !child_cos[1]);
+ g_ptr_array_add (cos, child_cos[0]);
+ g_free (child_cos);
+ }
+
+ child_cos = nmt_newt_widget_get_components (rows[i].widget);
+ for (c = 0; child_cos[c]; c++)
+ g_ptr_array_add (cos, child_cos[c]);
+ g_free (child_cos);
+
+ if (rows[i].extra) {
+ child_cos = nmt_newt_widget_get_components (rows[i].extra);
+ for (c = 0; child_cos[c]; c++)
+ g_ptr_array_add (cos, child_cos[c]);
+ g_free (child_cos);
+ }
+ }
+
+ g_ptr_array_add (cos, NULL);
+ return (newtComponent *) g_ptr_array_free (cos, FALSE);
+}
+
+static NmtPageGridFormState *
+get_form_state (NmtNewtWidget *widget)
+{
+ NmtNewtForm *form = nmt_newt_widget_get_form (widget);
+ NmtPageGridFormState *state;
+
+ if (!form)
+ return NULL;
+
+ state = g_object_get_data (G_OBJECT (form), "NmtPageGridFormState");
+ if (state)
+ return state;
+
+ state = g_new0 (NmtPageGridFormState, 1);
+ g_object_set_data_full (G_OBJECT (form), "NmtPageGridFormState", state, g_free);
+ return state;
+}
+
+static void
+nmt_page_grid_realize (NmtNewtWidget *widget)
+{
+ NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (widget);
+ NmtNewtWidget *parent;
+
+ NMT_NEWT_WIDGET_CLASS (nmt_page_grid_parent_class)->realize (widget);
+
+ /* This is a hack, but it's the simplest way to make it work... */
+ priv->indent = 0;
+
+ parent = nmt_newt_widget_get_parent (widget);
+ while (parent) {
+ if (NMT_IS_NEWT_SECTION (parent)) {
+ priv->indent = 2;
+ break;
+ }
+ parent = nmt_newt_widget_get_parent (parent);
+ }
+}
+
+static void
+nmt_page_grid_unrealize (NmtNewtWidget *widget)
+{
+ NmtPageGridFormState *state = get_form_state (widget);
+
+ if (state)
+ memset (state->col_widths, 0, sizeof (state->col_widths));
+
+ NMT_NEWT_WIDGET_CLASS (nmt_page_grid_parent_class)->unrealize (widget);
+}
+
+static void
+nmt_page_grid_size_request (NmtNewtWidget *widget,
+ int *width,
+ int *height)
+{
+ NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (widget);
+ NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data;
+ NmtPageGridFormState *state = get_form_state (widget);
+ gboolean add_padding = FALSE;
+ int i;
+
+ g_free (priv->row_heights);
+ priv->row_heights = g_new0 (int, priv->rows->len);
+
+ *height = 0;
+ for (i = 0; i < priv->rows->len; i++) {
+ int lwidth, lheight, wwidth, wheight, ewidth, eheight;
+
+ if (!nmt_newt_widget_get_visible (rows[i].widget))
+ continue;
+
+ if (rows[i].label) {
+ nmt_newt_widget_size_request (rows[i].label, &lwidth, &lheight);
+ lwidth += priv->indent;
+ state->col_widths[0] = MAX (state->col_widths[0], lwidth);
+
+ nmt_newt_widget_size_request (rows[i].widget, &wwidth, &wheight);
+ state->col_widths[1] = MAX (state->col_widths[1], wwidth);
+ priv->row_heights[i] = wheight;
+
+ add_padding = TRUE;
+ } else {
+ nmt_newt_widget_size_request (rows[i].widget, &wwidth, &wheight);
+ priv->row_heights[i] = wheight;
+ }
+
+ if (rows[i].extra) {
+ nmt_newt_widget_size_request (rows[i].extra, &ewidth, &eheight);
+ state->col_widths[2] = MAX (state->col_widths[2], ewidth);
+ priv->row_heights[i] = MAX (priv->row_heights[i], eheight);
+ }
+
+ *height += priv->row_heights[i];
+ }
+
+ *width = state->col_widths[0] + state->col_widths[1] + state->col_widths[2];
+ if (add_padding)
+ *width += 2;
+}
+
+
+static void
+nmt_page_grid_size_allocate (NmtNewtWidget *widget,
+ int x,
+ int y,
+ int width,
+ int height)
+{
+ NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (widget);
+ NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data;
+ NmtPageGridFormState *state = get_form_state (widget);
+ int col0_width, col1_width, col2_width;
+ int i, row;
+
+ col0_width = state->col_widths[0] - priv->indent;
+ col1_width = state->col_widths[1];
+ col2_width = state->col_widths[2];
+
+ for (i = row = 0; i < priv->rows->len; i++) {
+ if (!nmt_newt_widget_get_visible (rows[i].widget))
+ continue;
+
+ if (rows[i].label) {
+ int lwidth, lheight, lx;
+
+ if (rows[i].flags & NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT)
+ lx = x;
+ else {
+ nmt_newt_widget_size_request (rows[i].label, &lwidth, &lheight);
+ lx = x + col0_width - lwidth;
+ }
+
+ nmt_newt_widget_size_allocate (rows[i].label,
+ lx,
+ y + row,
+ col0_width,
+ priv->row_heights[i]);
+
+ nmt_newt_widget_size_allocate (rows[i].widget,
+ x + col0_width + 1,
+ y + row,
+ col1_width,
+ priv->row_heights[i]);
+ if (rows[i].extra) {
+ int wwidth, wheight, ex;
+
+ if (rows[i].flags & NMT_PAGE_GRID_ROW_EXTRA_ALIGN_RIGHT)
+ ex = x + col0_width + col1_width + 2;
+ else {
+ nmt_newt_widget_size_request (rows[i].widget, &wwidth, &wheight);
+ ex = x + col0_width + wwidth + 2;
+ }
+
+ nmt_newt_widget_size_allocate (rows[i].extra,
+ ex,
+ y + row,
+ col2_width,
+ priv->row_heights[i]);
+ }
+ } else {
+ nmt_newt_widget_size_allocate (rows[i].widget,
+ x,
+ y + row,
+ col0_width + col1_width + col2_width + 2,
+ priv->row_heights[i]);
+ }
+
+ row += priv->row_heights[i];
+ }
+}
+
+static void
+nmt_page_grid_class_init (NmtPageGridClass *grid_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (grid_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (grid_class);
+ NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (grid_class);
+
+ g_type_class_add_private (grid_class, sizeof (NmtPageGridPrivate));
+
+ /* virtual methods */
+ object_class->finalize = nmt_page_grid_finalize;
+
+ widget_class->realize = nmt_page_grid_realize;
+ widget_class->unrealize = nmt_page_grid_unrealize;
+ widget_class->get_components = nmt_page_grid_get_components;
+ widget_class->size_request = nmt_page_grid_size_request;
+ widget_class->size_allocate = nmt_page_grid_size_allocate;
+
+ container_class->remove = nmt_page_grid_remove;
+}
diff --git a/clients/tui/nmt-page-grid.h b/clients/tui/nmt-page-grid.h
new file mode 100644
index 0000000000..40ff96c410
--- /dev/null
+++ b/clients/tui/nmt-page-grid.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_GRID_H
+#define NMT_PAGE_GRID_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_GRID (nmt_page_grid_get_type ())
+#define NMT_PAGE_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_GRID, NmtPageGrid))
+#define NMT_PAGE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_GRID, NmtPageGridClass))
+#define NMT_IS_PAGE_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_GRID))
+#define NMT_IS_PAGE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_GRID))
+#define NMT_PAGE_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_GRID, NmtPageGridClass))
+
+typedef struct {
+ NmtNewtContainer parent;
+
+} NmtPageGrid;
+
+typedef struct {
+ NmtNewtContainerClass parent;
+
+} NmtPageGridClass;
+
+GType nmt_page_grid_get_type (void);
+
+typedef enum {
+ NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT = (1 << 0),
+ NMT_PAGE_GRID_ROW_EXTRA_ALIGN_RIGHT = (1 << 1)
+} NmtPageGridRowFlags;
+
+NmtNewtWidget *nmt_page_grid_new (void);
+
+void nmt_page_grid_append (NmtPageGrid *grid,
+ const char *label,
+ NmtNewtWidget *widget,
+ NmtNewtWidget *extra);
+void nmt_page_grid_set_row_flags (NmtPageGrid *grid,
+ NmtNewtWidget *widget,
+ NmtPageGridRowFlags flags);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_GRID_H */
diff --git a/clients/tui/nmt-page-infiniband.c b/clients/tui/nmt-page-infiniband.c
new file mode 100644
index 0000000000..59fa68000f
--- /dev/null
+++ b/clients/tui/nmt-page-infiniband.c
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-infiniband
+ * @short_description: The editor page for InfiniBand connections
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-infiniband.h"
+#include "nmt-mtu-entry.h"
+
+G_DEFINE_TYPE (NmtPageInfiniband, nmt_page_infiniband, NMT_TYPE_PAGE_DEVICE)
+
+NmtNewtWidget *
+nmt_page_infiniband_new (NMConnection *conn,
+ NmtDeviceEntry *deventry)
+{
+ return g_object_new (NMT_TYPE_PAGE_INFINIBAND,
+ "connection", conn,
+ "title", _("INFINIBAND"),
+ "device-entry", deventry,
+ NULL);
+}
+
+static void
+nmt_page_infiniband_init (NmtPageInfiniband *infiniband)
+{
+}
+
+static NmtNewtPopupEntry transport_mode[] = {
+ { N_("Datagram"), "datagram" },
+ { N_("Connected"), "connected" },
+ { NULL, NULL }
+};
+
+static void
+nmt_page_infiniband_constructed (GObject *object)
+{
+ NmtPageInfiniband *infiniband = NMT_PAGE_INFINIBAND (object);
+ NmtDeviceEntry *deventry;
+ NmtPageGrid *grid;
+ NMSettingInfiniband *s_ib;
+ NmtNewtWidget *widget;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (infiniband));
+ s_ib = nm_connection_get_setting_infiniband (conn);
+ if (!s_ib) {
+ nm_connection_add_setting (conn, nm_setting_infiniband_new ());
+ s_ib = nm_connection_get_setting_infiniband (conn);
+ }
+ /* initialize 'transport-mode' if it is NULL */
+ if (!nm_setting_infiniband_get_transport_mode (s_ib)) {
+ g_object_set (G_OBJECT (s_ib),
+ NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
+ NULL);
+ }
+
+ deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object));
+ g_object_bind_property (s_ib, NM_SETTING_INFINIBAND_MAC_ADDRESS,
+ deventry, "mac-address",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ grid = NMT_PAGE_GRID (infiniband);
+
+ widget = nmt_newt_popup_new (transport_mode);
+ g_object_bind_property (s_ib, NM_SETTING_INFINIBAND_TRANSPORT_MODE,
+ widget, "active-id",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Transport mode"), widget, NULL);
+
+ widget = nmt_mtu_entry_new ();
+ g_object_bind_property (s_ib, NM_SETTING_INFINIBAND_MTU,
+ widget, "mtu",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("MTU"), widget, NULL);
+
+ G_OBJECT_CLASS (nmt_page_infiniband_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_infiniband_class_init (NmtPageInfinibandClass *infiniband_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (infiniband_class);
+
+ object_class->constructed = nmt_page_infiniband_constructed;
+}
diff --git a/clients/tui/nmt-page-infiniband.h b/clients/tui/nmt-page-infiniband.h
new file mode 100644
index 0000000000..706b7b60c3
--- /dev/null
+++ b/clients/tui/nmt-page-infiniband.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_INFINIBAND_H
+#define NMT_PAGE_INFINIBAND_H
+
+#include "nmt-page-device.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_INFINIBAND (nmt_page_infiniband_get_type ())
+#define NMT_PAGE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfiniband))
+#define NMT_PAGE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfinibandClass))
+#define NMT_IS_PAGE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_INFINIBAND))
+#define NMT_IS_PAGE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_INFINIBAND))
+#define NMT_PAGE_INFINIBAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfinibandClass))
+
+typedef struct {
+ NmtPageDevice parent;
+
+} NmtPageInfiniband;
+
+typedef struct {
+ NmtPageDeviceClass parent;
+
+} NmtPageInfinibandClass;
+
+GType nmt_page_infiniband_get_type (void);
+
+NmtNewtWidget *nmt_page_infiniband_new (NMConnection *conn,
+ NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_INFINIBAND_H */
diff --git a/clients/tui/nmt-page-ip4.c b/clients/tui/nmt-page-ip4.c
new file mode 100644
index 0000000000..1c2d31744c
--- /dev/null
+++ b/clients/tui/nmt-page-ip4.c
@@ -0,0 +1,203 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-ip4
+ * @short_description: The editor page for IP4 configuration
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-ip4.h"
+#include "nmt-ip-entry.h"
+#include "nmt-address-list.h"
+#include "nmt-route-editor.h"
+
+#include "nm-editor-bindings.h"
+
+G_DEFINE_TYPE (NmtPageIP4, nmt_page_ip4, NMT_TYPE_EDITOR_PAGE)
+
+static NmtNewtPopupEntry ip4methods[] = {
+ { N_("Disabled"), NM_SETTING_IP4_CONFIG_METHOD_DISABLED },
+ { N_("Automatic"), NM_SETTING_IP4_CONFIG_METHOD_AUTO },
+ { N_("Link-Local"), NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL },
+ { N_("Manual"), NM_SETTING_IP4_CONFIG_METHOD_MANUAL },
+ { N_("Shared"), NM_SETTING_IP4_CONFIG_METHOD_SHARED },
+ { NULL, NULL }
+};
+
+NmtNewtWidget *
+nmt_page_ip4_new (NMConnection *conn)
+{
+ return g_object_new (NMT_TYPE_PAGE_IP4,
+ "connection", conn,
+ "title", _("IPv4 CONFIGURATION"),
+ NULL);
+}
+
+gboolean
+nmt_page_ip4_is_non_empty (NmtPageIP4 *ip4)
+{
+ NMConnection *conn;
+ NMSettingIP4Config *s_ip4;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ip4));
+ s_ip4 = nm_connection_get_setting_ip4_config (conn);
+ if ( !g_strcmp0 (nm_setting_ip4_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)
+ || nm_setting_ip4_config_get_num_addresses (s_ip4))
+ return TRUE;
+ return FALSE;
+}
+
+static void
+nmt_page_ip4_init (NmtPageIP4 *ip4)
+{
+}
+
+static void
+edit_routes (NmtNewtButton *button,
+ gpointer user_data)
+{
+ NMSetting *s_ip4 = user_data;
+ NmtNewtForm *form;
+
+ form = nmt_route_editor_new (s_ip4);
+ nmt_newt_form_run_sync (form);
+ g_object_unref (form);
+}
+
+static gboolean
+ip4_routes_transform_to_description (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GPtrArray *routes;
+ char *text;
+
+ routes = g_value_get_boxed (source_value);
+ if (!routes || !routes->len)
+ text = g_strdup (_("(No custom routes)"));
+ else {
+ text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
+ "One custom route",
+ "%d custom routes",
+ routes->len),
+ routes->len);
+ }
+
+ g_value_take_string (target_value, text);
+ return TRUE;
+}
+
+static void
+nmt_page_ip4_constructed (GObject *object)
+{
+ NmtPageIP4 *ip4 = NMT_PAGE_IP4 (object);
+ NmtPageGrid *grid;
+ NMSettingIP4Config *s_ip4;
+ NmtNewtWidget *widget, *button;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ip4));
+ s_ip4 = nm_connection_get_setting_ip4_config (conn);
+ if (!s_ip4) {
+ s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+ g_object_set (G_OBJECT (s_ip4),
+ NM_SETTING_IP4_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+ NULL);
+ nm_connection_add_setting (conn, (NMSetting *) s_ip4);
+ }
+
+ widget = nmt_newt_popup_new (ip4methods);
+ g_object_bind_property (s_ip4, NM_SETTING_IP4_CONFIG_METHOD,
+ widget, "active-id",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_editor_page_set_header_widget (NMT_EDITOR_PAGE (ip4), widget);
+
+ grid = NMT_PAGE_GRID (ip4);
+
+ widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4_WITH_PREFIX);
+ nm_editor_bind_ip4_addresses_with_prefix_to_strv (s_ip4, NM_SETTING_IP4_CONFIG_ADDRESSES,
+ widget, "strings",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Addresses"), widget, NULL);
+
+ widget = nmt_ip_entry_new (25, AF_INET, FALSE, TRUE);
+ nm_editor_bind_ip4_gateway_to_string (s_ip4, NM_SETTING_IP4_CONFIG_ADDRESSES,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Gateway"), widget, NULL);
+
+ widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4);
+ nm_editor_bind_ip4_addresses_to_strv (s_ip4, NM_SETTING_IP4_CONFIG_DNS,
+ widget, "strings",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("DNS servers"), widget, NULL);
+
+ widget = nmt_address_list_new (NMT_ADDRESS_LIST_HOSTNAME);
+ g_object_bind_property (s_ip4, NM_SETTING_IP4_CONFIG_DNS_SEARCH,
+ widget, "strings",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Search domains"), widget, NULL);
+
+ nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+ widget = g_object_new (NMT_TYPE_NEWT_LABEL,
+ "text", "",
+ "style", NMT_NEWT_LABEL_PLAIN,
+ NULL);
+ g_object_bind_property_full (s_ip4, NM_SETTING_IP4_CONFIG_ROUTES,
+ widget, "text",
+ G_BINDING_SYNC_CREATE,
+ ip4_routes_transform_to_description,
+ NULL, NULL, NULL);
+ button = nmt_newt_button_new (_("Edit..."));
+ g_signal_connect (button, "clicked", G_CALLBACK (edit_routes), s_ip4);
+ nmt_page_grid_append (grid, _("Routing"), widget, button);
+
+ widget = nmt_newt_checkbox_new (_("Never use this network for default route"));
+ g_object_bind_property (s_ip4, NM_SETTING_IP4_CONFIG_NEVER_DEFAULT,
+ widget, "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+
+ nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+ widget = nmt_newt_checkbox_new (_("Require IPv4 addressing for this connection"));
+ g_object_bind_property (s_ip4, NM_SETTING_IP4_CONFIG_MAY_FAIL,
+ widget, "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL |
+ G_BINDING_INVERT_BOOLEAN);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+
+ G_OBJECT_CLASS (nmt_page_ip4_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_ip4_class_init (NmtPageIP4Class *ip4_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (ip4_class);
+
+ object_class->constructed = nmt_page_ip4_constructed;
+}
diff --git a/clients/tui/nmt-page-ip4.h b/clients/tui/nmt-page-ip4.h
new file mode 100644
index 0000000000..f3bb933e0e
--- /dev/null
+++ b/clients/tui/nmt-page-ip4.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_IP4_H
+#define NMT_PAGE_IP4_H
+
+#include "nmt-editor-page.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_IP4 (nmt_page_ip4_get_type ())
+#define NMT_PAGE_IP4(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_IP4, NmtPageIP4))
+#define NMT_PAGE_IP4_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_IP4, NmtPageIP4Class))
+#define NMT_IS_PAGE_IP4(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_IP4))
+#define NMT_IS_PAGE_IP4_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_IP4))
+#define NMT_PAGE_IP4_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_IP4, NmtPageIP4Class))
+
+typedef struct {
+ NmtEditorPage parent;
+
+} NmtPageIP4;
+
+typedef struct {
+ NmtEditorPageClass parent;
+
+} NmtPageIP4Class;
+
+GType nmt_page_ip4_get_type (void);
+
+NmtNewtWidget *nmt_page_ip4_new (NMConnection *conn);
+
+gboolean nmt_page_ip4_is_non_empty (NmtPageIP4 *ip4);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_IP4_H */
diff --git a/clients/tui/nmt-page-ip6.c b/clients/tui/nmt-page-ip6.c
new file mode 100644
index 0000000000..ae859bd931
--- /dev/null
+++ b/clients/tui/nmt-page-ip6.c
@@ -0,0 +1,201 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-ip6
+ * @short_description: The editor page for IP6 configuration
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-ip6.h"
+#include "nmt-ip-entry.h"
+#include "nmt-address-list.h"
+#include "nmt-route-editor.h"
+
+#include "nm-editor-bindings.h"
+
+G_DEFINE_TYPE (NmtPageIP6, nmt_page_ip6, NMT_TYPE_EDITOR_PAGE)
+
+static NmtNewtPopupEntry ip6methods[] = {
+ { N_("Ignore"), NM_SETTING_IP6_CONFIG_METHOD_IGNORE },
+ { N_("Automatic"), NM_SETTING_IP6_CONFIG_METHOD_AUTO },
+ { N_("Automatic (DHCP-only)"), NM_SETTING_IP6_CONFIG_METHOD_DHCP },
+ { N_("Link-Local"), NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL },
+ { N_("Manual"), NM_SETTING_IP6_CONFIG_METHOD_MANUAL },
+ { NULL, NULL }
+};
+
+NmtNewtWidget *
+nmt_page_ip6_new (NMConnection *conn)
+{
+ return g_object_new (NMT_TYPE_PAGE_IP6,
+ "connection", conn,
+ "title", _("IPv6 CONFIGURATION"),
+ NULL);
+}
+
+gboolean
+nmt_page_ip6_is_non_empty (NmtPageIP6 *ip6)
+{
+ NMConnection *conn;
+ NMSettingIP6Config *s_ip6;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ip6));
+ s_ip6 = nm_connection_get_setting_ip6_config (conn);
+ if ( !g_strcmp0 (nm_setting_ip6_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)
+ || nm_setting_ip6_config_get_num_addresses (s_ip6))
+ return TRUE;
+ return FALSE;
+}
+
+static void
+nmt_page_ip6_init (NmtPageIP6 *ip6)
+{
+}
+
+static void
+edit_routes (NmtNewtButton *button,
+ gpointer user_data)
+{
+ NMSetting *s_ip6 = user_data;
+ NmtNewtForm *form;
+
+ form = nmt_route_editor_new (s_ip6);
+ nmt_newt_form_run_sync (form);
+ g_object_unref (form);
+}
+
+static gboolean
+ip6_routes_transform_to_description (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GPtrArray *routes;
+ char *text;
+
+ routes = g_value_get_boxed (source_value);
+ if (!routes || !routes->len)
+ text = g_strdup (_("(No custom routes)"));
+ else {
+ text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE,
+ "One custom route",
+ "%d custom routes",
+ routes->len),
+ routes->len);
+ }
+
+ g_value_take_string (target_value, text);
+ return TRUE;
+}
+
+static void
+nmt_page_ip6_constructed (GObject *object)
+{
+ NmtPageIP6 *ip6 = NMT_PAGE_IP6 (object);
+ NmtPageGrid *grid;
+ NMSettingIP6Config *s_ip6;
+ NmtNewtWidget *widget, *button;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ip6));
+ s_ip6 = nm_connection_get_setting_ip6_config (conn);
+ if (!s_ip6) {
+ s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new ();
+ g_object_set (G_OBJECT (s_ip6),
+ NM_SETTING_IP6_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+ NULL);
+ nm_connection_add_setting (conn, (NMSetting *) s_ip6);
+ }
+
+ widget = nmt_newt_popup_new (ip6methods);
+ g_object_bind_property (s_ip6, NM_SETTING_IP6_CONFIG_METHOD,
+ widget, "active-id",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_editor_page_set_header_widget (NMT_EDITOR_PAGE (ip6), widget);
+
+ grid = NMT_PAGE_GRID (ip6);
+
+ widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP6_WITH_PREFIX);
+ nm_editor_bind_ip6_addresses_with_prefix_to_strv (s_ip6, NM_SETTING_IP6_CONFIG_ADDRESSES,
+ widget, "strings",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Addresses"), widget, NULL);
+
+ widget = nmt_ip_entry_new (25, AF_INET6, FALSE, TRUE);
+ nm_editor_bind_ip6_gateway_to_string (s_ip6, NM_SETTING_IP6_CONFIG_ADDRESSES,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Gateway"), widget, NULL);
+
+ widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP6);
+ nm_editor_bind_ip6_addresses_to_strv (s_ip6, NM_SETTING_IP6_CONFIG_DNS,
+ widget, "strings",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("DNS servers"), widget, NULL);
+
+ widget = nmt_address_list_new (NMT_ADDRESS_LIST_HOSTNAME);
+ g_object_bind_property (s_ip6, NM_SETTING_IP6_CONFIG_DNS_SEARCH,
+ widget, "strings",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Search domains"), widget, NULL);
+
+ widget = g_object_new (NMT_TYPE_NEWT_LABEL,
+ "text", "",
+ "style", NMT_NEWT_LABEL_PLAIN,
+ NULL);
+ g_object_bind_property_full (s_ip6, NM_SETTING_IP6_CONFIG_ROUTES,
+ widget, "text",
+ G_BINDING_SYNC_CREATE,
+ ip6_routes_transform_to_description,
+ NULL, NULL, NULL);
+ button = nmt_newt_button_new (_("Edit..."));
+ g_signal_connect (button, "clicked", G_CALLBACK (edit_routes), s_ip6);
+ nmt_page_grid_append (grid, _("Routing"), widget, button);
+
+ widget = nmt_newt_checkbox_new (_("Never use this network for default route"));
+ g_object_bind_property (s_ip6, NM_SETTING_IP6_CONFIG_NEVER_DEFAULT,
+ widget, "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+
+ nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+ widget = nmt_newt_checkbox_new (_("Require IPv6 addressing for this connection"));
+ g_object_bind_property (s_ip6, NM_SETTING_IP6_CONFIG_MAY_FAIL,
+ widget, "active",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL |
+ G_BINDING_INVERT_BOOLEAN);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+
+ G_OBJECT_CLASS (nmt_page_ip6_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_ip6_class_init (NmtPageIP6Class *ip6_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (ip6_class);
+
+ object_class->constructed = nmt_page_ip6_constructed;
+}
diff --git a/clients/tui/nmt-page-ip6.h b/clients/tui/nmt-page-ip6.h
new file mode 100644
index 0000000000..d0d2bfa298
--- /dev/null
+++ b/clients/tui/nmt-page-ip6.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_IP6_H
+#define NMT_PAGE_IP6_H
+
+#include "nmt-editor-page.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_IP6 (nmt_page_ip6_get_type ())
+#define NMT_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_IP6, NmtPageIP6))
+#define NMT_PAGE_IP6_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_IP6, NmtPageIP6Class))
+#define NMT_IS_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_IP6))
+#define NMT_IS_PAGE_IP6_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_IP6))
+#define NMT_PAGE_IP6_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_IP6, NmtPageIP6Class))
+
+typedef struct {
+ NmtEditorPage parent;
+
+} NmtPageIP6;
+
+typedef struct {
+ NmtEditorPageClass parent;
+
+} NmtPageIP6Class;
+
+GType nmt_page_ip6_get_type (void);
+
+NmtNewtWidget *nmt_page_ip6_new (NMConnection *conn);
+
+gboolean nmt_page_ip6_is_non_empty (NmtPageIP6 *ip6);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_IP6_H */
diff --git a/clients/tui/nmt-page-main.c b/clients/tui/nmt-page-main.c
new file mode 100644
index 0000000000..d22ec83459
--- /dev/null
+++ b/clients/tui/nmt-page-main.c
@@ -0,0 +1,328 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-main
+ * @short_description: The top-level #NmtEditorPage for a connection
+ *
+ * #NmtPageMain is the top-level #NmtEditorPage for a connection. It
+ * handles #NMSettingConnection properties, and embeds the other pages
+ * within itself.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <nm-device.h>
+#include <nm-utils.h>
+
+#include "nmt-page-main.h"
+#include "nmt-device-entry.h"
+#include "nmt-mac-entry.h"
+#include "nmt-mtu-entry.h"
+#include "nmtui.h"
+
+#include "nmt-page-bond.h"
+#include "nmt-page-bridge.h"
+#include "nmt-page-bridge-port.h"
+#include "nmt-page-ethernet.h"
+#include "nmt-page-infiniband.h"
+#include "nmt-page-ip4.h"
+#include "nmt-page-ip6.h"
+#include "nmt-page-team.h"
+#include "nmt-page-team-port.h"
+#include "nmt-page-vlan.h"
+#include "nmt-page-wifi.h"
+
+G_DEFINE_TYPE (NmtPageMain, nmt_page_main, NMT_TYPE_EDITOR_PAGE)
+
+#define NMT_PAGE_MAIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_MAIN, NmtPageMainPrivate))
+
+typedef struct {
+ NMEditorConnectionTypeData *type_data;
+} NmtPageMainPrivate;
+
+enum {
+ PROP_0,
+
+ PROP_TYPE_DATA,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_page_main_new:
+ * @conn: the #NMConnection to display
+ * @type_data: @conn's #NMEditorConnectionTypeData
+ *
+ * Creates a new #NmtPageMain
+ *
+ * Returns: a new #NmtPageMain
+ */
+NmtNewtWidget *
+nmt_page_main_new (NMConnection *conn,
+ NMEditorConnectionTypeData *type_data)
+{
+ return g_object_new (NMT_TYPE_PAGE_MAIN,
+ "connection", conn,
+ "type-data", type_data,
+ NULL);
+}
+
+static void
+nmt_page_main_init (NmtPageMain *page)
+{
+}
+
+static gboolean
+permissions_transform_to_allusers (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GSList *perms = g_value_get_boxed (source_value);
+
+ g_value_set_boolean (target_value, perms == NULL);
+ return TRUE;
+}
+
+static gboolean
+permissions_transform_from_allusers (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ gboolean allusers = g_value_get_boolean (source_value);
+ GSList *perms = NULL;
+
+ if (allusers) {
+ char *perm = g_strdup_printf ("user:%s:", g_get_user_name ());
+
+ perms = g_slist_prepend (perms, perm);
+ }
+ g_value_take_boxed (target_value, perms);
+ return TRUE;
+}
+
+static NmtNewtWidget *
+build_section_for_page (NmtEditorPage *page,
+ gboolean open)
+{
+ NmtNewtWidget *section, *header, *toggle;
+
+ g_return_val_if_fail (nmt_newt_widget_get_parent (NMT_NEWT_WIDGET (page)) == NULL, NULL);
+
+ section = nmt_newt_section_new ();
+
+ toggle = nmt_newt_toggle_button_new (_("Hide"), _("Show"));
+
+ header = nmt_page_grid_new ();
+ nmt_page_grid_append (NMT_PAGE_GRID (header),
+ nmt_editor_page_get_title (page),
+ nmt_editor_page_get_header_widget (page),
+ toggle);
+ nmt_page_grid_set_row_flags (NMT_PAGE_GRID (header),
+ nmt_editor_page_get_header_widget (page),
+ NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT |
+ NMT_PAGE_GRID_ROW_EXTRA_ALIGN_RIGHT);
+ nmt_newt_section_set_header (NMT_NEWT_SECTION (section), header);
+
+ nmt_newt_section_set_body (NMT_NEWT_SECTION (section), NMT_NEWT_WIDGET (page));
+
+ g_object_bind_property (toggle, "active",
+ section, "open",
+ G_BINDING_SYNC_CREATE);
+
+ if (open || !nmt_newt_widget_get_valid (section))
+ nmt_newt_toggle_button_set_active (NMT_NEWT_TOGGLE_BUTTON (toggle), TRUE);
+
+ return section;
+}
+
+static void
+nmt_page_main_constructed (GObject *object)
+{
+ NmtPageMain *page_main = NMT_PAGE_MAIN (object);
+ NmtPageMainPrivate *priv = NMT_PAGE_MAIN_GET_PRIVATE (page_main);
+ NmtPageGrid *grid;
+ NMConnection *conn;
+ NMSettingConnection *s_con;
+ NmtNewtWidget *widget, *section, *page, *separator;
+ NmtDeviceEntry *deventry;
+ GType hardware_type;
+ const char *slave_type;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (page_main));
+ s_con = nm_connection_get_setting_connection (conn);
+
+ grid = NMT_PAGE_GRID (page_main);
+
+ widget = nmt_newt_entry_new (40, NMT_NEWT_ENTRY_NONEMPTY);
+ g_object_bind_property (s_con, NM_SETTING_CONNECTION_ID,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Profile name"), widget, NULL);
+
+ if (priv->type_data->virtual)
+ hardware_type = G_TYPE_NONE;
+ else
+ hardware_type = priv->type_data->device_type;
+
+ widget = nmt_device_entry_new (_("Device"), 40, hardware_type);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+ deventry = NMT_DEVICE_ENTRY (widget);
+ g_object_bind_property (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME,
+ deventry, "interface-name",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+ if (nm_connection_is_type (conn, NM_SETTING_BOND_SETTING_NAME))
+ page = nmt_page_bond_new (conn, deventry);
+ else if (nm_connection_is_type (conn, NM_SETTING_BRIDGE_SETTING_NAME))
+ page = nmt_page_bridge_new (conn, deventry);
+ else if (nm_connection_is_type (conn, NM_SETTING_INFINIBAND_SETTING_NAME))
+ page = nmt_page_infiniband_new (conn, deventry);
+ else if (nm_connection_is_type (conn, NM_SETTING_TEAM_SETTING_NAME))
+ page = nmt_page_team_new (conn, deventry);
+ else if (nm_connection_is_type (conn, NM_SETTING_VLAN_SETTING_NAME))
+ page = nmt_page_vlan_new (conn, deventry);
+ else if (nm_connection_is_type (conn, NM_SETTING_WIRED_SETTING_NAME))
+ page = nmt_page_ethernet_new (conn, deventry);
+ else if (nm_connection_is_type (conn, NM_SETTING_WIRELESS_SETTING_NAME))
+ page = nmt_page_wifi_new (conn, deventry);
+ else
+ page = NULL;
+
+ if (page) {
+ gboolean show_by_default = nmt_page_device_get_show_by_default (NMT_PAGE_DEVICE (page));
+
+ section = build_section_for_page (NMT_EDITOR_PAGE (page), show_by_default);
+ nmt_page_grid_append (grid, NULL, section, NULL);
+ }
+
+ nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+ slave_type = nm_setting_connection_get_slave_type (s_con);
+ if (slave_type) {
+ if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME)) {
+ page = nmt_page_bridge_port_new (conn);
+ section = build_section_for_page (NMT_EDITOR_PAGE (page), TRUE);
+ nmt_page_grid_append (grid, NULL, section, NULL);
+ } else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME)) {
+ page = nmt_page_team_port_new (conn);
+ section = build_section_for_page (NMT_EDITOR_PAGE (page), TRUE);
+ nmt_page_grid_append (grid, NULL, section, NULL);
+ }
+ } else {
+ page = nmt_page_ip4_new (conn);
+ section = build_section_for_page (NMT_EDITOR_PAGE (page),
+ nmt_page_ip4_is_non_empty (NMT_PAGE_IP4 (page)));
+ nmt_page_grid_append (grid, NULL, section, NULL);
+
+ /* Add a separator between ip4 and ip6 that's only visible if ip4 is open */
+ separator = nmt_newt_separator_new ();
+ g_object_bind_property (section, "open", separator, "visible", G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, NULL, separator, NULL);
+
+ page = nmt_page_ip6_new (conn);
+ section = build_section_for_page (NMT_EDITOR_PAGE (page),
+ nmt_page_ip6_is_non_empty (NMT_PAGE_IP6 (page)));
+ nmt_page_grid_append (grid, NULL, section, NULL);
+ nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+ }
+
+ widget = nmt_newt_checkbox_new (_("Automatically connect"));
+ g_object_bind_property (s_con, NM_SETTING_CONNECTION_AUTOCONNECT,
+ widget, "active",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+
+ widget = nmt_newt_checkbox_new (_("Available to all users"));
+ g_object_bind_property_full (s_con, NM_SETTING_CONNECTION_PERMISSIONS,
+ widget, "active",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
+ permissions_transform_to_allusers,
+ permissions_transform_from_allusers,
+ NULL, NULL);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+
+ G_OBJECT_CLASS (nmt_page_main_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_main_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtPageMainPrivate *priv = NMT_PAGE_MAIN_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_TYPE_DATA:
+ priv->type_data = g_value_get_pointer (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_page_main_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtPageMainPrivate *priv = NMT_PAGE_MAIN_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_TYPE_DATA:
+ g_value_set_pointer (value, priv->type_data);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_page_main_class_init (NmtPageMainClass *main_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (main_class);
+
+ g_type_class_add_private (main_class, sizeof (NmtPageMainPrivate));
+
+ object_class->constructed = nmt_page_main_constructed;
+ object_class->set_property = nmt_page_main_set_property;
+ object_class->get_property = nmt_page_main_get_property;
+
+ /**
+ * NmtPageMain:type-data:
+ *
+ * The page's connection's #NMEditorConnectionTypeData
+ */
+ g_object_class_install_property
+ (object_class, PROP_TYPE_DATA,
+ g_param_spec_pointer ("type-data", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-page-main.h b/clients/tui/nmt-page-main.h
new file mode 100644
index 0000000000..20de14f36a
--- /dev/null
+++ b/clients/tui/nmt-page-main.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_MAIN_H
+#define NMT_PAGE_MAIN_H
+
+#include "nmt-editor-page.h"
+#include "nm-editor-utils.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_MAIN (nmt_page_main_get_type ())
+#define NMT_PAGE_MAIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_MAIN, NmtPageMain))
+#define NMT_PAGE_MAIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_MAIN, NmtPageMainClass))
+#define NMT_IS_PAGE_MAIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_MAIN))
+#define NMT_IS_PAGE_MAIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_MAIN))
+#define NMT_PAGE_MAIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_MAIN, NmtPageMainClass))
+
+typedef struct {
+ NmtEditorPage parent;
+
+} NmtPageMain;
+
+typedef struct {
+ NmtEditorPageClass parent;
+
+} NmtPageMainClass;
+
+GType nmt_page_main_get_type (void);
+
+NmtNewtWidget *nmt_page_main_new (NMConnection *conn,
+ NMEditorConnectionTypeData *type_data);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_MAIN_H */
diff --git a/clients/tui/nmt-page-team-port.c b/clients/tui/nmt-page-team-port.c
new file mode 100644
index 0000000000..aec6b80394
--- /dev/null
+++ b/clients/tui/nmt-page-team-port.c
@@ -0,0 +1,125 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-team-port
+ * @short_description: The editor page for Team ports.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-team-port.h"
+
+G_DEFINE_TYPE (NmtPageTeamPort, nmt_page_team_port, NMT_TYPE_EDITOR_PAGE)
+
+#define NMT_PAGE_TEAM_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortPrivate))
+
+typedef struct {
+ NMSettingTeamPort *s_port;
+
+} NmtPageTeamPortPrivate;
+
+NmtNewtWidget *
+nmt_page_team_port_new (NMConnection *conn)
+{
+ return g_object_new (NMT_TYPE_PAGE_TEAM_PORT,
+ "connection", conn,
+ "title", _("TEAM PORT"),
+ NULL);
+}
+
+static void
+nmt_page_team_port_init (NmtPageTeamPort *team)
+{
+}
+
+static void
+edit_clicked (NmtNewtButton *button,
+ gpointer user_data)
+{
+ NmtPageTeamPort *team = user_data;
+ NmtPageTeamPortPrivate *priv = NMT_PAGE_TEAM_PORT_GET_PRIVATE (team);
+ const char *config;
+ char *new_config;
+
+ config = nm_setting_team_port_get_config (priv->s_port);
+ if (!config)
+ config = "";
+
+ new_config = nmt_newt_edit_string (config);
+
+ if (new_config && !*new_config)
+ g_clear_pointer (&new_config, g_free);
+ g_object_set (G_OBJECT (priv->s_port),
+ NM_SETTING_TEAM_PORT_CONFIG, new_config,
+ NULL);
+ g_free (new_config);
+}
+
+static void
+nmt_page_team_port_constructed (GObject *object)
+{
+ NmtPageTeamPort *team = NMT_PAGE_TEAM_PORT (object);
+ NmtPageTeamPortPrivate *priv = NMT_PAGE_TEAM_PORT_GET_PRIVATE (team);
+ NmtNewtGrid *grid;
+ NMSettingTeamPort *s_port;
+ NmtNewtWidget *widget;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (team));
+ s_port = nm_connection_get_setting_team_port (conn);
+ if (!s_port) {
+ nm_connection_add_setting (conn, nm_setting_team_port_new ());
+ s_port = nm_connection_get_setting_team_port (conn);
+ }
+ priv->s_port = s_port;
+
+ widget = nmt_newt_grid_new ();
+ nmt_page_grid_append (NMT_PAGE_GRID (team), NULL, widget, NULL);
+
+ grid = NMT_NEWT_GRID (widget);
+
+ widget = nmt_newt_label_new (_("JSON configuration"));
+ nmt_newt_grid_add (grid, widget, 0, 2);
+
+ widget = nmt_newt_textbox_new (NMT_NEWT_TEXTBOX_SCROLLABLE | NMT_NEWT_TEXTBOX_SET_BACKGROUND, 60);
+ g_object_bind_property (s_port, NM_SETTING_TEAM_PORT_CONFIG,
+ widget, "text",
+ G_BINDING_SYNC_CREATE);
+ nmt_newt_grid_add (grid, widget, 0, 3);
+ nmt_newt_widget_set_padding (widget, 2, 0, 2, 1);
+
+ widget = nmt_newt_button_new (_("Edit..."));
+ g_signal_connect (widget, "clicked", G_CALLBACK (edit_clicked), team);
+ nmt_newt_grid_add (grid, widget, 0, 4);
+
+ G_OBJECT_CLASS (nmt_page_team_port_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_team_port_class_init (NmtPageTeamPortClass *team_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (team_class);
+
+ g_type_class_add_private (team_class, sizeof (NmtPageTeamPortPrivate));
+
+ object_class->constructed = nmt_page_team_port_constructed;
+}
diff --git a/clients/tui/nmt-page-team-port.h b/clients/tui/nmt-page-team-port.h
new file mode 100644
index 0000000000..d4ec7a557f
--- /dev/null
+++ b/clients/tui/nmt-page-team-port.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_TEAM_PORT_H
+#define NMT_PAGE_TEAM_PORT_H
+
+#include "nmt-page-device.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_TEAM_PORT (nmt_page_team_port_get_type ())
+#define NMT_PAGE_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPort))
+#define NMT_PAGE_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortClass))
+#define NMT_IS_PAGE_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_TEAM_PORT))
+#define NMT_IS_PAGE_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_TEAM_PORT))
+#define NMT_PAGE_TEAM_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortClass))
+
+typedef struct {
+ NmtEditorPage parent;
+
+} NmtPageTeamPort;
+
+typedef struct {
+ NmtEditorPageClass parent;
+
+} NmtPageTeamPortClass;
+
+GType nmt_page_team_port_get_type (void);
+
+NmtNewtWidget *nmt_page_team_port_new (NMConnection *conn);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_TEAM_PORT_H */
diff --git a/clients/tui/nmt-page-team.c b/clients/tui/nmt-page-team.c
new file mode 100644
index 0000000000..81db3d33f7
--- /dev/null
+++ b/clients/tui/nmt-page-team.c
@@ -0,0 +1,195 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-team
+ * @short_description: The editor page for Team connections
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-team.h"
+
+#include "nmt-slave-list.h"
+
+G_DEFINE_TYPE (NmtPageTeam, nmt_page_team, NMT_TYPE_PAGE_DEVICE)
+
+#define NMT_PAGE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_TEAM, NmtPageTeamPrivate))
+
+typedef struct {
+ NmtSlaveList *slaves;
+
+ NMSettingTeam *s_team;
+ GType slave_type;
+
+} NmtPageTeamPrivate;
+
+NmtNewtWidget *
+nmt_page_team_new (NMConnection *conn,
+ NmtDeviceEntry *deventry)
+{
+ return g_object_new (NMT_TYPE_PAGE_TEAM,
+ "connection", conn,
+ "title", _("TEAM"),
+ "device-entry", deventry,
+ NULL);
+}
+
+static void
+nmt_page_team_init (NmtPageTeam *team)
+{
+ NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team);
+
+ priv->slave_type = G_TYPE_NONE;
+}
+
+static void
+slaves_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NmtPageTeam *team = NMT_PAGE_TEAM (user_data);
+ NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team);
+ GPtrArray *slaves;
+
+ g_object_get (object, "connections", &slaves, NULL);
+ if (slaves->len == 0) {
+ priv->slave_type = G_TYPE_NONE;
+ } else if (priv->slave_type == G_TYPE_NONE) {
+ NMConnection *slave = slaves->pdata[0];
+
+ if (nm_connection_is_type (slave, NM_SETTING_INFINIBAND_SETTING_NAME))
+ priv->slave_type = NM_TYPE_SETTING_INFINIBAND;
+ else
+ priv->slave_type = NM_TYPE_SETTING_WIRED;
+ }
+}
+
+static gboolean
+team_connection_type_filter (GType connection_type,
+ gpointer user_data)
+{
+ NmtPageTeam *team = user_data;
+ NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team);
+
+ if (priv->slave_type != NM_TYPE_SETTING_WIRED) {
+ if (connection_type == NM_TYPE_SETTING_INFINIBAND)
+ return TRUE;
+ }
+ if (priv->slave_type != NM_TYPE_SETTING_INFINIBAND) {
+ if ( connection_type == NM_TYPE_SETTING_WIRED
+ || connection_type == NM_TYPE_SETTING_WIRELESS
+ || connection_type == NM_TYPE_SETTING_VLAN)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+edit_clicked (NmtNewtButton *button,
+ gpointer user_data)
+{
+ NmtPageTeam *team = user_data;
+ NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team);
+ const char *config;
+ char *new_config;
+
+ config = nm_setting_team_get_config (priv->s_team);
+ if (!config)
+ config = "";
+
+ new_config = nmt_newt_edit_string (config);
+
+ if (new_config && !*new_config)
+ g_clear_pointer (&new_config, g_free);
+ g_object_set (G_OBJECT (priv->s_team),
+ NM_SETTING_TEAM_CONFIG, new_config,
+ NULL);
+ g_free (new_config);
+}
+
+static void
+nmt_page_team_constructed (GObject *object)
+{
+ NmtPageTeam *team = NMT_PAGE_TEAM (object);
+ NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team);
+ NmtDeviceEntry *deventry;
+ NmtNewtGrid *grid;
+ NMSettingTeam *s_team;
+ NmtNewtWidget *widget;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (team));
+ s_team = nm_connection_get_setting_team (conn);
+ if (!s_team) {
+ nm_connection_add_setting (conn, nm_setting_team_new ());
+ s_team = nm_connection_get_setting_team (conn);
+ }
+ priv->s_team = s_team;
+
+ deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object));
+ g_object_bind_property (s_team, NM_SETTING_TEAM_INTERFACE_NAME,
+ deventry, "interface-name",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ widget = nmt_newt_grid_new ();
+ nmt_page_grid_append (NMT_PAGE_GRID (team), NULL, widget, NULL);
+
+ grid = NMT_NEWT_GRID (widget);
+
+ widget = nmt_newt_label_new (_("Slaves"));
+ nmt_newt_grid_add (grid, widget, 0, 0);
+
+ widget = nmt_slave_list_new (conn, team_connection_type_filter, team);
+ g_signal_connect (widget, "notify::connections",
+ G_CALLBACK (slaves_changed), team);
+ nmt_newt_grid_add (grid, widget, 0, 1);
+ nmt_newt_widget_set_padding (widget, 0, 0, 0, 1);
+ priv->slaves = NMT_SLAVE_LIST (widget);
+ slaves_changed (G_OBJECT (priv->slaves), NULL, team);
+
+ widget = nmt_newt_label_new (_("JSON configuration"));
+ nmt_newt_grid_add (grid, widget, 0, 2);
+
+ widget = nmt_newt_textbox_new (NMT_NEWT_TEXTBOX_SCROLLABLE | NMT_NEWT_TEXTBOX_SET_BACKGROUND, 60);
+ g_object_bind_property (s_team, NM_SETTING_TEAM_CONFIG,
+ widget, "text",
+ G_BINDING_SYNC_CREATE);
+ nmt_newt_grid_add (grid, widget, 0, 3);
+ nmt_newt_widget_set_padding (widget, 2, 0, 2, 1);
+
+ widget = nmt_newt_button_new (_("Edit..."));
+ g_signal_connect (widget, "clicked", G_CALLBACK (edit_clicked), team);
+ nmt_newt_grid_add (grid, widget, 0, 4);
+
+ G_OBJECT_CLASS (nmt_page_team_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_team_class_init (NmtPageTeamClass *team_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (team_class);
+
+ g_type_class_add_private (team_class, sizeof (NmtPageTeamPrivate));
+
+ object_class->constructed = nmt_page_team_constructed;
+}
diff --git a/clients/tui/nmt-page-team.h b/clients/tui/nmt-page-team.h
new file mode 100644
index 0000000000..49a81dd55e
--- /dev/null
+++ b/clients/tui/nmt-page-team.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_TEAM_H
+#define NMT_PAGE_TEAM_H
+
+#include "nmt-page-device.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_TEAM (nmt_page_team_get_type ())
+#define NMT_PAGE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_TEAM, NmtPageTeam))
+#define NMT_PAGE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_TEAM, NmtPageTeamClass))
+#define NMT_IS_PAGE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_TEAM))
+#define NMT_IS_PAGE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_TEAM))
+#define NMT_PAGE_TEAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_TEAM, NmtPageTeamClass))
+
+typedef struct {
+ NmtPageDevice parent;
+
+} NmtPageTeam;
+
+typedef struct {
+ NmtPageDeviceClass parent;
+
+} NmtPageTeamClass;
+
+GType nmt_page_team_get_type (void);
+
+NmtNewtWidget *nmt_page_team_new (NMConnection *conn,
+ NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_TEAM_H */
diff --git a/clients/tui/nmt-page-vlan.c b/clients/tui/nmt-page-vlan.c
new file mode 100644
index 0000000000..f6de671c85
--- /dev/null
+++ b/clients/tui/nmt-page-vlan.c
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-vlan
+ * @short_description: The editor page for VLAN connections
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <nm-device-ethernet.h>
+
+#include "nm-editor-bindings.h"
+
+#include "nmt-page-vlan.h"
+#include "nmt-device-entry.h"
+#include "nmt-mac-entry.h"
+#include "nmt-mtu-entry.h"
+
+G_DEFINE_TYPE (NmtPageVlan, nmt_page_vlan, NMT_TYPE_PAGE_DEVICE)
+
+#define NMT_PAGE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_VLAN, NmtPageVlanPrivate))
+
+typedef struct {
+ NMSettingWired *s_wired;
+
+} NmtPageVlanPrivate;
+
+NmtNewtWidget *
+nmt_page_vlan_new (NMConnection *conn,
+ NmtDeviceEntry *deventry)
+{
+ return g_object_new (NMT_TYPE_PAGE_VLAN,
+ "connection", conn,
+ "title", _("VLAN"),
+ "device-entry", deventry,
+ NULL);
+}
+
+static void
+nmt_page_vlan_init (NmtPageVlan *vlan)
+{
+}
+
+static gboolean
+vlan_device_filter (NmtDeviceEntry *deventry,
+ NMDevice *device,
+ gpointer user_data)
+{
+ // FIXME
+ return NM_IS_DEVICE_ETHERNET (device);
+}
+
+static void
+nmt_page_vlan_constructed (GObject *object)
+{
+ NmtPageVlan *vlan = NMT_PAGE_VLAN (object);
+ NmtPageVlanPrivate *priv = NMT_PAGE_VLAN_GET_PRIVATE (vlan);
+ NmtDeviceEntry *deventry;
+ NmtPageGrid *grid;
+ NMSettingWired *s_wired;
+ NMSettingVlan *s_vlan;
+ NmtNewtWidget *widget, *parent, *id_entry;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (vlan));
+ s_vlan = nm_connection_get_setting_vlan (conn);
+ if (!s_vlan) {
+ nm_connection_add_setting (conn, nm_setting_vlan_new ());
+ s_vlan = nm_connection_get_setting_vlan (conn);
+ }
+ s_wired = nm_connection_get_setting_wired (conn);
+ if (!s_wired) {
+ /* It makes things simpler if we always have a NMSettingWired;
+ * we'll hold a ref on one, and add it to and remove it from
+ * the connection as needed.
+ */
+ s_wired = NM_SETTING_WIRED (nm_setting_wired_new ());
+ }
+ priv->s_wired = g_object_ref_sink (s_wired);
+
+ deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object));
+ g_object_bind_property (s_vlan, NM_SETTING_VLAN_INTERFACE_NAME,
+ deventry, "interface-name",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ grid = NMT_PAGE_GRID (vlan);
+
+ nm_editor_bind_vlan_name (s_vlan);
+
+ widget = parent = nmt_device_entry_new (_("Parent"), 40, G_TYPE_NONE);
+ nmt_device_entry_set_device_filter (NMT_DEVICE_ENTRY (widget),
+ vlan_device_filter, vlan);
+ g_object_bind_property (s_vlan, NM_SETTING_VLAN_PARENT,
+ widget, "interface-name",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ g_object_bind_property (s_wired, NM_SETTING_WIRED_MAC_ADDRESS,
+ widget, "mac-address",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, NULL, widget, NULL);
+
+ widget = id_entry = nmt_newt_entry_numeric_new (8, 0, 4095);
+ g_object_bind_property (s_vlan, NM_SETTING_VLAN_ID,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("VLAN id"), widget, NULL);
+
+ nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+ widget = nmt_mac_entry_new (40, ETH_ALEN);
+ g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS,
+ widget, "mac-address",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Cloned MAC address"), widget, NULL);
+
+ widget = nmt_mtu_entry_new ();
+ g_object_bind_property (s_wired, NM_SETTING_WIRED_MTU,
+ widget, "mtu",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("MTU"), widget, NULL);
+
+ G_OBJECT_CLASS (nmt_page_vlan_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_vlan_finalize (GObject *object)
+{
+ NmtPageVlanPrivate *priv = NMT_PAGE_VLAN_GET_PRIVATE (object);
+
+ g_clear_object (&priv->s_wired);
+
+ G_OBJECT_CLASS (nmt_page_vlan_parent_class)->finalize (object);
+}
+
+static void
+nmt_page_vlan_class_init (NmtPageVlanClass *vlan_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (vlan_class);
+
+ g_type_class_add_private (vlan_class, sizeof (NmtPageVlanPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_page_vlan_constructed;
+ object_class->finalize = nmt_page_vlan_finalize;
+}
diff --git a/clients/tui/nmt-page-vlan.h b/clients/tui/nmt-page-vlan.h
new file mode 100644
index 0000000000..57d0024388
--- /dev/null
+++ b/clients/tui/nmt-page-vlan.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_VLAN_H
+#define NMT_PAGE_VLAN_H
+
+#include "nmt-page-device.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_VLAN (nmt_page_vlan_get_type ())
+#define NMT_PAGE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_VLAN, NmtPageVlan))
+#define NMT_PAGE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_VLAN, NmtPageVlanClass))
+#define NMT_IS_PAGE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_VLAN))
+#define NMT_IS_PAGE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_VLAN))
+#define NMT_PAGE_VLAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_VLAN, NmtPageVlanClass))
+
+typedef struct {
+ NmtPageDevice parent;
+
+} NmtPageVlan;
+
+typedef struct {
+ NmtPageDeviceClass parent;
+
+} NmtPageVlanClass;
+
+GType nmt_page_vlan_get_type (void);
+
+NmtNewtWidget *nmt_page_vlan_new (NMConnection *conn,
+ NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_VLAN_H */
diff --git a/clients/tui/nmt-page-wifi.c b/clients/tui/nmt-page-wifi.c
new file mode 100644
index 0000000000..153aee7ca5
--- /dev/null
+++ b/clients/tui/nmt-page-wifi.c
@@ -0,0 +1,389 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-wifi
+ * @short_description: The editor page for Wi-Fi connections
+ *
+ * #NmtPageWifi is the editor page for Wi-Fi connections, which
+ * includes both #NMSettingWireless and #NMSettingWirelessSecurity
+ * properties.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+#include <nm-utils.h>
+
+#include "nmt-page-wifi.h"
+#include "nmt-mac-entry.h"
+#include "nmt-mtu-entry.h"
+#include "nmt-password-fields.h"
+
+#include "nm-editor-bindings.h"
+
+G_DEFINE_TYPE (NmtPageWifi, nmt_page_wifi, NMT_TYPE_PAGE_DEVICE)
+
+#define NMT_PAGE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_WIFI, NmtPageWifiPrivate))
+
+typedef struct {
+ NMSettingWirelessSecurity *s_wsec;
+
+} NmtPageWifiPrivate;
+
+NmtNewtWidget *
+nmt_page_wifi_new (NMConnection *conn,
+ NmtDeviceEntry *deventry)
+{
+ return g_object_new (NMT_TYPE_PAGE_WIFI,
+ "connection", conn,
+ "title", _("WI-FI"),
+ "device-entry", deventry,
+ NULL);
+}
+
+static void
+nmt_page_wifi_init (NmtPageWifi *wifi)
+{
+}
+
+static NmtNewtPopupEntry wifi_mode[] = {
+ { NC_("Wi-Fi", "Client"), NM_SETTING_WIRELESS_MODE_INFRA },
+ { N_("Access Point"), NM_SETTING_WIRELESS_MODE_AP },
+ { N_("Ad-Hoc Network"), NM_SETTING_WIRELESS_MODE_ADHOC },
+ { NULL, NULL }
+};
+
+static NmtNewtPopupEntry wifi_band[] = {
+ { NC_("Wi-Fi", "Automatic"), NULL },
+ /* 802.11a Wi-Fi network */
+ { N_("A (5 GHz)"), "a" },
+ /* 802.11b / 802.11g Wi-Fi network */
+ { N_("B/G (2.4 GHz)"), "bg" },
+ { NULL, NULL }
+};
+
+static NmtNewtPopupEntry wifi_security[] = {
+ { NC_("Wi-Fi security", "None"), "none" },
+ { N_("WPA & WPA2 Personal"), "wpa-personal" },
+ { N_("WPA & WPA2 Enterprise"), "wpa-enterprise" },
+ { N_("WEP 40/128-bit Key (Hex or ASCII)"), "wep-key" },
+ { N_("WEP 128-bit Passphrase"), "wep-passphrase" },
+ { N_("Dynamic WEP (802.1x)"), "dynamic-wep" },
+ { N_("LEAP"), "leap" },
+ { NULL, NULL }
+};
+
+static NmtNewtPopupEntry wep_index[] = {
+ { NC_("WEP key index", "1 (Default)"), "1" },
+ { NC_("WEP key index", "2"), "2" },
+ { NC_("WEP key index", "3"), "3" },
+ { NC_("WEP key index", "4"), "4" },
+ { NULL, NULL }
+};
+
+static NmtNewtPopupEntry wep_auth[] = {
+ { N_("Open System"), "open" },
+ { N_("Shared Key"), "shared" },
+ { NULL, NULL }
+};
+
+static gboolean
+mode_transform_to_band_visibility (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ if (!g_strcmp0 (g_value_get_string (source_value), NM_SETTING_WIRELESS_MODE_INFRA))
+ g_value_set_boolean (target_value, FALSE);
+ else
+ g_value_set_boolean (target_value, TRUE);
+ return TRUE;
+}
+
+static gboolean
+band_transform_to_channel_visibility (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ g_value_set_boolean (target_value, g_value_get_string (source_value) != NULL);
+ return TRUE;
+}
+
+static gboolean
+ssid_transform_to_entry (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ GByteArray *ssid;
+ char *utf8;
+
+ ssid = g_value_get_boxed (source_value);
+ utf8 = nm_utils_ssid_to_utf8 (ssid);
+ g_value_take_string (target_value, utf8);
+ return TRUE;
+}
+
+static gboolean
+ssid_transform_from_entry (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NMSettingWireless *s_wireless = user_data;
+ const char *text;
+ const GByteArray *old_ssid;
+ GByteArray *ssid;
+ char *utf8;
+
+ text = g_value_get_string (source_value);
+
+ old_ssid = nm_setting_wireless_get_ssid (s_wireless);
+ utf8 = nm_utils_ssid_to_utf8 (old_ssid);
+
+ if (!g_strcmp0 (text, utf8)) {
+ g_free (utf8);
+ return FALSE;
+ }
+ g_free (utf8);
+
+ ssid = g_byte_array_new ();
+ g_byte_array_append (ssid, (guint8 *)text, strlen (text));
+ g_value_take_boxed (target_value, ssid);
+ return TRUE;
+}
+
+static void
+nmt_page_wifi_constructed (GObject *object)
+{
+ NmtPageWifiPrivate *priv = NMT_PAGE_WIFI_GET_PRIVATE (object);
+ NmtPageWifi *wifi = NMT_PAGE_WIFI (object);
+ NmtDeviceEntry *deventry;
+ NmtPageGrid *grid;
+ NMSettingWireless *s_wireless;
+ NMSettingWirelessSecurity *s_wsec;
+ NmtNewtWidget *widget, *hbox, *subgrid;
+ NmtNewtWidget *mode, *band, *security, *entry;
+ NmtNewtStack *stack;
+ NMConnection *conn;
+
+ conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (wifi));
+ s_wireless = nm_connection_get_setting_wireless (conn);
+ if (!s_wireless) {
+ nm_connection_add_setting (conn, nm_setting_wireless_new ());
+ s_wireless = nm_connection_get_setting_wireless (conn);
+ }
+
+ s_wsec = nm_connection_get_setting_wireless_security (conn);
+ if (!s_wsec) {
+ /* It makes things simpler if we always have a
+ * NMSettingWirelessSecurity; we'll hold a ref on one, and add
+ * it to and remove it from the connection as needed.
+ */
+ s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ());
+ }
+ priv->s_wsec = g_object_ref_sink (s_wsec);
+
+ deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object));
+ g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS,
+ deventry, "mac-address",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ grid = NMT_PAGE_GRID (wifi);
+
+ widget = nmt_newt_entry_new (40, NMT_NEWT_ENTRY_NONEMPTY);
+ g_object_bind_property_full (s_wireless, NM_SETTING_WIRELESS_SSID,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
+ ssid_transform_to_entry,
+ ssid_transform_from_entry,
+ s_wireless, NULL);
+ nmt_page_grid_append (grid, _("SSID"), widget, NULL);
+
+ widget = nmt_newt_popup_new (wifi_mode);
+ g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MODE,
+ widget, "active-id",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Mode"), widget, NULL);
+ mode = widget;
+
+ hbox = nmt_newt_grid_new ();
+ widget = nmt_newt_popup_new (wifi_band);
+ g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_BAND,
+ widget, "active-id",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_newt_grid_add (NMT_NEWT_GRID (hbox), widget, 0, 0);
+ band = widget;
+
+ widget = nmt_newt_entry_numeric_new (10, 0, 255);
+ g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_CHANNEL,
+ widget, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_newt_grid_add (NMT_NEWT_GRID (hbox), widget, 1, 0);
+ nmt_newt_widget_set_padding (widget, 1, 0, 0, 0);
+
+ g_object_bind_property_full (band, "active-id", widget, "visible",
+ G_BINDING_SYNC_CREATE,
+ band_transform_to_channel_visibility,
+ NULL, NULL, NULL);
+ g_object_bind_property_full (mode, "active-id", hbox, "visible",
+ G_BINDING_SYNC_CREATE,
+ mode_transform_to_band_visibility,
+ NULL, NULL, NULL);
+ nmt_page_grid_append (grid, _("Channel"), hbox, NULL);
+
+ nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+ widget = nmt_newt_popup_new (wifi_security);
+ nmt_page_grid_append (grid, _("Security"), widget, NULL);
+ security = widget;
+
+ widget = nmt_newt_stack_new ();
+ stack = NMT_NEWT_STACK (widget);
+
+ /* none */
+ subgrid = nmt_page_grid_new ();
+ nmt_newt_stack_add (stack, "none", subgrid);
+
+ /* wpa-personal */
+ subgrid = nmt_page_grid_new ();
+ widget = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD);
+ g_object_bind_property (s_wsec, NM_SETTING_WIRELESS_SECURITY_PSK,
+ widget, "password",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Password"), widget, NULL);
+ nmt_newt_stack_add (stack, "wpa-personal", subgrid);
+
+ /* "wpa-enterprise" */
+ // FIXME
+ widget = nmt_newt_label_new (_("(No support for wpa-enterprise yet...)"));
+ nmt_newt_stack_add (stack, "wpa-enterprise", widget);
+
+ /* wep-key */
+ subgrid = nmt_page_grid_new ();
+
+ widget = entry = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD);
+ nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Key"), widget, NULL);
+
+ widget = nmt_newt_popup_new (wep_index);
+ nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("WEP index"), widget, NULL);
+
+ nm_editor_bind_wireless_security_wep_key (s_wsec,
+ entry, "password",
+ widget, "active",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ widget = nmt_newt_popup_new (wep_auth);
+ nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Authentication"), widget, NULL);
+
+ nmt_newt_stack_add (stack, "wep-key", subgrid);
+
+ /* wep-passphrase */
+ subgrid = nmt_page_grid_new ();
+
+ widget = entry = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD);
+ nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Password"), widget, NULL);
+
+ widget = nmt_newt_popup_new (wep_index);
+ nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("WEP index"), widget, NULL);
+
+ nm_editor_bind_wireless_security_wep_key (s_wsec,
+ entry, "password",
+ widget, "active",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ widget = nmt_newt_popup_new (wep_auth);
+ nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Authentication"), widget, NULL);
+
+ nmt_newt_stack_add (stack, "wep-passphrase", subgrid);
+
+ /* "dynamic-wep" */
+ // FIXME
+ widget = nmt_newt_label_new (_("(No support for dynamic-wep yet...)"));
+ nmt_newt_stack_add (stack, "dynamic-wep", widget);
+
+ /* leap */
+ subgrid = nmt_page_grid_new ();
+
+ widget = nmt_newt_entry_new (40, NMT_NEWT_ENTRY_NONEMPTY);
+ nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Username"), widget, NULL);
+
+ widget = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD);
+ g_object_bind_property (s_wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
+ widget, "password",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Password"), widget, NULL);
+
+ nmt_newt_stack_add (stack, "leap", subgrid);
+
+ nmt_page_grid_append (grid, NULL, NMT_NEWT_WIDGET (stack), NULL);
+ g_object_bind_property (security, "active-id",
+ stack, "active-id",
+ G_BINDING_SYNC_CREATE);
+ nm_editor_bind_wireless_security_method (conn, s_wsec, security, "active-id",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+ nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+ widget = nmt_mac_entry_new (40, ETH_ALEN);
+ g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_BSSID,
+ widget, "mac-address",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("BSSID"), widget, NULL);
+
+ widget = nmt_mac_entry_new (40, ETH_ALEN);
+ g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS,
+ widget, "mac-address",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("Cloned MAC address"), widget, NULL);
+
+ widget = nmt_mtu_entry_new ();
+ g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MTU,
+ widget, "mtu",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ nmt_page_grid_append (grid, _("MTU"), widget, NULL);
+
+ G_OBJECT_CLASS (nmt_page_wifi_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_wifi_finalize (GObject *object)
+{
+ NmtPageWifiPrivate *priv = NMT_PAGE_WIFI_GET_PRIVATE (object);
+
+ g_clear_object (&priv->s_wsec);
+
+ G_OBJECT_CLASS (nmt_page_wifi_parent_class)->constructed (object);
+}
+
+
+static void
+nmt_page_wifi_class_init (NmtPageWifiClass *wifi_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (wifi_class);
+
+ g_type_class_add_private (wifi_class, sizeof (NmtPageWifiPrivate));
+
+ object_class->constructed = nmt_page_wifi_constructed;
+ object_class->finalize = nmt_page_wifi_finalize;
+}
diff --git a/clients/tui/nmt-page-wifi.h b/clients/tui/nmt-page-wifi.h
new file mode 100644
index 0000000000..06cb2a9805
--- /dev/null
+++ b/clients/tui/nmt-page-wifi.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_WIFI_H
+#define NMT_PAGE_WIFI_H
+
+#include "nmt-page-device.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_WIFI (nmt_page_wifi_get_type ())
+#define NMT_PAGE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_WIFI, NmtPageWifi))
+#define NMT_PAGE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_WIFI, NmtPageWifiClass))
+#define NMT_IS_PAGE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_WIFI))
+#define NMT_IS_PAGE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_WIFI))
+#define NMT_PAGE_WIFI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_WIFI, NmtPageWifiClass))
+
+typedef struct {
+ NmtPageDevice parent;
+
+} NmtPageWifi;
+
+typedef struct {
+ NmtPageDeviceClass parent;
+
+} NmtPageWifiClass;
+
+GType nmt_page_wifi_get_type (void);
+
+NmtNewtWidget *nmt_page_wifi_new (NMConnection *conn,
+ NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_WIFI_H */
diff --git a/clients/tui/nmt-password-dialog.c b/clients/tui/nmt-password-dialog.c
new file mode 100644
index 0000000000..3023404d96
--- /dev/null
+++ b/clients/tui/nmt-password-dialog.c
@@ -0,0 +1,346 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-password-dialog
+ * @short_description: A password dialog
+ *
+ * #NmtPasswordDialog is the password dialog used to get connection
+ * secrets when activating a connection.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-password-dialog.h"
+#include "nmt-secret-agent.h"
+#include "nmtui.h"
+
+G_DEFINE_TYPE (NmtPasswordDialog, nmt_password_dialog, NMT_TYPE_NEWT_FORM)
+
+#define NMT_PASSWORD_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogPrivate))
+
+typedef struct {
+ char *request_id;
+ char *prompt;
+ GPtrArray *secrets;
+ GPtrArray *entries;
+
+ NmtNewtWidget *ok, *cancel;
+ NmtNewtWidget *last_entry;
+ NmtNewtWidget *secret_grid;
+
+ gboolean succeeded;
+} NmtPasswordDialogPrivate;
+
+enum {
+ PROP_0,
+ PROP_REQUEST_ID,
+ PROP_PROMPT,
+ PROP_SECRETS,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_password_dialog_new:
+ * @request_id: the request ID from the #NmtSecretAgent
+ * @title: the dialog title
+ * @prompt: the prompt text to display
+ * @secrets: (element-type #NmtSecretAgentSecret): the secrets requested
+ *
+ * Creates a new #NmtPasswordDialog to request passwords from
+ * the user.
+ *
+ * Returns: a new #NmtPasswordDialog.
+ */
+NmtNewtForm *
+nmt_password_dialog_new (const char *request_id,
+ const char *title,
+ const char *prompt,
+ GPtrArray *secrets)
+{
+ return g_object_new (NMT_TYPE_PASSWORD_DIALOG,
+ "request-id", request_id,
+ "title", title,
+ "prompt", prompt,
+ "secrets", secrets,
+ "escape-exits", TRUE,
+ NULL);
+}
+
+static void
+nmt_password_dialog_init (NmtPasswordDialog *dialog)
+{
+ NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ priv->entries = g_ptr_array_new ();
+}
+
+static void
+maybe_save_input_and_exit (NmtNewtWidget *widget,
+ gpointer dialog,
+ newtComponent co)
+{
+ NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+ int i;
+
+ /* This gets invoked when the user types Return in the final entry,
+ * but the form may not be fully valid in that case.
+ */
+ if (!nmt_newt_widget_get_valid (priv->secret_grid))
+ return;
+
+ priv->succeeded = TRUE;
+
+ for (i = 0; i < priv->secrets->len; i++) {
+ NmtSecretAgentSecret *secret = priv->secrets->pdata[i];
+
+ g_free (secret->value);
+ g_object_get (priv->entries->pdata[i], "text", &secret->value, NULL);
+ }
+
+ nmt_newt_form_quit (nmt_newt_widget_get_form (widget));
+}
+
+static void
+nmt_password_dialog_constructed (GObject *object)
+{
+ NmtPasswordDialog *dialog = NMT_PASSWORD_DIALOG (object);
+ NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+ NmtNewtWidget *widget;
+ NmtNewtGrid *grid, *secret_grid;
+ NmtNewtButtonBox *bbox;
+ int i;
+
+ widget = nmt_newt_grid_new ();
+ nmt_newt_form_set_content (NMT_NEWT_FORM (dialog), widget);
+ grid = NMT_NEWT_GRID (widget);
+
+ widget = nmt_newt_textbox_new (0, 60);
+ nmt_newt_textbox_set_text (NMT_NEWT_TEXTBOX (widget), priv->prompt);
+ nmt_newt_grid_add (grid, widget, 0, 0);
+
+ widget = nmt_newt_grid_new ();
+ nmt_newt_grid_add (grid, widget, 0, 1);
+ nmt_newt_widget_set_padding (widget, 0, 1, 0, 1);
+ priv->secret_grid = widget;
+ secret_grid = NMT_NEWT_GRID (widget);
+
+ for (i = 0; i < priv->secrets->len; i++) {
+ NmtSecretAgentSecret *secret = priv->secrets->pdata[i];
+ NmtNewtEntryFlags flags;
+
+ widget = nmt_newt_label_new (secret->name);
+ nmt_newt_grid_add (secret_grid, widget, 0, i);
+ nmt_newt_widget_set_padding (widget, 4, 0, 1, 0);
+
+ flags = NMT_NEWT_ENTRY_NONEMPTY;
+ if (secret->password)
+ flags |= NMT_NEWT_ENTRY_PASSWORD;
+ widget = nmt_newt_entry_new (30, flags);
+ nmt_newt_grid_add (secret_grid, widget, 1, i);
+ g_ptr_array_add (priv->entries, widget);
+
+ if (i == priv->secrets->len - 1) {
+ priv->last_entry = widget;
+ g_signal_connect (widget, "activated",
+ G_CALLBACK (maybe_save_input_and_exit), dialog);
+ }
+ }
+
+ widget = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL);
+ nmt_newt_grid_add (grid, widget, 0, 2);
+ bbox = NMT_NEWT_BUTTON_BOX (widget);
+
+ priv->cancel = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("Cancel"));
+ nmt_newt_widget_set_exit_on_activate (priv->cancel, TRUE);
+
+ priv->ok = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("OK"));
+ g_signal_connect (priv->ok, "activated",
+ G_CALLBACK (maybe_save_input_and_exit), dialog);
+ g_object_bind_property (priv->secret_grid, "valid",
+ priv->ok, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ G_OBJECT_CLASS (nmt_password_dialog_parent_class)->constructed (object);
+}
+
+static void
+nmt_password_dialog_finalize (GObject *object)
+{
+ NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (object);
+
+ g_free (priv->request_id);
+ g_free (priv->prompt);
+ g_clear_pointer (&priv->entries, g_ptr_array_unref);
+ g_clear_pointer (&priv->secrets, g_ptr_array_unref);
+
+ G_OBJECT_CLASS (nmt_password_dialog_parent_class)->finalize (object);
+}
+
+static void
+nmt_password_dialog_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_REQUEST_ID:
+ priv->request_id = g_value_dup_string (value);
+ break;
+ case PROP_PROMPT:
+ priv->prompt = g_value_dup_string (value);
+ break;
+ case PROP_SECRETS:
+ priv->secrets = g_value_dup_boxed (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_password_dialog_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_REQUEST_ID:
+ g_value_set_string (value, priv->request_id);
+ break;
+ case PROP_PROMPT:
+ g_value_set_string (value, priv->prompt);
+ break;
+ case PROP_SECRETS:
+ g_value_set_boxed (value, priv->secrets);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_password_dialog_class_init (NmtPasswordDialogClass *dialog_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (dialog_class);
+
+ g_type_class_add_private (dialog_class, sizeof (NmtPasswordDialogPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_password_dialog_constructed;
+ object_class->set_property = nmt_password_dialog_set_property;
+ object_class->get_property = nmt_password_dialog_get_property;
+ object_class->finalize = nmt_password_dialog_finalize;
+
+ /**
+ * NmtPasswordDialog:request-id:
+ *
+ * The request ID from the #NmtSecretAgent
+ */
+ g_object_class_install_property
+ (object_class, PROP_REQUEST_ID,
+ g_param_spec_string ("request-id", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtPasswordDialog:prompt:
+ *
+ * The prompt text.
+ */
+ g_object_class_install_property
+ (object_class, PROP_PROMPT,
+ g_param_spec_string ("prompt", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtPasswordDialog:secrets:
+ *
+ * The array of request secrets
+ *
+ * Element-Type: #NmtSecretAgentSecret.
+ */
+ g_object_class_install_property
+ (object_class, PROP_SECRETS,
+ g_param_spec_boxed ("secrets", "", "",
+ G_TYPE_PTR_ARRAY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+/**
+ * nmt_password_dialog_succeeded:
+ * @dialog: the #NmtPasswordDialog
+ *
+ * After the dialog has exited, returns %TRUE if the user clicked
+ * "OK", %FALSE if "Cancel".
+ *
+ * Returns: whether the dialog succeeded.
+ */
+gboolean
+nmt_password_dialog_succeeded (NmtPasswordDialog *dialog)
+{
+ NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ return priv->succeeded;
+}
+
+/**
+ * nmt_password_dialog_get_request_id:
+ * @dialog: the #NmtPasswordDialog
+ *
+ * Gets the dialog's request ID.
+ *
+ * Returns: the dialog's request ID.
+ */
+const char *
+nmt_password_dialog_get_request_id (NmtPasswordDialog *dialog)
+{
+ NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ return priv->request_id;
+}
+
+/**
+ * nmt_password_dialog_get_secrets:
+ * @dialog: the #NmtPasswordDialog
+ *
+ * Gets the dialog's secrets array.
+ *
+ * Returns: (transfer none): the dialog's secrets array.
+ */
+GPtrArray *
+nmt_password_dialog_get_secrets (NmtPasswordDialog *dialog)
+{
+ NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+ return priv->secrets;
+}
diff --git a/clients/tui/nmt-password-dialog.h b/clients/tui/nmt-password-dialog.h
new file mode 100644
index 0000000000..36c9f51e19
--- /dev/null
+++ b/clients/tui/nmt-password-dialog.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PASSWORD_DIALOG_H
+#define NMT_PASSWORD_DIALOG_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PASSWORD_DIALOG (nmt_password_dialog_get_type ())
+#define NMT_PASSWORD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialog))
+#define NMT_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass))
+#define NMT_IS_PASSWORD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PASSWORD_DIALOG))
+#define NMT_IS_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PASSWORD_DIALOG))
+#define NMT_PASSWORD_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass))
+
+typedef struct {
+ NmtNewtForm parent;
+
+} NmtPasswordDialog;
+
+typedef struct {
+ NmtNewtFormClass parent;
+
+} NmtPasswordDialogClass;
+
+GType nmt_password_dialog_get_type (void);
+
+NmtNewtForm *nmt_password_dialog_new (const char *request_id,
+ const char *title,
+ const char *prompt,
+ GPtrArray *secrets);
+
+gboolean nmt_password_dialog_succeeded (NmtPasswordDialog *dialog);
+
+const char *nmt_password_dialog_get_request_id (NmtPasswordDialog *dialog);
+GPtrArray *nmt_password_dialog_get_secrets (NmtPasswordDialog *dialog);
+
+G_END_DECLS
+
+#endif /* NMT_PASSWORD_DIALOG_H */
diff --git a/clients/tui/nmt-password-fields.c b/clients/tui/nmt-password-fields.c
new file mode 100644
index 0000000000..8fba33fd97
--- /dev/null
+++ b/clients/tui/nmt-password-fields.c
@@ -0,0 +1,315 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-password-fields
+ * @short_description: Widgets for password-related data
+ *
+ * #NmtPasswordFields provides an entry to type a password into, followed
+ * optionally by an "Ask for this password every time" checkbox and/or a
+ * "Show password" checkbox that toggles whether the password is visible.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-password-fields.h"
+
+G_DEFINE_TYPE (NmtPasswordFields, nmt_password_fields, NMT_TYPE_NEWT_GRID)
+
+#define NMT_PASSWORD_FIELDS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsPrivate))
+
+typedef struct {
+ NmtPasswordFieldsExtras extras;
+
+ NmtNewtEntry *entry;
+ NmtNewtCheckbox *always_ask;
+ NmtNewtCheckbox *show_password;
+
+ char *init_password;
+
+} NmtPasswordFieldsPrivate;
+
+enum {
+ PROP_0,
+ PROP_WIDTH,
+ PROP_EXTRAS,
+ PROP_PASSWORD,
+ PROP_ALWAYS_ASK,
+ PROP_SHOW_PASSWORD,
+
+ LAST_PROP
+};
+
+/**
+ * NmtPasswordFieldsExtras:
+ * @NMT_PASSWORD_FIELDS_ALWAYS_ASK: show an "Always ask" checkbox
+ * @NMT_PASSWORD_FIELDS_SHOW_PASSWORD: show a "Show password" checkbox
+ *
+ * Extra widgets to include in an #NmtPasswordFields
+ */
+
+/**
+ * nmt_password_fields_new:
+ * @width: width in characters of the password entry
+ * @extras: extra widgets to show
+ *
+ * Creates a new #NmtPasswordFields
+ *
+ * Returns: a new #NmtPasswordFields
+ */
+NmtNewtWidget *
+nmt_password_fields_new (int width,
+ NmtPasswordFieldsExtras extras)
+{
+ return g_object_new (NMT_TYPE_PASSWORD_FIELDS,
+ "width", width,
+ "extras", extras,
+ NULL);
+}
+
+static void
+nmt_password_fields_set_password (NmtPasswordFields *fields,
+ const char *password)
+{
+ NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields);
+
+ if (!g_strcmp0 (password, nmt_newt_entry_get_text (priv->entry)))
+ return;
+
+ nmt_newt_entry_set_text (priv->entry, password);
+ g_object_notify (G_OBJECT (fields), "password");
+}
+
+static const char *
+nmt_password_fields_get_password (NmtPasswordFields *fields)
+{
+ NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields);
+
+ return nmt_newt_entry_get_text (priv->entry);
+}
+
+static void
+always_ask_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer fields)
+{
+ g_object_notify (fields, "always-ask");
+}
+
+static void
+show_password_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer fields)
+{
+ g_object_notify (fields, "show-password");
+}
+
+static void
+nmt_password_fields_init (NmtPasswordFields *fields)
+{
+ NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields);
+
+ priv->entry = NMT_NEWT_ENTRY (nmt_newt_entry_new (-1, 0));
+ priv->always_ask = NMT_NEWT_CHECKBOX (nmt_newt_checkbox_new (_("Ask for this password every time")));
+ priv->show_password = NMT_NEWT_CHECKBOX (nmt_newt_checkbox_new (_("Show password")));
+}
+
+static void
+nmt_password_fields_constructed (GObject *object)
+{
+ NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (object);
+ NmtNewtGrid *grid = NMT_NEWT_GRID (object);
+
+ nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (priv->entry), 0, 0);
+
+ if (priv->extras & NMT_PASSWORD_FIELDS_ALWAYS_ASK) {
+ nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (priv->always_ask), 0, 1);
+ g_signal_connect (priv->always_ask, "notify::active",
+ G_CALLBACK (always_ask_changed), object);
+ } else
+ g_clear_object (&priv->always_ask);
+
+ if (priv->extras & NMT_PASSWORD_FIELDS_SHOW_PASSWORD) {
+ nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (priv->show_password), 0, 2);
+ g_signal_connect (priv->show_password, "notify::active",
+ G_CALLBACK (show_password_changed), object);
+ g_object_bind_property (priv->show_password, "active",
+ priv->entry, "password",
+ G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
+ } else
+ g_clear_object (&priv->show_password);
+
+ G_OBJECT_CLASS (nmt_password_fields_parent_class)->constructed (object);
+}
+
+static void
+nmt_password_fields_finalize (GObject *object)
+{
+ NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (object);
+
+ if (priv->always_ask) {
+ g_signal_handlers_disconnect_by_func (priv->always_ask,
+ G_CALLBACK (always_ask_changed), object);
+ }
+ if (priv->show_password) {
+ g_signal_handlers_disconnect_by_func (priv->show_password,
+ G_CALLBACK (show_password_changed), object);
+ }
+
+ G_OBJECT_CLASS (nmt_password_fields_parent_class)->finalize (object);
+}
+
+static void
+nmt_password_fields_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtPasswordFields *fields = NMT_PASSWORD_FIELDS (object);
+ NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields);
+
+ switch (prop_id) {
+ case PROP_WIDTH:
+ nmt_newt_entry_set_width (priv->entry, g_value_get_int (value));
+ break;
+ case PROP_EXTRAS:
+ priv->extras = g_value_get_uint (value);
+ nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (fields));
+ break;
+ case PROP_PASSWORD:
+ nmt_password_fields_set_password (fields, g_value_get_string (value));
+ break;
+ case PROP_ALWAYS_ASK:
+ if (priv->always_ask)
+ nmt_newt_checkbox_set_active (priv->always_ask, g_value_get_boolean (value));
+ break;
+ case PROP_SHOW_PASSWORD:
+ if (priv->show_password)
+ nmt_newt_checkbox_set_active (priv->show_password, g_value_get_boolean (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_password_fields_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtPasswordFields *entry = NMT_PASSWORD_FIELDS (object);
+ NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (entry);
+
+ switch (prop_id) {
+ case PROP_WIDTH:
+ g_value_set_int (value, nmt_newt_entry_get_width (priv->entry));
+ break;
+ case PROP_EXTRAS:
+ g_value_set_uint (value, priv->extras);
+ break;
+ case PROP_PASSWORD:
+ g_value_set_string (value, nmt_password_fields_get_password (entry));
+ break;
+ case PROP_ALWAYS_ASK:
+ if (priv->always_ask)
+ g_value_set_boolean (value, nmt_newt_checkbox_get_active (priv->always_ask));
+ break;
+ case PROP_SHOW_PASSWORD:
+ if (priv->show_password)
+ g_value_set_boolean (value, nmt_newt_checkbox_get_active (priv->show_password));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_password_fields_class_init (NmtPasswordFieldsClass *entry_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+ g_type_class_add_private (entry_class, sizeof (NmtPasswordFieldsPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_password_fields_constructed;
+ object_class->set_property = nmt_password_fields_set_property;
+ object_class->get_property = nmt_password_fields_get_property;
+ object_class->finalize = nmt_password_fields_finalize;
+
+ /**
+ * NmtPasswordFields:width:
+ *
+ * The width in characters of the password entry
+ */
+ g_object_class_install_property
+ (object_class, PROP_WIDTH,
+ g_param_spec_int ("width", "", "",
+ -1, 80, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtPasswordFields:extras:
+ *
+ * The extra widgets to show
+ */
+ g_object_class_install_property
+ (object_class, PROP_EXTRAS,
+ g_param_spec_uint ("extras", "", "",
+ 0, 0xFFFF, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtPasswordFields:password:
+ *
+ * The entered password.
+ */
+ g_object_class_install_property
+ (object_class, PROP_PASSWORD,
+ g_param_spec_string ("password", "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtPasswordFields:always-ask:
+ *
+ * The current state of the "Always ask" checkbox.
+ */
+ g_object_class_install_property
+ (object_class, PROP_ALWAYS_ASK,
+ g_param_spec_boolean ("always-ask", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtPasswordFields:show-password:
+ *
+ * The current state of the "Show password" checkbox.
+ */
+ g_object_class_install_property
+ (object_class, PROP_SHOW_PASSWORD,
+ g_param_spec_boolean ("show-password", "", "",
+ FALSE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-password-fields.h b/clients/tui/nmt-password-fields.h
new file mode 100644
index 0000000000..2ef830aa24
--- /dev/null
+++ b/clients/tui/nmt-password-fields.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PASSWORD_FIELDS_H
+#define NMT_PASSWORD_FIELDS_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PASSWORD_FIELDS (nmt_password_fields_get_type ())
+#define NMT_PASSWORD_FIELDS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFields))
+#define NMT_PASSWORD_FIELDS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass))
+#define NMT_IS_PASSWORD_FIELDS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PASSWORD_FIELDS))
+#define NMT_IS_PASSWORD_FIELDS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PASSWORD_FIELDS))
+#define NMT_PASSWORD_FIELDS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass))
+
+typedef struct {
+ NmtNewtGrid parent;
+
+} NmtPasswordFields;
+
+typedef struct {
+ NmtNewtGridClass parent;
+
+} NmtPasswordFieldsClass;
+
+GType nmt_password_fields_get_type (void);
+
+typedef enum {
+ NMT_PASSWORD_FIELDS_ALWAYS_ASK = (1 << 0),
+ NMT_PASSWORD_FIELDS_SHOW_PASSWORD = (1 << 1),
+} NmtPasswordFieldsExtras;
+
+NmtNewtWidget *nmt_password_fields_new (int width,
+ NmtPasswordFieldsExtras extras);
+
+G_END_DECLS
+
+#endif /* NMT_PASSWORD_FIELDS_H */
diff --git a/clients/tui/nmt-route-editor.c b/clients/tui/nmt-route-editor.c
new file mode 100644
index 0000000000..98fd9ccbb6
--- /dev/null
+++ b/clients/tui/nmt-route-editor.c
@@ -0,0 +1,220 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-route-editor
+ * @short_description: Route editing dialog
+ *
+ * #NmtRouteEditor implements a form for editing IPv4 or IPv6 routes.
+ * This was implemented as a separate dialog because it seemed too
+ * wide to fit into the main window.
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-route-editor.h"
+#include "nmt-route-table.h"
+
+G_DEFINE_TYPE (NmtRouteEditor, nmt_route_editor, NMT_TYPE_NEWT_FORM)
+
+#define NMT_ROUTE_EDITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorPrivate))
+
+typedef struct {
+ NMSetting *orig_setting;
+ NMSetting *edit_setting;
+
+} NmtRouteEditorPrivate;
+
+enum {
+ PROP_0,
+ PROP_SETTING,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_route_editor_new:
+ * @setting: the #NMSettingIP4Config or #NMSettingIP6Config to edit
+ *
+ * Creates a new #NmtRouteEditor to edit the routes in @setting
+ *
+ * Returns: a new #NmtRouteEditor
+ */
+NmtNewtForm *
+nmt_route_editor_new (NMSetting *setting)
+{
+ return g_object_new (NMT_TYPE_ROUTE_EDITOR,
+ "setting", setting,
+ NULL);
+}
+
+static void
+nmt_route_editor_init (NmtRouteEditor *entry)
+{
+}
+
+static void
+save_routes_and_exit (NmtNewtButton *button,
+ gpointer user_data)
+{
+ NmtRouteEditor *editor = user_data;
+ NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (editor);
+ const char *property;
+ GBinding *binding;
+
+ if (NM_IS_SETTING_IP4_CONFIG (priv->edit_setting))
+ property = NM_SETTING_IP4_CONFIG_ROUTES;
+ else
+ property = NM_SETTING_IP6_CONFIG_ROUTES;
+
+ /* Because of the complicated dbus-glib GTypes, it's easier to cheat
+ * and use GBinding to do this than it is to copy the value by hand.
+ */
+ binding = g_object_bind_property (priv->edit_setting, property,
+ priv->orig_setting, property,
+ G_BINDING_SYNC_CREATE);
+ g_object_unref (binding);
+
+ nmt_newt_form_quit (NMT_NEWT_FORM (editor));
+}
+
+static void
+nmt_route_editor_constructed (GObject *object)
+{
+ NmtRouteEditor *editor = NMT_ROUTE_EDITOR (object);
+ NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (editor);
+ NmtNewtWidget *vbox, *routes, *buttons, *ok, *cancel;
+
+ if (G_OBJECT_CLASS (nmt_route_editor_parent_class)->constructed)
+ G_OBJECT_CLASS (nmt_route_editor_parent_class)->constructed (object);
+
+ if (NM_IS_SETTING_IP4_CONFIG (priv->edit_setting)) {
+ routes = nmt_route_table_new (AF_INET);
+ g_object_bind_property (priv->edit_setting, NM_SETTING_IP4_CONFIG_ROUTES,
+ routes, "ip4-routes",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ } else {
+ routes = nmt_route_table_new (AF_INET6);
+ g_object_bind_property (priv->edit_setting, NM_SETTING_IP6_CONFIG_ROUTES,
+ routes, "ip6-routes",
+ G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+ }
+
+ vbox = nmt_newt_grid_new ();
+ nmt_newt_grid_add (NMT_NEWT_GRID (vbox), routes, 0, 0);
+
+ buttons = nmt_newt_grid_new ();
+ nmt_newt_grid_add (NMT_NEWT_GRID (vbox), buttons, 0, 1);
+ nmt_newt_widget_set_padding (buttons, 0, 1, 0, 0);
+
+ cancel = g_object_ref_sink (nmt_newt_button_new (_("Cancel")));
+ nmt_newt_widget_set_exit_on_activate (cancel, TRUE);
+ nmt_newt_grid_add (NMT_NEWT_GRID (buttons), cancel, 0, 0);
+ nmt_newt_grid_set_flags (NMT_NEWT_GRID (buttons), cancel,
+ NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_ANCHOR_RIGHT |
+ NMT_NEWT_GRID_FILL_Y);
+
+ ok = g_object_ref_sink (nmt_newt_button_new (_("OK")));
+ g_signal_connect (ok, "clicked", G_CALLBACK (save_routes_and_exit), editor);
+ nmt_newt_grid_add (NMT_NEWT_GRID (buttons), ok, 1, 0);
+ nmt_newt_widget_set_padding (ok, 1, 0, 0, 0);
+ g_object_bind_property (routes, "valid",
+ ok, "sensitive",
+ G_BINDING_SYNC_CREATE);
+
+ nmt_newt_form_set_content (NMT_NEWT_FORM (editor), vbox);
+}
+
+static void
+nmt_route_editor_finalize (GObject *object)
+{
+ NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object);
+
+ g_clear_object (&priv->orig_setting);
+ g_clear_object (&priv->edit_setting);
+
+ G_OBJECT_CLASS (nmt_route_editor_parent_class)->finalize (object);
+}
+
+static void
+nmt_route_editor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_SETTING:
+ priv->orig_setting = g_value_dup_object (value);
+ priv->edit_setting = nm_setting_duplicate (priv->orig_setting);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_route_editor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_SETTING:
+ g_value_set_object (value, priv->edit_setting);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_route_editor_class_init (NmtRouteEditorClass *entry_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+ g_type_class_add_private (entry_class, sizeof (NmtRouteEditorPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_route_editor_constructed;
+ object_class->set_property = nmt_route_editor_set_property;
+ object_class->get_property = nmt_route_editor_get_property;
+ object_class->finalize = nmt_route_editor_finalize;
+
+ /**
+ * NmtRouteEditor:setting:
+ *
+ * The #NMSettingIP4Config or #NMSettingIP6Config whose routes are
+ * being edited.
+ */
+ g_object_class_install_property
+ (object_class, PROP_SETTING,
+ g_param_spec_object ("setting", "", "",
+ NM_TYPE_SETTING,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-route-editor.h b/clients/tui/nmt-route-editor.h
new file mode 100644
index 0000000000..e1040dffd9
--- /dev/null
+++ b/clients/tui/nmt-route-editor.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_ROUTE_EDITOR_H
+#define NMT_ROUTE_EDITOR_H
+
+#include <nm-connection.h>
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_ROUTE_EDITOR (nmt_route_editor_get_type ())
+#define NMT_ROUTE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditor))
+#define NMT_ROUTE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass))
+#define NMT_IS_ROUTE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_EDITOR))
+#define NMT_IS_ROUTE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_EDITOR))
+#define NMT_ROUTE_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass))
+
+typedef struct {
+ NmtNewtForm parent;
+
+} NmtRouteEditor;
+
+typedef struct {
+ NmtNewtFormClass parent;
+
+} NmtRouteEditorClass;
+
+GType nmt_route_editor_get_type (void);
+
+NmtNewtForm *nmt_route_editor_new (NMSetting *setting);
+
+G_END_DECLS
+
+#endif /* NMT_ROUTE_EDITOR_H */
diff --git a/clients/tui/nmt-route-entry.c b/clients/tui/nmt-route-entry.c
new file mode 100644
index 0000000000..70a9003b43
--- /dev/null
+++ b/clients/tui/nmt-route-entry.c
@@ -0,0 +1,328 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-route-entry
+ * @short_description: A set of widgets describing a single route.
+ *
+ * #NmtRouteEntry provides a set of three entry widgets, for entering
+ * a network/prefix, a next hop, and a metric.
+ *
+ * This is used as a building block by #NmtRouteTable.
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+#include <nm-setting-ip4-config.h>
+#include <nm-setting-ip6-config.h>
+
+#include "nmt-route-entry.h"
+#include "nmt-ip-entry.h"
+
+#include "nm-editor-bindings.h"
+
+G_DEFINE_TYPE (NmtRouteEntry, nmt_route_entry, NMT_TYPE_NEWT_GRID)
+
+#define NMT_ROUTE_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryPrivate))
+
+typedef struct {
+ NmtNewtWidget *dest, *next_hop, *metric;
+
+ int family;
+ int ip_entry_width, metric_entry_width;
+ NMIP4Route *ip4_route;
+ NMIP6Route *ip6_route;
+} NmtRouteEntryPrivate;
+
+enum {
+ PROP_0,
+ PROP_FAMILY,
+ PROP_IP_ENTRY_WIDTH,
+ PROP_METRIC_ENTRY_WIDTH,
+ PROP_IP4_ROUTE,
+ PROP_IP6_ROUTE,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_route_entry_new:
+ * @family: the address family, eg %AF_INET
+ * @ip_entry_width: the width in characters for the IP address entries
+ * @metric_entry_width: the width in characters for the metric entry
+ *
+ * Creates a new #NmtRouteEntry
+ *
+ * Returns: a new #NmtRouteEntry
+ */
+NmtNewtWidget *
+nmt_route_entry_new (int family,
+ int ip_entry_width,
+ int metric_entry_width)
+{
+ return g_object_new (NMT_TYPE_ROUTE_ENTRY,
+ "family", family,
+ "ip-entry-width", ip_entry_width,
+ "metric-entry-width", metric_entry_width,
+ NULL);
+}
+
+static void
+nmt_route_entry_init (NmtRouteEntry *entry)
+{
+}
+
+static gboolean
+entry_validity_transform_to_warning_label (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ if (g_value_get_boolean (source_value))
+ g_value_set_string (target_value, " ");
+ else
+ g_value_set_string (target_value, "!");
+ return TRUE;
+}
+
+static NmtNewtWidget *
+create_warning_label (NmtNewtWidget *entry)
+{
+ NmtNewtWidget *label;
+
+ label = g_object_new (NMT_TYPE_NEWT_LABEL,
+ "highlight", TRUE,
+ NULL);
+ g_object_bind_property_full (entry, "valid",
+ label, "text",
+ G_BINDING_SYNC_CREATE,
+ entry_validity_transform_to_warning_label,
+ NULL, NULL, NULL);
+ return label;
+}
+
+static void
+nmt_route_entry_constructed (GObject *object)
+{
+ NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object);
+ NmtNewtGrid *grid = NMT_NEWT_GRID (object);
+ NmtNewtWidget *warning_label;
+
+ priv->dest = nmt_ip_entry_new (priv->ip_entry_width, priv->family, TRUE, FALSE);
+ priv->next_hop = nmt_ip_entry_new (priv->ip_entry_width, priv->family, FALSE, TRUE);
+ priv->metric = nmt_newt_entry_numeric_new (priv->metric_entry_width, 0, 65535);
+
+ nmt_newt_grid_add (grid, priv->dest, 0, 0);
+ warning_label = create_warning_label (priv->dest);
+ nmt_newt_grid_add (grid, warning_label, 1, 0);
+
+ nmt_newt_grid_add (grid, priv->next_hop, 2, 0);
+ nmt_newt_widget_set_padding (priv->next_hop, 1, 0, 0, 0);
+ warning_label = create_warning_label (priv->next_hop);
+ nmt_newt_grid_add (grid, warning_label, 3, 0);
+
+ nmt_newt_grid_add (grid, priv->metric, 4, 0);
+ nmt_newt_widget_set_padding (priv->metric, 1, 0, 0, 0);
+
+ if (priv->family == AF_INET) {
+ nm_editor_bind_ip4_route_to_strings (object, "ip4-route",
+ priv->dest, "text",
+ priv->next_hop, "text",
+ priv->metric, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ } else if (priv->family == AF_INET6) {
+ nm_editor_bind_ip6_route_to_strings (object, "ip6-route",
+ priv->dest, "text",
+ priv->next_hop, "text",
+ priv->metric, "text",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+ } else
+ g_assert_not_reached ();
+
+ G_OBJECT_CLASS (nmt_route_entry_parent_class)->constructed (object);
+}
+
+static newtComponent
+nmt_route_entry_get_focus_component (NmtNewtWidget *widget)
+{
+ NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (widget);
+
+ return nmt_newt_widget_get_focus_component (priv->dest);
+}
+
+static void
+nmt_route_entry_finalize (GObject *object)
+{
+ NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object);
+
+ g_clear_pointer (&priv->ip4_route, nm_ip4_route_unref);
+ g_clear_pointer (&priv->ip6_route, nm_ip6_route_unref);
+
+ G_OBJECT_CLASS (nmt_route_entry_parent_class)->finalize (object);
+}
+
+static void
+nmt_route_entry_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FAMILY:
+ priv->family = g_value_get_int (value);
+ break;
+ case PROP_IP_ENTRY_WIDTH:
+ priv->ip_entry_width = g_value_get_int (value);
+ break;
+ case PROP_METRIC_ENTRY_WIDTH:
+ priv->metric_entry_width = g_value_get_int (value);
+ break;
+ case PROP_IP4_ROUTE:
+ g_return_if_fail (priv->family == AF_INET);
+ if (priv->ip4_route)
+ nm_ip4_route_unref (priv->ip4_route);
+ priv->ip4_route = g_value_dup_boxed (value);
+ break;
+ case PROP_IP6_ROUTE:
+ g_return_if_fail (priv->family == AF_INET6);
+ if (priv->ip6_route)
+ nm_ip6_route_unref (priv->ip6_route);
+ priv->ip6_route = g_value_dup_boxed (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_route_entry_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FAMILY:
+ g_value_set_int (value, priv->family);
+ break;
+ case PROP_IP_ENTRY_WIDTH:
+ g_value_set_int (value, priv->ip_entry_width);
+ break;
+ case PROP_METRIC_ENTRY_WIDTH:
+ g_value_set_int (value, priv->metric_entry_width);
+ break;
+ case PROP_IP4_ROUTE:
+ g_return_if_fail (priv->family == AF_INET);
+ g_value_set_boxed (value, priv->ip4_route);
+ break;
+ case PROP_IP6_ROUTE:
+ g_return_if_fail (priv->family == AF_INET6);
+ g_value_set_boxed (value, priv->ip6_route);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_route_entry_class_init (NmtRouteEntryClass *entry_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (entry_class);
+
+ g_type_class_add_private (entry_class, sizeof (NmtRouteEntryPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_route_entry_constructed;
+ object_class->set_property = nmt_route_entry_set_property;
+ object_class->get_property = nmt_route_entry_get_property;
+ object_class->finalize = nmt_route_entry_finalize;
+
+ widget_class->get_focus_component = nmt_route_entry_get_focus_component;
+
+ /**
+ * NmtRouteEntry:family:
+ *
+ * The address family of the route, eg, %AF_INET
+ */
+ g_object_class_install_property
+ (object_class, PROP_FAMILY,
+ g_param_spec_int ("family", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtRouteEntry:ip-entry-width:
+ *
+ * The width in characters of the IP address entries
+ */
+ g_object_class_install_property
+ (object_class, PROP_IP_ENTRY_WIDTH,
+ g_param_spec_int ("ip-entry-width", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtRouteEntry:metric-entry-width:
+ *
+ * The width in characters of the metric entry
+ */
+ g_object_class_install_property
+ (object_class, PROP_METRIC_ENTRY_WIDTH,
+ g_param_spec_int ("metric-entry-width", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtRouteEntry:ip4-route:
+ *
+ * The contents of the entries, as an #NMIP4Route. Only valid
+ * if #NmtRouteEntry:family is %AF_INET.
+ */
+ g_object_class_install_property
+ (object_class, PROP_IP4_ROUTE,
+ g_param_spec_boxed ("ip4-route", "", "",
+ nm_ip4_route_get_type (),
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtRouteEntry:ip6-route:
+ *
+ * The contents of the entries, as an #NMIP6Route. Only valid
+ * if #NmtRouteEntry:family is %AF_INET6.
+ */
+ g_object_class_install_property
+ (object_class, PROP_IP6_ROUTE,
+ g_param_spec_boxed ("ip6-route", "", "",
+ nm_ip6_route_get_type (),
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-route-entry.h b/clients/tui/nmt-route-entry.h
new file mode 100644
index 0000000000..d37b14cd5b
--- /dev/null
+++ b/clients/tui/nmt-route-entry.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_ROUTE_ENTRY_H
+#define NMT_ROUTE_ENTRY_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_ROUTE_ENTRY (nmt_route_entry_get_type ())
+#define NMT_ROUTE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntry))
+#define NMT_ROUTE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass))
+#define NMT_IS_ROUTE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_ENTRY))
+#define NMT_IS_ROUTE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_ENTRY))
+#define NMT_ROUTE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass))
+
+typedef struct {
+ NmtNewtGrid parent;
+
+} NmtRouteEntry;
+
+typedef struct {
+ NmtNewtGridClass parent;
+
+} NmtRouteEntryClass;
+
+GType nmt_route_entry_get_type (void);
+
+NmtNewtWidget *nmt_route_entry_new (int family,
+ int ip_entry_width,
+ int metric_entry_width);
+
+G_END_DECLS
+
+#endif /* NMT_ROUTE_ENTRY_H */
diff --git a/clients/tui/nmt-route-table.c b/clients/tui/nmt-route-table.c
new file mode 100644
index 0000000000..c2edf2ac5c
--- /dev/null
+++ b/clients/tui/nmt-route-table.c
@@ -0,0 +1,398 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-route-table
+ * @short_description: An editable list of IP4 or IP6 routes
+ *
+ * #NmtRouteTable implements a list of #NmtRouteEntry, plus headers,
+ * and buttons to add and remove entries.
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+#include <dbus/dbus-glib.h>
+#include <nm-utils.h>
+
+#include "nmt-route-table.h"
+#include "nmt-route-entry.h"
+#include "nmt-widget-list.h"
+
+G_DEFINE_TYPE (NmtRouteTable, nmt_route_table, NMT_TYPE_NEWT_GRID)
+
+#define NMT_ROUTE_TABLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ROUTE_TABLE, NmtRouteTablePrivate))
+
+typedef struct {
+ int family;
+
+ int ip_entry_width;
+ int metric_entry_width;
+
+ GSList *routes;
+ NmtNewtWidget *list;
+} NmtRouteTablePrivate;
+
+enum {
+ PROP_0,
+ PROP_FAMILY,
+ PROP_IP4_ROUTES,
+ PROP_IP6_ROUTES,
+
+ LAST_PROP
+};
+
+/**
+ * nmt_route_table_new:
+ * @family: the address family, eg %AF_INET
+ *
+ * Creates a new #NmtRouteTable
+ *
+ * Returns: a new #NmtRouteTable
+ */
+NmtNewtWidget *
+nmt_route_table_new (int family)
+{
+ return g_object_new (NMT_TYPE_ROUTE_TABLE,
+ "family", family,
+ NULL);
+}
+
+static gboolean
+route_list_transform_to_route (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NmtRouteTable *table = NMT_ROUTE_TABLE (g_binding_get_source (binding));
+ NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table);
+ int n = GPOINTER_TO_INT (user_data);
+ gpointer route;
+
+ route = g_slist_nth_data (priv->routes, n);
+ if (route)
+ g_value_set_boxed (target_value, route);
+ return route != NULL;
+}
+
+static gboolean
+route_list_transform_from_route (GBinding *binding,
+ const GValue *source_value,
+ GValue *target_value,
+ gpointer user_data)
+{
+ NmtRouteTable *table = NMT_ROUTE_TABLE (g_binding_get_source (binding));
+ NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table);
+ int n = GPOINTER_TO_INT (user_data);
+ GSList *routes, *nth;
+
+ nth = g_slist_nth (priv->routes, n);
+ if (!nth)
+ return FALSE;
+
+ routes = priv->routes;
+ priv->routes = NULL;
+
+ if (nth->data) {
+ if (priv->family == AF_INET)
+ nm_ip4_route_unref (nth->data);
+ else if (priv->family == AF_INET6)
+ nm_ip6_route_unref (nth->data);
+ }
+ nth->data = g_value_dup_boxed (source_value);
+
+ if (priv->family == AF_INET) {
+ nm_utils_ip4_routes_to_gvalue (routes, target_value);
+ g_slist_free_full (routes, (GDestroyNotify) nm_ip4_route_unref);
+ } else if (priv->family == AF_INET6) {
+ nm_utils_ip6_routes_to_gvalue (routes, target_value);
+ g_slist_free_full (routes, (GDestroyNotify) nm_ip6_route_unref);
+ }
+
+ return TRUE;
+}
+
+static NmtNewtWidget *
+create_route_entry (NmtWidgetList *list,
+ int num,
+ gpointer table)
+{
+ NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table);
+ NmtNewtWidget *entry;
+
+ entry = nmt_route_entry_new (priv->family,
+ priv->ip_entry_width,
+ priv->metric_entry_width);
+
+ if (priv->family == AF_INET) {
+ g_object_bind_property_full (table, "ip4-routes",
+ entry, "ip4-route",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
+ route_list_transform_to_route,
+ route_list_transform_from_route,
+ GINT_TO_POINTER (num), NULL);
+ } else {
+ g_object_bind_property_full (table, "ip6-routes",
+ entry, "ip6-route",
+ G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
+ route_list_transform_to_route,
+ route_list_transform_from_route,
+ GINT_TO_POINTER (num), NULL);
+ }
+ return entry;
+}
+
+static void
+add_route (NmtWidgetList *list,
+ gpointer table)
+{
+ NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table);
+
+ if (priv->family == AF_INET) {
+ NMIP4Route *route;
+
+ route = nm_ip4_route_new ();
+ nm_ip4_route_set_prefix (route, 32);
+ priv->routes = g_slist_append (priv->routes, route);
+ nmt_widget_list_set_length (list, g_slist_length (priv->routes));
+ g_object_notify (table, "ip4-routes");
+ } else {
+ NMIP6Route *route;
+
+ route = nm_ip6_route_new ();
+ nm_ip6_route_set_prefix (route, 128);
+ priv->routes = g_slist_append (priv->routes, route);
+ nmt_widget_list_set_length (list, g_slist_length (priv->routes));
+ g_object_notify (table, "ip6-routes");
+ }
+}
+
+static void
+remove_route (NmtWidgetList *list,
+ int num,
+ gpointer table)
+{
+ NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table);
+ GSList *nth;
+ gpointer route;
+
+ nth = g_slist_nth (priv->routes, num);
+ if (!nth)
+ return;
+
+ route = nth->data;
+ priv->routes = g_slist_delete_link (priv->routes, nth);
+ nmt_widget_list_set_length (list, g_slist_length (priv->routes));
+
+ if (priv->family == AF_INET) {
+ nm_ip4_route_unref (route);
+ g_object_notify (table, "ip4-routes");
+ } else {
+ nm_ip6_route_unref (route);
+ g_object_notify (table, "ip6-routes");
+ }
+}
+
+static void
+nmt_route_table_init (NmtRouteTable *table)
+{
+ NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table);
+ NmtNewtWidget *header, *empty;
+ NmtNewtWidget *dest_prefix_label, *next_hop_label, *metric_label;
+ int dest_prefix_width, next_hop_width, metric_width;
+ char *text;
+
+ header = nmt_newt_grid_new ();
+
+ text = g_strdup_printf ("%s/%s", _("Destination"), _("Prefix"));
+ dest_prefix_width = nmt_newt_text_width (text);
+ dest_prefix_label = g_object_new (NMT_TYPE_NEWT_LABEL,
+ "text", text,
+ "style", NMT_NEWT_LABEL_PLAIN,
+ NULL);
+ g_free (text);
+ nmt_newt_grid_add (NMT_NEWT_GRID (header), dest_prefix_label, 0, 0);
+
+ text = _("Next Hop");
+ next_hop_label = g_object_new (NMT_TYPE_NEWT_LABEL,
+ "text", text,
+ "style", NMT_NEWT_LABEL_PLAIN,
+ NULL);
+ next_hop_width = nmt_newt_text_width (text);
+ nmt_newt_grid_add (NMT_NEWT_GRID (header), next_hop_label, 1, 0);
+
+ text = _("Metric");
+ metric_label = g_object_new (NMT_TYPE_NEWT_LABEL,
+ "text", text,
+ "style", NMT_NEWT_LABEL_PLAIN,
+ NULL);
+ metric_width = nmt_newt_text_width (text);
+ nmt_newt_grid_add (NMT_NEWT_GRID (header), metric_label, 2, 0);
+
+ priv->ip_entry_width = MAX (20, MAX (dest_prefix_width, next_hop_width));
+ priv->metric_entry_width = MAX (7, metric_width);
+
+ nmt_newt_widget_set_padding (dest_prefix_label,
+ 0, 0, priv->ip_entry_width - dest_prefix_width, 0);
+ nmt_newt_widget_set_padding (next_hop_label,
+ 2, 0, priv->ip_entry_width - next_hop_width, 0);
+ nmt_newt_widget_set_padding (metric_label,
+ 2, 0, priv->metric_entry_width - metric_width, 0);
+
+ nmt_newt_grid_add (NMT_NEWT_GRID (table), header, 0, 0);
+
+ empty = nmt_newt_label_new (_("No custom routes are defined."));
+ priv->list = nmt_widget_list_new (create_route_entry, table, NULL, empty);
+ g_signal_connect (priv->list, "add-clicked", G_CALLBACK (add_route), table);
+ g_signal_connect (priv->list, "remove-clicked", G_CALLBACK (remove_route), table);
+ nmt_newt_grid_add (NMT_NEWT_GRID (table), priv->list, 0, 1);
+}
+
+static void
+nmt_route_table_finalize (GObject *object)
+{
+ NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (object);
+
+ if (priv->family == AF_INET)
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+ else if (priv->family == AF_INET6)
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip6_route_unref);
+
+ G_OBJECT_CLASS (nmt_route_table_parent_class)->finalize (object);
+}
+
+static void
+nmt_route_table_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FAMILY:
+ priv->family = g_value_get_int (value);
+ break;
+ case PROP_IP4_ROUTES:
+ g_return_if_fail (priv->family == AF_INET);
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref);
+ priv->routes = nm_utils_ip4_routes_from_gvalue (value);
+ nmt_widget_list_set_length (NMT_WIDGET_LIST (priv->list),
+ g_slist_length (priv->routes));
+ break;
+ case PROP_IP6_ROUTES:
+ g_return_if_fail (priv->family == AF_INET6);
+ g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip6_route_unref);
+ priv->routes = nm_utils_ip6_routes_from_gvalue (value);
+ nmt_widget_list_set_length (NMT_WIDGET_LIST (priv->list),
+ g_slist_length (priv->routes));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_route_table_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_FAMILY:
+ g_value_set_int (value, priv->family);
+ break;
+ case PROP_IP4_ROUTES:
+ g_return_if_fail (priv->family == AF_INET);
+ nm_utils_ip4_routes_to_gvalue (priv->routes, value);
+ break;
+ case PROP_IP6_ROUTES:
+ g_return_if_fail (priv->family == AF_INET6);
+ nm_utils_ip6_routes_to_gvalue (priv->routes, value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+#define DBUS_TYPE_G_ARRAY_OF_UINT (dbus_g_type_get_collection ("GArray", G_TYPE_UINT))
+#define DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_ARRAY_OF_UINT))
+#define DBUS_TYPE_G_IP6_ROUTE (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID))
+#define DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_IP6_ROUTE))
+
+static void
+nmt_route_table_class_init (NmtRouteTableClass *table_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (table_class);
+
+ g_type_class_add_private (table_class, sizeof (NmtRouteTablePrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_route_table_set_property;
+ object_class->get_property = nmt_route_table_get_property;
+ object_class->finalize = nmt_route_table_finalize;
+
+ /**
+ * NmtRouteTable:family:
+ *
+ * The network address family of the routes, eg %AF_INET
+ */
+ g_object_class_install_property
+ (object_class, PROP_FAMILY,
+ g_param_spec_int ("family", "", "",
+ -1, G_MAXINT, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtRouteTable:ip4-routes:
+ *
+ * The array of routes, suitable for binding to
+ * #NMSettingIP4Config:routes.
+ *
+ * Only valid if #NmtRouteTable:family is %AF_INET
+ */
+ g_object_class_install_property
+ (object_class, PROP_IP4_ROUTES,
+ g_param_spec_boxed ("ip4-routes", "", "",
+ DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtRouteTable:ip6-routes:
+ *
+ * The array of routes, suitable for binding to
+ * #NMSettingIP6Config:routes.
+ *
+ * Only valid if #NmtRouteTable:family is %AF_INET6
+ */
+ g_object_class_install_property
+ (object_class, PROP_IP6_ROUTES,
+ g_param_spec_boxed ("ip6-routes", "", "",
+ DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-route-table.h b/clients/tui/nmt-route-table.h
new file mode 100644
index 0000000000..217bf40da0
--- /dev/null
+++ b/clients/tui/nmt-route-table.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_ROUTE_TABLE_H
+#define NMT_ROUTE_TABLE_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_ROUTE_TABLE (nmt_route_table_get_type ())
+#define NMT_ROUTE_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTable))
+#define NMT_ROUTE_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass))
+#define NMT_IS_ROUTE_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_TABLE))
+#define NMT_IS_ROUTE_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_TABLE))
+#define NMT_ROUTE_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass))
+
+typedef struct {
+ NmtNewtGrid parent;
+
+} NmtRouteTable;
+
+typedef struct {
+ NmtNewtGridClass parent;
+
+} NmtRouteTableClass;
+
+GType nmt_route_table_get_type (void);
+
+NmtNewtWidget *nmt_route_table_new (int family);
+
+G_END_DECLS
+
+#endif /* NMT_ROUTE_TABLE_H */
diff --git a/clients/tui/nmt-secret-agent.c b/clients/tui/nmt-secret-agent.c
new file mode 100644
index 0000000000..7939a84379
--- /dev/null
+++ b/clients/tui/nmt-secret-agent.c
@@ -0,0 +1,646 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2011-2013 Red Hat, Inc.
+ * Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
+ */
+
+/**
+ * SECTION:nmt-secret-agent
+ * @short_description: A secret agent
+ *
+ * #NmtSecretAgent is the secret agent used by nmtui-connect.
+ *
+ * This is a stripped-down version of gnome-shell's ShellNetworkAgent,
+ * with bits of the corresponding JavaScript code squished down into
+ * it. It is intended to eventually be generic enough that it could
+ * replace ShellNetworkAgent.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <nm-utils.h>
+
+#include "nmt-secret-agent.h"
+#include "nmt-newt.h"
+
+G_DEFINE_TYPE (NmtSecretAgent, nmt_secret_agent, NM_TYPE_SECRET_AGENT)
+
+#define NMT_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_SECRET_AGENT, NmtSecretAgentPrivate))
+
+enum {
+ REQUEST_SECRETS,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct {
+ NmtSecretAgent *self;
+
+ gchar *request_id;
+ NMConnection *connection;
+ gchar **hints;
+ NMSecretAgentGetSecretsFunc callback;
+ gpointer callback_data;
+} NmtSecretAgentRequest;
+
+typedef struct {
+ /* <char *request_id, NmtSecretAgentRequest *request> */
+ GHashTable *requests;
+} NmtSecretAgentPrivate;
+
+static void
+nmt_secret_agent_request_free (gpointer data)
+{
+ NmtSecretAgentRequest *request = data;
+
+ g_object_unref (request->self);
+ g_object_unref (request->connection);
+ g_strfreev (request->hints);
+
+ g_slice_free (NmtSecretAgentRequest, request);
+}
+
+static void
+nmt_secret_agent_init (NmtSecretAgent *agent)
+{
+ NmtSecretAgentPrivate *priv = NMT_SECRET_AGENT_GET_PRIVATE (agent);
+
+ priv->requests = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, nmt_secret_agent_request_free);
+}
+
+static void
+nmt_secret_agent_finalize (GObject *object)
+{
+ NmtSecretAgentPrivate *priv = NMT_SECRET_AGENT_GET_PRIVATE (object);
+ GError *error;
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+
+ error = g_error_new (NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
+ "The secret agent is going away");
+
+ g_hash_table_iter_init (&iter, priv->requests);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ NmtSecretAgentRequest *request = value;
+
+ request->callback (NM_SECRET_AGENT (object),
+ request->connection,
+ NULL, error,
+ request->callback_data);
+ }
+
+ g_hash_table_destroy (priv->requests);
+ g_error_free (error);
+
+ G_OBJECT_CLASS (nmt_secret_agent_parent_class)->finalize (object);
+}
+
+static gboolean
+strv_has (gchar **haystack,
+ gchar *needle)
+{
+ gchar *iter;
+
+ for (iter = *haystack; iter; iter++) {
+ if (g_strcmp0 (iter, needle) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/**
+ * NmtSecretAgentSecret:
+ * @name: the user-visible name of the secret. Eg, "WEP Passphrase".
+ * @value: the value of the secret
+ * @password: %TRUE if this secret represents a password, %FALSE
+ * if it represents non-secret data.
+ *
+ * A single "secret" being requested.
+ */
+
+typedef struct {
+ NmtSecretAgentSecret base;
+
+ NMSetting *setting;
+ char *property;
+
+ NmtNewtEntryValidator validator;
+ gpointer validator_data;
+} NmtSecretAgentSecretReal;
+
+static void
+nmt_secret_agent_secret_free (NmtSecretAgentSecret *secret)
+{
+ NmtSecretAgentSecretReal *real = (NmtSecretAgentSecretReal *)secret;
+
+ g_free (secret->name);
+ g_free (secret->value);
+ g_free (real->property);
+ g_clear_object (&real->setting);
+
+ g_slice_free (NmtSecretAgentSecretReal, real);
+}
+
+static NmtSecretAgentSecret *
+nmt_secret_agent_secret_new (const char *name,
+ NMSetting *setting,
+ const char *property,
+ gboolean password)
+{
+ NmtSecretAgentSecretReal *real;
+
+ real = g_slice_new0 (NmtSecretAgentSecretReal);
+ real->base.name = g_strdup (name);
+ real->base.password = password;
+
+ if (setting) {
+ real->setting = g_object_ref (setting);
+ real->property = g_strdup (property);
+
+ g_object_get (setting, property, &real->base.value, NULL);
+ }
+
+ return &real->base;
+}
+
+static gboolean
+add_8021x_secrets (NmtSecretAgentRequest *request,
+ GPtrArray *secrets)
+{
+ NMSetting8021x *s_8021x = nm_connection_get_setting_802_1x (request->connection);
+ const char *eap_method;
+ NmtSecretAgentSecret *secret;
+
+ eap_method = nm_setting_802_1x_get_eap_method (s_8021x, 0);
+ if (!eap_method)
+ return FALSE;
+
+ if ( !strcmp (eap_method, "md5")
+ || !strcmp (eap_method, "leap")
+ || !strcmp (eap_method, "ttls")
+ || !strcmp (eap_method, "peap")) {
+ /* TTLS and PEAP are actually much more complicated, but this complication
+ * is not visible here since we only care about phase2 authentication
+ * (and don't even care of which one)
+ */
+ secret = nmt_secret_agent_secret_new (_("Username"),
+ NM_SETTING (s_8021x),
+ NM_SETTING_802_1X_IDENTITY,
+ FALSE);
+ g_ptr_array_add (secrets, secret);
+ secret = nmt_secret_agent_secret_new (_("Password"),
+ NM_SETTING (s_8021x),
+ NM_SETTING_802_1X_PASSWORD,
+ TRUE);
+ g_ptr_array_add (secrets, secret);
+ return TRUE;
+ }
+
+ if (!strcmp (eap_method, "tls")) {
+ secret = nmt_secret_agent_secret_new (_("Identity"),
+ NM_SETTING (s_8021x),
+ NM_SETTING_802_1X_IDENTITY,
+ FALSE);
+ g_ptr_array_add (secrets, secret);
+ secret = nmt_secret_agent_secret_new (_("Private key password"),
+ NM_SETTING (s_8021x),
+ NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
+ TRUE);
+ g_ptr_array_add (secrets, secret);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+add_wireless_secrets (NmtSecretAgentRequest *request,
+ GPtrArray *secrets)
+{
+ NMSettingWirelessSecurity *s_wsec = nm_connection_get_setting_wireless_security (request->connection);
+ const char *key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
+ NmtSecretAgentSecret *secret;
+
+ if (!key_mgmt)
+ return FALSE;
+
+ if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) {
+ secret = nmt_secret_agent_secret_new (_("Password"),
+ NM_SETTING (s_wsec),
+ NM_SETTING_WIRELESS_SECURITY_PSK,
+ TRUE);
+ g_ptr_array_add (secrets, secret);
+ return TRUE;
+ }
+
+ if (!strcmp (key_mgmt, "none")) {
+ int index;
+ char *key;
+
+ index = nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec);
+ key = g_strdup_printf ("wep-key%d", index);
+ secret = nmt_secret_agent_secret_new (_("Key"),
+ NM_SETTING (s_wsec),
+ key,
+ TRUE);
+ g_free (key);
+
+#if 0
+ nmt_secret_agent_secret_set_validator (secret, static_wep_key_validate,
+ nm_setting_wireless_security_get_wep_key_type (s_wsec));
+#endif
+ g_ptr_array_add (secrets, secret);
+ return TRUE;
+ }
+
+ if (!strcmp (key_mgmt, "iee8021x")) {
+ if (!g_strcmp0 (nm_setting_wireless_security_get_auth_alg (s_wsec), "leap")) {
+ secret = nmt_secret_agent_secret_new (_("Password"),
+ NM_SETTING (s_wsec),
+ NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
+ TRUE);
+ g_ptr_array_add (secrets, secret);
+ return TRUE;
+ } else
+ return add_8021x_secrets (request, secrets);
+ }
+
+ if (!strcmp (key_mgmt, "wpa-eap"))
+ return add_8021x_secrets (request, secrets);
+
+ return FALSE;
+}
+
+static gboolean
+add_pppoe_secrets (NmtSecretAgentRequest *request,
+ GPtrArray *secrets)
+{
+ NMSettingPPPOE *s_pppoe = nm_connection_get_setting_pppoe (request->connection);
+ NmtSecretAgentSecret *secret;
+
+ secret = nmt_secret_agent_secret_new (_("Username"),
+ NM_SETTING (s_pppoe),
+ NM_SETTING_PPPOE_USERNAME,
+ FALSE);
+ g_ptr_array_add (secrets, secret);
+ secret = nmt_secret_agent_secret_new (_("Service"),
+ NM_SETTING (s_pppoe),
+ NM_SETTING_PPPOE_SERVICE,
+ FALSE);
+ g_ptr_array_add (secrets, secret);
+ secret = nmt_secret_agent_secret_new (_("Password"),
+ NM_SETTING (s_pppoe),
+ NM_SETTING_PPPOE_PASSWORD,
+ TRUE);
+ g_ptr_array_add (secrets, secret);
+ return TRUE;
+}
+
+static void
+request_secrets_from_ui (NmtSecretAgentRequest *request)
+{
+ GPtrArray *secrets;
+ NmtSecretAgentSecret *secret;
+ const char *title;
+ char *msg;
+ gboolean ok = TRUE;
+
+ secrets = g_ptr_array_new_with_free_func ((GDestroyNotify) nmt_secret_agent_secret_free);
+
+ if (nm_connection_is_type (request->connection, NM_SETTING_WIRELESS_SETTING_NAME)) {
+ NMSettingWireless *s_wireless;
+ char *ssid;
+
+ s_wireless = nm_connection_get_setting_wireless (request->connection);
+ ssid = nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wireless));
+
+ title = _("Authentication required by wireless network");
+ msg = g_strdup_printf (_("Passwords or encryption keys are required to access the wireless network '%s'."), ssid);
+
+ ok = add_wireless_secrets (request, secrets);
+ } else if (nm_connection_is_type (request->connection, NM_SETTING_WIRED_SETTING_NAME)) {
+ NMSettingConnection *s_con;
+
+ s_con = nm_connection_get_setting_connection (request->connection);
+
+ title = _("Wired 802.1X authentication");
+ msg = NULL;
+
+ secret = nmt_secret_agent_secret_new (_("Network name"),
+ NM_SETTING (s_con),
+ NM_SETTING_CONNECTION_ID,
+ FALSE);
+ g_ptr_array_add (secrets, secret);
+ ok = add_8021x_secrets (request, secrets);
+ } else if (nm_connection_is_type (request->connection, NM_SETTING_PPPOE_SETTING_NAME)) {
+ title = _("DSL authentication");
+ msg = NULL;
+
+ ok = add_pppoe_secrets (request, secrets);
+ } else if (nm_connection_is_type (request->connection, NM_SETTING_GSM_SETTING_NAME)) {
+ NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (request->connection);
+
+ if (strv_has (request->hints, "pin")) {
+ title = _("PIN code required");
+ msg = g_strdup (_("PIN code is needed for the mobile broadband device"));
+
+ secret = nmt_secret_agent_secret_new (_("PIN"),
+ NM_SETTING (s_gsm),
+ NM_SETTING_GSM_PIN,
+ FALSE);
+ g_ptr_array_add (secrets, secret);
+ } else {
+ title = _("Mobile broadband network password");
+ msg = g_strdup_printf (_("A password is required to connect to '%s'."),
+ nm_connection_get_id (request->connection));
+
+ secret = nmt_secret_agent_secret_new (_("Password"),
+ NM_SETTING (s_gsm),
+ NM_SETTING_GSM_PASSWORD,
+ TRUE);
+ g_ptr_array_add (secrets, secret);
+ }
+ } else if (nm_connection_is_type (request->connection, NM_SETTING_CDMA_SETTING_NAME)) {
+ NMSettingCdma *s_cdma = nm_connection_get_setting_cdma (request->connection);
+
+ title = _("Mobile broadband network password");
+ msg = g_strdup_printf (_("A password is required to connect to '%s'."),
+ nm_connection_get_id (request->connection));
+
+ secret = nmt_secret_agent_secret_new (_("Password"),
+ NM_SETTING (s_cdma),
+ NM_SETTING_CDMA_PASSWORD,
+ TRUE);
+ g_ptr_array_add (secrets, secret);
+ } else if (nm_connection_is_type (request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
+ NMSetting *setting;
+
+ setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_GSM_SETTING_NAME);
+ if (!setting)
+ setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_CDMA_SETTING_NAME);
+
+ title = _("Mobile broadband network password");
+ msg = g_strdup_printf (_("A password is required to connect to '%s'."),
+ nm_connection_get_id (request->connection));
+
+ secret = nmt_secret_agent_secret_new (_("Password"),
+ setting,
+ "password",
+ TRUE);
+ g_ptr_array_add (secrets, secret);
+ } else
+ ok = FALSE;
+
+ if (!ok) {
+ g_ptr_array_unref (secrets);
+ return;
+ }
+
+ g_signal_emit (request->self, signals[REQUEST_SECRETS], 0,
+ request->request_id, title, msg, secrets);
+}
+
+static void
+nmt_secret_agent_get_secrets (NMSecretAgent *agent,
+ NMConnection *connection,
+ const gchar *connection_path,
+ const gchar *setting_name,
+ const gchar **hints,
+ NMSecretAgentGetSecretsFlags flags,
+ NMSecretAgentGetSecretsFunc callback,
+ gpointer callback_data)
+{
+ NmtSecretAgent *self = NMT_SECRET_AGENT (agent);
+ NmtSecretAgentPrivate *priv = NMT_SECRET_AGENT_GET_PRIVATE (self);
+ NmtSecretAgentRequest *request;
+ NMSettingConnection *s_con;
+ const char *connection_type;
+ char *request_id;
+ GError *error;
+
+ request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
+ if (g_hash_table_lookup (priv->requests, request_id) != NULL) {
+ /* We already have a request pending for this (connection, setting) */
+ error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
+ "Request for %s secrets already pending", request_id);
+ nope:
+ callback (agent, connection, NULL, error, callback_data);
+ g_error_free (error);
+ g_free (request_id);
+ return;
+ }
+
+ s_con = nm_connection_get_setting_connection (connection);
+ connection_type = nm_setting_connection_get_connection_type (s_con);
+
+ if (!strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME)) {
+ /* We don't support VPN secrets yet */
+ error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS,
+ "VPN secrets not supported");
+ goto nope;
+ }
+
+ if (!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)) {
+ /* We don't do stored passwords */
+ error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS,
+ "Stored passwords not supported");
+ goto nope;
+ }
+
+ request = g_slice_new (NmtSecretAgentRequest);
+ request->self = g_object_ref (self);
+ request->connection = g_object_ref (connection);
+ request->hints = g_strdupv ((gchar **)hints);
+ request->callback = callback;
+ request->callback_data = callback_data;
+ request->request_id = request_id;
+ g_hash_table_replace (priv->requests, request->request_id, request);
+
+ request_secrets_from_ui (request);
+}
+
+static void
+gvalue_destroy_notify (gpointer data)
+{
+ GValue *value = data;
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+/**
+ * nmt_secret_agent_response:
+ * @self: the #NmtSecretAgent
+ * @request_id: the request ID being responded to
+ * @secrets: (allow-none): the array of secrets, or %NULL
+ *
+ * Response to a #NmtSecretAgent::get-secrets signal.
+ *
+ * If the user provided secrets, the caller should set the
+ * corresponding <literal>value</literal> fields in the
+ * #NmtSecretAgentSecrets (freeing any initial values they had), and
+ * pass the array to nmt_secret_agent_response(). If the user
+ * cancelled the request, @secrets should be NULL.
+ */
+void
+nmt_secret_agent_response (NmtSecretAgent *self,
+ const char *request_id,
+ GPtrArray *secrets)
+{
+ NmtSecretAgentPrivate *priv;
+ NmtSecretAgentRequest *request;
+ GHashTable *hash = NULL, *setting_hash;
+ GValue *value;
+ GError *error = NULL;
+ int i;
+
+ g_return_if_fail (NMT_IS_SECRET_AGENT (self));
+
+ priv = NMT_SECRET_AGENT_GET_PRIVATE (self);
+ request = g_hash_table_lookup (priv->requests, request_id);
+ g_return_if_fail (request != NULL);
+
+ if (secrets) {
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_hash_table_unref);
+ for (i = 0; i < secrets->len; i++) {
+ NmtSecretAgentSecretReal *secret = secrets->pdata[i];
+
+ setting_hash = g_hash_table_lookup (hash, nm_setting_get_name (secret->setting));
+ if (!setting_hash) {
+ setting_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, gvalue_destroy_notify);
+ g_hash_table_insert (hash, (char *)nm_setting_get_name (secret->setting),
+ setting_hash);
+ }
+
+ value = g_slice_new0 (GValue);
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_string (value, secret->base.value);
+
+ g_hash_table_insert (setting_hash, g_strdup (secret->property), value);
+ }
+ } else {
+ error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED,
+ "User cancelled");
+ }
+
+ request->callback (NM_SECRET_AGENT (self), request->connection, hash, error, request->callback_data);
+
+ g_clear_pointer (&hash, g_hash_table_unref);
+ g_clear_error (&error);
+ g_hash_table_remove (priv->requests, request_id);
+}
+
+static void
+nmt_secret_agent_cancel_get_secrets (NMSecretAgent *agent,
+ const gchar *connection_path,
+ const gchar *setting_name)
+{
+ /* We don't support cancellation. Sorry! */
+}
+
+static void
+nmt_secret_agent_save_secrets (NMSecretAgent *agent,
+ NMConnection *connection,
+ const gchar *connection_path,
+ NMSecretAgentSaveSecretsFunc callback,
+ gpointer callback_data)
+{
+ /* We don't support secret storage */
+ callback (agent, connection, NULL, callback_data);}
+
+static void
+nmt_secret_agent_delete_secrets (NMSecretAgent *agent,
+ NMConnection *connection,
+ const gchar *connection_path,
+ NMSecretAgentDeleteSecretsFunc callback,
+ gpointer callback_data)
+{
+ /* We don't support secret storage, so there's nothing to delete. */
+ callback (agent, connection, NULL, callback_data);
+}
+
+void
+nmt_secret_agent_class_init (NmtSecretAgentClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ NMSecretAgentClass *agent_class = NM_SECRET_AGENT_CLASS (klass);
+
+ g_type_class_add_private (klass, sizeof (NmtSecretAgentPrivate));
+
+ gobject_class->finalize = nmt_secret_agent_finalize;
+
+ agent_class->get_secrets = nmt_secret_agent_get_secrets;
+ agent_class->cancel_get_secrets = nmt_secret_agent_cancel_get_secrets;
+ agent_class->save_secrets = nmt_secret_agent_save_secrets;
+ agent_class->delete_secrets = nmt_secret_agent_delete_secrets;
+
+ /**
+ * NmtSecretAgent::request-secrets:
+ * @agent: the #NmtSecretAgent
+ * @request_id: request ID, to eventually pass to
+ * nmt_secret_agent_response().
+ * @title: a title for the password dialog
+ * @prompt: a prompt message for the password dialog
+ * @secrets: (element-type #NmtSecretAgentSecret): array of secrets
+ * being requested.
+ *
+ * Emitted when the agent requires secrets from the user.
+ *
+ * The application should create a password dialog (eg,
+ * #NmtPasswordDialog) with the given title and prompt, and an
+ * entry for each element of @secrets. If any of the secrets
+ * already have a <literal>value</literal> filled in, the
+ * corresponding entry should be initialized to that value.
+ *
+ * When the dialog is complete, the app must call
+ * nmt_secret_agent_response() with the results.
+ */
+ signals[REQUEST_SECRETS] = g_signal_new ("request-secrets",
+ G_TYPE_FROM_CLASS (klass),
+ 0, 0, NULL, NULL, NULL,
+ G_TYPE_NONE,
+ 4,
+ G_TYPE_STRING, /* request_id */
+ G_TYPE_STRING, /* title */
+ G_TYPE_STRING, /* prompt */
+ G_TYPE_PTR_ARRAY);
+}
+
+/**
+ * nmt_secret_agent_new:
+ *
+ * Creates a new #NmtSecretAgent.
+ *
+ * Returns: a new #NmtSecretAgent
+ */
+NMSecretAgent *
+nmt_secret_agent_new (void)
+{
+ return g_object_new (NMT_TYPE_SECRET_AGENT,
+ NM_SECRET_AGENT_IDENTIFIER, "nmtui",
+ NM_SECRET_AGENT_AUTO_REGISTER, FALSE,
+ NULL);
+}
diff --git a/clients/tui/nmt-secret-agent.h b/clients/tui/nmt-secret-agent.h
new file mode 100644
index 0000000000..04b1581946
--- /dev/null
+++ b/clients/tui/nmt-secret-agent.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_SECRET_AGENT_H
+#define NMT_SECRET_AGENT_H
+
+#include <nm-secret-agent.h>
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_SECRET_AGENT (nmt_secret_agent_get_type ())
+#define NMT_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_SECRET_AGENT, NmtSecretAgent))
+#define NMT_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_SECRET_AGENT, NmtSecretAgentClass))
+#define NMT_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_SECRET_AGENT))
+#define NMT_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_SECRET_AGENT))
+#define NMT_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_SECRET_AGENT, NmtSecretAgentClass))
+
+typedef struct {
+ NMSecretAgent parent;
+
+} NmtSecretAgent;
+
+typedef struct {
+ NMSecretAgentClass parent;
+
+} NmtSecretAgentClass;
+
+typedef struct {
+ char *name, *value;
+ gboolean password;
+} NmtSecretAgentSecret;
+
+GType nmt_secret_agent_get_type (void);
+
+NMSecretAgent *nmt_secret_agent_new (void);
+void nmt_secret_agent_response (NmtSecretAgent *self,
+ const char *request_id,
+ GPtrArray *secrets);
+
+G_END_DECLS
+
+#endif /* NMT_SECRET_AGENT_H */
diff --git a/clients/tui/nmt-slave-list.c b/clients/tui/nmt-slave-list.c
new file mode 100644
index 0000000000..d6b60be42a
--- /dev/null
+++ b/clients/tui/nmt-slave-list.c
@@ -0,0 +1,267 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-slave-list:
+ * @short_description: An editable list of a connection's slaves
+ *
+ * #NmtSlaveList implements an #NmtEditConnectionList for the
+ * slaves of a connection.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include <nm-remote-connection.h>
+
+#include "nmt-slave-list.h"
+
+G_DEFINE_TYPE (NmtSlaveList, nmt_slave_list, NMT_TYPE_EDIT_CONNECTION_LIST)
+
+#define NMT_SLAVE_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_SLAVE_LIST, NmtSlaveListPrivate))
+
+typedef struct {
+ NMConnection *master;
+ const char *master_type, *master_uuid;
+
+ NmtAddConnectionTypeFilter type_filter;
+ gpointer type_filter_data;
+} NmtSlaveListPrivate;
+
+enum {
+ PROP_0,
+ PROP_MASTER,
+ PROP_TYPE_FILTER,
+ PROP_TYPE_FILTER_DATA,
+
+ LAST_PROP
+};
+
+static gboolean nmt_slave_list_connection_filter (NmtEditConnectionList *list,
+ NMConnection *connection,
+ gpointer user_data);
+
+/**
+ * nmt_slave_list_new:
+ * @master: the master #NMConnection whose slaves are being listed
+ * @type_filter: (allow-none): a function to limit the availble slave types
+ * @type_filter_data: (allow-none): data for @type_filter.
+ *
+ * Creates a new #NmtSlaveList.
+ *
+ * If @type_filter is non-%NULL, it will be used to limit the connection
+ * types that are available when the user clicks on the "Add" button to add
+ * a new slave. If the @type_filter filters the list down to only a single
+ * connection type, then the user will not be presented with a connection-type
+ * dialog, and will instead be immediately taken to an editor window for the
+ * new slave after clicking "Add".
+ *
+ * Returns: a new #NmtSlaveList.
+ */
+NmtNewtWidget *
+nmt_slave_list_new (NMConnection *master,
+ NmtAddConnectionTypeFilter type_filter,
+ gpointer type_filter_data)
+{
+ return g_object_new (NMT_TYPE_SLAVE_LIST,
+ "master", master,
+ "type-filter", type_filter,
+ "type-filter-data", type_filter_data,
+ "grouped", FALSE,
+ "connection-filter", nmt_slave_list_connection_filter,
+ NULL);
+}
+
+static void
+nmt_slave_list_init (NmtSlaveList *list)
+{
+}
+
+static void
+nmt_slave_list_finalize (GObject *object)
+{
+ NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object);
+
+ g_object_unref (priv->master);
+
+ G_OBJECT_CLASS (nmt_slave_list_parent_class)->finalize (object);
+}
+
+static gboolean
+nmt_slave_list_connection_filter (NmtEditConnectionList *list,
+ NMConnection *connection,
+ gpointer user_data)
+{
+ NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (list);
+ NMSettingConnection *s_con;
+ const char *master, *master_ifname, *slave_type;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_return_val_if_fail (s_con != NULL, FALSE);
+
+ slave_type = nm_setting_connection_get_slave_type (s_con);
+ if (g_strcmp0 (slave_type, priv->master_type) != 0)
+ return FALSE;
+
+ master = nm_setting_connection_get_master (s_con);
+ if (!master)
+ return FALSE;
+
+ master_ifname = nm_connection_get_virtual_iface_name (priv->master);
+ if (g_strcmp0 (master, master_ifname) != 0 && g_strcmp0 (master, priv->master_uuid) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+nmt_slave_list_add_connection (NmtEditConnectionList *list)
+{
+ NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (list);
+
+ nmt_add_connection_full (_("Select the type of slave connection you wish to add."), NULL,
+ priv->master, priv->type_filter, priv->type_filter_data);
+}
+
+static void
+nmt_slave_list_edit_connection (NmtEditConnectionList *list,
+ NMConnection *connection)
+{
+ nmt_edit_connection (connection);
+}
+
+static void
+nmt_slave_list_remove_connection (NmtEditConnectionList *list,
+ NMRemoteConnection *connection)
+{
+ nmt_remove_connection (connection);
+}
+
+static void
+nmt_slave_list_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MASTER:
+ priv->master = g_value_dup_object (value);
+ if (priv->master) {
+ NMSettingConnection *s_con = nm_connection_get_setting_connection (priv->master);
+
+ priv->master_type = nm_setting_connection_get_connection_type (s_con);
+ priv->master_uuid = nm_setting_connection_get_uuid (s_con);
+ }
+ break;
+ case PROP_TYPE_FILTER:
+ priv->type_filter = g_value_get_pointer (value);
+ break;
+ case PROP_TYPE_FILTER_DATA:
+ priv->type_filter_data = g_value_get_pointer (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_slave_list_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MASTER:
+ g_value_set_object (value, priv->master);
+ break;
+ case PROP_TYPE_FILTER:
+ g_value_set_pointer (value, priv->type_filter);
+ break;
+ case PROP_TYPE_FILTER_DATA:
+ g_value_set_pointer (value, priv->type_filter_data);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_slave_list_class_init (NmtSlaveListClass *list_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+ NmtEditConnectionListClass *connection_list_class = NMT_EDIT_CONNECTION_LIST_CLASS (list_class);
+
+ g_type_class_add_private (list_class, sizeof (NmtSlaveListPrivate));
+
+ /* virtual methods */
+ object_class->set_property = nmt_slave_list_set_property;
+ object_class->get_property = nmt_slave_list_get_property;
+ object_class->finalize = nmt_slave_list_finalize;
+
+ connection_list_class->add_connection = nmt_slave_list_add_connection;
+ connection_list_class->edit_connection = nmt_slave_list_edit_connection;
+ connection_list_class->remove_connection = nmt_slave_list_remove_connection;
+
+ /**
+ * NmtSlaveList:master:
+ *
+ * The master #NMConnection whose slaves are being displayed.
+ */
+ g_object_class_install_property
+ (object_class, PROP_MASTER,
+ g_param_spec_object ("master", "", "",
+ NM_TYPE_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtSlaveList:type-filter:
+ *
+ * If non-%NULL, this will be used to limit the connection types
+ * that are available when the user clicks on the "Add" button to
+ * add a new slave. If the filter filters the list down to only a
+ * single connection type, then the user will not be presented
+ * with a connection-type dialog, and will instead be immediately
+ * taken to an editor window for the new slave after clicking
+ * "Add".
+ */
+ g_object_class_install_property
+ (object_class, PROP_TYPE_FILTER,
+ g_param_spec_pointer ("type-filter", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtSlaveList:type-filter-data:
+ *
+ * User data passed to #NmtSlaveList:type-filter
+ */
+ g_object_class_install_property
+ (object_class, PROP_TYPE_FILTER_DATA,
+ g_param_spec_pointer ("type-filter-data", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-slave-list.h b/clients/tui/nmt-slave-list.h
new file mode 100644
index 0000000000..d10cd3a73a
--- /dev/null
+++ b/clients/tui/nmt-slave-list.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_SLAVE_LIST_H
+#define NMT_SLAVE_LIST_H
+
+#include "nmt-edit-connection-list.h"
+#include "nmtui-edit.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_SLAVE_LIST (nmt_slave_list_get_type ())
+#define NMT_SLAVE_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveList))
+#define NMT_SLAVE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass))
+#define NMT_IS_SLAVE_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_SLAVE_LIST))
+#define NMT_IS_SLAVE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_SLAVE_LIST))
+#define NMT_SLAVE_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass))
+
+typedef struct {
+ NmtEditConnectionList parent;
+
+} NmtSlaveList;
+
+typedef struct {
+ NmtEditConnectionListClass parent;
+
+} NmtSlaveListClass;
+
+GType nmt_slave_list_get_type (void);
+
+NmtNewtWidget *nmt_slave_list_new (NMConnection *master,
+ NmtAddConnectionTypeFilter type_filter,
+ gpointer type_filter_data);
+
+G_END_DECLS
+
+#endif /* NMT_SLAVE_LIST_H */
diff --git a/clients/tui/nmt-utils.c b/clients/tui/nmt-utils.c
new file mode 100644
index 0000000000..f2bf70db82
--- /dev/null
+++ b/clients/tui/nmt-utils.c
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-utils
+ * @short_description: Miscellaneous nmtui-specific utilities
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-utils.h"
+
+/**
+ * NmtSyncOp:
+ *
+ * A helper object used when synchronously waiting for an asynchronous
+ * operation to complete.
+ *
+ * The caller first does:
+ *
+ * |[
+ * NmtSyncOp op;
+ *
+ * nmt_sync_op_init (&op);
+ * ]|
+ *
+ * It then passes the op as the user_data to the async operation's
+ * callback function, and then calls nmt_sync_op_wait_boolean() or
+ * nmt_sync_op_wait_pointer() to wait for the result.
+ *
+ * When the async callback is invoked, it should call
+ * nmt_sync_op_complete_boolean() or nmt_sync_op_complete_pointer() to
+ * return a result or an error to the caller.
+ *
+ * There is no free/clear function; any memory that needs to be freed
+ * will have been returned to the caller from
+ * nmt_sync_op_wait_boolean() or nmt_sync_op_wait_pointer(), so there
+ * is nothing left that needs to be freed.
+ */
+
+typedef struct {
+ gpointer result;
+ GError *error;
+ gpointer complete;
+} NmtSyncOpReal;
+
+/**
+ * nmt_sync_op_init:
+ * @op: pointer to a stack-allocated #NmtSyncOp
+ *
+ * Initializes @op before use.
+ */
+void
+nmt_sync_op_init (NmtSyncOp *op)
+{
+ memset (op, 0, sizeof (*op));
+}
+
+/**
+ * nmt_sync_op_wait_boolean:
+ * @op: the #NmtSyncOp
+ * @error: return location for a #GError
+ *
+ * This runs the main loop until @op's operation returns, and then
+ * returns the result or error.
+ *
+ * Returns: the result of the operation.
+ */
+gboolean
+nmt_sync_op_wait_boolean (NmtSyncOp *op,
+ GError **error)
+{
+ return GPOINTER_TO_UINT (nmt_sync_op_wait_pointer (op, error));
+}
+
+/**
+ * nmt_sync_op_complete_boolean:
+ * @op: the #NmtSyncOp
+ * @result: the result of the operation
+ * @error: (allow-none): the error, or %NULL
+ *
+ * Completes @op and returns @result and/or @error to the caller.
+ */
+void
+nmt_sync_op_complete_boolean (NmtSyncOp *op,
+ gboolean result,
+ GError *error)
+{
+ nmt_sync_op_complete_pointer (op, GUINT_TO_POINTER (result), error);
+}
+
+/**
+ * nmt_sync_op_wait_pointer:
+ * @op: the #NmtSyncOp
+ * @error: return location for a #GError
+ *
+ * This runs the main loop until @op's operation returns, and then
+ * returns the result or error.
+ *
+ * Returns: the result of the operation.
+ */
+gpointer
+nmt_sync_op_wait_pointer (NmtSyncOp *op,
+ GError **error)
+{
+ NmtSyncOpReal *real = (NmtSyncOpReal *)op;
+
+ while (!real->complete)
+ g_main_context_iteration (NULL, TRUE);
+
+ if (real->error)
+ g_propagate_error (error, real->error);
+ return real->result;
+}
+
+/**
+ * nmt_sync_op_complete_pointer:
+ * @op: the #NmtSyncOp
+ * @result: the result of the operation
+ * @error: (allow-none): the error, or %NULL
+ *
+ * Completes @op and returns @result and/or @error to the caller.
+ */
+void
+nmt_sync_op_complete_pointer (NmtSyncOp *op,
+ gpointer result,
+ GError *error)
+{
+ NmtSyncOpReal *real = (NmtSyncOpReal *)op;
+
+ real->result = result;
+ real->error = error ? g_error_copy (error) : NULL;
+ real->complete = GUINT_TO_POINTER (TRUE);
+}
diff --git a/clients/tui/nmt-utils.h b/clients/tui/nmt-utils.h
new file mode 100644
index 0000000000..7e24fcab19
--- /dev/null
+++ b/clients/tui/nmt-utils.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_UTILS_H
+#define NMT_UTILS_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+ gpointer private[3];
+} NmtSyncOp;
+
+void nmt_sync_op_init (NmtSyncOp *op);
+
+gboolean nmt_sync_op_wait_boolean (NmtSyncOp *op,
+ GError **error);
+void nmt_sync_op_complete_boolean (NmtSyncOp *op,
+ gboolean result,
+ GError *error);
+
+gpointer nmt_sync_op_wait_pointer (NmtSyncOp *op,
+ GError **error);
+void nmt_sync_op_complete_pointer (NmtSyncOp *op,
+ gpointer result,
+ GError *error);
+
+G_END_DECLS
+
+#endif /* NMT_UTILS_H */
diff --git a/clients/tui/nmt-widget-list.c b/clients/tui/nmt-widget-list.c
new file mode 100644
index 0000000000..6dc3f5c926
--- /dev/null
+++ b/clients/tui/nmt-widget-list.c
@@ -0,0 +1,496 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-widget-list
+ * @short_description: A list of widgets, with Add and Remove buttons
+ *
+ * #NmtWidgetList presents a homogeneous list of widgets, with "Remove"
+ * buttons next to each one, and an "Add" button at the button to add
+ * new ones.
+ *
+ * It is the base class for #NmtAddressList, and is used internally by
+ * #NmtRouteTable.
+ *
+ * FIXME: The way this works is sort of weird.
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-widget-list.h"
+#include "nmt-newt.h"
+
+G_DEFINE_TYPE (NmtWidgetList, nmt_widget_list, NMT_TYPE_NEWT_GRID)
+
+#define NMT_WIDGET_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_WIDGET_LIST, NmtWidgetListPrivate))
+
+typedef struct {
+ int length;
+
+ NmtWidgetListCallback create_callback;
+ gpointer user_data;
+ GDestroyNotify destroy_notify;
+
+ NmtNewtWidget *empty_widget;
+
+ GPtrArray *widgets;
+ GPtrArray *remove_buttons;
+
+ NmtNewtWidget *add_button;
+ GBinding *add_sensitivity;
+} NmtWidgetListPrivate;
+
+enum {
+ PROP_0,
+ PROP_CREATE_CALLBACK,
+ PROP_USER_DATA,
+ PROP_DESTROY_NOTIFY,
+ PROP_EMPTY_WIDGET,
+ PROP_LENGTH,
+
+ LAST_PROP
+};
+
+enum {
+ ADD_CLICKED,
+ REMOVE_CLICKED,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void add_clicked (NmtNewtButton *button, gpointer user_data);
+static void remove_clicked (NmtNewtButton *button, gpointer user_data);
+
+/**
+ * NmtWidgetListCallback:
+ * @list: the #NmtWidgetList
+ * @n: the number of the widget being added
+ * @user_data: the callback's user data
+ *
+ * Called by #NmtWidgetList to ask for a new widget to be created.
+ *
+ * Note that the widget is not created to go with any particular
+ * value, but rather is created to be at a certain spot in the list.
+ * When an element is deleted from the list, it is actually always
+ * the last widget in the list that is removed, but it is assumed
+ * that the widget list is bound to some array-valued property, and
+ * so when an element is deleted from that array, the widgets will
+ * all update themselves automatically when the array changes.
+ *
+ * Returns: a new widget for the list
+ */
+
+/**
+ * nmt_widget_list_new:
+ * @create_callback: callback to create new widgets
+ * @user_data: user data for @create_callback
+ * @destroy_notify: #GDestroyNotify for @user_data
+ * @empty_widget: (allow-none): a widget to display when there are
+ * no "real" widgets in the list.
+ *
+ * Creates a new #NmtWidgetList.
+ *
+ * Returns: a new #NmtWidgetList.
+ */
+NmtNewtWidget *
+nmt_widget_list_new (NmtWidgetListCallback create_callback,
+ gpointer user_data,
+ GDestroyNotify destroy_notify,
+ NmtNewtWidget *empty_widget)
+{
+ return g_object_new (NMT_TYPE_WIDGET_LIST,
+ "create-callback", create_callback,
+ "user-data", user_data,
+ "destroy-notify", destroy_notify,
+ "empty-widget", empty_widget,
+ NULL);
+}
+
+static void
+nmt_widget_list_init (NmtWidgetList *list)
+{
+ NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list);
+
+ priv->widgets = g_ptr_array_new ();
+ priv->remove_buttons = g_ptr_array_new ();
+
+ priv->add_button = nmt_newt_button_new (_("Add..."));
+ g_signal_connect (priv->add_button, "clicked",
+ G_CALLBACK (add_clicked), list);
+ nmt_newt_grid_add (NMT_NEWT_GRID (list), priv->add_button, 0, 0);
+}
+
+static void
+nmt_widget_list_constructed (GObject *object)
+{
+ NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object);
+
+ if (priv->length == 0 && priv->empty_widget) {
+ nmt_newt_widget_set_visible (priv->empty_widget, TRUE);
+ nmt_newt_grid_move (NMT_NEWT_GRID (object), priv->add_button, 0, 1);
+ }
+
+ G_OBJECT_CLASS (nmt_widget_list_parent_class)->constructed (object);
+}
+
+static void
+nmt_widget_list_finalize (GObject *object)
+{
+ NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object);
+
+ g_ptr_array_unref (priv->widgets);
+ g_ptr_array_unref (priv->remove_buttons);
+
+ if (priv->user_data && priv->destroy_notify)
+ priv->destroy_notify (priv->user_data);
+
+ g_clear_object (&priv->empty_widget);
+
+ G_OBJECT_CLASS (nmt_widget_list_parent_class)->finalize (object);
+}
+
+static void
+ensure_widgets (NmtWidgetList *list)
+{
+ NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list);
+ NmtNewtWidget *widget, *button, *focus;
+ gboolean was_empty;
+ NmtNewtForm *form;
+ int i;
+
+ was_empty = priv->widgets->len == 0;
+
+ if (priv->length < priv->widgets->len) {
+ /* remove excess widgets */
+ for (i = priv->length; i < priv->widgets->len; i++) {
+ nmt_newt_container_remove (NMT_NEWT_CONTAINER (list), priv->widgets->pdata[i]);
+ nmt_newt_container_remove (NMT_NEWT_CONTAINER (list), priv->remove_buttons->pdata[i]);
+ }
+ g_ptr_array_set_size (priv->widgets, priv->length);
+ g_ptr_array_set_size (priv->remove_buttons, priv->length);
+
+ } else if (priv->length > priv->widgets->len) {
+ /* add new widgets */
+ for (i = priv->widgets->len; i < priv->length; i++) {
+ widget = NMT_WIDGET_LIST_GET_CLASS (list)->create_widget (list, i);
+
+ nmt_newt_grid_add (NMT_NEWT_GRID (list), widget, 0, i);
+ g_ptr_array_add (priv->widgets, widget);
+
+ button = nmt_newt_button_new (_("Remove"));
+ g_signal_connect (button, "clicked",
+ G_CALLBACK (remove_clicked), list);
+
+ nmt_newt_grid_add (NMT_NEWT_GRID (list), button, 1, i);
+ nmt_newt_widget_set_padding (button, 1, 0, 0, 0);
+ g_ptr_array_add (priv->remove_buttons, button);
+ }
+
+ } else
+ return;
+
+ if (priv->widgets->len == 0 && priv->empty_widget) {
+ nmt_newt_widget_set_visible (priv->empty_widget, TRUE);
+ nmt_newt_grid_move (NMT_NEWT_GRID (list), priv->add_button, 0, 1);
+ } else {
+ if (was_empty && priv->empty_widget)
+ nmt_newt_widget_set_visible (priv->empty_widget, FALSE);
+ nmt_newt_grid_move (NMT_NEWT_GRID (list), priv->add_button, 0, priv->length);
+ }
+
+ form = nmt_newt_widget_get_form (NMT_NEWT_WIDGET (list));
+ if (form) {
+ if (priv->widgets->len) {
+ if (was_empty)
+ focus = priv->widgets->pdata[0];
+ else
+ focus = priv->widgets->pdata[priv->widgets->len - 1];
+ } else
+ focus = priv->add_button;
+ nmt_newt_form_set_focus (form, focus);
+ }
+
+ g_clear_object (&priv->add_sensitivity);
+ if (priv->widgets->len) {
+ widget = priv->widgets->pdata[priv->widgets->len - 1];
+ priv->add_sensitivity = g_object_bind_property (widget, "valid",
+ priv->add_button, "sensitive",
+ G_BINDING_SYNC_CREATE);
+ g_object_add_weak_pointer (G_OBJECT (priv->add_sensitivity),
+ (gpointer *)&priv->add_sensitivity);
+ }
+}
+
+static void
+add_clicked (NmtNewtButton *button, gpointer list)
+{
+ g_signal_emit (G_OBJECT (list), signals[ADD_CLICKED], 0, NULL);
+}
+
+static void
+remove_clicked (NmtNewtButton *button, gpointer list)
+{
+ NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list);
+ int i;
+
+ for (i = 0; i < priv->remove_buttons->len; i++) {
+ if (priv->remove_buttons->pdata[i] == (gpointer)button)
+ break;
+ }
+ g_return_if_fail (i < priv->remove_buttons->len);
+
+ g_signal_emit (G_OBJECT (list), signals[REMOVE_CLICKED], 0, i, NULL);
+}
+
+static NmtNewtWidget *
+nmt_widget_list_real_create_widget (NmtWidgetList *list,
+ int n)
+{
+ NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list);
+
+ g_return_val_if_fail (priv->create_callback != NULL, NULL);
+
+ return priv->create_callback (list, n, priv->user_data);
+}
+
+/**
+ * nmt_widget_list_get_length:
+ * @list: the #NmtNewtWidgetList
+ *
+ * Gets the number of widgets in the list.
+ *
+ * Returns: the number of widgets in the list.
+ */
+int
+nmt_widget_list_get_length (NmtWidgetList *list)
+{
+ NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list);
+
+ return priv->length;
+}
+
+/**
+ * nmt_widget_list_set_length:
+ * @list: the #NmtNewtWidgetList
+ * @length: the new length
+ *
+ * Changes the number of widgets in the list. Widgets will be added or
+ * deleted as necessary.
+ */
+void
+nmt_widget_list_set_length (NmtWidgetList *list,
+ int length)
+{
+ NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list);
+
+ if (priv->length != length) {
+ priv->length = length;
+ g_object_notify (G_OBJECT (list), "length");
+ }
+
+ ensure_widgets (list);
+}
+
+static void
+nmt_widget_list_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_CREATE_CALLBACK:
+ priv->create_callback = g_value_get_pointer (value);
+ break;
+ case PROP_USER_DATA:
+ priv->user_data = g_value_get_pointer (value);
+ break;
+ case PROP_DESTROY_NOTIFY:
+ priv->destroy_notify = g_value_get_pointer (value);
+ break;
+ case PROP_LENGTH:
+ priv->length = g_value_get_int (value);
+ ensure_widgets (NMT_WIDGET_LIST (object));
+ break;
+ case PROP_EMPTY_WIDGET:
+ priv->empty_widget = g_value_get_object (value);
+ if (priv->empty_widget) {
+ g_object_ref_sink (priv->empty_widget);
+ nmt_newt_grid_add (NMT_NEWT_GRID (object), priv->empty_widget, 0, 0);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_widget_list_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_CREATE_CALLBACK:
+ g_value_set_pointer (value, priv->create_callback);
+ break;
+ case PROP_USER_DATA:
+ g_value_set_pointer (value, priv->user_data);
+ break;
+ case PROP_DESTROY_NOTIFY:
+ g_value_set_pointer (value, priv->destroy_notify);
+ break;
+ case PROP_LENGTH:
+ g_value_set_int (value, priv->length);
+ break;
+ case PROP_EMPTY_WIDGET:
+ g_value_set_object (value, priv->empty_widget);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_widget_list_class_init (NmtWidgetListClass *list_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+
+ g_type_class_add_private (list_class, sizeof (NmtWidgetListPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_widget_list_constructed;
+ object_class->set_property = nmt_widget_list_set_property;
+ object_class->get_property = nmt_widget_list_get_property;
+ object_class->finalize = nmt_widget_list_finalize;
+
+ list_class->create_widget = nmt_widget_list_real_create_widget;
+
+ /* signals */
+
+ /**
+ * NmtNewtWidget::add-clicked:
+ * @list: the #NmtNewtWidgetList
+ *
+ * Emitted when the user clicks the "Add" button. The caller can
+ * decide whether or not to add a new widget, and call
+ * nmt_widget_list_set_length() with the new length if so.
+ *
+ * FIXME: the "Add" button should be insensitive if it's
+ * not going to work.
+ */
+ signals[ADD_CLICKED] =
+ g_signal_new ("add-clicked",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NmtWidgetListClass, add_clicked),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+ /**
+ * NmtNewtWidget::remove-clicked:
+ * @list: the #NmtNewtWidgetList
+ * @n: the widget being removed
+ *
+ * Emitted when the user clicks one of the "Remove" buttons. The
+ * caller can decide whether or not to remove the widget, and
+ * call nmt_widget_list_set_length() with the new length if so.
+ *
+ * FIXME: the "Remove" button should be insensitive if it's not
+ * going to work.
+ */
+ signals[REMOVE_CLICKED] =
+ g_signal_new ("remove-clicked",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NmtWidgetListClass, remove_clicked),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+
+ /* properties */
+
+ /**
+ * NmtWidgetList:create-callback:
+ *
+ * Callback called to create a new widget.
+ */
+ g_object_class_install_property
+ (object_class, PROP_CREATE_CALLBACK,
+ g_param_spec_pointer ("create-callback", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtWidgetList:user-data:
+ *
+ * User data for #NmtWidgetList:create-callback
+ */
+ g_object_class_install_property
+ (object_class, PROP_USER_DATA,
+ g_param_spec_pointer ("user-data", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtWidgetList:destroy-notify:
+ *
+ * #GDestroyNotify for #NmtWidgetList:user-data
+ */
+ g_object_class_install_property
+ (object_class, PROP_DESTROY_NOTIFY,
+ g_param_spec_pointer ("destroy-notify", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtWidgetList:length:
+ *
+ * The length of the widget list; changing this value will add or
+ * remove widgets from the list.
+ */
+ g_object_class_install_property
+ (object_class, PROP_LENGTH,
+ g_param_spec_int ("length", "", "",
+ 0, G_MAXINT, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS));
+ /**
+ * NmtWidgetList:empty-widget:
+ *
+ * If non-%NULL, this widget will be displayed when there are
+ * no "real" widgets in the list.
+ */
+ g_object_class_install_property
+ (object_class, PROP_EMPTY_WIDGET,
+ g_param_spec_object ("empty-widget", "", "",
+ NMT_TYPE_NEWT_WIDGET,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-widget-list.h b/clients/tui/nmt-widget-list.h
new file mode 100644
index 0000000000..7dbeb21588
--- /dev/null
+++ b/clients/tui/nmt-widget-list.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_WIDGET_LIST_H
+#define NMT_WIDGET_LIST_H
+
+#include "nmt-newt-grid.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_WIDGET_LIST (nmt_widget_list_get_type ())
+#define NMT_WIDGET_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetList))
+#define NMT_WIDGET_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass))
+#define NMT_IS_WIDGET_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_WIDGET_LIST))
+#define NMT_IS_WIDGET_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_WIDGET_LIST))
+#define NMT_WIDGET_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass))
+
+typedef struct {
+ NmtNewtGrid parent;
+
+} NmtWidgetList;
+
+typedef struct {
+ NmtNewtGridClass parent;
+
+ /* signals */
+ void (*add_clicked) (NmtWidgetList *list);
+ void (*remove_clicked) (NmtWidgetList *list,
+ int n);
+
+ /* methods */
+ NmtNewtWidget * (*create_widget) (NmtWidgetList *list,
+ int n);
+
+} NmtWidgetListClass;
+
+GType nmt_widget_list_get_type (void);
+
+typedef NmtNewtWidget * (*NmtWidgetListCallback) (NmtWidgetList *list,
+ int n,
+ gpointer user_data);
+
+NmtNewtWidget *nmt_widget_list_new (NmtWidgetListCallback create_callback,
+ gpointer user_data,
+ GDestroyNotify destroy_notify,
+ NmtNewtWidget *empty_widget);
+
+int nmt_widget_list_get_length (NmtWidgetList *list);
+void nmt_widget_list_set_length (NmtWidgetList *list,
+ int length);
+
+G_END_DECLS
+
+#endif /* NMT_WIDGET_LIST_H */
diff --git a/clients/tui/nmtui-connect.c b/clients/tui/nmtui-connect.c
new file mode 100644
index 0000000000..a86b8df57d
--- /dev/null
+++ b/clients/tui/nmtui-connect.c
@@ -0,0 +1,353 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmtui-connect
+ * @short_description: nm-applet-like functionality
+ *
+ * nmtui-connect implements activating #NMConnections, including
+ * presenting a password dialog if necessary.
+ *
+ * It's supposed to also implement deactivating them, but it doesn't.
+ * FIXME.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+#include <nm-utils.h>
+
+#include "nmt-newt.h"
+
+#include "nmtui.h"
+#include "nmtui-connect.h"
+#include "nmt-connect-connection-list.h"
+#include "nmt-password-dialog.h"
+#include "nmt-secret-agent.h"
+#include "nmt-utils.h"
+
+static void
+secrets_requested (NmtSecretAgent *agent,
+ const char *request_id,
+ const char *title,
+ const char *msg,
+ GPtrArray *secrets,
+ gpointer user_data)
+{
+ NmtNewtForm *form;
+
+ form = nmt_password_dialog_new (request_id, title, msg, secrets);
+ nmt_newt_form_run_sync (form);
+
+ if (nmt_password_dialog_succeeded (NMT_PASSWORD_DIALOG (form)))
+ nmt_secret_agent_response (agent, request_id, secrets);
+ else
+ nmt_secret_agent_response (agent, request_id, NULL);
+
+ g_object_unref (form);
+}
+
+static void
+connect_cancelled (NmtNewtForm *form,
+ gpointer user_data)
+{
+ NmtSyncOp *op = user_data;
+ GError *error = NULL;
+
+ error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
+ nmt_sync_op_complete_boolean (op, FALSE, error);
+ g_clear_error (&error);
+}
+
+static void
+activate_ac_state_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ NmtSyncOp *op = user_data;
+ NMActiveConnectionState state;
+ GError *error = NULL;
+
+ state = nm_active_connection_get_state (NM_ACTIVE_CONNECTION (object));
+ if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING)
+ return;
+
+ if (state != NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
+ error = g_error_new_literal (NM_CLIENT_ERROR, NM_CLIENT_ERROR_UNKNOWN,
+ _("Activation failed"));
+ }
+
+ nmt_sync_op_complete_boolean (op, error == NULL, error);
+ g_clear_error (&error);
+}
+
+static void
+activate_callback (NMClient *client,
+ NMActiveConnection *ac,
+ GError *error,
+ gpointer user_data)
+{
+ NmtSyncOp *op = user_data;
+
+ if (error)
+ nmt_sync_op_complete_pointer (op, NULL, error);
+ else
+ nmt_sync_op_complete_pointer (op, g_object_ref (ac), NULL);
+}
+
+static void
+add_and_activate_callback (NMClient *client,
+ NMActiveConnection *ac,
+ const char *new_connection_path,
+ GError *error,
+ gpointer user_data)
+{
+ /* We don't care about @new_connection_path, so... */
+ activate_callback (client, ac, error, user_data);
+}
+
+static void
+activate_connection (NMConnection *connection,
+ NMDevice *device,
+ NMObject *specific_object)
+{
+ NmtNewtForm *form;
+ NMSecretAgent *agent;
+ NmtNewtWidget *label;
+ NmtSyncOp op;
+ const char *specific_object_path;
+ NMActiveConnection *ac;
+ GError *error = NULL;
+
+ form = g_object_new (NMT_TYPE_NEWT_FORM,
+ "escape-exits", TRUE,
+ NULL);
+ label = nmt_newt_label_new (_("Connecting..."));
+ nmt_newt_form_set_content (form, label);
+
+ agent = nmt_secret_agent_new ();
+ nm_secret_agent_register (agent);
+ g_signal_connect (agent, "request-secrets", G_CALLBACK (secrets_requested), NULL);
+
+ specific_object_path = specific_object ? nm_object_get_path (specific_object) : NULL;
+
+ /* There's no way to cancel an nm_client_activate_connection() /
+ * nm_client_add_and_activate_connection() call, so we always let them
+ * complete, even if the user hits Esc; they shouldn't normally take long
+ * to complete anyway.
+ */
+
+ nmt_sync_op_init (&op);
+ if (connection) {
+ nm_client_activate_connection (nm_client,
+ connection, device, specific_object_path,
+ activate_callback, &op);
+ } else {
+ nm_client_add_and_activate_connection (nm_client,
+ NULL, device, specific_object_path,
+ add_and_activate_callback, &op);
+ }
+
+ nmt_newt_form_show (form);
+
+ ac = nmt_sync_op_wait_pointer (&op, &error);
+ if (!ac) {
+ nmt_newt_message_dialog (_("Could not activate connection: %s"), error->message);
+ g_clear_error (&error);
+ goto done;
+ } else if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
+ /* Already active */
+ goto done;
+ } else if (!nmt_newt_widget_get_realized (NMT_NEWT_WIDGET (form))) {
+ /* User already hit Esc */
+ goto done;
+ }
+
+ /* Now wait for the connection to actually reach the ACTIVATED state,
+ * allowing the user to cancel if it takes too long.
+ */
+
+ nmt_sync_op_init (&op);
+
+ g_signal_connect (form, "quit", G_CALLBACK (connect_cancelled), &op);
+ g_signal_connect (ac, "notify::" NM_ACTIVE_CONNECTION_STATE,
+ G_CALLBACK (activate_ac_state_changed), &op);
+
+ if (!nmt_sync_op_wait_boolean (&op, &error)) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ nmt_newt_message_dialog (_("Could not activate connection: %s"), error->message);
+ g_clear_error (&error);
+ }
+
+ g_signal_handlers_disconnect_by_func (form, G_CALLBACK (connect_cancelled), &op);
+ g_signal_handlers_disconnect_by_func (ac, G_CALLBACK (activate_ac_state_changed), &op);
+
+ done:
+ if (nmt_newt_widget_get_realized (NMT_NEWT_WIDGET (form)))
+ nmt_newt_form_quit (form);
+ g_object_unref (form);
+
+ /* If the activation failed very quickly, then agent won't be registered yet,
+ * and nm_secret_agent_unregister() would complain if we called it...
+ */
+ if (nm_secret_agent_get_registered (agent))
+ nm_secret_agent_unregister (agent);
+ g_object_unref (agent);
+}
+
+static void
+listbox_activated (NmtNewtListbox *listbox,
+ gpointer user_data)
+{
+ NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST (listbox);
+ NMConnection *connection;
+ NMDevice *device;
+ NMObject *specific_object;
+ NMActiveConnection *ac;
+
+ if (!nmt_connect_connection_list_get_selection (list,
+ &connection,
+ &device,
+ &specific_object,
+ &ac))
+ return;
+
+ if (ac)
+ nm_client_deactivate_connection (nm_client, ac);
+ else
+ activate_connection (connection, device, specific_object);
+}
+
+static void
+activate_clicked (NmtNewtButton *button,
+ gpointer listbox)
+{
+ listbox_activated (listbox, NULL);
+}
+
+static void
+listbox_active_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer button)
+{
+ NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST (object);
+ static const char *activate, *deactivate;
+ static int deactivate_padding, activate_padding;
+ NMActiveConnection *ac;
+ gboolean has_selection;
+
+ if (G_UNLIKELY (activate == NULL)) {
+ int activate_width, deactivate_width;
+
+ activate = _("Activate");
+ activate_width = nmt_newt_text_width (activate);
+ deactivate = _("Deactivate");
+ deactivate_width = nmt_newt_text_width (deactivate);
+
+ activate_padding = MAX (0, deactivate_width - activate_width);
+ deactivate_padding = MAX (0, activate_width - deactivate_width);
+ }
+
+ has_selection = nmt_connect_connection_list_get_selection (list, NULL, NULL, NULL, &ac);
+
+ nmt_newt_component_set_sensitive (button, has_selection);
+ if (has_selection && ac) {
+ nmt_newt_button_set_label (button, deactivate);
+ nmt_newt_widget_set_padding (button, 0, 0, deactivate_padding, 0);
+ } else {
+ nmt_newt_button_set_label (button, activate);
+ nmt_newt_widget_set_padding (button, 0, 0, activate_padding, 0);
+ }
+}
+
+static NmtNewtForm *
+nmt_connect_connection_list (void)
+{
+ int screen_width, screen_height;
+ NmtNewtForm *form;
+ NmtNewtWidget *list, *activate, *quit, *bbox, *grid;
+
+ newtGetScreenSize (&screen_width, &screen_height);
+
+ form = g_object_new (NMT_TYPE_NEWT_FORM,
+ "y", 2,
+ "height", screen_height - 4,
+ "escape-exits", TRUE,
+ NULL);
+
+ grid = nmt_newt_grid_new ();
+
+ list = nmt_connect_connection_list_new ();
+ nmt_newt_grid_add (NMT_NEWT_GRID (grid), list, 0, 0);
+ nmt_newt_grid_set_flags (NMT_NEWT_GRID (grid), list,
+ NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y |
+ NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_EXPAND_Y);
+ g_signal_connect (list, "activated", G_CALLBACK (listbox_activated), NULL);
+
+ bbox = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_VERTICAL);
+ nmt_newt_grid_add (NMT_NEWT_GRID (grid), bbox, 1, 0);
+ nmt_newt_widget_set_padding (bbox, 1, 1, 0, 1);
+
+ activate = nmt_newt_button_box_add_start (NMT_NEWT_BUTTON_BOX (bbox), _("Activate"));
+ g_signal_connect (list, "notify::active", G_CALLBACK (listbox_active_changed), activate);
+ listbox_active_changed (G_OBJECT (list), NULL, activate);
+ g_signal_connect (activate, "clicked", G_CALLBACK (activate_clicked), list);
+
+ quit = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("Quit"));
+ nmt_newt_widget_set_exit_on_activate (quit, TRUE);
+
+ nmt_newt_form_set_content (form, grid);
+ return form;
+}
+
+static NmtNewtForm *
+nmt_connect_connection (const char *identifier)
+{
+ NmtNewtWidget *list;
+ NMConnection *connection;
+ NMDevice *device;
+ NMObject *specific_object;
+ NMActiveConnection *ac;
+
+ list = nmt_connect_connection_list_new ();
+ if (!nmt_connect_connection_list_get_connection (NMT_CONNECT_CONNECTION_LIST (list),
+ identifier,
+ &connection,
+ &device,
+ &specific_object,
+ &ac))
+ nmt_newt_message_dialog (_("No such connection '%s'"), identifier);
+ else if (ac)
+ nmt_newt_message_dialog (_("Connection is already active"));
+ else
+ activate_connection (connection, device, specific_object);
+ g_object_unref (list);
+
+ return NULL;
+}
+
+NmtNewtForm *
+nmtui_connect (int argc, char **argv)
+{
+ if (argc == 2)
+ return nmt_connect_connection (argv[1]);
+ else
+ return nmt_connect_connection_list ();
+}
diff --git a/clients/tui/nmtui-connect.h b/clients/tui/nmtui-connect.h
new file mode 100644
index 0000000000..8310ac389a
--- /dev/null
+++ b/clients/tui/nmtui-connect.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMTUI_CONNECT_H
+#define NMTUI_CONNECT_H
+
+G_BEGIN_DECLS
+
+NmtNewtForm *nmtui_connect (int argc, char **argv);
+
+G_END_DECLS
+
+#endif /* NMTUI_CONNECT_H */
diff --git a/clients/tui/nmtui-edit.c b/clients/tui/nmtui-edit.c
new file mode 100644
index 0000000000..2e59204b64
--- /dev/null
+++ b/clients/tui/nmtui-edit.c
@@ -0,0 +1,510 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmtui-edit
+ * @short_description: nm-connection-editor-like functionality
+ *
+ * nmtui-edit implements editing #NMConnections.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+#include <nm-utils.h>
+
+#include "nmtui.h"
+#include "nmtui-edit.h"
+#include "nmt-edit-connection-list.h"
+#include "nmt-editor.h"
+#include "nmt-utils.h"
+
+#include "nm-editor-utils.h"
+
+static void
+list_add_connection (NmtEditConnectionList *list,
+ gpointer form)
+{
+ nmt_add_connection ();
+ nmt_newt_form_set_focus (form, NMT_NEWT_WIDGET (list));
+}
+
+static void
+list_edit_connection (NmtEditConnectionList *list,
+ NMConnection *connection,
+ gpointer form)
+{
+ nmt_edit_connection (connection);
+ nmt_newt_form_set_focus (form, NMT_NEWT_WIDGET (list));
+}
+
+static void
+list_remove_connection (NmtEditConnectionList *list,
+ NMRemoteConnection *connection,
+ gpointer form)
+{
+ nmt_remove_connection (connection);
+ nmt_newt_form_set_focus (form, NMT_NEWT_WIDGET (list));
+}
+
+static gboolean
+edit_connection_list_filter (NmtEditConnectionList *list,
+ NMConnection *connection,
+ gpointer user_data)
+{
+ NMSettingConnection *s_con;
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_return_val_if_fail (s_con != NULL, FALSE);
+
+ return (nm_setting_connection_get_slave_type (s_con) == NULL);
+}
+
+static NmtNewtForm *
+nmt_edit_main_connection_list (void)
+{
+ int screen_width, screen_height;
+ NmtNewtForm *form;
+ NmtNewtWidget *quit, *list;
+
+ newtGetScreenSize (&screen_width, &screen_height);
+
+ form = g_object_new (NMT_TYPE_NEWT_FORM,
+ "y", 2,
+ "height", screen_height - 4,
+ "escape-exits", TRUE,
+ NULL);
+
+ quit = nmt_newt_button_new (_("Quit"));
+ nmt_newt_widget_set_exit_on_activate (quit, TRUE);
+
+ list = g_object_new (NMT_TYPE_EDIT_CONNECTION_LIST,
+ "extra-widget", quit,
+ "connection-filter", edit_connection_list_filter,
+ NULL);
+
+ g_signal_connect (list, "add-connection",
+ G_CALLBACK (list_add_connection), form);
+ g_signal_connect (list, "edit-connection",
+ G_CALLBACK (list_edit_connection), form);
+ g_signal_connect (list, "remove-connection",
+ G_CALLBACK (list_remove_connection), form);
+
+ nmt_newt_form_set_content (form, list);
+ return form;
+}
+
+#define NMT_TYPE_ADD_CONNECTION (nmt_add_connection_get_type ())
+#define NMT_ADD_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ADD_CONNECTION, NmtAddConnection))
+#define NMT_IS_ADD_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ADD_CONNECTION))
+
+typedef NmtNewtForm NmtAddConnection;
+typedef NmtNewtFormClass NmtAddConnectionClass;
+
+GType nmt_add_connection_get_type (void);
+
+G_DEFINE_TYPE (NmtAddConnection, nmt_add_connection, NMT_TYPE_NEWT_FORM)
+
+#define NMT_ADD_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ADD_CONNECTION, NmtAddConnectionPrivate))
+
+typedef struct {
+ NmtNewtTextbox *textbox;
+ NmtNewtListbox *listbox;
+
+ char *primary_text;
+ char *secondary_text;
+ NMConnection *master;
+ NmtAddConnectionTypeFilter type_filter;
+ gpointer type_filter_data;
+
+ gboolean single_type;
+} NmtAddConnectionPrivate;
+
+enum {
+ PROP_0,
+
+ PROP_PRIMARY_TEXT,
+ PROP_SECONDARY_TEXT,
+ PROP_MASTER,
+ PROP_TYPE_FILTER,
+ PROP_TYPE_FILTER_DATA,
+
+ LAST_PROP
+};
+
+static void
+create_connection (NmtNewtWidget *widget, gpointer list)
+{
+ NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (list);
+ GType type = (GType) GPOINTER_TO_SIZE (nmt_newt_listbox_get_active_key (priv->listbox));
+ NMConnection *connection;
+
+ connection = nm_editor_utils_create_connection (type, priv->master, nm_settings);
+ nmt_edit_connection (connection);
+ g_object_unref (connection);
+
+ nmt_newt_form_quit (list);
+}
+
+static void
+nmt_add_connection_init (NmtAddConnection *form)
+{
+ NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (form);
+ NmtNewtWidget *textbox, *listbox, *button;
+ NmtNewtGrid *grid, *buttons;
+
+ grid = NMT_NEWT_GRID (nmt_newt_grid_new ());
+
+ textbox = nmt_newt_textbox_new (0, 60);
+ priv->textbox = NMT_NEWT_TEXTBOX (textbox);
+ nmt_newt_grid_add (grid, textbox, 0, 0);
+
+ listbox = nmt_newt_listbox_new (5, NMT_NEWT_LISTBOX_SCROLL);
+ priv->listbox = NMT_NEWT_LISTBOX (listbox);
+ g_signal_connect (priv->listbox, "activated", G_CALLBACK (create_connection), form);
+ nmt_newt_grid_add (grid, listbox, 0, 1);
+ nmt_newt_widget_set_padding (listbox, 0, 1, 0, 0);
+ nmt_newt_grid_set_flags (grid, listbox, NMT_NEWT_GRID_EXPAND_X);
+
+ // FIXME: VPN description textbox
+
+ buttons = NMT_NEWT_GRID (nmt_newt_grid_new ());
+ nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (buttons), 0, 2);
+ nmt_newt_widget_set_padding (NMT_NEWT_WIDGET (buttons), 0, 1, 0, 0);
+
+ button = g_object_ref_sink (nmt_newt_button_new (_("Cancel")));
+ nmt_newt_widget_set_exit_on_activate (button, TRUE);
+ nmt_newt_grid_add (NMT_NEWT_GRID (buttons), button, 0, 0);
+ nmt_newt_widget_set_padding (button, 0, 0, 1, 0);
+ nmt_newt_grid_set_flags (NMT_NEWT_GRID (buttons), button,
+ NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_ANCHOR_RIGHT |
+ NMT_NEWT_GRID_FILL_Y);
+
+ button = g_object_ref_sink (nmt_newt_button_new (_("Create")));
+ g_signal_connect (button, "clicked", G_CALLBACK (create_connection), form);
+ nmt_newt_grid_add (NMT_NEWT_GRID (buttons), button, 1, 0);
+
+ nmt_newt_form_set_content (NMT_NEWT_FORM (form), NMT_NEWT_WIDGET (grid));
+}
+
+static void
+nmt_add_connection_constructed (GObject *object)
+{
+ NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object);
+ NMEditorConnectionTypeData **types;
+ char *text;
+ int i, num_types;
+
+ if (priv->secondary_text) {
+ text = g_strdup_printf ("%s\n\n%s",
+ priv->primary_text,
+ priv->secondary_text);
+ } else
+ text = g_strdup (priv->primary_text);
+ nmt_newt_textbox_set_text (priv->textbox, text);
+ g_free (text);
+
+ types = nm_editor_utils_get_connection_type_list ();
+ for (i = num_types = 0; types[i]; i++) {
+ if (priv->type_filter && !priv->type_filter (types[i]->setting_type, priv->type_filter_data))
+ continue;
+ nmt_newt_listbox_append (priv->listbox, types[i]->name,
+ GSIZE_TO_POINTER (types[i]->setting_type));
+ num_types++;
+ }
+
+ if (num_types == 1)
+ priv->single_type = TRUE;
+
+ G_OBJECT_CLASS (nmt_add_connection_parent_class)->constructed (object);
+}
+
+static void
+nmt_add_connection_show (NmtNewtForm *form)
+{
+ NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (form);
+
+ if (priv->single_type) {
+ nmt_newt_listbox_set_active (priv->listbox, 0);
+ create_connection (NMT_NEWT_WIDGET (priv->listbox), g_object_ref (form));
+ } else
+ NMT_NEWT_FORM_CLASS (nmt_add_connection_parent_class)->show (form);
+}
+
+static void
+nmt_add_connection_finalize (GObject *object)
+{
+ NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object);
+
+ g_free (priv->primary_text);
+ g_free (priv->secondary_text);
+ g_clear_object (&priv->master);
+
+ G_OBJECT_CLASS (nmt_add_connection_parent_class)->finalize (object);
+}
+
+static void
+nmt_add_connection_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_PRIMARY_TEXT:
+ priv->primary_text = g_value_dup_string (value);
+ break;
+ case PROP_SECONDARY_TEXT:
+ priv->secondary_text = g_value_dup_string (value);
+ break;
+ case PROP_MASTER:
+ priv->master = g_value_dup_object (value);
+ break;
+ case PROP_TYPE_FILTER:
+ priv->type_filter = g_value_get_pointer (value);
+ break;
+ case PROP_TYPE_FILTER_DATA:
+ priv->type_filter_data = g_value_get_pointer (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_add_connection_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_PRIMARY_TEXT:
+ g_value_set_string (value, priv->primary_text);
+ break;
+ case PROP_SECONDARY_TEXT:
+ g_value_set_string (value, priv->secondary_text);
+ break;
+ case PROP_MASTER:
+ g_value_set_object (value, priv->master);
+ break;
+ case PROP_TYPE_FILTER:
+ g_value_set_pointer (value, priv->type_filter);
+ break;
+ case PROP_TYPE_FILTER_DATA:
+ g_value_set_pointer (value, priv->type_filter_data);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+nmt_add_connection_class_init (NmtAddConnectionClass *add_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (add_class);
+ NmtNewtFormClass *form_class = NMT_NEWT_FORM_CLASS (add_class);
+
+ g_type_class_add_private (add_class, sizeof (NmtAddConnectionPrivate));
+
+ /* virtual methods */
+ object_class->constructed = nmt_add_connection_constructed;
+ object_class->set_property = nmt_add_connection_set_property;
+ object_class->get_property = nmt_add_connection_get_property;
+ object_class->finalize = nmt_add_connection_finalize;
+
+ form_class->show = nmt_add_connection_show;
+
+ g_object_class_install_property
+ (object_class, PROP_PRIMARY_TEXT,
+ g_param_spec_string ("primary-text", "", "",
+ _("Select the type of connection you wish to create."),
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property
+ (object_class, PROP_SECONDARY_TEXT,
+ g_param_spec_string ("secondary-text", "", "",
+#if 0
+ _("If you are creating a VPN, and the VPN connection you "
+ "wish to create does not appear in the list, you may "
+ "not have the correct VPN plugin installed."),
+#else
+ NULL,
+#endif
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property
+ (object_class, PROP_MASTER,
+ g_param_spec_object ("master", "", "",
+ NM_TYPE_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property
+ (object_class, PROP_TYPE_FILTER,
+ g_param_spec_pointer ("type-filter", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property
+ (object_class, PROP_TYPE_FILTER_DATA,
+ g_param_spec_pointer ("type-filter-data", "", "",
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+void
+nmt_add_connection (void)
+{
+ NmtNewtForm *form;
+
+ form = g_object_new (NMT_TYPE_ADD_CONNECTION,
+ "title", _("New Connection"),
+ NULL);
+ nmt_newt_form_show (form);
+ g_object_unref (form);
+}
+
+void
+nmt_add_connection_full (const char *primary_text,
+ const char *secondary_text,
+ NMConnection *master,
+ NmtAddConnectionTypeFilter type_filter,
+ gpointer type_filter_data)
+{
+ NmtNewtForm *form;
+
+ form = g_object_new (NMT_TYPE_ADD_CONNECTION,
+ "title", _("New Connection"),
+ "primary-text", primary_text,
+ "secondary-text", secondary_text,
+ "master", master,
+ "type-filter", type_filter,
+ "type-filter-data", type_filter_data,
+ NULL);
+ nmt_newt_form_show (form);
+ g_object_unref (form);
+}
+
+void
+nmt_edit_connection (NMConnection *connection)
+{
+ NmtNewtForm *editor;
+
+ editor = nmt_editor_new (connection);
+ if (!editor)
+ return;
+
+ nmt_newt_form_show (editor);
+ g_object_unref (editor);
+}
+
+typedef struct {
+ NmtSyncOp op;
+ gboolean got_callback, got_signal;
+} ConnectionDeleteData;
+
+static void
+connection_deleted_callback (NMRemoteConnection *connection,
+ GError *error,
+ gpointer user_data)
+{
+ ConnectionDeleteData *data = user_data;
+
+ if (error) {
+ nmt_newt_message_dialog (_("Unable to delete connection: %s"),
+ error->message);
+ } else
+ data->got_callback = TRUE;
+
+ if (error || (data->got_callback && data->got_signal))
+ nmt_sync_op_complete_boolean (&data->op, error == NULL, error);
+}
+
+static void
+connection_removed_signal (NMRemoteConnection *connection,
+ gpointer user_data)
+{
+ ConnectionDeleteData *data = user_data;
+
+ data->got_signal = TRUE;
+ if (data->got_callback && data->got_signal)
+ nmt_sync_op_complete_boolean (&data->op, TRUE, NULL);
+}
+
+void
+nmt_remove_connection (NMRemoteConnection *connection)
+{
+ ConnectionDeleteData data;
+ int choice;
+ GError *error = NULL;
+
+ choice = nmt_newt_choice_dialog (_("Cancel"),
+ _("Delete"),
+ _("Are you sure you want to delete the connection '%s'?"),
+ nm_connection_get_id (NM_CONNECTION (connection)));
+ if (choice == 1)
+ return;
+
+ data.got_callback = data.got_signal = FALSE;
+ nmt_sync_op_init (&data.op);
+
+ g_object_ref (connection);
+ g_signal_connect (connection, NM_REMOTE_CONNECTION_REMOVED,
+ G_CALLBACK (connection_removed_signal), &data);
+ nm_remote_connection_delete (connection, connection_deleted_callback, &data);
+
+ if (!nmt_sync_op_wait_boolean (&data.op, &error)) {
+ nmt_newt_message_dialog (_("Could not delete connection: %s"),
+ error->message);
+ g_error_free (error);
+ }
+
+ g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed_signal), &data);
+ g_object_unref (connection);
+}
+
+NmtNewtForm *
+nmtui_edit (int argc, char **argv)
+{
+ NMConnection *conn = NULL;
+
+ if (argc == 2) {
+ if (nm_utils_is_uuid (argv[1]))
+ conn = NM_CONNECTION (nm_remote_settings_get_connection_by_uuid (nm_settings, argv[1]));
+ if (!conn)
+ conn = NM_CONNECTION (nm_remote_settings_get_connection_by_id (nm_settings, argv[1]));
+
+ if (!conn) {
+ nmt_newt_message_dialog ("%s: no such connection '%s'\n", argv[0], argv[1]);
+ return NULL;
+ }
+
+ return nmt_editor_new (conn);
+ } else
+ return nmt_edit_main_connection_list ();
+}
diff --git a/clients/tui/nmtui-edit.h b/clients/tui/nmtui-edit.h
new file mode 100644
index 0000000000..dae91cfac7
--- /dev/null
+++ b/clients/tui/nmtui-edit.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMTUI_EDIT_H
+#define NMTUI_EDIT_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+typedef gboolean (*NmtAddConnectionTypeFilter) (GType connection_type,
+ gpointer user_data);
+
+NmtNewtForm *nmtui_edit (int argc, char **argv);
+
+void nmt_add_connection (void);
+void nmt_add_connection_full (const char *primary_text,
+ const char *secondary_text,
+ NMConnection *master,
+ NmtAddConnectionTypeFilter type_filter,
+ gpointer type_filter_data);
+
+void nmt_edit_connection (NMConnection *connection);
+
+void nmt_remove_connection (NMRemoteConnection *connection);
+
+G_END_DECLS
+
+#endif /* NMTUI_EDIT_H */
diff --git a/clients/tui/nmtui-hostname.c b/clients/tui/nmtui-hostname.c
new file mode 100644
index 0000000000..7c8aff3a85
--- /dev/null
+++ b/clients/tui/nmtui-hostname.c
@@ -0,0 +1,124 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmtui-hostname
+ * @short_description: hostname-setting functionality
+ *
+ * nmtui-hostname implements the "set hostname" functionality
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-newt.h"
+
+#include "nmtui.h"
+#include "nmtui-hostname.h"
+#include "nmt-utils.h"
+
+static char *
+nmtui_hostname_run_dialog (void)
+{
+ NmtNewtForm *form;
+ NmtNewtWidget *widget, *ok, *cancel;
+ NmtNewtGrid *grid;
+ NmtNewtEntry *entry;
+ NmtNewtButtonBox *bbox;
+ char *hostname, *ret = NULL;
+
+ form = g_object_new (NMT_TYPE_NEWT_FORM,
+ "title", _("Set Hostname"),
+ "escape-exits", TRUE,
+ NULL);
+
+ widget = nmt_newt_grid_new ();
+ nmt_newt_form_set_content (form, widget);
+ grid = NMT_NEWT_GRID (widget);
+
+ widget = nmt_newt_label_new (_("Hostname"));
+ nmt_newt_grid_add (grid, widget, 0, 0);
+
+ widget = nmt_newt_entry_new (40, 0);
+ nmt_newt_widget_set_exit_on_activate (widget, TRUE);
+ nmt_newt_grid_add (grid, widget, 1, 0);
+ nmt_newt_widget_set_padding (widget, 1, 0, 0, 0);
+ entry = NMT_NEWT_ENTRY (widget);
+
+ widget = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL);
+ nmt_newt_grid_add (grid, widget, 1, 1);
+ nmt_newt_widget_set_padding (widget, 0, 1, 0, 0);
+ bbox = NMT_NEWT_BUTTON_BOX (widget);
+
+ cancel = nmt_newt_button_box_add_end (bbox, _("Cancel"));
+ nmt_newt_widget_set_exit_on_activate (cancel, TRUE);
+ ok = nmt_newt_button_box_add_end (bbox, _("OK"));
+ nmt_newt_widget_set_exit_on_activate (ok, TRUE);
+
+ g_object_get (G_OBJECT (nm_settings),
+ NM_REMOTE_SETTINGS_HOSTNAME, &hostname,
+ NULL);
+ nmt_newt_entry_set_text (entry, hostname);
+ g_free (hostname);
+
+ widget = nmt_newt_form_run_sync (form);
+ if (widget == (NmtNewtWidget *)entry || widget == ok)
+ ret = g_strdup (nmt_newt_entry_get_text (entry));
+
+ g_object_unref (form);
+ return ret;
+}
+
+static void
+hostname_set (NMRemoteSettings *settings,
+ GError *error,
+ gpointer op)
+{
+ nmt_sync_op_complete_boolean (op, error == NULL, error);
+}
+
+NmtNewtForm *
+nmtui_hostname (int argc, char **argv)
+{
+ const char *hostname;
+ char *tmp = NULL;
+ NmtSyncOp op;
+ GError *error = NULL;
+
+ if (argc == 2)
+ hostname = argv[1];
+ else
+ hostname = tmp = nmtui_hostname_run_dialog ();
+
+ if (hostname) {
+ nmt_sync_op_init (&op);
+ nm_remote_settings_save_hostname (nm_settings, hostname, hostname_set, &op);
+ if (nmt_sync_op_wait_boolean (&op, &error)) {
+ /* Translators: this indicates the result. ie, "I have set the hostname to ..." */
+ nmt_newt_message_dialog (_("Set hostname to '%s'"), hostname);
+ } else {
+ nmt_newt_message_dialog (_("Unable to set hostname: %s"), error->message);
+ g_error_free (error);
+ }
+
+ g_free (tmp);
+ }
+
+ return NULL;
+}
diff --git a/clients/tui/nmtui-hostname.h b/clients/tui/nmtui-hostname.h
new file mode 100644
index 0000000000..a14bc69937
--- /dev/null
+++ b/clients/tui/nmtui-hostname.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMTUI_HOSTNAME_H
+#define NMTUI_HOSTNAME_H
+
+G_BEGIN_DECLS
+
+NmtNewtForm *nmtui_hostname (int argc, char **argv);
+
+G_END_DECLS
+
+#endif /* NMTUI_HOSTNAME_H */
diff --git a/clients/tui/nmtui.c b/clients/tui/nmtui.c
new file mode 100644
index 0000000000..7fcac445a2
--- /dev/null
+++ b/clients/tui/nmtui.c
@@ -0,0 +1,310 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmtui
+ * @short_description: nmtui toplevel
+ *
+ * The top level of nmtui. Exists mostly just to call nmtui_connect(),
+ * nmtui_edit(), and nmtui_hostname().
+ */
+
+#include "config.h"
+
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <nm-client.h>
+#include <nm-connection.h>
+#include <nm-remote-settings.h>
+#include <nm-utils.h>
+
+#include "nmt-newt.h"
+#include "nm-editor-bindings.h"
+
+#include "nmtui.h"
+#include "nmtui-edit.h"
+#include "nmtui-connect.h"
+#include "nmtui-hostname.h"
+
+NMClient *nm_client;
+NMRemoteSettings *nm_settings;
+static GMainLoop *loop;
+
+typedef NmtNewtForm * (*NmtuiSubprogram) (int argc, char **argv);
+
+static const struct {
+ const char *name, *shortcut, *arg;
+ const char *display_name;
+ NmtuiSubprogram func;
+} subprograms[] = {
+ { "edit", "nmtui-edit", N_("connection"),
+ N_("Edit a connection"),
+ nmtui_edit },
+ { "connect", "nmtui-connect", N_("connection"),
+ N_("Activate a connection"),
+ nmtui_connect },
+ { "hostname", "nmtui-hostname", N_("new hostname"),
+ N_("Set system hostname"),
+ nmtui_hostname }
+};
+static const int num_subprograms = G_N_ELEMENTS (subprograms);
+
+static void
+quit_func (int argc, char **argv)
+{
+ nmtui_quit ();
+}
+
+static NmtNewtForm *
+nmtui_main (int argc, char **argv)
+{
+ NmtNewtForm *form;
+ NmtNewtWidget *widget, *ok;
+ NmtNewtGrid *grid;
+ NmtNewtListbox *listbox;
+ NmtNewtButtonBox *bbox;
+ NmtuiSubprogram subprogram = NULL;
+ int i;
+
+ form = g_object_new (NMT_TYPE_NEWT_FORM,
+ "title", _("NetworkManager TUI"),
+ "escape-exits", TRUE,
+ NULL);
+
+ widget = nmt_newt_grid_new ();
+ nmt_newt_form_set_content (form, widget);
+ grid = NMT_NEWT_GRID (widget);
+
+ widget = nmt_newt_label_new (_("Please select an option"));
+ nmt_newt_grid_add (grid, widget, 0, 0);
+
+ widget = g_object_new (NMT_TYPE_NEWT_LISTBOX,
+ "height", num_subprograms + 2,
+ "skip-null-keys", TRUE,
+ NULL);
+ nmt_newt_grid_add (grid, widget, 0, 1);
+ nmt_newt_widget_set_padding (widget, 0, 1, 0, 1);
+ nmt_newt_widget_set_exit_on_activate (widget, TRUE);
+ listbox = NMT_NEWT_LISTBOX (widget);
+
+ for (i = 0; i < num_subprograms; i++) {
+ nmt_newt_listbox_append (listbox, _(subprograms[i].display_name),
+ subprograms[i].func);
+ }
+ nmt_newt_listbox_append (listbox, "", NULL);
+ nmt_newt_listbox_append (listbox, _("Quit"), quit_func);
+
+ widget = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL);
+ nmt_newt_grid_add (grid, widget, 0, 2);
+ bbox = NMT_NEWT_BUTTON_BOX (widget);
+
+ ok = nmt_newt_button_box_add_end (bbox, _("OK"));
+ nmt_newt_widget_set_exit_on_activate (ok, TRUE);
+
+ widget = nmt_newt_form_run_sync (form);
+ if (widget)
+ subprogram = nmt_newt_listbox_get_active_key (listbox);
+ g_object_unref (form);
+
+ if (subprogram)
+ return subprogram (argc, argv);
+ else
+ return NULL;
+}
+
+/**
+ * nmtui_quit:
+ *
+ * Causes nmtui to exit.
+ */
+void
+nmtui_quit (void)
+{
+ g_main_loop_quit (loop);
+}
+
+static void
+connections_read (NMRemoteSettings *settings,
+ gpointer user_data)
+{
+ gboolean *got_connections = user_data;
+
+ *got_connections = TRUE;
+}
+
+static void
+usage (void)
+{
+ const char *argv0 = g_get_prgname ();
+ const char *usage_str = _("Usage");
+ int i;
+
+ for (i = 0; i < num_subprograms; i++) {
+ if (!strcmp (argv0, subprograms[i].shortcut)) {
+ g_printerr ("%s: %s [%s]\n", usage_str, argv0, _(subprograms[i].arg));
+ exit (1);
+ }
+ }
+
+ g_printerr ("%s: nmtui\n", usage_str);
+ for (i = 0; i < num_subprograms; i++) {
+ g_printerr ("%*s nmtui %s [%s]\n",
+ nmt_newt_text_width (usage_str), " ",
+ subprograms[i].name,
+ _(subprograms[i].arg));
+ }
+ exit (1);
+}
+
+typedef struct {
+ NmtuiSubprogram subprogram;
+ int argc;
+ char **argv;
+} NmtuiStartupData;
+
+static void
+toplevel_form_quit (NmtNewtForm *form,
+ gpointer user_data)
+{
+ nmtui_quit ();
+}
+
+static gboolean
+idle_run_subprogram (gpointer user_data)
+{
+ NmtuiStartupData *data = user_data;
+ NmtNewtForm *form;
+
+ form = data->subprogram (data->argc, data->argv);
+ if (form) {
+ g_signal_connect (form, "quit", G_CALLBACK (toplevel_form_quit), NULL);
+ nmt_newt_form_show (form);
+ g_object_unref (form);
+ } else
+ nmtui_quit ();
+
+ return FALSE;
+}
+
+gboolean sleep_on_startup = FALSE;
+gboolean noinit = FALSE;
+
+GOptionEntry entries[] = {
+ { "sleep", 's', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &sleep_on_startup,
+ "Sleep on startup", NULL },
+ { "noinit", 'n', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &noinit,
+ "Don't initialize newt", NULL },
+ { NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+ gboolean got_connections = FALSE;
+ GOptionContext *opts;
+ GError *error = NULL;
+ NmtuiStartupData startup_data;
+ const char *prgname;
+ int i;
+
+ setlocale (LC_ALL, "");
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ opts = g_option_context_new (NULL);
+ g_option_context_add_main_entries (opts, entries, NULL);
+
+ if (!g_option_context_parse (opts, &argc, &argv, &error)) {
+ g_printerr ("%s: %s: %s\n",
+ argv[0],
+ _("Could not parse arguments"),
+ error->message);
+ exit (1);
+ }
+ g_option_context_free (opts);
+
+ nm_editor_bindings_init ();
+
+ nm_client = nm_client_new ();
+ if (!nm_client_get_manager_running (nm_client)) {
+ g_printerr ("%s\n", _("NetworkManager is not running."));
+ exit (1);
+ }
+
+ nm_settings = nm_remote_settings_new (NULL);
+ g_signal_connect (nm_settings, NM_REMOTE_SETTINGS_CONNECTIONS_READ,
+ G_CALLBACK (connections_read), &got_connections);
+ /* coverity[loop_condition] */
+ while (!got_connections)
+ g_main_context_iteration (NULL, TRUE);
+
+ if (sleep_on_startup)
+ sleep (5);
+
+ startup_data.subprogram = NULL;
+ prgname = g_get_prgname ();
+ if (g_str_has_prefix (prgname, "lt-"))
+ prgname += 3;
+ if (!strcmp (prgname, "nmtui")) {
+ if (argc > 1) {
+ for (i = 0; i < num_subprograms; i++) {
+ if (!strcmp (argv[1], subprograms[i].name)) {
+ argc--;
+ argv[0] = (char *) subprograms[i].shortcut;
+ memmove (&argv[1], &argv[2], argc * sizeof (char *));
+ startup_data.subprogram = subprograms[i].func;
+ break;
+ }
+ }
+ } else
+ startup_data.subprogram = nmtui_main;
+ } else {
+ for (i = 0; i < num_subprograms; i++) {
+ if (!strcmp (prgname, subprograms[i].shortcut)) {
+ startup_data.subprogram = subprograms[i].func;
+ break;
+ }
+ }
+ }
+ if (!startup_data.subprogram)
+ usage ();
+
+ if (!noinit)
+ nmt_newt_init ();
+
+ startup_data.argc = argc;
+ startup_data.argv = argv;
+ g_idle_add (idle_run_subprogram, &startup_data);
+ loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (loop);
+ g_main_loop_unref (loop);
+
+ if (!noinit)
+ nmt_newt_finished ();
+
+ g_object_unref (nm_client);
+ g_object_unref (nm_settings);
+
+ return 0;
+}
diff --git a/clients/tui/nmtui.h b/clients/tui/nmtui.h
new file mode 100644
index 0000000000..3e462b1130
--- /dev/null
+++ b/clients/tui/nmtui.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMTUI_H
+#define NMTUI_H
+
+#include <nm-client.h>
+#include <nm-remote-settings.h>
+
+G_BEGIN_DECLS
+
+extern NMClient *nm_client;
+extern NMRemoteSettings *nm_settings;
+
+void nmtui_quit (void);
+
+G_END_DECLS
+
+#endif /* NMTUI_H */
diff --git a/clients/tui/vpn-helpers.c b/clients/tui/vpn-helpers.c
new file mode 100644
index 0000000000..f4e32837a8
--- /dev/null
+++ b/clients/tui/vpn-helpers.c
@@ -0,0 +1,424 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:vpn-helpers
+ * @short_description: VPN-related utilities
+ *
+ * This is copied directly from libnm-gtk and should probably
+ * eventually move into libnm-glib.
+ *
+ * It is also currently unused in nmtui.
+ *
+ * FIXME.
+ */
+
+#include <string.h>
+#include <glib.h>
+#include <gmodule.h>
+#include <glib/gi18n.h>
+
+#include <nm-connection.h>
+#include <nm-setting-connection.h>
+#include <nm-setting-vpn.h>
+
+#include "vpn-helpers.h"
+
+#define NM_VPN_API_SUBJECT_TO_CHANGE
+#include "nm-vpn-plugin-ui-interface.h"
+
+#define VPN_NAME_FILES_DIR SYSCONFDIR"/NetworkManager/VPN"
+
+static GHashTable *plugins = NULL;
+
+G_DEFINE_QUARK (NMA_ERROR, nma_error)
+#define NMA_ERROR nma_error_quark ()
+#define NMA_ERROR_GENERIC 0
+
+NMVpnPluginUiInterface *
+vpn_get_plugin_by_service (const char *service)
+{
+ g_return_val_if_fail (service != NULL, NULL);
+
+ return g_hash_table_lookup (plugins, service);
+}
+
+GHashTable *
+vpn_get_plugins (GError **error)
+{
+ GDir *dir;
+ const char *f;
+
+ if (error)
+ g_return_val_if_fail (*error == NULL, NULL);
+
+ if (plugins)
+ return plugins;
+
+ dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
+ if (!dir) {
+ g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "Couldn't read VPN .name files directory " VPN_NAME_FILES_DIR ".");
+ return NULL;
+ }
+
+ plugins = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref);
+
+ while ((f = g_dir_read_name (dir))) {
+ char *path = NULL, *service = NULL;
+ char *so_path = NULL, *so_name = NULL;
+ GKeyFile *keyfile = NULL;
+ GModule *module;
+ NMVpnPluginUiFactory factory = NULL;
+
+ if (!g_str_has_suffix (f, ".name"))
+ continue;
+
+ path = g_strdup_printf ("%s/%s", VPN_NAME_FILES_DIR, f);
+
+ keyfile = g_key_file_new ();
+ if (!g_key_file_load_from_file (keyfile, path, 0, NULL))
+ goto next;
+
+ service = g_key_file_get_string (keyfile, "VPN Connection", "service", NULL);
+ if (!service)
+ goto next;
+
+ so_path = g_key_file_get_string (keyfile, "GNOME", "properties", NULL);
+ if (!so_path)
+ goto next;
+
+ /* Remove any path and extension components, then reconstruct path
+ * to the SO in LIBDIR
+ */
+ so_name = g_path_get_basename (so_path);
+ g_free (so_path);
+ so_path = g_strdup_printf ("%s/NetworkManager/%s", LIBDIR, so_name);
+ g_free (so_name);
+
+ module = g_module_open (so_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+ if (!module) {
+ g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "Cannot load the VPN plugin which provides the "
+ "service '%s'.", service);
+ goto next;
+ }
+
+ if (g_module_symbol (module, "nm_vpn_plugin_ui_factory", (gpointer) &factory)) {
+ NMVpnPluginUiInterface *plugin;
+ GError *factory_error = NULL;
+ gboolean success = FALSE;
+
+ plugin = factory (&factory_error);
+ if (plugin) {
+ char *plug_name = NULL, *plug_service = NULL;
+
+ /* Validate plugin properties */
+ g_object_get (G_OBJECT (plugin),
+ NM_VPN_PLUGIN_UI_INTERFACE_NAME, &plug_name,
+ NM_VPN_PLUGIN_UI_INTERFACE_SERVICE, &plug_service,
+ NULL);
+ if (!plug_name || !strlen (plug_name)) {
+ g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': missing plugin name",
+ g_module_name (module));
+ } else if (!plug_service || strcmp (plug_service, service)) {
+ g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': invalid service name",
+ g_module_name (module));
+ } else {
+ /* Success! */
+ g_object_set_data_full (G_OBJECT (plugin), "gmodule", module,
+ (GDestroyNotify) g_module_close);
+ g_hash_table_insert (plugins, g_strdup (service), plugin);
+ success = TRUE;
+ }
+ g_free (plug_name);
+ g_free (plug_service);
+ } else {
+ g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': %s",
+ g_module_name (module), g_module_error ());
+ }
+
+ if (!success)
+ g_module_close (module);
+ } else {
+ g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot locate nm_vpn_plugin_ui_factory() in '%s': %s",
+ g_module_name (module), g_module_error ());
+ g_module_close (module);
+ }
+
+ next:
+ g_free (so_path);
+ g_free (service);
+ g_key_file_free (keyfile);
+ g_free (path);
+ }
+ g_dir_close (dir);
+
+ return plugins;
+}
+
+#if 0
+typedef struct {
+ VpnImportSuccessCallback callback;
+ gpointer user_data;
+} ActionInfo;
+
+static void
+import_vpn_from_file_cb (GtkWidget *dialog, gint response, gpointer user_data)
+{
+ char *filename = NULL;
+ ActionInfo *info = (ActionInfo *) user_data;
+ GHashTableIter iter;
+ gpointer key;
+ NMVpnPluginUiInterface *plugin;
+ NMConnection *connection = NULL;
+ GError *error = NULL;
+
+ if (response != GTK_RESPONSE_ACCEPT)
+ goto out;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ if (!filename) {
+ g_warning ("%s: didn't get a filename back from the chooser!", __func__);
+ goto out;
+ }
+
+ g_hash_table_iter_init (&iter, plugins);
+ while (!connection && g_hash_table_iter_next (&iter, &key, (gpointer *)&plugin)) {
+ g_clear_error (&error);
+ connection = nm_vpn_plugin_ui_interface_import (plugin, filename, &error);
+ }
+
+ if (connection)
+ info->callback (connection, info->user_data);
+ else {
+ GtkWidget *err_dialog;
+ char *bname = g_path_get_basename (filename);
+
+ err_dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Cannot import VPN connection"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog),
+ _("The file '%s' could not be read or does not contain recognized VPN connection information\n\nError: %s."),
+ bname, error ? error->message : "unknown error");
+ g_free (bname);
+ g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL);
+ g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_widget_show_all (err_dialog);
+ gtk_window_present (GTK_WINDOW (err_dialog));
+ }
+
+ g_clear_error (&error);
+ g_free (filename);
+
+out:
+ gtk_widget_hide (dialog);
+ gtk_widget_destroy (dialog);
+ g_free (info);
+}
+
+static void
+destroy_import_chooser (GtkWidget *dialog, gpointer user_data)
+{
+ g_free (user_data);
+ gtk_widget_destroy (dialog);
+}
+
+void
+vpn_import (VpnImportSuccessCallback callback, gpointer user_data)
+{
+ GtkWidget *dialog;
+ ActionInfo *info;
+ const char *home_folder;
+
+ dialog = gtk_file_chooser_dialog_new (_("Select file to import"),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+ NULL);
+ home_folder = g_get_home_dir ();
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder);
+
+ info = g_malloc0 (sizeof (ActionInfo));
+ info->callback = callback;
+ info->user_data = user_data;
+
+ g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (destroy_import_chooser), info);
+ g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (import_vpn_from_file_cb), info);
+ gtk_widget_show_all (dialog);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+export_vpn_to_file_cb (GtkWidget *dialog, gint response, gpointer user_data)
+{
+ NMConnection *connection = NM_CONNECTION (user_data);
+ char *filename = NULL;
+ GError *error = NULL;
+ NMVpnPluginUiInterface *plugin;
+ NMSettingConnection *s_con = NULL;
+ NMSettingVPN *s_vpn = NULL;
+ const char *service_type;
+ const char *id = NULL;
+ gboolean success = FALSE;
+
+ if (response != GTK_RESPONSE_ACCEPT)
+ goto out;
+
+ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
+ if (!filename) {
+ g_set_error (&error, NMA_ERROR, NMA_ERROR_GENERIC, "no filename");
+ goto done;
+ }
+
+ if (g_file_test (filename, G_FILE_TEST_EXISTS)) {
+ int replace_response;
+ GtkWidget *replace_dialog;
+ char *bname;
+
+ bname = g_path_get_basename (filename);
+ replace_dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_QUESTION,
+ GTK_BUTTONS_CANCEL,
+ _("A file named \"%s\" already exists."),
+ bname);
+ gtk_dialog_add_buttons (GTK_DIALOG (replace_dialog), _("_Replace"), GTK_RESPONSE_OK, NULL);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (replace_dialog),
+ _("Do you want to replace %s with the VPN connection you are saving?"), bname);
+ g_free (bname);
+ replace_response = gtk_dialog_run (GTK_DIALOG (replace_dialog));
+ gtk_widget_destroy (replace_dialog);
+ if (replace_response != GTK_RESPONSE_OK)
+ goto out;
+ }
+
+ s_con = nm_connection_get_setting_connection (connection);
+ id = s_con ? nm_setting_connection_get_id (s_con) : NULL;
+ if (!id) {
+ g_set_error (&error, NMA_ERROR, NMA_ERROR_GENERIC, "connection setting invalid");
+ goto done;
+ }
+
+ s_vpn = nm_connection_get_setting_vpn (connection);
+ service_type = s_vpn ? nm_setting_vpn_get_service_type (s_vpn) : NULL;
+
+ if (!service_type) {
+ g_set_error (&error, NMA_ERROR, NMA_ERROR_GENERIC, "VPN setting invalid");
+ goto done;
+ }
+
+ plugin = vpn_get_plugin_by_service (service_type);
+ if (plugin)
+ success = nm_vpn_plugin_ui_interface_export (plugin, filename, connection, &error);
+
+done:
+ if (!success) {
+ GtkWidget *err_dialog;
+ char *bname = filename ? g_path_get_basename (filename) : g_strdup ("(none)");
+
+ err_dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("Cannot export VPN connection"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog),
+ _("The VPN connection '%s' could not be exported to %s.\n\nError: %s."),
+ id ? id : "(unknown)", bname, error ? error->message : "unknown error");
+ g_free (bname);
+ g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL);
+ g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_widget_show_all (err_dialog);
+ gtk_window_present (GTK_WINDOW (err_dialog));
+ }
+
+out:
+ if (error)
+ g_error_free (error);
+ g_object_unref (connection);
+
+ gtk_widget_hide (dialog);
+ gtk_widget_destroy (dialog);
+}
+
+void
+vpn_export (NMConnection *connection)
+{
+ GtkWidget *dialog;
+ NMVpnPluginUiInterface *plugin;
+ NMSettingVPN *s_vpn = NULL;
+ const char *service_type;
+ const char *home_folder;
+
+ s_vpn = nm_connection_get_setting_vpn (connection);
+ service_type = s_vpn ? nm_setting_vpn_get_service_type (s_vpn) : NULL;
+
+ if (!service_type) {
+ g_warning ("%s: invalid VPN connection!", __func__);
+ return;
+ }
+
+ dialog = gtk_file_chooser_dialog_new (_("Export VPN connection..."),
+ NULL,
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
+ NULL);
+ home_folder = g_get_home_dir ();
+ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder);
+
+ plugin = vpn_get_plugin_by_service (service_type);
+ if (plugin) {
+ char *suggested = NULL;
+
+ suggested = nm_vpn_plugin_ui_interface_get_suggested_name (plugin, connection);
+ if (suggested) {
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), suggested);
+ g_free (suggested);
+ }
+ }
+
+ g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (gtk_widget_destroy), NULL);
+ g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (export_vpn_to_file_cb), g_object_ref (connection));
+ gtk_widget_show_all (dialog);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+#endif
+
+gboolean
+vpn_supports_ipv6 (NMConnection *connection)
+{
+ NMSettingVPN *s_vpn;
+ const char *service_type;
+ NMVpnPluginUiInterface *plugin;
+ guint32 capabilities;
+
+ s_vpn = nm_connection_get_setting_vpn (connection);
+ g_return_val_if_fail (s_vpn != NULL, FALSE);
+
+ service_type = nm_setting_vpn_get_service_type (s_vpn);
+ g_return_val_if_fail (service_type != NULL, FALSE);
+
+ plugin = vpn_get_plugin_by_service (service_type);
+ g_return_val_if_fail (plugin != NULL, FALSE);
+
+ capabilities = nm_vpn_plugin_ui_interface_get_capabilities (plugin);
+ return (capabilities & NM_VPN_PLUGIN_UI_CAPABILITY_IPV6) != 0;
+}
diff --git a/clients/tui/vpn-helpers.h b/clients/tui/vpn-helpers.h
new file mode 100644
index 0000000000..28019ca9c8
--- /dev/null
+++ b/clients/tui/vpn-helpers.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * 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/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef _VPN_HELPERS_H_
+#define _VPN_HELPERS_H_
+
+#include <glib.h>
+#include <nm-connection.h>
+
+#define NM_VPN_API_SUBJECT_TO_CHANGE
+#include <nm-vpn-plugin-ui-interface.h>
+
+GHashTable *vpn_get_plugins (GError **error);
+
+NMVpnPluginUiInterface *vpn_get_plugin_by_service (const char *service);
+
+typedef void (*VpnImportSuccessCallback) (NMConnection *connection, gpointer user_data);
+void vpn_import (VpnImportSuccessCallback callback, gpointer user_data);
+
+void vpn_export (NMConnection *connection);
+
+gboolean vpn_supports_ipv6 (NMConnection *connection);
+
+#endif /* _VPN_HELPERS_H_ */