nmtui: initial import of nmtui
authorDan Winship <danw@gnome.org>
Mon, 2 Dec 2013 21:20:26 +0000 (16:20 -0500)
committerDan Winship <danw@gnome.org>
Fri, 13 Dec 2013 15:00:51 +0000 (16:00 +0100)
nmtui is a TUI (curses-based Text User Interface) for NetworkManager

129 files changed:
.gitignore
Makefile.am
configure.ac
po/POTFILES.in
tui/Makefile.am [new file with mode: 0644]
tui/newt/Makefile.am [new file with mode: 0644]
tui/newt/nmt-newt-button-box.c [new file with mode: 0644]
tui/newt/nmt-newt-button-box.h [new file with mode: 0644]
tui/newt/nmt-newt-button.c [new file with mode: 0644]
tui/newt/nmt-newt-button.h [new file with mode: 0644]
tui/newt/nmt-newt-checkbox.c [new file with mode: 0644]
tui/newt/nmt-newt-checkbox.h [new file with mode: 0644]
tui/newt/nmt-newt-component.c [new file with mode: 0644]
tui/newt/nmt-newt-component.h [new file with mode: 0644]
tui/newt/nmt-newt-container.c [new file with mode: 0644]
tui/newt/nmt-newt-container.h [new file with mode: 0644]
tui/newt/nmt-newt-entry-numeric.c [new file with mode: 0644]
tui/newt/nmt-newt-entry-numeric.h [new file with mode: 0644]
tui/newt/nmt-newt-entry.c [new file with mode: 0644]
tui/newt/nmt-newt-entry.h [new file with mode: 0644]
tui/newt/nmt-newt-form.c [new file with mode: 0644]
tui/newt/nmt-newt-form.h [new file with mode: 0644]
tui/newt/nmt-newt-grid.c [new file with mode: 0644]
tui/newt/nmt-newt-grid.h [new file with mode: 0644]
tui/newt/nmt-newt-hacks.c [new file with mode: 0644]
tui/newt/nmt-newt-hacks.h [new file with mode: 0644]
tui/newt/nmt-newt-label.c [new file with mode: 0644]
tui/newt/nmt-newt-label.h [new file with mode: 0644]
tui/newt/nmt-newt-listbox.c [new file with mode: 0644]
tui/newt/nmt-newt-listbox.h [new file with mode: 0644]
tui/newt/nmt-newt-popup.c [new file with mode: 0644]
tui/newt/nmt-newt-popup.h [new file with mode: 0644]
tui/newt/nmt-newt-section.c [new file with mode: 0644]
tui/newt/nmt-newt-section.h [new file with mode: 0644]
tui/newt/nmt-newt-separator.c [new file with mode: 0644]
tui/newt/nmt-newt-separator.h [new file with mode: 0644]
tui/newt/nmt-newt-stack.c [new file with mode: 0644]
tui/newt/nmt-newt-stack.h [new file with mode: 0644]
tui/newt/nmt-newt-textbox.c [new file with mode: 0644]
tui/newt/nmt-newt-textbox.h [new file with mode: 0644]
tui/newt/nmt-newt-toggle-button.c [new file with mode: 0644]
tui/newt/nmt-newt-toggle-button.h [new file with mode: 0644]
tui/newt/nmt-newt-types.h [new file with mode: 0644]
tui/newt/nmt-newt-utils.c [new file with mode: 0644]
tui/newt/nmt-newt-utils.h [new file with mode: 0644]
tui/newt/nmt-newt-widget.c [new file with mode: 0644]
tui/newt/nmt-newt-widget.h [new file with mode: 0644]
tui/newt/nmt-newt.h [new file with mode: 0644]
tui/nm-editor-bindings.c [new file with mode: 0644]
tui/nm-editor-bindings.h [new file with mode: 0644]
tui/nm-editor-utils.c [new file with mode: 0644]
tui/nm-editor-utils.h [new file with mode: 0644]
tui/nm-gvaluearray-compat.h [new file with mode: 0644]
tui/nm-ui-utils.c [new file with mode: 0644]
tui/nm-ui-utils.h [new file with mode: 0644]
tui/nmt-address-list.c [new file with mode: 0644]
tui/nmt-address-list.h [new file with mode: 0644]
tui/nmt-connect-connection-list.c [new file with mode: 0644]
tui/nmt-connect-connection-list.h [new file with mode: 0644]
tui/nmt-device-entry.c [new file with mode: 0644]
tui/nmt-device-entry.h [new file with mode: 0644]
tui/nmt-edit-connection-list.c [new file with mode: 0644]
tui/nmt-edit-connection-list.h [new file with mode: 0644]
tui/nmt-editor-page.c [new file with mode: 0644]
tui/nmt-editor-page.h [new file with mode: 0644]
tui/nmt-editor.c [new file with mode: 0644]
tui/nmt-editor.h [new file with mode: 0644]
tui/nmt-ip-entry.c [new file with mode: 0644]
tui/nmt-ip-entry.h [new file with mode: 0644]
tui/nmt-mac-entry.c [new file with mode: 0644]
tui/nmt-mac-entry.h [new file with mode: 0644]
tui/nmt-mtu-entry.c [new file with mode: 0644]
tui/nmt-mtu-entry.h [new file with mode: 0644]
tui/nmt-page-bond.c [new file with mode: 0644]
tui/nmt-page-bond.h [new file with mode: 0644]
tui/nmt-page-bridge-port.c [new file with mode: 0644]
tui/nmt-page-bridge-port.h [new file with mode: 0644]
tui/nmt-page-bridge.c [new file with mode: 0644]
tui/nmt-page-bridge.h [new file with mode: 0644]
tui/nmt-page-device.c [new file with mode: 0644]
tui/nmt-page-device.h [new file with mode: 0644]
tui/nmt-page-ethernet.c [new file with mode: 0644]
tui/nmt-page-ethernet.h [new file with mode: 0644]
tui/nmt-page-grid.c [new file with mode: 0644]
tui/nmt-page-grid.h [new file with mode: 0644]
tui/nmt-page-infiniband.c [new file with mode: 0644]
tui/nmt-page-infiniband.h [new file with mode: 0644]
tui/nmt-page-ip4.c [new file with mode: 0644]
tui/nmt-page-ip4.h [new file with mode: 0644]
tui/nmt-page-ip6.c [new file with mode: 0644]
tui/nmt-page-ip6.h [new file with mode: 0644]
tui/nmt-page-main.c [new file with mode: 0644]
tui/nmt-page-main.h [new file with mode: 0644]
tui/nmt-page-team-port.c [new file with mode: 0644]
tui/nmt-page-team-port.h [new file with mode: 0644]
tui/nmt-page-team.c [new file with mode: 0644]
tui/nmt-page-team.h [new file with mode: 0644]
tui/nmt-page-vlan.c [new file with mode: 0644]
tui/nmt-page-vlan.h [new file with mode: 0644]
tui/nmt-page-wifi.c [new file with mode: 0644]
tui/nmt-page-wifi.h [new file with mode: 0644]
tui/nmt-password-dialog.c [new file with mode: 0644]
tui/nmt-password-dialog.h [new file with mode: 0644]
tui/nmt-password-fields.c [new file with mode: 0644]
tui/nmt-password-fields.h [new file with mode: 0644]
tui/nmt-route-editor.c [new file with mode: 0644]
tui/nmt-route-editor.h [new file with mode: 0644]
tui/nmt-route-entry.c [new file with mode: 0644]
tui/nmt-route-entry.h [new file with mode: 0644]
tui/nmt-route-table.c [new file with mode: 0644]
tui/nmt-route-table.h [new file with mode: 0644]
tui/nmt-secret-agent.c [new file with mode: 0644]
tui/nmt-secret-agent.h [new file with mode: 0644]
tui/nmt-slave-list.c [new file with mode: 0644]
tui/nmt-slave-list.h [new file with mode: 0644]
tui/nmt-utils.c [new file with mode: 0644]
tui/nmt-utils.h [new file with mode: 0644]
tui/nmt-widget-list.c [new file with mode: 0644]
tui/nmt-widget-list.h [new file with mode: 0644]
tui/nmtui-connect.c [new file with mode: 0644]
tui/nmtui-connect.h [new file with mode: 0644]
tui/nmtui-edit.c [new file with mode: 0644]
tui/nmtui-edit.h [new file with mode: 0644]
tui/nmtui-hostname.c [new file with mode: 0644]
tui/nmtui-hostname.h [new file with mode: 0644]
tui/nmtui.c [new file with mode: 0644]
tui/nmtui.h [new file with mode: 0644]
tui/vpn-helpers.c [new file with mode: 0644]
tui/vpn-helpers.h [new file with mode: 0644]

