summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore6
-rw-r--r--Makefile.am1
-rw-r--r--configure.ac14
-rw-r--r--po/POTFILES.in32
-rw-r--r--tui/Makefile.am122
-rw-r--r--tui/newt/Makefile.am51
-rw-r--r--tui/newt/nmt-newt-button-box.c391
-rw-r--r--tui/newt/nmt-newt-button-box.h65
-rw-r--r--tui/newt/nmt-newt-button.c260
-rw-r--r--tui/newt/nmt-newt-button.h53
-rw-r--r--tui/newt/nmt-newt-checkbox.c235
-rw-r--r--tui/newt/nmt-newt-checkbox.h53
-rw-r--r--tui/newt/nmt-newt-component.c307
-rw-r--r--tui/newt/nmt-newt-component.h57
-rw-r--r--tui/newt/nmt-newt-container.c252
-rw-r--r--tui/newt/nmt-newt-container.h61
-rw-r--r--tui/newt/nmt-newt-entry-numeric.c213
-rw-r--r--tui/newt/nmt-newt-entry-numeric.h51
-rw-r--r--tui/newt/nmt-newt-entry.c536
-rw-r--r--tui/newt/nmt-newt-entry.h74
-rw-r--r--tui/newt/nmt-newt-form.c656
-rw-r--r--tui/newt/nmt-newt-form.h62
-rw-r--r--tui/newt/nmt-newt-grid.c472
-rw-r--r--tui/newt/nmt-newt-grid.h72
-rw-r--r--tui/newt/nmt-newt-hacks.c103
-rw-r--r--tui/newt/nmt-newt-hacks.h42
-rw-r--r--tui/newt/nmt-newt-label.c323
-rw-r--r--tui/newt/nmt-newt-label.h66
-rw-r--r--tui/newt/nmt-newt-listbox.c539
-rw-r--r--tui/newt/nmt-newt-listbox.h71
-rw-r--r--tui/newt/nmt-newt-popup.c343
-rw-r--r--tui/newt/nmt-newt-popup.h62
-rw-r--r--tui/newt/nmt-newt-section.c408
-rw-r--r--tui/newt/nmt-newt-section.h57
-rw-r--r--tui/newt/nmt-newt-separator.c66
-rw-r--r--tui/newt/nmt-newt-separator.h49
-rw-r--r--tui/newt/nmt-newt-stack.c366
-rw-r--r--tui/newt/nmt-newt-stack.h61
-rw-r--r--tui/newt/nmt-newt-textbox.c292
-rw-r--r--tui/newt/nmt-newt-textbox.h59
-rw-r--r--tui/newt/nmt-newt-toggle-button.c234
-rw-r--r--tui/newt/nmt-newt-toggle-button.h54
-rw-r--r--tui/newt/nmt-newt-types.h49
-rw-r--r--tui/newt/nmt-newt-utils.c333
-rw-r--r--tui/newt/nmt-newt-utils.h51
-rw-r--r--tui/newt/nmt-newt-widget.c648
-rw-r--r--tui/newt/nmt-newt-widget.h117
-rw-r--r--tui/newt/nmt-newt.h39
-rw-r--r--tui/nm-editor-bindings.c1666
-rw-r--r--tui/nm-editor-bindings.h99
-rw-r--r--tui/nm-editor-utils.c420
-rw-r--r--tui/nm-editor-utils.h44
-rw-r--r--tui/nm-gvaluearray-compat.h101
-rw-r--r--tui/nm-ui-utils.c587
-rw-r--r--tui/nm-ui-utils.h41
-rw-r--r--tui/nmt-address-list.c284
-rw-r--r--tui/nmt-address-list.h57
-rw-r--r--tui/nmt-connect-connection-list.c820
-rw-r--r--tui/nmt-connect-connection-list.h58
-rw-r--r--tui/nmt-device-entry.c589
-rw-r--r--tui/nmt-device-entry.h61
-rw-r--r--tui/nmt-edit-connection-list.c550
-rw-r--r--tui/nmt-edit-connection-list.h61
-rw-r--r--tui/nmt-editor-page.c227
-rw-r--r--tui/nmt-editor-page.h57
-rw-r--r--tui/nmt-editor.c330
-rw-r--r--tui/nmt-editor.h51
-rw-r--r--tui/nmt-ip-entry.c265
-rw-r--r--tui/nmt-ip-entry.h52
-rw-r--r--tui/nmt-mac-entry.c217
-rw-r--r--tui/nmt-mac-entry.h54
-rw-r--r--tui/nmt-mtu-entry.c191
-rw-r--r--tui/nmt-mtu-entry.h49
-rw-r--r--tui/nmt-page-bond.c436
-rw-r--r--tui/nmt-page-bond.h50
-rw-r--r--tui/nmt-page-bridge-port.c92
-rw-r--r--tui/nmt-page-bridge-port.h49
-rw-r--r--tui/nmt-page-bridge.c153
-rw-r--r--tui/nmt-page-bridge.h50
-rw-r--r--tui/nmt-page-device.c151
-rw-r--r--tui/nmt-page-device.h51
-rw-r--r--tui/nmt-page-ethernet.c97
-rw-r--r--tui/nmt-page-ethernet.h50
-rw-r--r--tui/nmt-page-grid.c458
-rw-r--r--tui/nmt-page-grid.h62
-rw-r--r--tui/nmt-page-infiniband.c101
-rw-r--r--tui/nmt-page-infiniband.h50
-rw-r--r--tui/nmt-page-ip4.c199
-rw-r--r--tui/nmt-page-ip4.h51
-rw-r--r--tui/nmt-page-ip6.c197
-rw-r--r--tui/nmt-page-ip6.h51
-rw-r--r--tui/nmt-page-main.c327
-rw-r--r--tui/nmt-page-main.h51
-rw-r--r--tui/nmt-page-team-port.c125
-rw-r--r--tui/nmt-page-team-port.h49
-rw-r--r--tui/nmt-page-team.c195
-rw-r--r--tui/nmt-page-team.h50
-rw-r--r--tui/nmt-page-vlan.c163
-rw-r--r--tui/nmt-page-vlan.h50
-rw-r--r--tui/nmt-page-wifi.c389
-rw-r--r--tui/nmt-page-wifi.h50
-rw-r--r--tui/nmt-password-dialog.c343
-rw-r--r--tui/nmt-password-dialog.h57
-rw-r--r--tui/nmt-password-fields.c310
-rw-r--r--tui/nmt-password-fields.h55
-rw-r--r--tui/nmt-route-editor.c219
-rw-r--r--tui/nmt-route-editor.h51
-rw-r--r--tui/nmt-route-entry.c323
-rw-r--r--tui/nmt-route-entry.h51
-rw-r--r--tui/nmt-route-table.c388
-rw-r--r--tui/nmt-route-table.h49
-rw-r--r--tui/nmt-secret-agent.c645
-rw-r--r--tui/nmt-secret-agent.h57
-rw-r--r--tui/nmt-slave-list.c264
-rw-r--r--tui/nmt-slave-list.h52
-rw-r--r--tui/nmt-utils.c151
-rw-r--r--tui/nmt-utils.h46
-rw-r--r--tui/nmt-widget-list.c491
-rw-r--r--tui/nmt-widget-list.h69
-rw-r--r--tui/nmtui-connect.c130
-rw-r--r--tui/nmtui-connect.h28
-rw-r--r--tui/nmtui-edit.c527
-rw-r--r--tui/nmtui-edit.h44
-rw-r--r--tui/nmtui-hostname.c124
-rw-r--r--tui/nmtui-hostname.h28
-rw-r--r--tui/nmtui.c274
-rw-r--r--tui/nmtui.h34
-rw-r--r--tui/vpn-helpers.c424
-rw-r--r--tui/vpn-helpers.h39
129 files changed, 24528 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index 50d14854cf..71f7e5333f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -215,6 +215,12 @@ valgrind-*.log
/cli/src/nmcli
+/tui/newt/libnmt-newt.a
+/tui/nmtui
+/tui/nmtui-connect
+/tui/nmtui-edit
+/tui/nmtui-hostname
+
/tools/generate-settings-spec
/vapi/*.vapi
diff --git a/Makefile.am b/Makefile.am
index ebaad4bd55..ab2941cc6c 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,6 +9,7 @@ SUBDIRS = \
src \
callouts \
cli \
+ tui \
tools \
policy \
data \
diff --git a/configure.ac b/configure.ac
index 41bdb08a37..f19b9adea3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -653,6 +653,17 @@ else
libndp_location=system
fi
+AC_ARG_WITH(nmtui, AS_HELP_STRING([--with-nmtui=yes|no], [Build nmtui]))
+if test "$with_nmtui" != no; then
+ PKG_CHECK_MODULES(NEWT, [libnewt >= 0.52.15], [build_nmtui=yes], [build_nmtui=no])
+else
+ build_nmtui=no
+fi
+if test "$with_nmtui" = yes -a "$build_nmtui" = no; then
+ AC_MSG_ERROR([You must have libnewt installed to build nmtui.])
+fi
+AM_CONDITIONAL(BUILD_NMTUI, test "$build_nmtui" = yes)
+
NM_COMPILER_WARNINGS
@@ -741,6 +752,8 @@ tools/Makefile
cli/Makefile
cli/src/Makefile
cli/completion/Makefile
+tui/Makefile
+tui/newt/Makefile
test/Makefile
initscript/RedHat/NetworkManager
initscript/Debian/NetworkManager
@@ -817,6 +830,7 @@ echo " modemmanager-1: $with_modem_manager_1"
echo " concheck: $enable_concheck"
echo " libndp: $libndp_location"
echo " libteamdctl: $enable_teamdctl"
+echo " nmtui: $build_nmtui"
echo
echo "Configuration plugins"
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3be5735052..98516c1972 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -61,4 +61,34 @@ src/nm-sleep-monitor-systemd.c
src/settings/plugins/ifcfg-rh/reader.c
src/settings/nm-settings-utils.c
test/nm-online.c
-
+tui/newt/nmt-newt-utils.c
+tui/nm-editor-utils.c
+tui/nm-ui-utils.c
+tui/nmt-connect-connection-list.c
+tui/nmt-device-entry.c
+tui/nmt-edit-connection-list.c
+tui/nmt-editor.c
+tui/nmt-mtu-entry.c
+tui/nmt-page-bond.c
+tui/nmt-page-bridge-port.c
+tui/nmt-page-bridge.c
+tui/nmt-page-ethernet.c
+tui/nmt-page-infiniband.c
+tui/nmt-page-ip4.c
+tui/nmt-page-ip6.c
+tui/nmt-page-main.c
+tui/nmt-page-team-port.c
+tui/nmt-page-team.c
+tui/nmt-page-vlan.c
+tui/nmt-page-wifi.c
+tui/nmt-password-dialog.c
+tui/nmt-password-fields.c
+tui/nmt-route-editor.c
+tui/nmt-route-table.c
+tui/nmt-secret-agent.c
+tui/nmt-slave-list.c
+tui/nmt-widget-list.c
+tui/nmtui-connect.c
+tui/nmtui-edit.c
+tui/nmtui-hostname.c
+tui/nmtui.c
diff --git a/tui/Makefile.am b/tui/Makefile.am
new file mode 100644
index 0000000000..6c02ffc2c9
--- /dev/null
+++ b/tui/Makefile.am
@@ -0,0 +1,122 @@
+if BUILD_NMTUI
+
+SUBDIRS = newt .
+
+AM_CPPFLAGS= \
+ -I$(top_srcdir) \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/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) \
+ -DLOCALEDIR=\""$(localedir)"\" \
+ $(NULL)
+
+bin_PROGRAMS = nmtui
+bin_SCRIPTS = nmtui-edit nmtui-connect nmtui-hostname
+
+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 \
+ nm-gvaluearray-compat.h \
+ nm-ui-utils.c \
+ nm-ui-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)
+
+$(bin_SCRIPTS):
+ ln -s nmtui $@
+
+CLEANFILES = $(bin_SCRIPTS)
+
+endif
diff --git a/tui/newt/Makefile.am b/tui/newt/Makefile.am
new file mode 100644
index 0000000000..c5b295cade
--- /dev/null
+++ b/tui/newt/Makefile.am
@@ -0,0 +1,51 @@
+AM_CPPFLAGS= \
+ $(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/tui/newt/nmt-newt-button-box.c b/tui/newt/nmt-newt-button-box.c
new file mode 100644
index 0000000000..d38ed10d1a
--- /dev/null
+++ b/tui/newt/nmt-newt-button-box.c
@@ -0,0 +1,391 @@
+/* -*- 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 + (height - child_height) / 2,
+ child_width, child_height);
+ child_x += child_width + 1;
+ } else {
+ nmt_newt_widget_size_allocate (child,
+ child_x + (width - child_width) / 2, 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 + (height - child_height) / 2,
+ child_width, child_height);
+ child_x -= child_width + 1;
+ } else {
+ nmt_newt_widget_size_allocate (child,
+ child_x + (width - child_width) / 2,
+ 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/tui/newt/nmt-newt-button-box.h b/tui/newt/nmt-newt-button-box.h
new file mode 100644
index 0000000000..970588a4d0
--- /dev/null
+++ b/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/tui/newt/nmt-newt-button.c b/tui/newt/nmt-newt-button.c
new file mode 100644
index 0000000000..cdec789814
--- /dev/null
+++ b/tui/newt/nmt-newt-button.c
@@ -0,0 +1,260 @@
+/* -*- 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_LAST,
+ 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/tui/newt/nmt-newt-button.h b/tui/newt/nmt-newt-button.h
new file mode 100644
index 0000000000..52e95c5d13
--- /dev/null
+++ b/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/tui/newt/nmt-newt-checkbox.c b/tui/newt/nmt-newt-checkbox.c
new file mode 100644
index 0000000000..168fada20e
--- /dev/null
+++ b/tui/newt/nmt-newt-checkbox.c
@@ -0,0 +1,235 @@
+/* -*- 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/tui/newt/nmt-newt-checkbox.h b/tui/newt/nmt-newt-checkbox.h
new file mode 100644
index 0000000000..c277386b18
--- /dev/null
+++ b/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/tui/newt/nmt-newt-component.c b/tui/newt/nmt-newt-component.c
new file mode 100644
index 0000000000..877b53dbf1
--- /dev/null
+++ b/tui/newt/nmt-newt-component.c
@@ -0,0 +1,307 @@
+/* -*- 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/tui/newt/nmt-newt-component.h b/tui/newt/nmt-newt-component.h
new file mode 100644
index 0000000000..2ffa018e1a
--- /dev/null
+++ b/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/tui/newt/nmt-newt-container.c b/tui/newt/nmt-newt-container.c
new file mode 100644
index 0000000000..03f677fbdc
--- /dev/null
+++ b/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/tui/newt/nmt-newt-container.h b/tui/newt/nmt-newt-container.h
new file mode 100644
index 0000000000..6f182925d5
--- /dev/null
+++ b/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/tui/newt/nmt-newt-entry-numeric.c b/tui/newt/nmt-newt-entry-numeric.c
new file mode 100644
index 0000000000..76ca1c37a9
--- /dev/null
+++ b/tui/newt/nmt-newt-entry-numeric.c
@@ -0,0 +1,213 @@
+/* -*- 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 pthe
+ * 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/tui/newt/nmt-newt-entry-numeric.h b/tui/newt/nmt-newt-entry-numeric.h
new file mode 100644
index 0000000000..43ac34490a
--- /dev/null
+++ b/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/tui/newt/nmt-newt-entry.c b/tui/newt/nmt-newt-entry.c
new file mode 100644
index 0000000000..c98fcee8a4
--- /dev/null
+++ b/tui/newt/nmt-newt-entry.c
@@ -0,0 +1,536 @@
+/* -*- 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/tui/newt/nmt-newt-entry.h b/tui/newt/nmt-newt-entry.h
new file mode 100644
index 0000000000..8df0b13701
--- /dev/null
+++ b/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/tui/newt/nmt-newt-form.c b/tui/newt/nmt-newt-form.c
new file mode 100644
index 0000000000..ff8eb83609
--- /dev/null
+++ b/tui/newt/nmt-newt-form.c
@@ -0,0 +1,656 @@
+/* -*- 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_X,
+ PROP_Y,
+ PROP_WIDTH,
+ PROP_HEIGHT,
+ PROP_PADDING,
+ PROP_ESCAPE_EXITS,
+
+ LAST_PROP
+};
+
+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, either at construct time or later.
+ *
+ * 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 (priv->content);
+
+ 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;
+ newtPopWindow ();
+
+ nmt_newt_widget_unrealize (priv->content);
+}
+
+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_TIMER)
+ return;
+
+ if ( es.reason == NEWT_EXIT_HOTKEY
+ || es.reason == NEWT_EXIT_ERROR) {
+ g_clear_object (&priv->focus);
+ nmt_newt_form_quit (form);
+ return;
+ }
+
+ if (es.reason == NEWT_EXIT_COMPONENT) {
+ 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 {
+ focus = nmt_newt_widget_find_component (priv->content,
+ newtFormGetCurrent (priv->form));
+ if (focus)
+ nmt_newt_form_set_focus (form, focus);
+ }
+}
+
+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);
+ g_object_unref (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);
+ }
+}
+
+/**
+ * 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_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;
+
+ /**
+ * 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: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/tui/newt/nmt-newt-form.h b/tui/newt/nmt-newt-form.h
new file mode 100644
index 0000000000..e320d8723c
--- /dev/null
+++ b/tui/newt/nmt-newt-form.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_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;
+
+ 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/tui/newt/nmt-newt-grid.c b/tui/newt/nmt-newt-grid.c
new file mode 100644
index 0000000000..ac96733452
--- /dev/null
+++ b/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/tui/newt/nmt-newt-grid.h b/tui/newt/nmt-newt-grid.h
new file mode 100644
index 0000000000..f36a38ee8f
--- /dev/null
+++ b/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/tui/newt/nmt-newt-hacks.c b/tui/newt/nmt-newt-hacks.c
new file mode 100644
index 0000000000..2d9b1725ae
--- /dev/null
+++ b/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/tui/newt/nmt-newt-hacks.h b/tui/newt/nmt-newt-hacks.h
new file mode 100644
index 0000000000..ba8464af0d
--- /dev/null
+++ b/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/tui/newt/nmt-newt-label.c b/tui/newt/nmt-newt-label.c
new file mode 100644
index 0000000000..a9d44b04f1
--- /dev/null
+++ b/tui/newt/nmt-newt-label.c
@@ -0,0 +1,323 @@
+/* -*- 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/tui/newt/nmt-newt-label.h b/tui/newt/nmt-newt-label.h
new file mode 100644
index 0000000000..a4e0dcca5c
--- /dev/null
+++ b/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/tui/newt/nmt-newt-listbox.c b/tui/newt/nmt-newt-listbox.c
new file mode 100644
index 0000000000..2e3655aaa4
--- /dev/null
+++ b/tui/newt/nmt-newt-listbox.c
@@ -0,0 +1,539 @@
+/* -*- 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);
+ 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/tui/newt/nmt-newt-listbox.h b/tui/newt/nmt-newt-listbox.h
new file mode 100644
index 0000000000..da29207418
--- /dev/null
+++ b/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 active);
+
+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/tui/newt/nmt-newt-popup.c b/tui/newt/nmt-newt-popup.c
new file mode 100644
index 0000000000..e9757eac20
--- /dev/null
+++ b/tui/newt/nmt-newt-popup.c
@@ -0,0 +1,343 @@
+/* -*- 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/tui/newt/nmt-newt-popup.h b/tui/newt/nmt-newt-popup.h
new file mode 100644
index 0000000000..643ea2d8c4
--- /dev/null
+++ b/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/tui/newt/nmt-newt-section.c b/tui/newt/nmt-newt-section.c
new file mode 100644
index 0000000000..094b41b731
--- /dev/null
+++ b/tui/newt/nmt-newt-section.c
@@ -0,0 +1,408 @@
+/* -*- 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_clear_pointer (&closed_glyph, g_free);
+ g_clear_pointer (&open_glyph, g_free);
+ g_clear_pointer (&line_glyph, g_free);
+ g_clear_pointer (&end_glyph, g_free);
+
+ closed_glyph = g_strdup ("-");
+ open_glyph = g_strdup ("+");
+ line_glyph = g_strdup ("|");
+ end_glyph = g_strdup ("\\");
+ }
+}
diff --git a/tui/newt/nmt-newt-section.h b/tui/newt/nmt-newt-section.h
new file mode 100644
index 0000000000..a943ba2000
--- /dev/null
+++ b/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/tui/newt/nmt-newt-separator.c b/tui/newt/nmt-newt-separator.c
new file mode 100644
index 0000000000..07deb1af90
--- /dev/null
+++ b/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/tui/newt/nmt-newt-separator.h b/tui/newt/nmt-newt-separator.h
new file mode 100644
index 0000000000..3f4183d588
--- /dev/null
+++ b/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/tui/newt/nmt-newt-stack.c b/tui/newt/nmt-newt-stack.c
new file mode 100644
index 0000000000..1b31d58fe2
--- /dev/null
+++ b/tui/newt/nmt-newt-stack.c
@@ -0,0 +1,366 @@
+/* -*- 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/tui/newt/nmt-newt-stack.h b/tui/newt/nmt-newt-stack.h
new file mode 100644
index 0000000000..09afe4baf6
--- /dev/null
+++ b/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/tui/newt/nmt-newt-textbox.c b/tui/newt/nmt-newt-textbox.c
new file mode 100644
index 0000000000..888bde28f9
--- /dev/null
+++ b/tui/newt/nmt-newt-textbox.c
@@ -0,0 +1,292 @@
+/* -*- 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, len;
+
+ 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++) {
+ len = g_utf8_strlen (lines[i], -1);
+ if (len > priv->width)
+ priv->width = len;
+ }
+ 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/tui/newt/nmt-newt-textbox.h b/tui/newt/nmt-newt-textbox.h
new file mode 100644
index 0000000000..b3743aea05
--- /dev/null
+++ b/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/tui/newt/nmt-newt-toggle-button.c b/tui/newt/nmt-newt-toggle-button.c
new file mode 100644
index 0000000000..d435e459e5
--- /dev/null
+++ b/tui/newt/nmt-newt-toggle-button.c
@@ -0,0 +1,234 @@
+/* -*- 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/tui/newt/nmt-newt-toggle-button.h b/tui/newt/nmt-newt-toggle-button.h
new file mode 100644
index 0000000000..e54c6d3fa1
--- /dev/null
+++ b/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/tui/newt/nmt-newt-types.h b/tui/newt/nmt-newt-types.h
new file mode 100644
index 0000000000..c26ff900ef
--- /dev/null
+++ b/tui/newt/nmt-newt-types.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_TYPES_H
+#define NMT_NEWT_TYPES_H
+
+#include <glib-object.h>
+#include <newt.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/tui/newt/nmt-newt-utils.c b/tui/newt/nmt-newt-utils.c
new file mode 100644
index 0000000000..f2148d2b02
--- /dev/null
+++ b/tui/newt/nmt-newt-utils.c
@@ -0,0 +1,333 @@
+/* -*- 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 <glib/gi18n-lib.h>
+
+#include "nmt-newt-utils.h"
+
+static void
+nmt_newt_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 ();
+}
+
+/**
+ * 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.
+ *
+ * FIXME: Currently this also calls g_log_set_default_handler() to set
+ * up a log handler that displays g_warning()s and the like as pop-up
+ * windows, but in the long run that should only happen for
+ * debug/developer builds.
+ */
+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");
+
+ g_log_set_default_handler (nmt_newt_g_log_handler, 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 (nmt_newt_g_log_handler, g_log_default_handler);
+}
+
+/**
+ * nmt_newt_error_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".
+ *
+ * FIXME: it's not just for errors any more!
+ */
+void
+nmt_newt_error_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_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_error_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_error_dialog (_("Could not create temporary file: %s"), error->message);
+ g_error_free (error);
+ goto done;
+ }
+
+ if (!g_spawn_check_exit_status (status, &error)) {
+ nmt_newt_error_dialog (_("Editor failed: %s"), error->message);
+ g_error_free (error);
+ goto done;
+ }
+
+ if (!g_file_get_contents (filename, &new_data, NULL, &error)) {
+ nmt_newt_error_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/tui/newt/nmt-newt-utils.h b/tui/newt/nmt-newt-utils.h
new file mode 100644
index 0000000000..1823e3c2a7
--- /dev/null
+++ b/tui/newt/nmt-newt-utils.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_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);
+
+void nmt_newt_error_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/tui/newt/nmt-newt-widget.c b/tui/newt/nmt-newt-widget.c
new file mode 100644
index 0000000000..f7c8d20fe5
--- /dev/null
+++ b/tui/newt/nmt-newt-widget.c
@@ -0,0 +1,648 @@
+/* -*- 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_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_LAST,
+ 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_LAST,
+ 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/tui/newt/nmt-newt-widget.h b/tui/newt/nmt-newt-widget.h
new file mode 100644
index 0000000000..265f2455d1
--- /dev/null
+++ b/tui/newt/nmt-newt-widget.h
@@ -0,0 +1,117 @@
+/* -*- 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);
+
+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/tui/newt/nmt-newt.h b/tui/newt/nmt-newt.h
new file mode 100644
index 0000000000..6a9c8d9fb5
--- /dev/null
+++ b/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/tui/nm-editor-bindings.c b/tui/nm-editor-bindings.c
new file mode 100644
index 0000000000..949dcd7d2a
--- /dev/null
+++ b/tui/nm-editor-bindings.c
@@ -0,0 +1,1666 @@
+/* -*- 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 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)
+ *prefix = 32;
+
+ 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_INET, 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_INET, &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;
+ const 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;
+ const 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 = 0;
+
+ /* 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;
+ }
+}
+
+
+#define DBUS_TYPE_G_LIST_OF_STRING (dbus_g_type_get_collection ("GSList", G_TYPE_STRING))
+
+static void
+convert_slist_to_strv (const GValue *src_value, GValue *dest_value)
+{
+ GSList *slist;
+ char **strv;
+ int len, i = 0;
+
+ slist = g_value_get_boxed (src_value);
+ len = g_slist_length (slist);
+
+ strv = g_new (char *, len + 1);
+ for (i = 0; slist; slist = slist->next, i++)
+ strv[i] = g_strdup (slist->data);
+ strv[i] = NULL;
+
+ g_value_take_boxed (dest_value, strv);
+}
+
+void
+nm_editor_bindings_init (void)
+{
+ /* FIXME: libnm registers strv->list, but not list->strv */
+ g_value_register_transform_func (DBUS_TYPE_G_LIST_OF_STRING,
+ G_TYPE_STRV,
+ convert_slist_to_strv);
+}
diff --git a/tui/nm-editor-bindings.h b/tui/nm-editor-bindings.h
new file mode 100644
index 0000000000..a7a32dd142
--- /dev/null
+++ b/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/tui/nm-editor-utils.c b/tui/nm-editor-utils.c
new file mode 100644
index 0000000000..7989eac84b
--- /dev/null
+++ b/tui/nm-editor-utils.c
@@ -0,0 +1,420 @@
+/* -*- 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;
+
+ }
+ g_return_val_if_fail (type_data != NULL, 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/tui/nm-editor-utils.h b/tui/nm-editor-utils.h
new file mode 100644
index 0000000000..4b9ad5edbb
--- /dev/null
+++ b/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/tui/nm-gvaluearray-compat.h b/tui/nm-gvaluearray-compat.h
new file mode 100644
index 0000000000..017d336af5
--- /dev/null
+++ b/tui/nm-gvaluearray-compat.h
@@ -0,0 +1,101 @@
+/* -*- 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_GVALUEARRAY_COMPAT_H
+#define NM_GVALUEARRAY_COMPAT_H
+
+#include <glib.h>
+
+#define g_value_array_get_type() \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_get_type (); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_get_nth(value_array, index_) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_get_nth (value_array, index_); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_new(n_prealloced) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_new (n_prealloced); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_free(value_array) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_free (value_array); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_copy(value_array) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_copy (value_array); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_prepend(value_array, value) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_prepend (value_array, value); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_append(value_array, value) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_append (value_array, value); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_insert(value_array, index_, value) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_insert (value_array, index_, value); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_remove(value_array, index_) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_remove (value_array, index_); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_sort(value_array, compare_func) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_sort (value_array, compare_func); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#define g_value_array_sort_with_data(value_array, compare_func, user_data) \
+ G_GNUC_EXTENSION ({ \
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS \
+ g_value_array_sort_with_data (value_array, compare_func, user_data); \
+ G_GNUC_END_IGNORE_DEPRECATIONS \
+ })
+
+#endif /* NM_GVALUEARRAY_COMPAT_H */
diff --git a/tui/nm-ui-utils.c b/tui/nm-ui-utils.c
new file mode 100644
index 0000000000..3609c91ab0
--- /dev/null
+++ b/tui/nm-ui-utils.c
@@ -0,0 +1,587 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager Applet -- allow user control over networking
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nm-ui-utils
+ * @short_description: Applet/Connection editor utilities
+ *
+ * This is stolen directly from libnm-gtk and should probably
+ * eventually migrate into libnm-glib. FIXME.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib/gi18n.h>
+#include <gudev/gudev.h>
+
+#include <nm-device.h>
+
+#include "nm-ui-utils.h"
+
+static char *ignored_words[] = {
+ "Semiconductor",
+ "Components",
+ "Corporation",
+ "Communications",
+ "Company",
+ "Corp.",
+ "Corp",
+ "Co.",
+ "Inc.",
+ "Inc",
+ "Incorporated",
+ "Ltd.",
+ "Limited.",
+ "Intel?",
+ "chipset",
+ "adapter",
+ "[hex]",
+ "NDIS",
+ "Module",
+ NULL
+};
+
+static char *ignored_phrases[] = {
+ "Multiprotocol MAC/baseband processor",
+ "Wireless LAN Controller",
+ "Wireless LAN Adapter",
+ "Wireless Adapter",
+ "Network Connection",
+ "Wireless Cardbus Adapter",
+ "Wireless CardBus Adapter",
+ "54 Mbps Wireless PC Card",
+ "Wireless PC Card",
+ "Wireless PC",
+ "PC Card with XJACK(r) Antenna",
+ "Wireless cardbus",
+ "Wireless LAN PC Card",
+ "Technology Group Ltd.",
+ "Communication S.p.A.",
+ "Business Mobile Networks BV",
+ "Mobile Broadband Minicard Composite Device",
+ "Mobile Communications AB",
+ "(PC-Suite Mode)",
+ NULL
+};
+
+static char *
+fixup_desc_string (const char *desc)
+{
+ char *p, *temp;
+ char **words, **item;
+ GString *str;
+
+ p = temp = g_strdup (desc);
+ while (*p) {
+ if (*p == '_' || *p == ',')
+ *p = ' ';
+ p++;
+ }
+
+ /* Attempt to shorten ID by ignoring certain phrases */
+ for (item = ignored_phrases; *item; item++) {
+ guint32 ignored_len = strlen (*item);
+
+ p = strstr (temp, *item);
+ if (p)
+ memmove (p, p + ignored_len, strlen (p + ignored_len) + 1); /* +1 for the \0 */
+ }
+
+ /* Attmept to shorten ID by ignoring certain individual words */
+ words = g_strsplit (temp, " ", 0);
+ str = g_string_new_len (NULL, strlen (temp));
+ g_free (temp);
+
+ for (item = words; *item; item++) {
+ int i = 0;
+ gboolean ignore = FALSE;
+
+ if (g_ascii_isspace (**item) || (**item == '\0'))
+ continue;
+
+ while (ignored_words[i] && !ignore) {
+ if (!strcmp (*item, ignored_words[i]))
+ ignore = TRUE;
+ i++;
+ }
+
+ if (!ignore) {
+ if (str->len)
+ g_string_append_c (str, ' ');
+ g_string_append (str, *item);
+ }
+ }
+ g_strfreev (words);
+
+ temp = str->str;
+ g_string_free (str, FALSE);
+
+ return temp;
+}
+
+#define VENDOR_TAG "nma_utils_get_device_vendor"
+#define PRODUCT_TAG "nma_utils_get_device_product"
+#define DESCRIPTION_TAG "nma_utils_get_device_description"
+
+static void
+get_description (NMDevice *device)
+{
+ char *description = NULL;
+ const char *dev_product;
+ const char *dev_vendor;
+ char *product, *pdown;
+ char *vendor, *vdown;
+ GString *str;
+
+ dev_product = nm_device_get_product (device);
+ dev_vendor = nm_device_get_vendor (device);
+ if (!dev_product || !dev_vendor) {
+ g_object_set_data (G_OBJECT (device),
+ DESCRIPTION_TAG,
+ (char *) nm_device_get_iface (device));
+ return;
+ }
+
+ product = fixup_desc_string (dev_product);
+ vendor = fixup_desc_string (dev_vendor);
+
+ str = g_string_new_len (NULL, strlen (vendor) + strlen (product) + 1);
+
+ /* Another quick hack; if all of the fixed up vendor string
+ * is found in product, ignore the vendor.
+ */
+ pdown = g_ascii_strdown (product, -1);
+ vdown = g_ascii_strdown (vendor, -1);
+ if (!strstr (pdown, vdown)) {
+ g_string_append (str, vendor);
+ g_string_append_c (str, ' ');
+ }
+ g_free (pdown);
+ g_free (vdown);
+
+ g_string_append (str, product);
+
+ description = g_string_free (str, FALSE);
+
+ g_object_set_data_full (G_OBJECT (device),
+ VENDOR_TAG, vendor,
+ (GDestroyNotify) g_free);
+ g_object_set_data_full (G_OBJECT (device),
+ PRODUCT_TAG, product,
+ (GDestroyNotify) g_free);
+ g_object_set_data_full (G_OBJECT (device),
+ DESCRIPTION_TAG, description,
+ (GDestroyNotify) g_free);
+}
+
+/**
+ * nma_utils_get_device_vendor:
+ * @device: an #NMDevice
+ *
+ * Gets a cleaned-up version of #NMDevice:vendor for @device. This
+ * removes strings like "Inc." that would just take up unnecessary
+ * space in the UI.
+ *
+ * Returns: a cleaned-up vendor string, or %NULL if the vendor is
+ * not known
+ */
+const char *
+nma_utils_get_device_vendor (NMDevice *device)
+{
+ const char *vendor;
+
+ g_return_val_if_fail (device != NULL, NULL);
+
+ vendor = g_object_get_data (G_OBJECT (device), VENDOR_TAG);
+ if (!vendor) {
+ get_description (device);
+ vendor = g_object_get_data (G_OBJECT (device), VENDOR_TAG);
+ }
+
+ return vendor;
+}
+
+/**
+ * nma_utils_get_device_product:
+ * @device: an #NMDevice
+ *
+ * Gets a cleaned-up version of #NMDevice:product for @device. This
+ * removes strings like "Wireless LAN Adapter" that would just take up
+ * unnecessary space in the UI.
+ *
+ * Returns: a cleaned-up product string, or %NULL if the product name
+ * is not known
+ */
+const char *
+nma_utils_get_device_product (NMDevice *device)
+{
+ const char *product;
+
+ g_return_val_if_fail (device != NULL, NULL);
+
+ product = g_object_get_data (G_OBJECT (device), PRODUCT_TAG);
+ if (!product) {
+ get_description (device);
+ product = g_object_get_data (G_OBJECT (device), PRODUCT_TAG);
+ }
+
+ return product;
+}
+
+/**
+ * nma_utils_get_device_description:
+ * @device: an #NMDevice
+ *
+ * Gets a description of @device, incorporating the results of
+ * nma_utils_get_device_vendor() and
+ * nma_utils_get_device_product().
+ *
+ * Returns: a description of @device. If either the vendor or the
+ * product name is unknown, this returns the interface name.
+ */
+const char *
+nma_utils_get_device_description (NMDevice *device)
+{
+ const char *description;
+
+ g_return_val_if_fail (device != NULL, NULL);
+
+ description = g_object_get_data (G_OBJECT (device), DESCRIPTION_TAG);
+ if (!description) {
+ get_description (device);
+ description = g_object_get_data (G_OBJECT (device), DESCRIPTION_TAG);
+ }
+
+ return description;
+}
+
+static gboolean
+find_duplicates (char **names,
+ gboolean *duplicates,
+ int num_devices)
+{
+ int i, j;
+ gboolean found_any = FALSE;
+
+ memset (duplicates, 0, num_devices * sizeof (gboolean));
+ for (i = 0; i < num_devices; i++) {
+ if (duplicates[i])
+ continue;
+ for (j = i + 1; j < num_devices; j++) {
+ if (duplicates[j])
+ continue;
+ if (!strcmp (names[i], names[j]))
+ duplicates[i] = duplicates[j] = found_any = TRUE;
+ }
+ }
+
+ return found_any;
+}
+
+/**
+ * nma_utils_get_device_generic_type_name:
+ * @device: an #NMDevice
+ *
+ * Gets a "generic" name for the type of @device.
+ *
+ * Returns: @device's generic type name
+ */
+const char *
+nma_utils_get_device_generic_type_name (NMDevice *device)
+{
+ switch (nm_device_get_device_type (device)) {
+ case NM_DEVICE_TYPE_ETHERNET:
+ case NM_DEVICE_TYPE_INFINIBAND:
+ return _("Wired");
+ default:
+ return nma_utils_get_device_type_name (device);
+ }
+}
+
+/**
+ * nma_utils_get_device_type_name:
+ * @device: an #NMDevice
+ *
+ * Gets a specific name for the type of @device.
+ *
+ * Returns: @device's generic type name
+ */
+const char *
+nma_utils_get_device_type_name (NMDevice *device)
+{
+ switch (nm_device_get_device_type (device)) {
+ case NM_DEVICE_TYPE_ETHERNET:
+ return _("Ethernet");
+ case NM_DEVICE_TYPE_WIFI:
+ return _("Wi-Fi");
+ case NM_DEVICE_TYPE_BT:
+ return _("Bluetooth");
+ case NM_DEVICE_TYPE_OLPC_MESH:
+ return _("OLPC Mesh");
+ case NM_DEVICE_TYPE_WIMAX:
+ return _("WiMAX");
+ case NM_DEVICE_TYPE_MODEM:
+ return _("Mobile Broadband");
+ case NM_DEVICE_TYPE_INFINIBAND:
+ return _("InfiniBand");
+ case NM_DEVICE_TYPE_BOND:
+ return _("Bond");
+ case NM_DEVICE_TYPE_TEAM:
+ return _("Team");
+ case NM_DEVICE_TYPE_BRIDGE:
+ return _("Bridge");
+ case NM_DEVICE_TYPE_VLAN:
+ return _("VLAN");
+ case NM_DEVICE_TYPE_ADSL:
+ return _("ADSL");
+ default:
+ return _("Unknown");
+ }
+}
+
+static char *
+get_device_type_name_with_iface (NMDevice *device)
+{
+ const char *type_name = nma_utils_get_device_type_name (device);
+
+ switch (nm_device_get_device_type (device)) {
+ case NM_DEVICE_TYPE_BOND:
+ case NM_DEVICE_TYPE_TEAM:
+ case NM_DEVICE_TYPE_BRIDGE:
+ case NM_DEVICE_TYPE_VLAN:
+ return g_strdup_printf ("%s (%s)", type_name, nm_device_get_iface (device));
+ default:
+ return g_strdup (type_name);
+ }
+}
+
+static char *
+get_device_generic_type_name_with_iface (NMDevice *device)
+{
+ switch (nm_device_get_device_type (device)) {
+ case NM_DEVICE_TYPE_ETHERNET:
+ case NM_DEVICE_TYPE_INFINIBAND:
+ return g_strdup (_("Wired"));
+ default:
+ return get_device_type_name_with_iface (device);
+ }
+}
+
+#define BUS_TAG "nm-ui-utils.c:get_bus_name"
+
+static const char *
+get_bus_name (GUdevClient *uclient, NMDevice *device)
+{
+ GUdevDevice *udevice;
+ const char *ifname, *bus;
+ char *display_bus;
+
+ bus = g_object_get_data (G_OBJECT (device), BUS_TAG);
+ if (bus) {
+ if (*bus)
+ return bus;
+ else
+ return NULL;
+ }
+
+ ifname = nm_device_get_iface (device);
+ if (!ifname)
+ return NULL;
+
+ udevice = g_udev_client_query_by_subsystem_and_name (uclient, "net", ifname);
+ if (!udevice)
+ udevice = g_udev_client_query_by_subsystem_and_name (uclient, "tty", ifname);
+ if (!udevice)
+ return NULL;
+
+ bus = g_udev_device_get_property (udevice, "ID_BUS");
+ if (!g_strcmp0 (bus, "pci"))
+ display_bus = g_strdup (_("PCI"));
+ else if (!g_strcmp0 (bus, "usb"))
+ display_bus = g_strdup (_("USB"));
+ else {
+ /* Use "" instead of NULL so we can tell later that we've
+ * already tried.
+ */
+ display_bus = g_strdup ("");
+ }
+
+ g_object_set_data_full (G_OBJECT (device),
+ BUS_TAG, display_bus,
+ (GDestroyNotify) g_free);
+ if (*display_bus)
+ return display_bus;
+ else
+ return NULL;
+}
+
+/**
+ * nma_utils_disambiguate_device_names:
+ * @devices: (array length=num_devices): a set of #NMDevice
+ * @num_devices: length of @devices
+ *
+ * Generates a list of short-ish unique presentation names for the
+ * devices in @devices.
+ *
+ * Returns: (transfer full) (array zero-terminated=1): the device names
+ */
+char **
+nma_utils_disambiguate_device_names (NMDevice **devices,
+ int num_devices)
+{
+ static const char *subsys[3] = { "net", "tty", NULL };
+ GUdevClient *uclient;
+ char **names;
+ gboolean *duplicates;
+ int i;
+
+ names = g_new (char *, num_devices + 1);
+ duplicates = g_new (gboolean, num_devices);
+
+ /* Generic device name */
+ for (i = 0; i < num_devices; i++)
+ names[i] = get_device_generic_type_name_with_iface (devices[i]);
+ if (!find_duplicates (names, duplicates, num_devices))
+ goto done;
+
+ /* Try specific names (eg, "Ethernet" and "InfiniBand" rather
+ * than "Wired")
+ */
+ for (i = 0; i < num_devices; i++) {
+ if (duplicates[i]) {
+ g_free (names[i]);
+ names[i] = get_device_type_name_with_iface (devices[i]);
+ }
+ }
+ if (!find_duplicates (names, duplicates, num_devices))
+ goto done;
+
+ /* Try prefixing bus name (eg, "PCI Ethernet" vs "USB Ethernet") */
+ uclient = g_udev_client_new (subsys);
+ for (i = 0; i < num_devices; i++) {
+ if (duplicates[i]) {
+ const char *bus = get_bus_name (uclient, devices[i]);
+ char *name;
+
+ if (!bus)
+ continue;
+
+ g_free (names[i]);
+ name = get_device_type_name_with_iface (devices[i]);
+ /* Translators: the first %s is a bus name (eg, "USB") or
+ * product name, the second is a device type (eg,
+ * "Ethernet"). You can change this to something like
+ * "%2$s (%1$s)" if there's no grammatical way to combine
+ * the strings otherwise.
+ */
+ names[i] = g_strdup_printf (C_("long device name", "%s %s"),
+ bus, name);
+ g_free (name);
+ }
+ }
+ g_object_unref (uclient);
+ if (!find_duplicates (names, duplicates, num_devices))
+ goto done;
+
+ /* Try prefixing vendor name */
+ for (i = 0; i < num_devices; i++) {
+ if (duplicates[i]) {
+ const char *vendor = nma_utils_get_device_vendor (devices[i]);
+ char *name;
+
+ if (!vendor)
+ continue;
+
+ g_free (names[i]);
+ name = get_device_type_name_with_iface (devices[i]);
+ names[i] = g_strdup_printf (C_("long device name", "%s %s"),
+ vendor,
+ nma_utils_get_device_type_name (devices[i]));
+ g_free (name);
+ }
+ }
+ if (!find_duplicates (names, duplicates, num_devices))
+ goto done;
+
+ /* We have multiple identical network cards, so we have to differentiate
+ * them by interface name.
+ */
+ for (i = 0; i < num_devices; i++) {
+ if (duplicates[i]) {
+ const char *interface = nm_device_get_iface (devices[i]);
+
+ if (!interface)
+ continue;
+
+ g_free (names[i]);
+ names[i] = g_strdup_printf ("%s (%s)",
+ nma_utils_get_device_type_name (devices[i]),
+ interface);
+ }
+ }
+
+ done:
+ g_free (duplicates);
+ names[num_devices] = NULL;
+ return names;
+}
+
+/**
+ * nma_utils_get_connection_device_name:
+ * @connection: an #NMConnection for a virtual device type
+ *
+ * Returns the name that nma_utils_disambiguate_device_names() would
+ * return for the virtual device that would be created for @connection.
+ * Eg, "VLAN (eth1.1)".
+ *
+ * Returns: (transfer full): the name of @connection's device
+ */
+char *
+nma_utils_get_connection_device_name (NMConnection *connection)
+{
+ const char *iface, *type, *display_type;
+ NMSettingConnection *s_con;
+
+ iface = nm_connection_get_virtual_iface_name (connection);
+ g_return_val_if_fail (iface != NULL, NULL);
+
+ s_con = nm_connection_get_setting_connection (connection);
+ g_return_val_if_fail (s_con != NULL, NULL);
+ type = nm_setting_connection_get_connection_type (s_con);
+
+ if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME))
+ display_type = _("Bond");
+ else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME))
+ display_type = _("Team");
+ else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME))
+ display_type = _("Bridge");
+ else if (!strcmp (type, NM_SETTING_VLAN_SETTING_NAME))
+ display_type = _("VLAN");
+ else {
+ g_warning ("Unrecognized virtual device type '%s'", type);
+ display_type = type;
+ }
+
+ return g_strdup_printf ("%s (%s)", display_type, iface);
+}
diff --git a/tui/nm-ui-utils.h b/tui/nm-ui-utils.h
new file mode 100644
index 0000000000..693df441c8
--- /dev/null
+++ b/tui/nm-ui-utils.h
@@ -0,0 +1,41 @@
+/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2007 - 2012 Red Hat, Inc.
+ */
+
+
+/* WARNING: this file is private API between nm-applet and various GNOME
+ * bits; it may change without notice and is not guaranteed to be stable.
+ */
+
+#ifndef NMA_UI_UTILS_H
+#define NMA_UI_UTILS_H
+
+#include <nm-device.h>
+
+const char *nma_utils_get_device_vendor (NMDevice *device);
+const char *nma_utils_get_device_product (NMDevice *device);
+const char *nma_utils_get_device_description (NMDevice *device);
+const char *nma_utils_get_device_generic_type_name (NMDevice *device);
+const char *nma_utils_get_device_type_name (NMDevice *device);
+
+char **nma_utils_disambiguate_device_names (NMDevice **devices,
+ int num_devices);
+char *nma_utils_get_connection_device_name (NMConnection *connection);
+
+#endif /* NMA_UI_UTILS_H */
+
diff --git a/tui/nmt-address-list.c b/tui/nmt-address-list.c
new file mode 100644
index 0000000000..da301a2fda
--- /dev/null
+++ b/tui/nmt-address-list.c
@@ -0,0 +1,284 @@
+/* -*- 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/tui/nmt-address-list.h b/tui/nmt-address-list.h
new file mode 100644
index 0000000000..df7a4f7962
--- /dev/null
+++ b/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/tui/nmt-connect-connection-list.c b/tui/nmt-connect-connection-list.c
new file mode 100644
index 0000000000..12d16da54e
--- /dev/null
+++ b/tui/nmt-connect-connection-list.c
@@ -0,0 +1,820 @@
+/* -*- 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"
+#include "nmt-utils.h"
+#include "nmt-password-dialog.h"
+#include "nmt-secret-agent.h"
+#include "nm-ui-utils.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;
+} NmtConnectConnection;
+
+typedef struct {
+ GSList *nmt_devices;
+} NmtConnectConnectionListPrivate;
+
+/**
+ * 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_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);
+ }
+ }
+}
+
+static void
+add_connections_for_aps (NmtConnectDevice *nmtdev,
+ GSList *connections)
+{
+ NmtConnectConnection *nmtconn;
+ NMConnection *conn;
+ NMAccessPoint *ap;
+ const GPtrArray *aps;
+ GSList *iter;
+ int i;
+
+ aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (nmtdev->device));
+ if (!aps)
+ return;
+
+ for (i = 0; i < aps->len; i++) {
+ ap = aps->pdata[i];
+
+ if (!nm_access_point_get_ssid (ap))
+ continue;
+
+ 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);
+ }
+}
+
+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;
+ 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 = nma_utils_get_connection_device_name (conn);
+ nmtdev = g_hash_table_lookup (devices_by_name, name);
+ if (nmtdev)
+ g_free (name);
+ else {
+ nmtdev = g_slice_new0 (NmtConnectDevice);
+ nmtdev->name = name;
+ 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 gboolean
+connection_is_active (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 (!strcmp (path, ac_path))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+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 = nma_utils_disambiguate_device_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, g_utf8_strlen (nmtconn->name, -1));
+ }
+ }
+
+ 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 && connection_is_active (nmtconn->conn, acs))
+ active_col = '*';
+ else
+ active_col = ' ';
+
+ if (nmtconn->ap) {
+ guint8 strength = nm_access_point_get_strength (nmtconn->ap);
+
+ if (strength > 80)
+ strength_col = " ▂▄▆█";
+ else if (strength > 55)
+ strength_col = " ▂▄▆_";
+ else if (strength > 30)
+ strength_col = " ▂▄__";
+ else if (strength > 5)
+ strength_col = " ▂___";
+ else
+ strength_col = " ____";
+ } else
+ strength_col = "";
+
+ row = g_strdup_printf ("%c %s%-*s%s",
+ active_col,
+ nmtconn->name,
+ (int)(max_width - g_utf8_strlen (nmtconn->name, -1)), "",
+ strength_col);
+
+ nmt_newt_listbox_append (listbox, row, nmtconn);
+ g_free (row);
+ }
+ }
+
+ priv->nmt_devices = nmt_devices;
+}
+
+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);
+}
+
+typedef struct {
+ NmtNewtForm *form;
+ NMSecretAgent *agent;
+} NmtActivationData;
+
+static void
+nmt_activation_data_free (NmtActivationData *data)
+{
+ g_object_unref (data->form);
+ g_object_unref (data->agent);
+ g_slice_free (NmtActivationData, data);
+}
+
+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 gboolean
+idle_unref_ac (gpointer ac)
+{
+ g_object_unref (ac);
+ return FALSE;
+}
+
+static void
+activation_complete (GSimpleAsyncResult *simple,
+ GError *error)
+{
+ NmtActivationData *data = g_object_get_data (G_OBJECT (simple), "NmtActivationData");
+
+ if (error)
+ g_simple_async_result_set_from_error (simple, error);
+ else
+ g_simple_async_result_set_op_res_gboolean (simple, TRUE);
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+
+ nmt_newt_form_quit (data->form);
+ /* If we bail out too soon, the agent won't have completed registering,
+ * and nm_secret_agent_unregister() would complain.
+ */
+ if (nm_secret_agent_get_registered (data->agent))
+ nm_secret_agent_unregister (data->agent);
+}
+
+static void
+activate_ac_state_changed (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = 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"));
+ }
+
+ g_signal_handlers_disconnect_by_func (object, G_CALLBACK (activate_ac_state_changed), simple);
+
+ /* Work around NMObject bug for now: fix is 1981323b */
+ g_idle_add (idle_unref_ac, object);
+
+ activation_complete (simple, error);
+ g_clear_error (&error);
+}
+
+static void
+activation_callback (NMClient *client,
+ NMActiveConnection *ac,
+ GError *error,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple = user_data;
+
+ if (error || nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
+ activation_complete (simple, error);
+ return;
+ }
+
+ g_object_ref (ac);
+ g_signal_connect (ac, "notify::" NM_ACTIVE_CONNECTION_STATE,
+ G_CALLBACK (activate_ac_state_changed), simple);
+}
+
+static void
+activate_nmt_connection_async (NmtConnectConnectionList *list,
+ NmtConnectConnection *nmtconn,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ NmtActivationData *data;
+ NmtNewtWidget *label;
+ GSimpleAsyncResult *simple;
+
+ data = g_slice_new (NmtActivationData);
+
+ data->form = g_object_new (NMT_TYPE_NEWT_FORM, NULL);
+ label = nmt_newt_label_new (_("Connecting..."));
+ nmt_newt_form_set_content (data->form, label);
+
+ data->agent = nmt_secret_agent_new ();
+ nm_secret_agent_register (data->agent);
+ g_signal_connect (data->agent, "request-secrets",
+ G_CALLBACK (secrets_requested), NULL);
+
+ simple = g_simple_async_result_new (G_OBJECT (list), callback, user_data,
+ activate_nmt_connection_async);
+ g_object_set_data_full (G_OBJECT (simple), "NmtActivationData", data,
+ (GDestroyNotify)nmt_activation_data_free);
+
+ /* FIXME: cancel button */
+ nm_client_activate_connection (nm_client,
+ nmtconn->conn, nmtconn->device,
+ nmtconn->ap ? nm_object_get_path (NM_OBJECT (nmtconn->ap)) : NULL,
+ activation_callback, simple);
+ nmt_newt_form_show (data->form);
+}
+
+static gboolean
+activate_nmt_connection_finish (NmtConnectConnectionList *list,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
+
+static void
+activate_complete (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ NmtSyncOp *op = user_data;
+ GError *error = NULL;
+
+ if (activate_nmt_connection_finish (NMT_CONNECT_CONNECTION_LIST (object), result, &error))
+ nmt_sync_op_complete_boolean (op, TRUE, NULL);
+ else
+ nmt_sync_op_complete_boolean (op, FALSE, error);
+ g_clear_error (&error);
+}
+
+static void
+nmt_connect_connection_list_activated (NmtNewtWidget *widget)
+{
+ NmtConnectConnection *nmtconn;
+ NmtSyncOp op;
+ GError *error = NULL;
+
+ nmtconn = nmt_newt_listbox_get_active_key (NMT_NEWT_LISTBOX (widget));
+ g_return_if_fail (nmtconn != NULL);
+
+ nmt_sync_op_init (&op);
+ activate_nmt_connection_async (NMT_CONNECT_CONNECTION_LIST (widget), nmtconn,
+ activate_complete, &op);
+ if (!nmt_sync_op_wait_boolean (&op, &error)) {
+ nmt_newt_error_dialog (_("Could not activate connection: %s"), error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+nmt_connect_connection_list_class_init (NmtConnectConnectionListClass *list_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+ NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (list_class);
+
+ 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;
+
+ widget_class->activated = nmt_connect_connection_list_activated;
+}
+
+/**
+ * nmt_connect_connection_list_activate_async:
+ * @list: an #NmtConnectConnectionList
+ * @identifier: a connection ID or UUID
+ * @callback: #GAsyncReadyCallback
+ * @user_data: data for @callback
+ *
+ * Asynchronously begins to activate the connection identified by
+ * @identifier, and invokes @callback when it completes.
+ */
+void
+nmt_connect_connection_list_activate_async (NmtConnectConnectionList *list,
+ const char *identifier,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (list);
+ GSList *diter, *citer;
+ NmtConnectDevice *nmtdev;
+ NmtConnectConnection *nmtconn = NULL;
+ NMConnection *conn = NULL;
+
+ if (nm_utils_is_uuid (identifier))
+ conn = NM_CONNECTION (nm_remote_settings_get_connection_by_uuid (nm_settings, identifier));
+ else {
+ GSList *conns, *iter;
+
+ conns = nm_remote_settings_list_connections (nm_settings);
+ for (iter = conns; iter; iter = iter->next) {
+ NMConnection *candidate = iter->data;
+
+ if (!strcmp (identifier, nm_connection_get_id (candidate))) {
+ conn = candidate;
+ break;
+ }
+ }
+ g_slist_free (conns);
+ }
+
+ 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 activate;
+ else
+ continue;
+ }
+
+ if (nmtconn->ssid && !strcmp (identifier, nmtconn->ssid))
+ goto activate;
+ }
+
+ if (!conn && !nmtdev->device && !strcmp (identifier, nm_device_get_ip_iface (nmtdev->device))) {
+ nmtconn = nmtdev->conns->data;
+ goto activate;
+ }
+ }
+
+ g_printerr ("%s: no such connection '%s'\n", g_get_prgname (), identifier);
+ exit (1);
+
+ activate:
+ activate_nmt_connection_async (list, nmtconn, callback, user_data);
+}
+
+/**
+ * nmt_connect_connection_list_activate_finish:
+ * @list: an #NmtConnectConnectionList
+ * @result: the #GAsyncResult passed to the callback
+ * @error: return location for a #GError
+ *
+ * Gets the result of an nmt_connect_connection_list_activate_async() call.
+ *
+ * Returns: success or failure
+ */
+gboolean
+nmt_connect_connection_list_activate_finish (NmtConnectConnectionList *list,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error);
+}
diff --git a/tui/nmt-connect-connection-list.h b/tui/nmt-connect-connection-list.h
new file mode 100644
index 0000000000..14e94ec528
--- /dev/null
+++ b/tui/nmt-connect-connection-list.h
@@ -0,0 +1,58 @@
+/* -*- 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);
+
+void nmt_connect_connection_list_activate_async (NmtConnectConnectionList *list,
+ const char *identifier,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean nmt_connect_connection_list_activate_finish (NmtConnectConnectionList *list,
+ GAsyncResult *result,
+ GError **error);
+
+
+G_END_DECLS
+
+#endif /* NMT_CONNECT_CONNECTION_LIST_H */
diff --git a/tui/nmt-device-entry.c b/tui/nmt-device-entry.c
new file mode 100644
index 0000000000..3a418fd13a
--- /dev/null
+++ b/tui/nmt-device-entry.c
@@ -0,0 +1,589 @@
+/* -*- 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)
+ 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/tui/nmt-device-entry.h b/tui/nmt-device-entry.h
new file mode 100644
index 0000000000..2b959aaa85
--- /dev/null
+++ b/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/tui/nmt-edit-connection-list.c b/tui/nmt-edit-connection-list.c
new file mode 100644
index 0000000000..eeba78ddad
--- /dev/null
+++ b/tui/nmt-edit-connection-list.c
@@ -0,0 +1,550 @@
+/* -*- 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);
+ NmtNewtListbox *listbox;
+ GSList *iter, *next;
+ gboolean did_header = FALSE, did_vpn = FALSE;
+ NMEditorConnectionTypeData **types;
+ NMConnection *conn;
+ int i;
+
+ 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);
+
+ listbox = NMT_NEWT_LISTBOX (priv->listbox);
+ nmt_newt_listbox_clear (listbox);
+
+ if (!priv->grouped) {
+ /* Just add the connections in order */
+ for (iter = priv->connections; iter; iter = iter->next) {
+ conn = iter->data;
+ nmt_newt_listbox_append (listbox, nm_connection_get_id (conn), conn);
+ }
+ return;
+ }
+
+ types = nm_editor_utils_get_connection_type_list ();
+ for (i = 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 (listbox, types[i]->name, NULL);
+ did_header = TRUE;
+ }
+
+ indented = g_strdup_printf (" %s", nm_connection_get_id (conn));
+ nmt_newt_listbox_append (listbox, indented, conn);
+ g_free (indented);
+ }
+ }
+}
+
+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_LAST,
+ 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_LAST,
+ 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_LAST,
+ 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/tui/nmt-edit-connection-list.h b/tui/nmt-edit-connection-list.h
new file mode 100644
index 0000000000..35157e94dc
--- /dev/null
+++ b/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/tui/nmt-editor-page.c b/tui/nmt-editor-page.c
new file mode 100644
index 0000000000..549a86a6f5
--- /dev/null
+++ b/tui/nmt-editor-page.c
@@ -0,0 +1,227 @@
+/* -*- 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/tui/nmt-editor-page.h b/tui/nmt-editor-page.h
new file mode 100644
index 0000000000..d9a97102ee
--- /dev/null
+++ b/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/tui/nmt-editor.c b/tui/nmt-editor.c
new file mode 100644
index 0000000000..7aa2276ab7
--- /dev/null
+++ b/tui/nmt-editor.c
@@ -0,0 +1,330 @@
+/* -*- 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_error_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_error_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", 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_error_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_error_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_error_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)
+ 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/tui/nmt-editor.h b/tui/nmt-editor.h
new file mode 100644
index 0000000000..a991a7666a
--- /dev/null
+++ b/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/tui/nmt-ip-entry.c b/tui/nmt-ip-entry.c
new file mode 100644
index 0000000000..85a29fcce5
--- /dev/null
+++ b/tui/nmt-ip-entry.c
@@ -0,0 +1,265 @@
+/* -*- 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/tui/nmt-ip-entry.h b/tui/nmt-ip-entry.h
new file mode 100644
index 0000000000..ceb355efcf
--- /dev/null
+++ b/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/tui/nmt-mac-entry.c b/tui/nmt-mac-entry.c
new file mode 100644
index 0000000000..d76c097f20
--- /dev/null
+++ b/tui/nmt-mac-entry.c
@@ -0,0 +1,217 @@
+/* -*- 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 <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);
+
+ g_object_set (G_OBJECT (entry), "text", fixed, NULL);
+ return TRUE;
+ }
+
+ 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_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->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/tui/nmt-mac-entry.h b/tui/nmt-mac-entry.h
new file mode 100644
index 0000000000..33a3232986
--- /dev/null
+++ b/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/tui/nmt-mtu-entry.c b/tui/nmt-mtu-entry.c
new file mode 100644
index 0000000000..da3746ccbb
--- /dev/null
+++ b/tui/nmt-mtu-entry.c
@@ -0,0 +1,191 @@
+/* -*- 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/tui/nmt-mtu-entry.h b/tui/nmt-mtu-entry.h
new file mode 100644
index 0000000000..dfe416d0e7
--- /dev/null
+++ b/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/tui/nmt-page-bond.c b/tui/nmt-page-bond.c
new file mode 100644
index 0000000000..55a6ffeebb
--- /dev/null
+++ b/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/tui/nmt-page-bond.h b/tui/nmt-page-bond.h
new file mode 100644
index 0000000000..c8e69dd5ee
--- /dev/null
+++ b/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/tui/nmt-page-bridge-port.c b/tui/nmt-page-bridge-port.c
new file mode 100644
index 0000000000..ab7a78887f
--- /dev/null
+++ b/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/tui/nmt-page-bridge-port.h b/tui/nmt-page-bridge-port.h
new file mode 100644
index 0000000000..7fe3a92c43
--- /dev/null
+++ b/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/tui/nmt-page-bridge.c b/tui/nmt-page-bridge.c
new file mode 100644
index 0000000000..713d83507e
--- /dev/null
+++ b/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/tui/nmt-page-bridge.h b/tui/nmt-page-bridge.h
new file mode 100644
index 0000000000..b11d350291
--- /dev/null
+++ b/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/tui/nmt-page-device.c b/tui/nmt-page-device.c
new file mode 100644
index 0000000000..8ab0747e59
--- /dev/null
+++ b/tui/nmt-page-device.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-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/tui/nmt-page-device.h b/tui/nmt-page-device.h
new file mode 100644
index 0000000000..8c90a67a08
--- /dev/null
+++ b/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/tui/nmt-page-ethernet.c b/tui/nmt-page-ethernet.c
new file mode 100644
index 0000000000..9eb08fe5b3
--- /dev/null
+++ b/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/tui/nmt-page-ethernet.h b/tui/nmt-page-ethernet.h
new file mode 100644
index 0000000000..5e001562f7
--- /dev/null
+++ b/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/tui/nmt-page-grid.c b/tui/nmt-page-grid.c
new file mode 100644
index 0000000000..89ba9330f4
--- /dev/null
+++ b/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/tui/nmt-page-grid.h b/tui/nmt-page-grid.h
new file mode 100644
index 0000000000..40ff96c410
--- /dev/null
+++ b/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/tui/nmt-page-infiniband.c b/tui/nmt-page-infiniband.c
new file mode 100644
index 0000000000..c1238ab678
--- /dev/null
+++ b/tui/nmt-page-infiniband.c
@@ -0,0 +1,101 @@
+/* -*- 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);
+ }
+
+ 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/tui/nmt-page-infiniband.h b/tui/nmt-page-infiniband.h
new file mode 100644
index 0000000000..706b7b60c3
--- /dev/null
+++ b/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/tui/nmt-page-ip4.c b/tui/nmt-page-ip4.c
new file mode 100644
index 0000000000..210387c511
--- /dev/null
+++ b/tui/nmt-page-ip4.c
@@ -0,0 +1,199 @@
+/* -*- 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 ();
+ 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);
+ 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/tui/nmt-page-ip4.h b/tui/nmt-page-ip4.h
new file mode 100644
index 0000000000..f3bb933e0e
--- /dev/null
+++ b/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/tui/nmt-page-ip6.c b/tui/nmt-page-ip6.c
new file mode 100644
index 0000000000..b40c00fdf8
--- /dev/null
+++ b/tui/nmt-page-ip6.c
@@ -0,0 +1,197 @@
+/* -*- 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 ();
+ 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);
+ 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/tui/nmt-page-ip6.h b/tui/nmt-page-ip6.h
new file mode 100644
index 0000000000..d0d2bfa298
--- /dev/null
+++ b/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/tui/nmt-page-main.c b/tui/nmt-page-main.c
new file mode 100644
index 0000000000..f49b155b3c
--- /dev/null
+++ b/tui/nmt-page-main.c
@@ -0,0 +1,327 @@
+/* -*- 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/tui/nmt-page-main.h b/tui/nmt-page-main.h
new file mode 100644
index 0000000000..20de14f36a
--- /dev/null
+++ b/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/tui/nmt-page-team-port.c b/tui/nmt-page-team-port.c
new file mode 100644
index 0000000000..aec6b80394
--- /dev/null
+++ b/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/tui/nmt-page-team-port.h b/tui/nmt-page-team-port.h
new file mode 100644
index 0000000000..d4ec7a557f
--- /dev/null
+++ b/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/tui/nmt-page-team.c b/tui/nmt-page-team.c
new file mode 100644
index 0000000000..81db3d33f7
--- /dev/null
+++ b/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/tui/nmt-page-team.h b/tui/nmt-page-team.h
new file mode 100644
index 0000000000..49a81dd55e
--- /dev/null
+++ b/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/tui/nmt-page-vlan.c b/tui/nmt-page-vlan.c
new file mode 100644
index 0000000000..f6de671c85
--- /dev/null
+++ b/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/tui/nmt-page-vlan.h b/tui/nmt-page-vlan.h
new file mode 100644
index 0000000000..57d0024388
--- /dev/null
+++ b/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/tui/nmt-page-wifi.c b/tui/nmt-page-wifi.c
new file mode 100644
index 0000000000..153aee7ca5
--- /dev/null
+++ b/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/tui/nmt-page-wifi.h b/tui/nmt-page-wifi.h
new file mode 100644
index 0000000000..06cb2a9805
--- /dev/null
+++ b/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/tui/nmt-password-dialog.c b/tui/nmt-password-dialog.c
new file mode 100644
index 0000000000..00c9a17e04
--- /dev/null
+++ b/tui/nmt-password-dialog.c
@@ -0,0 +1,343 @@
+/* -*- 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,
+ newtComponent co,
+ gpointer dialog)
+{
+ 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/tui/nmt-password-dialog.h b/tui/nmt-password-dialog.h
new file mode 100644
index 0000000000..36c9f51e19
--- /dev/null
+++ b/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/tui/nmt-password-fields.c b/tui/nmt-password-fields.c
new file mode 100644
index 0000000000..ec8b521825
--- /dev/null
+++ b/tui/nmt-password-fields.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: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/tui/nmt-password-fields.h b/tui/nmt-password-fields.h
new file mode 100644
index 0000000000..2ef830aa24
--- /dev/null
+++ b/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/tui/nmt-route-editor.c b/tui/nmt-route-editor.c
new file mode 100644
index 0000000000..053cdb0275
--- /dev/null
+++ b/tui/nmt-route-editor.c
@@ -0,0 +1,219 @@
+/* -*- 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/tui/nmt-route-editor.h b/tui/nmt-route-editor.h
new file mode 100644
index 0000000000..e1040dffd9
--- /dev/null
+++ b/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/tui/nmt-route-entry.c b/tui/nmt-route-entry.c
new file mode 100644
index 0000000000..db8c254d74
--- /dev/null
+++ b/tui/nmt-route-entry.c
@@ -0,0 +1,323 @@
+/* -*- 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_INET);
+ 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/tui/nmt-route-entry.h b/tui/nmt-route-entry.h
new file mode 100644
index 0000000000..d37b14cd5b
--- /dev/null
+++ b/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/tui/nmt-route-table.c b/tui/nmt-route-table.c
new file mode 100644
index 0000000000..8f9cb24dfe
--- /dev/null
+++ b/tui/nmt-route-table.c
@@ -0,0 +1,388 @@
+/* -*- 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);
+ g_value_take_boxed (target_value, routes);
+
+ 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_len, next_hop_len, metric_len;
+ char *text;
+
+ header = nmt_newt_grid_new ();
+
+ text = g_strdup_printf ("%s/%s", _("Destination"), _("Prefix"));
+ dest_prefix_len = g_utf8_strlen (text, -1);
+ 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_len = g_utf8_strlen (text, -1);
+ 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_len = g_utf8_strlen (text, -1);
+ nmt_newt_grid_add (NMT_NEWT_GRID (header), metric_label, 2, 0);
+
+ priv->ip_entry_width = MAX (20, MAX (dest_prefix_len, next_hop_len));
+ priv->metric_entry_width = MAX (7, metric_len);
+
+ nmt_newt_widget_set_padding (dest_prefix_label,
+ 0, 0, priv->ip_entry_width - dest_prefix_len, 0);
+ nmt_newt_widget_set_padding (next_hop_label,
+ 2, 0, priv->ip_entry_width - next_hop_len, 0);
+ nmt_newt_widget_set_padding (metric_label,
+ 2, 0, priv->metric_entry_width - metric_len, 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/tui/nmt-route-table.h b/tui/nmt-route-table.h
new file mode 100644
index 0000000000..217bf40da0
--- /dev/null
+++ b/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/tui/nmt-secret-agent.c b/tui/nmt-secret-agent.c
new file mode 100644
index 0000000000..f0a01cda0a
--- /dev/null
+++ b/tui/nmt-secret-agent.c
@@ -0,0 +1,645 @@
+/* -*- 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 ((request = 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, NULL, callback_data);
+ 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/tui/nmt-secret-agent.h b/tui/nmt-secret-agent.h
new file mode 100644
index 0000000000..04b1581946
--- /dev/null
+++ b/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/tui/nmt-slave-list.c b/tui/nmt-slave-list.c
new file mode 100644
index 0000000000..284614aa2f
--- /dev/null
+++ b/tui/nmt-slave-list.c
@@ -0,0 +1,264 @@
+/* -*- 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/tui/nmt-slave-list.h b/tui/nmt-slave-list.h
new file mode 100644
index 0000000000..d10cd3a73a
--- /dev/null
+++ b/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/tui/nmt-utils.c b/tui/nmt-utils.c
new file mode 100644
index 0000000000..f2bf70db82
--- /dev/null
+++ b/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/tui/nmt-utils.h b/tui/nmt-utils.h
new file mode 100644
index 0000000000..7e24fcab19
--- /dev/null
+++ b/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/tui/nmt-widget-list.c b/tui/nmt-widget-list.c
new file mode 100644
index 0000000000..eb3e850fca
--- /dev/null
+++ b/tui/nmt-widget-list.c
@@ -0,0 +1,491 @@
+/* -*- 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_LAST,
+ 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_LAST,
+ 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/tui/nmt-widget-list.h b/tui/nmt-widget-list.h
new file mode 100644
index 0000000000..7dbeb21588
--- /dev/null
+++ b/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/tui/nmtui-connect.c b/tui/nmtui-connect.c
new file mode 100644
index 0000000000..9307100a25
--- /dev/null
+++ b/tui/nmtui-connect.c
@@ -0,0 +1,130 @@
+/* -*- 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-utils.h"
+
+static void
+connect_complete (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ NmtSyncOp *op = user_data;
+ GError *error = NULL;
+
+ if (nmt_connect_connection_list_activate_finish (NMT_CONNECT_CONNECTION_LIST (object),
+ result, &error))
+ nmt_sync_op_complete_boolean (op, TRUE, NULL);
+ else
+ nmt_sync_op_complete_boolean (op, FALSE, error);
+ g_clear_error (&error);
+}
+
+static void
+nmt_connect_connection (const char *identifier)
+{
+ NmtNewtWidget *list;
+ NmtSyncOp op;
+ GError *error = NULL;
+
+ nmt_sync_op_init (&op);
+ list = nmt_connect_connection_list_new ();
+ nmt_connect_connection_list_activate_async (NMT_CONNECT_CONNECTION_LIST (list), identifier,
+ connect_complete, &op);
+ if (!nmt_sync_op_wait_boolean (&op, &error)) {
+ nmt_newt_error_dialog (_("Could not activate connection: %s"), error->message);
+ g_error_free (error);
+ }
+ g_object_unref (list);
+}
+
+static void
+quit_clicked (NmtNewtButton *button,
+ gpointer user_data)
+{
+ nmtui_quit ();
+}
+
+static void
+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);
+
+ 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);
+
+ // FIXME: the activate button doesn't do anything
+ activate = nmt_newt_button_box_add_start (NMT_NEWT_BUTTON_BOX (bbox), _("Activate"));
+ quit = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("Quit"));
+ nmt_newt_widget_set_exit_on_activate (quit, TRUE);
+ g_signal_connect (quit, "clicked", G_CALLBACK (quit_clicked), NULL);
+
+ nmt_newt_form_set_content (form, grid);
+ nmt_newt_form_show (form);
+ g_object_unref (form);
+}
+
+void
+nmtui_connect (int argc, char **argv)
+{
+ if (argc == 2)
+ nmt_connect_connection (argv[1]);
+ else
+ nmt_connect_connection_list ();
+}
diff --git a/tui/nmtui-connect.h b/tui/nmtui-connect.h
new file mode 100644
index 0000000000..818b860ed6
--- /dev/null
+++ b/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
+
+void nmtui_connect (int argc, char **argv);
+
+G_END_DECLS
+
+#endif /* NMTUI_CONNECT_H */
diff --git a/tui/nmtui-edit.c b/tui/nmtui-edit.c
new file mode 100644
index 0000000000..f7ca4caa0d
--- /dev/null
+++ b/tui/nmtui-edit.c
@@ -0,0 +1,527 @@
+/* -*- 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 void
+quit_clicked (NmtNewtButton *button,
+ gpointer user_data)
+{
+ nmtui_quit ();
+}
+
+static void
+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);
+ g_signal_connect (quit, "clicked", G_CALLBACK (quit_clicked), NULL);
+
+ 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);
+ nmt_newt_form_show (form);
+ g_object_unref (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_error_dialog (_("OK"),
+ _("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_error_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);
+}
+
+void
+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]));
+ else {
+ GSList *conns, *iter;
+
+ conns = nm_remote_settings_list_connections (nm_settings);
+ for (iter = conns; iter; iter = iter->next) {
+ NMConnection *candidate = iter->data;
+
+ if (!strcmp (argv[1], nm_connection_get_id (candidate))) {
+ conn = candidate;
+ break;
+ }
+ }
+ g_slist_free (conns);
+ }
+
+ if (!conn) {
+ g_printerr ("%s: no such connection '%s'\n", argv[0], argv[1]);
+ exit (1);
+ }
+
+ nmt_edit_connection (conn);
+ } else
+ nmt_edit_main_connection_list ();
+}
diff --git a/tui/nmtui-edit.h b/tui/nmtui-edit.h
new file mode 100644
index 0000000000..a28c8a6f4f
--- /dev/null
+++ b/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);
+
+void 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/tui/nmtui-hostname.c b/tui/nmtui-hostname.c
new file mode 100644
index 0000000000..11216bc6ce
--- /dev/null
+++ b/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);
+}
+
+void
+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_error_dialog (_("Set hostname to '%s'"), hostname);
+ } else {
+ nmt_newt_error_dialog (_("Unable to set hostname: %s"), error->message);
+ g_error_free (error);
+ }
+
+ g_free (tmp);
+ }
+
+ nmtui_quit ();
+}
diff --git a/tui/nmtui-hostname.h b/tui/nmtui-hostname.h
new file mode 100644
index 0000000000..921b231b2f
--- /dev/null
+++ b/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
+
+void nmtui_hostname (int argc, char **argv);
+
+G_END_DECLS
+
+#endif /* NMTUI_HOSTNAME_H */
diff --git a/tui/nmtui.c b/tui/nmtui.c
new file mode 100644
index 0000000000..ab4ee0fc72
--- /dev/null
+++ b/tui/nmtui.c
@@ -0,0 +1,274 @@
+/* -*- 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 "nmtui.h"
+#include "nmtui-edit.h"
+#include "nmtui-connect.h"
+#include "nmtui-hostname.h"
+#include "nm-editor-bindings.h"
+
+NMClient *nm_client;
+NMRemoteSettings *nm_settings;
+static GMainLoop *loop;
+
+typedef void (*NmtuiSubprogram) (int argc, char **argv);
+
+static const struct {
+ const char *name, *shortcut, *usage;
+ const char *display_name;
+ NmtuiSubprogram func;
+} subprograms[] = {
+ { "edit", "nmtui-edit", " [connection]",
+ N_("Edit a connection"),
+ nmtui_edit },
+ { "connect", "nmtui-connect", " [connection]",
+ N_("Activate a connection"),
+ nmtui_connect },
+ { "hostname", "nmtui-hostname", " [new hostname]",
+ N_("Set system hostname"),
+ nmtui_hostname }
+};
+static const int num_subprograms = G_N_ELEMENTS (subprograms);
+
+static void
+nmtui_main (int argc, char **argv)
+{
+ NmtNewtForm *form;
+ NmtNewtWidget *widget, *cancel, *ok;
+ NmtNewtGrid *grid;
+ NmtNewtListbox *listbox;
+ NmtNewtButtonBox *bbox;
+ 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 = nmt_newt_listbox_new (num_subprograms, 0);
+ 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);
+ }
+
+ 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);
+
+ 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);
+
+ widget = nmt_newt_form_run_sync (form);
+ if (widget == ok || widget == (NmtNewtWidget *)listbox) {
+ NmtuiSubprogram subprogram = nmt_newt_listbox_get_active_key (listbox);
+
+ subprogram (argc, argv);
+ } else
+ nmtui_quit ();
+ g_object_unref (form);
+}
+
+/**
+ * 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 ();
+ int i;
+
+ for (i = 0; i < num_subprograms; i++) {
+ if (!strcmp (argv0, subprograms[i].shortcut)) {
+ g_printerr ("Usage: %s%s\n", argv0, subprograms[i].usage);
+ exit (1);
+ }
+ }
+
+ g_printerr ("Usage: nmtui\n");
+ for (i = 0; i < num_subprograms; i++) {
+ g_printerr (" nmtui %s%s\n", subprograms[i].name,
+ subprograms[i].usage);
+ }
+ exit (1);
+}
+
+typedef struct {
+ NmtuiSubprogram subprogram;
+ int argc;
+ char **argv;
+} NmtuiStartupData;
+
+static gboolean
+idle_run_subprogram (gpointer user_data)
+{
+ NmtuiStartupData *data = user_data;
+
+ data->subprogram (data->argc, data->argv);
+ return FALSE;
+}
+
+gboolean sleep_on_startup = FALSE;
+gboolean noinit = FALSE;
+
+GOptionEntry entries[] = {
+ { "sleep", 's', 0, G_OPTION_ARG_NONE, &sleep_on_startup,
+ "Sleep on startup", NULL },
+ { "noinit", 'n', 0, 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: Could not parse arguments: %s\n",
+ argv[0], error->message);
+ exit (1);
+ }
+ g_option_context_free (opts);
+
+ nm_client = nm_client_new ();
+
+ nm_settings = nm_remote_settings_new (NULL);
+ g_signal_connect (nm_settings, NM_REMOTE_SETTINGS_CONNECTIONS_READ,
+ G_CALLBACK (connections_read), &got_connections);
+ while (!got_connections)
+ g_main_context_iteration (NULL, TRUE);
+
+ if (sleep_on_startup)
+ sleep (5);
+
+ nm_editor_bindings_init ();
+
+ 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/tui/nmtui.h b/tui/nmtui.h
new file mode 100644
index 0000000000..3e462b1130
--- /dev/null
+++ b/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/tui/vpn-helpers.c b/tui/vpn-helpers.c
new file mode 100644
index 0000000000..f4e32837a8
--- /dev/null
+++ b/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/tui/vpn-helpers.h b/tui/vpn-helpers.h
new file mode 100644
index 0000000000..28019ca9c8
--- /dev/null
+++ b/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_ */