index 50d1485..71f7e53 100644 (file)
@@ -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
index ebaad4b..ab2941c 100644 (file)
@@ -9,6 +9,7 @@ SUBDIRS = \
        src \
        callouts \
        cli \
+       tui \
        tools \
        policy \
        data \
index 41bdb08..f19b9ad 100644 (file)
@@ -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"
index 3be5735..98516c1 100644 (file)
@@ -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 (file)
index 0000000..6c02ffc
--- /dev/null
@@ -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 (file)
index 0000000..c5b295c
--- /dev/null
@@ -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 (file)
index 0000000..d38ed10
--- /dev/null
@@ -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 (file)
index 0000000..970588a
--- /dev/null
@@ -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 (file)
index 0000000..cdec789
--- /dev/null
@@ -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 (file)
index 0000000..52e95c5
--- /dev/null
@@ -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 (file)
index 0000000..168fada
--- /dev/null
@@ -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 (file)
index 0000000..c277386
--- /dev/null
@@ -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 (file)
index 0000000..877b53d
--- /dev/null
@@ -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 (file)
index 0000000..2ffa018
--- /dev/null
@@ -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 (file)
index 0000000..03f677f
--- /dev/null
@@ -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 (file)
index 0000000..6f18292
--- /dev/null
@@ -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 (file)
index 0000000..76ca1c3
--- /dev/null
@@ -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 (file)
index 0000000..43ac344
--- /dev/null
@@ -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 (file)
index 0000000..c98fcee
--- /dev/null
@@ -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 (file)
index 0000000..8df0b13
--- /dev/null
@@ -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 (file)
index 0000000..ff8eb83
--- /dev/null
@@ -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 (file)
index 0000000..e320d87
--- /dev/null
@@ -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 (file)
index 0000000..ac96733
--- /dev/null
@@ -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 (file)
index 0000000..f36a38e
--- /dev/null
@@ -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 (file)
index 0000000..2d9b172
--- /dev/null
@@ -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 (file)
index 0000000..ba8464a
--- /dev/null
@@ -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 (file)
index 0000000..a9d44b0
--- /dev/null
@@ -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 (file)
index 0000000..a4e0dcc
--- /dev/null
@@ -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 (file)
index 0000000..2e3655a
--- /dev/null
@@ -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 (file)
index 0000000..da29207
--- /dev/null
@@ -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 (file)
index 0000000..e9757ea
--- /dev/null
@@ -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 (file)
index 0000000..643ea2d
--- /dev/null
@@ -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 (file)
index 0000000..094b41b
--- /dev/null
@@ -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 (file)
index 0000000..a943ba2
--- /dev/null
@@ -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 (file)
index 0000000..07deb1a
--- /dev/null
@@ -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 (file)
index 0000000..3f4183d
--- /dev/null
@@ -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 (file)
index 0000000..1b31d58
--- /dev/null
@@ -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 (file)
index 0000000..09afe4b
--- /dev/null
@@ -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 (file)
index 0000000..888bde2
--- /dev/null
@@ -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 (file)
index 0000000..b3743ae
--- /dev/null
@@ -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 (file)
index 0000000..d435e45
--- /dev/null
@@ -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 (file)
index 0000000..e54c6d3
--- /dev/null
@@ -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 (file)
index 0000000..c26ff90
--- /dev/null
@@ -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 (file)
index 0000000..f2148d2
--- /dev/null
@@ -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 (file)
index 0000000..1823e3c
--- /dev/null
@@ -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 (file)
index 0000000..f7c8d20
--- /dev/null
@@ -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 (file)
index 0000000..265f245
--- /dev/null
@@ -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 (file)
index 0000000..6a9c8d9
--- /dev/null
@@ -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 (file)
index 0000000..949dcd7
--- /dev/null
@@ -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 (file)
index 0000000..a7a32dd
--- /dev/null
@@ -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 (file)
index 0000000..7989eac
--- /dev/null
@@ -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 (file)
index 0000000..4b9ad5e
--- /dev/null
@@ -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 (file)
index 0000000..017d336
--- /dev/null
@@ -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 (file)
index 0000000..3609c91
--- /dev/null
@@ -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 (file)
index 0000000..693df44
--- /dev/null
@@ -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 (file)
index 0000000..da301a2
--- /dev/null
@@ -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 (file)
index 0000000..df7a4f7
--- /dev/null
@@ -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 (file)
index 0000000..12d16da
--- /dev/null
@@ -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 (file)
index 0000000..14e94ec
--- /dev/null
@@ -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 (file)
index 0000000..3a418fd
--- /dev/null
@@ -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 (file)
index 0000000..2b959aa
--- /dev/null
@@ -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 (file)
index 0000000..eeba78d
--- /dev/null
@@ -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 (file)
index 0000000..35157e9
--- /dev/null
@@ -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 (file)
index 0000000..549a86a
--- /dev/null
@@ -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 (file)
index 0000000..d9a9710
--- /dev/null
@@ -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 (file)
index 0000000..7aa2276
--- /dev/null
@@ -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 (file)
index 0000000..a991a76
--- /dev/null
@@ -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 (file)
index 0000000..85a29fc
--- /dev/null
@@ -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 (file)
index 0000000..ceb355e
--- /dev/null
@@ -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 (file)
index 0000000..d76c097
--- /dev/null
@@ -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 (file)
index 0000000..33a3232
--- /dev/null
@@ -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 (file)
index 0000000..da3746c
--- /dev/null
@@ -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 (file)
index 0000000..dfe416d
--- /dev/null
@@ -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 (file)
index 0000000..55a6ffe
--- /dev/null
@@ -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 (file)
index 0000000..c8e69dd
--- /dev/null
@@ -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 (file)
index 0000000..ab7a788
--- /dev/null
@@ -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 (file)
index 0000000..7fe3a92
--- /dev/null
@@ -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 (file)
index 0000000..713d835
--- /dev/null
@@ -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 (file)
index 0000000..b11d350
--- /dev/null
@@ -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 (file)
index 0000000..8ab0747
--- /dev/null
@@ -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 (file)
index 0000000..8c90a67
--- /dev/null
@@ -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 (file)
index 0000000..9eb08fe
--- /dev/null
@@ -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 (file)
index 0000000..5e00156
--- /dev/null
@@ -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 (file)
index 0000000..89ba933
--- /dev/null
@@ -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 (file)
index 0000000..40ff96c
--- /dev/null
@@ -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 (file)
index 0000000..c1238ab
--- /dev/null
@@ -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 (file)
index 0000000..706b7b6
--- /dev/null
@@ -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 (file)
index 0000000..210387c
--- /dev/null
@@ -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 (file)
index 0000000..f3bb933
--- /dev/null
@@ -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 (file)
index 0000000..b40c00f
--- /dev/null
@@ -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 (file)
index 0000000..d0d2bfa
--- /dev/null
@@ -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 (file)
index 0000000..f49b155
--- /dev/null
@@ -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 (file)
index 0000000..20de14f
--- /dev/null
@@ -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 (file)
index 0000000..aec6b80
--- /dev/null
@@ -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 (file)
index 0000000..d4ec7a5
--- /dev/null
@@ -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 (file)
index 0000000..81db3d3
--- /dev/null
@@ -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 (file)
index 0000000..49a81dd
--- /dev/null
@@ -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 (file)
index 0000000..f6de671
--- /dev/null
@@ -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 (file)
index 0000000..57d0024
--- /dev/null
@@ -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 (file)
index 0000000..153aee7
--- /dev/null
@@ -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 (file)
index 0000000..06cb2a9
--- /dev/null
@@ -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 (file)
index 0000000..00c9a17
--- /dev/null
@@ -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 (file)
index 0000000..36c9f51
--- /dev/null
@@ -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 (file)
index 0000000..ec8b521
--- /dev/null
@@ -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 (file)
index 0000000..2ef830a
--- /dev/null
@@ -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 (file)
index 0000000..053cdb0
--- /dev/null
@@ -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 (file)
index 0000000..e1040df
--- /dev/null
@@ -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 (file)
index 0000000..db8c254
--- /dev/null
@@ -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 (file)
index 0000000..d37b14c
--- /dev/null
@@ -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 (file)
index 0000000..8f9cb24
--- /dev/null
@@ -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 (file)
index 0000000..217bf40
--- /dev/null
@@ -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 (file)
index 0000000..f0a01cd
--- /dev/null
@@ -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 (file)
index 0000000..04b1581
--- /dev/null
@@ -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 (file)
index 0000000..284614a
--- /dev/null
@@ -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 (file)
index 0000000..d10cd3a
--- /dev/null
@@ -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 (file)
index 0000000..f2bf70d
--- /dev/null
@@ -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 (file)
index 0000000..7e24fca
--- /dev/null
@@ -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 (file)
index 0000000..eb3e850
--- /dev/null
@@ -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 (file)
index 0000000..7dbeb21
--- /dev/null
@@ -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 (file)
index 0000000..9307100
--- /dev/null
@@ -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 (file)
index 0000000..818b860
--- /dev/null
@@ -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 (file)
index 0000000..f7ca4ca
--- /dev/null
@@ -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 (file)
index 0000000..a28c8a6
--- /dev/null
@@ -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 (file)
index 0000000..11216bc
--- /dev/null
@@ -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 (file)
index 0000000..921b231
--- /dev/null
@@ -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 (file)
index 0000000..ab4ee0f
--- /dev/null
@@ -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 (file)
index 0000000..3e462b1
--- /dev/null
@@ -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 (file)
index 0000000..f4e3283
--- /dev/null
@@ -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 (file)
index 0000000..28019ca
--- /dev/null
@@ -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_ */