summary refs log tree commit diff
path: root/clients/tui
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2015-01-22 00:29:39 +0100
committerMichael Biebl <biebl@debian.org>2015-01-22 00:29:39 +0100
commit2c032d8f1c6292c1338a615e6ec40252889ba85c (patch)
tree1f77182220b2b0264288ba4a476ab47e5bc48716 /clients/tui
parent33491bc4279481db8ae47213e34a6d695a0e8830 (diff)
Imported Upstream version 1.0.0 upstream/1.0.0
Diffstat (limited to 'clients/tui')
-rw-r--r--clients/tui/Makefile.am128
-rw-r--r--clients/tui/Makefile.in1131
-rw-r--r--clients/tui/newt/Makefile.am53
-rw-r--r--clients/tui/newt/Makefile.in805
-rw-r--r--clients/tui/newt/nmt-newt-button-box.c386
-rw-r--r--clients/tui/newt/nmt-newt-button-box.h65
-rw-r--r--clients/tui/newt/nmt-newt-button.c261
-rw-r--r--clients/tui/newt/nmt-newt-button.h53
-rw-r--r--clients/tui/newt/nmt-newt-checkbox.c237
-rw-r--r--clients/tui/newt/nmt-newt-checkbox.h53
-rw-r--r--clients/tui/newt/nmt-newt-component.c309
-rw-r--r--clients/tui/newt/nmt-newt-component.h57
-rw-r--r--clients/tui/newt/nmt-newt-container.c252
-rw-r--r--clients/tui/newt/nmt-newt-container.h61
-rw-r--r--clients/tui/newt/nmt-newt-entry-numeric.c215
-rw-r--r--clients/tui/newt/nmt-newt-entry-numeric.h51
-rw-r--r--clients/tui/newt/nmt-newt-entry.c540
-rw-r--r--clients/tui/newt/nmt-newt-entry.h74
-rw-r--r--clients/tui/newt/nmt-newt-form.c753
-rw-r--r--clients/tui/newt/nmt-newt-form.h66
-rw-r--r--clients/tui/newt/nmt-newt-grid.c472
-rw-r--r--clients/tui/newt/nmt-newt-grid.h72
-rw-r--r--clients/tui/newt/nmt-newt-hacks.c103
-rw-r--r--clients/tui/newt/nmt-newt-hacks.h42
-rw-r--r--clients/tui/newt/nmt-newt-label.c326
-rw-r--r--clients/tui/newt/nmt-newt-label.h66
-rw-r--r--clients/tui/newt/nmt-newt-listbox.c548
-rw-r--r--clients/tui/newt/nmt-newt-listbox.h71
-rw-r--r--clients/tui/newt/nmt-newt-popup.c360
-rw-r--r--clients/tui/newt/nmt-newt-popup.h62
-rw-r--r--clients/tui/newt/nmt-newt-section.c447
-rw-r--r--clients/tui/newt/nmt-newt-section.h57
-rw-r--r--clients/tui/newt/nmt-newt-separator.c66
-rw-r--r--clients/tui/newt/nmt-newt-separator.h49
-rw-r--r--clients/tui/newt/nmt-newt-stack.c368
-rw-r--r--clients/tui/newt/nmt-newt-stack.h61
-rw-r--r--clients/tui/newt/nmt-newt-textbox.c295
-rw-r--r--clients/tui/newt/nmt-newt-textbox.h59
-rw-r--r--clients/tui/newt/nmt-newt-toggle-button.c237
-rw-r--r--clients/tui/newt/nmt-newt-toggle-button.h54
-rw-r--r--clients/tui/newt/nmt-newt-types.h50
-rw-r--r--clients/tui/newt/nmt-newt-utils.c393
-rw-r--r--clients/tui/newt/nmt-newt-utils.h53
-rw-r--r--clients/tui/newt/nmt-newt-widget.c668
-rw-r--r--clients/tui/newt/nmt-newt-widget.h118
-rw-r--r--clients/tui/newt/nmt-newt.h39
-rw-r--r--clients/tui/nm-editor-bindings.c1101
-rw-r--r--clients/tui/nm-editor-bindings.h76
-rw-r--r--clients/tui/nm-editor-utils.c414
-rw-r--r--clients/tui/nm-editor-utils.h43
-rw-r--r--clients/tui/nmt-address-list.c285
-rw-r--r--clients/tui/nmt-address-list.h57
-rw-r--r--clients/tui/nmt-connect-connection-list.c701
-rw-r--r--clients/tui/nmt-connect-connection-list.h61
-rw-r--r--clients/tui/nmt-device-entry.c573
-rw-r--r--clients/tui/nmt-device-entry.h60
-rw-r--r--clients/tui/nmt-edit-connection-list.c554
-rw-r--r--clients/tui/nmt-edit-connection-list.h59
-rw-r--r--clients/tui/nmt-editor-grid.c463
-rw-r--r--clients/tui/nmt-editor-grid.h62
-rw-r--r--clients/tui/nmt-editor-page-device.c131
-rw-r--r--clients/tui/nmt-editor-page-device.h50
-rw-r--r--clients/tui/nmt-editor-page.c179
-rw-r--r--clients/tui/nmt-editor-page.h58
-rw-r--r--clients/tui/nmt-editor-section.c282
-rw-r--r--clients/tui/nmt-editor-section.h56
-rw-r--r--clients/tui/nmt-editor.c520
-rw-r--r--clients/tui/nmt-editor.h51
-rw-r--r--clients/tui/nmt-ip-entry.c268
-rw-r--r--clients/tui/nmt-ip-entry.h52
-rw-r--r--clients/tui/nmt-mac-entry.c227
-rw-r--r--clients/tui/nmt-mac-entry.h51
-rw-r--r--clients/tui/nmt-mtu-entry.c192
-rw-r--r--clients/tui/nmt-mtu-entry.h49
-rw-r--r--clients/tui/nmt-page-bond.c437
-rw-r--r--clients/tui/nmt-page-bond.h50
-rw-r--r--clients/tui/nmt-page-bridge-port.c95
-rw-r--r--clients/tui/nmt-page-bridge-port.h49
-rw-r--r--clients/tui/nmt-page-bridge.c150
-rw-r--r--clients/tui/nmt-page-bridge.h50
-rw-r--r--clients/tui/nmt-page-dsl.c141
-rw-r--r--clients/tui/nmt-page-dsl.h50
-rw-r--r--clients/tui/nmt-page-ethernet.c99
-rw-r--r--clients/tui/nmt-page-ethernet.h50
-rw-r--r--clients/tui/nmt-page-infiniband.c110
-rw-r--r--clients/tui/nmt-page-infiniband.h50
-rw-r--r--clients/tui/nmt-page-ip4.c202
-rw-r--r--clients/tui/nmt-page-ip4.h49
-rw-r--r--clients/tui/nmt-page-ip6.c200
-rw-r--r--clients/tui/nmt-page-ip6.h49
-rw-r--r--clients/tui/nmt-page-ppp.c288
-rw-r--r--clients/tui/nmt-page-ppp.h49
-rw-r--r--clients/tui/nmt-page-team-port.c129
-rw-r--r--clients/tui/nmt-page-team-port.h49
-rw-r--r--clients/tui/nmt-page-team.c193
-rw-r--r--clients/tui/nmt-page-team.h50
-rw-r--r--clients/tui/nmt-page-vlan.c158
-rw-r--r--clients/tui/nmt-page-vlan.h50
-rw-r--r--clients/tui/nmt-page-wifi.c400
-rw-r--r--clients/tui/nmt-page-wifi.h50
-rw-r--r--clients/tui/nmt-password-dialog.c345
-rw-r--r--clients/tui/nmt-password-dialog.h57
-rw-r--r--clients/tui/nmt-password-fields.c319
-rw-r--r--clients/tui/nmt-password-fields.h55
-rw-r--r--clients/tui/nmt-route-editor.c210
-rw-r--r--clients/tui/nmt-route-editor.h51
-rw-r--r--clients/tui/nmt-route-entry.c291
-rw-r--r--clients/tui/nmt-route-entry.h51
-rw-r--r--clients/tui/nmt-route-table.c334
-rw-r--r--clients/tui/nmt-route-table.h49
-rw-r--r--clients/tui/nmt-slave-list.c266
-rw-r--r--clients/tui/nmt-slave-list.h52
-rw-r--r--clients/tui/nmt-utils.c151
-rw-r--r--clients/tui/nmt-utils.h46
-rw-r--r--clients/tui/nmt-widget-list.c496
-rw-r--r--clients/tui/nmt-widget-list.h69
-rw-r--r--clients/tui/nmtui-connect.c365
-rw-r--r--clients/tui/nmtui-connect.h28
-rw-r--r--clients/tui/nmtui-edit.c577
-rw-r--r--clients/tui/nmtui-edit.h44
-rw-r--r--clients/tui/nmtui-hostname.c128
-rw-r--r--clients/tui/nmtui-hostname.h28
-rw-r--r--clients/tui/nmtui.c293
-rw-r--r--clients/tui/nmtui.h32
124 files changed, 24945 insertions, 0 deletions
diff --git a/clients/tui/Makefile.am b/clients/tui/Makefile.am
new file mode 100644
index 00000000..ad9335da
--- /dev/null
+++ b/clients/tui/Makefile.am
@@ -0,0 +1,128 @@
+if BUILD_NMTUI
+
+SUBDIRS = newt .
+
+AM_CPPFLAGS= \
+	-I$(top_srcdir) \
+	-I$(top_srcdir)/include \
+	-I$(top_srcdir)/libnm-core \
+	-I$(top_builddir)/libnm-core \
+	-I$(top_srcdir)/libnm \
+	-I$(top_builddir)/libnm \
+	-I$(srcdir)/newt \
+	-I$(top_srcdir)/clients/common \
+	$(GLIB_CFLAGS) \
+	$(NEWT_CFLAGS) \
+	$(GUDEV_CFLAGS) \
+	-DG_LOG_DOMAIN=\""nmtui"\" \
+	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+	-DLOCALEDIR=\""$(localedir)"\" \
+	$(NULL)
+
+bin_PROGRAMS = nmtui
+links = nmtui-edit nmtui-connect nmtui-hostname
+
+install-exec-hook:
+	for link in $(links); do \
+		ln -f $(DESTDIR)$(bindir)/nmtui $(DESTDIR)$(bindir)/$$link; \
+	done
+
+uninstall-hook:
+	for link in $(links); do \
+		rm -f $(DESTDIR)$(bindir)/$$link; \
+	done
+
+nmtui_SOURCES = \
+	nmtui.c \
+	nmtui.h \
+	nmtui-connect.c \
+	nmtui-connect.h \
+	nmtui-edit.c \
+	nmtui-edit.h \
+	nmtui-hostname.c \
+	nmtui-hostname.h \
+	\
+	nm-editor-bindings.c \
+	nm-editor-bindings.h \
+	nm-editor-utils.c \
+	nm-editor-utils.h \
+	\
+	nmt-address-list.c \
+	nmt-address-list.h \
+	nmt-connect-connection-list.c \
+	nmt-connect-connection-list.h \
+	nmt-device-entry.c \
+	nmt-device-entry.h \
+	nmt-edit-connection-list.c \
+	nmt-edit-connection-list.h \
+	nmt-editor-grid.c \
+	nmt-editor-grid.h \
+	nmt-editor-page.c \
+	nmt-editor-page.h \
+	nmt-editor-page-device.c \
+	nmt-editor-page-device.h \
+	nmt-editor-section.c \
+	nmt-editor-section.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-dsl.c \
+	nmt-page-dsl.h \
+	nmt-page-ethernet.c \
+	nmt-page-ethernet.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-ppp.c \
+	nmt-page-ppp.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-slave-list.c \
+	nmt-slave-list.h \
+	nmt-utils.c \
+	nmt-utils.h \
+	nmt-widget-list.c \
+	nmt-widget-list.h \
+	$(srcdir)/../common/nm-secret-agent-simple.c \
+	$(srcdir)/../common/nm-secret-agent-simple.h \
+	$(NULL)
+
+nmtui_LDADD = \
+	$(top_builddir)/libnm/libnm.la \
+	$(builddir)/newt/libnmt-newt.a \
+	$(GUDEV_LIBS) \
+	$(NEWT_LIBS) \
+	$(GLIB_LIBS) \
+	$(NULL)
+
+endif
diff --git a/clients/tui/Makefile.in b/clients/tui/Makefile.in
new file mode 100644
index 00000000..86b2a3b9
--- /dev/null
+++ b/clients/tui/Makefile.in
@@ -0,0 +1,1131 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@BUILD_NMTUI_TRUE@bin_PROGRAMS = nmtui$(EXEEXT)
+subdir = clients/tui
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/build-aux/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gettext.m4 \
+	$(top_srcdir)/m4/gnome-code-coverage.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
+	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+PROGRAMS = $(bin_PROGRAMS)
+am__nmtui_SOURCES_DIST = nmtui.c nmtui.h nmtui-connect.c \
+	nmtui-connect.h nmtui-edit.c nmtui-edit.h nmtui-hostname.c \
+	nmtui-hostname.h nm-editor-bindings.c nm-editor-bindings.h \
+	nm-editor-utils.c nm-editor-utils.h nmt-address-list.c \
+	nmt-address-list.h nmt-connect-connection-list.c \
+	nmt-connect-connection-list.h nmt-device-entry.c \
+	nmt-device-entry.h nmt-edit-connection-list.c \
+	nmt-edit-connection-list.h nmt-editor-grid.c nmt-editor-grid.h \
+	nmt-editor-page.c nmt-editor-page.h nmt-editor-page-device.c \
+	nmt-editor-page-device.h nmt-editor-section.c \
+	nmt-editor-section.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-dsl.c nmt-page-dsl.h \
+	nmt-page-ethernet.c nmt-page-ethernet.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-ppp.c nmt-page-ppp.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-slave-list.c nmt-slave-list.h \
+	nmt-utils.c nmt-utils.h nmt-widget-list.c nmt-widget-list.h \
+	$(srcdir)/../common/nm-secret-agent-simple.c \
+	$(srcdir)/../common/nm-secret-agent-simple.h
+@BUILD_NMTUI_TRUE@am_nmtui_OBJECTS = nmtui.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmtui-connect.$(OBJEXT) nmtui-edit.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmtui-hostname.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nm-editor-bindings.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nm-editor-utils.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-address-list.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-connect-connection-list.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-device-entry.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-edit-connection-list.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-editor-grid.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-editor-page.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-editor-page-device.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-editor-section.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-editor.$(OBJEXT) nmt-ip-entry.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-mac-entry.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-mtu-entry.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-bond.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-bridge.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-bridge-port.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-dsl.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-ethernet.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-infiniband.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-ip4.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-ip6.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-ppp.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-team.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-team-port.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-vlan.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-page-wifi.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-password-dialog.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-password-fields.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-route-editor.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-route-entry.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-route-table.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-slave-list.$(OBJEXT) nmt-utils.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nmt-widget-list.$(OBJEXT) \
+@BUILD_NMTUI_TRUE@	nm-secret-agent-simple.$(OBJEXT)
+nmtui_OBJECTS = $(am_nmtui_OBJECTS)
+am__DEPENDENCIES_1 =
+@BUILD_NMTUI_TRUE@nmtui_DEPENDENCIES = $(top_builddir)/libnm/libnm.la \
+@BUILD_NMTUI_TRUE@	$(builddir)/newt/libnmt-newt.a \
+@BUILD_NMTUI_TRUE@	$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+@BUILD_NMTUI_TRUE@	$(am__DEPENDENCIES_1)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(nmtui_SOURCES)
+DIST_SOURCES = $(am__nmtui_SOURCES_DIST)
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+	ctags-recursive dvi-recursive html-recursive info-recursive \
+	install-data-recursive install-dvi-recursive \
+	install-exec-recursive install-html-recursive \
+	install-info-recursive install-pdf-recursive \
+	install-ps-recursive install-recursive installcheck-recursive \
+	installdirs-recursive pdf-recursive ps-recursive \
+	tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive	\
+  distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+  $(RECURSIVE_TARGETS) \
+  $(RECURSIVE_CLEAN_TARGETS) \
+  $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+	distdir
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = newt .
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+  dir0=`pwd`; \
+  sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+  sed_rest='s,^[^/]*/*,,'; \
+  sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+  sed_butlast='s,/*[^/]*$$,,'; \
+  while test -n "$$dir1"; do \
+    first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+    if test "$$first" != "."; then \
+      if test "$$first" = ".."; then \
+        dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+        dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+      else \
+        first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+        if test "$$first2" = "$$first"; then \
+          dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+        else \
+          dir2="../$$dir2"; \
+        fi; \
+        dir0="$$dir0"/"$$first"; \
+      fi; \
+    fi; \
+    dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+  done; \
+  reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CKDB_PATH = @CKDB_PATH@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
+DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
+DLLTOOL = @DLLTOOL@
+DNSMASQ_PATH = @DNSMASQ_PATH@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MAKEFILE = @GLIB_MAKEFILE@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
+IPTABLES_PATH = @IPTABLES_PATH@
+IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
+IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
+KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
+LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
+MM_GLIB_LIBS = @MM_GLIB_LIBS@
+MOC = @MOC@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
+NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
+NM_MICRO_VERSION = @NM_MICRO_VERSION@
+NM_MINOR_VERSION = @NM_MINOR_VERSION@
+NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
+NM_VERSION = @NM_VERSION@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
+QT_CFLAGS = @QT_CFLAGS@
+QT_LIBS = @QT_LIBS@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@
+SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@
+SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
+SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_DIR = @UDEV_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+nmbinary = @nmbinary@
+nmconfdir = @nmconfdir@
+nmdatadir = @nmdatadir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+with_dhclient = @with_dhclient@
+with_dhcpcd = @with_dhcpcd@
+with_netconfig = @with_netconfig@
+with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
+@BUILD_NMTUI_TRUE@SUBDIRS = newt .
+@BUILD_NMTUI_TRUE@AM_CPPFLAGS = \
+@BUILD_NMTUI_TRUE@	-I$(top_srcdir) \
+@BUILD_NMTUI_TRUE@	-I$(top_srcdir)/include \
+@BUILD_NMTUI_TRUE@	-I$(top_srcdir)/libnm-core \
+@BUILD_NMTUI_TRUE@	-I$(top_builddir)/libnm-core \
+@BUILD_NMTUI_TRUE@	-I$(top_srcdir)/libnm \
+@BUILD_NMTUI_TRUE@	-I$(top_builddir)/libnm \
+@BUILD_NMTUI_TRUE@	-I$(srcdir)/newt \
+@BUILD_NMTUI_TRUE@	-I$(top_srcdir)/clients/common \
+@BUILD_NMTUI_TRUE@	$(GLIB_CFLAGS) \
+@BUILD_NMTUI_TRUE@	$(NEWT_CFLAGS) \
+@BUILD_NMTUI_TRUE@	$(GUDEV_CFLAGS) \
+@BUILD_NMTUI_TRUE@	-DG_LOG_DOMAIN=\""nmtui"\" \
+@BUILD_NMTUI_TRUE@	-DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \
+@BUILD_NMTUI_TRUE@	-DLOCALEDIR=\""$(localedir)"\" \
+@BUILD_NMTUI_TRUE@	$(NULL)
+
+@BUILD_NMTUI_TRUE@links = nmtui-edit nmtui-connect nmtui-hostname
+@BUILD_NMTUI_TRUE@nmtui_SOURCES = \
+@BUILD_NMTUI_TRUE@	nmtui.c \
+@BUILD_NMTUI_TRUE@	nmtui.h \
+@BUILD_NMTUI_TRUE@	nmtui-connect.c \
+@BUILD_NMTUI_TRUE@	nmtui-connect.h \
+@BUILD_NMTUI_TRUE@	nmtui-edit.c \
+@BUILD_NMTUI_TRUE@	nmtui-edit.h \
+@BUILD_NMTUI_TRUE@	nmtui-hostname.c \
+@BUILD_NMTUI_TRUE@	nmtui-hostname.h \
+@BUILD_NMTUI_TRUE@	\
+@BUILD_NMTUI_TRUE@	nm-editor-bindings.c \
+@BUILD_NMTUI_TRUE@	nm-editor-bindings.h \
+@BUILD_NMTUI_TRUE@	nm-editor-utils.c \
+@BUILD_NMTUI_TRUE@	nm-editor-utils.h \
+@BUILD_NMTUI_TRUE@	\
+@BUILD_NMTUI_TRUE@	nmt-address-list.c \
+@BUILD_NMTUI_TRUE@	nmt-address-list.h \
+@BUILD_NMTUI_TRUE@	nmt-connect-connection-list.c \
+@BUILD_NMTUI_TRUE@	nmt-connect-connection-list.h \
+@BUILD_NMTUI_TRUE@	nmt-device-entry.c \
+@BUILD_NMTUI_TRUE@	nmt-device-entry.h \
+@BUILD_NMTUI_TRUE@	nmt-edit-connection-list.c \
+@BUILD_NMTUI_TRUE@	nmt-edit-connection-list.h \
+@BUILD_NMTUI_TRUE@	nmt-editor-grid.c \
+@BUILD_NMTUI_TRUE@	nmt-editor-grid.h \
+@BUILD_NMTUI_TRUE@	nmt-editor-page.c \
+@BUILD_NMTUI_TRUE@	nmt-editor-page.h \
+@BUILD_NMTUI_TRUE@	nmt-editor-page-device.c \
+@BUILD_NMTUI_TRUE@	nmt-editor-page-device.h \
+@BUILD_NMTUI_TRUE@	nmt-editor-section.c \
+@BUILD_NMTUI_TRUE@	nmt-editor-section.h \
+@BUILD_NMTUI_TRUE@	nmt-editor.c \
+@BUILD_NMTUI_TRUE@	nmt-editor.h \
+@BUILD_NMTUI_TRUE@	nmt-ip-entry.c \
+@BUILD_NMTUI_TRUE@	nmt-ip-entry.h \
+@BUILD_NMTUI_TRUE@	nmt-mac-entry.c \
+@BUILD_NMTUI_TRUE@	nmt-mac-entry.h \
+@BUILD_NMTUI_TRUE@	nmt-mtu-entry.c \
+@BUILD_NMTUI_TRUE@	nmt-mtu-entry.h \
+@BUILD_NMTUI_TRUE@	nmt-page-bond.c \
+@BUILD_NMTUI_TRUE@	nmt-page-bond.h \
+@BUILD_NMTUI_TRUE@	nmt-page-bridge.c \
+@BUILD_NMTUI_TRUE@	nmt-page-bridge.h \
+@BUILD_NMTUI_TRUE@	nmt-page-bridge-port.c \
+@BUILD_NMTUI_TRUE@	nmt-page-bridge-port.h \
+@BUILD_NMTUI_TRUE@	nmt-page-dsl.c \
+@BUILD_NMTUI_TRUE@	nmt-page-dsl.h \
+@BUILD_NMTUI_TRUE@	nmt-page-ethernet.c \
+@BUILD_NMTUI_TRUE@	nmt-page-ethernet.h \
+@BUILD_NMTUI_TRUE@	nmt-page-infiniband.c \
+@BUILD_NMTUI_TRUE@	nmt-page-infiniband.h \
+@BUILD_NMTUI_TRUE@	nmt-page-ip4.c \
+@BUILD_NMTUI_TRUE@	nmt-page-ip4.h \
+@BUILD_NMTUI_TRUE@	nmt-page-ip6.c \
+@BUILD_NMTUI_TRUE@	nmt-page-ip6.h \
+@BUILD_NMTUI_TRUE@	nmt-page-ppp.c \
+@BUILD_NMTUI_TRUE@	nmt-page-ppp.h \
+@BUILD_NMTUI_TRUE@	nmt-page-team.c \
+@BUILD_NMTUI_TRUE@	nmt-page-team.h \
+@BUILD_NMTUI_TRUE@	nmt-page-team-port.c \
+@BUILD_NMTUI_TRUE@	nmt-page-team-port.h \
+@BUILD_NMTUI_TRUE@	nmt-page-vlan.c \
+@BUILD_NMTUI_TRUE@	nmt-page-vlan.h \
+@BUILD_NMTUI_TRUE@	nmt-page-wifi.c \
+@BUILD_NMTUI_TRUE@	nmt-page-wifi.h \
+@BUILD_NMTUI_TRUE@	nmt-password-dialog.c \
+@BUILD_NMTUI_TRUE@	nmt-password-dialog.h \
+@BUILD_NMTUI_TRUE@	nmt-password-fields.c \
+@BUILD_NMTUI_TRUE@	nmt-password-fields.h \
+@BUILD_NMTUI_TRUE@	nmt-route-editor.c \
+@BUILD_NMTUI_TRUE@	nmt-route-editor.h \
+@BUILD_NMTUI_TRUE@	nmt-route-entry.c \
+@BUILD_NMTUI_TRUE@	nmt-route-entry.h \
+@BUILD_NMTUI_TRUE@	nmt-route-table.c \
+@BUILD_NMTUI_TRUE@	nmt-route-table.h \
+@BUILD_NMTUI_TRUE@	nmt-slave-list.c \
+@BUILD_NMTUI_TRUE@	nmt-slave-list.h \
+@BUILD_NMTUI_TRUE@	nmt-utils.c \
+@BUILD_NMTUI_TRUE@	nmt-utils.h \
+@BUILD_NMTUI_TRUE@	nmt-widget-list.c \
+@BUILD_NMTUI_TRUE@	nmt-widget-list.h \
+@BUILD_NMTUI_TRUE@	$(srcdir)/../common/nm-secret-agent-simple.c \
+@BUILD_NMTUI_TRUE@	$(srcdir)/../common/nm-secret-agent-simple.h \
+@BUILD_NMTUI_TRUE@	$(NULL)
+
+@BUILD_NMTUI_TRUE@nmtui_LDADD = \
+@BUILD_NMTUI_TRUE@	$(top_builddir)/libnm/libnm.la \
+@BUILD_NMTUI_TRUE@	$(builddir)/newt/libnmt-newt.a \
+@BUILD_NMTUI_TRUE@	$(GUDEV_LIBS) \
+@BUILD_NMTUI_TRUE@	$(NEWT_LIBS) \
+@BUILD_NMTUI_TRUE@	$(GLIB_LIBS) \
+@BUILD_NMTUI_TRUE@	$(NULL)
+
+all: all-recursive
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu clients/tui/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu clients/tui/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-binPROGRAMS: $(bin_PROGRAMS)
+	@$(NORMAL_INSTALL)
+	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+	if test -n "$$list"; then \
+	  echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+	  $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+	fi; \
+	for p in $$list; do echo "$$p $$p"; done | \
+	sed 's/$(EXEEXT)$$//' | \
+	while read p p1; do if test -f $$p \
+	 || test -f $$p1 \
+	  ; then echo "$$p"; echo "$$p"; else :; fi; \
+	done | \
+	sed -e 'p;s,.*/,,;n;h' \
+	    -e 's|.*|.|' \
+	    -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+	sed 'N;N;N;s,\n, ,g' | \
+	$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+	  { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+	    if ($$2 == $$4) files[d] = files[d] " " $$1; \
+	    else { print "f", $$3 "/" $$4, $$1; } } \
+	  END { for (d in files) print "f", d, files[d] }' | \
+	while read type dir files; do \
+	    if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+	    test -z "$$files" || { \
+	    echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+	    $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+	    } \
+	; done
+
+uninstall-binPROGRAMS:
+	@$(NORMAL_UNINSTALL)
+	@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+	files=`for p in $$list; do echo "$$p"; done | \
+	  sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+	      -e 's/$$/$(EXEEXT)/' \
+	`; \
+	test -n "$$list" || exit 0; \
+	echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+	cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+	@list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+	echo " rm -f" $$list; \
+	rm -f $$list || exit $$?; \
+	test -n "$(EXEEXT)" || exit 0; \
+	list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+	echo " rm -f" $$list; \
+	rm -f $$list
+
+nmtui$(EXEEXT): $(nmtui_OBJECTS) $(nmtui_DEPENDENCIES) $(EXTRA_nmtui_DEPENDENCIES) 
+	@rm -f nmtui$(EXEEXT)
+	$(AM_V_CCLD)$(LINK) $(nmtui_OBJECTS) $(nmtui_LDADD) $(LIBS)
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-editor-bindings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-editor-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nm-secret-agent-simple.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-address-list.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-connect-connection-list.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-device-entry.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-edit-connection-list.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-editor-grid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-editor-page-device.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-editor-page.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-editor-section.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-editor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-ip-entry.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-mac-entry.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-mtu-entry.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-bond.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-bridge-port.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-bridge.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-dsl.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-ethernet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-infiniband.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-ip4.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-ip6.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-ppp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-team-port.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-team.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-vlan.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-page-wifi.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-password-dialog.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-password-fields.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-route-editor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-route-entry.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-route-table.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-slave-list.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-widget-list.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmtui-connect.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmtui-edit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmtui-hostname.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmtui.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+nm-secret-agent-simple.o: $(srcdir)/../common/nm-secret-agent-simple.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-secret-agent-simple.o -MD -MP -MF $(DEPDIR)/nm-secret-agent-simple.Tpo -c -o nm-secret-agent-simple.o `test -f '$(srcdir)/../common/nm-secret-agent-simple.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-secret-agent-simple.c
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-secret-agent-simple.Tpo $(DEPDIR)/nm-secret-agent-simple.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(srcdir)/../common/nm-secret-agent-simple.c' object='nm-secret-agent-simple.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-secret-agent-simple.o `test -f '$(srcdir)/../common/nm-secret-agent-simple.c' || echo '$(srcdir)/'`$(srcdir)/../common/nm-secret-agent-simple.c
+
+nm-secret-agent-simple.obj: $(srcdir)/../common/nm-secret-agent-simple.c
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-secret-agent-simple.obj -MD -MP -MF $(DEPDIR)/nm-secret-agent-simple.Tpo -c -o nm-secret-agent-simple.obj `if test -f '$(srcdir)/../common/nm-secret-agent-simple.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-secret-agent-simple.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-secret-agent-simple.c'; fi`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/nm-secret-agent-simple.Tpo $(DEPDIR)/nm-secret-agent-simple.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$(srcdir)/../common/nm-secret-agent-simple.c' object='nm-secret-agent-simple.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-secret-agent-simple.obj `if test -f '$(srcdir)/../common/nm-secret-agent-simple.c'; then $(CYGPATH_W) '$(srcdir)/../common/nm-secret-agent-simple.c'; else $(CYGPATH_W) '$(srcdir)/$(srcdir)/../common/nm-secret-agent-simple.c'; fi`
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+#     (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+	@fail=; \
+	if $(am__make_keepgoing); then \
+	  failcom='fail=yes'; \
+	else \
+	  failcom='exit 1'; \
+	fi; \
+	dot_seen=no; \
+	target=`echo $@ | sed s/-recursive//`; \
+	case "$@" in \
+	  distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+	  *) list='$(SUBDIRS)' ;; \
+	esac; \
+	for subdir in $$list; do \
+	  echo "Making $$target in $$subdir"; \
+	  if test "$$subdir" = "."; then \
+	    dot_seen=yes; \
+	    local_target="$$target-am"; \
+	  else \
+	    local_target="$$target"; \
+	  fi; \
+	  ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+	  || eval $$failcom; \
+	done; \
+	if test "$$dot_seen" = "no"; then \
+	  $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+	fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+	  include_option=--etags-include; \
+	  empty_fix=.; \
+	else \
+	  include_option=--include; \
+	  empty_fix=; \
+	fi; \
+	list='$(SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    test ! -f $$subdir/TAGS || \
+	      set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+	  fi; \
+	done; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+	@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+	  if test "$$subdir" = .; then :; else \
+	    $(am__make_dryrun) \
+	      || test -d "$(distdir)/$$subdir" \
+	      || $(MKDIR_P) "$(distdir)/$$subdir" \
+	      || exit 1; \
+	    dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+	    $(am__relativize); \
+	    new_distdir=$$reldir; \
+	    dir1=$$subdir; dir2="$(top_distdir)"; \
+	    $(am__relativize); \
+	    new_top_distdir=$$reldir; \
+	    echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+	    echo "     am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+	    ($(am__cd) $$subdir && \
+	      $(MAKE) $(AM_MAKEFLAGS) \
+	        top_distdir="$$new_top_distdir" \
+	        distdir="$$new_distdir" \
+		am__remove_distdir=: \
+		am__skip_length_check=: \
+		am__skip_mode_fix=: \
+	        distdir) \
+	      || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-recursive
+all-am: Makefile $(PROGRAMS)
+installdirs: installdirs-recursive
+installdirs-am:
+	for dir in "$(DESTDIR)$(bindir)"; do \
+	  test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+	done
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+@BUILD_NMTUI_FALSE@install-exec-hook:
+@BUILD_NMTUI_FALSE@uninstall-hook:
+clean: clean-recursive
+
+clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
+
+distclean: distclean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS
+	@$(NORMAL_INSTALL)
+	$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS
+	@$(NORMAL_INSTALL)
+	$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: $(am__recursive_targets) install-am install-exec-am \
+	install-strip uninstall-am
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \
+	check-am clean clean-binPROGRAMS clean-generic clean-libtool \
+	cscopelist-am ctags ctags-am distclean distclean-compile \
+	distclean-generic distclean-libtool distclean-tags distdir dvi \
+	dvi-am html html-am info info-am install install-am \
+	install-binPROGRAMS install-data install-data-am install-dvi \
+	install-dvi-am install-exec install-exec-am install-exec-hook \
+	install-html install-html-am install-info install-info-am \
+	install-man install-pdf install-pdf-am install-ps \
+	install-ps-am install-strip installcheck installcheck-am \
+	installdirs installdirs-am maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \
+	uninstall-hook
+
+
+@BUILD_NMTUI_TRUE@install-exec-hook:
+@BUILD_NMTUI_TRUE@	for link in $(links); do \
+@BUILD_NMTUI_TRUE@		ln -f $(DESTDIR)$(bindir)/nmtui $(DESTDIR)$(bindir)/$$link; \
+@BUILD_NMTUI_TRUE@	done
+
+@BUILD_NMTUI_TRUE@uninstall-hook:
+@BUILD_NMTUI_TRUE@	for link in $(links); do \
+@BUILD_NMTUI_TRUE@		rm -f $(DESTDIR)$(bindir)/$$link; \
+@BUILD_NMTUI_TRUE@	done
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/clients/tui/newt/Makefile.am b/clients/tui/newt/Makefile.am
new file mode 100644
index 00000000..e572238b
--- /dev/null
+++ b/clients/tui/newt/Makefile.am
@@ -0,0 +1,53 @@
+AM_CPPFLAGS= \
+	-I$(top_srcdir)/include \
+	-DG_LOG_DOMAIN=\""nmtui"\" \
+	$(GLIB_CFLAGS) \
+	$(NEWT_CFLAGS) \
+	$(NULL)
+
+noinst_LIBRARIES = libnmt-newt.a
+
+libnmt_newt_a_SOURCES = \
+	nmt-newt.h \
+	nmt-newt-types.h \
+	nmt-newt-button.c \
+	nmt-newt-button.h \
+	nmt-newt-button-box.c \
+	nmt-newt-button-box.h \
+	nmt-newt-checkbox.c \
+	nmt-newt-checkbox.h \
+	nmt-newt-component.c \
+	nmt-newt-component.h \
+	nmt-newt-container.c \
+	nmt-newt-container.h \
+	nmt-newt-entry.c \
+	nmt-newt-entry.h \
+	nmt-newt-entry-numeric.c \
+	nmt-newt-entry-numeric.h \
+	nmt-newt-form.c \
+	nmt-newt-form.h \
+	nmt-newt-grid.c \
+	nmt-newt-grid.h \
+	nmt-newt-hacks.c \
+	nmt-newt-hacks.h \
+	nmt-newt-label.c \
+	nmt-newt-label.h \
+	nmt-newt-listbox.c \
+	nmt-newt-listbox.h \
+	nmt-newt-popup.c \
+	nmt-newt-popup.h \
+	nmt-newt-section.c \
+	nmt-newt-section.h \
+	nmt-newt-separator.c \
+	nmt-newt-separator.h \
+	nmt-newt-stack.c \
+	nmt-newt-stack.h \
+	nmt-newt-textbox.c \
+	nmt-newt-textbox.h \
+	nmt-newt-toggle-button.c \
+	nmt-newt-toggle-button.h \
+	nmt-newt-utils.c \
+	nmt-newt-utils.h \
+	nmt-newt-widget.c \
+	nmt-newt-widget.h \
+	$(NULL)
diff --git a/clients/tui/newt/Makefile.in b/clients/tui/newt/Makefile.in
new file mode 100644
index 00000000..64abce69
--- /dev/null
+++ b/clients/tui/newt/Makefile.in
@@ -0,0 +1,805 @@
+# Makefile.in generated by automake 1.13.4 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+  case $${target_option-} in \
+      ?) ;; \
+      *) echo "am__make_running_with_option: internal error: invalid" \
+              "target option '$${target_option-}' specified" >&2; \
+         exit 1;; \
+  esac; \
+  has_opt=no; \
+  sane_makeflags=$$MAKEFLAGS; \
+  if $(am__is_gnu_make); then \
+    sane_makeflags=$$MFLAGS; \
+  else \
+    case $$MAKEFLAGS in \
+      *\\[\ \	]*) \
+        bs=\\; \
+        sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+          | sed "s/$$bs$$bs[$$bs $$bs	]*//g"`;; \
+    esac; \
+  fi; \
+  skip_next=no; \
+  strip_trailopt () \
+  { \
+    flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+  }; \
+  for flg in $$sane_makeflags; do \
+    test $$skip_next = yes && { skip_next=no; continue; }; \
+    case $$flg in \
+      *=*|--*) continue;; \
+        -*I) strip_trailopt 'I'; skip_next=yes;; \
+      -*I?*) strip_trailopt 'I';; \
+        -*O) strip_trailopt 'O'; skip_next=yes;; \
+      -*O?*) strip_trailopt 'O';; \
+        -*l) strip_trailopt 'l'; skip_next=yes;; \
+      -*l?*) strip_trailopt 'l';; \
+      -[dEDm]) skip_next=yes;; \
+      -[JT]) skip_next=yes;; \
+    esac; \
+    case $$flg in \
+      *$$target_option*) has_opt=yes; break;; \
+    esac; \
+  done; \
+  test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = clients/tui/newt
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+	$(top_srcdir)/build-aux/depcomp
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \
+	$(top_srcdir)/m4/compiler_warnings.m4 \
+	$(top_srcdir)/m4/gettext.m4 \
+	$(top_srcdir)/m4/gnome-code-coverage.m4 \
+	$(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \
+	$(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/intltool.m4 \
+	$(top_srcdir)/m4/introspection.m4 $(top_srcdir)/m4/lib-ld.m4 \
+	$(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \
+	$(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+	$(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+	$(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \
+	$(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \
+	$(top_srcdir)/m4/vapigen.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+	$(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+LIBRARIES = $(noinst_LIBRARIES)
+ARFLAGS = cru
+AM_V_AR = $(am__v_AR_@AM_V@)
+am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@)
+am__v_AR_0 = @echo "  AR      " $@;
+am__v_AR_1 = 
+libnmt_newt_a_AR = $(AR) $(ARFLAGS)
+libnmt_newt_a_LIBADD =
+am_libnmt_newt_a_OBJECTS = nmt-newt-button.$(OBJEXT) \
+	nmt-newt-button-box.$(OBJEXT) nmt-newt-checkbox.$(OBJEXT) \
+	nmt-newt-component.$(OBJEXT) nmt-newt-container.$(OBJEXT) \
+	nmt-newt-entry.$(OBJEXT) nmt-newt-entry-numeric.$(OBJEXT) \
+	nmt-newt-form.$(OBJEXT) nmt-newt-grid.$(OBJEXT) \
+	nmt-newt-hacks.$(OBJEXT) nmt-newt-label.$(OBJEXT) \
+	nmt-newt-listbox.$(OBJEXT) nmt-newt-popup.$(OBJEXT) \
+	nmt-newt-section.$(OBJEXT) nmt-newt-separator.$(OBJEXT) \
+	nmt-newt-stack.$(OBJEXT) nmt-newt-textbox.$(OBJEXT) \
+	nmt-newt-toggle-button.$(OBJEXT) nmt-newt-utils.$(OBJEXT) \
+	nmt-newt-widget.$(OBJEXT)
+libnmt_newt_a_OBJECTS = $(am_libnmt_newt_a_OBJECTS)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo "  GEN     " $@;
+am__v_GEN_1 = 
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 = 
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+	$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 = 
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+	$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+	$(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo "  CC      " $@;
+am__v_CC_1 = 
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+	$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+	$(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo "  CCLD    " $@;
+am__v_CCLD_1 = 
+SOURCES = $(libnmt_newt_a_SOURCES)
+DIST_SOURCES = $(libnmt_newt_a_SOURCES)
+am__can_run_installinfo = \
+  case $$AM_UPDATE_INFO_DIR in \
+    n|no|NO) false;; \
+    *) (install-info --version) >/dev/null 2>&1;; \
+  esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates.  Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+  BEGIN { nonempty = 0; } \
+  { items[$$0] = 1; nonempty = 1; } \
+  END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique.  This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+  list='$(am__tagged_files)'; \
+  unique=`for i in $$list; do \
+    if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+  done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALL_LINGUAS = @ALL_LINGUAS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BLUEZ5_CFLAGS = @BLUEZ5_CFLAGS@
+BLUEZ5_LIBS = @BLUEZ5_LIBS@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CKDB_PATH = @CKDB_PATH@
+CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
+CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
+CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DBUS_CFLAGS = @DBUS_CFLAGS@
+DBUS_GLIB_100_CFLAGS = @DBUS_GLIB_100_CFLAGS@
+DBUS_GLIB_100_LIBS = @DBUS_GLIB_100_LIBS@
+DBUS_LIBS = @DBUS_LIBS@
+DBUS_SYS_DIR = @DBUS_SYS_DIR@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DHCLIENT_PATH = @DHCLIENT_PATH@
+DHCPCD_PATH = @DHCPCD_PATH@
+DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@
+DLLTOOL = @DLLTOOL@
+DNSMASQ_PATH = @DNSMASQ_PATH@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GENHTML = @GENHTML@
+GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@
+GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MAKEFILE = @GLIB_MAKEFILE@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUTLS_CFLAGS = @GNUTLS_CFLAGS@
+GNUTLS_LIBS = @GNUTLS_LIBS@
+GREP = @GREP@
+GTKDOC_CHECK = @GTKDOC_CHECK@
+GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@
+GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@
+GTKDOC_MKPDF = @GTKDOC_MKPDF@
+GTKDOC_REBASE = @GTKDOC_REBASE@
+GUDEV_CFLAGS = @GUDEV_CFLAGS@
+GUDEV_LIBS = @GUDEV_LIBS@
+HTML_DIR = @HTML_DIR@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTLTOOL_EXTRACT = @INTLTOOL_EXTRACT@
+INTLTOOL_MERGE = @INTLTOOL_MERGE@
+INTLTOOL_PERL = @INTLTOOL_PERL@
+INTLTOOL_UPDATE = @INTLTOOL_UPDATE@
+INTLTOOL_V_MERGE = @INTLTOOL_V_MERGE@
+INTLTOOL_V_MERGE_OPTIONS = @INTLTOOL_V_MERGE_OPTIONS@
+INTLTOOL__v_MERGE_ = @INTLTOOL__v_MERGE_@
+INTLTOOL__v_MERGE_0 = @INTLTOOL__v_MERGE_0@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+INTROSPECTION_CFLAGS = @INTROSPECTION_CFLAGS@
+INTROSPECTION_COMPILER = @INTROSPECTION_COMPILER@
+INTROSPECTION_GENERATE = @INTROSPECTION_GENERATE@
+INTROSPECTION_GIRDIR = @INTROSPECTION_GIRDIR@
+INTROSPECTION_LIBS = @INTROSPECTION_LIBS@
+INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@
+INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@
+INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@
+IPTABLES_PATH = @IPTABLES_PATH@
+IWMX_SDK_CFLAGS = @IWMX_SDK_CFLAGS@
+IWMX_SDK_LIBS = @IWMX_SDK_LIBS@
+KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@
+LCOV = @LCOV@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBDL = @LIBDL@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBM = @LIBM@
+LIBNDP_CFLAGS = @LIBNDP_CFLAGS@
+LIBNDP_LIBS = @LIBNDP_LIBS@
+LIBNL_CFLAGS = @LIBNL_CFLAGS@
+LIBNL_LIBS = @LIBNL_LIBS@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@
+LIBSOUP_LIBS = @LIBSOUP_LIBS@
+LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@
+LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MM_GLIB_CFLAGS = @MM_GLIB_CFLAGS@
+MM_GLIB_LIBS = @MM_GLIB_LIBS@
+MOC = @MOC@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+NEWT_CFLAGS = @NEWT_CFLAGS@
+NEWT_LIBS = @NEWT_LIBS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT = @NM_CONFIG_DEFAULT_AUTH_POLKIT_TEXT@
+NM_MAJOR_VERSION = @NM_MAJOR_VERSION@
+NM_MICRO_VERSION = @NM_MICRO_VERSION@
+NM_MINOR_VERSION = @NM_MINOR_VERSION@
+NM_MODIFY_SYSTEM_POLICY = @NM_MODIFY_SYSTEM_POLICY@
+NM_VERSION = @NM_VERSION@
+NSS_CFLAGS = @NSS_CFLAGS@
+NSS_LIBS = @NSS_LIBS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+POLKIT_CFLAGS = @POLKIT_CFLAGS@
+POLKIT_LIBS = @POLKIT_LIBS@
+POSUB = @POSUB@
+PPPD_PATH = @PPPD_PATH@
+PPPD_PLUGIN_DIR = @PPPD_PLUGIN_DIR@
+PPPOE_PATH = @PPPOE_PATH@
+QT_CFLAGS = @QT_CFLAGS@
+QT_LIBS = @QT_LIBS@
+RANLIB = @RANLIB@
+READLINE_LIBS = @READLINE_LIBS@
+SED = @SED@
+SELINUX_CFLAGS = @SELINUX_CFLAGS@
+SELINUX_LIBS = @SELINUX_LIBS@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SYSTEMD_200_CFLAGS = @SYSTEMD_200_CFLAGS@
+SYSTEMD_200_LIBS = @SYSTEMD_200_LIBS@
+SYSTEMD_INHIBIT_CFLAGS = @SYSTEMD_INHIBIT_CFLAGS@
+SYSTEMD_INHIBIT_LIBS = @SYSTEMD_INHIBIT_LIBS@
+SYSTEMD_LOGIN_CFLAGS = @SYSTEMD_LOGIN_CFLAGS@
+SYSTEMD_LOGIN_LIBS = @SYSTEMD_LOGIN_LIBS@
+SYSTEM_CA_PATH = @SYSTEM_CA_PATH@
+UDEV_DIR = @UDEV_DIR@
+USE_NLS = @USE_NLS@
+UUID_CFLAGS = @UUID_CFLAGS@
+UUID_LIBS = @UUID_LIBS@
+VALGRIND_RULES = @VALGRIND_RULES@
+VAPIGEN = @VAPIGEN@
+VAPIGEN_MAKEFILE = @VAPIGEN_MAKEFILE@
+VAPIGEN_VAPIDIR = @VAPIGEN_VAPIDIR@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+intltool__v_merge_options_ = @intltool__v_merge_options_@
+intltool__v_merge_options_0 = @intltool__v_merge_options_0@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+nmbinary = @nmbinary@
+nmconfdir = @nmconfdir@
+nmdatadir = @nmdatadir@
+nmrundir = @nmrundir@
+nmstatedir = @nmstatedir@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+systemdsystemunitdir = @systemdsystemunitdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+with_dhclient = @with_dhclient@
+with_dhcpcd = @with_dhcpcd@
+with_netconfig = @with_netconfig@
+with_resolvconf = @with_resolvconf@
+with_valgrind = @with_valgrind@
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/include \
+	-DG_LOG_DOMAIN=\""nmtui"\" \
+	$(GLIB_CFLAGS) \
+	$(NEWT_CFLAGS) \
+	$(NULL)
+
+noinst_LIBRARIES = libnmt-newt.a
+libnmt_newt_a_SOURCES = \
+	nmt-newt.h \
+	nmt-newt-types.h \
+	nmt-newt-button.c \
+	nmt-newt-button.h \
+	nmt-newt-button-box.c \
+	nmt-newt-button-box.h \
+	nmt-newt-checkbox.c \
+	nmt-newt-checkbox.h \
+	nmt-newt-component.c \
+	nmt-newt-component.h \
+	nmt-newt-container.c \
+	nmt-newt-container.h \
+	nmt-newt-entry.c \
+	nmt-newt-entry.h \
+	nmt-newt-entry-numeric.c \
+	nmt-newt-entry-numeric.h \
+	nmt-newt-form.c \
+	nmt-newt-form.h \
+	nmt-newt-grid.c \
+	nmt-newt-grid.h \
+	nmt-newt-hacks.c \
+	nmt-newt-hacks.h \
+	nmt-newt-label.c \
+	nmt-newt-label.h \
+	nmt-newt-listbox.c \
+	nmt-newt-listbox.h \
+	nmt-newt-popup.c \
+	nmt-newt-popup.h \
+	nmt-newt-section.c \
+	nmt-newt-section.h \
+	nmt-newt-separator.c \
+	nmt-newt-separator.h \
+	nmt-newt-stack.c \
+	nmt-newt-stack.h \
+	nmt-newt-textbox.c \
+	nmt-newt-textbox.h \
+	nmt-newt-toggle-button.c \
+	nmt-newt-toggle-button.h \
+	nmt-newt-utils.c \
+	nmt-newt-utils.h \
+	nmt-newt-widget.c \
+	nmt-newt-widget.h \
+	$(NULL)
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am  $(am__configure_deps)
+	@for dep in $?; do \
+	  case '$(am__configure_deps)' in \
+	    *$$dep*) \
+	      ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+	        && { if test -f $@; then exit 0; else break; fi; }; \
+	      exit 1;; \
+	  esac; \
+	done; \
+	echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu clients/tui/newt/Makefile'; \
+	$(am__cd) $(top_srcdir) && \
+	  $(AUTOMAKE) --gnu clients/tui/newt/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+	@case '$?' in \
+	  *config.status*) \
+	    cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+	  *) \
+	    echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+	    cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+	esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+	cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+clean-noinstLIBRARIES:
+	-test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES)
+
+libnmt-newt.a: $(libnmt_newt_a_OBJECTS) $(libnmt_newt_a_DEPENDENCIES) $(EXTRA_libnmt_newt_a_DEPENDENCIES) 
+	$(AM_V_at)-rm -f libnmt-newt.a
+	$(AM_V_AR)$(libnmt_newt_a_AR) libnmt-newt.a $(libnmt_newt_a_OBJECTS) $(libnmt_newt_a_LIBADD)
+	$(AM_V_at)$(RANLIB) libnmt-newt.a
+
+mostlyclean-compile:
+	-rm -f *.$(OBJEXT)
+
+distclean-compile:
+	-rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-button-box.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-button.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-checkbox.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-component.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-container.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-entry-numeric.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-entry.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-form.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-grid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-hacks.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-label.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-listbox.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-popup.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-section.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-separator.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-stack.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-textbox.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-toggle-button.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-utils.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nmt-newt-widget.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	$(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+	-rm -f *.lo
+
+clean-libtool:
+	-rm -rf .libs _libs
+
+ID: $(am__tagged_files)
+	$(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	set x; \
+	here=`pwd`; \
+	$(am__define_uniq_tagged_files); \
+	shift; \
+	if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+	  test -n "$$unique" || unique=$$empty_fix; \
+	  if test $$# -gt 0; then \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      "$$@" $$unique; \
+	  else \
+	    $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+	      $$unique; \
+	  fi; \
+	fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+	$(am__define_uniq_tagged_files); \
+	test -z "$(CTAGS_ARGS)$$unique" \
+	  || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+	     $$unique
+
+GTAGS:
+	here=`$(am__cd) $(top_builddir) && pwd` \
+	  && $(am__cd) $(top_srcdir) \
+	  && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+	list='$(am__tagged_files)'; \
+	case "$(srcdir)" in \
+	  [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+	  *) sdir=$(subdir)/$(srcdir) ;; \
+	esac; \
+	for i in $$list; do \
+	  if test -f "$$i"; then \
+	    echo "$(subdir)/$$i"; \
+	  else \
+	    echo "$$sdir/$$i"; \
+	  fi; \
+	done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+	-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+	@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+	list='$(DISTFILES)'; \
+	  dist_files=`for file in $$list; do echo $$file; done | \
+	  sed -e "s|^$$srcdirstrip/||;t" \
+	      -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+	case $$dist_files in \
+	  */*) $(MKDIR_P) `echo "$$dist_files" | \
+			   sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+			   sort -u` ;; \
+	esac; \
+	for file in $$dist_files; do \
+	  if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+	  if test -d $$d/$$file; then \
+	    dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+	    if test -d "$(distdir)/$$file"; then \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+	      cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+	      find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+	    fi; \
+	    cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+	  else \
+	    test -f "$(distdir)/$$file" \
+	    || cp -p $$d/$$file "$(distdir)/$$file" \
+	    || exit 1; \
+	  fi; \
+	done
+check-am: all-am
+check: check-am
+all-am: Makefile $(LIBRARIES)
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+	@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+	if test -z '$(STRIP)'; then \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	      install; \
+	else \
+	  $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+	    install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+	    "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+	fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+	-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+	-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+	@echo "This command is intended for maintainers to use"
+	@echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \
+	mostlyclean-am
+
+distclean: distclean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+	distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+	-rm -rf ./$(DEPDIR)
+	-rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+	mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+	clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \
+	ctags-am distclean distclean-compile distclean-generic \
+	distclean-libtool distclean-tags distdir dvi dvi-am html \
+	html-am info info-am install install-am install-data \
+	install-data-am install-dvi install-dvi-am install-exec \
+	install-exec-am install-html install-html-am install-info \
+	install-info-am install-man install-pdf install-pdf-am \
+	install-ps install-ps-am install-strip installcheck \
+	installcheck-am installdirs maintainer-clean \
+	maintainer-clean-generic mostlyclean mostlyclean-compile \
+	mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+	tags tags-am uninstall uninstall-am
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/clients/tui/newt/nmt-newt-button-box.c b/clients/tui/newt/nmt-newt-button-box.c
new file mode 100644
index 00000000..4fba1daf
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-button-box.c
@@ -0,0 +1,386 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-button-box
+ * @short_description: A container for #NmtNewtButtons
+ *
+ * #NmtNewtButtonBox is a container for creating and holding
+ * #NmtNewtButtons.
+ *
+ * A button box can be either horizontally or vertically laid out, and
+ * has two sections within it: the "start" (left or top) and "end"
+ * (right or bottom). Buttons are added from left to right or top to bottom
+ * within each of the two sections.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-button-box.h"
+#include "nmt-newt-button.h"
+
+G_DEFINE_TYPE (NmtNewtButtonBox, nmt_newt_button_box, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_NEWT_BUTTON_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxPrivate))
+
+typedef struct {
+	NmtNewtButtonBoxOrientation orientation;
+	GPtrArray *start_buttons, *end_buttons;
+} NmtNewtButtonBoxPrivate;
+
+enum {
+	PROP_0,
+
+	PROP_ORIENTATION,
+
+	LAST_PROP
+};
+
+/**
+ * NmtNewtButtonBoxOrientation:
+ * @NMT_NEWT_BUTTON_BOX_HORIZONTAL: horizontal
+ * @NMT_NEWT_BUTTON_BOX_VERTICAL: vertical
+ *
+ * The orientation of an #NmtNewtButtonBox
+ */
+
+/**
+ * nmt_newt_button_box_new:
+ * @orientation: the orientation
+ *
+ * Creates a new #NmtNewtButtonBox
+ *
+ * Returns: a new #NmtNewtButtonBox
+ */
+NmtNewtWidget *
+nmt_newt_button_box_new (NmtNewtButtonBoxOrientation orientation)
+{
+	return g_object_new (NMT_TYPE_NEWT_BUTTON_BOX,
+	                     "orientation", orientation,
+	                     NULL);
+}
+
+static void
+nmt_newt_button_box_init (NmtNewtButtonBox *bbox)
+{
+	NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox);
+
+	priv->start_buttons = g_ptr_array_new ();
+	priv->end_buttons = g_ptr_array_new ();
+}
+
+/**
+ * nmt_newt_button_box_add_start:
+ * @bbox: an #NmtNewtButtonBox
+ * @label: the label for the newt button
+ *
+ * Creates a new #NmtNewtButton with the given @label, adds it
+ * to the "start" section of @bbox, and returns the newly-created
+ * button.
+ *
+ * Returns: the newly-created button, already added to @bbox
+ */
+NmtNewtWidget *
+nmt_newt_button_box_add_start (NmtNewtButtonBox *bbox,
+                               const char       *label)
+{
+	NmtNewtWidget *button;
+
+	button = nmt_newt_button_new (label);
+	nmt_newt_button_box_add_widget_start (bbox, button);
+	return button;
+}
+
+/**
+ * nmt_newt_button_box_add_widget_start:
+ * @bbox: an #NmtNewtButtonBox
+ * @widget: the #NmtNewtWidget to add
+ *
+ * Adds the given widget to the "start" section of @bbox.
+ */
+void
+nmt_newt_button_box_add_widget_start (NmtNewtButtonBox *bbox,
+                                      NmtNewtWidget    *widget)
+{
+	NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox);
+
+	NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)->
+		add (NMT_NEWT_CONTAINER (bbox), widget);
+	g_ptr_array_add (priv->start_buttons, widget);
+}
+
+/**
+ * nmt_newt_button_box_add_end:
+ * @bbox: an #NmtNewtButtonBox
+ * @label: the label for the newt button
+ *
+ * Creates a new #NmtNewtButton with the given @label, adds it
+ * to the "end" section of @bbox, and returns the newly-created
+ * button.
+ *
+ * Returns: the newly-created button, already added to @bbox
+ */
+NmtNewtWidget *
+nmt_newt_button_box_add_end (NmtNewtButtonBox *bbox,
+                             const char       *label)
+{
+	NmtNewtWidget *button;
+
+	button = nmt_newt_button_new (label);
+	nmt_newt_button_box_add_widget_end (bbox, button);
+	return button;
+}
+
+/**
+ * nmt_newt_button_box_add_widget_end:
+ * @bbox: an #NmtNewtButtonBox
+ * @widget: the #NmtNewtWidget to add
+ *
+ * Adds the given widget to the "end" section of @bbox.
+ */
+void
+nmt_newt_button_box_add_widget_end (NmtNewtButtonBox *bbox,
+                                    NmtNewtWidget    *widget)
+{
+	NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox);
+
+	NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)->
+		add (NMT_NEWT_CONTAINER (bbox), widget);
+	g_ptr_array_add (priv->end_buttons, widget);
+}
+
+static void
+nmt_newt_button_box_remove (NmtNewtContainer *container,
+                            NmtNewtWidget    *child)
+{
+	NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (container);
+	int i;
+
+	NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)->
+		remove (container, child);
+
+	for (i = 0; i < priv->start_buttons->len; i++) {
+		if (priv->start_buttons->pdata[i] == (gpointer) child) {
+			g_ptr_array_remove_index (priv->start_buttons, i);
+			return;
+		}
+	}
+	for (i = 0; i < priv->end_buttons->len; i++) {
+		if (priv->end_buttons->pdata[i] == (gpointer) child) {
+			g_ptr_array_remove_index (priv->end_buttons, i);
+			return;
+		}
+	}
+}
+
+static void
+add_buttons (GPtrArray *buttons, GPtrArray *cos)
+{
+	NmtNewtWidget *child;
+	newtComponent *child_cos;
+	int i, c;
+
+	for (i = 0; i < buttons->len; i++) {
+		child = buttons->pdata[i];
+
+		if (!nmt_newt_widget_get_visible (child))
+			continue;
+
+		child_cos = nmt_newt_widget_get_components (child);
+		for (c = 0; child_cos[c]; c++)
+			g_ptr_array_add (cos, child_cos[c]);
+		g_free (child_cos);
+	}
+}
+
+static newtComponent *
+nmt_newt_button_box_get_components (NmtNewtWidget *widget)
+{
+	NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget);
+	GPtrArray *cos;
+
+	cos = g_ptr_array_new ();
+	add_buttons (priv->start_buttons, cos);
+	add_buttons (priv->end_buttons, cos);
+	g_ptr_array_add (cos, NULL);
+
+	return (newtComponent *) g_ptr_array_free (cos, FALSE);
+}
+
+static void
+size_request_buttons (NmtNewtButtonBox *bbox,
+                      GPtrArray        *buttons,
+                      int              *width,
+                      int              *height)
+{
+	NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox);
+	int child_width, child_height;
+	int i;
+
+	for (i = 0; i < buttons->len; i++) {
+		NmtNewtWidget *child = buttons->pdata[i];
+
+		nmt_newt_widget_size_request (child, &child_width, &child_height);
+		if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) {
+			*width += child_width;
+			if (i > 0)
+				*width += 1;
+			*height = MAX (*height, child_height);
+		} else {
+			*height += child_height;
+			if (i > 0)
+				*height += 1;
+			*width = MAX (*width, child_width);
+		}
+	}
+}
+
+static void
+nmt_newt_button_box_size_request (NmtNewtWidget *widget,
+                                  int           *width,
+                                  int           *height)
+{
+	NmtNewtButtonBox *bbox = NMT_NEWT_BUTTON_BOX (widget);
+	NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget);
+
+	*width = *height = 0;
+	size_request_buttons (bbox, priv->start_buttons, width, height);
+	size_request_buttons (bbox, priv->end_buttons, width, height);
+
+	if (priv->start_buttons && priv->end_buttons) {
+		if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL)
+			*width += 1;
+		else
+			*height += 1;
+	}
+}
+
+static void
+nmt_newt_button_box_size_allocate (NmtNewtWidget *widget,
+                                   int            x,
+                                   int            y,
+                                   int            width,
+                                   int            height)
+{
+	NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget);
+	NmtNewtWidget *child;
+	int child_x, child_y, child_width, child_height;
+	int i;
+
+	child_x = x;
+	child_y = y;
+	for (i = 0; i < priv->start_buttons->len; i++) {
+		child = priv->start_buttons->pdata[i];
+		nmt_newt_widget_size_request (child, &child_width, &child_height);
+
+		if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) {
+			nmt_newt_widget_size_allocate (child, child_x, child_y, child_width, child_height);
+			child_x += child_width + 1;
+		} else {
+			nmt_newt_widget_size_allocate (child, child_x, child_y, child_width, child_height);
+			child_y += child_height + 1;
+		}
+	}
+
+	if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL)
+		child_x = x + width;
+	else
+		child_y = y + height;
+
+	for (i = priv->end_buttons->len - 1; i >= 0; i--) {
+		child = priv->end_buttons->pdata[i];
+		nmt_newt_widget_size_request (child, &child_width, &child_height);
+
+		if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) {
+			nmt_newt_widget_size_allocate (child,
+			                               child_x - child_width, child_y,
+			                               child_width, child_height);
+			child_x -= child_width + 1;
+		} else {
+			nmt_newt_widget_size_allocate (child,
+			                               child_x, child_y - child_height,
+			                               child_width, child_height);
+			child_y -= child_height + 1;
+		}
+	}
+}
+
+static void
+nmt_newt_button_box_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+	NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_ORIENTATION:
+		priv->orientation = g_value_get_int (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_button_box_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+	NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_ORIENTATION:
+		g_value_set_int (value, priv->orientation);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_button_box_class_init (NmtNewtButtonBoxClass *bbox_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (bbox_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (bbox_class);
+	NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (bbox_class);
+
+	g_type_class_add_private (bbox_class, sizeof (NmtNewtButtonBoxPrivate));
+
+	object_class->get_property = nmt_newt_button_box_get_property;
+	object_class->set_property = nmt_newt_button_box_set_property;
+
+	widget_class->get_components = nmt_newt_button_box_get_components;
+	widget_class->size_request   = nmt_newt_button_box_size_request;
+	widget_class->size_allocate  = nmt_newt_button_box_size_allocate;
+
+	container_class->remove = nmt_newt_button_box_remove;
+
+	g_object_class_install_property
+		(object_class, PROP_ORIENTATION,
+		 g_param_spec_int ("orientation", "", "",
+		                   0, G_MAXINT, 0,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_CONSTRUCT_ONLY |
+		                   G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-button-box.h b/clients/tui/newt/nmt-newt-button-box.h
new file mode 100644
index 00000000..970588a4
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-button-box.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_BUTTON_BOX_H
+#define NMT_NEWT_BUTTON_BOX_H
+
+#include "nmt-newt-grid.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_BUTTON_BOX            (nmt_newt_button_box_get_type ())
+#define NMT_NEWT_BUTTON_BOX(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBox))
+#define NMT_NEWT_BUTTON_BOX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxClass))
+#define NMT_IS_NEWT_BUTTON_BOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_BUTTON_BOX))
+#define NMT_IS_NEWT_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_BUTTON_BOX))
+#define NMT_NEWT_BUTTON_BOX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxClass))
+
+struct _NmtNewtButtonBox {
+	NmtNewtContainer parent;
+
+};
+
+typedef struct {
+	NmtNewtContainerClass parent;
+
+} NmtNewtButtonBoxClass;
+
+GType nmt_newt_button_box_get_type (void);
+
+typedef enum {
+	NMT_NEWT_BUTTON_BOX_HORIZONTAL,
+	NMT_NEWT_BUTTON_BOX_VERTICAL
+} NmtNewtButtonBoxOrientation;
+
+NmtNewtWidget *nmt_newt_button_box_new       (NmtNewtButtonBoxOrientation orientation);
+
+NmtNewtWidget *nmt_newt_button_box_add_start (NmtNewtButtonBox *bbox,
+                                              const char       *label);
+NmtNewtWidget *nmt_newt_button_box_add_end   (NmtNewtButtonBox *bbox,
+                                              const char       *label);
+
+void           nmt_newt_button_box_add_widget_start (NmtNewtButtonBox *bbox,
+                                                     NmtNewtWidget    *widget);
+void           nmt_newt_button_box_add_widget_end   (NmtNewtButtonBox *bbox,
+                                                     NmtNewtWidget    *widget);
+
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_BUTTON_BOX_H */
diff --git a/clients/tui/newt/nmt-newt-button.c b/clients/tui/newt/nmt-newt-button.c
new file mode 100644
index 00000000..00dbcd79
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-button.c
@@ -0,0 +1,261 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-button
+ * @short_description: Push buttons
+ *
+ * #NmtNewtButton implements a button widget.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-button.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtButton, nmt_newt_button, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonPrivate))
+
+typedef struct {
+	char *label;
+} NmtNewtButtonPrivate;
+
+enum {
+	PROP_0,
+	PROP_LABEL,
+
+	LAST_PROP
+};
+
+enum {
+	CLICKED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/**
+ * nmt_newt_button_new:
+ * @label: the (initial) button label
+ *
+ * Creates a new button.
+ *
+ * Returns: a new #NmtNewtButton
+ */
+NmtNewtWidget *
+nmt_newt_button_new (const char *label)
+{
+	return g_object_new (NMT_TYPE_NEWT_BUTTON,
+	                     "label", label,
+	                     NULL);
+}
+
+/**
+ * nmt_newt_button_set_label:
+ * @button: an #NmtNewtButton
+ * @label: the new label
+ *
+ * Updates @button's label.
+ */
+void
+nmt_newt_button_set_label (NmtNewtButton *button,
+                           const char    *label)
+{
+	NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (button);
+
+	if (!g_strcmp0 (priv->label, label))
+		return;
+
+	g_free (priv->label);
+	priv->label = g_strdup (label);
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (button));
+}
+
+/**
+ * nmt_newt_button_get_label:
+ * @button: an #NmtNewtButton
+ *
+ * Gets @button's label.
+ *
+ * Returns: @button's label.
+ */
+const char *
+nmt_newt_button_get_label (NmtNewtButton *button)
+{
+	NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (button);
+
+	return priv->label;
+}
+
+static void
+nmt_newt_button_init (NmtNewtButton *button)
+{
+}
+
+static void
+nmt_newt_button_finalize (GObject *object)
+{
+	NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (object);
+
+	g_free (priv->label);
+
+	G_OBJECT_CLASS (nmt_newt_button_parent_class)->finalize (object);
+}
+
+static newtComponent
+nmt_newt_button_build_component (NmtNewtComponent *component,
+                                 gboolean          sensitive)
+{
+	NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (component);
+	newtComponent co;
+	char *label = NULL, *label_lc;
+
+	if (sensitive) {
+		label_lc = nmt_newt_locale_from_utf8 (priv->label);
+		co = newtCompactButton (-1, -1, label_lc);
+		g_free (label_lc);
+	} else {
+		label = g_strdup_printf (" <%s>", priv->label);
+		label_lc = nmt_newt_locale_from_utf8 (label);
+		co = newtLabel (-1, -1, label_lc);
+		g_free (label_lc);
+		newtLabelSetColors (co, NMT_NEWT_COLORSET_DISABLED_BUTTON);
+	}
+
+	return co;
+}
+
+static void
+nmt_newt_button_size_request (NmtNewtWidget *widget,
+                              int           *width,
+                              int           *height)
+{
+	NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->size_request (widget, width, height);
+
+	/* remove the automatically-added left padding */
+	(*width)--;
+}
+
+static void
+nmt_newt_button_size_allocate (NmtNewtWidget *widget,
+                               int            x,
+                               int            y,
+                               int            width,
+                               int            height)
+{
+	/* account for the automatically-added left padding */
+	x--;
+	width++;
+
+	NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->size_allocate (widget, x, y, width, height);
+}
+
+static void
+nmt_newt_button_activated (NmtNewtWidget *widget)
+{
+	g_signal_emit (widget, signals[CLICKED], 0);
+
+	NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->activated (widget);
+}
+
+static void
+nmt_newt_button_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+	switch (prop_id) {
+	case PROP_LABEL:
+		nmt_newt_button_set_label (NMT_NEWT_BUTTON (object),
+		                           g_value_get_string (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_button_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+	NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_LABEL:
+		g_value_set_string (value, priv->label);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_button_class_init (NmtNewtButtonClass *button_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (button_class);
+	NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (button_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (button_class);
+
+	g_type_class_add_private (button_class, sizeof (NmtNewtButtonPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_button_set_property;
+	object_class->get_property = nmt_newt_button_get_property;
+	object_class->finalize     = nmt_newt_button_finalize;
+
+	widget_class->size_request  = nmt_newt_button_size_request;
+	widget_class->size_allocate = nmt_newt_button_size_allocate;
+	widget_class->activated     = nmt_newt_button_activated;
+
+	component_class->build_component = nmt_newt_button_build_component;
+
+	/* signals */
+
+	/**
+	 * NmtNewtButton::clicked:
+	 * @button: the #NmtNewtButton
+	 *
+	 * Emitted when the button is clicked.
+	 */
+	signals[CLICKED] =
+		g_signal_new ("clicked",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              0, NULL, NULL, NULL,
+		              G_TYPE_NONE, 0);
+
+	/* properties */
+
+	/**
+	 * NmtNewtButton:label:
+	 *
+	 * The button's label
+	 */
+	g_object_class_install_property
+		(object_class, PROP_LABEL,
+		 g_param_spec_string ("label", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-button.h b/clients/tui/newt/nmt-newt-button.h
new file mode 100644
index 00000000..52e95c5d
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-button.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_BUTTON_H
+#define NMT_NEWT_BUTTON_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_BUTTON            (nmt_newt_button_get_type ())
+#define NMT_NEWT_BUTTON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButton))
+#define NMT_NEWT_BUTTON_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonClass))
+#define NMT_IS_NEWT_BUTTON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_BUTTON))
+#define NMT_IS_NEWT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_BUTTON))
+#define NMT_NEWT_BUTTON_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonClass))
+
+struct _NmtNewtButton {
+	NmtNewtComponent parent;
+
+};
+
+typedef struct {
+	NmtNewtComponentClass parent;
+
+} NmtNewtButtonClass;
+
+GType nmt_newt_button_get_type (void);
+
+NmtNewtWidget *nmt_newt_button_new       (const char    *label);
+
+void           nmt_newt_button_set_label (NmtNewtButton *button,
+                                          const char    *label);
+const char    *nmt_newt_button_get_label (NmtNewtButton *button);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_BUTTON_H */
diff --git a/clients/tui/newt/nmt-newt-checkbox.c b/clients/tui/newt/nmt-newt-checkbox.c
new file mode 100644
index 00000000..69d9920a
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-checkbox.c
@@ -0,0 +1,237 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-checkbox
+ * @short_description: Checkboxes
+ *
+ * #NmtNewtCheckbox implements a checkbox widget.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-checkbox.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtCheckbox, nmt_newt_checkbox, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_CHECKBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxPrivate))
+
+typedef struct {
+	char *label_lc;
+	gboolean active;
+} NmtNewtCheckboxPrivate;
+
+enum {
+	PROP_0,
+	PROP_LABEL,
+	PROP_ACTIVE,
+
+	LAST_PROP
+};
+
+#define CHECKBOX_INACTIVE ' '
+#define CHECKBOX_ACTIVE   'X'
+#define CHECKBOX_STATES   " X"
+
+/**
+ * nmt_newt_checkbox_new:
+ * @label: the (initial) checkbox label
+ *
+ * Creates a new checkbox.
+ *
+ * Returns: a new #NmtNewtCheckbox
+ */
+NmtNewtWidget *
+nmt_newt_checkbox_new (const char *label)
+{
+	return g_object_new (NMT_TYPE_NEWT_CHECKBOX,
+	                     "label", label,
+	                     NULL);
+}
+
+/**
+ * nmt_newt_checkbox_set_active:
+ * @checkbox: an #NmtNewtCheckbox
+ * @active: whether @checkbox should be checked
+ *
+ * Updates @checkbox's checked state
+ */
+void
+nmt_newt_checkbox_set_active (NmtNewtCheckbox *checkbox,
+                              gboolean         active)
+{
+	NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox);
+	newtComponent co;
+
+	active = !!active;
+	if (active == priv->active)
+		return;
+
+	priv->active = active;
+
+	co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (checkbox));
+	if (co)
+		newtCheckboxSetValue (co, priv->active ? CHECKBOX_ACTIVE : CHECKBOX_INACTIVE);
+
+	g_object_notify (G_OBJECT (checkbox), "active");
+}
+
+/**
+ * nmt_newt_checkbox_get_active:
+ * @checkbox: an #NmtNewtCheckbox
+ *
+ * Gets @checkbox's checked state
+ *
+ * Returns: @checkbox's checked state
+ */
+gboolean
+nmt_newt_checkbox_get_active (NmtNewtCheckbox *checkbox)
+{
+	NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox);
+
+	return priv->active;
+}
+
+static void
+nmt_newt_checkbox_init (NmtNewtCheckbox *checkbox)
+{
+}
+
+static void
+nmt_newt_checkbox_finalize (GObject *object)
+{
+	NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object);
+
+	g_free (priv->label_lc);
+
+	G_OBJECT_CLASS (nmt_newt_checkbox_parent_class)->finalize (object);
+}
+
+static void
+checkbox_toggled_callback (newtComponent  co,
+                           void          *checkbox)
+{
+	NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox);
+	gboolean active;
+
+	active = (newtCheckboxGetValue (co) == CHECKBOX_ACTIVE);
+	if (active != priv->active) {
+		priv->active = active;
+		g_object_notify (checkbox, "active");
+	}
+}
+
+static newtComponent
+nmt_newt_checkbox_build_component (NmtNewtComponent *component,
+                                   gboolean          sensitive)
+{
+	NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (component);
+	newtComponent co;
+
+	co = newtCheckbox (-1, -1, priv->label_lc,
+	                   priv->active ? CHECKBOX_ACTIVE : CHECKBOX_INACTIVE,
+	                   CHECKBOX_STATES, NULL);
+	if (!sensitive)
+		newtCheckboxSetFlags (co, NEWT_FLAG_DISABLED, NEWT_FLAGS_SET);
+	newtComponentAddCallback (co, checkbox_toggled_callback, component);
+	return co;
+}
+
+static void
+nmt_newt_checkbox_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+	NmtNewtCheckbox *checkbox = NMT_NEWT_CHECKBOX (object);
+	NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_LABEL:
+		g_free (priv->label_lc);
+		priv->label_lc = nmt_newt_locale_from_utf8 (g_value_get_string (value));
+		break;
+	case PROP_ACTIVE:
+		nmt_newt_checkbox_set_active (checkbox, g_value_get_boolean (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_checkbox_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+	NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_LABEL:
+		g_value_take_string (value, nmt_newt_locale_to_utf8 (priv->label_lc));
+		break;
+	case PROP_ACTIVE:
+		g_value_set_boolean (value, priv->active);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_checkbox_class_init (NmtNewtCheckboxClass *checkbox_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (checkbox_class);
+	NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (checkbox_class);
+
+	g_type_class_add_private (checkbox_class, sizeof (NmtNewtCheckboxPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_checkbox_set_property;
+	object_class->get_property = nmt_newt_checkbox_get_property;
+	object_class->finalize     = nmt_newt_checkbox_finalize;
+
+	component_class->build_component = nmt_newt_checkbox_build_component;
+
+	/**
+	 * NmtNewtCheckbox:label:
+	 *
+	 * The checkbox's label
+	 */
+	g_object_class_install_property
+		(object_class, PROP_LABEL,
+		 g_param_spec_string ("label", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtCheckbox:active:
+	 *
+	 * The checkbox's checked state
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE,
+		 g_param_spec_boolean ("active", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-checkbox.h b/clients/tui/newt/nmt-newt-checkbox.h
new file mode 100644
index 00000000..c277386b
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-checkbox.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_CHECKBOX_H
+#define NMT_NEWT_CHECKBOX_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_CHECKBOX            (nmt_newt_checkbox_get_type ())
+#define NMT_NEWT_CHECKBOX(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckbox))
+#define NMT_NEWT_CHECKBOX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxClass))
+#define NMT_IS_NEWT_CHECKBOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_CHECKBOX))
+#define NMT_IS_NEWT_CHECKBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_CHECKBOX))
+#define NMT_NEWT_CHECKBOX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxClass))
+
+struct _NmtNewtCheckbox {
+	NmtNewtComponent parent;
+
+};
+
+typedef struct {
+	NmtNewtComponentClass parent;
+
+} NmtNewtCheckboxClass;
+
+GType nmt_newt_checkbox_get_type (void);
+
+NmtNewtWidget *nmt_newt_checkbox_new (const char *label);
+
+void     nmt_newt_checkbox_set_active (NmtNewtCheckbox *checkbox,
+                                       gboolean         active);
+gboolean nmt_newt_checkbox_get_active (NmtNewtCheckbox *checkbox);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_CHECKBOX_H */
diff --git a/clients/tui/newt/nmt-newt-component.c b/clients/tui/newt/nmt-newt-component.c
new file mode 100644
index 00000000..98f6df63
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-component.c
@@ -0,0 +1,309 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-component
+ * @short_description: Base class for widgets that wrap #newtComponents
+ *
+ * #NmtNewtComponent is the abstract class for #NmtNewtWidgets that
+ * wrap a (single) #newtComponent.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-component.h"
+#include "nmt-newt-form.h"
+#include "nmt-newt-hacks.h"
+
+G_DEFINE_ABSTRACT_TYPE (NmtNewtComponent, nmt_newt_component, NMT_TYPE_NEWT_WIDGET)
+
+#define NMT_NEWT_COMPONENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentPrivate))
+
+typedef struct {
+	newtComponent co;
+	gboolean own_component;
+	gboolean sensitive;
+} NmtNewtComponentPrivate;
+
+enum {
+	PROP_0,
+
+	PROP_COMPONENT,
+	PROP_SENSITIVE,
+
+	LAST_PROP
+};
+
+static void
+nmt_newt_component_init (NmtNewtComponent *component)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component);
+
+	priv->sensitive = TRUE;
+}
+
+static void
+nmt_newt_component_unrealize (NmtNewtWidget *widget)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+
+	if (!priv->co)
+		return;
+
+	newtComponentAddCallback (priv->co, NULL, NULL);
+	newtComponentAddDestroyCallback (priv->co, NULL, NULL);
+
+	if (priv->own_component)
+		newtComponentDestroy (priv->co);
+	priv->co = NULL;
+}
+
+static void
+component_destroy_callback (newtComponent  co,
+                            void          *component)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component);
+
+	priv->own_component = FALSE;
+	nmt_newt_widget_unrealize (component);
+	nmt_newt_widget_needs_rebuild (component);
+}
+
+static void
+nmt_newt_component_realize (NmtNewtWidget *widget)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+
+	nmt_newt_component_unrealize (widget);
+	priv->co = NMT_NEWT_COMPONENT_GET_CLASS (widget)->
+		build_component (NMT_NEWT_COMPONENT (widget), priv->sensitive);
+	priv->own_component = TRUE;
+	if (!priv->sensitive)
+		newtComponentTakesFocus (priv->co, FALSE);
+	newtComponentAddDestroyCallback (priv->co, component_destroy_callback, widget);
+}
+
+static newtComponent *
+nmt_newt_component_get_components (NmtNewtWidget *widget)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+	newtComponent *cos;
+
+	priv->own_component = FALSE;
+	cos = g_new0 (newtComponent, 2);
+	cos[0] = priv->co;
+	return cos;
+}
+
+static NmtNewtWidget *
+nmt_newt_component_find_component (NmtNewtWidget *widget,
+                                   newtComponent  co)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+
+	if (co == priv->co)
+		return widget;
+	else
+		return NULL;
+}
+
+static void
+nmt_newt_component_size_request (NmtNewtWidget *widget,
+                                 int           *width,
+                                 int           *height)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+
+	newtComponentGetSize (priv->co, width, height);
+}
+
+static void
+nmt_newt_component_size_allocate (NmtNewtWidget *widget,
+                                  int            x,
+                                  int            y,
+                                  int            width,
+                                  int            height)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+	newtGrid grid;
+
+	/* You can't directly place a newtComponent, so we create a newtGrid,
+	 * position the component within that, and then place the grid.
+	 */
+	grid = newtCreateGrid (1, 1);
+	newtGridSetField (grid, 0, 0,
+	                  NEWT_GRID_COMPONENT, priv->co,
+	                  x, y, 0, 0,
+	                  NEWT_ANCHOR_LEFT | NEWT_ANCHOR_TOP, 0);
+	newtGridPlace (grid, 0, 0);
+	newtGridFree (grid, FALSE);
+}
+
+static newtComponent
+nmt_newt_component_get_focus_component (NmtNewtWidget *widget)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget);
+
+	return priv->co;
+}
+
+/**
+ * nmt_newt_component_get_component:
+ * @component: an #NmtNewtComponent
+ *
+ * A simpler version of nmt_newt_widget_get_components() for the
+ * single-component case. Also, unlike
+ * nmt_newt_widget_get_component(), this does not realize the widget
+ * if it isn't already realized. FIXME: why?
+ *
+ * Returns: @component's #newtComponent
+ */
+newtComponent
+nmt_newt_component_get_component (NmtNewtComponent *component)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component);
+
+	return priv->co;
+}
+
+/**
+ * nmt_newt_component_get_sensitive:
+ * @component: an #NmtNewtComponent
+ *
+ * Gets @component's #NmtNewtComponent:sensitive property, indicating
+ * whether the widget is available for manipulation. Insensitive
+ * components will be skipped over in the keyboard tab chain, and may
+ * be displayed differently.
+ *
+ * Returns: @component's #NmtNewtComponent:sensitive property
+ */
+gboolean
+nmt_newt_component_get_sensitive (NmtNewtComponent *component)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component);
+
+	return priv->sensitive;
+}
+
+/**
+ * nmt_newt_component_set_sensitive:
+ * @component: an #NmtNewtComponent
+ * @sensitive: whether @component should be sensitive
+ *
+ * Sets @component's #NmtNewtComponent:sensitive property.
+ */
+void
+nmt_newt_component_set_sensitive (NmtNewtComponent *component,
+                                  gboolean          sensitive)
+{
+	NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component);
+
+	sensitive = !!sensitive;
+	if (priv->sensitive == sensitive)
+		return;
+
+	priv->sensitive = sensitive;
+	g_object_notify (G_OBJECT (component), "sensitive");
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (component));
+}
+
+static void
+nmt_newt_component_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+	NmtNewtComponent *component = NMT_NEWT_COMPONENT (object);
+
+	switch (prop_id) {
+	case PROP_SENSITIVE:
+		nmt_newt_component_set_sensitive (component, g_value_get_boolean (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_component_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+	NmtNewtComponent *component = NMT_NEWT_COMPONENT (object);
+
+	switch (prop_id) {
+	case PROP_COMPONENT:
+		g_value_set_pointer (value, nmt_newt_component_get_component (component));
+		break;
+	case PROP_SENSITIVE:
+		g_value_set_boolean (value, nmt_newt_component_get_sensitive (component));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_component_class_init (NmtNewtComponentClass *component_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (component_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (component_class);
+
+	g_type_class_add_private (component_class, sizeof (NmtNewtComponentPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_component_set_property;
+	object_class->get_property = nmt_newt_component_get_property;
+
+	widget_class->realize             = nmt_newt_component_realize;
+	widget_class->unrealize           = nmt_newt_component_unrealize;
+	widget_class->get_components      = nmt_newt_component_get_components;
+	widget_class->find_component      = nmt_newt_component_find_component;
+	widget_class->size_request        = nmt_newt_component_size_request;
+	widget_class->size_allocate       = nmt_newt_component_size_allocate;
+	widget_class->get_focus_component = nmt_newt_component_get_focus_component;
+
+	/* properties */
+
+	/**
+	 * NmtNewtComponent:component:
+	 *
+	 * The component's #newtComponent
+	 */
+	g_object_class_install_property
+		(object_class, PROP_COMPONENT,
+		 g_param_spec_pointer ("component", "", "",
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtComponent:sensitive:
+	 *
+	 * Whether the component is sensitive. Insensitive components will
+	 * be skipped over in the keyboard tab chain, and may be displayed
+	 * differently.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_SENSITIVE,
+		 g_param_spec_boolean ("sensitive", "", "",
+		                       TRUE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-component.h b/clients/tui/newt/nmt-newt-component.h
new file mode 100644
index 00000000..2ffa018e
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-component.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_COMPONENT_H
+#define NMT_NEWT_COMPONENT_H
+
+#include "nmt-newt-widget.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_COMPONENT            (nmt_newt_component_get_type ())
+#define NMT_NEWT_COMPONENT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponent))
+#define NMT_NEWT_COMPONENT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentClass))
+#define NMT_IS_NEWT_COMPONENT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_COMPONENT))
+#define NMT_IS_NEWT_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_COMPONENT))
+#define NMT_NEWT_COMPONENT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentClass))
+
+struct _NmtNewtComponent {
+	NmtNewtWidget parent;
+
+};
+
+typedef struct {
+	NmtNewtWidgetClass parent;
+
+	/* methods */
+	newtComponent (*build_component)    (NmtNewtComponent *component,
+	                                     gboolean          sensitive);
+
+} NmtNewtComponentClass;
+
+GType nmt_newt_component_get_type (void);
+
+newtComponent nmt_newt_component_get_component (NmtNewtComponent *component);
+
+gboolean      nmt_newt_component_get_sensitive (NmtNewtComponent *component);
+void          nmt_newt_component_set_sensitive (NmtNewtComponent *component,
+                                                gboolean          sensitive);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_COMPONENT_H */
diff --git a/clients/tui/newt/nmt-newt-container.c b/clients/tui/newt/nmt-newt-container.c
new file mode 100644
index 00000000..03f677fb
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-container.c
@@ -0,0 +1,252 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-container
+ * @short_description: Base class for containers
+ *
+ * #NmtNewtContainer is the base class for #NmtNewtWidgets that
+ * contain other widgets.
+ *
+ * #NmtNewtGrid is the most generic container type.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-container.h"
+#include "nmt-newt-component.h"
+
+G_DEFINE_ABSTRACT_TYPE (NmtNewtContainer, nmt_newt_container, NMT_TYPE_NEWT_WIDGET)
+
+#define NMT_NEWT_CONTAINER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerPrivate))
+
+typedef struct {
+	GPtrArray *children;
+
+} NmtNewtContainerPrivate;
+
+static void child_needs_rebuild (NmtNewtWidget *widget, gpointer user_data);
+
+static void
+nmt_newt_container_init (NmtNewtContainer *container)
+{
+	NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container);
+
+	priv->children = g_ptr_array_new ();
+}
+
+static void
+nmt_newt_container_finalize (GObject *object)
+{
+	NmtNewtContainer *container = NMT_NEWT_CONTAINER (object);
+	NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (object);
+
+	while (priv->children->len)
+		nmt_newt_container_remove (container, priv->children->pdata[0]);
+
+	G_OBJECT_CLASS (nmt_newt_container_parent_class)->finalize (object);
+}
+
+static void
+nmt_newt_container_realize (NmtNewtWidget *widget)
+{
+	NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget);
+	int i;
+
+	for (i = 0; i < priv->children->len; i++)
+		nmt_newt_widget_realize (priv->children->pdata[i]);
+}
+
+static void
+nmt_newt_container_unrealize (NmtNewtWidget *widget)
+{
+	NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget);
+	int i;
+
+	for (i = 0; i < priv->children->len; i++)
+		nmt_newt_widget_unrealize (priv->children->pdata[i]);
+}
+
+static void
+child_needs_rebuild (NmtNewtWidget *widget,
+                     gpointer       user_data)
+{
+	NmtNewtWidget *container = user_data;
+
+	nmt_newt_widget_needs_rebuild (container);
+}
+
+static void
+nmt_newt_container_real_child_validity_changed (NmtNewtContainer *container,
+                                                NmtNewtWidget    *widget)
+{
+	NmtNewtContainerPrivate *priv;
+	int i;
+
+	if (widget) {
+		if (!nmt_newt_widget_get_visible (widget))
+			return;
+		if (!nmt_newt_widget_get_valid (widget)) {
+			nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), FALSE);
+			return;
+		}
+	}
+
+	priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container);
+	for (i = 0; i < priv->children->len; i++) {
+		widget = priv->children->pdata[i];
+		if (   nmt_newt_widget_get_visible (widget)
+		       && !nmt_newt_widget_get_valid (widget)) {
+			nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), FALSE);
+			return;
+		}
+	}
+
+	nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), TRUE);
+}
+
+static void
+nmt_newt_container_child_validity_changed (NmtNewtContainer *container,
+                                           NmtNewtWidget    *widget)
+{
+	NMT_NEWT_CONTAINER_GET_CLASS (container)->child_validity_changed (container, widget);
+}
+
+static void
+child_validity_notify (GObject    *object,
+                       GParamSpec *pspec,
+                       gpointer    container)
+{
+	nmt_newt_container_child_validity_changed (container, NMT_NEWT_WIDGET (object));
+}
+
+static void
+nmt_newt_container_real_add (NmtNewtContainer *container,
+                             NmtNewtWidget    *widget)
+{
+	NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container);
+
+	g_signal_connect (widget, "needs-rebuild", G_CALLBACK (child_needs_rebuild), container);
+	g_signal_connect (widget, "notify::valid", G_CALLBACK (child_validity_notify), container);
+	g_ptr_array_add (priv->children, g_object_ref_sink (widget));
+	nmt_newt_widget_set_parent (widget, NMT_NEWT_WIDGET (container));
+
+	nmt_newt_container_child_validity_changed (container, widget);
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (container));
+}
+
+static void
+nmt_newt_container_real_remove (NmtNewtContainer *container,
+                                NmtNewtWidget    *widget)
+{
+	NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container);
+	int i;
+
+	for (i = 0; i < priv->children->len; i++) {
+		if (widget == priv->children->pdata[i]) {
+			g_ptr_array_remove_index (priv->children, i);
+			g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (child_needs_rebuild), container);
+			g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (child_validity_notify), container);
+			nmt_newt_widget_set_parent (widget, NULL);
+			g_object_unref (widget);
+
+			nmt_newt_container_child_validity_changed (container, NULL);
+			nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (container));
+			return;
+		}
+	}
+}
+
+/**
+ * nmt_newt_container_remove:
+ * @container: the #NmtNewtContainer
+ * @widget: the child to remove
+ *
+ * Removes @widget from @container.
+ *
+ * Note that there is not a corresponding
+ * <literal>nmt_newt_container_add ()</literal>; you must use
+ * container-type-specific methods to add widgets to containers.
+ */
+void
+nmt_newt_container_remove (NmtNewtContainer *container,
+                           NmtNewtWidget    *widget)
+{
+	NMT_NEWT_CONTAINER_GET_CLASS (container)->remove (container, widget);
+}
+
+static NmtNewtWidget *
+nmt_newt_container_find_component (NmtNewtWidget *widget,
+                                   newtComponent  co)
+{
+	NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget);
+	NmtNewtWidget *found, *child;
+	int i;
+
+	for (i = 0; i < priv->children->len; i++) {
+		child = priv->children->pdata[i];
+
+		found = nmt_newt_widget_find_component (child, co);
+		if (found)
+			return found;
+	}
+
+	return NULL;
+}
+
+/**
+ * nmt_newt_container_get_children:
+ * @container: an #NmtNewtContainer
+ *
+ * Gets a list of @container's children.
+ *
+ * Returns: (transfer full): a list of @container's children.
+ */
+GSList *
+nmt_newt_container_get_children (NmtNewtContainer *container)
+{
+	NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container);
+	GSList *ret;
+	int i;
+
+	for (i = 0, ret = NULL; i < priv->children->len; i++)
+		ret = g_slist_prepend (ret, g_object_ref (priv->children->pdata[i]));
+	return g_slist_reverse (ret);
+}
+
+static void
+nmt_newt_container_class_init (NmtNewtContainerClass *container_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (container_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (container_class);
+
+	g_type_class_add_private (container_class, sizeof (NmtNewtContainerPrivate));
+
+	/* virtual methods */
+	object_class->finalize = nmt_newt_container_finalize;
+
+	widget_class->realize        = nmt_newt_container_realize;
+	widget_class->unrealize      = nmt_newt_container_unrealize;
+	widget_class->find_component = nmt_newt_container_find_component;
+
+	container_class->add    = nmt_newt_container_real_add;
+	container_class->remove = nmt_newt_container_real_remove;
+	container_class->child_validity_changed = nmt_newt_container_real_child_validity_changed;
+}
diff --git a/clients/tui/newt/nmt-newt-container.h b/clients/tui/newt/nmt-newt-container.h
new file mode 100644
index 00000000..6f182925
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-container.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_CONTAINER_H
+#define NMT_NEWT_CONTAINER_H
+
+#include "nmt-newt-widget.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_CONTAINER            (nmt_newt_container_get_type ())
+#define NMT_NEWT_CONTAINER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainer))
+#define NMT_NEWT_CONTAINER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerClass))
+#define NMT_IS_NEWT_CONTAINER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_CONTAINER))
+#define NMT_IS_NEWT_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_CONTAINER))
+#define NMT_NEWT_CONTAINER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerClass))
+
+struct _NmtNewtContainer {
+	NmtNewtWidget parent;
+
+};
+
+typedef struct {
+	NmtNewtWidgetClass parent;
+
+	/* methods */
+	void (*add)                    (NmtNewtContainer *container,
+	                                NmtNewtWidget    *child);
+	void (*remove)                 (NmtNewtContainer *container,
+	                                NmtNewtWidget    *child);
+
+	void (*child_validity_changed) (NmtNewtContainer *container,
+	                                NmtNewtWidget    *child);
+
+} NmtNewtContainerClass;
+
+GType nmt_newt_container_get_type (void);
+
+void           nmt_newt_container_remove       (NmtNewtContainer *container,
+                                                NmtNewtWidget    *widget);
+
+GSList        *nmt_newt_container_get_children (NmtNewtContainer *container);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_CONTAINER_H */
diff --git a/clients/tui/newt/nmt-newt-entry-numeric.c b/clients/tui/newt/nmt-newt-entry-numeric.c
new file mode 100644
index 00000000..416b4b79
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-entry-numeric.c
@@ -0,0 +1,215 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-entry-numeric
+ * @short_description: Numeric text entry
+ *
+ * #NmtNewtEntryNumeric implements a numeric-only #NmtNewtEntry.
+ *
+ * #NmtNewtEntryNumeric provides its own #NmtNewtEntryFilter and
+ * #NmtNewtEntryValidator functions, so you should not set your own.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include "nmt-newt-entry-numeric.h"
+
+G_DEFINE_TYPE (NmtNewtEntryNumeric, nmt_newt_entry_numeric, NMT_TYPE_NEWT_ENTRY)
+
+#define NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericPrivate))
+
+typedef struct {
+	int min, max;
+} NmtNewtEntryNumericPrivate;
+
+enum {
+	PROP_0,
+	PROP_MINIMUM,
+	PROP_MAXIMUM,
+
+	LAST_PROP
+};
+
+/**
+ * nmt_newt_entry_numeric_new:
+ * @width: the entry's width in characters
+ * @min: the minimum valid value
+ * @max: the maximum valid value
+ *
+ * Creates a new #NmtNewtEntryNumeric, accepting values in the
+ * indicated range.
+ *
+ * Returns: a new #NmtNewtEntryNumeric
+ */
+NmtNewtWidget *
+nmt_newt_entry_numeric_new (int width,
+                            int min,
+                            int max)
+{
+	return g_object_new (NMT_TYPE_NEWT_ENTRY_NUMERIC,
+	                     "width", width,
+	                     "minimum", min,
+	                     "maximum", max,
+	                     NULL);
+}
+
+static gboolean
+newt_entry_numeric_filter (NmtNewtEntry *entry,
+                           const char   *text,
+                           int           ch,
+                           int           position,
+                           gpointer      user_data)
+{
+	NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (entry);
+
+	if (g_ascii_isdigit (ch))
+		return TRUE;
+
+	if (ch == '-' && position == 0 && priv->min < 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+static gboolean
+newt_entry_numeric_validate (NmtNewtEntry *entry,
+                             const char   *text,
+                             gpointer      user_data)
+{
+	NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (entry);
+	int val;
+	char *end;
+
+	if (!*text)
+		return FALSE;
+
+	val = strtoul (text, &end, 10);
+	if (*end)
+		return FALSE;
+	if (val < priv->min || val > priv->max)
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+nmt_newt_entry_numeric_init (NmtNewtEntryNumeric *entry)
+{
+	nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), newt_entry_numeric_filter, NULL);
+	nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), newt_entry_numeric_validate, NULL);
+}
+
+static void
+nmt_newt_entry_numeric_constructed (GObject *object)
+{
+	NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object);
+
+	if (!*nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object))) {
+		char buf[32];
+
+		g_snprintf (buf, sizeof (buf), "%d", priv->min);
+		nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), buf);
+	}
+
+	G_OBJECT_CLASS (nmt_newt_entry_numeric_parent_class)->constructed (object);
+}
+
+static void
+nmt_newt_entry_numeric_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+	NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MINIMUM:
+		priv->min = g_value_get_int (value);
+		break;
+	case PROP_MAXIMUM:
+		priv->max = g_value_get_int (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_entry_numeric_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+	NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MINIMUM:
+		g_value_set_int (value, priv->min);
+		break;
+	case PROP_MAXIMUM:
+		g_value_set_int (value, priv->max);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_entry_numeric_class_init (NmtNewtEntryNumericClass *entry_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+	g_type_class_add_private (entry_class, sizeof (NmtNewtEntryNumericPrivate));
+
+	/* virtual methods */
+	object_class->constructed = nmt_newt_entry_numeric_constructed;
+	object_class->set_property = nmt_newt_entry_numeric_set_property;
+	object_class->get_property = nmt_newt_entry_numeric_get_property;
+
+	/**
+	 * NmtNewtEntryNumeric:minimum:
+	 *
+	 * The minimum #NmtNewtWidget:valid value for the entry. If this
+	 * is non-negative, then the entry will not allow negative numbers
+	 * to be entered.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_MINIMUM,
+		 g_param_spec_int ("minimum", "", "",
+		                   G_MININT, G_MAXINT, 0,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_CONSTRUCT_ONLY |
+		                   G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtEntryNumeric:maximum:
+	 *
+	 * The maximum #NmtNewtWidget:valid value for the entry.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_MAXIMUM,
+		 g_param_spec_int ("maximum", "", "",
+		                   G_MININT, G_MAXINT, G_MAXINT,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_CONSTRUCT_ONLY |
+		                   G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-entry-numeric.h b/clients/tui/newt/nmt-newt-entry-numeric.h
new file mode 100644
index 00000000..43ac3449
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-entry-numeric.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_ENTRY_NUMERIC_H
+#define NMT_NEWT_ENTRY_NUMERIC_H
+
+#include "nmt-newt-entry.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_ENTRY_NUMERIC            (nmt_newt_entry_numeric_get_type ())
+#define NMT_NEWT_ENTRY_NUMERIC(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumeric))
+#define NMT_NEWT_ENTRY_NUMERIC_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericClass))
+#define NMT_IS_NEWT_ENTRY_NUMERIC(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC))
+#define NMT_IS_NEWT_ENTRY_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_ENTRY_NUMERIC))
+#define NMT_NEWT_ENTRY_NUMERIC_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericClass))
+
+struct _NmtNewtEntryNumeric {
+	NmtNewtEntry parent;
+
+};
+
+typedef struct {
+	NmtNewtEntryClass parent;
+
+} NmtNewtEntryNumericClass;
+
+GType nmt_newt_entry_numeric_get_type (void);
+
+NmtNewtWidget *nmt_newt_entry_numeric_new (int width,
+                                           int min,
+                                           int max);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_ENTRY_NUMERIC_H */
diff --git a/clients/tui/newt/nmt-newt-entry.c b/clients/tui/newt/nmt-newt-entry.c
new file mode 100644
index 00000000..7c92d00a
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-entry.c
@@ -0,0 +1,540 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-entry
+ * @short_description: Text entries
+ *
+ * #NmtNewtEntry implements entry widgets, with optional filtering and
+ * validation.
+ *
+ * See also #NmtNewtEntryNumeric, for numeric-only entries.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-entry.h"
+#include "nmt-newt-form.h"
+#include "nmt-newt-hacks.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtEntry, nmt_newt_entry, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryPrivate))
+
+typedef struct {
+	int width;
+	NmtNewtEntryFlags flags;
+	char *text;
+	int last_cursor_pos;
+	guint idle_update;
+
+	NmtNewtEntryFilter filter;
+	gpointer filter_data;
+
+	NmtNewtEntryValidator validator;
+	gpointer validator_data;
+} NmtNewtEntryPrivate;
+
+enum {
+	PROP_0,
+	PROP_TEXT,
+	PROP_WIDTH,
+	PROP_FLAGS,
+	PROP_PASSWORD,
+
+	LAST_PROP
+};
+
+/**
+ * NmtNewtEntryFlags:
+ * @NMT_NEWT_ENTRY_NOSCROLL: the entry content should not scroll left
+ *   and right
+ * @NMT_NEWT_ENTRY_PASSWORD: the entry should show '*'s instead of its
+ *   actual contents
+ * @NMT_NEWT_ENTRY_NONEMPTY: the entry should be considered not
+ *   #NmtNewtWidget:valid if it is empty.
+ *
+ * Flags describing an #NmtNewtEntry
+ */
+
+/**
+ * nmt_newt_entry_new:
+ * @width: the width in characters for the entry
+ * @flags: flags describing the entry
+ *
+ * Creates a new #NmtNewtEntry.
+ *
+ * Returns: a new #NmtNewtEntry
+ */
+NmtNewtWidget *
+nmt_newt_entry_new (int               width,
+                    NmtNewtEntryFlags flags)
+{
+	return g_object_new (NMT_TYPE_NEWT_ENTRY,
+	                     "width", width,
+	                     "flags", flags,
+	                     NULL);
+}
+
+/**
+ * NmtNewtEntryFilter:
+ * @entry: the #NmtNewtEntry
+ * @text: the current contents of @entry
+ * @ch: the character just typed
+ * @position: the position of the cursor in @entry
+ * @user_data: the data passed to nmt_newt_entry_set_filter()
+ *
+ * Callback function used to filter the contents of an entry.
+ *
+ * Returns: %TRUE if @ch should be accepted, %FALSE if not
+ */
+
+/**
+ * nmt_newt_entry_set_filter:
+ * @entry: an #NmtNewtEntry
+ * @filter: the function to use to filter the entry
+ * @user_data: data for @filter
+ *
+ * Sets a #NmtNewtEntryFilter on @entry, to allow filtering out
+ * certain characters from the entry.
+ *
+ * Note that @filter will only be called for printable characters (eg,
+ * not for cursor-control characters or the like), and that it will
+ * only be called for user input, not, eg, for
+ * nmt_newt_entry_set_text().
+ */
+void
+nmt_newt_entry_set_filter (NmtNewtEntry       *entry,
+                           NmtNewtEntryFilter  filter,
+                           gpointer            user_data)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+	priv->filter = filter;
+	priv->filter_data = user_data;
+}
+
+static void
+nmt_newt_entry_check_valid (NmtNewtEntry *entry)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+	gboolean valid;
+
+	if (   (priv->flags & NMT_NEWT_ENTRY_NONEMPTY)
+	    && *priv->text == '\0')
+		valid = FALSE;
+	else if (priv->validator)
+		valid = !!priv->validator (entry, priv->text, priv->validator_data);
+	else
+		valid = TRUE;
+
+	nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (entry), valid);
+}
+
+/**
+ * NmtNewtEntryValidator:
+ * @entry: the #NmtNewtEntry
+ * @text: the current contents of @entry
+ * @user_data: the data passed to nmt_newt_entry_set_validator()
+ *
+ * Callback function used to validate the contents of an entry.
+ *
+ * Returns: whether the entry is #NmtNewtWidget:valid
+ */
+
+/**
+ * nmt_newt_entry_set_validator:
+ * @entry: an #NmtNewtEntry
+ * @validator: the function to use to validate the entry
+ * @user_data: data for @validator
+ *
+ * Sets a #NmtNewtEntryValidator on @entry, to allow validation of
+ * the entry contents. If @validator returns %FALSE, then the entry
+ * will not be considered #NmtNewtWidget:valid.
+ */
+void
+nmt_newt_entry_set_validator (NmtNewtEntry          *entry,
+                              NmtNewtEntryValidator  validator,
+                              gpointer               user_data)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+	priv->validator = validator;
+	priv->validator_data = user_data;
+
+	nmt_newt_entry_check_valid (entry);
+}
+
+static void
+nmt_newt_entry_set_text_internal (NmtNewtEntry  *entry,
+                                  const char    *text,
+                                  newtComponent  co)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+	if (!text)
+		text = "";
+
+	if (!strcmp (priv->text, text))
+		return;
+
+	g_free (priv->text);
+	priv->text = g_strdup (text);
+
+	if (co) {
+		char *text_lc;
+
+		text_lc = priv->text ? nmt_newt_locale_from_utf8 (priv->text) : NULL;
+		newtEntrySet (co, text_lc, TRUE);
+		g_free (text_lc);
+		priv->last_cursor_pos = -1;
+	}
+
+	g_object_freeze_notify (G_OBJECT (entry));
+	nmt_newt_entry_check_valid (entry);
+	g_object_notify (G_OBJECT (entry), "text");
+	g_object_thaw_notify (G_OBJECT (entry));
+}
+
+/**
+ * nmt_newt_entry_set_text:
+ * @entry: an #NmtNewtEntry
+ * @text: the new text
+ *
+ * Updates @entry's text. Note that this skips the entry's
+ * #NmtNewtEntryFilter, but will cause its #NmtNewtEntryValidator to
+ * be re-run.
+ */
+void
+nmt_newt_entry_set_text (NmtNewtEntry *entry,
+                         const char   *text)
+{
+	newtComponent co;
+
+	co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (entry));
+	nmt_newt_entry_set_text_internal (entry, text, co);
+}
+
+/**
+ * nmt_newt_entry_get_text:
+ * @entry: an #NmtNewtEntry
+ *
+ * Gets @entry's text
+ *
+ * Returns: @entry's text
+ */
+const char *
+nmt_newt_entry_get_text (NmtNewtEntry *entry)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+	return priv->text;
+}
+
+/**
+ * nmt_newt_entry_set_width:
+ * @entry: an #NmtNewtEntpry
+ * @widget: the new width
+ *
+ * Updates @entry's width
+ */
+void
+nmt_newt_entry_set_width (NmtNewtEntry *entry,
+                          int           width)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+	if (priv->width == width)
+		return;
+
+	priv->width = width;
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry));
+
+	g_object_notify (G_OBJECT (entry), "width");
+}
+
+/**
+ * nmt_newt_entry_get_width:
+ * @entry: an #NmtNewtEntry
+ *
+ * Gets @entry's width
+ *
+ * Returns: @entry's width
+ */
+int
+nmt_newt_entry_get_width (NmtNewtEntry *entry)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+	return priv->width;
+}
+
+static void
+nmt_newt_entry_init (NmtNewtEntry *entry)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+	priv->text = g_strdup ("");
+	priv->last_cursor_pos = -1;
+}
+
+static void
+nmt_newt_entry_constructed (GObject *object)
+{
+	nmt_newt_entry_check_valid (NMT_NEWT_ENTRY (object));
+
+	G_OBJECT_CLASS (nmt_newt_entry_parent_class)->constructed (object);
+}
+
+static void
+nmt_newt_entry_finalize (GObject *object)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (object);
+
+	g_free (priv->text);
+	if (priv->idle_update)
+		g_source_remove (priv->idle_update);
+
+	G_OBJECT_CLASS (nmt_newt_entry_parent_class)->finalize (object);
+}
+
+static gboolean
+idle_update_entry (gpointer entry)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+	newtComponent co = nmt_newt_component_get_component (entry);
+	char *text;
+
+	priv->idle_update = 0;
+	if (!co)
+		return FALSE;
+
+	priv->last_cursor_pos = newtEntryGetCursorPosition (co);
+
+	text = nmt_newt_locale_to_utf8 (newtEntryGetValue (co));
+	nmt_newt_entry_set_text_internal (entry, text, NULL);
+	g_free (text);
+
+	return FALSE;
+}
+
+static int
+entry_filter (newtComponent  entry,
+              void          *self,
+              int            ch,
+              int            cursor)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (self);
+
+	if (g_ascii_isprint (ch)) {
+		if (priv->filter) {
+			char *text = nmt_newt_locale_to_utf8 (newtEntryGetValue (entry));
+
+			if (!priv->filter (self, text, ch, cursor, priv->filter_data)) {
+				g_free (text);
+				return 0;
+			}
+			g_free (text);
+		}
+	}
+
+	if (!priv->idle_update)
+		priv->idle_update = g_idle_add (idle_update_entry, self);
+	return ch;
+}
+
+static guint
+convert_flags (NmtNewtEntryFlags flags)
+{
+	guint newt_flags = NEWT_FLAG_RETURNEXIT;
+
+	if (!(flags & NMT_NEWT_ENTRY_NOSCROLL))
+		newt_flags |= NEWT_FLAG_SCROLL;
+	if (flags & NMT_NEWT_ENTRY_PASSWORD)
+		newt_flags |= NEWT_FLAG_PASSWORD;
+
+	return newt_flags;
+}
+
+static newtComponent
+nmt_newt_entry_build_component (NmtNewtComponent *component,
+                                gboolean          sensitive)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (component);
+	newtComponent co;
+	char *text_lc;
+	int flags;
+
+	flags = convert_flags (priv->flags);
+	if (!sensitive)
+		flags |= NEWT_FLAG_DISABLED;
+
+	text_lc = priv->text ? nmt_newt_locale_from_utf8 (priv->text) : NULL;
+	co = newtEntry (-1, -1, text_lc, priv->width, NULL, flags);
+	g_free (text_lc);
+
+	if (priv->last_cursor_pos != -1)
+		newtEntrySetCursorPosition (co, priv->last_cursor_pos);
+
+	newtEntrySetFilter (co, entry_filter, component);
+	return co;
+}
+
+static void
+nmt_newt_entry_activated (NmtNewtWidget *widget)
+{
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (widget);
+
+	if (priv->idle_update) {
+		g_source_remove (priv->idle_update);
+		idle_update_entry (widget);
+	}
+
+	NMT_NEWT_WIDGET_CLASS (nmt_newt_entry_parent_class)->activated (widget);
+}
+
+static void
+nmt_newt_entry_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+	NmtNewtEntry *entry = NMT_NEWT_ENTRY (object);
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+	switch (prop_id) {
+	case PROP_TEXT:
+		nmt_newt_entry_set_text (entry, g_value_get_string (value));
+		break;
+	case PROP_WIDTH:
+		nmt_newt_entry_set_width (entry, g_value_get_int (value));
+		break;
+	case PROP_FLAGS:
+		priv->flags = g_value_get_uint (value);
+		nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry));
+		break;
+	case PROP_PASSWORD:
+		if (g_value_get_boolean (value))
+			priv->flags |= NMT_NEWT_ENTRY_PASSWORD;
+		else
+			priv->flags &= ~NMT_NEWT_ENTRY_PASSWORD;
+		nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_entry_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+	NmtNewtEntry *entry = NMT_NEWT_ENTRY (object);
+	NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry);
+
+	switch (prop_id) {
+	case PROP_TEXT:
+		g_value_set_string (value, nmt_newt_entry_get_text (entry));
+		break;
+	case PROP_WIDTH:
+		g_value_set_int (value, priv->width);
+		break;
+	case PROP_FLAGS:
+		g_value_set_uint (value, priv->flags);
+		break;
+	case PROP_PASSWORD:
+		g_value_set_boolean (value, (priv->flags & NMT_NEWT_ENTRY_PASSWORD) != 0);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_entry_class_init (NmtNewtEntryClass *entry_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (entry_class);
+	NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (entry_class);
+
+	g_type_class_add_private (entry_class, sizeof (NmtNewtEntryPrivate));
+
+	/* virtual methods */
+	object_class->constructed  = nmt_newt_entry_constructed;
+	object_class->set_property = nmt_newt_entry_set_property;
+	object_class->get_property = nmt_newt_entry_get_property;
+	object_class->finalize     = nmt_newt_entry_finalize;
+
+	widget_class->activated = nmt_newt_entry_activated;
+
+	component_class->build_component    = nmt_newt_entry_build_component;
+
+	/**
+	 * NmtNewtEntry:text
+	 *
+	 * The entry's text
+	 */
+	g_object_class_install_property
+		(object_class, PROP_TEXT,
+		 g_param_spec_string ("text", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtEntry:width
+	 *
+	 * The entry's width in characters
+	 */
+	g_object_class_install_property
+		(object_class, PROP_WIDTH,
+		 g_param_spec_int ("width", "", "",
+		                   -1, 80, -1,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtEntry:flags
+	 *
+	 * The entry's #NmtNewtEntryFlags
+	 */
+	g_object_class_install_property
+		(object_class, PROP_FLAGS,
+		 g_param_spec_uint ("flags", "", "",
+		                    0, 0xFFFF, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtEntry:password
+	 *
+	 * %TRUE if #NmtNewtEntry:flags contains %NMT_NEWT_ENTRY_PASSWORD,
+	 * %FALSE if not.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_PASSWORD,
+		 g_param_spec_boolean ("password", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-entry.h b/clients/tui/newt/nmt-newt-entry.h
new file mode 100644
index 00000000..8df0b137
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-entry.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_ENTRY_H
+#define NMT_NEWT_ENTRY_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_ENTRY            (nmt_newt_entry_get_type ())
+#define NMT_NEWT_ENTRY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntry))
+#define NMT_NEWT_ENTRY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryClass))
+#define NMT_IS_NEWT_ENTRY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_ENTRY))
+#define NMT_IS_NEWT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_ENTRY))
+#define NMT_NEWT_ENTRY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryClass))
+
+struct _NmtNewtEntry {
+  NmtNewtComponent parent;
+
+};
+
+typedef struct {
+  NmtNewtComponentClass parent;
+
+} NmtNewtEntryClass;
+
+GType nmt_newt_entry_get_type (void);
+
+typedef gboolean (*NmtNewtEntryFilter) (NmtNewtEntry *, const char *text, int ch, int position, gpointer);
+typedef gboolean (*NmtNewtEntryValidator) (NmtNewtEntry *, const char *text, gpointer);
+
+typedef enum {
+	NMT_NEWT_ENTRY_NOSCROLL = (1 << 0),
+	NMT_NEWT_ENTRY_PASSWORD = (1 << 1),
+	NMT_NEWT_ENTRY_NONEMPTY = (1 << 2)
+} NmtNewtEntryFlags;
+
+NmtNewtWidget *nmt_newt_entry_new           (int                    width,
+                                             NmtNewtEntryFlags      flags);
+
+void           nmt_newt_entry_set_filter    (NmtNewtEntry          *entry,
+                                             NmtNewtEntryFilter     filter,
+                                             gpointer               user_data);
+void           nmt_newt_entry_set_validator (NmtNewtEntry          *entry,
+                                             NmtNewtEntryValidator  validator,
+                                             gpointer               user_data);
+
+void           nmt_newt_entry_set_text      (NmtNewtEntry          *entry,
+                                             const char            *text);
+const char    *nmt_newt_entry_get_text      (NmtNewtEntry          *entry);
+
+void           nmt_newt_entry_set_width     (NmtNewtEntry          *entry,
+                                             int                    width);
+int            nmt_newt_entry_get_width     (NmtNewtEntry          *entry);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_ENTRY_H */
diff --git a/clients/tui/newt/nmt-newt-form.c b/clients/tui/newt/nmt-newt-form.c
new file mode 100644
index 00000000..19bd3998
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-form.c
@@ -0,0 +1,753 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-form
+ * @short_description: The top-level NmtNewt widget
+ *
+ * #NmtNewtForm is the top-level widget that contains and presents a
+ * "form" (aka dialog) to the user.
+ */
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-newt-form.h"
+#include "nmt-newt-button.h"
+#include "nmt-newt-grid.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtForm, nmt_newt_form, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_NEWT_FORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_FORM, NmtNewtFormPrivate))
+
+typedef struct {
+	newtComponent form;
+	NmtNewtWidget *content;
+
+	guint x, y, width, height;
+	guint padding;
+	gboolean fixed_x, fixed_y;
+	gboolean fixed_width, fixed_height;
+	char *title_lc;
+
+	gboolean dirty, escape_exits;
+	NmtNewtWidget *focus;
+#ifdef HAVE_NEWTFORMGETSCROLLPOSITION
+	int scroll_position = 0;
+#endif
+} NmtNewtFormPrivate;
+
+enum {
+	PROP_0,
+	PROP_TITLE,
+	PROP_FULLSCREEN,
+	PROP_FULLSCREEN_VERTICAL,
+	PROP_FULLSCREEN_HORIZONTAL,
+	PROP_X,
+	PROP_Y,
+	PROP_WIDTH,
+	PROP_HEIGHT,
+	PROP_PADDING,
+	PROP_ESCAPE_EXITS,
+
+	LAST_PROP
+};
+
+enum {
+	QUIT,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void nmt_newt_form_redraw (NmtNewtForm *form);
+
+/**
+ * nmt_newt_form_new:
+ * @title: (allow-none): the form title
+ *
+ * Creates a new form, which will be shown centered on the screen.
+ * Compare nmt_newt_form_new_fullscreen(). You can also position a
+ * form manually by setting its #NmtNewtForm:x and #NmtNewtForm:y
+ * properties at construct time, and/or by setting
+ * #NmtNewtForm:fullscreen, #NmtNewtform:fullscreen-horizontal, or
+ * #NmtNewtForm:fullscreen-vertical.
+ *
+ * If @title is NULL, the form will have no title.
+ *
+ * Returns: a new #NmtNewtForm
+ */
+NmtNewtForm *
+nmt_newt_form_new (const char *title)
+{
+	return g_object_new (NMT_TYPE_NEWT_FORM,
+	                     "title", title,
+	                     NULL);
+}
+
+/**
+ * nmt_newt_form_new_fullscreen:
+ * @title: (allow-none): the form title
+ *
+ * Creates a new fullscreen form. Compare nmt_newt_form_new().
+ *
+ * If @title is NULL, the form will have no title.
+ *
+ * Returns: a new #NmtNewtForm
+ */
+NmtNewtForm *
+nmt_newt_form_new_fullscreen (const char *title)
+{
+	return g_object_new (NMT_TYPE_NEWT_FORM,
+	                     "title", title,
+	                     "fullscreen", TRUE,
+	                     NULL);
+}
+
+static void
+nmt_newt_form_init (NmtNewtForm *form)
+{
+	g_object_ref_sink (form);
+}
+
+static void
+nmt_newt_form_finalize (GObject *object)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object);
+
+	g_free (priv->title_lc);
+	g_clear_object (&priv->focus);
+
+	G_OBJECT_CLASS (nmt_newt_form_parent_class)->finalize (object);
+}
+
+static void
+nmt_newt_form_needs_rebuild (NmtNewtWidget *widget)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (widget);
+
+	if (!priv->dirty) {
+		priv->dirty = TRUE;
+		nmt_newt_form_redraw (NMT_NEWT_FORM (widget));
+	}
+}
+
+static void
+nmt_newt_form_remove (NmtNewtContainer *container,
+                      NmtNewtWidget    *widget)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (container);
+	NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_form_parent_class);
+
+	g_return_if_fail (widget == priv->content);
+
+	parent_class->remove (container, widget);
+	priv->content = NULL;
+}
+
+/**
+ * nmt_newt_form_set_content:
+ * @form: the #NmtNewtForm
+ * @content: the form's content
+ *
+ * Sets @form's content to be @content.
+ */
+void
+nmt_newt_form_set_content (NmtNewtForm      *form,
+                           NmtNewtWidget    *content)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+	NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_form_parent_class);
+
+	if (priv->content)
+		nmt_newt_form_remove (NMT_NEWT_CONTAINER (form), priv->content);
+
+	priv->content = content;
+
+	if (priv->content)
+		parent_class->add (NMT_NEWT_CONTAINER (form), content);
+}
+
+static void
+nmt_newt_form_build (NmtNewtForm *form)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+	int screen_height, screen_width, form_height, form_width;
+	newtComponent *cos;
+	int i;
+
+	priv->dirty = FALSE;
+	nmt_newt_widget_realize (NMT_NEWT_WIDGET (form));
+
+	nmt_newt_widget_size_request (priv->content, &form_width, &form_height);
+	newtGetScreenSize (&screen_width, &screen_height);
+
+	if (!priv->fixed_width)
+		priv->width = MIN (form_width + 2 * priv->padding, screen_width - 2);
+	if (!priv->fixed_height)
+		priv->height = MIN (form_height + 2 * priv->padding, screen_height - 2);
+
+	if (!priv->fixed_x)
+		priv->x = (screen_width - form_width) / 2;
+	if (!priv->fixed_y)
+		priv->y = (screen_height - form_height) / 2;
+
+	nmt_newt_widget_size_allocate (priv->content,
+	                               priv->padding,
+	                               priv->padding,
+	                               priv->width - 2 * priv->padding,
+	                               priv->height - 2 * priv->padding);
+
+	if (priv->height - 2 * priv->padding < form_height) {
+		newtComponent scroll_bar =
+			newtVerticalScrollbar (priv->width - 1, 0, priv->height,
+			                       NEWT_COLORSET_WINDOW,
+			                       NEWT_COLORSET_ACTCHECKBOX);
+
+		priv->form = newtForm (scroll_bar, NULL, NEWT_FLAG_NOF12);
+		newtFormAddComponent (priv->form, scroll_bar);
+		newtFormSetHeight (priv->form, priv->height - 2);
+	} else
+		priv->form = newtForm (NULL, NULL, NEWT_FLAG_NOF12);
+
+	if (priv->escape_exits)
+		newtFormAddHotKey (priv->form, NEWT_KEY_ESCAPE);
+
+	cos = nmt_newt_widget_get_components (priv->content);
+	for (i = 0; cos[i]; i++)
+		newtFormAddComponent (priv->form, cos[i]);
+	g_free (cos);
+
+	if (priv->focus) {
+		newtComponent fco;
+
+		fco = nmt_newt_widget_get_focus_component (priv->focus);
+		if (fco)
+			newtFormSetCurrent (priv->form, fco);
+	}
+#ifdef HAVE_NEWTFORMGETSCROLLPOSITION
+	if (priv->scroll_position)
+		newtFormSetScrollPosition (priv->form, priv->scroll_position);
+#endif
+
+	newtOpenWindow (priv->x, priv->y, priv->width, priv->height, priv->title_lc);
+}
+
+static void
+nmt_newt_form_destroy (NmtNewtForm *form)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+
+#ifdef HAVE_NEWTFORMGETSCROLLPOSITION
+	priv->scroll_position = newtFormGetScrollPosition (priv->form);
+#endif
+
+	newtFormDestroy (priv->form);
+	priv->form = NULL;
+	newtPopWindowNoRefresh ();
+
+	nmt_newt_widget_unrealize (NMT_NEWT_WIDGET (form));
+}
+
+/* A "normal" newt program would call newtFormRun() to run newt's main loop
+ * and process events. But we want to let GLib's main loop control the program
+ * (eg, so libnm can process D-Bus notifications). So we call this function
+ * to run a single iteration of newt's main loop (or rather, to run newt's
+ * main loop for 1ms) whenever there are events for newt to process (redrawing
+ * or keypresses).
+ */
+static void
+nmt_newt_form_iterate (NmtNewtForm *form)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+	NmtNewtWidget *focus;
+	struct newtExitStruct es;
+
+	if (priv->dirty) {
+		nmt_newt_form_destroy (form);
+		nmt_newt_form_build (form);
+	}
+
+	newtFormSetTimer (priv->form, 1);
+	newtFormRun (priv->form, &es);
+
+	if (   es.reason == NEWT_EXIT_HOTKEY
+	    || es.reason == NEWT_EXIT_ERROR) {
+		/* The user hit Esc or there was an error. */
+		g_clear_object (&priv->focus);
+		nmt_newt_form_quit (form);
+		return;
+	}
+
+	if (es.reason == NEWT_EXIT_COMPONENT) {
+		/* The user hit Return/Space on a component; update the form focus
+		 * to point that that component, and activate it.
+		 */
+		focus = nmt_newt_widget_find_component (priv->content, es.u.co);
+		if (focus) {
+			nmt_newt_form_set_focus (form, focus);
+			nmt_newt_widget_activated (focus);
+		}
+	} else {
+		/* The 1ms timer ran out. Update focus but don't do anything else. */
+		focus = nmt_newt_widget_find_component (priv->content,
+		                                        newtFormGetCurrent (priv->form));
+		if (focus)
+			nmt_newt_form_set_focus (form, focus);
+	}
+}
+
+/* @form_stack keeps track of all currently-displayed forms, from top to bottom.
+ * @keypress_source is the global stdin-monitoring GSource. When it triggers,
+ * nmt_newt_form_keypress_callback() iterates the top-most form, so it can
+ * process the keypress.
+ */
+static GSList *form_stack;
+static GSource *keypress_source;
+
+static gboolean
+nmt_newt_form_keypress_callback (int          fd,
+                                 GIOCondition condition,
+                                 gpointer     user_data)
+{
+	g_return_val_if_fail (form_stack != NULL, FALSE);
+
+	nmt_newt_form_iterate (form_stack->data);
+	return TRUE;
+}
+
+static gboolean
+nmt_newt_form_timeout_callback (gpointer user_data)
+{
+	if (form_stack)
+		nmt_newt_form_iterate (form_stack->data);
+	return FALSE;
+}
+
+static void
+nmt_newt_form_redraw (NmtNewtForm *form)
+{
+	g_timeout_add (0, nmt_newt_form_timeout_callback, NULL);
+}
+
+static void
+nmt_newt_form_real_show (NmtNewtForm *form)
+{
+	if (!keypress_source) {
+		GIOChannel *io;
+
+		io = g_io_channel_unix_new (STDIN_FILENO);
+		keypress_source = g_io_create_watch (io, G_IO_IN);
+		g_source_set_can_recurse (keypress_source, TRUE);
+		g_source_set_callback (keypress_source,
+		                       (GSourceFunc) nmt_newt_form_keypress_callback,
+		                       NULL, NULL);
+		g_source_attach (keypress_source, NULL);
+		g_io_channel_unref (io);
+	}
+
+	nmt_newt_form_build (form);
+	form_stack = g_slist_prepend (form_stack, g_object_ref (form));
+	nmt_newt_form_redraw (form);
+}
+
+/**
+ * nmt_newt_form_show:
+ * @form: an #NmtNewtForm
+ *
+ * Displays @form and begins running it asynchronously in the default
+ * #GMainContext. If another form is currently running, it will remain
+ * visible in the background, but will not be able to receive keyboard
+ * input until @form exits.
+ *
+ * Call nmt_newt_form_quit() to quit the form.
+ */
+void
+nmt_newt_form_show (NmtNewtForm *form)
+{
+	NMT_NEWT_FORM_GET_CLASS (form)->show (form);
+}
+
+/**
+ * nmt_newt_form_run_sync:
+ * @form: an #NmtNewtForm
+ *
+ * Displays @form as with nmt_newt_form_show(), but then iterates the
+ * #GMainContext internally until @form exits.
+ *
+ * Returns: the widget whose activation caused @form to exit, or
+ *   %NULL if it was not caused by a widget. FIXME: this exit value is
+ *   sort of weird and may not be 100% accurate anyway.
+ */
+NmtNewtWidget *
+nmt_newt_form_run_sync (NmtNewtForm *form)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+
+	nmt_newt_form_show (form);
+	while (priv->form)
+		g_main_context_iteration (NULL, TRUE);
+
+	return priv->focus;
+}
+
+/**
+ * nmt_newt_form_quit:
+ * @form: an #NmtNewtForm
+ *
+ * Causes @form to exit.
+ */
+void
+nmt_newt_form_quit (NmtNewtForm *form)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+
+	g_return_if_fail (priv->form != NULL);
+
+	nmt_newt_form_destroy (form);
+
+	form_stack = g_slist_remove (form_stack, form);
+
+	if (form_stack)
+		nmt_newt_form_iterate (form_stack->data);
+	else if (keypress_source) {
+		g_source_destroy (keypress_source);
+		g_clear_pointer (&keypress_source, g_source_unref);
+	}
+
+	g_signal_emit (form, signals[QUIT], 0);
+	g_object_unref (form);
+}
+
+/**
+ * nmt_newt_form_set_focus:
+ * @form: an #NmtNewtForm
+ * @widget: the widget to focus
+ *
+ * Focuses @widget in @form.
+ */
+void
+nmt_newt_form_set_focus (NmtNewtForm   *form,
+                         NmtNewtWidget *widget)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form);
+
+	g_return_if_fail (priv->form != NULL);
+
+	if (priv->focus == widget)
+		return;
+
+	if (priv->focus)
+		g_object_unref (priv->focus);
+	priv->focus = widget;
+	if (priv->focus)
+		g_object_ref (priv->focus);
+}
+
+static void
+nmt_newt_form_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object);
+	int screen_width, screen_height;
+
+	switch (prop_id) {
+	case PROP_TITLE:
+		if (g_value_get_string (value)) {
+			priv->title_lc = nmt_newt_locale_from_utf8 (g_value_get_string (value));
+		} else
+			priv->title_lc = NULL;
+		break;
+	case PROP_FULLSCREEN:
+		if (g_value_get_boolean (value)) {
+			newtGetScreenSize (&screen_width, &screen_height);
+			priv->x = priv->y = 2;
+			priv->fixed_x = priv->fixed_y = TRUE;
+			priv->width = screen_width - 4;
+			priv->height = screen_height - 4;
+			priv->fixed_width = priv->fixed_height = TRUE;
+		}
+		break;
+	case PROP_FULLSCREEN_VERTICAL:
+		if (g_value_get_boolean (value)) {
+			newtGetScreenSize (&screen_width, &screen_height);
+			priv->y = 2;
+			priv->fixed_y = TRUE;
+			priv->height = screen_height - 4;
+			priv->fixed_height = TRUE;
+		}
+		break;
+	case PROP_FULLSCREEN_HORIZONTAL:
+		if (g_value_get_boolean (value)) {
+			newtGetScreenSize (&screen_width, &screen_height);
+			priv->x = 2;
+			priv->fixed_x = TRUE;
+			priv->width = screen_width - 4;
+			priv->fixed_width = TRUE;
+		}
+		break;
+	case PROP_X:
+		if (g_value_get_uint (value)) {
+			priv->x = g_value_get_uint (value);
+			priv->fixed_x = TRUE;
+		}
+		break;
+	case PROP_Y:
+		if (g_value_get_uint (value)) {
+			priv->y = g_value_get_uint (value);
+			priv->fixed_y = TRUE;
+		}
+		break;
+	case PROP_WIDTH:
+		if (g_value_get_uint (value)) {
+			priv->width = g_value_get_uint (value);
+			priv->fixed_width = TRUE;
+		}
+		break;
+	case PROP_HEIGHT:
+		if (g_value_get_uint (value)) {
+			priv->height = g_value_get_uint (value);
+			priv->fixed_height = TRUE;
+		}
+		break;
+	case PROP_PADDING:
+		priv->padding = g_value_get_uint (value);
+		break;
+	case PROP_ESCAPE_EXITS:
+		priv->escape_exits = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_form_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+	NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_TITLE:
+		if (priv->title_lc) {
+			g_value_take_string (value, nmt_newt_locale_to_utf8 (priv->title_lc));
+		} else
+			g_value_set_string (value, NULL);
+		break;
+	case PROP_X:
+		g_value_set_uint (value, priv->x);
+		break;
+	case PROP_Y:
+		g_value_set_uint (value, priv->y);
+		break;
+	case PROP_WIDTH:
+		g_value_set_uint (value, priv->width);
+		break;
+	case PROP_HEIGHT:
+		g_value_set_uint (value, priv->height);
+		break;
+	case PROP_PADDING:
+		g_value_set_uint (value, priv->padding);
+		break;
+	case PROP_ESCAPE_EXITS:
+		g_value_set_boolean (value, priv->escape_exits);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_form_class_init (NmtNewtFormClass *form_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (form_class);
+	NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (form_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (form_class);
+
+	g_type_class_add_private (form_class, sizeof (NmtNewtFormPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_form_set_property;
+	object_class->get_property = nmt_newt_form_get_property;
+	object_class->finalize     = nmt_newt_form_finalize;
+
+	widget_class->needs_rebuild = nmt_newt_form_needs_rebuild;
+
+	container_class->remove = nmt_newt_form_remove;
+
+	form_class->show = nmt_newt_form_real_show;
+
+	/* signals */
+
+	/**
+	 * NmtNewtForm::quit:
+	 * @form: the #NmtNewtForm
+	 *
+	 * Emitted when the form quits.
+	 */
+	signals[QUIT] =
+		g_signal_new ("quit",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NmtNewtFormClass, quit),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 0);
+
+	/**
+	 * NmtNewtForm:title:
+	 *
+	 * The form's title. If non-%NULL, this will be displayed above
+	 * the form in its border.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_TITLE,
+		 g_param_spec_string ("title", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS |
+		                      G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * NmtNewtForm:fullscreen:
+	 *
+	 * If %TRUE, the form will fill the entire "screen" (ie, terminal
+	 * window).
+	 */
+	g_object_class_install_property
+		(object_class, PROP_FULLSCREEN,
+		 g_param_spec_boolean ("fullscreen", "", "",
+		                       FALSE,
+		                       G_PARAM_WRITABLE |
+		                       G_PARAM_STATIC_STRINGS |
+		                       G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * NmtNewtForm:fullscreen-vertical:
+	 *
+	 * If %TRUE, the form will fill the entire "screen" (ie, terminal
+	 * window) vertically, but not necessarily horizontally.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_FULLSCREEN_VERTICAL,
+		 g_param_spec_boolean ("fullscreen-vertical", "", "",
+		                       FALSE,
+		                       G_PARAM_WRITABLE |
+		                       G_PARAM_STATIC_STRINGS |
+		                       G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * NmtNewtForm:fullscreen-horizontal:
+	 *
+	 * If %TRUE, the form will fill the entire "screen" (ie, terminal
+	 * window) horizontally, but not necessarily vertically.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_FULLSCREEN_HORIZONTAL,
+		 g_param_spec_boolean ("fullscreen-horizontal", "", "",
+		                       FALSE,
+		                       G_PARAM_WRITABLE |
+		                       G_PARAM_STATIC_STRINGS |
+		                       G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * NmtNewtForm:x:
+	 *
+	 * The form's x coordinate. By default, the form will be centered
+	 * on the screen.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_X,
+		 g_param_spec_uint ("x", "", "",
+		                    0, G_MAXUINT, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS |
+		                    G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * NmtNewtForm:y:
+	 *
+	 * The form's y coordinate. By default, the form will be centered
+	 * on the screen.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_Y,
+		 g_param_spec_uint ("y", "", "",
+		                    0, G_MAXUINT, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS |
+		                    G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * NmtNewtForm:width:
+	 *
+	 * The form's width. By default, this will be determined by the
+	 * width of the form's content.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_WIDTH,
+		 g_param_spec_uint ("width", "", "",
+		                    0, G_MAXUINT, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS |
+		                    G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * NmtNewtForm:height:
+	 *
+	 * The form's height. By default, this will be determined by the
+	 * height of the form's content.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_HEIGHT,
+		 g_param_spec_uint ("height", "", "",
+		                    0, G_MAXUINT, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS |
+		                    G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * NmtNewtForm:padding:
+	 *
+	 * The padding between the form's content and its border.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_PADDING,
+		 g_param_spec_uint ("padding", "", "",
+		                    0, G_MAXUINT, 1,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS |
+		                    G_PARAM_CONSTRUCT_ONLY));
+	/**
+	 * NmtNewtForm:escape-exits:
+	 *
+	 * If %TRUE, then hitting the Escape key will cause the form to
+	 * exit.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ESCAPE_EXITS,
+		 g_param_spec_boolean ("escape-exits", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS |
+		                       G_PARAM_CONSTRUCT_ONLY));
+}
diff --git a/clients/tui/newt/nmt-newt-form.h b/clients/tui/newt/nmt-newt-form.h
new file mode 100644
index 00000000..be95eb68
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-form.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_FORM_H
+#define NMT_NEWT_FORM_H
+
+#include "nmt-newt-container.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_FORM            (nmt_newt_form_get_type ())
+#define NMT_NEWT_FORM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_FORM, NmtNewtForm))
+#define NMT_NEWT_FORM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_FORM, NmtNewtFormClass))
+#define NMT_IS_NEWT_FORM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_FORM))
+#define NMT_IS_NEWT_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_FORM))
+#define NMT_NEWT_FORM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_FORM, NmtNewtFormClass))
+
+struct _NmtNewtForm {
+	NmtNewtContainer parent;
+
+};
+
+typedef struct {
+	NmtNewtContainerClass parent;
+
+	/* signals */
+	void (*quit) (NmtNewtForm *form);
+
+	/* methods */
+	void (*show) (NmtNewtForm *form);
+
+} NmtNewtFormClass;
+
+GType nmt_newt_form_get_type (void);
+
+NmtNewtForm   *nmt_newt_form_new              (const char          *title);
+NmtNewtForm   *nmt_newt_form_new_fullscreen   (const char          *title);
+
+void           nmt_newt_form_set_content      (NmtNewtForm         *form,
+                                               NmtNewtWidget       *content);
+
+void           nmt_newt_form_show             (NmtNewtForm         *form);
+NmtNewtWidget *nmt_newt_form_run_sync         (NmtNewtForm         *form);
+void           nmt_newt_form_quit             (NmtNewtForm         *form);
+
+void           nmt_newt_form_set_focus        (NmtNewtForm         *form,
+                                               NmtNewtWidget       *widget);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_FORM_H */
diff --git a/clients/tui/newt/nmt-newt-grid.c b/clients/tui/newt/nmt-newt-grid.c
new file mode 100644
index 00000000..ac967334
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-grid.c
@@ -0,0 +1,472 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-grid
+ * @short_description: Grid container
+ *
+ * #NmtNewtGrid is the most general-purpose container widget in NmtNewt.
+ *
+ * An #NmtNewtGrid consists of a number of rows and columns. There is
+ * no pre-established maximum row or columns. Rather, rows and columns
+ * exist if and only if there are widgets in them.
+ *
+ * The width of each column is the width of the widest widget in that
+ * column, and the height of each row is the height of the tallest
+ * widget in that row. Empty rows and empty columns take up no space,
+ * so a grid with a single widget at 0,0 would look exactly the same
+ * if the widget was at 5,10 instead.
+ *
+ * If a widget's cell ends up being larger than the widget's requested
+ * size, then by default the widget will be centered in its cell.
+ * However, this can be modified by changing its #NmtNewtGridFlags.
+ * FIXME: the FILL/ANCHOR flags can be implemented in #NmtNewtWidget
+ * and so should move there. Less clear about the EXPAND flags, which
+ * must be implemented by the container...
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-grid.h"
+
+G_DEFINE_TYPE (NmtNewtGrid, nmt_newt_grid, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_NEWT_GRID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_GRID, NmtNewtGridPrivate))
+
+typedef struct {
+	NmtNewtWidget *widget;
+	int x, y;
+	NmtNewtGridFlags flags;
+	int req_height, req_width;
+} NmtNewtGridChild;
+
+typedef struct {
+	GArray *children;
+	int max_x, max_y;
+	int *row_heights, *col_widths;
+	gboolean *expand_rows, *expand_cols;
+	int n_expand_rows, n_expand_cols;
+	int req_height, req_width;
+} NmtNewtGridPrivate;
+
+/**
+ * nmt_newt_grid_new:
+ *
+ * Creates a new #NmtNewtGrid
+ *
+ * Returns: a new #NmtNewtGrid
+ */
+NmtNewtWidget *
+nmt_newt_grid_new (void)
+{
+	return g_object_new (NMT_TYPE_NEWT_GRID, NULL);
+}
+
+static void
+nmt_newt_grid_init (NmtNewtGrid *grid)
+{
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+
+	priv->children = g_array_new (FALSE, FALSE, sizeof (NmtNewtGridChild));
+}
+
+static void
+nmt_newt_grid_finalize (GObject *object)
+{
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (object);
+
+	g_array_unref (priv->children);
+	g_clear_pointer (&priv->row_heights, g_free);
+	g_clear_pointer (&priv->col_widths, g_free);
+	g_clear_pointer (&priv->expand_rows, g_free);
+	g_clear_pointer (&priv->expand_cols, g_free);
+
+	G_OBJECT_CLASS (nmt_newt_grid_parent_class)->finalize (object);
+}
+
+static int
+child_sort_func (gconstpointer a,
+                 gconstpointer b)
+{
+	NmtNewtGridChild *child_a = (NmtNewtGridChild *)a;
+	NmtNewtGridChild *child_b = (NmtNewtGridChild *)b;
+
+	if (child_a->y != child_b->y)
+		return child_a->y - child_b->y;
+	else
+		return child_a->x - child_b->x;
+}
+
+static newtComponent *
+nmt_newt_grid_get_components (NmtNewtWidget *widget)
+{
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (widget);
+	NmtNewtGridChild *children;
+	GPtrArray *cos;
+	newtComponent *child_cos;
+	int i, c;
+
+	g_array_sort (priv->children, child_sort_func);
+	children = (NmtNewtGridChild *)priv->children->data;
+
+	cos = g_ptr_array_new ();
+
+	for (i = 0; i < priv->children->len; i++) {
+		if (!nmt_newt_widget_get_visible (children[i].widget))
+			continue;
+
+		child_cos = nmt_newt_widget_get_components (children[i].widget);
+		for (c = 0; child_cos[c]; c++)
+			g_ptr_array_add (cos, child_cos[c]);
+		g_free (child_cos);
+	}
+	g_ptr_array_add (cos, NULL);
+
+	return (newtComponent *) g_ptr_array_free (cos, FALSE);
+}
+
+static void
+nmt_newt_grid_size_request (NmtNewtWidget *widget,
+                            int           *width,
+                            int           *height)
+{
+	NmtNewtGrid *grid = NMT_NEWT_GRID (widget);
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+	NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
+	int row, col, i;
+
+	g_free (priv->row_heights);
+	g_free (priv->col_widths);
+	g_free (priv->expand_rows);
+	g_free (priv->expand_cols);
+
+	priv->row_heights = g_new0 (int, priv->max_y + 1);
+	priv->col_widths = g_new0 (int, priv->max_x + 1);
+	priv->expand_rows = g_new0 (gboolean, priv->max_y + 1);
+	priv->expand_cols = g_new0 (gboolean, priv->max_x + 1);
+	priv->n_expand_rows = priv->n_expand_cols = 0;
+
+	for (row = 0; row < priv->max_y + 1; row++) {
+		for (col = 0; col < priv->max_x + 1; col++) {
+			for (i = 0; i < priv->children->len; i++) {
+				if (children[i].x != col || children[i].y != row)
+					continue;
+				if (!nmt_newt_widget_get_visible (children[i].widget))
+					continue;
+
+				nmt_newt_widget_size_request (children[i].widget,
+				                              &children[i].req_width,
+				                              &children[i].req_height);
+				if (children[i].req_height > priv->row_heights[row])
+					priv->row_heights[row] = children[i].req_height;
+				if (children[i].req_width > priv->col_widths[col])
+					priv->col_widths[col] = children[i].req_width;
+
+				if (   (children[i].flags & NMT_NEWT_GRID_EXPAND_X)
+				    && !priv->expand_cols[children[i].x]) {
+					priv->expand_cols[children[i].x] = TRUE;
+					priv->n_expand_cols++;
+				}
+				if (   (children[i].flags & NMT_NEWT_GRID_EXPAND_Y)
+				    && !priv->expand_rows[children[i].y]) {
+					priv->expand_rows[children[i].y] = TRUE;
+					priv->n_expand_rows++;
+				}
+			}
+		}
+	}
+
+	priv->req_height = priv->req_width = 0;
+	for (row = 0; row < priv->max_y + 1; row++)
+		priv->req_height += priv->row_heights[row];
+	for (col = 0; col < priv->max_x + 1; col++)
+		priv->req_width += priv->col_widths[col];
+
+	*height = priv->req_height;
+	*width = priv->req_width;
+}
+
+static void
+nmt_newt_grid_size_allocate (NmtNewtWidget *widget,
+                             int            x,
+                             int            y,
+                             int            width,
+                             int            height)
+{
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (widget);
+	NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data, *child;
+	int i, row, col;
+	int child_x, child_y, child_width, child_height;
+	int extra, extra_all, extra_some;
+
+	extra = width - priv->req_width;
+	if (extra > 0 && priv->n_expand_cols) {
+		extra_all = extra / priv->n_expand_cols;
+		extra_some = extra % priv->n_expand_cols;
+
+		for (col = 0; col < priv->max_x + 1; col++) {
+			if (!priv->expand_cols[col])
+				continue;
+			priv->col_widths[col] += extra_all;
+			if (extra_some) {
+				priv->col_widths[col]++;
+				extra_some--;
+			}
+		}
+	}
+
+	extra = height - priv->req_height;
+	if (extra > 0 && priv->n_expand_rows) {
+		extra_all = extra / priv->n_expand_rows;
+		extra_some = extra % priv->n_expand_rows;
+
+		for (row = 0; row < priv->max_y + 1; row++) {
+			if (!priv->expand_rows[row])
+				continue;
+			priv->row_heights[row] += extra_all;
+			if (extra_some) {
+				priv->row_heights[row]++;
+				extra_some--;
+			}
+		}
+	}
+
+	for (i = 0; i < priv->children->len; i++) {
+		child = &children[i];
+		if (!nmt_newt_widget_get_visible (child->widget))
+			continue;
+
+		child_x = x;
+		for (col = 0; col < child->x; col++)
+			child_x += priv->col_widths[col];
+
+		if ((child->flags & NMT_NEWT_GRID_FILL_X) == NMT_NEWT_GRID_FILL_X) {
+			child_width = priv->col_widths[child->x];
+		} else {
+			child_width = child->req_width;
+			if (child->flags & NMT_NEWT_GRID_ANCHOR_RIGHT)
+				child_x += priv->col_widths[child->x] - child->req_width;
+			else if (!(child->flags & NMT_NEWT_GRID_ANCHOR_LEFT))
+				child_x += (priv->col_widths[child->x] - child->req_width) / 2;
+		}
+
+		child_y = y;
+		for (row = 0; row < child->y; row++)
+			child_y += priv->row_heights[row];
+
+		if ((child->flags & NMT_NEWT_GRID_FILL_Y) == NMT_NEWT_GRID_FILL_Y) {
+			child_height = priv->row_heights[child->y];
+		} else {
+			child_height = child->req_height;
+			if (child->flags & NMT_NEWT_GRID_ANCHOR_BOTTOM)
+				child_y += priv->row_heights[child->y] - child->req_height;
+			else if (!(child->flags & NMT_NEWT_GRID_ANCHOR_TOP))
+				child_y += (priv->row_heights[child->y] - child->req_height) / 2;
+		}
+
+		nmt_newt_widget_size_allocate (child->widget,
+		                               child_x, child_y,
+		                               child_width, child_height);
+	}
+}
+
+static void
+nmt_newt_grid_find_size (NmtNewtGrid *grid)
+{
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+	NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
+	int i;
+
+	priv->max_x = priv->max_y = 0;
+	for (i = 0; i < priv->children->len; i++) {
+		if (children[i].x > priv->max_x)
+			priv->max_x = children[i].x;
+		if (children[i].y > priv->max_y)
+			priv->max_y = children[i].y;
+	}
+}
+
+/**
+ * nmt_newt_grid_add:
+ * @grid: an #NmtNewtGrid
+ * @widget: the widget to add
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * Adds @widget to @grid at @x, @y. See the discussion above for more
+ * details of exactly how this works.
+ */
+void
+nmt_newt_grid_add (NmtNewtGrid   *grid,
+                   NmtNewtWidget *widget,
+                   int            x,
+                   int            y)
+{
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+	NmtNewtGridChild child;
+
+	NMT_NEWT_CONTAINER_CLASS (nmt_newt_grid_parent_class)->add (NMT_NEWT_CONTAINER (grid), widget);
+
+	memset (&child, 0, sizeof (child));
+	child.widget = widget;
+	child.x = x;
+	child.y = y;
+	child.flags = NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y;
+	g_array_append_val (priv->children, child);
+
+	if (x > priv->max_x)
+		priv->max_x = x;
+	if (y > priv->max_y)
+		priv->max_y = y;
+}
+
+static int
+find_child (NmtNewtGrid   *grid,
+            NmtNewtWidget *widget)
+{
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+	NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
+	int i;
+
+	for (i = 0; i < priv->children->len; i++) {
+		if (children[i].widget == widget)
+			return i;
+	}
+
+	return -1;
+}
+
+static void
+nmt_newt_grid_remove (NmtNewtContainer *container,
+                      NmtNewtWidget    *widget)
+{
+	NmtNewtGrid *grid = NMT_NEWT_GRID (container);
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+	int i;
+
+	i = find_child (grid, widget);
+	if (i != -1) {
+		g_array_remove_index (priv->children, i);
+		nmt_newt_grid_find_size (grid);
+	}
+
+	NMT_NEWT_CONTAINER_CLASS (nmt_newt_grid_parent_class)->remove (container, widget);
+}
+
+/**
+ * nmt_newt_grid_move:
+ * @grid: an #NmtNewtGrid
+ * @widget: a child of @grid
+ * @x: x coordinate
+ * @y: y coordinate
+ *
+ * Moves @widget to the given new coordinates.
+ */
+void
+nmt_newt_grid_move (NmtNewtGrid   *grid,
+                    NmtNewtWidget *widget,
+                    int            x,
+                    int            y)
+{
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+	NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
+	int i;
+
+	i = find_child (grid, widget);
+	if (i != -1 && (children[i].x != x || children[i].y != y)) {
+		children[i].x = x;
+		children[i].y = y;
+		nmt_newt_grid_find_size (grid);
+		nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (grid));
+	}
+}
+
+/**
+ * NmtNewtGridFlags:
+ * @NMT_NEWT_GRID_EXPAND_X: The widget's cell should expand
+ *   horizontally if the grid as a whole is given more width than
+ *   it requested.
+ * @NMT_NEWT_GRID_EXPAND_Y: The widget's cell should expand
+ *   vertically if the grid as a whole is given more height than
+ *   it requested.
+ * @NMT_NEWT_GRID_ANCHOR_LEFT: If the widget's cell is wider than
+ *   the widget requested, the widget should be anchored to the
+ *   left of its cell rather than being centered.
+ * @NMT_NEWT_GRID_ANCHOR_RIGHT: If the widget's cell is wider than
+ *   the widget requested, the widget should be anchored to the
+ *   right of its cell rather than being centered.
+ * @NMT_NEWT_GRID_FILL_X: If the widget's cell is wider than
+ *   the widget requested, the widget should be allocated the
+ *   full width of the cell; this is equivalent to specifying
+ *   both %NMT_NEWT_GRID_ANCHOR_LEFT and %NMT_NEWT_GRID_ANCHOR_RIGHT.
+ * @NMT_NEWT_GRID_ANCHOR_TOP: If the widget's cell is taller than
+ *   the widget requested, the widget should be anchored to the
+ *   top of its cell rather than being centered.
+ * @NMT_NEWT_GRID_ANCHOR_BOTTOM: If the widget's cell is taller than
+ *   the widget requested, the widget should be anchored to the
+ *   bottom of its cell rather than being centered.
+ * @NMT_NEWT_GRID_FILL_Y: If the widget's cell is taller than
+ *   the widget requested, the widget should be allocated the
+ *   full height of the cell; this is equivalent to specifying
+ *   both %NMT_NEWT_GRID_ANCHOR_TOP and %NMT_NEWT_GRID_ANCHOR_BOTTOM.
+ *
+ * Flags describing how a widget is placed within its grid cell.
+ */
+
+/**
+ * nmt_newt_grid_set_flags:
+ * @grid: an #NmtNewtGrid
+ * @widget: a child of @grid
+ * @flags: #NmtNewtGridFlags for @widget
+ *
+ * Sets the #NmtNewtGridFlags on @widget
+ */
+void
+nmt_newt_grid_set_flags (NmtNewtGrid      *grid,
+                         NmtNewtWidget    *widget,
+                         NmtNewtGridFlags  flags)
+{
+	NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
+	NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
+	int i;
+
+	i = find_child (grid, widget);
+	if (i != -1)
+		children[i].flags = flags;
+}
+
+static void
+nmt_newt_grid_class_init (NmtNewtGridClass *grid_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (grid_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (grid_class);
+	NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (grid_class);
+
+	g_type_class_add_private (grid_class, sizeof (NmtNewtGridPrivate));
+
+	/* virtual methods */
+	object_class->finalize = nmt_newt_grid_finalize;
+
+	widget_class->get_components = nmt_newt_grid_get_components;
+	widget_class->size_request   = nmt_newt_grid_size_request;
+	widget_class->size_allocate  = nmt_newt_grid_size_allocate;
+
+	container_class->remove = nmt_newt_grid_remove;
+}
diff --git a/clients/tui/newt/nmt-newt-grid.h b/clients/tui/newt/nmt-newt-grid.h
new file mode 100644
index 00000000..f36a38ee
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-grid.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_GRID_H
+#define NMT_NEWT_GRID_H
+
+#include "nmt-newt-container.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_GRID            (nmt_newt_grid_get_type ())
+#define NMT_NEWT_GRID(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_GRID, NmtNewtGrid))
+#define NMT_NEWT_GRID_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_GRID, NmtNewtGridClass))
+#define NMT_IS_NEWT_GRID(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_GRID))
+#define NMT_IS_NEWT_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_GRID))
+#define NMT_NEWT_GRID_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_GRID, NmtNewtGridClass))
+
+struct _NmtNewtGrid {
+	NmtNewtContainer parent;
+
+};
+
+typedef struct {
+	NmtNewtContainerClass parent;
+
+} NmtNewtGridClass;
+
+GType nmt_newt_grid_get_type (void);
+
+typedef enum {
+	NMT_NEWT_GRID_EXPAND_X      = (1 << 0),
+	NMT_NEWT_GRID_EXPAND_Y      = (1 << 1),
+	NMT_NEWT_GRID_ANCHOR_LEFT   = (1 << 2),
+	NMT_NEWT_GRID_ANCHOR_RIGHT  = (1 << 3),
+	NMT_NEWT_GRID_FILL_X        = NMT_NEWT_GRID_ANCHOR_LEFT | NMT_NEWT_GRID_ANCHOR_RIGHT,
+	NMT_NEWT_GRID_ANCHOR_TOP    = (1 << 4),
+	NMT_NEWT_GRID_ANCHOR_BOTTOM = (1 << 5),
+	NMT_NEWT_GRID_FILL_Y        = NMT_NEWT_GRID_ANCHOR_TOP | NMT_NEWT_GRID_ANCHOR_BOTTOM,
+} NmtNewtGridFlags;
+
+NmtNewtWidget *nmt_newt_grid_new         (void);
+
+void           nmt_newt_grid_add         (NmtNewtGrid      *grid,
+                                          NmtNewtWidget    *widget,
+                                          int               x,
+                                          int               y);
+void           nmt_newt_grid_move        (NmtNewtGrid      *grid,
+                                          NmtNewtWidget    *widget,
+                                          int               x,
+                                          int               y);
+void           nmt_newt_grid_set_flags   (NmtNewtGrid      *grid,
+                                          NmtNewtWidget    *widget,
+                                          NmtNewtGridFlags  flags);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_GRID_H */
diff --git a/clients/tui/newt/nmt-newt-hacks.c b/clients/tui/newt/nmt-newt-hacks.c
new file mode 100644
index 00000000..2d9b1725
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-hacks.c
@@ -0,0 +1,103 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-hacks
+ * @short_description: Hacks!
+ *
+ * This contains hacky cheating implementations of certain newt
+ * functions that were added after 0.52.15.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-hacks.h"
+
+#if !defined (HAVE_NEWTCOMPONENTGETSIZE) || !defined (HAVE_NEWTENTRYGETCURSORPOSITION)
+struct newtComponent_0_52_15_struct_hack {
+	int height, width;
+	int top, left;
+	int takesFocus;
+	int isMapped;
+
+	struct componentOps *ops;
+
+	newtCallback callback;
+	void *callbackData;
+
+	newtCallback destroyCallback;
+	void *destroyCallbackData;
+
+	void *data;
+};
+#endif
+
+#ifndef HAVE_NEWTCOMPONENTGETSIZE
+void
+newtComponentGetSize (newtComponent  component,
+                      int           *width,
+                      int           *height)
+{
+	struct newtComponent_0_52_15_struct_hack *hack = (void *) component;
+
+	*width = hack->width;
+	*height = hack->height;
+}
+
+void
+newtComponentGetPosition (newtComponent  component,
+                          int           *left,
+                          int           *top)
+{
+	struct newtComponent_0_52_15_struct_hack *hack = (void *) component;
+
+	*left = hack->left;
+	*top = hack->top;
+}
+#endif
+
+#ifndef HAVE_NEWTENTRYGETCURSORPOSITION
+struct newtEntry_0_52_15_struct_hack {
+	int flags;
+	char *buf;
+	const char **resultPtr;
+	int bufAlloced;
+	int bufUsed;
+	int cursorPosition;
+	/* ... */
+};
+
+int
+newtEntryGetCursorPosition (newtComponent component)
+{
+	struct newtComponent_0_52_15_struct_hack *co_hack = (void *) component;
+	struct newtEntry_0_52_15_struct_hack *entry_hack = co_hack->data;
+
+	return entry_hack->cursorPosition;
+}
+
+void
+newtEntrySetCursorPosition (newtComponent component,
+                            int           position)
+{
+	struct newtComponent_0_52_15_struct_hack *co_hack = (void *) component;
+	struct newtEntry_0_52_15_struct_hack *entry_hack = co_hack->data;
+
+	entry_hack->cursorPosition = position;
+}
+#endif
diff --git a/clients/tui/newt/nmt-newt-hacks.h b/clients/tui/newt/nmt-newt-hacks.h
new file mode 100644
index 00000000..ba8464af
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-hacks.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_HACKS_H
+#define NMT_NEWT_HACKS_H
+
+#include "config.h"
+
+#include <newt.h>
+
+#ifndef HAVE_NEWTCOMPONENTGETSIZE
+void newtComponentGetSize     (newtComponent  component,
+                               int           *width,
+                               int           *height);
+
+void newtComponentGetPosition (newtComponent  component,
+                               int           *left,
+                               int           *top);
+#endif
+
+#ifndef HAVE_NEWTENTRYGETCURSORPOSITION
+int  newtEntryGetCursorPosition (newtComponent component);
+void newtEntrySetCursorPosition (newtComponent component,
+                                 int           position);
+#endif
+
+#endif /* NMT_NEWT_HACKS_H */
diff --git a/clients/tui/newt/nmt-newt-label.c b/clients/tui/newt/nmt-newt-label.c
new file mode 100644
index 00000000..9c4df8ed
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-label.c
@@ -0,0 +1,326 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-label
+ * @short_description: Labels
+ *
+ * #NmtNewtLabel implements a single-line label.
+ *
+ * See also #NmtNewtTextbox, for multiline.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-label.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtLabel, nmt_newt_label, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_LABEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_LABEL, NmtNewtLabelPrivate))
+
+typedef struct {
+	char *text;
+	NmtNewtLabelStyle style;
+	gboolean highlight;
+} NmtNewtLabelPrivate;
+
+enum {
+	PROP_0,
+	PROP_TEXT,
+	PROP_STYLE,
+	PROP_HIGHLIGHT,
+
+	LAST_PROP
+};
+
+/**
+ * nmt_newt_label_new:
+ * @text: the initial label text
+ *
+ * Creates a new #NmtNewtLabel
+ *
+ * Returns: a new #NmtNewtLabel
+ */
+NmtNewtWidget *
+nmt_newt_label_new (const char *text)
+{
+	return g_object_new (NMT_TYPE_NEWT_LABEL,
+	                     "text", text,
+	                     NULL);
+}
+
+/**
+ * nmt_newt_label_set_text:
+ * @label: an #NmtNewtLabel
+ * @text: the new text
+ *
+ * Updates @label's text.
+ */
+void
+nmt_newt_label_set_text (NmtNewtLabel *label,
+                         const char   *text)
+{
+	NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+	if (!g_strcmp0 (priv->text, text))
+		return;
+
+	g_free (priv->text);
+	priv->text = g_strdup (text);
+
+	g_object_notify (G_OBJECT (label), "text");
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label));
+}
+
+/**
+ * nmt_newt_label_get_text:
+ * @label: an #NmtNewtLabel
+ *
+ * Gets @label's text
+ *
+ * Returns: @label's text
+ */
+const char *
+nmt_newt_label_get_text (NmtNewtLabel *label)
+{
+	NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+	return priv->text;
+}
+
+/**
+ * NmtNewtLabelStyle:
+ * @NMT_NEWT_LABEL_NORMAL: a normal label
+ * @NMT_NEWT_LABEL_PLAIN: a "plain-looking" label
+ *
+ * The label style. Normal labels are blue. Plain labels are black,
+ * making them look more like they are text in their own right rather
+ * than just being a label for something else.
+ */
+
+/**
+ * nmt_newt_label_set_style:
+ * @label: an #NmtNewtLabel
+ * @style: the #NmtNewtLabelStyle
+ *
+ * Sets the style of @label
+ */
+void
+nmt_newt_label_set_style (NmtNewtLabel      *label,
+                          NmtNewtLabelStyle  style)
+{
+	NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+	if (priv->style == style)
+		return;
+
+	priv->style = style;
+	g_object_notify (G_OBJECT (label), "style");
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label));
+}
+
+/**
+ * nmt_newt_label_get_style:
+ * @label: an #NmtNewtLabel
+ *
+ * Gets the style of @label
+ *
+ * Returns: the style of @label
+ */
+NmtNewtLabelStyle
+nmt_newt_label_get_style (NmtNewtLabel *label)
+{
+	NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+	return priv->style;
+}
+
+/**
+ * nmt_newt_label_set_highlight:
+ * @label: an #NmtNewtLabel
+ * @highlight: %TRUE if @label should be highlighted
+ *
+ * Sets whether @label is highlighted. Highlighted labels are red;
+ * this is generally used to highlight invalid widgets.
+ */
+void
+nmt_newt_label_set_highlight (NmtNewtLabel *label,
+                              gboolean      highlight)
+{
+	NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+	highlight = !!highlight;
+	if (priv->highlight == highlight)
+		return;
+
+	priv->highlight = highlight;
+	g_object_notify (G_OBJECT (label), "highlight");
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label));
+}
+
+/**
+ * nmt_newt_label_get_highlight:
+ * @label: an #NmtNewtLabel
+ *
+ * Gets whether @label is highlighted.
+ *
+ * Returns: whether @label is highlighted.
+ */
+gboolean
+nmt_newt_label_get_highlight (NmtNewtLabel *label)
+{
+	NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label);
+
+	return priv->highlight;
+}
+
+static void
+nmt_newt_label_init (NmtNewtLabel *label)
+{
+}
+
+static void
+nmt_newt_label_finalize (GObject *object)
+{
+	NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (object);
+
+	g_free (priv->text);
+
+	G_OBJECT_CLASS (nmt_newt_label_parent_class)->finalize (object);
+}
+
+static newtComponent
+nmt_newt_label_build_component (NmtNewtComponent *component,
+                                gboolean          sensitive)
+{
+	NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (component);
+	newtComponent co;
+	char *text_lc;
+
+	text_lc = nmt_newt_locale_from_utf8 (priv->text);
+	co = newtLabel (-1, -1, text_lc);
+	g_free (text_lc);
+
+	if (priv->highlight)
+		newtLabelSetColors (co, NMT_NEWT_COLORSET_BAD_LABEL);
+	else if (priv->style == NMT_NEWT_LABEL_PLAIN)
+		newtLabelSetColors (co, NMT_NEWT_COLORSET_PLAIN_LABEL);
+
+	return co;
+}
+
+static void
+nmt_newt_label_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+	NmtNewtLabel *label = NMT_NEWT_LABEL (object);
+
+	switch (prop_id) {
+	case PROP_TEXT:
+		nmt_newt_label_set_text (label, g_value_get_string (value));
+		break;
+	case PROP_STYLE:
+		nmt_newt_label_set_style (label, g_value_get_int (value));
+		break;
+	case PROP_HIGHLIGHT:
+		nmt_newt_label_set_highlight (label, g_value_get_boolean (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_label_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+	NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_TEXT:
+		g_value_set_string (value, priv->text);
+		break;
+	case PROP_STYLE:
+		g_value_set_int (value, priv->style);
+		break;
+	case PROP_HIGHLIGHT:
+		g_value_set_boolean (value, priv->highlight);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_label_class_init (NmtNewtLabelClass *label_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (label_class);
+	NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (label_class);
+
+	g_type_class_add_private (label_class, sizeof (NmtNewtLabelPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_label_set_property;
+	object_class->get_property = nmt_newt_label_get_property;
+	object_class->finalize     = nmt_newt_label_finalize;
+
+	component_class->build_component = nmt_newt_label_build_component;
+
+	/**
+	 * NmtNewtLabel:text:
+	 *
+	 * The label's text
+	 */
+	g_object_class_install_property
+		(object_class, PROP_TEXT,
+		 g_param_spec_string ("text", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtLabel:style:
+	 *
+	 * The label's #NmtNewtLabelStyle
+	 */
+	g_object_class_install_property
+		(object_class, PROP_STYLE,
+		 g_param_spec_int ("style", "", "",
+		                   0, G_MAXINT, 0,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtLabel:highlight:
+	 *
+	 * Whether the label is highlighted.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_HIGHLIGHT,
+		 g_param_spec_boolean ("highlight", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-label.h b/clients/tui/newt/nmt-newt-label.h
new file mode 100644
index 00000000..a4e0dcca
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-label.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_LABEL_H
+#define NMT_NEWT_LABEL_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_LABEL            (nmt_newt_label_get_type ())
+#define NMT_NEWT_LABEL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabel))
+#define NMT_NEWT_LABEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_LABEL, NmtNewtLabelClass))
+#define NMT_IS_NEWT_LABEL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_LABEL))
+#define NMT_IS_NEWT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_LABEL))
+#define NMT_NEWT_LABEL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabelClass))
+
+struct _NmtNewtLabel {
+	NmtNewtComponent parent;
+
+};
+
+typedef struct {
+	NmtNewtComponentClass parent;
+
+} NmtNewtLabelClass;
+
+GType nmt_newt_label_get_type (void);
+
+typedef enum {
+	NMT_NEWT_LABEL_NORMAL,
+	NMT_NEWT_LABEL_PLAIN
+} NmtNewtLabelStyle;
+
+NmtNewtWidget     *nmt_newt_label_new           (const char        *text);
+
+void               nmt_newt_label_set_text      (NmtNewtLabel      *label,
+                                                 const char        *text);
+const char        *nmt_newt_label_get_text      (NmtNewtLabel      *label);
+
+void               nmt_newt_label_set_style     (NmtNewtLabel      *label,
+                                                 NmtNewtLabelStyle  style);
+NmtNewtLabelStyle  nmt_newt_label_get_style     (NmtNewtLabel      *label);
+
+void               nmt_newt_label_set_highlight (NmtNewtLabel      *label,
+                                                 gboolean           highlight);
+gboolean           nmt_newt_label_get_highlight (NmtNewtLabel      *label);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_LABEL_H */
diff --git a/clients/tui/newt/nmt-newt-listbox.c b/clients/tui/newt/nmt-newt-listbox.c
new file mode 100644
index 00000000..e450df91
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-listbox.c
@@ -0,0 +1,548 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-listbox
+ * @short_description: Single-choice listboxes
+ *
+ * #NmtNewtListbox implements a single-choice listbox.
+ *
+ * A listbox has some number of rows, each associated with an
+ * arbitrary pointer value. The pointer values do not need to be
+ * unique, but some APIs will not be usable if they aren't. You
+ * can also cause rows with %NULL keys to be treated specially.
+ *
+ * The listbox will emit #NmtNewtWidget::activate when the user
+ * presses Return on a selection.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-listbox.h"
+#include "nmt-newt-form.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtListbox, nmt_newt_listbox, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_LISTBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxPrivate))
+
+typedef struct {
+	int height, alloc_height, width;
+	gboolean fixed_height;
+	NmtNewtListboxFlags flags;
+
+	GPtrArray *entries;
+	GPtrArray *keys;
+
+	int active;
+	gpointer active_key;
+	gboolean skip_null_keys;
+
+} NmtNewtListboxPrivate;
+
+enum {
+	PROP_0,
+	PROP_HEIGHT,
+	PROP_FLAGS,
+	PROP_ACTIVE,
+	PROP_ACTIVE_KEY,
+	PROP_SKIP_NULL_KEYS,
+
+	LAST_PROP
+};
+
+/**
+ * NmtNewtListboxFlags:
+ * @NMT_NEWT_LISTBOX_SCROLL: the listbox should have a scroll bar.
+ * @NMT_NEWT_LISTBOX_BORDER: the listbox should have a border around it.
+ *
+ * Flags describing an #NmtNewtListbox
+ */
+
+/**
+ * nmt_newt_listbox_new:
+ * @height: the height of the listbox, or -1 for no fixed height
+ * @flags: the listbox flags
+ *
+ * Creates a new #NmtNewtListbox
+ *
+ * Returns: a new #NmtNewtListbox
+ */
+NmtNewtWidget *
+nmt_newt_listbox_new (int                 height,
+                      NmtNewtListboxFlags flags)
+{
+	return g_object_new (NMT_TYPE_NEWT_LISTBOX,
+	                     "height", height,
+	                     "flags", flags,
+	                     NULL);
+}
+
+/**
+ * nmt_newt_listbox_append:
+ * @listbox: an #NmtNewtListbox
+ * @entry: the text for the new row
+ * @key: (allow-none): the key associated with @entry
+ *
+ * Adds a row to @listbox.
+ */
+void
+nmt_newt_listbox_append (NmtNewtListbox *listbox,
+                         const char     *entry,
+                         gpointer        key)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+	g_ptr_array_add (priv->entries, nmt_newt_locale_from_utf8 (entry));
+	g_ptr_array_add (priv->keys, key);
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (listbox));
+}
+
+/**
+ * nmt_newt_listbox_clear:
+ * @listbox: an #NmtNewtListbox
+ *
+ * Clears the contents of @listbox.
+ */
+void
+nmt_newt_listbox_clear (NmtNewtListbox *listbox)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+	g_ptr_array_set_size (priv->entries, 0);
+	g_ptr_array_set_size (priv->keys, 0);
+
+	priv->active = -1;
+	priv->active_key = NULL;
+
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (listbox));
+}
+
+/**
+ * nmt_newt_listbox_set_active:
+ * @listbox: an #NmtNewtListbox
+ * @active: the row to make active
+ *
+ * Sets @active to be the currently-selected row in @listbox,
+ * scrolling it into view if needed.
+ */
+void
+nmt_newt_listbox_set_active (NmtNewtListbox *listbox,
+                             int             active)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+	if (active == priv->active)
+		return;
+
+	g_return_if_fail (active >= 0 && active < priv->entries->len);
+	g_return_if_fail (!priv->skip_null_keys || priv->keys->pdata[active]);
+
+	priv->active = active;
+	priv->active_key = priv->keys->pdata[active];
+
+	g_object_notify (G_OBJECT (listbox), "active");
+	g_object_notify (G_OBJECT (listbox), "active-key");
+}
+
+/**
+ * nmt_newt_listbox_set_active_key:
+ * @listbox: an #NmtNewtListbox
+ * @active_key: the key for the row to make active
+ *
+ * Selects the (first) row in @listbox with @active_key as its key,
+ * scrolling it into view if needed.
+ */
+void
+nmt_newt_listbox_set_active_key (NmtNewtListbox *listbox,
+                                 gpointer        active_key)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+	int i;
+
+	if (active_key == priv->active_key)
+		return;
+
+	g_return_if_fail (!priv->skip_null_keys || active_key);
+
+	for (i = 0; i < priv->keys->len; i++) {
+		if (priv->keys->pdata[i] == active_key) {
+			priv->active = i;
+			priv->active_key = active_key;
+
+			g_object_notify (G_OBJECT (listbox), "active");
+			g_object_notify (G_OBJECT (listbox), "active-key");
+			return;
+		}
+	}
+}
+
+/**
+ * nmt_newt_listbox_get_active:
+ * @listbox: an #NmtNewtListbox
+ *
+ * Gets the currently-selected row in @listbox.
+ *
+ * Returns: the currently-selected row in @listbox.
+ */
+int
+nmt_newt_listbox_get_active (NmtNewtListbox *listbox)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+	return priv->active;
+}
+
+/**
+ * nmt_newt_listbox_get_active_key:
+ * @listbox: an #NmtNewtListbox
+ *
+ * Gets the key of the currently-selected row in @listbox.
+ *
+ * Returns: the key of the currently-selected row in @listbox.
+ */
+gpointer
+nmt_newt_listbox_get_active_key (NmtNewtListbox *listbox)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+	return priv->active_key;
+}
+
+/**
+ * nmt_newt_listbox_set_height:
+ * @listbox: an #NmtNewtListbox
+ * @height: the new height, or -1 for no fixed heigh
+ *
+ * Updates @listbox's height.
+ */
+void
+nmt_newt_listbox_set_height (NmtNewtListbox *listbox,
+                             int             height)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+	priv->height = height;
+	priv->fixed_height = priv->height != 0;
+	g_object_notify (G_OBJECT (listbox), "height");
+}
+
+static void
+nmt_newt_listbox_init (NmtNewtListbox *listbox)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+	priv->entries = g_ptr_array_new_with_free_func (g_free);
+	priv->keys = g_ptr_array_new ();
+
+	priv->active = -1;
+}
+
+static void
+nmt_newt_listbox_finalize (GObject *object)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object);
+
+	g_ptr_array_unref (priv->entries);
+	g_ptr_array_unref (priv->keys);
+
+	G_OBJECT_CLASS (nmt_newt_listbox_parent_class)->finalize (object);
+}
+
+static void
+nmt_newt_listbox_size_request (NmtNewtWidget *widget,
+                               int           *width,
+                               int           *height)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (widget);
+
+	NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)->
+		size_request (widget, width, height);
+
+	priv->alloc_height = -1;
+	if (!priv->fixed_height)
+		*height = 1;
+	priv->width = *width;
+}
+
+static void
+nmt_newt_listbox_size_allocate (NmtNewtWidget *widget,
+                                int            x,
+                                int            y,
+                                int            width,
+                                int            height)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (widget);
+
+	if (width > priv->width) {
+		newtListboxSetWidth (nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget)),
+		                     width);
+	}
+
+	NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)->
+		size_allocate (widget, x, y, width, height);
+
+	priv->alloc_height = height;
+
+	if (!priv->fixed_height && height != priv->height) {
+		priv->height = height;
+		nmt_newt_widget_needs_rebuild (widget);
+	}
+}
+
+static void
+update_active_internal (NmtNewtListbox *listbox,
+                        int             new_active)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+
+	if (priv->active == new_active)
+		return;
+	if (new_active >= priv->keys->len)
+		return;
+
+	if (priv->skip_null_keys && !priv->keys->pdata[new_active]) {
+		if (new_active > priv->active) {
+			while (   new_active < priv->entries->len
+			       && !priv->keys->pdata[new_active])
+				new_active++;
+		} else {
+			while (   new_active >= 0
+			       && !priv->keys->pdata[new_active])
+				new_active--;
+		}
+
+		if (   new_active < 0
+		    || new_active >= priv->entries->len
+		    || !priv->keys->pdata[new_active]) {
+			g_assert (priv->active >= 0 && priv->active < priv->entries->len);
+			return;
+		}
+	}
+
+	nmt_newt_listbox_set_active (listbox, new_active);
+}
+
+static void
+selection_changed_callback (newtComponent  co,
+                            void          *user_data)
+{
+	NmtNewtListbox *listbox = user_data;
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox);
+	int new_active;
+
+	new_active = GPOINTER_TO_UINT (newtListboxGetCurrent (co));
+	update_active_internal (listbox, new_active);
+
+	if (priv->active != new_active)
+		newtListboxSetCurrent (co, priv->active);
+}
+
+static guint
+convert_flags (NmtNewtListboxFlags flags)
+{
+	guint newt_flags = NEWT_FLAG_RETURNEXIT;
+
+	if (flags & NMT_NEWT_LISTBOX_SCROLL)
+		newt_flags |= NEWT_FLAG_SCROLL;
+	if (flags & NMT_NEWT_LISTBOX_BORDER)
+		newt_flags |= NEWT_FLAG_BORDER;
+
+	return newt_flags;
+}
+
+static newtComponent
+nmt_newt_listbox_build_component (NmtNewtComponent *component,
+                                  gboolean          sensitive)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (component);
+	newtComponent co;
+	int i, active;
+
+	if (priv->active == -1)
+		update_active_internal (NMT_NEWT_LISTBOX (component), 0);
+	active = priv->active;
+
+	co = newtListbox (-1, -1, priv->height, convert_flags (priv->flags));
+	newtComponentAddCallback (co, selection_changed_callback, component);
+
+	for (i = 0; i < priv->entries->len; i++) {
+		newtListboxAppendEntry (co, priv->entries->pdata[i], GUINT_TO_POINTER (i));
+		if (active == -1 && priv->keys->pdata[i] == priv->active_key)
+			active = i;
+	}
+
+	if (active != -1)
+		newtListboxSetCurrent (co, active);
+
+	return co;
+}
+
+static void
+nmt_newt_listbox_activated (NmtNewtWidget *widget)
+{
+	NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (widget);
+	newtComponent co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget));
+
+	nmt_newt_listbox_set_active (listbox, GPOINTER_TO_UINT (newtListboxGetCurrent (co)));
+
+	NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)->activated (widget);
+}
+
+static void
+nmt_newt_listbox_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+	NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (object);
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_HEIGHT:
+		priv->height = g_value_get_int (value);
+		priv->fixed_height = (priv->height != 0);
+		break;
+	case PROP_FLAGS:
+		priv->flags = g_value_get_uint (value);
+		break;
+	case PROP_ACTIVE:
+		nmt_newt_listbox_set_active (listbox, g_value_get_int (value));
+		break;
+	case PROP_ACTIVE_KEY:
+		nmt_newt_listbox_set_active_key (listbox, g_value_get_pointer (value));
+		break;
+	case PROP_SKIP_NULL_KEYS:
+		priv->skip_null_keys = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_listbox_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+	NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_HEIGHT:
+		g_value_set_int (value, priv->height);
+		break;
+	case PROP_FLAGS:
+		g_value_set_uint (value, priv->flags);
+		break;
+	case PROP_ACTIVE:
+		g_value_set_int (value, priv->active);
+		break;
+	case PROP_ACTIVE_KEY:
+		g_value_set_pointer (value, priv->active_key);
+		break;
+	case PROP_SKIP_NULL_KEYS:
+		g_value_set_boolean (value, priv->skip_null_keys);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_listbox_class_init (NmtNewtListboxClass *listbox_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (listbox_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (listbox_class);
+	NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (listbox_class);
+
+	g_type_class_add_private (listbox_class, sizeof (NmtNewtListboxPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_listbox_set_property;
+	object_class->get_property = nmt_newt_listbox_get_property;
+	object_class->finalize     = nmt_newt_listbox_finalize;
+
+	widget_class->size_request  = nmt_newt_listbox_size_request;
+	widget_class->size_allocate = nmt_newt_listbox_size_allocate;
+	widget_class->activated     = nmt_newt_listbox_activated;
+
+	component_class->build_component = nmt_newt_listbox_build_component;
+
+	/* properties */
+
+	/**
+	 * NmtNewtListbox:height:
+	 *
+	 * The listbox's height, or -1 if it has no fixed height.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_HEIGHT,
+		 g_param_spec_int ("height", "", "",
+		                   -1, 255, -1,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtListbox:flags:
+	 *
+	 * The listbox's #NmtNewtListboxFlags.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_FLAGS,
+		 g_param_spec_uint ("flags", "", "",
+		                    0, 0xFFFF, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtListbox:active:
+	 *
+	 * The currently-selected row.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE,
+		 g_param_spec_int ("active", "", "",
+		                   0, G_MAXINT, 0,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtListbox:active-key:
+	 *
+	 * The key of the currently-selected row.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE_KEY,
+		 g_param_spec_pointer ("active-key", "", "",
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtListbox:skip-null-keys:
+	 *
+	 * If %TRUE, rows with %NULL key values will be skipped over when
+	 * navigating the list with the arrow keys.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_SKIP_NULL_KEYS,
+		 g_param_spec_boolean ("skip-null-keys", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-listbox.h b/clients/tui/newt/nmt-newt-listbox.h
new file mode 100644
index 00000000..c18c9f8e
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-listbox.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_LISTBOX_H
+#define NMT_NEWT_LISTBOX_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_LISTBOX            (nmt_newt_listbox_get_type ())
+#define NMT_NEWT_LISTBOX(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListbox))
+#define NMT_NEWT_LISTBOX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass))
+#define NMT_IS_NEWT_LISTBOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_LISTBOX))
+#define NMT_IS_NEWT_LISTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_LISTBOX))
+#define NMT_NEWT_LISTBOX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass))
+
+struct _NmtNewtListbox {
+	NmtNewtComponent parent;
+
+};
+
+typedef struct {
+	NmtNewtComponentClass parent;
+
+} NmtNewtListboxClass;
+
+GType nmt_newt_listbox_get_type (void);
+
+typedef enum {
+	NMT_NEWT_LISTBOX_SCROLL = (1 << 0),
+	NMT_NEWT_LISTBOX_BORDER = (1 << 1)
+} NmtNewtListboxFlags;
+
+NmtNewtWidget *nmt_newt_listbox_new            (int                  height,
+                                                NmtNewtListboxFlags  flags);
+
+void           nmt_newt_listbox_set_height     (NmtNewtListbox      *listbox,
+                                                int                  height);
+
+void           nmt_newt_listbox_append         (NmtNewtListbox      *listbox,
+                                                const char          *entry,
+                                                gpointer             key);
+void           nmt_newt_listbox_clear          (NmtNewtListbox      *listbox);
+
+void           nmt_newt_listbox_set_active     (NmtNewtListbox      *listbox,
+                                                int                  active);
+void           nmt_newt_listbox_set_active_key (NmtNewtListbox      *listbox,
+                                                gpointer             active_key);
+
+int            nmt_newt_listbox_get_active     (NmtNewtListbox      *listbox);
+gpointer       nmt_newt_listbox_get_active_key (NmtNewtListbox      *listbox);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_LISTBOX_H */
diff --git a/clients/tui/newt/nmt-newt-popup.c b/clients/tui/newt/nmt-newt-popup.c
new file mode 100644
index 00000000..b604eaaa
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-popup.c
@@ -0,0 +1,360 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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_set_padding (listbox, 1, 0, 1, 0);
+
+	nmt_newt_widget_size_request (listbox, &list_w, &list_h);
+
+	g_object_get (nmt_newt_widget_get_form (widget),
+	              "x", &window_x,
+	              "y", &window_y,
+	              NULL);
+	newtComponentGetPosition (nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget)),
+	                          &button_x, &button_y);
+	/* (window_x + button_x) is the screen X coordinate of the newtComponent. A
+	 * newtButton labelled "Foo" is rendered as " <Foo>" (with a preceding
+	 * space), so the "F" is at (window_x + button_x + 2). We've added 1 column
+	 * of padding to the left of the listbox, so we need to position the popup
+	 * at (window_x + button_x + 1) in order for its text to be aligned with the
+	 * button's text. (The x and y coordinates given to NmtNewtForm are the
+	 * coordinates of the top left of the window content, ignoring the border
+	 * graphics.)
+	 */
+	window_x += button_x + 1;
+	window_y += button_y - priv->active;
+
+	form = g_object_new (NMT_TYPE_NEWT_FORM,
+	                     "x", window_x,
+	                     "y", window_y,
+	                     "width", list_w,
+	                     "height", list_h,
+	                     "padding", 0,
+	                     "escape-exits", TRUE,
+	                     NULL);
+	nmt_newt_form_set_content (form, listbox);
+
+	ret = nmt_newt_form_run_sync (form);
+	if (ret == listbox)
+		active = nmt_newt_listbox_get_active (NMT_NEWT_LISTBOX (listbox));
+	else
+		active = priv->active;
+
+	g_object_unref (form);
+
+	if (active != priv->active) {
+		priv->active = active;
+		g_object_notify (G_OBJECT (widget), "active");
+		g_object_notify (G_OBJECT (widget), "active-id");
+		nmt_newt_widget_needs_rebuild (widget);
+	}
+
+	NMT_NEWT_WIDGET_CLASS (nmt_newt_popup_parent_class)->activated (widget);
+}
+
+/**
+ * nmt_newt_popup_get_active:
+ * @popup: a #NmtNewtPopup
+ *
+ * Gets the index of the active entry in @popup.
+ *
+ * Returns: the index of the active entry in @popup.
+ */
+int
+nmt_newt_popup_get_active (NmtNewtPopup *popup)
+{
+	NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup);
+
+	return priv->active;
+}
+
+/**
+ * nmt_newt_popup_set_active:
+ * @popup: a #NmtNewtPopup
+ * @active: the index of the new active entry
+ *
+ * Sets the active entry in @popup.
+ */
+void
+nmt_newt_popup_set_active (NmtNewtPopup *popup,
+                           int           active)
+{
+	NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup);
+
+	active = CLAMP (active, 0, priv->entries->len - 1);
+
+	if (active != priv->active) {
+		priv->active = active;
+		g_object_notify (G_OBJECT (popup), "active");
+		g_object_notify (G_OBJECT (popup), "active-id");
+	}
+}
+
+/**
+ * nmt_newt_popup_get_active_id:
+ * @popup: a #NmtNewtPopup
+ *
+ * Gets the textual ID of the active entry in @popup.
+ *
+ * Returns: the ID of the active entry in @popup.
+ */
+const char *
+nmt_newt_popup_get_active_id (NmtNewtPopup *popup)
+{
+	NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup);
+	NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data;
+
+	return entries[priv->active].id;
+}
+
+/**
+ * nmt_newt_popup_set_active_id:
+ * @popup: a #NmtNewtPopup
+ * @active_id: the ID of the new active entry
+ *
+ * Sets the active entry in @popup.
+ */
+void
+nmt_newt_popup_set_active_id (NmtNewtPopup *popup,
+                              const char   *active_id)
+{
+	NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup);
+	NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data;
+	int i;
+
+	for (i = 0; i < priv->entries->len; i++) {
+		if (!g_strcmp0 (active_id, entries[i].id)) {
+			nmt_newt_popup_set_active (popup, i);
+			return;
+		}
+	}
+}
+
+static void
+nmt_newt_popup_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+	NmtNewtPopup *popup = NMT_NEWT_POPUP (object);
+
+	switch (prop_id) {
+	case PROP_ACTIVE:
+		nmt_newt_popup_set_active (popup, g_value_get_uint (value));
+		break;
+	case PROP_ACTIVE_ID:
+		nmt_newt_popup_set_active_id (popup, g_value_get_string (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_popup_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+	NmtNewtPopup *popup = NMT_NEWT_POPUP (object);
+
+	switch (prop_id) {
+	case PROP_ACTIVE:
+		g_value_set_uint (value, nmt_newt_popup_get_active (popup));
+		break;
+	case PROP_ACTIVE_ID:
+		g_value_set_string (value, nmt_newt_popup_get_active_id (popup));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_popup_class_init (NmtNewtPopupClass *popup_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (popup_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (popup_class);
+	NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (popup_class);
+
+	g_type_class_add_private (popup_class, sizeof (NmtNewtPopupPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_popup_set_property;
+	object_class->get_property = nmt_newt_popup_get_property;
+	object_class->finalize     = nmt_newt_popup_finalize;
+
+	widget_class->activated = nmt_newt_popup_activated;
+
+	component_class->build_component = nmt_newt_popup_build_component;
+
+	/**
+	 * NmtNewtPopup:active:
+	 *
+	 * The index of the currently-active entry.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE,
+		 g_param_spec_uint ("active", "", "",
+		                    0, G_MAXUINT, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtPopup:active-id:
+	 *
+	 * The textual ID of the currently-active entry.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE_ID,
+		 g_param_spec_string ("active-id", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-popup.h b/clients/tui/newt/nmt-newt-popup.h
new file mode 100644
index 00000000..643ea2d8
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-popup.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_POPUP_H
+#define NMT_NEWT_POPUP_H
+
+#include "nmt-newt-button.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_POPUP            (nmt_newt_popup_get_type ())
+#define NMT_NEWT_POPUP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopup))
+#define NMT_NEWT_POPUP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_POPUP, NmtNewtPopupClass))
+#define NMT_IS_NEWT_POPUP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_POPUP))
+#define NMT_IS_NEWT_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_POPUP))
+#define NMT_NEWT_POPUP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopupClass))
+
+struct _NmtNewtPopup {
+	NmtNewtButton parent;
+
+};
+
+typedef struct {
+	NmtNewtButtonClass parent;
+
+} NmtNewtPopupClass;
+
+GType nmt_newt_popup_get_type (void);
+
+typedef struct {
+	char *label;
+	char *id;
+} NmtNewtPopupEntry;
+
+NmtNewtWidget *nmt_newt_popup_new           (NmtNewtPopupEntry *entries);
+
+int            nmt_newt_popup_get_active    (NmtNewtPopup      *popup);
+void           nmt_newt_popup_set_active    (NmtNewtPopup      *popup,
+                                             int                active);
+
+const char    *nmt_newt_popup_get_active_id (NmtNewtPopup      *popup);
+void           nmt_newt_popup_set_active_id (NmtNewtPopup      *popup,
+                                             const char        *active_id);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_POPUP_H */
diff --git a/clients/tui/newt/nmt-newt-section.c b/clients/tui/newt/nmt-newt-section.c
new file mode 100644
index 00000000..1a5035fc
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-section.c
@@ -0,0 +1,447 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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
+ * optionally 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;
+
+	gboolean show_border;
+	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_SHOW_BORDER,
+	PROP_OPEN,
+
+	LAST_PROP
+};
+
+/**
+ * nmt_newt_section_new:
+ * @show_border: whether to show the border on the side of the section
+ *
+ * Creates a new #NmtNewtSection
+ *
+ * Returns: a new #NmtNewtSection
+ */
+NmtNewtWidget *
+nmt_newt_section_new (gboolean show_border)
+{
+	return g_object_new (NMT_TYPE_NEWT_SECTION,
+	                     "show-border", show_border,
+	                     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->show_border = TRUE;
+
+	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 ();
+
+	if (priv->show_border) {
+		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 w_ignore, h_ignore;
+
+	g_return_if_fail (priv->header != NULL && priv->body != NULL);
+
+	if (priv->show_border)
+		nmt_newt_widget_size_request (priv->border_grid, &w_ignore, &h_ignore);
+	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;
+	if (priv->open)
+		*height = priv->hheight_req + priv->bheight_req + (priv->show_border ? 1 : 0);
+	else
+		*height = 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);
+
+	if (priv->show_border) {
+		int w_ignore, h_ignore;
+
+		adjust_border_for_allocation (priv, height);
+		nmt_newt_widget_size_request (priv->border_grid, &w_ignore, &h_ignore);
+		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);
+	} else
+		nmt_newt_widget_size_allocate (priv->header, x, 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_SHOW_BORDER:
+		priv->show_border = g_value_get_boolean (value);
+		nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (object));
+		break;
+	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_SHOW_BORDER:
+		g_value_set_boolean (value, priv->show_border);
+		break;
+	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:show-border:
+	 *
+	 * %TRUE if the section should show a border along the left side.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_SHOW_BORDER,
+		 g_param_spec_boolean ("show-border", "", "",
+		                       TRUE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NmtNewtSection:open:
+	 *
+	 * %TRUE if the section is open (ie, its body is visible), %FALSE
+	 * if not.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_OPEN,
+		 g_param_spec_boolean ("open", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+
+	/* globals */
+	closed_glyph = nmt_newt_locale_from_utf8 ("\342\225\220"); /* ═ */
+	open_glyph   = nmt_newt_locale_from_utf8 ("\342\225\244"); /* ╤ */
+	line_glyph   = nmt_newt_locale_from_utf8 ("\342\224\202"); /* │ */
+	end_glyph    = nmt_newt_locale_from_utf8 ("\342\224\224"); /* └ */
+	if (!*closed_glyph || !*open_glyph || !*line_glyph || !*end_glyph) {
+		g_free (closed_glyph);
+		g_free (open_glyph);
+		g_free (line_glyph);
+		g_free (end_glyph);
+
+		closed_glyph = g_strdup ("-");
+		open_glyph   = g_strdup ("+");
+		line_glyph   = g_strdup ("|");
+		end_glyph    = g_strdup ("\\");
+	}
+}
diff --git a/clients/tui/newt/nmt-newt-section.h b/clients/tui/newt/nmt-newt-section.h
new file mode 100644
index 00000000..6bf59ebb
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-section.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_SECTION_H
+#define NMT_NEWT_SECTION_H
+
+#include "nmt-newt-container.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_SECTION            (nmt_newt_section_get_type ())
+#define NMT_NEWT_SECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSection))
+#define NMT_NEWT_SECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass))
+#define NMT_IS_NEWT_SECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_SECTION))
+#define NMT_IS_NEWT_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_SECTION))
+#define NMT_NEWT_SECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass))
+
+struct _NmtNewtSection {
+	NmtNewtContainer parent;
+
+};
+
+typedef struct {
+	NmtNewtContainerClass parent;
+
+} NmtNewtSectionClass;
+
+GType nmt_newt_section_get_type (void);
+
+NmtNewtWidget *nmt_newt_section_new        (gboolean        show_border);
+
+void           nmt_newt_section_set_header (NmtNewtSection *section,
+                                            NmtNewtWidget  *header);
+NmtNewtWidget *nmt_newt_section_get_header (NmtNewtSection *section);
+
+void           nmt_newt_section_set_body   (NmtNewtSection *section,
+                                            NmtNewtWidget  *body);
+NmtNewtWidget *nmt_newt_section_get_body   (NmtNewtSection *section);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_SECTION_H */
diff --git a/clients/tui/newt/nmt-newt-separator.c b/clients/tui/newt/nmt-newt-separator.c
new file mode 100644
index 00000000..07deb1af
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-separator.c
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-separator
+ * @short_description: Separator
+ *
+ * #NmtNewtSeparator is just a blank label, which is used in a few places
+ * where a widget is needed but none is desired, or to add blank space
+ * between widgets in containers that don't implement padding.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-separator.h"
+
+G_DEFINE_TYPE (NmtNewtSeparator, nmt_newt_separator, NMT_TYPE_NEWT_COMPONENT)
+
+/**
+ * nmt_newt_separator_new:
+ *
+ * Creates a new #NmtNewtSeparator.
+ *
+ * Returns: a new #NmtNewtSeparator
+ */
+NmtNewtWidget *
+nmt_newt_separator_new (void)
+{
+	return g_object_new (NMT_TYPE_NEWT_SEPARATOR, NULL);
+}
+
+static void
+nmt_newt_separator_init (NmtNewtSeparator *separator)
+{
+}
+
+static newtComponent
+nmt_newt_separator_build_component (NmtNewtComponent *component,
+                                    gboolean          sensitive)
+{
+	return newtLabel (-1, -1, " ");
+}
+
+static void
+nmt_newt_separator_class_init (NmtNewtSeparatorClass *separator_class)
+{
+	NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (separator_class);
+
+	/* virtual methods */
+	component_class->build_component = nmt_newt_separator_build_component;
+}
diff --git a/clients/tui/newt/nmt-newt-separator.h b/clients/tui/newt/nmt-newt-separator.h
new file mode 100644
index 00000000..3f4183d5
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-separator.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_SEPARATOR_H
+#define NMT_NEWT_SEPARATOR_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_SEPARATOR            (nmt_newt_separator_get_type ())
+#define NMT_NEWT_SEPARATOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparator))
+#define NMT_NEWT_SEPARATOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparatorClass))
+#define NMT_IS_NEWT_SEPARATOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_SEPARATOR))
+#define NMT_IS_NEWT_SEPARATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_SEPARATOR))
+#define NMT_NEWT_SEPARATOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparatorClass))
+
+struct _NmtNewtSeparator {
+  NmtNewtComponent parent;
+
+};
+
+typedef struct {
+  NmtNewtComponentClass parent;
+
+} NmtNewtSeparatorClass;
+
+GType nmt_newt_separator_get_type (void);
+
+NmtNewtWidget *nmt_newt_separator_new (void);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_SEPARATOR_H */
diff --git a/clients/tui/newt/nmt-newt-stack.c b/clients/tui/newt/nmt-newt-stack.c
new file mode 100644
index 00000000..d94bdebf
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-stack.c
@@ -0,0 +1,368 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-stack
+ * @short_description: A stack of alternative widgets
+ *
+ * #NmtNewtStack implements a stack of widgets, only one of which is
+ * visible at any time.
+ *
+ * The height and width of the widget is determined only by its
+ * visible child. Likewise, the widget's #NmtNewtWidget:valid is
+ * determined only by the validity of its visible child, not its other
+ * children.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-stack.h"
+
+G_DEFINE_TYPE (NmtNewtStack, nmt_newt_stack, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_NEWT_STACK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_STACK, NmtNewtStackPrivate))
+
+typedef struct {
+	GPtrArray *children;
+	GPtrArray *ids;
+
+	guint active;
+} NmtNewtStackPrivate;
+
+enum {
+	PROP_0,
+	PROP_ACTIVE,
+	PROP_ACTIVE_ID,
+
+	LAST_PROP
+};
+
+/**
+ * nmt_newt_stack_new:
+ *
+ * Creates a new #NmtNewtStack
+ *
+ * Returns: a new #NmtNewtStack
+ */
+NmtNewtWidget *
+nmt_newt_stack_new (void)
+{
+	return g_object_new (NMT_TYPE_NEWT_STACK, NULL);
+}
+
+static void
+nmt_newt_stack_init (NmtNewtStack *stack)
+{
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+
+	priv->children = g_ptr_array_new ();
+	priv->ids = g_ptr_array_new_with_free_func (g_free);
+}
+
+static void
+nmt_newt_stack_finalize (GObject *object)
+{
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (object);
+
+	g_ptr_array_unref (priv->children);
+	g_ptr_array_unref (priv->ids);
+
+	G_OBJECT_CLASS (nmt_newt_stack_parent_class)->finalize (object);
+}
+
+static newtComponent *
+nmt_newt_stack_get_components (NmtNewtWidget *widget)
+{
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (widget);
+
+	if (priv->active > priv->children->len)
+		return NULL;
+
+	return nmt_newt_widget_get_components (priv->children->pdata[priv->active]);
+}
+
+static void
+nmt_newt_stack_size_request (NmtNewtWidget *widget,
+                             int           *width,
+                             int           *height)
+{
+	NmtNewtStack *stack = NMT_NEWT_STACK (widget);
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+	int i, child_width, child_height;
+
+	if (priv->active > priv->children->len) {
+		*width = *height = 0;
+		return;
+	}
+
+	/* We size-request all pages so that embedded NmtPageGrids will
+	 * participate in their size-grouping (so that switching pages
+	 * won't result in the column widths changing).
+	 */
+	for (i = 0; i < priv->children->len; i++) {
+		nmt_newt_widget_size_request (priv->children->pdata[i], &child_width, &child_height);
+		if (i == priv->active) {
+			*width = child_width;
+			*height = child_height;
+		}
+	}
+}
+
+static void
+nmt_newt_stack_size_allocate (NmtNewtWidget *widget,
+                              int            x,
+                              int            y,
+                              int            width,
+                              int            height)
+{
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (widget);
+
+	if (priv->active > priv->children->len)
+		return;
+
+	nmt_newt_widget_size_allocate (priv->children->pdata[priv->active], x, y, width, height);
+}
+
+/**
+ * nmt_newt_stack_add:
+ * @stack: an #NmtNewtStack
+ * @id: the ID for the new page
+ * @widget: the widget to add
+ *
+ * Adds @widget to @stack with the given @id.
+ */
+void
+nmt_newt_stack_add (NmtNewtStack  *stack,
+                    const char    *id,
+                    NmtNewtWidget *widget)
+{
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+
+	g_ptr_array_add (priv->children, widget);
+	g_ptr_array_add (priv->ids, g_strdup (id));
+
+	NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)->add (NMT_NEWT_CONTAINER (stack), widget);
+}
+
+static void
+nmt_newt_stack_remove (NmtNewtContainer *container,
+                       NmtNewtWidget    *widget)
+{
+	NmtNewtStack *stack = NMT_NEWT_STACK (container);
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+	int i;
+
+	NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)->remove (container, widget);
+
+	for (i = 0; i < priv->children->len; i++) {
+		if (priv->children->pdata[i] == widget) {
+			g_ptr_array_remove_index (priv->children, i);
+			g_ptr_array_remove_index (priv->ids, i);
+			return;
+		}
+	}
+}
+
+static void
+nmt_newt_stack_child_validity_changed (NmtNewtContainer *container,
+                                       NmtNewtWidget    *widget)
+{
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (container);
+
+	if (priv->active > priv->children->len)
+		return;
+
+	if (priv->children->pdata[priv->active] == (gpointer) widget) {
+		NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)->
+			child_validity_changed (container, widget);
+	}
+}
+
+/**
+ * nmt_newt_stack_set_active:
+ * @stack: an #NmtNewtStack
+ * @active: the index of the new active page
+ *
+ * Sets the active page on @stack to @active.
+ */
+void
+nmt_newt_stack_set_active (NmtNewtStack *stack,
+                           guint         active)
+{
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+
+	if (priv->active == active)
+		return;
+
+	priv->active = active;
+	g_object_notify (G_OBJECT (stack), "active");
+	g_object_notify (G_OBJECT (stack), "active-id");
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (stack));
+}
+
+/**
+ * nmt_newt_stack_get_active:
+ * @stack: an #NmtNewtStack
+ *
+ * Gets the index of the active page on @stack
+ *
+ * Returns: the index of the active page on @stack
+ */
+guint
+nmt_newt_stack_get_active (NmtNewtStack *stack)
+{
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+
+	return priv->active;
+}
+
+/**
+ * nmt_newt_stack_set_active_id:
+ * @stack: an #NmtNewtStack
+ * @active_id: the ID of the new active page
+ *
+ * Sets the active page on @stack to @active_id.
+ */
+void
+nmt_newt_stack_set_active_id (NmtNewtStack *stack,
+                              const char   *id)
+{
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+	int i;
+
+	if (!g_strcmp0 (priv->ids->pdata[priv->active], id))
+		return;
+
+	for (i = 0; i < priv->ids->len; i++) {
+		if (!g_strcmp0 (priv->ids->pdata[i], id)) {
+			priv->active = i;
+			g_object_notify (G_OBJECT (stack), "active");
+			g_object_notify (G_OBJECT (stack), "active-id");
+			nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (stack));
+			return;
+		}
+	}
+}
+
+/**
+ * nmt_newt_stack_get_active_id:
+ * @stack: an #NmtNewtStack
+ *
+ * Gets the ID of the active page on @stack
+ *
+ * Returns: the ID of the active page on @stack
+ */
+const char *
+nmt_newt_stack_get_active_id (NmtNewtStack *stack)
+{
+	NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack);
+
+	if (priv->active > priv->children->len)
+		return NULL;
+
+	return priv->ids->pdata[priv->active];
+}
+
+static void
+nmt_newt_stack_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+	NmtNewtStack *stack = NMT_NEWT_STACK (object);
+
+	switch (prop_id) {
+	case PROP_ACTIVE:
+		nmt_newt_stack_set_active (stack, g_value_get_uint (value));
+		break;
+	case PROP_ACTIVE_ID:
+		nmt_newt_stack_set_active_id (stack, g_value_get_string (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_stack_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+	NmtNewtStack *stack = NMT_NEWT_STACK (object);
+
+	switch (prop_id) {
+	case PROP_ACTIVE:
+		g_value_set_uint (value, nmt_newt_stack_get_active (stack));
+		break;
+	case PROP_ACTIVE_ID:
+		g_value_set_string (value, nmt_newt_stack_get_active_id (stack));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_stack_class_init (NmtNewtStackClass *stack_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (stack_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (stack_class);
+	NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (stack_class);
+
+	g_type_class_add_private (stack_class, sizeof (NmtNewtStackPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_stack_set_property;
+	object_class->get_property = nmt_newt_stack_get_property;
+	object_class->finalize = nmt_newt_stack_finalize;
+
+	widget_class->get_components = nmt_newt_stack_get_components;
+	widget_class->size_request   = nmt_newt_stack_size_request;
+	widget_class->size_allocate  = nmt_newt_stack_size_allocate;
+
+	container_class->remove = nmt_newt_stack_remove;
+	container_class->child_validity_changed = nmt_newt_stack_child_validity_changed;
+
+	/**
+	 * NmtNewtStack:active:
+	 *
+	 * The index of the active page
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE,
+		 g_param_spec_uint ("active", "", "",
+		                    0, G_MAXUINT, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtStack:active-id:
+	 *
+	 * The ID of the active page
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE_ID,
+		 g_param_spec_string ("active-id", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-stack.h b/clients/tui/newt/nmt-newt-stack.h
new file mode 100644
index 00000000..09afe4ba
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-stack.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_STACK_H
+#define NMT_NEWT_STACK_H
+
+#include "nmt-newt-container.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_STACK            (nmt_newt_stack_get_type ())
+#define NMT_NEWT_STACK(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_STACK, NmtNewtStack))
+#define NMT_NEWT_STACK_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_STACK, NmtNewtStackClass))
+#define NMT_IS_NEWT_STACK(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_STACK))
+#define NMT_IS_NEWT_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_STACK))
+#define NMT_NEWT_STACK_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_STACK, NmtNewtStackClass))
+
+struct _NmtNewtStack {
+	NmtNewtContainer parent;
+
+};
+
+typedef struct {
+	NmtNewtContainerClass parent;
+
+} NmtNewtStackClass;
+
+GType nmt_newt_stack_get_type (void);
+
+NmtNewtWidget *nmt_newt_stack_new           (void);
+
+void           nmt_newt_stack_add           (NmtNewtStack  *stack,
+                                             const char    *id,
+                                             NmtNewtWidget *widget);
+
+void           nmt_newt_stack_set_active    (NmtNewtStack  *stack,
+                                             guint          active);
+guint          nmt_newt_stack_get_active    (NmtNewtStack  *stack);
+
+void           nmt_newt_stack_set_active_id (NmtNewtStack  *stack,
+                                             const char    *id);
+const char *   nmt_newt_stack_get_active_id (NmtNewtStack  *stack);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_STACK_H */
diff --git a/clients/tui/newt/nmt-newt-textbox.c b/clients/tui/newt/nmt-newt-textbox.c
new file mode 100644
index 00000000..3d297160
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-textbox.c
@@ -0,0 +1,295 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-textbox
+ * @short_description: Multi-line text box
+ *
+ * #NmtNewtTextbox implements a multi-line text, optionally with
+ * word-wrapping.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-newt-textbox.h"
+#include "nmt-newt-utils.h"
+
+G_DEFINE_TYPE (NmtNewtTextbox, nmt_newt_textbox, NMT_TYPE_NEWT_COMPONENT)
+
+#define NMT_NEWT_TEXTBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxPrivate))
+
+typedef struct {
+	int wrap_width;
+	NmtNewtTextboxFlags flags;
+
+	char *text;
+	int width, height;
+} NmtNewtTextboxPrivate;
+
+enum {
+	PROP_0,
+	PROP_TEXT,
+	PROP_FLAGS,
+	PROP_WRAP_WIDTH,
+
+	LAST_PROP
+};
+
+/**
+ * NmtNewtTextboxFlags:
+ * @NMT_NEWT_TEXTBOX_SCROLLABLE: the textbox should be scollable.
+ * @NMT_NEWT_TEXTBOX_SET_BACKGROUND: the textbox should have a
+ *   white background
+ *
+ * Flags for an #NmtNewtTextbox
+ */
+
+/**
+ * nmt_newt_textbox_new:
+ * @flags: the textbox's flags
+ * @wrap_width: width in characters at which to word-wrap, or
+ *   0 to not wrap.
+ *
+ * Creates a new #NmtNewtTextbox.
+ *
+ * Returns: a new #NmtNewtTextbox
+ */
+NmtNewtWidget *
+nmt_newt_textbox_new (NmtNewtTextboxFlags flags,
+                      int                 wrap_width)
+{
+	return g_object_new (NMT_TYPE_NEWT_TEXTBOX,
+	                     "flags", flags,
+	                     "wrap-width", wrap_width,
+	                     NULL);
+}
+
+/**
+ * nmt_newt_textbox_get_text:
+ * @textbox: an #NmtNewtTextbox
+ *
+ * Gets @textbox's text
+ *
+ * Returns: @textbox's text
+ */
+void
+nmt_newt_textbox_set_text (NmtNewtTextbox *textbox,
+                           const char     *text)
+{
+	NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox);
+	char **lines;
+	int i, width;
+
+	if (!text)
+		text = "";
+	if (!strcmp (priv->text, text))
+		return;
+
+	g_free (priv->text);
+	priv->text = g_strdup (text);
+
+	priv->width = priv->height = 0;
+	lines = g_strsplit (priv->text, "\n", -1);
+	for (i = 0; lines[i]; i++) {
+		width = nmt_newt_text_width (lines[i]);
+		if (width > priv->width)
+			priv->width = width;
+	}
+	g_free (lines);
+	priv->height = MIN (i, 1);
+
+	g_object_notify (G_OBJECT (textbox), "text");
+	nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (textbox));
+}
+
+/**
+ * nmt_newt_textbox_get_text:
+ * @textbox: an #NmtNewtTextbox
+ *
+ * Gets @textbox's text
+ *
+ * Returns: @textbox's text
+ */
+const char *
+nmt_newt_textbox_get_text (NmtNewtTextbox *textbox)
+{
+	NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox);
+
+	return priv->text;
+}
+
+static void
+nmt_newt_textbox_init (NmtNewtTextbox *textbox)
+{
+	NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox);
+
+	priv->text = g_strdup ("");
+}
+
+static void
+nmt_newt_textbox_finalize (GObject *object)
+{
+	NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (object);
+
+	g_free (priv->text);
+
+	G_OBJECT_CLASS (nmt_newt_textbox_parent_class)->finalize (object);
+}
+
+static guint
+convert_flags (NmtNewtTextboxFlags flags)
+{
+	guint newt_flags = 0;
+
+	if (flags & NMT_NEWT_TEXTBOX_SCROLLABLE)
+		newt_flags |= NEWT_FLAG_SCROLL;
+
+	return newt_flags;
+}
+
+static newtComponent
+nmt_newt_textbox_build_component (NmtNewtComponent *component,
+                                gboolean          sensitive)
+{
+	NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (component);
+	newtComponent co;
+	const char *text;
+	char *text_lc;
+
+	text = priv->text;
+	if (!*text)
+		text = "\n";
+
+	text_lc = nmt_newt_locale_from_utf8 (text);
+	if (priv->wrap_width > 0) {
+		co = newtTextboxReflowed (-1, -1, text_lc, priv->wrap_width, 0, 0, 0);
+	} else {
+		co = newtTextbox (-1, -1, priv->width, priv->height, convert_flags (priv->flags));
+		newtTextboxSetText (co, text_lc);
+	}
+	g_free (text_lc);
+
+	if (priv->flags & NMT_NEWT_TEXTBOX_SET_BACKGROUND)
+		newtTextboxSetColors (co, NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, NEWT_COLORSET_ACTTEXTBOX);
+
+	return co;
+}
+
+static void
+nmt_newt_textbox_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+	NmtNewtTextbox *textbox = NMT_NEWT_TEXTBOX (object);
+	NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox);
+
+	switch (prop_id) {
+	case PROP_TEXT:
+		nmt_newt_textbox_set_text (textbox, g_value_get_string (value));
+		break;
+	case PROP_FLAGS:
+		priv->flags = g_value_get_uint (value);
+		break;
+	case PROP_WRAP_WIDTH:
+		priv->wrap_width = g_value_get_int (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_textbox_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+	NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_TEXT:
+		g_value_set_string (value, priv->text);
+		break;
+	case PROP_FLAGS:
+		g_value_set_uint (value, priv->flags);
+		break;
+	case PROP_WRAP_WIDTH:
+		g_value_set_int (value, priv->wrap_width);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_textbox_class_init (NmtNewtTextboxClass *textbox_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (textbox_class);
+	NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (textbox_class);
+
+	g_type_class_add_private (textbox_class, sizeof (NmtNewtTextboxPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_textbox_set_property;
+	object_class->get_property = nmt_newt_textbox_get_property;
+	object_class->finalize     = nmt_newt_textbox_finalize;
+
+	component_class->build_component = nmt_newt_textbox_build_component;
+
+	/**
+	 * NmtNewtTextbox:text:
+	 *
+	 * The textbox's text
+	 */
+	g_object_class_install_property
+		(object_class, PROP_TEXT,
+		 g_param_spec_string ("text", "", "",
+		                      "",
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtTextbox:flags:
+	 *
+	 * The textbox's flags
+	 */
+	g_object_class_install_property
+		(object_class, PROP_FLAGS,
+		 g_param_spec_uint ("flags", "", "",
+		                    0, G_MAXUINT, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtTextbox:wrap-width:
+	 *
+	 * The width in characters at which the textbox's text
+	 * will wrap, or 0 if it does not wrap.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_WRAP_WIDTH,
+		 g_param_spec_int ("wrap-width", "", "",
+		                   0, G_MAXINT, 0,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_CONSTRUCT_ONLY |
+		                   G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-textbox.h b/clients/tui/newt/nmt-newt-textbox.h
new file mode 100644
index 00000000..b3743aea
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-textbox.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_TEXTBOX_H
+#define NMT_NEWT_TEXTBOX_H
+
+#include "nmt-newt-component.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_TEXTBOX            (nmt_newt_textbox_get_type ())
+#define NMT_NEWT_TEXTBOX(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextbox))
+#define NMT_NEWT_TEXTBOX_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxClass))
+#define NMT_IS_NEWT_TEXTBOX(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_TEXTBOX))
+#define NMT_IS_NEWT_TEXTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_TEXTBOX))
+#define NMT_NEWT_TEXTBOX_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxClass))
+
+struct _NmtNewtTextbox {
+	NmtNewtComponent parent;
+
+};
+
+typedef struct {
+	NmtNewtComponentClass parent;
+
+} NmtNewtTextboxClass;
+
+GType nmt_newt_textbox_get_type (void);
+
+typedef enum {
+	NMT_NEWT_TEXTBOX_SCROLLABLE     = (1 << 0),
+	NMT_NEWT_TEXTBOX_SET_BACKGROUND = (1 << 1)
+} NmtNewtTextboxFlags;
+
+NmtNewtWidget *nmt_newt_textbox_new      (NmtNewtTextboxFlags  flags,
+                                          int                  wrap_width);
+
+void           nmt_newt_textbox_set_text (NmtNewtTextbox      *textbox,
+                                          const char          *text);
+const char    *nmt_newt_textbox_get_text (NmtNewtTextbox      *textbox);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_TEXTBOX_H */
diff --git a/clients/tui/newt/nmt-newt-toggle-button.c b/clients/tui/newt/nmt-newt-toggle-button.c
new file mode 100644
index 00000000..ec4d9aca
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-toggle-button.c
@@ -0,0 +1,237 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-toggle-button
+ * @short_description: Toggle buttons
+ *
+ * #NmtNewtToggleButton implements a two-state toggle button.
+ */
+
+#include "config.h"
+
+#include "nmt-newt-toggle-button.h"
+
+G_DEFINE_TYPE (NmtNewtToggleButton, nmt_newt_toggle_button, NMT_TYPE_NEWT_BUTTON)
+
+#define NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonPrivate))
+
+typedef struct {
+	char *on_label, *off_label;
+	gboolean active;
+} NmtNewtToggleButtonPrivate;
+
+enum {
+	PROP_0,
+	PROP_ON_LABEL,
+	PROP_OFF_LABEL,
+	PROP_ACTIVE,
+
+	LAST_PROP
+};
+
+/**
+ * nmt_newt_toggle_button_new:
+ * @on_label: the button's label when it is in the "on" state
+ * @off_label: the button's label when it is in the "off" state
+ *
+ * Creates a new #NmtNewtToggleButton
+ *
+ * Returns: a new #NmtNewtToggleButton
+ */
+NmtNewtWidget *
+nmt_newt_toggle_button_new (const char *on_label,
+                            const char *off_label)
+{
+	return g_object_new (NMT_TYPE_NEWT_TOGGLE_BUTTON,
+	                     "on-label", on_label,
+	                     "off-label", off_label,
+	                     NULL);
+}
+
+/**
+ * nmt_newt_toggle_button_get_active:
+ * @button: an #NmtNewtToggleButton
+ *
+ * Gets whether @button is currently "on" or "off"
+ *
+ * Returns: whether @button is currently "on" (%TRUE) or "off" (%FALSE)
+ */
+gboolean
+nmt_newt_toggle_button_get_active (NmtNewtToggleButton *button)
+{
+	NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (button);
+
+	return priv->active;
+}
+
+/**
+ * nmt_newt_toggle_button_set_active:
+ * @button: an #NmtNewtToggleButton
+ * @active: whether @button should be "on" or "off"
+ *
+ * Sets whether @button is currently "on" or "off"
+ */
+void
+nmt_newt_toggle_button_set_active (NmtNewtToggleButton *button,
+                                   gboolean             active)
+{
+	NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (button);
+
+	if (priv->active == active)
+		return;
+
+	priv->active = active;
+	g_object_set (G_OBJECT (button),
+	              "label", active ? priv->on_label : priv->off_label,
+	              NULL);
+	g_object_notify (G_OBJECT (button), "active");
+}
+
+static void
+nmt_newt_toggle_button_init (NmtNewtToggleButton *button)
+{
+}
+
+static void
+nmt_newt_toggle_button_finalize (GObject *object)
+{
+	NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object);
+
+	g_free (priv->on_label);
+	g_free (priv->off_label);
+
+	G_OBJECT_CLASS (nmt_newt_toggle_button_parent_class)->finalize (object);
+}
+
+static void
+nmt_newt_toggle_button_activated (NmtNewtWidget *widget)
+{
+	NmtNewtToggleButton *button = NMT_NEWT_TOGGLE_BUTTON (widget);
+
+	nmt_newt_toggle_button_set_active (button, !nmt_newt_toggle_button_get_active (button));
+
+	NMT_NEWT_WIDGET_CLASS (nmt_newt_toggle_button_parent_class)->activated (widget);
+}
+
+static void
+nmt_newt_toggle_button_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+	NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_ON_LABEL:
+		g_free (priv->on_label);
+		priv->on_label = g_value_dup_string (value);
+		if (priv->active)
+			g_object_set (object, "label", priv->on_label, NULL);
+		break;
+	case PROP_OFF_LABEL:
+		g_free (priv->off_label);
+		priv->off_label = g_value_dup_string (value);
+		if (!priv->active)
+			g_object_set (object, "label", priv->off_label, NULL);
+		break;
+	case PROP_ACTIVE:
+		priv->active = g_value_get_boolean (value);
+		g_object_set (object,
+		              "label", priv->active ? priv->on_label : priv->off_label,
+		              NULL);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_toggle_button_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+	NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_ON_LABEL:
+		g_value_set_string (value, priv->on_label);
+		break;
+	case PROP_OFF_LABEL:
+		g_value_set_string (value, priv->off_label);
+		break;
+	case PROP_ACTIVE:
+		g_value_set_boolean (value, priv->active);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_toggle_button_class_init (NmtNewtToggleButtonClass *button_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (button_class);
+	NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (button_class);
+
+	g_type_class_add_private (button_class, sizeof (NmtNewtToggleButtonPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_toggle_button_set_property;
+	object_class->get_property = nmt_newt_toggle_button_get_property;
+	object_class->finalize     = nmt_newt_toggle_button_finalize;
+
+	widget_class->activated = nmt_newt_toggle_button_activated;
+
+	/**
+	 * NmtNewtToggleButton:on-label:
+	 *
+	 * The label the button displays when it is "on".
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ON_LABEL,
+		 g_param_spec_string ("on-label", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtToggleButton:off-label:
+	 *
+	 * The label the button displays when it is "off".
+	 */
+	g_object_class_install_property
+		(object_class, PROP_OFF_LABEL,
+		 g_param_spec_string ("off-label", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtToggleButton:active:
+	 *
+	 * Whether the button is currently "on" (%TRUE) or "off" (%FALSE)
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ACTIVE,
+		 g_param_spec_boolean ("active", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-toggle-button.h b/clients/tui/newt/nmt-newt-toggle-button.h
new file mode 100644
index 00000000..e54c6d3f
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-toggle-button.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_TOGGLE_BUTTON_H
+#define NMT_NEWT_TOGGLE_BUTTON_H
+
+#include "nmt-newt-button.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_TOGGLE_BUTTON            (nmt_newt_toggle_button_get_type ())
+#define NMT_NEWT_TOGGLE_BUTTON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButton))
+#define NMT_NEWT_TOGGLE_BUTTON_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonClass))
+#define NMT_IS_NEWT_TOGGLE_BUTTON(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON))
+#define NMT_IS_NEWT_TOGGLE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_TOGGLE_BUTTON))
+#define NMT_NEWT_TOGGLE_BUTTON_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonClass))
+
+struct _NmtNewtToggleButton {
+	NmtNewtButton parent;
+
+};
+
+typedef struct {
+	NmtNewtButtonClass parent;
+
+} NmtNewtToggleButtonClass;
+
+GType nmt_newt_toggle_button_get_type (void);
+
+NmtNewtWidget *nmt_newt_toggle_button_new        (const char          *on_label,
+                                                  const char          *off_label);
+
+gboolean       nmt_newt_toggle_button_get_active (NmtNewtToggleButton *button);
+void           nmt_newt_toggle_button_set_active (NmtNewtToggleButton *button,
+                                                  gboolean             active);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_TOGGLE_BUTTON_H */
diff --git a/clients/tui/newt/nmt-newt-types.h b/clients/tui/newt/nmt-newt-types.h
new file mode 100644
index 00000000..608f7ff6
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-types.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_TYPES_H
+#define NMT_NEWT_TYPES_H
+
+#include <glib-object.h>
+#include <newt.h>
+#include "nm-glib-compat.h"
+
+G_BEGIN_DECLS
+
+typedef struct _NmtNewtButton        NmtNewtButton;
+typedef struct _NmtNewtButtonBox     NmtNewtButtonBox;
+typedef struct _NmtNewtCheckbox      NmtNewtCheckbox;
+typedef struct _NmtNewtComponent     NmtNewtComponent;
+typedef struct _NmtNewtContainer     NmtNewtContainer;
+typedef struct _NmtNewtEntry         NmtNewtEntry;
+typedef struct _NmtNewtEntryNumeric  NmtNewtEntryNumeric;
+typedef struct _NmtNewtForm          NmtNewtForm;
+typedef struct _NmtNewtGrid          NmtNewtGrid;
+typedef struct _NmtNewtLabel         NmtNewtLabel;
+typedef struct _NmtNewtListbox       NmtNewtListbox;
+typedef struct _NmtNewtPopup         NmtNewtPopup;
+typedef struct _NmtNewtSection       NmtNewtSection;
+typedef struct _NmtNewtSectionBorder NmtNewtSectionBorder;
+typedef struct _NmtNewtSeparator     NmtNewtSeparator;
+typedef struct _NmtNewtStack         NmtNewtStack;
+typedef struct _NmtNewtTextbox       NmtNewtTextbox;
+typedef struct _NmtNewtToggleButton  NmtNewtToggleButton;
+typedef struct _NmtNewtWidget        NmtNewtWidget;
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_COMPONENT_H */
diff --git a/clients/tui/newt/nmt-newt-utils.c b/clients/tui/newt/nmt-newt-utils.c
new file mode 100644
index 00000000..70a65a84
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-utils.c
@@ -0,0 +1,393 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-utils
+ * @short_description: Utility functions
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nm-glib-compat.h"
+#include "nmt-newt-utils.h"
+
+static void
+nmt_newt_dialog_g_log_handler (const char     *log_domain,
+                               GLogLevelFlags  log_level,
+                               const char     *message,
+                               gpointer        user_data)
+{
+	const char *level_name;
+	char *full_message;
+	int screen_width, screen_height;
+	newtComponent text, ok, form;
+	newtGrid grid;
+
+	g_assert (!(log_level & G_LOG_FLAG_RECURSION));
+
+	if (log_level & G_LOG_LEVEL_DEBUG)
+		return;
+
+	switch (log_level & G_LOG_LEVEL_MASK) {
+	case G_LOG_LEVEL_ERROR:
+		level_name = "ERROR";
+		break;
+	case G_LOG_LEVEL_CRITICAL:
+		level_name = "CRITICAL";
+		break;
+	case G_LOG_LEVEL_WARNING:
+		level_name = "WARNING";
+		break;
+	case G_LOG_LEVEL_MESSAGE:
+		level_name = "Message";
+		break;
+	default:
+		level_name = NULL;
+	}
+
+	full_message = g_strdup_printf ("%s%s%s%s%s",
+	                                log_domain ? log_domain : "",
+	                                log_domain && level_name ? " " : "",
+	                                level_name ? level_name : "",
+	                                log_domain || level_name ? ": " : "",
+	                                message);
+
+	/* newtWinMessage() wraps the window too narrowly by default, so
+	 * we don't want to use that. But we intentionally avoid using any
+	 * NmtNewt classes, to avoid possible error recursion.
+	 */
+
+	newtGetScreenSize (&screen_width, &screen_height);
+	text = newtTextboxReflowed (-1, -1, full_message, MAX (70, screen_width - 10), 0, 0, 0);
+	g_free (full_message);
+
+	ok = newtButton (-1, -1, "OK");
+
+	grid = newtCreateGrid (1, 2);
+	newtGridSetField (grid, 0, 0, NEWT_GRID_COMPONENT, text, 0, 0, 0, 0, 0, 0);
+	newtGridSetField (grid, 0, 1, NEWT_GRID_COMPONENT, ok, 0, 1, 0, 0,
+	                  NEWT_ANCHOR_RIGHT, 0);
+
+	newtGridWrappedWindow (grid, (char *) (level_name ? level_name : ""));
+	newtGridFree (grid, TRUE);
+
+	form = newtForm (NULL, NULL, 0);
+	newtFormAddComponents (form, text, ok, NULL);
+	newtRunForm (form);
+	newtFormDestroy (form);
+	newtPopWindow ();
+}
+
+static void
+nmt_newt_basic_g_log_handler (const char     *log_domain,
+                              GLogLevelFlags  log_level,
+                              const char     *message,
+                              gpointer        user_data)
+{
+	newtSuspend ();
+	g_log_default_handler (log_domain, log_level, message, NULL);
+	newtResume ();
+}
+
+static void
+nmt_newt_suspend_callback (gpointer user_data)
+{
+	newtSuspend ();
+	kill (getpid (), SIGTSTP);
+	newtResume ();
+}
+
+/**
+ * nmt_newt_init:
+ *
+ * Wrapper for newtInit() that also does some nmt-newt-internal setup.
+ * This should be called once, before any other nmt-newt functions.
+ */
+void
+nmt_newt_init (void)
+{
+	newtInit ();
+	newtCls ();
+
+	newtSetColor (NEWT_COLORSET_CHECKBOX, "black", "lightgray");
+	newtSetColor (NMT_NEWT_COLORSET_BAD_LABEL, "red", "lightgray");
+	newtSetColor (NMT_NEWT_COLORSET_PLAIN_LABEL, "black", "lightgray");
+	newtSetColor (NMT_NEWT_COLORSET_DISABLED_BUTTON, "blue", "lightgray");
+	newtSetColor (NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, "black", "white");
+
+	if (g_getenv ("NMTUI_DEBUG"))
+		g_log_set_default_handler (nmt_newt_dialog_g_log_handler, NULL);
+	else
+		g_log_set_default_handler (nmt_newt_basic_g_log_handler, NULL);
+
+	newtSetSuspendCallback (nmt_newt_suspend_callback, NULL);
+}
+
+/**
+ * nmt_newt_finished:
+ *
+ * Wrapper for newtFinished(). Should be called at the end of the program.
+ */
+void
+nmt_newt_finished (void)
+{
+	newtFinished ();
+	g_log_set_default_handler (g_log_default_handler, NULL);
+}
+
+/**
+ * nmt_newt_message_dialog:
+ * @message: a printf()-style message format
+ * @...: arguments
+ *
+ * Displays the given message in a dialog box with a single "OK"
+ * button, and returns after the user clicks "OK".
+ */
+void
+nmt_newt_message_dialog  (const char *message,
+                          ...)
+{
+	va_list ap;
+	char *msg, *msg_lc, *ok_lc;
+
+	va_start (ap, message);
+	msg = g_strdup_vprintf (message, ap);
+	va_end (ap);
+
+	msg_lc = nmt_newt_locale_from_utf8 (msg);
+	ok_lc = nmt_newt_locale_from_utf8 (_("OK"));
+	newtWinMessage (NULL, ok_lc, "%s", msg_lc);
+
+	g_free (ok_lc);
+	g_free (msg_lc);
+	g_free (msg);
+}
+
+/**
+ * nmt_newt_choice_dialog:
+ * @button1: the label for the first button
+ * @button2: the label for the second button
+ * @message: a printf()-style message format
+ * @...: arguments
+ *
+ * Displays the given message in a dialog box with two buttons with
+ * the indicated labels, and waits for the user to click one.
+ *
+ * Returns: which button was clicked: 0 for @button1 or 1 for @button2
+ */
+int
+nmt_newt_choice_dialog (const char *button1,
+                        const char *button2,
+                        const char *message,
+                        ...)
+{
+	va_list ap;
+	char *msg, *msg_lc, *button1_lc, *button2_lc;
+	int choice;
+
+	va_start (ap, message);
+	msg = g_strdup_vprintf (message, ap);
+	va_end (ap);
+
+	msg_lc = nmt_newt_locale_from_utf8 (msg);
+	button1_lc = nmt_newt_locale_from_utf8 (button1);
+	button2_lc = nmt_newt_locale_from_utf8 (button2);
+	choice = newtWinChoice (NULL, button1_lc, button2_lc, "%s", msg_lc);
+
+	g_free (button1_lc);
+	g_free (button2_lc);
+	g_free (msg_lc);
+	g_free (msg);
+
+	return choice;
+}
+
+/**
+ * nmt_newt_locale_to_utf8:
+ * @str_lc: a string in the user's locale encoding
+ *
+ * Convenience wrapper around g_locale_to_utf8().
+ *
+ * Note that libnewt works in terms of the user's locale character
+ * set, NOT UTF-8, so all strings received from libnewt must be
+ * converted back to UTF-8 before being returned to the caller or used
+ * in other APIs.
+ *
+ * Returns: @str_lc, converted to UTF-8.
+ */
+char *
+nmt_newt_locale_to_utf8 (const char *str_lc)
+{
+	char *str_utf8;
+
+	str_utf8 = g_locale_to_utf8 (str_lc, -1, NULL, NULL, NULL);
+	if (!str_utf8)
+		str_utf8 = g_strdup ("");
+	return str_utf8;
+}
+
+/**
+ * nmt_newt_locale_from_utf8:
+ * @str_utf8: a UTF-8 string
+ *
+ * Convenience wrapper around g_locale_from_utf8().
+ *
+ * Note that libnewt works in terms of the user's locale character
+ * set, NOT UTF-8, so all strings from nmt-newt must be converted to
+ * locale encoding before being passed to libnewt.
+ *
+ * Returns: @str_utf8, converted to the user's locale encoding.
+ */
+char *
+nmt_newt_locale_from_utf8 (const char *str_utf8)
+{
+	char *str_lc;
+
+	str_lc = g_locale_from_utf8 (str_utf8, -1, NULL, NULL, NULL);
+	if (!str_lc)
+		str_lc = g_strdup ("");
+	return str_lc;
+}
+
+/**
+ * nmt_newt_text_width
+ * @str: a UTF-8 string
+ *
+ * Computes the width (in terminal columns) of @str.
+ *
+ * Returns: the width of @str
+ */
+int
+nmt_newt_text_width (const char *str)
+{
+	int width;
+	gunichar ch;
+
+	for (width = 0; *str; str = g_utf8_next_char (str)) {
+		ch = g_utf8_get_char (str);
+
+		/* Based on _vte_iso2022_unichar_width */
+		if (G_LIKELY (ch < 0x80))
+			width += 1;
+		else if (G_UNLIKELY (g_unichar_iszerowidth (ch)))
+			width += 0;
+		else if (G_UNLIKELY (g_unichar_iswide (ch)))
+			width += 2;
+		else
+			width += 1;
+	}
+
+	return width;
+}
+
+/**
+ * nmt_newt_edit_string:
+ * @data: data to edit
+ *
+ * libnewt does not have a multi-line editable text component, so
+ * nmt-newt provides this function instead, which will open the user's
+ * editor to edit a file containing the given @data (ensuring that the
+ * current screen state is saved before starting the editor and
+ * restored after it returns).
+ *
+ * Returns: the edited data, or %NULL if an error occurred.
+ */
+char *
+nmt_newt_edit_string (const char *data)
+{
+	gssize len, nwrote;
+	char *filename, *argv[3];
+	GError *error = NULL;
+	int fd, status;
+	char *new_data = NULL;
+
+	fd = g_file_open_tmp ("XXXXXX.json", &filename, &error);
+	if (fd == -1) {
+		nmt_newt_message_dialog (_("Could not create temporary file: %s"), error->message);
+		g_error_free (error);
+		return NULL;
+	}
+
+	len = data ? strlen (data) : 0;
+	while (len) {
+		do
+			nwrote = write (fd, data, len);
+		while (nwrote == -1 && errno == EINTR);
+
+		len -= nwrote;
+		data += nwrote;
+	}
+	close (fd);
+
+	argv[0] = (char *) g_getenv ("VISUAL");
+	if (!argv[0])
+		argv[0] = (char *) g_getenv ("EDITOR");
+	if (!argv[0])
+		argv[0] = (char *) "vi";
+	argv[1] = filename;
+	argv[2] = NULL;
+
+	newtSuspend ();
+	g_spawn_sync (NULL, argv, NULL,
+	              G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN,
+	              NULL, NULL, NULL, NULL,
+	              &status, &error);
+	newtResume ();
+
+	if (error) {
+		nmt_newt_message_dialog (_("Could not create temporary file: %s"), error->message);
+		g_error_free (error);
+		goto done;
+	}
+
+#if GLIB_CHECK_VERSION (2, 34, 0)
+	G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+	if (!g_spawn_check_exit_status (status, &error)) {
+		nmt_newt_message_dialog (_("Editor failed: %s"), error->message);
+		g_error_free (error);
+		goto done;
+	}
+	G_GNUC_END_IGNORE_DEPRECATIONS
+#else
+	if (WIFEXITED (status)) {
+		if (WEXITSTATUS (status) != 0)
+			nmt_newt_message_dialog (_("Editor failed with status %d"), WEXITSTATUS (status));
+	} else if (WIFSIGNALED (status))
+		nmt_newt_message_dialog (_("Editor failed with signal %d"), WTERMSIG (status));
+#endif
+
+	if (!g_file_get_contents (filename, &new_data, NULL, &error)) {
+		nmt_newt_message_dialog (_("Could not re-read file: %s"), error->message);
+		g_error_free (error);
+		goto done;
+	}
+
+ done:
+	unlink (filename);
+	g_free (filename);
+
+	return new_data;
+}	
+
diff --git a/clients/tui/newt/nmt-newt-utils.h b/clients/tui/newt/nmt-newt-utils.h
new file mode 100644
index 00000000..3b37868a
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-utils.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_UTILS_H
+#define NMT_NEWT_UTILS_H
+
+#include <newt.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+void nmt_newt_init     (void);
+void nmt_newt_finished (void);
+
+typedef enum {
+	NMT_NEWT_COLORSET_BAD_LABEL = NEWT_COLORSET_CUSTOM (0),
+	NMT_NEWT_COLORSET_PLAIN_LABEL,
+	NMT_NEWT_COLORSET_DISABLED_BUTTON,
+	NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND
+} NmtNewtColorsets;
+
+char *nmt_newt_locale_to_utf8   (const char *str_lc);
+char *nmt_newt_locale_from_utf8 (const char *str_utf8);
+
+int   nmt_newt_text_width       (const char *str);
+
+void nmt_newt_message_dialog  (const char *message,
+                               ...);
+int  nmt_newt_choice_dialog (const char *button1,
+                             const char *button2,
+                             const char *message,
+                             ...);
+
+char *nmt_newt_edit_string (const char *data);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_UTILS_H */
diff --git a/clients/tui/newt/nmt-newt-widget.c b/clients/tui/newt/nmt-newt-widget.c
new file mode 100644
index 00000000..24c94db9
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-widget.c
@@ -0,0 +1,668 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-newt-widget
+ * @short_description: Base TUI Widget class
+ *
+ * #NmtNewtWidget is the abstract base class for nmt-newt. All widgets
+ * inherit from one of its two subclasses: #NmtNewtComponent, for
+ * widgets that wrap a (single) #newtComponent, and #NmtNewtContainer,
+ * for widgets consisting of multiple components. See those classes
+ * for more details.
+ *
+ * With the exception of #NmtNewtForm, all widgets start out with a
+ * floating reference, which will be sunk by the container they are
+ * added to. #NmtNewtForm is the "top-level" widget type, and so does
+ * not have a floating reference.
+ *
+ * FIXME: need RTL support
+ */
+
+#include "config.h"
+
+#include "nmt-newt-widget.h"
+#include "nmt-newt-form.h"
+
+G_DEFINE_ABSTRACT_TYPE (NmtNewtWidget, nmt_newt_widget, G_TYPE_INITIALLY_UNOWNED)
+
+#define NMT_NEWT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetPrivate))
+
+typedef struct {
+	NmtNewtWidget *parent;
+	gboolean visible, realized, valid;
+	gboolean exit_on_activate;
+
+	int pad_left, pad_top, pad_right, pad_bottom;
+} NmtNewtWidgetPrivate;
+
+enum {
+	PROP_0,
+
+	PROP_PARENT,
+	PROP_VISIBLE,
+	PROP_VALID,
+	PROP_EXIT_ON_ACTIVATE,
+
+	LAST_PROP
+};
+
+enum {
+	NEEDS_REBUILD,
+	ACTIVATED,
+
+	LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+nmt_newt_widget_init (NmtNewtWidget *widget)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	priv->visible = TRUE;
+	priv->valid = TRUE;
+}
+
+static void
+nmt_newt_widget_finalize (GObject *object)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (object);
+
+	nmt_newt_widget_unrealize (NMT_NEWT_WIDGET (object));
+	g_clear_object (&priv->parent);
+
+	G_OBJECT_CLASS (nmt_newt_widget_parent_class)->finalize (object);
+}
+
+/**
+ * nmt_newt_widget_realize:
+ * @widget: an #NmtNewtWidget
+ *
+ * "Realizes" @widget. That is, creates #newtComponents corresponding
+ * to @widget and its children.
+ *
+ * You should not need to call this yourself; an #NmtNewtForm will
+ * cause its children to be realized and unrealized as needed.
+ */
+void
+nmt_newt_widget_realize (NmtNewtWidget *widget)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	if (!priv->realized) {
+		NMT_NEWT_WIDGET_GET_CLASS (widget)->realize (widget);
+		priv->realized = TRUE;
+	}
+}
+
+/**
+ * nmt_newt_widget_unrealize:
+ * @widget: an #NmtNewtWidget
+ *
+ * "Unrealizes" @widget, destroying its #newtComponents.
+ *
+ * You should not need to call this yourself; an #NmtNewtForm will
+ * cause its children to be realized and unrealized as needed.
+ */
+void
+nmt_newt_widget_unrealize (NmtNewtWidget *widget)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	if (priv->realized) {
+		NMT_NEWT_WIDGET_GET_CLASS (widget)->unrealize (widget);
+		priv->realized = FALSE;
+	}
+}
+
+/**
+ * nmt_newt_widget_get_realized:
+ * @widget: an #NmtNewtWidget
+ *
+ * Checks if @widget is realized or not.
+ *
+ * Returns: whether @widget is realized.
+ */
+gboolean
+nmt_newt_widget_get_realized (NmtNewtWidget *widget)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	return priv->realized;
+}
+
+/**
+ * nmt_newt_widget_get_components:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets the #newtComponents that make up @widget, if @widget is
+ * visible. If @widget has not yet been realized, it will be realized
+ * first.
+ *
+ * If this function is called on a widget, then the widget will assume
+ * that someone else is now responsible for destroying the components,
+ * and so it will not destroy them itself when the widget is
+ * destroyed. Normally, components will end up being destroyed by the
+ * #NmtNewtForm they are added to.
+ *
+ * Returns: a %NULL-terminated array of components, in focus-chain
+ *   order. You must free the array with g_free() when you are done
+ *   with it.
+ */
+newtComponent *
+nmt_newt_widget_get_components (NmtNewtWidget *widget)
+{
+	if (nmt_newt_widget_get_visible (widget)) {
+		nmt_newt_widget_realize (widget);
+		return NMT_NEWT_WIDGET_GET_CLASS (widget)->get_components (widget);
+	} else
+		return NULL;
+}
+
+/**
+ * nmt_newt_widget_find_component:
+ * @widget: an #NmtNewtWidget
+ * @co: a #newtComponent
+ *
+ * Finds the widget inside @widget that owns @co.
+ *
+ * Return value: @co's owner, or %NULL if it was not found.
+ */
+NmtNewtWidget *
+nmt_newt_widget_find_component (NmtNewtWidget *widget,
+                                newtComponent  co)
+{
+	return NMT_NEWT_WIDGET_GET_CLASS (widget)->find_component (widget, co);
+}
+
+/**
+ * nmt_newt_widget_set_padding:
+ * @widget: an #NmtNewtWidget
+ * @pad_left: padding on the left of @widget
+ * @pad_top: padding on the top of @widget
+ * @pad_right: padding on the right of @widget
+ * @pad_bottom: padding on the bottom of @widget
+ *
+ * Sets the padding on @widget.
+ */
+void
+nmt_newt_widget_set_padding (NmtNewtWidget *widget,
+                             int            pad_left,
+                             int            pad_top,
+                             int            pad_right,
+                             int            pad_bottom)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	priv->pad_left = pad_left;
+	priv->pad_top = pad_top;
+	priv->pad_right = pad_right;
+	priv->pad_bottom = pad_bottom;
+}
+
+/**
+ * nmt_newt_widget_size_request:
+ * @widget: an #NmtNewtWidget
+ * @width: (out): on output, the widget's requested width
+ * @height: (out): on output, the widget's requested height
+ *
+ * Asks @widget for its requested size. If @widget is not visible,
+ * this will return 0, 0. If @widget has not yet been realized, it
+ * will be realized first.
+ */
+void
+nmt_newt_widget_size_request (NmtNewtWidget *widget,
+                              int           *width,
+                              int           *height)
+{
+	if (nmt_newt_widget_get_visible (widget)) {
+		NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+		nmt_newt_widget_realize (widget);
+		NMT_NEWT_WIDGET_GET_CLASS (widget)->size_request (widget, width, height);
+
+		*width += priv->pad_left + priv->pad_right;
+		*height += priv->pad_top + priv->pad_bottom;
+	} else
+		*width = *height = 0;
+}
+
+/**
+ * nmt_newt_widget_size_allocate:
+ * @widget: an #NmtNewtWidget
+ * @x: the widget's (absolute) X coordinate
+ * @y: the widget's (absolute) Y coordinate
+ * @width: the widget's allocated width
+ * @height: the widget's allocated height
+ *
+ * Positions @widget at the given coordinates, with the given size. If
+ * @widget is not visible, this has no effect. If @widget has not yet
+ * been realized, it will be realized first.
+ *
+ * @x and @y are absolute coordinates (ie, relative to the screen /
+ * terminal window, not relative to @widget's parent).
+ *
+ * In general, the results are undefined if @width or @height is less
+ * than the widget's requested size. If @width or @height is larger
+ * than the requested size, most #NmtNewtComponents will ignore the
+ * extra space, but some components and most containers will expand to
+ * fit.
+ */
+void
+nmt_newt_widget_size_allocate (NmtNewtWidget *widget,
+                               int            x,
+                               int            y,
+                               int            width,
+                               int            height)
+{
+	if (nmt_newt_widget_get_visible (widget)) {
+		NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+		nmt_newt_widget_realize (widget);
+		x += priv->pad_left;
+		y += priv->pad_top;
+		width -= priv->pad_left + priv->pad_right;
+		height -= priv->pad_top + priv->pad_bottom;
+
+		NMT_NEWT_WIDGET_GET_CLASS (widget)->size_allocate (widget, x, y, width, height);
+	}
+}
+
+/**
+ * nmt_newt_widget_get_focus_component:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets the #newtComponent that should be given the keyboard focus when
+ * @widget is focused.
+ *
+ * Returns: the #newtComponent to focus, or %NULL if @widget can't
+ *   take the focus.
+ */
+newtComponent
+nmt_newt_widget_get_focus_component (NmtNewtWidget *widget)
+{
+	if (!NMT_NEWT_WIDGET_GET_CLASS (widget)->get_focus_component)
+		return NULL;
+
+	return NMT_NEWT_WIDGET_GET_CLASS (widget)->get_focus_component (widget);
+}
+
+static void
+nmt_newt_widget_real_activated (NmtNewtWidget *widget)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	if (priv->exit_on_activate)
+		nmt_newt_form_quit (nmt_newt_widget_get_form (widget));
+}	
+
+/**
+ * nmt_newt_widget_activated:
+ * @widget: an #NmtNewtWidget
+ *
+ * Tells @widget that its #newtComponent has been activated (ie, the
+ * user hit "Return" on it) and emits #NmtNewtWidget::activated.
+ *
+ * If #NmtNewtWidget:exit-on-activate is set on @widget, then this
+ * will call nmt_newt_form_quit() on the widget's form.
+ */
+void
+nmt_newt_widget_activated (NmtNewtWidget *widget)
+{
+	g_signal_emit (widget, signals[ACTIVATED], 0);
+}
+
+/**
+ * nmt_newt_widget_get_exit_on_activate:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets @widget's #NmtNewtWidget:exit-on-activate flag, qv.
+ *
+ * Returns: @widget's #NmtNewtWidget:exit-on-activate flag
+ */
+gboolean
+nmt_newt_widget_get_exit_on_activate (NmtNewtWidget *widget)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	return priv->exit_on_activate;
+}
+
+/**
+ * nmt_newt_widget_set_exit_on_activate:
+ * @widget: an #NmtNewtWidget
+ * @exit_on_activate: whether @widget should exit on activate.
+ *
+ * Sets @widget's #NmtNewtWidget:exit-on-activate flag, qv.
+ */
+void
+nmt_newt_widget_set_exit_on_activate (NmtNewtWidget *widget,
+                                      gboolean       exit_on_activate)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	exit_on_activate = !!exit_on_activate;
+	if (priv->exit_on_activate != exit_on_activate) {
+		priv->exit_on_activate = exit_on_activate;
+		g_object_notify (G_OBJECT (widget), "exit-on-activate");
+	}
+} 
+
+/**
+ * nmt_newt_widget_get_visible:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets @widget's #NmtNewtWidget:visible flag, qv.
+ *
+ * Returns: @widget's #NmtNewtWidget:visible flag
+ */
+gboolean
+nmt_newt_widget_get_visible (NmtNewtWidget *widget)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	return priv->visible;
+}
+
+/**
+ * nmt_newt_widget_set_visible:
+ * @widget: an #NmtNewtWidget
+ * @visible: whether @widget should be visible
+ *
+ * Sets @widget's #NmtNewtWidget:visible flag, qv.
+ */
+void
+nmt_newt_widget_set_visible (NmtNewtWidget *widget,
+                             gboolean       visible)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	visible = !!visible;
+	if (priv->visible != visible) {
+		priv->visible = visible;
+		g_object_notify (G_OBJECT (widget), "visible");
+		nmt_newt_widget_needs_rebuild (widget);
+	}
+}
+
+/**
+ * nmt_newt_widget_set_parent:
+ * @widget: an #NmtNewtWidget
+ * @parent: @widget's parent
+ *
+ * Sets @widget's parent to @parent. This is used internally by
+ * #NmtNewtContainer implementations; you must use an appropriate
+ * container-specific method to actually add a widget to a container.
+ */
+void
+nmt_newt_widget_set_parent (NmtNewtWidget *widget,
+                            NmtNewtWidget *parent)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	g_clear_object (&priv->parent);
+	priv->parent = parent ? g_object_ref (parent) : NULL;
+	g_object_notify (G_OBJECT (widget), "parent");
+}
+
+/**
+ * nmt_newt_widget_get_parent:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets @widget's parent
+ *
+ * Returns: @widget's parent
+ */
+NmtNewtWidget *
+nmt_newt_widget_get_parent (NmtNewtWidget *widget)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	return priv->parent;
+}
+
+/**
+ * nmt_newt_widget_get_form:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets @widget's top-level form.
+ *
+ * Returns: @widget's #NmtNewtForm
+ */
+NmtNewtForm *
+nmt_newt_widget_get_form (NmtNewtWidget *widget)
+{
+	while (widget) {
+		if (NMT_IS_NEWT_FORM (widget))
+			return NMT_NEWT_FORM (widget);
+		widget = nmt_newt_widget_get_parent (widget);
+	}
+
+	return NULL;
+}
+
+/**
+ * nmt_newt_widget_get_valid:
+ * @widget: an #NmtNewtWidget
+ *
+ * Gets @widget's #NmtNewtWidget:valid flag, indicating whether its
+ * content is valid.
+ *
+ * Returns: @widget's #NmtNewtWidget:valid flag
+ */
+gboolean
+nmt_newt_widget_get_valid (NmtNewtWidget *widget)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	return priv->valid;
+}
+
+/**
+ * nmt_newt_widget_set_valid:
+ * @widget: an #NmtNewtWidget
+ * @valid: whether @widget is valid
+ *
+ * Sets @widget's #NmtNewtWidget:valid flag, indicating whether its
+ * content is valid.
+ *
+ * This method should be considered "protected"; if you change it, the
+ * widget implementation will likely just change it back at some
+ * point.
+ */
+void
+nmt_newt_widget_set_valid (NmtNewtWidget *widget,
+                           gboolean       valid)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget);
+
+	valid = !!valid;
+	if (priv->valid == valid)
+		return;
+
+	priv->valid = valid;
+	g_object_notify (G_OBJECT (widget), "valid");
+}
+
+/**
+ * nmt_newt_widget_needs_rebuilds:
+ * @widget: an #NmtNewtWidget
+ *
+ * Marks @widget as needing to be "rebuilt" (ie, re-realized). This is
+ * called automatically in some cases (such as when adding a widget to
+ * or removing it from a container). #NmtNewtComponent implementations
+ * should also call this if they need to make some change that can
+ * only be done by destroying their current #newtComponent and
+ * creating a new one.
+ */
+void
+nmt_newt_widget_needs_rebuild (NmtNewtWidget *widget)
+{
+	g_signal_emit (widget, signals[NEEDS_REBUILD], 0);
+}
+
+static void
+nmt_newt_widget_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+	NmtNewtWidget *widget = NMT_NEWT_WIDGET (object);
+
+	switch (prop_id) {
+	case PROP_PARENT:
+		nmt_newt_widget_set_parent (widget, g_value_get_object (value));
+		break;
+	case PROP_VISIBLE:
+		nmt_newt_widget_set_visible (widget, g_value_get_boolean (value));
+		break;
+	case PROP_EXIT_ON_ACTIVATE:
+		nmt_newt_widget_set_exit_on_activate (widget, g_value_get_boolean (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_widget_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+	NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_PARENT:
+		g_value_set_object (value, priv->parent);
+		break;
+	case PROP_VISIBLE:
+		g_value_set_boolean (value, priv->visible);
+		break;
+	case PROP_VALID:
+		g_value_set_boolean (value, priv->valid);
+		break;
+	case PROP_EXIT_ON_ACTIVATE:
+		g_value_set_boolean (value, priv->exit_on_activate);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_newt_widget_class_init (NmtNewtWidgetClass *widget_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (widget_class);
+
+	g_type_class_add_private (widget_class, sizeof (NmtNewtWidgetPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_newt_widget_set_property;
+	object_class->get_property = nmt_newt_widget_get_property;
+	object_class->finalize     = nmt_newt_widget_finalize;
+
+	widget_class->activated = nmt_newt_widget_real_activated;
+
+	/* signals */
+
+	/**
+	 * NmtNewtWidget::needs-rebuild:
+	 * @widget: the #NmtNewtWidget
+	 *
+	 * Emitted when nmt_newt_widget_need_rebuild() is called on @widget
+	 * or any of its children. This signal propagates up the container
+	 * hierarchy, eventually reaching the top-level #NmtNewtForm.
+	 */
+	signals[NEEDS_REBUILD] =
+		g_signal_new ("needs-rebuild",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NmtNewtWidgetClass, needs_rebuild),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 0);
+
+	/**
+	 * NmtNewtWidget::activated:
+	 * @widget: the #NmtNewtWidget
+	 *
+	 * Emitted when the widget's #newtComponent is activated.
+	 */
+	signals[ACTIVATED] =
+		g_signal_new ("activated",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NmtNewtWidgetClass, activated),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 0);
+
+	/* properties */
+
+	/**
+	 * NmtNewtWidget:parent:
+	 *
+	 * The widget's parent widget, or %NULL if it has no parent.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_PARENT,
+		 g_param_spec_object ("parent", "", "",
+		                      NMT_TYPE_NEWT_WIDGET,
+		                      G_PARAM_READABLE |
+		                      G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtWidget:visible:
+	 *
+	 * Whether the widget is visible. Invisible widgets do not get
+	 * realized or sized.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_VISIBLE,
+		 g_param_spec_boolean ("visible", "", "",
+		                       TRUE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtWidget:valid:
+	 *
+	 * Whether the widget's content is considered valid. Components
+	 * determine their own validity. A container, by default, is
+	 * considered valid if all of its children are valid.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_VALID,
+		 g_param_spec_boolean ("valid", "", "",
+		                       TRUE,
+		                       G_PARAM_READABLE |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtNewtWidget:exit-on-activate:
+	 *
+	 * If %TRUE, the widget will call nmt_newt_form_quit() on its form
+	 * when it is activated.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_EXIT_ON_ACTIVATE,
+		 g_param_spec_boolean ("exit-on-activate", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/newt/nmt-newt-widget.h b/clients/tui/newt/nmt-newt-widget.h
new file mode 100644
index 00000000..a526a674
--- /dev/null
+++ b/clients/tui/newt/nmt-newt-widget.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_WIDGET_H
+#define NMT_NEWT_WIDGET_H
+
+#include "nmt-newt-types.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_NEWT_WIDGET            (nmt_newt_widget_get_type ())
+#define NMT_NEWT_WIDGET(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidget))
+#define NMT_NEWT_WIDGET_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass))
+#define NMT_IS_NEWT_WIDGET(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_WIDGET))
+#define NMT_IS_NEWT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_WIDGET))
+#define NMT_NEWT_WIDGET_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass))
+
+struct _NmtNewtWidget {
+	GInitiallyUnowned parent;
+
+};
+
+typedef struct {
+	GInitiallyUnownedClass parent;
+
+	/* signals */
+	void            (*needs_rebuild)       (NmtNewtWidget *widget);
+	void            (*activated)           (NmtNewtWidget *widget);
+
+	/* methods */
+	void            (*realize)             (NmtNewtWidget *widget);
+	void            (*unrealize)           (NmtNewtWidget *widget);
+
+	newtComponent * (*get_components)      (NmtNewtWidget *widget);
+	NmtNewtWidget * (*find_component)      (NmtNewtWidget *widget,
+	                                        newtComponent  co);
+
+	void            (*size_request)        (NmtNewtWidget *widget,
+	                                        int           *width,
+	                                        int           *height);
+	void            (*size_allocate)       (NmtNewtWidget *widget,
+	                                        int            x,
+	                                        int            y,
+	                                        int            width,
+	                                        int            height);
+
+	newtComponent   (*get_focus_component) (NmtNewtWidget *widget);
+
+} NmtNewtWidgetClass;
+
+GType nmt_newt_widget_get_type (void);
+
+void           nmt_newt_widget_realize        (NmtNewtWidget *widget);
+void           nmt_newt_widget_unrealize      (NmtNewtWidget *widget);
+gboolean       nmt_newt_widget_get_realized   (NmtNewtWidget *widget);
+
+newtComponent *nmt_newt_widget_get_components (NmtNewtWidget *widget);
+
+void           nmt_newt_widget_set_padding    (NmtNewtWidget *widget,
+                                               int            pad_left,
+                                               int            pad_top,
+                                               int            pad_right,
+                                               int            pad_bottom);
+
+void           nmt_newt_widget_size_request   (NmtNewtWidget *widget,
+                                               int           *width,
+                                               int           *height);
+void           nmt_newt_widget_size_allocate  (NmtNewtWidget *widget,
+                                               int            x,
+                                               int            y,
+                                               int            width,
+                                               int            height);
+
+void           nmt_newt_widget_set_parent     (NmtNewtWidget *widget,
+                                               NmtNewtWidget *parent);
+NmtNewtWidget *nmt_newt_widget_get_parent     (NmtNewtWidget *widget);
+
+NmtNewtForm   *nmt_newt_widget_get_form       (NmtNewtWidget *widget);
+
+gboolean       nmt_newt_widget_get_visible    (NmtNewtWidget *widget);
+void           nmt_newt_widget_set_visible    (NmtNewtWidget *widget,
+                                               gboolean       visible);
+
+newtComponent  nmt_newt_widget_get_focus_component  (NmtNewtWidget *widget);
+
+void           nmt_newt_widget_activated            (NmtNewtWidget *widget);
+gboolean       nmt_newt_widget_get_exit_on_activate (NmtNewtWidget *widget);
+void           nmt_newt_widget_set_exit_on_activate (NmtNewtWidget *widget,
+                                                     gboolean       exit_on_activate);
+
+gboolean       nmt_newt_widget_get_valid      (NmtNewtWidget *widget);
+
+NmtNewtWidget *nmt_newt_widget_find_component (NmtNewtWidget *widget,
+                                               newtComponent  co);
+
+/* protected */
+void           nmt_newt_widget_needs_rebuild  (NmtNewtWidget *widget);
+void           nmt_newt_widget_set_valid      (NmtNewtWidget *widget,
+                                               gboolean       valid);
+
+G_END_DECLS
+
+#endif /* NMT_NEWT_WIDGET_H */
diff --git a/clients/tui/newt/nmt-newt.h b/clients/tui/newt/nmt-newt.h
new file mode 100644
index 00000000..6a9c8d9f
--- /dev/null
+++ b/clients/tui/newt/nmt-newt.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_NEWT_H
+#define NMT_NEWT_H
+
+#include "nmt-newt-button.h"
+#include "nmt-newt-button-box.h"
+#include "nmt-newt-checkbox.h"
+#include "nmt-newt-entry.h"
+#include "nmt-newt-entry-numeric.h"
+#include "nmt-newt-form.h"
+#include "nmt-newt-grid.h"
+#include "nmt-newt-label.h"
+#include "nmt-newt-listbox.h"
+#include "nmt-newt-popup.h"
+#include "nmt-newt-section.h"
+#include "nmt-newt-separator.h"
+#include "nmt-newt-stack.h"
+#include "nmt-newt-textbox.h"
+#include "nmt-newt-toggle-button.h"
+#include "nmt-newt-utils.h"
+
+#endif /* NMT_NEWT_H */
diff --git a/clients/tui/nm-editor-bindings.c b/clients/tui/nm-editor-bindings.c
new file mode 100644
index 00000000..9218c31f
--- /dev/null
+++ b/clients/tui/nm-editor-bindings.c
@@ -0,0 +1,1101 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 "nm-editor-bindings.h"
+#include "nm-gvaluearray-compat.h"
+
+static void
+value_transform_string_int (const GValue *src_value,
+                            GValue       *dest_value)
+{
+	long val;
+	char *end;
+
+	val = strtol (g_value_get_string (src_value), &end, 10);
+	if (val < G_MININT || val > G_MAXINT || *end)
+		return;
+
+	g_value_set_int (dest_value, (int) val);
+}
+
+static void
+value_transform_string_uint (const GValue *src_value,
+                             GValue       *dest_value)
+{
+	long val;
+	char *end;
+
+	val = strtol (g_value_get_string (src_value), &end, 10);
+	if (val < 0 || val > G_MAXUINT || *end)
+		return;
+
+	g_value_set_uint (dest_value, (gint) val);
+}
+
+void
+nm_editor_bindings_init (void)
+{
+	/* glib registers number -> string, but not string -> number */
+	g_value_register_transform_func (G_TYPE_STRING, G_TYPE_INT, value_transform_string_int);
+	g_value_register_transform_func (G_TYPE_STRING, G_TYPE_UINT, value_transform_string_uint);
+}
+
+static gboolean
+parse_addr_prefix (const char  *text,
+                   int          family,
+                   char       **addr,
+                   guint32     *prefix)
+{
+	const char *slash;
+	char *addrstr, *end;
+	gboolean valid;
+
+	slash = strchr (text, '/');
+
+	if (slash)
+		addrstr = g_strndup (text, slash - text);
+	else
+		addrstr = g_strdup (text);
+	valid = nm_utils_ipaddr_valid (family, addrstr);
+
+	if (slash) {
+		*prefix = strtoul (slash + 1, &end, 10);
+		if (   *end
+		    || *prefix == 0
+		    || (family == AF_INET && *prefix > 32)
+		    || (family == AF_INET6 && *prefix > 128))
+			valid = FALSE;
+	} else if (prefix) {
+		if (family == AF_INET)
+			*prefix = 32;
+		else
+			*prefix = 128;
+	}
+
+	if (addr && valid)
+		*addr = addrstr;
+	else
+		g_free (addrstr);
+	return valid;
+}
+
+static gboolean
+ip_addresses_with_prefix_to_strv (GBinding     *binding,
+                                  const GValue *source_value,
+                                  GValue       *target_value,
+                                  gpointer      user_data)
+{
+	GPtrArray *addrs;
+	NMIPAddress *addr;
+	const char *addrstr;
+	guint32 prefix;
+	char **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];
+		addrstr = nm_ip_address_get_address (addr);
+		prefix = nm_ip_address_get_prefix (addr);
+
+		if (addrstr)
+			strings[i] = g_strdup_printf ("%s/%d", addrstr, (int) prefix);
+		else
+			strings[i] = g_strdup ("");
+	}
+
+	g_value_take_boxed (target_value, strings);
+	return TRUE;
+}
+
+static gboolean
+ip_addresses_with_prefix_from_strv (GBinding     *binding,
+                                    const GValue *source_value,
+                                    GValue       *target_value,
+                                    gpointer      user_data)
+{
+	int family = GPOINTER_TO_INT (user_data);
+	char **strings;
+	GPtrArray *addrs;
+	NMIPAddress *addr;
+	char *addrstr;
+	guint32 prefix;
+	int i;
+
+	strings = g_value_get_boxed (source_value);
+	/* Fetch the original property value, so as to preserve their extra attributes */
+	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) {
+			if (family == AF_INET)
+				addr = nm_ip_address_new (AF_INET, "0.0.0.0", 32, NULL);
+			else
+				addr = nm_ip_address_new (AF_INET6, "::", 128, NULL);
+			g_ptr_array_add (addrs, addr);
+		} else
+			addr = addrs->pdata[i];
+
+		if (!parse_addr_prefix (strings[i], family, &addrstr, &prefix)) {
+			g_ptr_array_unref (addrs);
+			return FALSE;
+		}
+
+		nm_ip_address_set_address (addr, addrstr);
+		nm_ip_address_set_prefix (addr, prefix);
+		g_free (addrstr);
+	}
+
+	g_ptr_array_set_size (addrs, i);
+	g_value_take_boxed (target_value, addrs);
+	return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip_addresses_with_prefix_to_strv:
+ * @family: the IP address family
+ * @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 #GPtrArray-of-#NMIPAddress property @source_property on @source to
+ * the %G_TYPE_STRV property @target_property on @target.
+ *
+ * Each #NMIPAddress in @source_property will be converted to a string of the
+ * form "ip.ad.dr.ess/prefix" or "ip:ad:dr:ess/prefix" in @target_property (and
+ * vice versa if %G_BINDING_BIDIRECTIONAL) is specified.
+ */
+void
+nm_editor_bind_ip_addresses_with_prefix_to_strv (int            family,
+                                                 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,
+	                             ip_addresses_with_prefix_to_strv,
+	                             ip_addresses_with_prefix_from_strv,
+	                             GINT_TO_POINTER (family), NULL);
+}
+
+static gboolean
+ip_addresses_check_and_copy (GBinding     *binding,
+                             const GValue *source_value,
+                             GValue       *target_value,
+                             gpointer      user_data)
+{
+	int family = GPOINTER_TO_INT (user_data);
+	char **strings;
+	int i;
+
+	strings = g_value_get_boxed (source_value);
+
+	for (i = 0; strings[i]; i++) {
+		if (!nm_utils_ipaddr_valid (family, strings[i]))
+			return FALSE;
+	}
+
+	g_value_set_boxed (target_value, strings);
+	return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip_addresses_to_strv:
+ * @family: the IP address family
+ * @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 %G_TYPE_STRV property @source_property on @source to the
+ * %G_TYPE_STRV property @target_property on @target, verifying that
+ * each string is a valid address of type @family when copying.
+ */
+void
+nm_editor_bind_ip_addresses_to_strv (int            family,
+                                     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,
+	                             ip_addresses_check_and_copy,
+	                             ip_addresses_check_and_copy,
+	                             GINT_TO_POINTER (family), NULL);
+}
+
+static gboolean
+ip_gateway_to_string (GBinding     *binding,
+                      const GValue *source_value,
+                      GValue       *target_value,
+                      gpointer      user_data)
+{
+	g_value_set_string (target_value, g_value_get_string (source_value));
+	return TRUE;
+}
+
+static gboolean
+ip_gateway_from_string (GBinding     *binding,
+                        const GValue *source_value,
+                        GValue       *target_value,
+                        gpointer      user_data)
+{
+	int family = GPOINTER_TO_INT (user_data);
+	const char *gateway;
+
+	gateway = g_value_get_string (source_value);
+	if (gateway && !nm_utils_ipaddr_valid (family, gateway))
+		gateway = NULL;
+
+	g_value_set_string (target_value, gateway);
+	return TRUE;
+}
+
+static gboolean
+ip_addresses_to_gateway (GBinding     *binding,
+                         const GValue *source_value,
+                         GValue       *target_value,
+                         gpointer      user_data)
+{
+	GPtrArray *addrs;
+
+	addrs = g_value_get_boxed (source_value);
+	if (addrs->len == 0) {
+		g_value_set_string (target_value, NULL);
+		return TRUE;
+	} else
+		return FALSE;
+}
+
+static gboolean
+ip_addresses_to_sensitivity (GBinding     *binding,
+                             const GValue *source_value,
+                             GValue       *target_value,
+                             gpointer      user_data)
+{
+	GPtrArray *addrs;
+
+	addrs = g_value_get_boxed (source_value);
+	g_value_set_boolean (target_value, addrs->len != 0);
+	return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip_gateway_to_string:
+ * @family: the IP address family
+ * @source: the source #NMSettingIPConfig
+ * @target: the target object (eg, an #NmtIPEntry)
+ * @target_property: the property on @target to bind (eg, "text")
+ * @target_sensitive_property: the "sensitivity" property on @target to bind
+ * @flags: %GBindingFlags
+ *
+ * Binds the #NMSettingIPConfig:gateway property on @source to the
+ * %G_TYPE_STRING property @target_property and %G_TYPE_BOOLEAN property
+ * @target_sensitive_property on @target, also taking the
+ * #NMSettingIPConfig:addresses property on @source into account.
+ *
+ * In particular, if @source has no static IP addresses, then @target_property
+ * will be set to "" and @target_sensitive_property will be set to %FALSE.
+ *
+ * If @source has at least one static IP address, then
+ * @target_sensitive_property will be set to %TRUE, @target_property will be
+ * initialized from @source's #NMSettingIPConfig:gateway, and @source will be
+ * updated with the value of @target_property whenever it contains a valid IP
+ * address.
+ */
+void
+nm_editor_bind_ip_gateway_to_string (int                family,
+                                     NMSettingIPConfig *source,
+                                     gpointer           target,
+                                     const gchar       *target_property,
+                                     const gchar       *target_sensitive_property,
+                                     GBindingFlags      flags)
+{
+	g_object_bind_property_full (source, "gateway",
+	                             target, target_property,
+	                             flags,
+	                             ip_gateway_to_string,
+	                             ip_gateway_from_string,
+	                             GINT_TO_POINTER (family), NULL);
+	g_object_bind_property_full (source, "addresses",
+	                             source, "gateway",
+	                             (flags & G_BINDING_SYNC_CREATE),
+	                             ip_addresses_to_gateway,
+	                             NULL,
+	                             NULL, NULL);
+	g_object_bind_property_full (source, "addresses",
+	                             target, target_sensitive_property,
+	                             (flags & G_BINDING_SYNC_CREATE),
+	                             ip_addresses_to_sensitivity,
+	                             NULL,
+	                             NULL, NULL);
+}
+
+static gboolean
+ip_route_transform_to_dest_string (GBinding     *binding,
+                                   const GValue *source_value,
+                                   GValue       *target_value,
+                                   gpointer      user_data)
+{
+	NMIPRoute *route;
+	const char *addrstr;
+	char *string;
+
+	route = g_value_get_boxed (source_value);
+	if (route)
+		addrstr = nm_ip_route_get_dest (route);
+	else
+		addrstr = NULL;
+
+	if (addrstr) {
+		string = g_strdup_printf ("%s/%d", addrstr, (int) nm_ip_route_get_prefix (route));
+		g_value_take_string (target_value, string);
+	} else
+		g_value_set_string (target_value, "");
+	return TRUE;
+}
+
+static gboolean
+ip_route_transform_to_next_hop_string (GBinding     *binding,
+                                       const GValue *source_value,
+                                       GValue       *target_value,
+                                       gpointer      user_data)
+{
+	NMIPRoute *route;
+	const char *addrstr;
+
+	route = g_value_get_boxed (source_value);
+	if (route) {
+		addrstr = nm_ip_route_get_next_hop (route);
+		if (!addrstr)
+			addrstr = "";
+	} else
+		addrstr = "";
+
+	g_value_set_string (target_value, addrstr);
+	return TRUE;
+}
+
+static gboolean
+ip_route_transform_to_metric_string (GBinding     *binding,
+                                     const GValue *source_value,
+                                     GValue       *target_value,
+                                     gpointer      user_data)
+{
+	NMIPRoute *route;
+	char *string;
+
+	route = g_value_get_boxed (source_value);
+	if (route && nm_ip_route_get_dest (route) && nm_ip_route_get_metric (route) != -1) {
+		string = g_strdup_printf ("%lu", (gulong) nm_ip_route_get_metric (route));
+		g_value_take_string (target_value, string);
+	} else
+		g_value_set_string (target_value, "");
+	return TRUE;
+}
+
+static gboolean
+ip_route_transform_from_dest_string (GBinding     *binding,
+                                     const GValue *source_value,
+                                     GValue       *target_value,
+                                     gpointer      user_data)
+{
+	int family = GPOINTER_TO_INT (user_data);
+	NMIPRoute *route;
+	const char *text;
+	char *addrstr;
+	guint32 prefix;
+
+	text = g_value_get_string (source_value);
+	if (!parse_addr_prefix (text, family, &addrstr, &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_ip_route_set_dest (route, addrstr);
+	nm_ip_route_set_prefix (route, prefix);
+	g_free (addrstr);
+
+	g_value_take_boxed (target_value, route);
+	return TRUE;
+}
+
+static gboolean
+ip_route_transform_from_next_hop_string (GBinding     *binding,
+                                         const GValue *source_value,
+                                         GValue       *target_value,
+                                         gpointer      user_data)
+{
+	int family = GPOINTER_TO_INT (user_data);
+	NMIPRoute *route;
+	const char *text;
+
+	text = g_value_get_string (source_value);
+	if (*text) {
+		if (!nm_utils_ipaddr_valid (family, text))
+			return FALSE;
+	} else
+		text = NULL;
+
+	/* Fetch the original property value */
+	g_object_get (g_binding_get_source (binding),
+	              g_binding_get_source_property (binding), &route,
+	              NULL);
+
+	nm_ip_route_set_next_hop (route, text);
+
+	g_value_take_boxed (target_value, route);
+	return TRUE;
+}
+
+static gboolean
+ip_route_transform_from_metric_string (GBinding     *binding,
+                                       const GValue *source_value,
+                                       GValue       *target_value,
+                                       gpointer      user_data)
+{
+	NMIPRoute *route;
+	const char *text;
+	gint64 metric;
+
+	text = g_value_get_string (source_value);
+	if (*text)
+		metric = strtoul (text, NULL, 10);
+	else
+		metric = -1;
+
+	/* Fetch the original property value */
+	g_object_get (g_binding_get_source (binding),
+	              g_binding_get_source_property (binding), &route,
+	              NULL);
+
+	nm_ip_route_set_metric (route, metric);
+
+	g_value_take_boxed (target_value, route);
+	return TRUE;
+}
+
+/**
+ * nm_editor_bind_ip_route_to_strings:
+ * @family: the IP address family
+ * @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 #NMIPRoute-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_property
+ * is a plain IP address, and @metric_target_property is a number.
+ */
+void
+nm_editor_bind_ip_route_to_strings (int            family,
+                                    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,
+	                             ip_route_transform_to_dest_string,
+	                             ip_route_transform_from_dest_string,
+	                             GINT_TO_POINTER (family), NULL);
+	g_object_bind_property_full (source, source_property,
+	                             next_hop_target, next_hop_target_property,
+	                             flags,
+	                             ip_route_transform_to_next_hop_string,
+	                             ip_route_transform_from_next_hop_string,
+	                             GINT_TO_POINTER (family), NULL);
+	g_object_bind_property_full (source, source_property,
+	                             metric_target, metric_target_property,
+	                             flags,
+	                             ip_route_transform_to_metric_string,
+	                             ip_route_transform_from_metric_string,
+	                             GINT_TO_POINTER (family), 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;
+	NMSettingConnection *s_con;
+
+	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_connection_get_interface_name (binding->s_con);
+	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,
+                          NMSettingConnection *s_con)
+{
+	NMEditorVlanWidgetBinding *binding;
+	const char *ifname;
+
+	binding = g_slice_new0 (NMEditorVlanWidgetBinding);
+	binding->s_vlan = s_vlan;
+	binding->s_con = s_con;
+
+	g_signal_connect (s_con, "notify::" NM_SETTING_CONNECTION_INTERFACE_NAME,
+	                  G_CALLBACK (vlan_settings_changed), binding);
+
+	g_object_weak_ref (G_OBJECT (s_vlan), vlan_target_destroyed, binding);
+
+	ifname = nm_setting_connection_get_interface_name (s_con);
+	if (!parse_interface_name (ifname, &binding->last_ifname_parent, &binding->last_ifname_id)) {
+		binding->last_ifname_parent = NULL;
+		binding->last_ifname_id = 0;
+	}
+}
diff --git a/clients/tui/nm-editor-bindings.h b/clients/tui/nm-editor-bindings.h
new file mode 100644
index 00000000..c4ed95d5
--- /dev/null
+++ b/clients/tui/nm-editor-bindings.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 <NetworkManager.h>
+
+G_BEGIN_DECLS
+
+void nm_editor_bindings_init (void);
+
+void nm_editor_bind_ip_addresses_with_prefix_to_strv  (int            family,
+                                                       gpointer       source,
+                                                       const gchar   *source_property,
+                                                       gpointer       target,
+                                                       const gchar   *target_property,
+                                                       GBindingFlags  flags);
+void nm_editor_bind_ip_addresses_to_strv              (int            family,
+                                                       gpointer       source,
+                                                       const gchar   *source_property,
+                                                       gpointer       target,
+                                                       const gchar   *target_property,
+                                                       GBindingFlags  flags);
+
+void nm_editor_bind_ip_gateway_to_string              (int            family,
+                                                       NMSettingIPConfig *source,
+                                                       gpointer       target,
+                                                       const gchar   *target_property,
+                                                       const gchar   *target_sensitive_property,
+                                                       GBindingFlags  flags);
+
+void nm_editor_bind_ip_route_to_strings               (int            family,
+                                                       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,
+                                                       NMSettingConnection *s_con);
+
+G_END_DECLS
+
+#endif /* NM_EDITOR_BINDINGS_H */
diff --git a/clients/tui/nm-editor-utils.c b/clients/tui/nm-editor-utils.c
new file mode 100644
index 00000000..f936c959
--- /dev/null
+++ b/clients/tui/nm-editor-utils.c
@@ -0,0 +1,414 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You 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 <NetworkManager.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);
+#endif
+
+	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);
+
+	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,
+                               NMClient   *client)
+{
+	const GPtrArray *conns;
+	GSList *names = NULL, *iter;
+	char *cname = NULL;
+	int i = 0;
+
+	conns = nm_client_get_connections (client);
+	for (i = 0; i < conns->len; i++) {
+		const char *id;
+
+		id = nm_connection_get_id (NM_CONNECTION (conns->pdata[i]));
+		g_assert (id);
+		names = g_slist_append (names, (gpointer) id);
+	}
+
+	/* Find the next available unique connection name */
+	for (i = 1; !cname && i < 10000; i++) {
+		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
+ * @client: an #NMClient
+ *
+ * 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,
+                                   NMClient     *client)
+{
+	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_setting_lookup_type (master_setting_type);
+	}
+
+	types = nm_editor_utils_get_connection_type_list ();
+	for (i = 0; types[i]; i++) {
+		if (types[i]->setting_type == type)
+			type_data = (NMEditorConnectionTypeDataReal *)types[i];
+		if (types[i]->setting_type == master_type)
+			slave_setting_type = types[i]->slave_setting_type;
+
+	}
+	if (!type_data) {
+		g_return_val_if_reached (NULL);
+		return NULL;
+	}
+
+	connection = nm_simple_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, client);
+
+	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_setting_lookup_type (conn_type);
+	g_return_val_if_fail (conn_gtype != G_TYPE_INVALID, NULL);
+
+	types = nm_editor_utils_get_connection_type_list ();
+	for (i = 0; types[i]; i++) {
+		if (types[i]->setting_type == conn_gtype)
+			return types[i];
+	}
+
+	return NULL;
+}
diff --git a/clients/tui/nm-editor-utils.h b/clients/tui/nm-editor-utils.h
new file mode 100644
index 00000000..5e402ce7
--- /dev/null
+++ b/clients/tui/nm-editor-utils.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You 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 <NetworkManager.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,
+                                                 NMClient     *client);
+
+G_END_DECLS
+
+#endif /* NM_EDITOR_UTILS_H */
diff --git a/clients/tui/nmt-address-list.c b/clients/tui/nmt-address-list.c
new file mode 100644
index 00000000..42873c52
--- /dev/null
+++ b/clients/tui/nmt-address-list.c
@@ -0,0 +1,285 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 <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 = priv->strings ? g_strv_length (priv->strings) : 0;
+	priv->strings = g_renew (char *, priv->strings, len + 2);
+	priv->strings[len] = g_strdup ("");
+	priv->strings[len + 1] = NULL;
+
+	nmt_widget_list_set_length (list, len + 1);
+	g_object_notify (G_OBJECT (list), "strings");
+}
+
+static void
+nmt_address_list_remove_clicked (NmtWidgetList *list,
+                                 int            num)
+{
+	NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list);
+	int len;
+
+	len = g_strv_length (priv->strings);
+	g_free (priv->strings[num]);
+	memmove (priv->strings + num, priv->strings + num + 1, (len - num) * sizeof (char *));
+
+	nmt_widget_list_set_length (list, len - 1);
+	g_object_notify (G_OBJECT (list), "strings");
+}
+
+static void
+nmt_address_list_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+	NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_LIST_TYPE:
+		priv->list_type = g_value_get_uint (value);
+		break;
+	case PROP_STRINGS:
+		g_strfreev (priv->strings);
+		priv->strings = g_value_dup_boxed (value);
+		if (!priv->strings)
+			priv->strings = g_new0 (char *, 1);
+		nmt_widget_list_set_length (NMT_WIDGET_LIST (object),
+		                            g_strv_length (priv->strings));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_address_list_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+	NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_LIST_TYPE:
+		g_value_set_uint (value, priv->list_type);
+		break;
+	case PROP_STRINGS:
+		g_value_set_boxed (value, priv->strings);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_address_list_class_init (NmtAddressListClass *list_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+	NmtWidgetListClass *widget_list_class = NMT_WIDGET_LIST_CLASS (list_class);
+
+	g_type_class_add_private (list_class, sizeof (NmtAddressListPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_address_list_set_property;
+	object_class->get_property = nmt_address_list_get_property;
+
+	widget_list_class->create_widget  = nmt_address_list_create_widget;
+	widget_list_class->add_clicked    = nmt_address_list_add_clicked;
+	widget_list_class->remove_clicked = nmt_address_list_remove_clicked;
+
+	/**
+	 * NmtAddressList:list-type:
+	 *
+	 * The type of address the list holds.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_LIST_TYPE,
+		 g_param_spec_uint ("list-type", "", "",
+		                    0, G_MAXUINT, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtAddressList:strings:
+	 *
+	 * The strings in the list's entries.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_STRINGS,
+		 g_param_spec_boxed ("strings", "", "",
+		                     G_TYPE_STRV,
+		                     G_PARAM_READWRITE |
+		                     G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-address-list.h b/clients/tui/nmt-address-list.h
new file mode 100644
index 00000000..df7a4f79
--- /dev/null
+++ b/clients/tui/nmt-address-list.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_ADDRESS_LIST_H
+#define NMT_ADDRESS_LIST_H
+
+#include "nmt-widget-list.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_ADDRESS_LIST            (nmt_address_list_get_type ())
+#define NMT_ADDRESS_LIST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressList))
+#define NMT_ADDRESS_LIST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass))
+#define NMT_IS_ADDRESS_LIST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ADDRESS_LIST))
+#define NMT_IS_ADDRESS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ADDRESS_LIST))
+#define NMT_ADDRESS_LIST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass))
+
+typedef struct {
+	NmtWidgetList parent;
+
+} NmtAddressList;
+
+typedef struct {
+	NmtWidgetListClass parent;
+
+} NmtAddressListClass;
+
+GType nmt_address_list_get_type (void);
+
+typedef enum {
+	NMT_ADDRESS_LIST_IP4_WITH_PREFIX,
+	NMT_ADDRESS_LIST_IP4,
+	NMT_ADDRESS_LIST_IP6_WITH_PREFIX,
+	NMT_ADDRESS_LIST_IP6,
+	NMT_ADDRESS_LIST_HOSTNAME
+} NmtAddressListType;
+
+NmtNewtWidget *nmt_address_list_new (NmtAddressListType list_type);
+
+G_END_DECLS
+
+#endif /* NMT_ADDRESS_LIST_H */
diff --git a/clients/tui/nmt-connect-connection-list.c b/clients/tui/nmt-connect-connection-list.c
new file mode 100644
index 00000000..08058066
--- /dev/null
+++ b/clients/tui/nmt-connect-connection-list.c
@@ -0,0 +1,701 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 <NetworkManager.h>
+
+#include "nmtui.h"
+#include "nmt-connect-connection-list.h"
+
+G_DEFINE_TYPE (NmtConnectConnectionList, nmt_connect_connection_list, NMT_TYPE_NEWT_LISTBOX)
+
+#define NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListPrivate))
+
+typedef struct {
+	char *name;
+	NMDevice *device;
+
+	int sort_order;
+
+	GSList *conns;
+} NmtConnectDevice;
+
+typedef struct {
+	const char *name;
+	char *ssid;
+
+	NMConnection *conn;
+	NMAccessPoint *ap;
+	NMDevice *device;
+	NMActiveConnection *active;
+} NmtConnectConnection;
+
+typedef struct {
+	GSList *nmt_devices;
+} NmtConnectConnectionListPrivate;
+
+/**
+ * nmt_connect_connection_list_new:
+ *
+ * Creates a new #NmtConnectConnectionList
+ *
+ * Returns: a new #NmtConnectConnectionList
+ */
+NmtNewtWidget *
+nmt_connect_connection_list_new (void)
+{
+	return g_object_new (NMT_TYPE_CONNECT_CONNECTION_LIST,
+	                     "flags", NMT_NEWT_LISTBOX_SCROLL | NMT_NEWT_LISTBOX_BORDER,
+	                     "skip-null-keys", TRUE,
+	                     NULL);
+}
+
+static void
+nmt_connect_connection_list_init (NmtConnectConnectionList *list)
+{
+}
+
+static void
+nmt_connect_connection_free (NmtConnectConnection *nmtconn)
+{
+	g_clear_object (&nmtconn->conn);
+	g_clear_object (&nmtconn->ap);
+	g_clear_object (&nmtconn->active);
+	g_free (nmtconn->ssid);
+}
+
+static void
+nmt_connect_device_free (NmtConnectDevice *nmtdev)
+{
+	g_clear_pointer (&nmtdev->name, g_free);
+	g_clear_object (&nmtdev->device);
+
+	g_slist_free_full (nmtdev->conns, (GDestroyNotify) nmt_connect_connection_free);
+}
+
+static const char *device_sort_order[] = {
+	"NMDeviceEthernet",
+	"NMDeviceInfiniband",
+	"NMDeviceWifi",
+	NM_SETTING_VLAN_SETTING_NAME,
+	NM_SETTING_BOND_SETTING_NAME,
+	NM_SETTING_TEAM_SETTING_NAME,
+	NM_SETTING_BRIDGE_SETTING_NAME,
+	"NMDeviceModem",
+	"NMDeviceBt"
+};
+static const int device_sort_order_len = G_N_ELEMENTS (device_sort_order);
+
+static int
+get_sort_order_for_device (NMDevice *device)
+{
+	const char *type;
+	int i;
+
+	type = G_OBJECT_TYPE_NAME (device);
+	for (i = 0; i < device_sort_order_len; i++) {
+		if (!strcmp (type, device_sort_order[i]))
+			return i;
+	}
+
+	return -1;
+}
+
+static int
+get_sort_order_for_connection (NMConnection *conn)
+{
+	NMSettingConnection *s_con;
+	const char *type;
+	int i;
+
+	s_con = nm_connection_get_setting_connection (conn);
+	type = nm_setting_connection_get_connection_type (s_con);
+
+	for (i = 0; i < device_sort_order_len; i++) {
+		if (!strcmp (type, device_sort_order[i]))
+			return i;
+	}
+
+	return -1;
+}
+
+static int
+sort_connections (gconstpointer  a,
+                  gconstpointer  b)
+{
+	NmtConnectConnection *nmta = (NmtConnectConnection *)a;
+	NmtConnectConnection *nmtb = (NmtConnectConnection *)b;
+
+	/* If nmta and nmtb both have NMConnections, sort them by timestamp */
+	if (nmta->conn && nmtb->conn) {
+		NMSettingConnection *s_con_a, *s_con_b;
+		guint64 time_a, time_b;
+
+		s_con_a = nm_connection_get_setting_connection (nmta->conn);
+		s_con_b = nm_connection_get_setting_connection (nmtb->conn);
+
+		time_a = nm_setting_connection_get_timestamp (s_con_a);
+		time_b = nm_setting_connection_get_timestamp (s_con_b);
+
+		return (int) (time_b - time_a);
+	}
+
+	/* If one is an NMConnection and the other is an NMAccessPoint, the
+	 * connection comes first.
+	 */
+	if (nmta->conn)
+		return -1;
+	else if (nmtb->conn)
+		return 1;
+
+	g_return_val_if_fail (nmta->ap && nmtb->ap, 0);
+
+	/* If both are access points, then sort by strength */
+	return nm_access_point_get_strength (nmtb->ap) - nm_access_point_get_strength (nmta->ap);
+}
+
+static void
+add_connections_for_device (NmtConnectDevice *nmtdev,
+                            const GPtrArray  *connections)
+{
+	int i;
+
+	for (i = 0; i < connections->len; i++) {
+		NMConnection *conn = connections->pdata[i];
+		NMSettingConnection *s_con;
+
+		s_con = nm_connection_get_setting_connection (conn);
+		if (nm_setting_connection_get_master (s_con))
+			continue;
+
+		if (nm_device_connection_valid (nmtdev->device, conn)) {
+			NmtConnectConnection *nmtconn = g_slice_new0 (NmtConnectConnection);
+
+			nmtconn->name = nm_connection_get_id (conn);
+			nmtconn->device = nmtdev->device;
+			nmtconn->conn = g_object_ref (conn);
+			nmtdev->conns = g_slist_prepend (nmtdev->conns, nmtconn);
+		}
+	}
+}
+
+/* stolen from nm-applet */
+static char *
+hash_ap (NMAccessPoint *ap)
+{
+	unsigned char input[66];
+	GBytes *ssid;
+	NM80211Mode mode;
+	guint32 flags;
+	guint32 wpa_flags;
+	guint32 rsn_flags;
+
+	memset (&input[0], 0, sizeof (input));
+
+	ssid = nm_access_point_get_ssid (ap);
+	if (ssid)
+		memcpy (input, g_bytes_get_data (ssid, NULL), g_bytes_get_size (ssid));
+
+	mode = nm_access_point_get_mode (ap);
+	if (mode == NM_802_11_MODE_INFRA)
+		input[32] |= (1 << 0);
+	else if (mode == NM_802_11_MODE_ADHOC)
+		input[32] |= (1 << 1);
+	else
+		input[32] |= (1 << 2);
+
+	/* Separate out no encryption, WEP-only, and WPA-capable */
+	flags = nm_access_point_get_flags (ap);
+	wpa_flags = nm_access_point_get_wpa_flags (ap);
+	rsn_flags = nm_access_point_get_rsn_flags (ap);
+	if (  !(flags & NM_802_11_AP_FLAGS_PRIVACY)
+	      && (wpa_flags == NM_802_11_AP_SEC_NONE)
+	      && (rsn_flags == NM_802_11_AP_SEC_NONE))
+		input[32] |= (1 << 3);
+	else if (   (flags & NM_802_11_AP_FLAGS_PRIVACY)
+	            && (wpa_flags == NM_802_11_AP_SEC_NONE)
+	            && (rsn_flags == NM_802_11_AP_SEC_NONE))
+		input[32] |= (1 << 4);
+	else if (   !(flags & NM_802_11_AP_FLAGS_PRIVACY)
+	            &&  (wpa_flags != NM_802_11_AP_SEC_NONE)
+	            &&  (rsn_flags != NM_802_11_AP_SEC_NONE))
+		input[32] |= (1 << 5);
+	else
+		input[32] |= (1 << 6);
+
+	/* duplicate it */
+	memcpy (&input[33], &input[0], 32);
+	return g_compute_checksum_for_data (G_CHECKSUM_MD5, input, sizeof (input));
+}
+
+static void
+add_connections_for_aps (NmtConnectDevice *nmtdev,
+                         const GPtrArray  *connections)
+{
+	NmtConnectConnection *nmtconn;
+	NMConnection *conn;
+	NMAccessPoint *ap;
+	const GPtrArray *aps;
+	GHashTable *seen_ssids;
+	GBytes *ssid;
+	char *ap_hash;
+	int i, c;
+
+	aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (nmtdev->device));
+	if (!aps->len)
+		return;
+
+	seen_ssids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+	for (i = 0; i < aps->len; i++) {
+		ap = aps->pdata[i];
+
+		if (!nm_access_point_get_ssid (ap))
+			continue;
+
+		ap_hash = hash_ap (ap);
+		if (g_hash_table_contains (seen_ssids, ap_hash)) {
+			g_free (ap_hash);
+			continue;
+		}
+		g_hash_table_add (seen_ssids, ap_hash);
+
+		nmtconn = g_slice_new0 (NmtConnectConnection);
+		nmtconn->device = nmtdev->device;
+		nmtconn->ap = g_object_ref (ap);
+		ssid = nm_access_point_get_ssid (ap);
+		if (ssid)
+			nmtconn->ssid = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
+			                                       g_bytes_get_size (ssid));
+
+		for (c = 0; c < connections->len; c++) {
+			conn = connections->pdata[c];
+			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 (!nmtconn->name)
+			nmtconn->name = nmtconn->ssid ? nmtconn->ssid : "<unknown>";
+
+		nmtdev->conns = g_slist_prepend (nmtdev->conns, nmtconn);
+	}
+
+	g_hash_table_unref (seen_ssids);
+}
+
+static GSList *
+append_nmt_devices_for_devices (GSList           *nmt_devices,
+                                const GPtrArray  *devices,
+                                char            **names,
+                                const GPtrArray  *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,
+                                        const GPtrArray *connections)
+{
+	NmtConnectDevice *nmtdev = NULL;
+	int i;
+	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 (i = 0; i < connections->len; i++) {
+		conn = connections->pdata[i];
+		sort_order = get_sort_order_for_connection (conn);
+		if (sort_order == -1)
+			continue;
+
+		name = nm_connection_get_virtual_device_description (conn);
+		if (name)
+			nmtdev = g_hash_table_lookup (devices_by_name, name);
+		if (nmtdev)
+			g_free (name);
+		else {
+			nmtdev = g_slice_new0 (NmtConnectDevice);
+			nmtdev->name = name ? name : g_strdup ("Unknown");
+			nmtdev->sort_order = sort_order;
+
+			g_hash_table_insert (devices_by_name, nmtdev->name, nmtdev);
+			nmt_devices = g_slist_prepend (nmt_devices, nmtdev);
+		}
+
+		nmtconn = g_slice_new0 (NmtConnectConnection);
+		nmtconn->name = nm_connection_get_id (conn);
+		nmtconn->conn = g_object_ref (conn);
+
+		nmtdev->conns = g_slist_insert_sorted (nmtdev->conns, nmtconn, sort_connections);
+	}
+
+	g_hash_table_destroy (devices_by_name);
+	return nmt_devices;
+}
+
+static GSList *
+append_nmt_devices_for_vpns (GSList          *nmt_devices,
+                             const GPtrArray *connections)
+{
+	NmtConnectDevice *nmtdev;
+	int i;
+	NMConnection *conn;
+	NmtConnectConnection *nmtconn;
+
+	nmtdev = g_slice_new0 (NmtConnectDevice);
+	nmtdev->name = g_strdup (_("VPN"));
+	nmtdev->sort_order = 100;
+
+	for (i = 0; i < connections->len; i++) {
+		conn = connections->pdata[i];
+		if (!nm_connection_is_type (conn, NM_SETTING_VPN_SETTING_NAME))
+			continue;
+
+		nmtconn = g_slice_new0 (NmtConnectConnection);
+		nmtconn->name = nm_connection_get_id (conn);
+		nmtconn->conn = g_object_ref (conn);
+
+		nmtdev->conns = g_slist_insert_sorted (nmtdev->conns, nmtconn, sort_connections);
+	}
+
+	if (nmtdev->conns)
+		nmt_devices = g_slist_prepend (nmt_devices, nmtdev);
+	else
+		nmt_connect_device_free (nmtdev);
+
+	return nmt_devices;
+}
+
+static int
+sort_nmt_devices (gconstpointer  a,
+                  gconstpointer  b)
+{
+	NmtConnectDevice *nmta = (NmtConnectDevice *)a;
+	NmtConnectDevice *nmtb = (NmtConnectDevice *)b;
+
+	if (nmta->sort_order != nmtb->sort_order)
+		return nmta->sort_order - nmtb->sort_order;
+
+	return strcmp (nmta->name, nmtb->name);
+}
+
+static NMActiveConnection *
+connection_find_ac (NMConnection    *conn,
+                    const GPtrArray *acs)
+{
+	NMActiveConnection *ac;
+	NMRemoteConnection *ac_conn;
+	int i;
+
+	for (i = 0; i < acs->len; i++) {
+		ac = acs->pdata[i];
+		ac_conn = nm_active_connection_get_connection (ac);
+
+		if (conn == NM_CONNECTION (ac_conn))
+			return ac;
+	}
+
+	return NULL;
+}
+
+static void
+nmt_connect_connection_list_rebuild (NmtConnectConnectionList *list)
+{
+	NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (list);
+	NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (list);
+	const GPtrArray *devices, *acs, *connections;
+	int max_width;
+	char **names, *row, active_col;
+	const char *strength_col;
+	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_client_get_connections (nm_client);
+
+	nmt_devices = NULL;
+
+	names = nm_device_disambiguate_names ((NMDevice **) devices->pdata, devices->len);
+	nmt_devices = append_nmt_devices_for_devices (nmt_devices, devices, names, connections);
+	g_strfreev (names);
+
+	nmt_devices = append_nmt_devices_for_virtual_devices (nmt_devices, connections);
+	nmt_devices = append_nmt_devices_for_vpns (nmt_devices, connections);
+
+	nmt_devices = g_slist_sort (nmt_devices, sort_nmt_devices);
+
+	max_width = 0;
+	for (diter = nmt_devices; diter; diter = diter->next) {
+		nmtdev = diter->data;
+		for (citer = nmtdev->conns; citer; citer = citer->next) {
+			nmtconn = citer->data;
+
+			max_width = MAX (max_width, nmt_newt_text_width (nmtconn->name));
+		}
+	}
+
+	for (diter = nmt_devices; diter; diter = diter->next) {
+		nmtdev = diter->data;
+
+		if (diter != nmt_devices)
+			nmt_newt_listbox_append (listbox, "", NULL);
+		nmt_newt_listbox_append (listbox, nmtdev->name, NULL);
+
+		for (citer = nmtdev->conns; citer; citer = citer->next) {
+			nmtconn = citer->data;
+
+			if (nmtconn->conn)
+				nmtconn->active = connection_find_ac (nmtconn->conn, acs);
+			if (nmtconn->active) {
+				g_object_ref (nmtconn->active);
+				active_col = '*';
+			} else
+				active_col = ' ';
+
+			if (nmtconn->ap) {
+				guint8 strength = nm_access_point_get_strength (nmtconn->ap);
+
+				strength_col = nm_utils_wifi_strength_bars (strength);
+			} else
+				strength_col = NULL;
+
+			row = g_strdup_printf ("%c %s%-*s%s%s",
+			                       active_col,
+			                       nmtconn->name,
+			                       (int)(max_width - nmt_newt_text_width (nmtconn->name)), "",
+			                       strength_col ? " " : "",
+			                       strength_col ? strength_col : "");
+
+			nmt_newt_listbox_append (listbox, row, nmtconn);
+			g_free (row);
+		}
+	}
+
+	priv->nmt_devices = nmt_devices;
+
+	g_object_notify (G_OBJECT (listbox), "active");
+	g_object_notify (G_OBJECT (listbox), "active-key");
+}
+
+static void
+rebuild_on_property_changed (GObject    *object,
+                             GParamSpec *spec,
+                             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_property_changed), list);
+	g_signal_connect (nm_client, "notify::" NM_CLIENT_CONNECTIONS,
+	                  G_CALLBACK (rebuild_on_property_changed), list);
+	g_signal_connect (nm_client, "notify::" NM_CLIENT_DEVICES,
+	                  G_CALLBACK (rebuild_on_property_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_property_changed), object);
+
+	G_OBJECT_CLASS (nmt_connect_connection_list_parent_class)->finalize (object);
+}
+
+static void
+nmt_connect_connection_list_class_init (NmtConnectConnectionListClass *list_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+
+	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;
+}
+
+/**
+ * nmt_connect_connection_list_get_connection:
+ * @list: an #NmtConnectConnectionList
+ * @identifier: a connection ID or UUID, or device name
+ * @connection: (out) (transfer none): the #NMConnection to be activated
+ * @device: (out) (transfer none): the #NMDevice to activate @connection on
+ * @specific_object: (out) (transfer none): the "specific object" to connect to
+ * @active: (out) (transfer none): the #NMActiveConnection corresponding
+ *   to the selection, if any.
+ *
+ * Gets information about the indicated connection.
+ *
+ * Returns: %TRUE if there was a match, %FALSE if not.
+ */
+gboolean
+nmt_connect_connection_list_get_connection (NmtConnectConnectionList  *list,
+                                            const char                *identifier,
+                                            NMConnection             **connection,
+                                            NMDevice                 **device,
+                                            NMObject                 **specific_object,
+                                            NMActiveConnection       **active)
+{
+	NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (list);
+	GSList *diter, *citer;
+	NmtConnectDevice *nmtdev;
+	NmtConnectConnection *nmtconn = NULL;
+	NMConnection *conn = NULL;
+
+	g_return_val_if_fail (identifier, FALSE);
+
+	if (nm_utils_is_uuid (identifier))
+		conn = NM_CONNECTION (nm_client_get_connection_by_uuid (nm_client, identifier));
+	if (!conn)
+		conn = NM_CONNECTION (nm_client_get_connection_by_id (nm_client, identifier));
+
+	for (diter = priv->nmt_devices; diter; diter = diter->next) {
+		nmtdev = diter->data;
+		if (!nmtdev->conns)
+			continue;
+
+		for (citer = nmtdev->conns; citer; citer = citer->next) {
+			nmtconn = citer->data;
+			if (conn) {
+				if (conn == nmtconn->conn)
+					goto found;
+			} else if (nmtconn->ssid && !strcmp (identifier, nmtconn->ssid))
+				goto found;
+		}
+
+		if (!conn && nmtdev->device && !strcmp (identifier, nm_device_get_ip_iface (nmtdev->device))) {
+			nmtconn = nmtdev->conns->data;
+			goto found;
+		}
+	}
+
+	return FALSE;
+
+ found:
+	if (connection)
+		*connection = nmtconn->conn;
+	if (device)
+		*device = nmtconn->device;
+	if (specific_object)
+		*specific_object = NM_OBJECT (nmtconn->ap);
+	if (active)
+		*active = nmtconn->active;
+
+	return TRUE;
+}
+
+/**
+ * nmt_connect_connection_list_get_selection:
+ * @list: an #NmtConnectConnectionList
+ * @connection: (out) (transfer none): the #NMConnection to be activated
+ * @device: (out) (transfer none): the #NMDevice to activate @connection on
+ * @specific_object: (out) (transfer none): the "specific object" to connect to
+ * @active: (out) (transfer none): the #NMActiveConnection corresponding
+ *   to the selection, if any.
+ *
+ * Gets information about the selected row.
+ *
+ * Returns: %TRUE if there is a selection, %FALSE if not.
+ */
+gboolean
+nmt_connect_connection_list_get_selection (NmtConnectConnectionList  *list,
+                                           NMConnection             **connection,
+                                           NMDevice                 **device,
+                                           NMObject                 **specific_object,
+                                           NMActiveConnection       **active)
+{
+	NmtConnectConnection *nmtconn;
+
+	nmtconn = nmt_newt_listbox_get_active_key (NMT_NEWT_LISTBOX (list));
+	if (!nmtconn)
+		return FALSE;
+
+	if (connection)
+		*connection = nmtconn->conn;
+	if (device)
+		*device = nmtconn->device;
+	if (specific_object)
+		*specific_object = NM_OBJECT (nmtconn->ap);
+	if (active)
+		*active = nmtconn->active;
+
+	return TRUE;
+}
diff --git a/clients/tui/nmt-connect-connection-list.h b/clients/tui/nmt-connect-connection-list.h
new file mode 100644
index 00000000..d4790043
--- /dev/null
+++ b/clients/tui/nmt-connect-connection-list.h
@@ -0,0 +1,61 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_CONNECT_CONNECTION_LIST_H
+#define NMT_CONNECT_CONNECTION_LIST_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_CONNECT_CONNECTION_LIST            (nmt_connect_connection_list_get_type ())
+#define NMT_CONNECT_CONNECTION_LIST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionList))
+#define NMT_CONNECT_CONNECTION_LIST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListClass))
+#define NMT_IS_CONNECT_CONNECTION_LIST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST))
+#define NMT_IS_CONNECT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_CONNECT_CONNECTION_LIST))
+#define NMT_CONNECT_CONNECTION_LIST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListClass))
+
+typedef struct {
+	NmtNewtListbox parent;
+
+} NmtConnectConnectionList;
+
+typedef struct {
+	NmtNewtListboxClass parent;
+
+} NmtConnectConnectionListClass;
+
+GType nmt_connect_connection_list_get_type (void);
+
+NmtNewtWidget *nmt_connect_connection_list_new (void);
+
+gboolean nmt_connect_connection_list_get_connection (NmtConnectConnectionList  *list,
+                                                     const char                *identifier,
+                                                     NMConnection             **connection,
+                                                     NMDevice                 **device,
+                                                     NMObject                 **specific_object,
+                                                     NMActiveConnection       **active);
+gboolean nmt_connect_connection_list_get_selection  (NmtConnectConnectionList  *list,
+                                                     NMConnection             **connection,
+                                                     NMDevice                 **device,
+                                                     NMObject                 **specific_object,
+                                                     NMActiveConnection       **active);
+
+G_END_DECLS
+
+#endif /* NMT_CONNECT_CONNECTION_LIST_H */
diff --git a/clients/tui/nmt-device-entry.c b/clients/tui/nmt-device-entry.c
new file mode 100644
index 00000000..2af5e584
--- /dev/null
+++ b/clients/tui/nmt-device-entry.c
@@ -0,0 +1,573 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 #NmtEditorGrid 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 <NetworkManager.h>
+
+#include "nmtui.h"
+#include "nmt-device-entry.h"
+
+G_DEFINE_TYPE (NmtDeviceEntry, nmt_device_entry, NMT_TYPE_EDITOR_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;
+	char *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';
+	}
+
+	len = nm_utils_hwaddr_len (priv->arptype);
+	if (   nm_utils_hwaddr_aton (words[0], buf, len)
+	    && (!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], buf, len))) {
+		*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);
+	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);
+	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 = g_strdup (priv->mac_address);
+		mac_device = find_device_by_mac_address (deventry, priv->mac_address);
+	} else {
+		mac = NULL;
+		mac_device = NULL;
+	}
+
+	if (!ifname && mac_device)
+		ifname = nm_device_get_iface (mac_device);
+	if (!mac && ifname_device && (priv->hardware_type != G_TYPE_NONE))
+		g_object_get (G_OBJECT (ifname_device), "hw-address", &mac, NULL);
+
+	if (ifname_device && mac_device && ifname_device != mac_device) {
+		/* Mismatch! */
+		text = g_strdup_printf ("%s != %s", priv->interface_name, mac);
+	} else if (ifname && mac) {
+		if (ifname_device)
+			text = g_strdup_printf ("%s (%s)", ifname, mac);
+		else
+			text = g_strdup_printf ("%s (%s)", mac, ifname);
+	} else if (ifname)
+		text = g_strdup (ifname);
+	else if (mac)
+		text = g_strdup (mac);
+	else
+		text = g_strdup ("");
+
+	priv->updating = TRUE;
+	g_object_set (G_OBJECT (priv->entry), "text", text, NULL);
+	priv->updating = FALSE;
+	g_free (text);
+
+	g_free (mac);
+}
+
+static gboolean
+nmt_device_entry_set_interface_name (NmtDeviceEntry *deventry,
+                                     const char     *interface_name)
+{
+	NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+
+	if (g_strcmp0 (interface_name, priv->interface_name) != 0) {
+		g_free (priv->interface_name);
+		priv->interface_name = g_strdup (interface_name);
+
+		g_object_notify (G_OBJECT (deventry), "interface-name");
+		return TRUE;
+	} else
+		return FALSE;
+}
+
+static gboolean
+nmt_device_entry_set_mac_address (NmtDeviceEntry *deventry,
+                                  const char     *mac_address)
+{
+	NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry);
+	gboolean changed;
+
+	if (mac_address && !priv->mac_address) {
+		priv->mac_address = g_strdup (mac_address);
+		changed = TRUE;
+	} else if (!mac_address && priv->mac_address) {
+		g_clear_pointer (&priv->mac_address, g_free);
+		changed = TRUE;
+	} else if (   mac_address && priv->mac_address
+	           && !nm_utils_hwaddr_matches (mac_address, -1, priv->mac_address, -1)) {
+		g_free (priv->mac_address);
+		priv->mac_address = g_strdup (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;
+
+	nmt_device_entry_set_interface_name (deventry, ifname);
+	g_free (ifname);
+
+	nmt_device_entry_set_mac_address (deventry, mac);
+	g_free (mac);
+}
+
+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_editor_grid_append (NMT_EDITOR_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);
+	g_free (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;
+	const char *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_string (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_string (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_string ("mac-address", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-device-entry.h b/clients/tui/nmt-device-entry.h
new file mode 100644
index 00000000..05d5d40f
--- /dev/null
+++ b/clients/tui/nmt-device-entry.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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-editor-grid.h"
+
+#include <NetworkManager.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 {
+	NmtEditorGrid parent;
+
+} NmtDeviceEntry;
+
+typedef struct {
+	NmtEditorGridClass parent;
+
+} NmtDeviceEntryClass;
+
+GType nmt_device_entry_get_type (void);
+
+NmtNewtWidget *nmt_device_entry_new (const char *label,
+                                     int         width,
+                                     GType       hardware_type);
+
+typedef gboolean (*NmtDeviceEntryDeviceFilter) (NmtDeviceEntry *deventry,
+                                                NMDevice       *device,
+                                                gpointer        user_data);
+void nmt_device_entry_set_device_filter (NmtDeviceEntry             *deventry,
+                                         NmtDeviceEntryDeviceFilter  filter,
+                                         gpointer                    user_data);
+
+G_END_DECLS
+
+#endif /* NMT_DEVICE_ENTRY_H */
diff --git a/clients/tui/nmt-edit-connection-list.c b/clients/tui/nmt-edit-connection-list.c
new file mode 100644
index 00000000..e1b982dd
--- /dev/null
+++ b/clients/tui/nmt-edit-connection-list.c
@@ -0,0 +1,554 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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);
+
+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_changed (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_changed), list);
+		g_object_unref (conn);
+	}
+	g_slist_free (priv->connections);
+	priv->connections = NULL;
+}
+
+static void
+nmt_edit_connection_list_rebuild (NmtEditConnectionList *list)
+{
+	NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+	const GPtrArray *connections;
+	GSList *iter;
+	gboolean did_header = FALSE, did_vpn = FALSE;
+	NMEditorConnectionTypeData **types;
+	NMConnection *conn, *selected_conn;
+	int i, row, selected_row;
+
+	selected_row = nmt_newt_listbox_get_active (priv->listbox);
+	selected_conn = nmt_newt_listbox_get_active_key (priv->listbox);
+
+	free_connections (list);
+	connections = nm_client_get_connections (nm_client);
+	for (i = 0; i < connections->len; i++) {
+		conn = connections->pdata[i];
+
+		if (   priv->connection_filter
+		    && !priv->connection_filter (list, conn, priv->connection_filter_data))
+			continue;
+
+		g_signal_connect (conn, NM_CONNECTION_CHANGED,
+		                  G_CALLBACK (rebuild_on_connection_changed), list);
+		priv->connections = g_slist_prepend (priv->connections, g_object_ref (conn));
+	}
+	priv->connections = g_slist_sort (priv->connections, sort_by_timestamp);
+	g_object_notify (G_OBJECT (list), "connections");
+	g_object_notify (G_OBJECT (list), "num-connections");
+
+	nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->edit),
+	                                  priv->connections != NULL);
+	nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->delete),
+	                                  priv->connections != NULL);
+
+	nmt_newt_listbox_clear (priv->listbox);
+
+	if (!priv->grouped) {
+		/* Just add the connections in order */
+		for (iter = priv->connections, row = 0; iter; iter = iter->next, row++) {
+			conn = iter->data;
+			nmt_newt_listbox_append (priv->listbox, nm_connection_get_id (conn), conn);
+			if (conn == selected_conn)
+				selected_row = row;
+		}
+		if (selected_row >= row)
+			selected_row = row - 1;
+		nmt_newt_listbox_set_active (priv->listbox, selected_row);
+
+		return;
+	}
+
+	types = nm_editor_utils_get_connection_type_list ();
+	for (i = row = 0; types[i]; i++) {
+		if (types[i]->setting_type == NM_TYPE_SETTING_VPN) {
+			if (did_vpn)
+				continue;
+			did_vpn = TRUE;
+		}
+
+		did_header = FALSE;
+
+		for (iter = priv->connections; iter; iter = iter->next) {
+			NMSetting *setting;
+			char *indented;
+
+			conn = iter->data;
+			setting = nm_connection_get_setting (conn, types[i]->setting_type);
+			if (!setting)
+				continue;
+			if (!nm_connection_is_type (conn, nm_setting_get_name (setting)))
+				continue;
+
+			if (!did_header) {
+				nmt_newt_listbox_append (priv->listbox, types[i]->name, NULL);
+				if (row == selected_row)
+					selected_row++;
+				row++;
+				did_header = TRUE;
+			}
+
+			indented = g_strdup_printf ("  %s", nm_connection_get_id (conn));
+			nmt_newt_listbox_append (priv->listbox, indented, conn);
+			g_free (indented);
+
+			if (conn == selected_conn)
+				selected_row = row;
+			row++;
+		}
+	}
+
+	if (selected_row >= row)
+		selected_row = row - 1;
+	nmt_newt_listbox_set_active (priv->listbox, selected_row);
+}
+
+static void
+rebuild_on_connections_changed (GObject    *object,
+                                GParamSpec *pspec,
+                                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_client, "notify::" NM_CLIENT_CONNECTIONS,
+	                  G_CALLBACK (rebuild_on_connections_changed), list);
+
+	nmt_edit_connection_list_rebuild (list);
+
+	G_OBJECT_CLASS (nmt_edit_connection_list_parent_class)->constructed (object);
+}
+
+static void
+add_clicked (NmtNewtButton *button, gpointer list)
+{
+	g_signal_emit (list, signals[ADD_CONNECTION], 0);
+}
+
+static void
+edit_clicked (NmtNewtButton *button, gpointer list)
+{
+	NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+	NMConnection *connection;
+
+	connection = nmt_newt_listbox_get_active_key (priv->listbox);
+	g_return_if_fail (connection != NULL);
+
+	g_signal_emit (list, signals[EDIT_CONNECTION], 0, connection);
+}
+
+static void
+delete_clicked (NmtNewtButton *button, gpointer list)
+{
+	NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+	NMRemoteConnection *connection;
+
+	connection = nmt_newt_listbox_get_active_key (priv->listbox);
+	g_return_if_fail (connection != NULL);
+
+	g_signal_emit (list, signals[REMOVE_CONNECTION], 0, connection);
+}
+
+static void
+listbox_activated (NmtNewtWidget *listbox, gpointer list)
+{
+	NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list);
+
+	edit_clicked (NMT_NEWT_BUTTON (priv->edit), list);
+}
+
+static void
+nmt_edit_connection_list_finalize (GObject *object)
+{
+	NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object);
+
+	free_connections (NMT_EDIT_CONNECTION_LIST (object));
+	g_clear_object (&priv->extra);
+
+	G_OBJECT_CLASS (nmt_edit_connection_list_parent_class)->finalize (object);
+}
+
+static void
+nmt_edit_connection_list_set_property (GObject      *object,
+                                       guint         prop_id,
+                                       const GValue *value,
+                                       GParamSpec   *pspec)
+{
+	NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_GROUPED:
+		priv->grouped = g_value_get_boolean (value);
+		break;
+	case PROP_CONNECTION_FILTER:
+		priv->connection_filter = g_value_get_pointer (value);
+		break;
+	case PROP_CONNECTION_FILTER_DATA:
+		priv->connection_filter_data = g_value_get_pointer (value);
+		break;
+	case PROP_EXTRA_WIDGET:
+		priv->extra = g_value_get_object (value);
+		if (priv->extra)
+			g_object_ref_sink (priv->extra);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_edit_connection_list_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+	NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object);
+	GPtrArray *connections;
+	GSList *iter;
+
+	switch (prop_id) {
+	case PROP_GROUPED:
+		g_value_set_boolean (value, priv->grouped);
+		break;
+	case PROP_CONNECTION_FILTER:
+		g_value_set_pointer (value, priv->connection_filter);
+		break;
+	case PROP_CONNECTION_FILTER_DATA:
+		g_value_set_pointer (value, priv->connection_filter_data);
+		break;
+	case PROP_EXTRA_WIDGET:
+		g_value_set_object (value, priv->extra);
+		break;
+	case PROP_CONNECTIONS:
+		connections = g_ptr_array_new_with_free_func (g_object_unref);
+		for (iter = priv->connections; iter; iter = iter->next)
+			g_ptr_array_add (connections, g_object_ref (iter->data));
+		g_value_take_boxed (value, connections);
+		break;
+	case PROP_NUM_CONNECTIONS:
+		g_value_set_int (value, g_slist_length (priv->connections));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_edit_connection_list_class_init (NmtEditConnectionListClass *list_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+
+	g_type_class_add_private (list_class, sizeof (NmtEditConnectionListPrivate));
+
+	/* virtual methods */
+	object_class->constructed  = nmt_edit_connection_list_constructed;
+	object_class->set_property = nmt_edit_connection_list_set_property;
+	object_class->get_property = nmt_edit_connection_list_get_property;
+	object_class->finalize     = nmt_edit_connection_list_finalize;
+
+	/* signals */
+
+	/**
+	 * NmtEditConnectionList::add-connection:
+	 * @list: the #NmtEditConnectionList
+	 *
+	 * Emitted when the user clicks the list's "Add" button.
+	 */
+	signals[ADD_CONNECTION] =
+		g_signal_new ("add-connection",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NmtEditConnectionListClass, add_connection),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 0);
+
+	/**
+	 * NmtEditConnectionList::edit-connection:
+	 * @list: the #NmtEditConnectionList
+	 * @connection: the connection to edit
+	 *
+	 * Emitted when the user clicks the list's "Edit" button, or
+	 * hits "Return" on the listbox.
+	 */
+	signals[EDIT_CONNECTION] =
+		g_signal_new ("edit-connection",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NmtEditConnectionListClass, edit_connection),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1,
+		              NM_TYPE_CONNECTION);
+
+	/**
+	 * NmtEditConnectionList::remove-connection:
+	 * @list: the #NmtEditConnectionList
+	 * @connection: the connection to remove
+	 *
+	 * Emitted when the user clicks the list's "Delete" button.
+	 */
+	signals[REMOVE_CONNECTION] =
+		g_signal_new ("remove-connection",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NmtEditConnectionListClass, remove_connection),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1,
+		              NM_TYPE_CONNECTION);
+
+	/* properties */
+
+	/**
+	 * NmtEditConnectionList:grouped:
+	 *
+	 * If %TRUE, connections should be grouped by type, with headers
+	 * indicating the types (as in the main connection list). If %FALSE,
+	 * they will not be grouped (as in slave connection lists).
+	 */
+	g_object_class_install_property
+		(object_class, PROP_GROUPED,
+		 g_param_spec_boolean ("grouped", "", "",
+		                       TRUE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
+	
+	/**
+	 * NmtEditConnectionListFilter:
+	 * @list: the #NmtEditConnectionList
+	 * @connection: an #NMConnection
+	 * @user_data: the user data
+	 *
+	 * Decides whether @connection should be displayed in @list.
+	 *
+	 * Returns: %TRUE or %FALSE
+	 */
+	/**
+	 * NmtEditConnectionList:connection-filter:
+	 *
+	 * A callback function for filtering which connections appear in
+	 * the list.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_CONNECTION_FILTER,
+		 g_param_spec_pointer ("connection-filter", "", "",
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtEditConnectionList:connection-filter-data:
+	 *
+	 * Data for the #NmtEditConnectionList:connection-filter.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_CONNECTION_FILTER_DATA,
+		 g_param_spec_pointer ("connection-filter-data", "", "",
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NmtEditConnectionList:extra-widget:
+	 *
+	 * An extra button widget to display at the bottom of the button
+	 * box.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_EXTRA_WIDGET,
+		 g_param_spec_object ("extra-widget", "", "",
+		                      NMT_TYPE_NEWT_WIDGET,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NmtEditConnectionList:connections:
+	 *
+	 * The list of connections in the widget.
+	 *
+	 * Element-Type: #NMConnection
+	 */
+	g_object_class_install_property
+		(object_class, PROP_CONNECTIONS,
+		 g_param_spec_boxed ("connections", "", "",
+		                     G_TYPE_PTR_ARRAY,
+		                     G_PARAM_READABLE |
+		                     G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NmtEditConnectionList:num-connections:
+	 *
+	 * The number of connections in the widget.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_NUM_CONNECTIONS,
+		 g_param_spec_int ("num-connections", "", "",
+		                   0, G_MAXINT, 0,
+		                   G_PARAM_READABLE |
+		                   G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-edit-connection-list.h b/clients/tui/nmt-edit-connection-list.h
new file mode 100644
index 00000000..c31c0a8c
--- /dev/null
+++ b/clients/tui/nmt-edit-connection-list.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_EDIT_CONNECTION_LIST_H
+#define NMT_EDIT_CONNECTION_LIST_H
+
+#include "nmt-newt.h"
+
+#include <NetworkManager.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);
+
+G_END_DECLS
+
+#endif /* NMT_EDIT_CONNECTION_LIST_H */
diff --git a/clients/tui/nmt-editor-grid.c b/clients/tui/nmt-editor-grid.c
new file mode 100644
index 00000000..118bf01d
--- /dev/null
+++ b/clients/tui/nmt-editor-grid.c
@@ -0,0 +1,463 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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-grid
+ * @short_description: Grid widget for #NmtEditorPages
+ *
+ * #NmtEditorGrid 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 #NmtEditorGrids 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. #NmtEditorGrid 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-editor-grid.h"
+
+G_DEFINE_TYPE (NmtEditorGrid, nmt_editor_grid, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_EDITOR_GRID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR_GRID, NmtEditorGridPrivate))
+
+typedef struct {
+	GArray *rows;
+	int *row_heights;
+	int indent;
+} NmtEditorGridPrivate;
+
+typedef struct {
+	NmtNewtWidget *label;
+	NmtNewtWidget *widget;
+	NmtNewtWidget *extra;
+	NmtEditorGridRowFlags flags;
+} NmtEditorGridRow;
+
+typedef struct {
+	int col_widths[3];
+} NmtEditorGridFormState;
+
+/**
+ * nmt_editor_grid_new:
+ *
+ * Creates a new #NmtEditorGrid
+ *
+ * Returns: a new #NmtEditorGrid
+ */ 
+NmtNewtWidget *
+nmt_editor_grid_new (void)
+{
+	return g_object_new (NMT_TYPE_EDITOR_GRID,
+	                     NULL);
+}
+
+static void
+nmt_editor_grid_init (NmtEditorGrid *grid)
+{
+	NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (grid);
+
+	priv->rows = g_array_new (FALSE, TRUE, sizeof (NmtEditorGridRow));
+}
+
+static void
+nmt_editor_grid_finalize (GObject *object)
+{
+	NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (object);
+
+	g_array_unref (priv->rows);
+	g_clear_pointer (&priv->row_heights, g_free);
+
+	G_OBJECT_CLASS (nmt_editor_grid_parent_class)->finalize (object);
+}
+
+/**
+ * nmt_editor_grid_append:
+ * @grid: the #NmtEditorGrid
+ * @label: (allow-none): the label text for @widget, or %NULL
+ * @widget: (allow-none): the (main) widget
+ * @extra: (allow-none): optional extra widget
+ *
+ * Adds a row to @grid.
+ *
+ * If @label and @widget are both 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 either @label or @widget is %NULL, then the other column will expand into
+ * it.
+ *
+ * See also nmt_editor_grid_set_row_flags().
+ */
+void
+nmt_editor_grid_append (NmtEditorGrid   *grid,
+                      const char    *label,
+                      NmtNewtWidget *widget,
+                      NmtNewtWidget *extra)
+{
+	NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (grid);
+	NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_editor_grid_parent_class);
+	NmtNewtContainer *container = NMT_NEWT_CONTAINER (grid);
+	NmtEditorGridRow row;
+
+	g_return_if_fail (label != NULL || widget != NULL);
+
+	memset (&row, 0, sizeof (row));
+
+	if (label && !widget) {
+		widget = nmt_newt_label_new (label);
+		label = NULL;
+	}
+
+	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_editor_grid_find_widget (NmtEditorGrid   *grid,
+                           NmtNewtWidget *widget)
+{
+	NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (grid);
+	NmtEditorGridRow *rows = (NmtEditorGridRow *) 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;
+}
+
+/**
+ * NmtEditorGridRowFlags:
+ * @NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT: the row's label should be
+ *   aligned left instead of right.
+ * @NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT: the row's extra widget
+ *   should be aligned right instead of left.
+ *
+ * Flags to alter an #NmtEditorGrid row's layout.
+ */
+
+/**
+ * nmt_editor_grid_set_row_flags:
+ * @grid: an #NmtEditorGrid
+ * @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_editor_grid_set_row_flags (NmtEditorGrid         *grid,
+                             NmtNewtWidget       *widget,
+                             NmtEditorGridRowFlags  flags)
+{
+	NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (grid);
+	NmtEditorGridRow *rows = (NmtEditorGridRow *) priv->rows->data;
+	int i;
+
+	i = nmt_editor_grid_find_widget (grid, widget);
+	if (i != -1)
+		rows[i].flags = flags;
+}
+
+static void
+nmt_editor_grid_remove (NmtNewtContainer *container,
+                      NmtNewtWidget    *widget)
+{
+	NmtEditorGrid *grid = NMT_EDITOR_GRID (container);
+	NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (grid);
+	NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_editor_grid_parent_class);
+	NmtEditorGridRow *rows = (NmtEditorGridRow *) priv->rows->data;
+	int i;
+
+	i = nmt_editor_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_editor_grid_get_components (NmtNewtWidget *widget)
+{
+	NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (widget);
+	NmtEditorGridRow *rows = (NmtEditorGridRow *) 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 NmtEditorGridFormState *
+get_form_state (NmtNewtWidget *widget)
+{
+	NmtNewtForm *form = nmt_newt_widget_get_form (widget);
+	NmtEditorGridFormState *state;
+
+	if (!form)
+		return NULL;
+
+	state = g_object_get_data (G_OBJECT (form), "NmtEditorGridFormState");
+	if (state)
+		return state;
+
+	state = g_new0 (NmtEditorGridFormState, 1);
+	g_object_set_data_full (G_OBJECT (form), "NmtEditorGridFormState", state, g_free);
+	return state;
+}
+
+static void
+nmt_editor_grid_realize (NmtNewtWidget *widget)
+{
+	NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (widget);
+	NmtNewtWidget *parent;
+
+	NMT_NEWT_WIDGET_CLASS (nmt_editor_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_editor_grid_unrealize (NmtNewtWidget *widget)
+{
+	NmtEditorGridFormState *state = get_form_state (widget);
+
+	if (state)
+		memset (state->col_widths, 0, sizeof (state->col_widths));
+
+	NMT_NEWT_WIDGET_CLASS (nmt_editor_grid_parent_class)->unrealize (widget);
+}
+
+static void
+nmt_editor_grid_size_request (NmtNewtWidget *widget,
+                            int           *width,
+                            int           *height)
+{
+	NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (widget);
+	NmtEditorGridRow *rows = (NmtEditorGridRow *) priv->rows->data;
+	NmtEditorGridFormState *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_editor_grid_size_allocate (NmtNewtWidget *widget,
+                             int            x,
+                             int            y,
+                             int            width,
+                             int            height)
+{
+	NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE (widget);
+	NmtEditorGridRow *rows = (NmtEditorGridRow *) priv->rows->data;
+	NmtEditorGridFormState *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_EDITOR_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]);
+		} else {
+			nmt_newt_widget_size_allocate (rows[i].widget,
+			                               x,
+			                               y + row,
+			                               col0_width + col1_width + 1,
+			                               priv->row_heights[i]);
+		}
+
+		if (rows[i].extra) {
+			int wwidth, wheight, ex;
+
+			if (rows[i].flags & NMT_EDITOR_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]);
+		}
+
+		row += priv->row_heights[i];
+	}
+}
+
+static void
+nmt_editor_grid_class_init (NmtEditorGridClass *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 (NmtEditorGridPrivate));
+
+	/* virtual methods */
+	object_class->finalize = nmt_editor_grid_finalize;
+
+	widget_class->realize        = nmt_editor_grid_realize;
+	widget_class->unrealize      = nmt_editor_grid_unrealize;
+	widget_class->get_components = nmt_editor_grid_get_components;
+	widget_class->size_request   = nmt_editor_grid_size_request;
+	widget_class->size_allocate  = nmt_editor_grid_size_allocate;
+
+	container_class->remove = nmt_editor_grid_remove;
+}
diff --git a/clients/tui/nmt-editor-grid.h b/clients/tui/nmt-editor-grid.h
new file mode 100644
index 00000000..2ae01d71
--- /dev/null
+++ b/clients/tui/nmt-editor-grid.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_EDITOR_GRID_H
+#define NMT_EDITOR_GRID_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_EDITOR_GRID            (nmt_editor_grid_get_type ())
+#define NMT_EDITOR_GRID(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_GRID, NmtEditorGrid))
+#define NMT_EDITOR_GRID_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_GRID, NmtEditorGridClass))
+#define NMT_IS_EDITOR_GRID(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR_GRID))
+#define NMT_IS_EDITOR_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR_GRID))
+#define NMT_EDITOR_GRID_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR_GRID, NmtEditorGridClass))
+
+typedef struct {
+	NmtNewtContainer parent;
+
+} NmtEditorGrid;
+
+typedef struct {
+	NmtNewtContainerClass parent;
+
+} NmtEditorGridClass;
+
+GType nmt_editor_grid_get_type (void);
+
+typedef enum {
+	NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT = (1 << 0),
+	NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT = (1 << 1)
+} NmtEditorGridRowFlags;
+
+NmtNewtWidget *nmt_editor_grid_new              (void);
+
+void           nmt_editor_grid_append           (NmtEditorGrid         *grid,
+                                               const char          *label,
+                                               NmtNewtWidget       *widget,
+                                               NmtNewtWidget       *extra);
+void           nmt_editor_grid_set_row_flags    (NmtEditorGrid         *grid,
+                                               NmtNewtWidget       *widget,
+                                               NmtEditorGridRowFlags  flags);
+
+G_END_DECLS
+
+#endif /* NMT_EDITOR_GRID_H */
diff --git a/clients/tui/nmt-editor-page-device.c b/clients/tui/nmt-editor-page-device.c
new file mode 100644
index 00000000..0a9b18e0
--- /dev/null
+++ b/clients/tui/nmt-editor-page-device.c
@@ -0,0 +1,131 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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-device
+ * @short_description: Abstract base class for "device" editor pages
+ *
+ * #NmtEditorPageDevice 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-editor-page-device.h"
+
+G_DEFINE_TYPE (NmtEditorPageDevice, nmt_editor_page_device, NMT_TYPE_EDITOR_PAGE)
+
+#define NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDevicePrivate))
+
+typedef struct {
+	NmtDeviceEntry *device_entry;
+	gboolean show_by_default;
+} NmtEditorPageDevicePrivate;
+
+enum {
+	PROP_0,
+
+	PROP_DEVICE_ENTRY,
+
+	LAST_PROP
+};
+
+static void
+nmt_editor_page_device_init (NmtEditorPageDevice *device)
+{
+}
+
+static void
+nmt_editor_page_device_finalize (GObject *object)
+{
+	NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE (object);
+
+	g_clear_object (&priv->device_entry);
+
+	G_OBJECT_CLASS (nmt_editor_page_device_parent_class)->finalize (object);
+}
+
+NmtDeviceEntry *
+nmt_editor_page_device_get_device_entry (NmtEditorPageDevice *page)
+{
+	NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE (page);
+
+	return priv->device_entry;
+}
+
+static void
+nmt_editor_page_device_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+	NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_DEVICE_ENTRY:
+		priv->device_entry = g_value_dup_object (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_editor_page_device_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+	NmtEditorPageDevicePrivate *priv = NMT_EDITOR_PAGE_DEVICE_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_DEVICE_ENTRY:
+		g_value_set_object (value, priv->device_entry);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_editor_page_device_class_init (NmtEditorPageDeviceClass *page_device_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (page_device_class);
+
+	g_type_class_add_private (page_device_class, sizeof (NmtEditorPageDevicePrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_editor_page_device_set_property;
+	object_class->get_property = nmt_editor_page_device_get_property;
+	object_class->finalize     = nmt_editor_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));
+}
diff --git a/clients/tui/nmt-editor-page-device.h b/clients/tui/nmt-editor-page-device.h
new file mode 100644
index 00000000..f911602c
--- /dev/null
+++ b/clients/tui/nmt-editor-page-device.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_EDITOR_PAGE_DEVICE_H
+#define NMT_EDITOR_PAGE_DEVICE_H
+
+#include "nmt-editor-page.h"
+#include "nmt-device-entry.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_EDITOR_PAGE_DEVICE            (nmt_editor_page_device_get_type ())
+#define NMT_EDITOR_PAGE_DEVICE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDevice))
+#define NMT_EDITOR_PAGE_DEVICE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDeviceClass))
+#define NMT_IS_EDITOR_PAGE_DEVICE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR_PAGE_DEVICE))
+#define NMT_IS_EDITOR_PAGE_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR_PAGE_DEVICE))
+#define NMT_EDITOR_PAGE_DEVICE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR_PAGE_DEVICE, NmtEditorPageDeviceClass))
+
+typedef struct {
+	NmtEditorPage parent;
+
+} NmtEditorPageDevice;
+
+typedef struct {
+	NmtEditorPageClass parent;
+
+} NmtEditorPageDeviceClass;
+
+GType nmt_editor_page_device_get_type (void);
+
+NmtDeviceEntry *nmt_editor_page_device_get_device_entry (NmtEditorPageDevice *page);
+
+G_END_DECLS
+
+#endif /* NMT_EDITOR_PAGE_DEVICE_H */
diff --git a/clients/tui/nmt-editor-page.c b/clients/tui/nmt-editor-page.c
new file mode 100644
index 00000000..a46d0ed4
--- /dev/null
+++ b/clients/tui/nmt-editor-page.c
@@ -0,0 +1,179 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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".
+ * A "page" is a set of related #NmtEditorSections.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-editor-page.h"
+
+G_DEFINE_ABSTRACT_TYPE (NmtEditorPage, nmt_editor_page, G_TYPE_OBJECT)
+
+#define NMT_EDITOR_PAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR_PAGE, NmtEditorPagePrivate))
+
+typedef struct {
+	NMConnection *connection;
+	GSList *sections;
+
+} NmtEditorPagePrivate;
+
+enum {
+	PROP_0,
+
+	PROP_CONNECTION,
+
+	LAST_PROP
+};
+
+static void
+nmt_editor_page_init (NmtEditorPage *page)
+{
+}
+
+static void
+nmt_editor_page_finalize (GObject *object)
+{
+	NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (object);
+
+	g_clear_object (&priv->connection);
+	g_slist_free_full (priv->sections, g_object_unref);
+
+	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_get_sections:
+ * @page: the #NmtEditorPage
+ *
+ * Gets the page's list of sections to display.
+ *
+ * Returns: (transfer none): the list of sections; this is the internal list
+ * used by the page and must not be modified or freed.
+ */
+GSList *
+nmt_editor_page_get_sections (NmtEditorPage *page)
+{
+	NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page);
+
+	return priv->sections;
+}
+
+/**
+ * nmt_editor_page_add_section:
+ * @page: the #NmtEditorPage
+ * @section: the #NmtEditorSection
+ *
+ * Adds a section to the page. This should only be called by #NmtEditorPage
+ * subclasses.
+ */
+void
+nmt_editor_page_add_section (NmtEditorPage *page,
+                             NmtEditorSection *section)
+{
+	NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page);
+
+	priv->sections = g_slist_append (priv->sections, g_object_ref_sink (section));
+}
+
+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;
+	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;
+	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));
+}
diff --git a/clients/tui/nmt-editor-page.h b/clients/tui/nmt-editor-page.h
new file mode 100644
index 00000000..bbc0d9e3
--- /dev/null
+++ b/clients/tui/nmt-editor-page.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013-2014 Red Hat, Inc.
+ */
+
+#ifndef NMT_EDITOR_PAGE_H
+#define NMT_EDITOR_PAGE_H
+
+#include <NetworkManager.h>
+
+#include "nmt-editor-grid.h"
+#include "nmt-editor-section.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 {
+	GObject parent;
+
+} NmtEditorPage;
+
+typedef struct {
+	GObjectClass parent;
+
+} NmtEditorPageClass;
+
+GType nmt_editor_page_get_type (void);
+
+NMConnection  *nmt_editor_page_get_connection    (NmtEditorPage *page);
+
+GSList        *nmt_editor_page_get_sections      (NmtEditorPage *page);
+
+/*< protected >*/
+void           nmt_editor_page_add_section       (NmtEditorPage *page,
+                                                  NmtEditorSection *section);
+
+G_END_DECLS
+
+#endif /* NMT_EDITOR_PAGE_H */
diff --git a/clients/tui/nmt-editor-section.c b/clients/tui/nmt-editor-section.c
new file mode 100644
index 00000000..ff463f7f
--- /dev/null
+++ b/clients/tui/nmt-editor-section.c
@@ -0,0 +1,282 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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-section:
+ * @short_description: A section of the #NmtEditor
+ *
+ * #NmtEditorSection is the abstract base class for #NmtEditor sections.
+ */
+
+#include "config.h"
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-editor-section.h"
+#include "nmt-newt-toggle-button.h"
+
+G_DEFINE_TYPE (NmtEditorSection, nmt_editor_section, NMT_TYPE_NEWT_SECTION)
+
+#define NMT_EDITOR_SECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR_SECTION, NmtEditorSectionPrivate))
+
+typedef struct {
+	NmtEditorGrid *header, *body;
+	char *title;
+	NmtNewtWidget *header_widget;
+	NmtNewtWidget *toggle;
+
+	gboolean show_by_default;
+} NmtEditorSectionPrivate;
+
+enum {
+	PROP_0,
+
+	PROP_TITLE,
+	PROP_SHOW_BY_DEFAULT,
+	PROP_HEADER_WIDGET,
+
+	LAST_PROP
+};
+
+/**
+ * nmt_editor_section_new:
+ * @title: the section title
+ * @header_widget: (allow-none): the widget to show next to the title
+ * @show_by_default: whether the section should be open by default
+ *
+ * Creates a new #NmtEditorSection.
+ *
+ * Returns: a new #NmtEditorSection
+ */
+NmtEditorSection *
+nmt_editor_section_new (const char *title,
+                        NmtNewtWidget *header_widget,
+                        gboolean show_by_default)
+{
+	return g_object_new (NMT_TYPE_EDITOR_SECTION,
+	                     "title", title,
+	                     "header-widget", header_widget,
+	                     "show-by-default", show_by_default,
+	                     NULL);
+}
+
+static void
+rebuild_header (NmtEditorSection *section)
+{
+	NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section);
+
+	/* Removing any widget in an NmtEditorGrid removes its whole row, so we can
+	 * remove the existing title/widget/toggle by asking to remove toggle.
+	 */
+	nmt_newt_container_remove (NMT_NEWT_CONTAINER (priv->header), priv->toggle);
+
+	nmt_editor_grid_append (priv->header,
+	                        priv->title,
+	                        priv->header_widget,
+	                        priv->toggle);
+	nmt_editor_grid_set_row_flags (priv->header,
+	                               priv->toggle,
+	                               NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT |
+	                               NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT);
+}
+
+static void
+nmt_editor_section_init (NmtEditorSection *section)
+{
+	NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section);
+
+	priv->header = NMT_EDITOR_GRID (nmt_editor_grid_new ());
+	priv->body = NMT_EDITOR_GRID (nmt_editor_grid_new ());
+	priv->toggle = nmt_newt_toggle_button_new (_("Hide"), _("Show"));
+	g_object_ref_sink (priv->toggle);
+
+	nmt_newt_section_set_header (NMT_NEWT_SECTION (section), NMT_NEWT_WIDGET (priv->header));
+	nmt_newt_section_set_body (NMT_NEWT_SECTION (section), NMT_NEWT_WIDGET (priv->body));
+
+	g_object_bind_property (priv->toggle, "active",
+	                        section, "open",
+	                        G_BINDING_SYNC_CREATE);
+}
+
+static void
+nmt_editor_section_finalize (GObject *object)
+{
+	NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (object);
+
+	g_free (priv->title);
+	g_clear_object (&priv->header_widget);
+	g_clear_object (&priv->toggle);
+
+	G_OBJECT_CLASS (nmt_editor_section_parent_class)->finalize (object);
+}
+
+/**
+ * nmt_editor_section_get_header_widget:
+ * @section: the #NmtEditorSection
+ *
+ * Gets the section's header widget.
+ *
+ * Returns: the section's header widget.
+ */
+NmtNewtWidget *
+nmt_editor_section_get_header_widget (NmtEditorSection *section)
+{
+	NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section);
+
+	return priv->header_widget;
+}
+
+/**
+ * nmt_editor_section_get_body:
+ * @section: the #NmtEditorSection
+ *
+ * Gets the section's body grid, so that you can add things to it.
+ *
+ * Returns: the #NmtEditorGrid used for the section body
+ */
+NmtEditorGrid *
+nmt_editor_section_get_body (NmtEditorSection *section)
+{
+	NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section);
+
+	return priv->body;
+}
+
+/**
+ * nmt_editor_section_get_title:
+ * @section: the #NmtEditorSection
+ *
+ * Gets the section's title.
+ *
+ * Returns: the section's title
+ */
+const char *
+nmt_editor_section_get_title (NmtEditorSection *section)
+{
+	NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section);
+
+	return priv->title;
+}
+
+static void
+nmt_editor_section_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+	NmtEditorSection *section = NMT_EDITOR_SECTION (object);
+	NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (section);
+
+	switch (prop_id) {
+	case PROP_TITLE:
+		priv->title = g_value_dup_string (value);
+		rebuild_header (section);
+		break;
+	case PROP_SHOW_BY_DEFAULT:
+		priv->show_by_default = g_value_get_boolean (value);
+		nmt_newt_toggle_button_set_active (NMT_NEWT_TOGGLE_BUTTON (priv->toggle),
+		                                   priv->show_by_default);
+		break;
+	case PROP_HEADER_WIDGET:
+		priv->header_widget = g_value_get_object (value);
+		if (priv->header_widget)
+			g_object_ref_sink (priv->header_widget);
+		rebuild_header (section);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_editor_section_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+	NmtEditorSectionPrivate *priv = NMT_EDITOR_SECTION_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_TITLE:
+		g_value_set_string (value, priv->title);
+		break;
+	case PROP_SHOW_BY_DEFAULT:
+		g_value_set_boolean (value, priv->show_by_default);
+		break;
+	case PROP_HEADER_WIDGET:
+		g_value_set_object (value, priv->header_widget);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_editor_section_class_init (NmtEditorSectionClass *section_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (section_class);
+
+	g_type_class_add_private (section_class, sizeof (NmtEditorSectionPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_editor_section_set_property;
+	object_class->get_property = nmt_editor_section_get_property;
+	object_class->finalize     = nmt_editor_section_finalize;
+
+	/* properties */
+
+	/**
+	 * NmtEditorSection:title:
+	 *
+	 * The section'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));
+
+	/**
+	 * NmtEditorSection:show-by-default:
+	 *
+	 * Whether the section should be expanded by default.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_SHOW_BY_DEFAULT,
+		 g_param_spec_boolean ("show-by-default", "", "",
+		                       TRUE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+
+	/**
+	 * NmtEditorSection:header-widget:
+	 *
+	 * The widget (if any) that appears between the section title and its toggle
+	 * button.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_HEADER_WIDGET,
+		 g_param_spec_object ("header-widget", "", "",
+		                      NMT_TYPE_NEWT_WIDGET,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-editor-section.h b/clients/tui/nmt-editor-section.h
new file mode 100644
index 00000000..67410c71
--- /dev/null
+++ b/clients/tui/nmt-editor-section.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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_SECTION_H
+#define NMT_EDITOR_SECTION_H
+
+#include "nmt-newt-section.h"
+#include "nmt-editor-grid.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_EDITOR_SECTION            (nmt_editor_section_get_type ())
+#define NMT_EDITOR_SECTION(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_SECTION, NmtEditorSection))
+#define NMT_EDITOR_SECTION_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_SECTION, NmtEditorSectionClass))
+#define NMT_IS_EDITOR_SECTION(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR_SECTION))
+#define NMT_IS_EDITOR_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR_SECTION))
+#define NMT_EDITOR_SECTION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR_SECTION, NmtEditorSectionClass))
+
+typedef struct {
+	NmtNewtSection parent;
+
+} NmtEditorSection;
+
+typedef struct {
+	NmtNewtSectionClass parent;
+
+} NmtEditorSectionClass;
+
+GType nmt_editor_section_get_type (void);
+
+NmtEditorSection *nmt_editor_section_new               (const char *title,
+                                                        NmtNewtWidget *header_widget,
+                                                        gboolean show_by_default);
+
+const char       *nmt_editor_section_get_title         (NmtEditorSection *section);
+NmtNewtWidget    *nmt_editor_section_get_header_widget (NmtEditorSection *section);
+NmtEditorGrid    *nmt_editor_section_get_body          (NmtEditorSection *section);
+
+G_END_DECLS
+
+#endif /* NMT_EDITOR_SECTION_H */
diff --git a/clients/tui/nmt-editor.c b/clients/tui/nmt-editor.c
new file mode 100644
index 00000000..11fe758d
--- /dev/null
+++ b/clients/tui/nmt-editor.c
@@ -0,0 +1,520 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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-utils.h"
+
+#include "nmt-device-entry.h"
+#include "nmt-mac-entry.h"
+#include "nmt-mtu-entry.h"
+
+#include "nmt-page-bond.h"
+#include "nmt-page-bridge.h"
+#include "nmt-page-bridge-port.h"
+#include "nmt-page-dsl.h"
+#include "nmt-page-ethernet.h"
+#include "nmt-page-infiniband.h"
+#include "nmt-page-ip4.h"
+#include "nmt-page-ip6.h"
+#include "nmt-page-ppp.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 (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;
+
+	GSList *pages;
+	NmtNewtWidget *ok, *cancel;
+	gboolean running;
+} NmtEditorPrivate;
+
+enum {
+	PROP_0,
+	PROP_CONNECTION,
+	PROP_TYPE_DATA,
+
+	LAST_PROP
+};
+
+/**
+ * nmt_editor_new:
+ * @connection: the #NMConnection to edit
+ *
+ * Creates a new #NmtEditor to edit @connection.
+ *
+ * Returns: a new #NmtEditor
+ */
+NmtNewtForm *
+nmt_editor_new (NMConnection *connection)
+{
+	NMEditorConnectionTypeData *type_data;
+
+	type_data = nm_editor_utils_get_connection_type_data (connection);
+	if (!type_data) {
+		NMSettingConnection *s_con;
+
+		s_con = nm_connection_get_setting_connection (connection);
+		if (s_con) {
+			nmt_newt_message_dialog (_("Could not create editor for connection '%s' of type '%s'."),
+			                         nm_connection_get_id (connection),
+			                         nm_setting_connection_get_connection_type (s_con));
+		} else {
+			nmt_newt_message_dialog (_("Could not create editor for invalid connection '%s'."),
+			                         nm_connection_get_id (connection));
+		}
+
+		return NULL;
+	}
+
+	return g_object_new (NMT_TYPE_EDITOR,
+	                     "connection", connection,
+	                     "type-data", type_data,
+	                     "title", _("Edit Connection"),
+	                     "fullscreen-vertical", TRUE,
+	                     NULL);
+}
+
+static void
+nmt_editor_init (NmtEditor *entry)
+{
+}
+
+static void
+connection_updated (GObject      *connection,
+                    GAsyncResult *result,
+                    gpointer      op)
+{
+	GError *error = NULL;
+
+	nm_remote_connection_commit_changes_finish (NM_REMOTE_CONNECTION (connection), result, &error);
+	nmt_sync_op_complete_boolean (op, error == NULL, error);
+	g_clear_error (&error);
+}
+
+static void
+connection_added (GObject      *client,
+                  GAsyncResult *result,
+                  gpointer      op)
+{
+	NMRemoteConnection *connection;
+	GError *error = NULL;
+
+	connection = nm_client_add_connection_finish (NM_CLIENT (client), result, &error);
+	nmt_sync_op_complete_boolean (op, error == NULL, error);
+	g_clear_object (&connection);
+	g_clear_error (&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;
+
+	nm_connection_replace_settings_from_connection (priv->orig_connection,
+	                                                priv->edit_connection);
+
+	nmt_sync_op_init (&op);
+	if (NM_IS_REMOTE_CONNECTION (priv->orig_connection)) {
+		nm_remote_connection_commit_changes_async (NM_REMOTE_CONNECTION (priv->orig_connection),
+		                                           TRUE, NULL, connection_updated, &op);
+		if (!nmt_sync_op_wait_boolean (&op, &error)) {
+			nmt_newt_message_dialog (_("Unable to save connection: %s"),
+			                         error->message);
+			g_error_free (error);
+			return;
+		}
+
+		/* Clear secrets so they don't lay around in memory; they'll get
+		 * requested again anyway next time the connection is edited.
+		 */
+		nm_connection_clear_secrets (priv->orig_connection);
+	} else {
+		nm_client_add_connection_async (nm_client, priv->orig_connection, TRUE,
+		                                NULL, connection_added, &op);
+		if (!nmt_sync_op_wait_boolean (&op, &error)) {
+			nmt_newt_message_dialog (_("Unable to add new connection: %s"),
+			                         error->message);
+			g_error_free (error);
+			return;
+		}
+	}
+
+	nmt_newt_form_quit (NMT_NEWT_FORM (editor));
+}
+
+static void
+got_secrets (GObject      *object,
+             GAsyncResult *result,
+             gpointer      op)
+{
+	GVariant *secrets;
+	GError *error = NULL;
+
+	secrets = nm_remote_connection_get_secrets_finish (NM_REMOTE_CONNECTION (object),
+	                                                   result, &error);
+	if (secrets)
+		g_variant_ref (secrets);
+	nmt_sync_op_complete_pointer (op, secrets, error);
+	g_clear_error (&error);
+}
+
+static NMConnection *
+build_edit_connection (NMConnection *orig_connection)
+{
+	NMConnection *edit_connection;
+	GVariant *settings, *secrets;
+	GVariantIter iter;
+	const char *setting_name;
+	NmtSyncOp op;
+
+	edit_connection = nm_simple_connection_new_clone (orig_connection);
+
+	if (!NM_IS_REMOTE_CONNECTION (orig_connection))
+		return edit_connection;
+
+	settings = nm_connection_to_dbus (orig_connection, NM_CONNECTION_SERIALIZE_NO_SECRETS);
+	g_variant_iter_init (&iter, settings);
+	while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, NULL)) {
+		nmt_sync_op_init (&op);
+		nm_remote_connection_get_secrets_async (NM_REMOTE_CONNECTION (orig_connection),
+		                                        setting_name, NULL, got_secrets, &op);
+		/* FIXME: error handling */
+		secrets = nmt_sync_op_wait_pointer (&op, NULL);
+		if (secrets) {
+			(void) nm_connection_update_secrets (edit_connection, setting_name, secrets, NULL);
+			g_variant_unref (secrets);
+		}
+	}
+	g_variant_unref (settings);
+
+	return edit_connection;
+}
+
+static gboolean
+permissions_transform_to_allusers (GBinding     *binding,
+                                   const GValue *source_value,
+                                   GValue       *target_value,
+                                   gpointer      user_data)
+{
+	char **perms = g_value_get_boxed (source_value);
+
+	g_value_set_boolean (target_value, g_strv_length (perms) == 0);
+	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);
+	char **perms = NULL;
+
+	if (allusers) {
+		perms = g_new (char *, 2);
+
+		perms[0] = g_strdup_printf ("user:%s:", g_get_user_name ());
+		perms[1] = NULL;
+	}
+	g_value_take_boxed (target_value, perms);
+	return TRUE;
+}
+
+static NmtNewtWidget *
+add_sections_for_page (NmtEditor *editor, NmtEditorGrid *grid, NmtEditorPage *page)
+{
+	NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (editor);
+	NmtNewtWidget *first_section = NULL;
+	const GSList *sections, *iter;
+
+	g_return_val_if_fail (NMT_IS_EDITOR_PAGE (page), NULL);
+
+	priv->pages = g_slist_prepend (priv->pages, page);
+
+	sections = nmt_editor_page_get_sections (page);
+	for (iter = sections; iter; iter = iter->next) {
+		if (!first_section)
+			first_section = iter->data;
+		nmt_editor_grid_append (grid, NULL, iter->data, NULL);
+	}
+
+	return first_section;
+}
+
+static void
+nmt_editor_constructed (GObject *object)
+{
+	NmtEditor *editor = NMT_EDITOR (object);
+	NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (editor);
+	NMSettingConnection *s_con;
+	NmtNewtWidget *vbox, *widget, *buttons;
+	NmtEditorGrid *grid;
+	const char *deventry_label;
+	NmtDeviceEntry *deventry;
+	GType hardware_type;
+	const char *slave_type;
+	NmtEditorPage *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 ();
+
+	s_con = nm_connection_get_setting_connection (priv->edit_connection);
+
+	grid = NMT_EDITOR_GRID (nmt_editor_grid_new ());
+	nmt_newt_grid_add (NMT_NEWT_GRID (vbox), NMT_NEWT_WIDGET (grid), 0, 0);
+
+	/* Add the top widgets */
+
+	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_editor_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;
+
+	/* For connections involving multiple network devices, clarify which one
+	 * NMSettingConnection:interface-name refers to.
+	 */
+	if (nm_connection_is_type (priv->edit_connection, NM_SETTING_PPPOE_SETTING_NAME))
+		deventry_label = _("Ethernet device");
+	else
+		deventry_label = _("Device");
+
+	widget = nmt_device_entry_new (deventry_label, 40, hardware_type);
+	nmt_editor_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_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+	/* Now add the various pages... */
+
+	if (nm_connection_is_type (priv->edit_connection, NM_SETTING_BOND_SETTING_NAME))
+		page = nmt_page_bond_new (priv->edit_connection, deventry);
+	else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_BRIDGE_SETTING_NAME))
+		page = nmt_page_bridge_new (priv->edit_connection, deventry);
+	else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_INFINIBAND_SETTING_NAME))
+		page = nmt_page_infiniband_new (priv->edit_connection, deventry);
+	else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_PPPOE_SETTING_NAME))
+		page = nmt_page_dsl_new (priv->edit_connection, deventry);
+	else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_TEAM_SETTING_NAME))
+		page = nmt_page_team_new (priv->edit_connection, deventry);
+	else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_VLAN_SETTING_NAME))
+		page = nmt_page_vlan_new (priv->edit_connection, deventry);
+	else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_WIRED_SETTING_NAME))
+		page = nmt_page_ethernet_new (priv->edit_connection, deventry);
+	else if (nm_connection_is_type (priv->edit_connection, NM_SETTING_WIRELESS_SETTING_NAME))
+		page = nmt_page_wifi_new (priv->edit_connection, deventry);
+	else
+		g_assert_not_reached ();
+
+	add_sections_for_page (editor, grid, page);
+	nmt_editor_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))
+			add_sections_for_page (editor, grid, nmt_page_bridge_port_new (priv->edit_connection));
+		else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME))
+			add_sections_for_page (editor, grid, nmt_page_team_port_new (priv->edit_connection));
+	} else {
+		NmtNewtWidget *section;
+
+		section = add_sections_for_page (editor, grid, nmt_page_ip4_new (priv->edit_connection));
+
+		/* Add a separator between ip4 and ip6 that's only visible if ip4 is open */
+		widget = nmt_newt_separator_new ();
+		g_object_bind_property (section, "open", widget, "visible", G_BINDING_SYNC_CREATE);
+		nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+		add_sections_for_page (editor, grid, nmt_page_ip6_new (priv->edit_connection));
+
+		nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+	}
+
+	/* And finally the bottom widgets */
+
+	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_editor_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_editor_grid_append (grid, NULL, widget, NULL);
+
+	/* And the button box */
+
+	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 (NMT_NEWT_WIDGET (grid), "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_slist_free_full (priv->pages, g_object_unref);
+
+	g_clear_object (&priv->ok);
+	g_clear_object (&priv->cancel);
+
+	G_OBJECT_CLASS (nmt_editor_parent_class)->finalize (object);
+}
+
+static void
+nmt_editor_set_property (GObject      *object,
+                         guint         prop_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+	NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_CONNECTION:
+		priv->orig_connection = g_value_dup_object (value);
+		break;
+	case PROP_TYPE_DATA:
+		priv->type_data = g_value_get_pointer (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_editor_get_property (GObject    *object,
+                         guint       prop_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
+{
+	NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_CONNECTION:
+		g_value_set_object (value, priv->orig_connection);
+		break;
+	case PROP_TYPE_DATA:
+		g_value_set_pointer (value, priv->type_data);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_editor_class_init (NmtEditorClass *entry_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+	g_type_class_add_private (entry_class, sizeof (NmtEditorPrivate));
+
+	/* virtual methods */
+	object_class->constructed  = nmt_editor_constructed;
+	object_class->set_property = nmt_editor_set_property;
+	object_class->get_property = nmt_editor_get_property;
+	object_class->finalize     = nmt_editor_finalize;
+
+	/**
+	 * NmtEditor:connection:
+	 *
+	 * The connection being edited.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_CONNECTION,
+		 g_param_spec_object ("connection", "", "",
+		                      NM_TYPE_CONNECTION,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtEditor:type-data:
+	 *
+	 * The #NmEditorConnectionTypeData for #NmtEditor:connection.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_TYPE_DATA,
+		 g_param_spec_pointer ("type-data", "", "",
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-editor.h b/clients/tui/nmt-editor.h
new file mode 100644
index 00000000..db10b82a
--- /dev/null
+++ b/clients/tui/nmt-editor.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_EDITOR_H
+#define NMT_EDITOR_H
+
+#include <NetworkManager.h>
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_EDITOR            (nmt_editor_get_type ())
+#define NMT_EDITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR, NmtEditor))
+#define NMT_EDITOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR, NmtEditorClass))
+#define NMT_IS_EDITOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR))
+#define NMT_IS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR))
+#define NMT_EDITOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR, NmtEditorClass))
+
+typedef struct {
+	NmtNewtForm parent;
+
+} NmtEditor;
+
+typedef struct {
+	NmtNewtFormClass parent;
+
+} NmtEditorClass;
+
+GType nmt_editor_get_type (void);
+
+NmtNewtForm *nmt_editor_new (NMConnection *connection);
+
+G_END_DECLS
+
+#endif /* NMT_EDITOR_H */
diff --git a/clients/tui/nmt-ip-entry.c b/clients/tui/nmt-ip-entry.c
new file mode 100644
index 00000000..30c7e2e4
--- /dev/null
+++ b/clients/tui/nmt-ip-entry.c
@@ -0,0 +1,268 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-ip-entry
+ * @short_description: #NmtNewtEntry for IP address entry
+ *
+ * #NmtIPEntry is an #NmtNewtEntry for entering IP addresses, or IP
+ * address/prefix combination. It will only allow typing characters
+ * that are valid in an IP address, and will set its
+ * #NmtNewtWidget:valid property depending on whether it currently
+ * contains a valid IP address.
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-ip-entry.h"
+
+G_DEFINE_TYPE (NmtIPEntry, nmt_ip_entry, NMT_TYPE_NEWT_ENTRY)
+
+#define NMT_IP_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_IP_ENTRY, NmtIPEntryPrivate))
+
+typedef struct {
+	int family;
+	gboolean prefix;
+	gboolean optional;
+
+} NmtIPEntryPrivate;
+
+enum {
+	PROP_0,
+	PROP_FAMILY,
+	PROP_PREFIX,
+	PROP_OPTIONAL,
+
+	LAST_PROP
+};
+
+/**
+ * nmt_ip_entry_new:
+ * @width: the width of the entry
+ * @family: the IP address family. Eg, %AF_INET
+ * @prefix: whether to require a trailing "/prefix"
+ * @optional: whether the address is optional
+ *
+ * Creates a new #NmtIPEntry, to accept IP addresses in the indicated
+ * @family, or (if @prefix is %TRUE), to accept IP address/prefix combos.
+ *
+ * If @optional is %TRUE then the address is considered optional, and
+ * so will still be #NmtNewtWidget:valid even when it is empty. If
+ * @optional is %FALSE, the entry will be invalid when it is empty.
+ */
+NmtNewtWidget *
+nmt_ip_entry_new (int      width,
+                  int      family,
+                  gboolean prefix,
+                  gboolean optional)
+{
+	return g_object_new (NMT_TYPE_IP_ENTRY,
+	                     "width", width,
+	                     "family", family,
+	                     "prefix", prefix,
+	                     "optional", optional,
+	                     NULL);
+}
+
+static gboolean
+ip_entry_filter (NmtNewtEntry *entry,
+                 const char   *text,
+                 int           ch,
+                 int           position,
+                 gpointer      user_data)
+{
+	NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (entry);
+	const char *slash;
+	gboolean inaddr;
+
+	if (g_ascii_isdigit (ch))
+		return TRUE;
+
+	slash = strchr (text, '/');
+	if (ch == '/')
+		return priv->prefix && slash == NULL;
+
+	inaddr = !slash || (position <= (slash - text));
+
+	if (priv->family == AF_INET) {
+		if (ch == '.')
+			return inaddr;
+		else
+			return FALSE;
+	} else if (priv->family == AF_INET6) {
+		if (g_ascii_isxdigit (ch) || ch == ':')
+			return inaddr;
+		else
+			return FALSE;
+	} else
+		g_return_val_if_reached (FALSE);
+}
+
+static gboolean
+ip_entry_validate (NmtNewtEntry *entry,
+                   const char   *text,
+                   gpointer      user_data)
+{
+	NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (entry);
+	guchar buf[16];
+	guint32 prefix;
+	const char *slash;
+	char *addrstr, *end;
+	gboolean valid;
+
+	if (!*text)
+		return priv->optional;
+
+	slash = strchr (text, '/');
+
+	if (slash) {
+		if (!priv->prefix)
+			return FALSE;
+		addrstr = g_strndup (text, slash - text);
+	} else
+		addrstr = g_strdup (text);
+	valid = (inet_pton (priv->family, addrstr, buf) == 1);
+	g_free (addrstr);
+
+	if (!valid)
+		return FALSE;
+
+	if (slash) {
+		prefix = strtoul (slash + 1, &end, 10);
+		if (   *end
+		    || prefix == 0
+		    || (priv->family == AF_INET && prefix > 32)
+		    || (priv->family == AF_INET6 && prefix > 128))
+			valid = FALSE;
+	}
+
+	return valid;
+}
+
+static void
+nmt_ip_entry_init (NmtIPEntry *entry)
+{
+	nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), ip_entry_filter, NULL);
+	nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), ip_entry_validate, NULL);
+}
+
+static void
+nmt_ip_entry_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+	NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FAMILY:
+		priv->family = g_value_get_int (value);
+		break;
+	case PROP_PREFIX:
+		priv->prefix = g_value_get_boolean (value);
+		break;
+	case PROP_OPTIONAL:
+		priv->optional = g_value_get_boolean (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_ip_entry_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+	NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_FAMILY:
+		g_value_set_int (value, priv->family);
+		break;
+	case PROP_PREFIX:
+		g_value_set_boolean (value, priv->prefix);
+		break;
+	case PROP_OPTIONAL:
+		g_value_set_boolean (value, priv->optional);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_ip_entry_class_init (NmtIPEntryClass *entry_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+	g_type_class_add_private (entry_class, sizeof (NmtIPEntryPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_ip_entry_set_property;
+	object_class->get_property = nmt_ip_entry_get_property;
+
+	/**
+	 * NmtIPEntry:family:
+	 *
+	 * The address family. Eg, %AF_INET
+	 */
+	g_object_class_install_property
+		(object_class, PROP_FAMILY,
+		 g_param_spec_int ("family", "", "",
+		                   0, G_MAXINT, 0,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_CONSTRUCT_ONLY |
+		                   G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtIPEntry:prefix:
+	 *
+	 * If %TRUE, the entry accepts address/prefix combinations. If
+	 * %FALSE it accepts just addresses.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_PREFIX,
+		 g_param_spec_boolean ("prefix", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtIPEntry:optional:
+	 *
+	 * If %TRUE, the entry will be #NmtNewtWidget:valid when it is
+	 * empty. If %FALSE, it will only be valid when it contains a
+	 * valid address or address/prefix.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_OPTIONAL,
+		 g_param_spec_boolean ("optional", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-ip-entry.h b/clients/tui/nmt-ip-entry.h
new file mode 100644
index 00000000..ceb355ef
--- /dev/null
+++ b/clients/tui/nmt-ip-entry.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_IP_ENTRY_H
+#define NMT_IP_ENTRY_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_IP_ENTRY            (nmt_ip_entry_get_type ())
+#define NMT_IP_ENTRY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_IP_ENTRY, NmtIPEntry))
+#define NMT_IP_ENTRY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_IP_ENTRY, NmtIPEntryClass))
+#define NMT_IS_IP_ENTRY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_IP_ENTRY))
+#define NMT_IS_IP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_IP_ENTRY))
+#define NMT_IP_ENTRY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_IP_ENTRY, NmtIPEntryClass))
+
+typedef struct {
+	NmtNewtEntry parent;
+
+} NmtIPEntry;
+
+typedef struct {
+	NmtNewtEntryClass parent;
+
+} NmtIPEntryClass;
+
+GType nmt_ip_entry_get_type (void);
+
+NmtNewtWidget *nmt_ip_entry_new (int      width,
+                                 int      family,
+                                 gboolean prefix,
+                                 gboolean optional);
+
+G_END_DECLS
+
+#endif /* NMT_IP_ENTRY_H */
diff --git a/clients/tui/nmt-mac-entry.c b/clients/tui/nmt-mac-entry.c
new file mode 100644
index 00000000..6501f09a
--- /dev/null
+++ b/clients/tui/nmt-mac-entry.c
@@ -0,0 +1,227 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-mac-entry
+ * @short_description: #NmtNewtEntry for hardware address entry
+ *
+ * #NmtMacEntry is an #NmtNewtEntry for entering hardware addresses.
+ * It will only allow typing characters that are valid in a hardware
+ * address, and will set its #NmtNewtWidget:valid property depending
+ * on whether it currently contains a valid hardware address.
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include <NetworkManager.h>
+
+#include "nmt-mac-entry.h"
+
+G_DEFINE_TYPE (NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY)
+
+#define NMT_MAC_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_MAC_ENTRY, NmtMacEntryPrivate))
+
+typedef struct {
+	int mac_length;
+	int mac_str_length;
+
+} NmtMacEntryPrivate;
+
+enum {
+	PROP_0,
+	PROP_MAC_LENGTH,
+	PROP_MAC_ADDRESS,
+
+	LAST_PROP
+};
+
+/**
+ * nmt_mac_entry_new:
+ * @width: the width in characters of the entry
+ * @mac_length: the length in bytes of the hardware address
+ *   (either %ETH_ALEN or %INFINIBAND_ALEN)
+ *
+ * Creates a new #NmtMacEntry.
+ *
+ * Returns: a new #NmtMacEntry.
+ */
+NmtNewtWidget *
+nmt_mac_entry_new (int width,
+                   int mac_length)
+{
+	return g_object_new (NMT_TYPE_MAC_ENTRY,
+	                     "width", width,
+	                     "mac-length", mac_length,
+	                     NULL);
+}
+
+static gboolean
+mac_filter (NmtNewtEntry *entry,
+            const char   *text,
+            int           ch,
+            int           position,
+            gpointer      user_data)
+{
+	NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry);
+
+	if (position >= priv->mac_str_length)
+		return FALSE;
+
+	return g_ascii_isxdigit (ch) || ch == ':';
+}
+
+static gboolean
+mac_validator (NmtNewtEntry *entry,
+               const char   *text,
+               gpointer      user_data)
+{
+	NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry);
+	const char *p;
+
+	if (!*text)
+		return TRUE;
+
+	p = text;
+	while (   g_ascii_isxdigit (p[0])
+	       && g_ascii_isxdigit (p[1])
+	       && p[2] == ':')
+		p += 3;
+
+	if (   !g_ascii_isxdigit (p[0])
+	    || !g_ascii_isxdigit (p[1]))
+		return FALSE;
+	p += 2;
+
+	if (!*p)
+		return (p - text == priv->mac_str_length);
+
+	if (g_ascii_isxdigit (p[0]) && !p[1]) {
+		char *fixed = g_strdup_printf ("%.*s:%c", (int)(p - text), text, *p);
+
+		nmt_newt_entry_set_text (entry, fixed);
+		g_free (fixed);
+
+		/* FIXME: NmtNewtEntry doesn't correctly deal with us calling set_text()
+		 * from inside the validator.
+		 */
+		nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry));
+	}
+
+	return FALSE;
+}
+
+static void
+nmt_mac_entry_init (NmtMacEntry *entry)
+{
+	nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), mac_filter, NULL);
+	nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), mac_validator, NULL);
+}
+
+static void
+nmt_mac_entry_notify (GObject    *object,
+                      GParamSpec *pspec)
+{
+	if (G_OBJECT_CLASS (nmt_mac_entry_parent_class)->notify)
+		G_OBJECT_CLASS (nmt_mac_entry_parent_class)->notify (object, pspec);
+
+	if (pspec->owner_type == NMT_TYPE_NEWT_ENTRY && !strcmp (pspec->name, "text"))
+		g_object_notify (object, "mac-address");
+}
+
+static void
+nmt_mac_entry_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+	NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (object);
+
+	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:
+		nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), g_value_get_string (value));
+		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);
+
+	switch (prop_id) {
+	case PROP_MAC_LENGTH:
+		g_value_set_int (value, priv->mac_length);
+		break;
+	case PROP_MAC_ADDRESS:
+		g_value_set_string (value, nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object)));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_mac_entry_class_init (NmtMacEntryClass *entry_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+	g_type_class_add_private (entry_class, sizeof (NmtMacEntryPrivate));
+
+	/* virtual methods */
+	object_class->notify = nmt_mac_entry_notify;
+	object_class->set_property = nmt_mac_entry_set_property;
+	object_class->get_property = nmt_mac_entry_get_property;
+
+	/**
+	 * NmtMacEntry:mac-length:
+	 *
+	 * The length in bytes of the hardware address type the entry
+	 * accepts: either %ETH_ALEN or %INFINIBAND_ALEN.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_MAC_LENGTH,
+		 g_param_spec_int ("mac-length", "", "",
+		                   0, INFINIBAND_ALEN, ETH_ALEN,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtMacEntry:mac-address:
+	 *
+	 * The MAC address, as a string (as with the various #NMSetting
+	 * "mac-address" properties).
+	 */
+	g_object_class_install_property
+		(object_class, PROP_MAC_ADDRESS,
+		 g_param_spec_string ("mac-address", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-mac-entry.h b/clients/tui/nmt-mac-entry.h
new file mode 100644
index 00000000..2e3e6cd1
--- /dev/null
+++ b/clients/tui/nmt-mac-entry.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_MAC_ENTRY_H
+#define NMT_MAC_ENTRY_H
+
+#include "nm-utils.h"
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_MAC_ENTRY            (nmt_mac_entry_get_type ())
+#define NMT_MAC_ENTRY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntry))
+#define NMT_MAC_ENTRY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass))
+#define NMT_IS_MAC_ENTRY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_MAC_ENTRY))
+#define NMT_IS_MAC_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_MAC_ENTRY))
+#define NMT_MAC_ENTRY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass))
+
+typedef struct {
+	NmtNewtEntry parent;
+
+} NmtMacEntry;
+
+typedef struct {
+	NmtNewtEntryClass parent;
+
+} NmtMacEntryClass;
+
+GType nmt_mac_entry_get_type (void);
+
+NmtNewtWidget *nmt_mac_entry_new (int width,
+                                  int mac_length);
+
+G_END_DECLS
+
+#endif /* NMT_MAC_ENTRY_H */
diff --git a/clients/tui/nmt-mtu-entry.c b/clients/tui/nmt-mtu-entry.c
new file mode 100644
index 00000000..f599557a
--- /dev/null
+++ b/clients/tui/nmt-mtu-entry.c
@@ -0,0 +1,192 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-mtu-entry
+ * @short_description: #NmtNewtEntry for MTU entry
+ *
+ * #NmtMtuEntry is an #NmtNewtEntry for entering MTU values. It will
+ * only allow typing numeric characters, and will set its
+ * #NmtNewtWidget:valid property depending on whether it currently
+ * contains a valid MTU.
+ *
+ * The entry also has an attached #NmtNewtLabel. When the entry value
+ * is "0", the label will read "(default)". Otherwise it reads "bytes",
+ * indicating the units used by the entry.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-mtu-entry.h"
+
+G_DEFINE_TYPE (NmtMtuEntry, nmt_mtu_entry, NMT_TYPE_NEWT_GRID)
+
+#define NMT_MTU_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_MTU_ENTRY, NmtMtuEntryPrivate))
+
+typedef struct {
+	int mtu;
+
+	NmtNewtEntry *entry;
+	NmtNewtLabel *label;
+
+} NmtMtuEntryPrivate;
+
+enum {
+	PROP_0,
+	PROP_MTU,
+
+	LAST_PROP
+};
+
+/**
+ * nmt_mtu_entry_new:
+ *
+ * Creates a new #NmtMtuEntry
+ *
+ * Returns: a new #NmtMtuEntry
+ */
+NmtNewtWidget *
+nmt_mtu_entry_new (void)
+{
+	return g_object_new (NMT_TYPE_MTU_ENTRY, NULL);
+}
+
+static gboolean
+mtu_validator (NmtNewtEntry *entry,
+               const char   *text,
+               gpointer      user_data)
+{
+	NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (user_data);
+
+	if (*text && !atoi (text)) {
+		nmt_newt_entry_set_text (entry, "");
+		text = "";
+	}
+
+	if (!*text)
+		nmt_newt_label_set_text (priv->label, _("(default)"));
+	else
+		nmt_newt_label_set_text (priv->label, _("bytes"));
+
+	return TRUE;
+}
+
+static gboolean
+mtu_transform_to_text (GBinding     *binding,
+                       const GValue *source_value,
+                       GValue       *target_value,
+                       gpointer      user_data)
+{
+	int mtu = g_value_get_int (source_value);
+
+	if (mtu)
+		g_value_transform (source_value, target_value);
+	else
+		g_value_set_string (target_value, "");
+	return TRUE;
+}
+
+static void
+nmt_mtu_entry_init (NmtMtuEntry *entry)
+{
+
+	NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (entry);
+	NmtNewtGrid *grid = NMT_NEWT_GRID (entry);
+	NmtNewtWidget *real_entry, *label;
+
+	real_entry = nmt_newt_entry_numeric_new (10, 0, 65535);
+	priv->entry = NMT_NEWT_ENTRY (real_entry);
+
+	label = nmt_newt_label_new (_("bytes"));
+	priv->label = NMT_NEWT_LABEL (label);
+
+	nmt_newt_grid_add (grid, real_entry, 0, 0);
+	nmt_newt_grid_add (grid, label, 1, 0);
+	nmt_newt_widget_set_padding (label, 1, 0, 0, 0);
+
+	nmt_newt_entry_set_validator (priv->entry, mtu_validator, entry);
+	g_object_bind_property_full (entry, "mtu", real_entry, "text",
+	                             G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE,
+	                             mtu_transform_to_text,
+	                             NULL,
+	                             NULL, NULL);
+}
+
+static void
+nmt_mtu_entry_set_property (GObject      *object,
+                            guint         prop_id,
+                            const GValue *value,
+                            GParamSpec   *pspec)
+{
+	NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MTU:
+		priv->mtu = g_value_get_int (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_mtu_entry_get_property (GObject    *object,
+                            guint       prop_id,
+                            GValue     *value,
+                            GParamSpec *pspec)
+{
+	NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MTU:
+		g_value_set_int (value, priv->mtu);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_mtu_entry_class_init (NmtMtuEntryClass *entry_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+	g_type_class_add_private (entry_class, sizeof (NmtMtuEntryPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_mtu_entry_set_property;
+	object_class->get_property = nmt_mtu_entry_get_property;
+
+	/**
+	 * NmtMtuEntry:mtu:
+	 *
+	 * The contents of the entry, as a number.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_MTU,
+		 g_param_spec_int ("mtu", "", "",
+		                   0, G_MAXINT, 0,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-mtu-entry.h b/clients/tui/nmt-mtu-entry.h
new file mode 100644
index 00000000..dfe416d0
--- /dev/null
+++ b/clients/tui/nmt-mtu-entry.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_MTU_ENTRY_H
+#define NMT_MTU_ENTRY_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_MTU_ENTRY            (nmt_mtu_entry_get_type ())
+#define NMT_MTU_ENTRY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntry))
+#define NMT_MTU_ENTRY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass))
+#define NMT_IS_MTU_ENTRY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_MTU_ENTRY))
+#define NMT_IS_MTU_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_MTU_ENTRY))
+#define NMT_MTU_ENTRY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass))
+
+typedef struct {
+	NmtNewtGrid parent;
+
+} NmtMtuEntry;
+
+typedef struct {
+	NmtNewtGridClass parent;
+
+} NmtMtuEntryClass;
+
+GType nmt_mtu_entry_get_type (void);
+
+NmtNewtWidget *nmt_mtu_entry_new (void);
+
+G_END_DECLS
+
+#endif /* NMT_MTU_ENTRY_H */
diff --git a/clients/tui/nmt-page-bond.c b/clients/tui/nmt-page-bond.c
new file mode 100644
index 00000000..b043b513
--- /dev/null
+++ b/clients/tui/nmt-page-bond.c
@@ -0,0 +1,437 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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_EDITOR_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;
+
+NmtEditorPage *
+nmt_page_bond_new (NMConnection   *conn,
+                   NmtDeviceEntry *deventry)
+{
+	return g_object_new (NMT_TYPE_PAGE_BOND,
+	                     "connection", conn,
+	                     "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),
+		              "strings", 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);
+		nm_setting_bond_add_option (priv->s_bond, NM_SETTING_BOND_OPTION_PRIMARY,
+		                            nmt_newt_entry_get_text (priv->primary));
+	} else {
+		nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), FALSE);
+		nm_setting_bond_remove_option (priv->s_bond, NM_SETTING_BOND_OPTION_PRIMARY);
+	}
+}
+
+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);
+	NmtEditorSection *section;
+	NmtEditorGrid *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;
+
+	section = nmt_editor_section_new (_("BOND"), NULL, TRUE);
+	grid = nmt_editor_section_get_body (section);
+
+	widget = nmt_newt_separator_new ();
+	nmt_editor_grid_append (grid, _("Slaves"), widget, NULL);
+	nmt_editor_grid_set_row_flags (grid, widget, NMT_EDITOR_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_editor_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_editor_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_editor_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_editor_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_editor_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_editor_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_editor_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_editor_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_editor_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);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (bond), section);
+
+	G_OBJECT_CLASS (nmt_page_bond_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_bond_class_init (NmtPageBondClass *bond_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (bond_class);
+
+	g_type_class_add_private (bond_class, sizeof (NmtPageBondPrivate));
+
+	object_class->constructed = nmt_page_bond_constructed;
+}
diff --git a/clients/tui/nmt-page-bond.h b/clients/tui/nmt-page-bond.h
new file mode 100644
index 00000000..b77477db
--- /dev/null
+++ b/clients/tui/nmt-page-bond.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_BOND_H
+#define NMT_PAGE_BOND_H
+
+#include "nmt-editor-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 {
+	NmtEditorPageDevice parent;
+
+} NmtPageBond;
+
+typedef struct {
+	NmtEditorPageDeviceClass parent;
+
+} NmtPageBondClass;
+
+GType nmt_page_bond_get_type (void);
+
+NmtEditorPage *nmt_page_bond_new (NMConnection   *conn,
+                                  NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_BOND_H */
diff --git a/clients/tui/nmt-page-bridge-port.c b/clients/tui/nmt-page-bridge-port.c
new file mode 100644
index 00000000..c9ef14ed
--- /dev/null
+++ b/clients/tui/nmt-page-bridge-port.c
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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)
+
+NmtEditorPage *
+nmt_page_bridge_port_new (NMConnection *conn)
+{
+	return g_object_new (NMT_TYPE_PAGE_BRIDGE_PORT,
+	                     "connection", conn,
+	                     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);
+	NmtEditorSection *section;
+	NmtEditorGrid *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);
+	}
+
+	section = nmt_editor_section_new (_("BRIDGE PORT"), NULL, TRUE);
+	grid = nmt_editor_section_get_body (section);
+
+	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_editor_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_editor_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_editor_grid_append (grid, NULL, widget, NULL);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (bridge), section);
+
+	G_OBJECT_CLASS (nmt_page_bridge_port_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_bridge_port_class_init (NmtPageBridgePortClass *bridge_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (bridge_class);
+
+	object_class->constructed = nmt_page_bridge_port_constructed;
+}
diff --git a/clients/tui/nmt-page-bridge-port.h b/clients/tui/nmt-page-bridge-port.h
new file mode 100644
index 00000000..c566dbc0
--- /dev/null
+++ b/clients/tui/nmt-page-bridge-port.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_BRIDGE_PORT_H
+#define NMT_PAGE_BRIDGE_PORT_H
+
+#include "nmt-editor-page.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_BRIDGE_PORT            (nmt_page_bridge_port_get_type ())
+#define NMT_PAGE_BRIDGE_PORT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePort))
+#define NMT_PAGE_BRIDGE_PORT_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass))
+#define NMT_IS_PAGE_BRIDGE_PORT(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_BRIDGE_PORT))
+#define NMT_IS_PAGE_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_BRIDGE_PORT))
+#define NMT_PAGE_BRIDGE_PORT_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass))
+
+typedef struct {
+	NmtEditorPage parent;
+
+} NmtPageBridgePort;
+
+typedef struct {
+	NmtEditorPageClass parent;
+
+} NmtPageBridgePortClass;
+
+GType nmt_page_bridge_port_get_type (void);
+
+NmtEditorPage *nmt_page_bridge_port_new (NMConnection *conn);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_BRIDGE_PORT_H */
diff --git a/clients/tui/nmt-page-bridge.c b/clients/tui/nmt-page-bridge.c
new file mode 100644
index 00000000..460be60b
--- /dev/null
+++ b/clients/tui/nmt-page-bridge.c
@@ -0,0 +1,150 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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_EDITOR_PAGE_DEVICE)
+
+NmtEditorPage *
+nmt_page_bridge_new (NMConnection   *conn,
+                     NmtDeviceEntry *deventry)
+{
+	return g_object_new (NMT_TYPE_PAGE_BRIDGE,
+	                     "connection", conn,
+	                     "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);
+	NmtEditorSection *section;
+	NmtEditorGrid *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);
+	}
+
+	section = nmt_editor_section_new (_("BRIDGE"), NULL, TRUE);
+	grid = nmt_editor_section_get_body (section);
+
+	widget = nmt_newt_separator_new ();
+	nmt_editor_grid_append (grid, _("Slaves"), widget, NULL);
+	nmt_editor_grid_set_row_flags (grid, widget, NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT);
+
+	widget = nmt_slave_list_new (conn, bridge_connection_type_filter, bridge);
+	nmt_editor_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_editor_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_editor_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_editor_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_editor_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_editor_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_editor_grid_append (grid, _("Max age"), widget, label);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (bridge), section);
+
+	G_OBJECT_CLASS (nmt_page_bridge_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_bridge_class_init (NmtPageBridgeClass *bridge_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (bridge_class);
+
+	object_class->constructed = nmt_page_bridge_constructed;
+}
diff --git a/clients/tui/nmt-page-bridge.h b/clients/tui/nmt-page-bridge.h
new file mode 100644
index 00000000..60cbdea2
--- /dev/null
+++ b/clients/tui/nmt-page-bridge.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_BRIDGE_H
+#define NMT_PAGE_BRIDGE_H
+
+#include "nmt-editor-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 {
+	NmtEditorPageDevice parent;
+
+} NmtPageBridge;
+
+typedef struct {
+	NmtEditorPageDeviceClass parent;
+
+} NmtPageBridgeClass;
+
+GType nmt_page_bridge_get_type (void);
+
+NmtEditorPage *nmt_page_bridge_new (NMConnection   *conn,
+                                    NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_BRIDGE_H */
diff --git a/clients/tui/nmt-page-dsl.c b/clients/tui/nmt-page-dsl.c
new file mode 100644
index 00000000..e8894d1a
--- /dev/null
+++ b/clients/tui/nmt-page-dsl.c
@@ -0,0 +1,141 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2014 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-dsl
+ * @short_description: The editor page for DSL connections
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-dsl.h"
+#include "nmt-page-ethernet.h"
+#include "nmt-page-ppp.h"
+#include "nmt-password-fields.h"
+
+G_DEFINE_TYPE (NmtPageDsl, nmt_page_dsl, NMT_TYPE_EDITOR_PAGE_DEVICE)
+
+#define NMT_PAGE_DSL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_DSL, NmtPageDslPrivate))
+
+typedef struct {
+	NmtEditorPage *ethernet_page, *ppp_page;
+
+} NmtPageDslPrivate;
+
+NmtEditorPage *
+nmt_page_dsl_new (NMConnection *conn,
+                  NmtDeviceEntry *deventry)
+{
+	return g_object_new (NMT_TYPE_PAGE_DSL,
+	                     "connection", conn,
+	                     "device-entry", deventry,
+	                     NULL);
+}
+
+static void
+nmt_page_dsl_init (NmtPageDsl *dsl)
+{
+}
+
+static NmtEditorSection *
+build_dsl_section (NmtPageDsl *dsl, NMSettingPppoe *s_pppoe)
+{
+	NmtEditorSection *section;
+	NmtEditorGrid *grid;
+	NmtNewtWidget *widget;
+
+	section = nmt_editor_section_new (_("DSL"), NULL, TRUE);
+	grid = nmt_editor_section_get_body (section);
+
+	widget = nmt_newt_entry_new (40, 0);
+	nmt_editor_grid_append (grid, _("Username"), widget, NULL);
+	g_object_bind_property (s_pppoe, NM_SETTING_PPPOE_USERNAME,
+	                        widget, "text",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+	widget = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD);
+	g_object_bind_property (s_pppoe, NM_SETTING_PPPOE_PASSWORD,
+	                        widget, "password",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+	nmt_editor_grid_append (grid, _("Password"), widget, NULL);
+
+	widget = nmt_newt_entry_new (40, 0);
+	nmt_editor_grid_append (grid, _("Service"), widget, NULL);
+	g_object_bind_property (s_pppoe, NM_SETTING_PPPOE_SERVICE,
+	                        widget, "text",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+	return section;
+}
+
+static void
+nmt_page_dsl_constructed (GObject *object)
+{
+	NmtPageDsl *dsl = NMT_PAGE_DSL (object);
+	NmtPageDslPrivate *priv = NMT_PAGE_DSL_GET_PRIVATE (dsl);
+	NMConnection *conn;
+	NMSettingPppoe *s_pppoe;
+	NmtEditorSection *section;
+	const GSList *sections, *iter;
+
+	conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (dsl));
+	s_pppoe = nm_connection_get_setting_pppoe (conn);
+	if (!s_pppoe) {
+		nm_connection_add_setting (conn, nm_setting_pppoe_new ());
+		s_pppoe = nm_connection_get_setting_pppoe (conn);
+	}
+
+	section = build_dsl_section (dsl, s_pppoe);
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (dsl), section);
+
+	priv->ethernet_page = nmt_page_ethernet_new (conn, nmt_editor_page_device_get_device_entry (NMT_EDITOR_PAGE_DEVICE (dsl)));
+	sections = nmt_editor_page_get_sections (priv->ethernet_page);
+	for (iter = sections; iter; iter = iter->next)
+		nmt_editor_page_add_section (NMT_EDITOR_PAGE (dsl), iter->data);
+
+	priv->ppp_page = nmt_page_ppp_new (conn);
+	sections = nmt_editor_page_get_sections (priv->ppp_page);
+	for (iter = sections; iter; iter = iter->next)
+		nmt_editor_page_add_section (NMT_EDITOR_PAGE (dsl), iter->data);
+
+	G_OBJECT_CLASS (nmt_page_dsl_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_dsl_finalize (GObject *object)
+{
+	NmtPageDsl *dsl = NMT_PAGE_DSL (object);
+	NmtPageDslPrivate *priv = NMT_PAGE_DSL_GET_PRIVATE (dsl);
+
+	g_clear_object (&priv->ethernet_page);
+	g_clear_object (&priv->ppp_page);
+
+	G_OBJECT_CLASS (nmt_page_dsl_parent_class)->finalize (object);
+}
+
+static void
+nmt_page_dsl_class_init (NmtPageDslClass *dsl_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (dsl_class);
+
+	object_class->constructed = nmt_page_dsl_constructed;
+	object_class->finalize = nmt_page_dsl_finalize;
+}
diff --git a/clients/tui/nmt-page-dsl.h b/clients/tui/nmt-page-dsl.h
new file mode 100644
index 00000000..b938ead2
--- /dev/null
+++ b/clients/tui/nmt-page-dsl.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_DSL_H
+#define NMT_PAGE_DSL_H
+
+#include "nmt-editor-page-device.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_DSL            (nmt_page_dsl_get_type ())
+#define NMT_PAGE_DSL(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_DSL, NmtPageDsl))
+#define NMT_PAGE_DSL_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_DSL, NmtPageDslClass))
+#define NMT_IS_PAGE_DSL(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_DSL))
+#define NMT_IS_PAGE_DSL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_DSL))
+#define NMT_PAGE_DSL_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_DSL, NmtPageDslClass))
+
+typedef struct {
+	NmtEditorPageDevice parent;
+
+} NmtPageDsl;
+
+typedef struct {
+	NmtEditorPageDeviceClass parent;
+
+} NmtPageDslClass;
+
+GType nmt_page_dsl_get_type (void);
+
+NmtEditorPage *nmt_page_dsl_new (NMConnection *conn,
+                                 NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_DSL_H */
diff --git a/clients/tui/nmt-page-ethernet.c b/clients/tui/nmt-page-ethernet.c
new file mode 100644
index 00000000..cf4ef2c4
--- /dev/null
+++ b/clients/tui/nmt-page-ethernet.c
@@ -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.
+ */
+
+/**
+ * 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_EDITOR_PAGE_DEVICE)
+
+NmtEditorPage *
+nmt_page_ethernet_new (NMConnection   *conn,
+                       NmtDeviceEntry *deventry)
+{
+	return g_object_new (NMT_TYPE_PAGE_ETHERNET,
+	                     "connection", conn,
+	                     "device-entry", deventry,
+	                     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;
+	NmtEditorSection *section;
+	NmtEditorGrid *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_editor_page_device_get_device_entry (NMT_EDITOR_PAGE_DEVICE (object));
+	g_object_bind_property (s_wired, NM_SETTING_WIRED_MAC_ADDRESS,
+	                        deventry, "mac-address",
+	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+	section = nmt_editor_section_new (_("ETHERNET"), NULL, FALSE);
+	grid = nmt_editor_section_get_body (section);
+
+	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_editor_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_editor_grid_append (grid, _("MTU"), widget, NULL);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (ethernet), section);
+
+	G_OBJECT_CLASS (nmt_page_ethernet_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_ethernet_class_init (NmtPageEthernetClass *ethernet_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (ethernet_class);
+
+	object_class->constructed = nmt_page_ethernet_constructed;
+}
diff --git a/clients/tui/nmt-page-ethernet.h b/clients/tui/nmt-page-ethernet.h
new file mode 100644
index 00000000..bdf3d677
--- /dev/null
+++ b/clients/tui/nmt-page-ethernet.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_ETHERNET_H
+#define NMT_PAGE_ETHERNET_H
+
+#include "nmt-editor-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 {
+	NmtEditorPageDevice parent;
+
+} NmtPageEthernet;
+
+typedef struct {
+	NmtEditorPageDeviceClass parent;
+
+} NmtPageEthernetClass;
+
+GType nmt_page_ethernet_get_type (void);
+
+NmtEditorPage *nmt_page_ethernet_new (NMConnection   *conn,
+                                      NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_ETHERNET_H */
diff --git a/clients/tui/nmt-page-infiniband.c b/clients/tui/nmt-page-infiniband.c
new file mode 100644
index 00000000..f39b0b84
--- /dev/null
+++ b/clients/tui/nmt-page-infiniband.c
@@ -0,0 +1,110 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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_EDITOR_PAGE_DEVICE)
+
+NmtEditorPage *
+nmt_page_infiniband_new (NMConnection   *conn,
+                         NmtDeviceEntry *deventry)
+{
+	return g_object_new (NMT_TYPE_PAGE_INFINIBAND,
+	                     "connection", conn,
+	                     "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;
+	NmtEditorSection *section;
+	NmtEditorGrid *grid;
+	NMSettingInfiniband *s_ib;
+	NmtNewtWidget *widget;
+	NMConnection *conn;
+
+	conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (infiniband));
+	s_ib = nm_connection_get_setting_infiniband (conn);
+	if (!s_ib) {
+		nm_connection_add_setting (conn, nm_setting_infiniband_new ());
+		s_ib = nm_connection_get_setting_infiniband (conn);
+	}
+	/* initialize 'transport-mode' if it is NULL */
+	if (!nm_setting_infiniband_get_transport_mode (s_ib)) {
+		g_object_set (G_OBJECT (s_ib),
+		              NM_SETTING_INFINIBAND_TRANSPORT_MODE, "datagram",
+		              NULL);
+	}
+
+	deventry = nmt_editor_page_device_get_device_entry (NMT_EDITOR_PAGE_DEVICE (object));
+	g_object_bind_property (s_ib, NM_SETTING_INFINIBAND_MAC_ADDRESS,
+	                        deventry, "mac-address",
+	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+	section = nmt_editor_section_new (_("INFINIBAND"), NULL, TRUE);
+	grid = nmt_editor_section_get_body (section);
+
+	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_editor_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_editor_grid_append (grid, _("MTU"), widget, NULL);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (infiniband), section);
+
+	G_OBJECT_CLASS (nmt_page_infiniband_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_infiniband_class_init (NmtPageInfinibandClass *infiniband_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (infiniband_class);
+
+	object_class->constructed = nmt_page_infiniband_constructed;
+}
diff --git a/clients/tui/nmt-page-infiniband.h b/clients/tui/nmt-page-infiniband.h
new file mode 100644
index 00000000..f51e0874
--- /dev/null
+++ b/clients/tui/nmt-page-infiniband.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_INFINIBAND_H
+#define NMT_PAGE_INFINIBAND_H
+
+#include "nmt-editor-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 {
+	NmtEditorPageDevice parent;
+
+} NmtPageInfiniband;
+
+typedef struct {
+	NmtEditorPageDeviceClass parent;
+
+} NmtPageInfinibandClass;
+
+GType nmt_page_infiniband_get_type (void);
+
+NmtEditorPage *nmt_page_infiniband_new (NMConnection   *conn,
+                                        NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_INFINIBAND_H */
diff --git a/clients/tui/nmt-page-ip4.c b/clients/tui/nmt-page-ip4.c
new file mode 100644
index 00000000..df3f0b64
--- /dev/null
+++ b/clients/tui/nmt-page-ip4.c
@@ -0,0 +1,202 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 }
+};
+
+NmtEditorPage *
+nmt_page_ip4_new (NMConnection *conn)
+{
+	return g_object_new (NMT_TYPE_PAGE_IP4,
+	                     "connection", conn,
+	                     NULL);
+}
+
+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);
+	gboolean show_by_default;
+	NmtEditorSection *section;
+	NmtEditorGrid *grid;
+	NMSettingIPConfig *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 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
+		g_object_set (G_OBJECT (s_ip4),
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
+		              NULL);
+		nm_connection_add_setting (conn, (NMSetting *) s_ip4);
+	}
+
+	widget = nmt_newt_popup_new (ip4methods);
+	g_object_bind_property (s_ip4, NM_SETTING_IP_CONFIG_METHOD,
+	                        widget, "active-id",
+	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+	if (!g_strcmp0 (nm_setting_ip_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_MANUAL))
+		show_by_default = TRUE;
+	else if (nm_setting_ip_config_get_num_addresses (s_ip4))
+		show_by_default = TRUE;
+	else
+		show_by_default = FALSE;
+
+	section = nmt_editor_section_new (_("IPv4 CONFIGURATION"), widget, show_by_default);
+	grid = nmt_editor_section_get_body (section);
+
+	widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4_WITH_PREFIX);
+	nm_editor_bind_ip_addresses_with_prefix_to_strv (AF_INET,
+	                                                 s_ip4, NM_SETTING_IP_CONFIG_ADDRESSES,
+	                                                 widget, "strings",
+	                                                 G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, _("Addresses"), widget, NULL);
+
+	widget = nmt_ip_entry_new (25, AF_INET, FALSE, TRUE);
+	nm_editor_bind_ip_gateway_to_string (AF_INET,
+	                                     s_ip4,
+	                                     widget, "text", "sensitive",
+	                                     G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, _("Gateway"), widget, NULL);
+
+	widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4);
+	nm_editor_bind_ip_addresses_to_strv (AF_INET,
+	                                     s_ip4, NM_SETTING_IP_CONFIG_DNS,
+	                                     widget, "strings",
+	                                     G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, _("DNS servers"), widget, NULL);
+
+	widget = nmt_address_list_new (NMT_ADDRESS_LIST_HOSTNAME);
+	g_object_bind_property (s_ip4, NM_SETTING_IP_CONFIG_DNS_SEARCH,
+	                        widget, "strings",
+	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, _("Search domains"), widget, NULL);
+
+	nmt_editor_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_IP_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_editor_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_IP_CONFIG_NEVER_DEFAULT,
+	                        widget, "active",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+	nmt_editor_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_IP_CONFIG_MAY_FAIL,
+	                        widget, "active",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_INVERT_BOOLEAN);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (ip4), section);
+
+	G_OBJECT_CLASS (nmt_page_ip4_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_ip4_class_init (NmtPageIP4Class *ip4_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (ip4_class);
+
+	object_class->constructed = nmt_page_ip4_constructed;
+}
diff --git a/clients/tui/nmt-page-ip4.h b/clients/tui/nmt-page-ip4.h
new file mode 100644
index 00000000..e3c5a003
--- /dev/null
+++ b/clients/tui/nmt-page-ip4.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_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);
+
+NmtEditorPage *nmt_page_ip4_new          (NMConnection *conn);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_IP4_H */
diff --git a/clients/tui/nmt-page-ip6.c b/clients/tui/nmt-page-ip6.c
new file mode 100644
index 00000000..1513dbfa
--- /dev/null
+++ b/clients/tui/nmt-page-ip6.c
@@ -0,0 +1,200 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 }
+};
+
+NmtEditorPage *
+nmt_page_ip6_new (NMConnection *conn)
+{
+	return g_object_new (NMT_TYPE_PAGE_IP6,
+	                     "connection", conn,
+	                     NULL);
+}
+
+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);
+	gboolean show_by_default;
+	NmtEditorSection *section;
+	NmtEditorGrid *grid;
+	NMSettingIPConfig *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 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
+		g_object_set (G_OBJECT (s_ip6),
+		              NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+		              NULL);
+		nm_connection_add_setting (conn, (NMSetting *) s_ip6);
+	}
+
+	widget = nmt_newt_popup_new (ip6methods);
+	g_object_bind_property (s_ip6, NM_SETTING_IP_CONFIG_METHOD,
+	                        widget, "active-id",
+	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+	if (!g_strcmp0 (nm_setting_ip_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_MANUAL))
+		show_by_default = TRUE;
+	else if (nm_setting_ip_config_get_num_addresses (s_ip6))
+		show_by_default = TRUE;
+	else
+		show_by_default = FALSE;
+
+	section = nmt_editor_section_new (_("IPv6 CONFIGURATION"), widget, show_by_default);
+	grid = nmt_editor_section_get_body (section);
+
+	widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP6_WITH_PREFIX);
+	nm_editor_bind_ip_addresses_with_prefix_to_strv (AF_INET6,
+	                                                 s_ip6, NM_SETTING_IP_CONFIG_ADDRESSES,
+	                                                 widget, "strings",
+	                                                 G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, _("Addresses"), widget, NULL);
+
+	widget = nmt_ip_entry_new (25, AF_INET6, FALSE, TRUE);
+	nm_editor_bind_ip_gateway_to_string (AF_INET6,
+	                                     s_ip6,
+	                                     widget, "text", "sensitive",
+	                                     G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, _("Gateway"), widget, NULL);
+
+	widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP6);
+	nm_editor_bind_ip_addresses_to_strv (AF_INET6,
+	                                     s_ip6, NM_SETTING_IP_CONFIG_DNS,
+	                                     widget, "strings",
+	                                     G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, _("DNS servers"), widget, NULL);
+
+	widget = nmt_address_list_new (NMT_ADDRESS_LIST_HOSTNAME);
+	g_object_bind_property (s_ip6, NM_SETTING_IP_CONFIG_DNS_SEARCH,
+	                        widget, "strings",
+	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+	nmt_editor_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_IP_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_editor_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_IP_CONFIG_NEVER_DEFAULT,
+	                        widget, "active",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+	nmt_editor_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_IP_CONFIG_MAY_FAIL,
+	                        widget, "active",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_INVERT_BOOLEAN);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (ip6), section);
+
+	G_OBJECT_CLASS (nmt_page_ip6_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_ip6_class_init (NmtPageIP6Class *ip6_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (ip6_class);
+
+	object_class->constructed = nmt_page_ip6_constructed;
+}
diff --git a/clients/tui/nmt-page-ip6.h b/clients/tui/nmt-page-ip6.h
new file mode 100644
index 00000000..498261e1
--- /dev/null
+++ b/clients/tui/nmt-page-ip6.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_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);
+
+NmtEditorPage *nmt_page_ip6_new          (NMConnection *conn);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_IP6_H */
diff --git a/clients/tui/nmt-page-ppp.c b/clients/tui/nmt-page-ppp.c
new file mode 100644
index 00000000..cec36549
--- /dev/null
+++ b/clients/tui/nmt-page-ppp.c
@@ -0,0 +1,288 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2014 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-page-ppp
+ * @short_description: The editor page for PPP configuration
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "nmt-page-ppp.h"
+#include "nmt-newt-section.h"
+#include "nmt-newt-separator.h"
+
+G_DEFINE_TYPE (NmtPagePpp, nmt_page_ppp, NMT_TYPE_EDITOR_PAGE)
+
+typedef struct {
+	guint32 lcp_echo_failure;
+	guint32 lcp_echo_interval;
+} NmtPagePppPrivate;
+
+#define NMT_PAGE_PPP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_PPP, NmtPagePppPrivate))
+
+NmtEditorPage *
+nmt_page_ppp_new (NMConnection *conn)
+{
+	return g_object_new (NMT_TYPE_PAGE_PPP,
+	                     "connection", conn,
+	                     NULL);
+}
+
+static void
+nmt_page_ppp_init (NmtPagePpp *ppp)
+{
+}
+
+static gboolean
+transform_lcp_echo_properties_to_checkbox (GBinding     *binding,
+                                           const GValue *from_value,
+                                           GValue       *to_value,
+                                           gpointer      user_data)
+{
+	NMSettingPpp *s_ppp = NM_SETTING_PPP (g_binding_get_source (binding));
+
+	if (   nm_setting_ppp_get_lcp_echo_interval (s_ppp) != 0
+	    && nm_setting_ppp_get_lcp_echo_failure (s_ppp) != 0)
+		g_value_set_boolean (to_value, TRUE);
+	else
+		g_value_set_boolean (to_value, FALSE);
+
+	return TRUE;
+}
+
+static gboolean
+transform_checkbox_to_lcp_echo_interval (GBinding     *binding,
+                                         const GValue *from_value,
+                                         GValue       *to_value,
+                                         gpointer      user_data)
+{
+	NmtPagePpp *ppp = user_data;
+	NmtPagePppPrivate *priv = NMT_PAGE_PPP_GET_PRIVATE (ppp);
+
+	if (g_value_get_boolean (from_value))
+		g_value_set_uint (to_value, priv->lcp_echo_interval);
+	else
+		g_value_set_uint (to_value, 0);
+
+	return TRUE;
+}
+
+static gboolean
+transform_checkbox_to_lcp_echo_failure (GBinding     *binding,
+                                        const GValue *from_value,
+                                        GValue       *to_value,
+                                        gpointer      user_data)
+{
+	NmtPagePpp *ppp = user_data;
+	NmtPagePppPrivate *priv = NMT_PAGE_PPP_GET_PRIVATE (ppp);
+
+	if (g_value_get_boolean (from_value))
+		g_value_set_uint (to_value, priv->lcp_echo_failure);
+	else
+		g_value_set_uint (to_value, 0);
+
+	return TRUE;
+}
+
+static void
+nmt_page_ppp_constructed (GObject *object)
+{
+	NmtPagePpp *ppp = NMT_PAGE_PPP (object);
+	NmtPagePppPrivate *priv = NMT_PAGE_PPP_GET_PRIVATE (ppp);
+	NmtEditorSection *section;
+	NmtEditorGrid *grid;
+	NMSettingPpp *s_ppp;
+	NmtNewtWidget *widget, *use_mppe;
+	NmtNewtGrid *auth_grid, *mppe_grid;
+	NmtNewtSection *auth_section, *mppe_section;
+	NMConnection *conn;
+
+	conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ppp));
+	s_ppp = nm_connection_get_setting_ppp (conn);
+	if (s_ppp) {
+		priv->lcp_echo_interval = nm_setting_ppp_get_lcp_echo_interval (s_ppp);
+		priv->lcp_echo_failure = nm_setting_ppp_get_lcp_echo_failure (s_ppp);
+	} else {
+		s_ppp = (NMSettingPpp *) nm_setting_ppp_new ();
+		nm_connection_add_setting (conn, (NMSetting *) s_ppp);
+
+		priv->lcp_echo_interval = 30;
+		priv->lcp_echo_failure = 5;
+	}
+
+	section = nmt_editor_section_new (_("PPP CONFIGURATION"), NULL, TRUE);
+	grid = nmt_editor_section_get_body (section);
+
+	/* Auth methods */
+	widget = nmt_newt_section_new (FALSE);
+	auth_section = NMT_NEWT_SECTION (widget);
+	g_object_set (auth_section, "open", TRUE, NULL);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+	widget = nmt_newt_label_new (_("Allowed authentication methods:"));
+	nmt_newt_section_set_header (auth_section, widget);
+
+	widget = nmt_newt_grid_new ();
+	auth_grid = NMT_NEWT_GRID (widget);
+	nmt_newt_section_set_body (auth_section, widget);
+
+	widget = nmt_newt_checkbox_new (_("EAP"));
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_REFUSE_EAP,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_INVERT_BOOLEAN |
+	                        G_BINDING_SYNC_CREATE);
+	nmt_newt_grid_add (auth_grid, widget, 0, 0);
+
+	widget = nmt_newt_checkbox_new (_("PAP"));
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_REFUSE_PAP,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_INVERT_BOOLEAN |
+	                        G_BINDING_SYNC_CREATE);
+	nmt_newt_grid_add (auth_grid, widget, 0, 1);
+
+	widget = nmt_newt_checkbox_new (_("CHAP"));
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_REFUSE_CHAP,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_INVERT_BOOLEAN |
+	                        G_BINDING_SYNC_CREATE);
+	nmt_newt_grid_add (auth_grid, widget, 0, 2);
+
+	widget = nmt_newt_checkbox_new (_("MSCHAPv2"));
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_REFUSE_MSCHAPV2,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_INVERT_BOOLEAN |
+	                        G_BINDING_SYNC_CREATE);
+	nmt_newt_grid_add (auth_grid, widget, 0, 3);
+
+	widget = nmt_newt_checkbox_new (_("MSCHAP"));
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_REFUSE_MSCHAP,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_INVERT_BOOLEAN |
+	                        G_BINDING_SYNC_CREATE);
+	nmt_newt_grid_add (auth_grid, widget, 0, 4);
+
+	nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+	/* MPPE */
+	widget = nmt_newt_section_new (FALSE);
+	mppe_section = NMT_NEWT_SECTION (widget);
+	g_object_set (mppe_section, "open", TRUE, NULL);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+	widget = nmt_newt_checkbox_new (_("Use point-to-point encryption (MPPE)"));
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_REQUIRE_MPPE,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_SYNC_CREATE);
+	use_mppe = widget;
+	nmt_newt_section_set_header (mppe_section, widget);
+
+	widget = nmt_newt_grid_new ();
+	mppe_grid = NMT_NEWT_GRID (widget);
+	nmt_newt_section_set_body (mppe_section, widget);
+
+	widget = nmt_newt_checkbox_new (_("Require 128-bit encryption"));
+	g_object_bind_property (use_mppe, "active",
+	                        widget, "sensitive",
+	                        G_BINDING_SYNC_CREATE);
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_REQUIRE_MPPE_128,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_SYNC_CREATE);
+	nmt_newt_grid_add (mppe_grid, widget, 0, 0);
+
+	widget = nmt_newt_checkbox_new (_("Use stateful MPPE"));
+	g_object_bind_property (use_mppe, "active",
+	                        widget, "sensitive",
+	                        G_BINDING_SYNC_CREATE);
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_MPPE_STATEFUL,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_SYNC_CREATE);
+	nmt_newt_grid_add (mppe_grid, widget, 0, 1);
+
+	nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+	widget = nmt_newt_checkbox_new (_("Allow BSD data compression"));
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_NOBSDCOMP,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_INVERT_BOOLEAN |
+	                        G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+	widget = nmt_newt_checkbox_new (_("Allow Deflate data compression"));
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_NODEFLATE,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_INVERT_BOOLEAN |
+	                        G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+	widget = nmt_newt_checkbox_new (_("Use TCP header compression"));
+	g_object_bind_property (s_ppp, NM_SETTING_PPP_NO_VJ_COMP,
+	                        widget, "active",
+	                        G_BINDING_BIDIRECTIONAL |
+	                        G_BINDING_INVERT_BOOLEAN |
+	                        G_BINDING_SYNC_CREATE);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+	nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+	widget = nmt_newt_checkbox_new (_("Send PPP echo packets"));
+	g_object_bind_property_full (s_ppp, NM_SETTING_PPP_LCP_ECHO_INTERVAL,
+	                             widget, "active",
+	                             G_BINDING_BIDIRECTIONAL |
+	                             G_BINDING_SYNC_CREATE,
+	                             transform_lcp_echo_properties_to_checkbox,
+	                             transform_checkbox_to_lcp_echo_interval,
+	                             ppp, NULL);
+	g_object_bind_property_full (s_ppp, NM_SETTING_PPP_LCP_ECHO_FAILURE,
+	                             widget, "active",
+	                             G_BINDING_BIDIRECTIONAL |
+	                             G_BINDING_SYNC_CREATE,
+	                             transform_lcp_echo_properties_to_checkbox,
+	                             transform_checkbox_to_lcp_echo_failure,
+	                             ppp, NULL);
+	nmt_editor_grid_append (grid, NULL, widget, NULL);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (ppp), section);
+
+	G_OBJECT_CLASS (nmt_page_ppp_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_ppp_class_init (NmtPagePppClass *ppp_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (ppp_class);
+
+	g_type_class_add_private (object_class, sizeof (NmtPagePppPrivate));
+
+	object_class->constructed = nmt_page_ppp_constructed;
+}
diff --git a/clients/tui/nmt-page-ppp.h b/clients/tui/nmt-page-ppp.h
new file mode 100644
index 00000000..79103232
--- /dev/null
+++ b/clients/tui/nmt-page-ppp.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2014 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_PPP_H
+#define NMT_PAGE_PPP_H
+
+#include "nmt-editor-page.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PAGE_PPP            (nmt_page_ppp_get_type ())
+#define NMT_PAGE_PPP(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_PPP, NmtPagePpp))
+#define NMT_PAGE_PPP_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_PPP, NmtPagePppClass))
+#define NMT_IS_PAGE_PPP(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_PPP))
+#define NMT_IS_PAGE_PPP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_PPP))
+#define NMT_PAGE_PPP_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_PPP, NmtPagePppClass))
+
+typedef struct {
+	NmtEditorPage parent;
+
+} NmtPagePpp;
+
+typedef struct {
+	NmtEditorPageClass parent;
+
+} NmtPagePppClass;
+
+GType nmt_page_ppp_get_type (void);
+
+NmtEditorPage *nmt_page_ppp_new (NMConnection *conn);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_PPP_H */
diff --git a/clients/tui/nmt-page-team-port.c b/clients/tui/nmt-page-team-port.c
new file mode 100644
index 00000000..645dc9d3
--- /dev/null
+++ b/clients/tui/nmt-page-team-port.c
@@ -0,0 +1,129 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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;
+
+NmtEditorPage *
+nmt_page_team_port_new (NMConnection *conn)
+{
+	return g_object_new (NMT_TYPE_PAGE_TEAM_PORT,
+	                     "connection", conn,
+	                     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);
+	NmtEditorSection *section;
+	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;
+
+	section = nmt_editor_section_new (_("TEAM PORT"), NULL, TRUE);
+
+	widget = nmt_newt_grid_new ();
+	nmt_editor_grid_append (nmt_editor_section_get_body (section), 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);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (team), section);
+
+	G_OBJECT_CLASS (nmt_page_team_port_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_team_port_class_init (NmtPageTeamPortClass *team_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (team_class);
+
+	g_type_class_add_private (team_class, sizeof (NmtPageTeamPortPrivate));
+
+	object_class->constructed = nmt_page_team_port_constructed;
+}
diff --git a/clients/tui/nmt-page-team-port.h b/clients/tui/nmt-page-team-port.h
new file mode 100644
index 00000000..2d8fa121
--- /dev/null
+++ b/clients/tui/nmt-page-team-port.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_TEAM_PORT_H
+#define NMT_PAGE_TEAM_PORT_H
+
+#include "nmt-editor-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);
+
+NmtEditorPage *nmt_page_team_port_new (NMConnection *conn);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_TEAM_PORT_H */
diff --git a/clients/tui/nmt-page-team.c b/clients/tui/nmt-page-team.c
new file mode 100644
index 00000000..e520b02a
--- /dev/null
+++ b/clients/tui/nmt-page-team.c
@@ -0,0 +1,193 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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_EDITOR_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;
+
+NmtEditorPage *
+nmt_page_team_new (NMConnection   *conn,
+                   NmtDeviceEntry *deventry)
+{
+	return g_object_new (NMT_TYPE_PAGE_TEAM,
+	                     "connection", conn,
+	                     "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);
+	NmtEditorSection *section;
+	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;
+
+	section = nmt_editor_section_new (_("TEAM"), NULL, TRUE);
+
+	widget = nmt_newt_grid_new ();
+	nmt_editor_grid_append (nmt_editor_section_get_body (section), 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);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (team), section);
+
+	G_OBJECT_CLASS (nmt_page_team_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_team_class_init (NmtPageTeamClass *team_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (team_class);
+
+	g_type_class_add_private (team_class, sizeof (NmtPageTeamPrivate));
+
+	object_class->constructed = nmt_page_team_constructed;
+}
diff --git a/clients/tui/nmt-page-team.h b/clients/tui/nmt-page-team.h
new file mode 100644
index 00000000..2d8bf07a
--- /dev/null
+++ b/clients/tui/nmt-page-team.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_TEAM_H
+#define NMT_PAGE_TEAM_H
+
+#include "nmt-editor-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 {
+	NmtEditorPageDevice parent;
+
+} NmtPageTeam;
+
+typedef struct {
+	NmtEditorPageDeviceClass parent;
+
+} NmtPageTeamClass;
+
+GType nmt_page_team_get_type (void);
+
+NmtEditorPage *nmt_page_team_new (NMConnection   *conn,
+                                  NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_TEAM_H */
diff --git a/clients/tui/nmt-page-vlan.c b/clients/tui/nmt-page-vlan.c
new file mode 100644
index 00000000..9b9c8f52
--- /dev/null
+++ b/clients/tui/nmt-page-vlan.c
@@ -0,0 +1,158 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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-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_EDITOR_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;
+
+NmtEditorPage *
+nmt_page_vlan_new (NMConnection   *conn,
+                   NmtDeviceEntry *deventry)
+{
+	return g_object_new (NMT_TYPE_PAGE_VLAN,
+	                     "connection", conn,
+	                     "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);
+	NmtEditorSection *section;
+	NmtEditorGrid *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);
+
+	section = nmt_editor_section_new (_("VLAN"), NULL, TRUE);
+	grid = nmt_editor_section_get_body (section);
+
+	nm_editor_bind_vlan_name (s_vlan, nm_connection_get_setting_connection (conn));
+
+	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_editor_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_editor_grid_append (grid, _("VLAN id"), widget, NULL);
+
+	nmt_editor_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_editor_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_editor_grid_append (grid, _("MTU"), widget, NULL);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (vlan), section);
+
+	G_OBJECT_CLASS (nmt_page_vlan_parent_class)->constructed (object);
+}
+
+static void
+nmt_page_vlan_finalize (GObject *object)
+{
+	NmtPageVlanPrivate *priv = NMT_PAGE_VLAN_GET_PRIVATE (object);
+
+	g_clear_object (&priv->s_wired);
+
+	G_OBJECT_CLASS (nmt_page_vlan_parent_class)->finalize (object);
+}
+
+static void
+nmt_page_vlan_class_init (NmtPageVlanClass *vlan_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (vlan_class);
+
+	g_type_class_add_private (vlan_class, sizeof (NmtPageVlanPrivate));
+
+	/* virtual methods */
+	object_class->constructed = nmt_page_vlan_constructed;
+	object_class->finalize    = nmt_page_vlan_finalize;
+}
diff --git a/clients/tui/nmt-page-vlan.h b/clients/tui/nmt-page-vlan.h
new file mode 100644
index 00000000..db7c4098
--- /dev/null
+++ b/clients/tui/nmt-page-vlan.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_VLAN_H
+#define NMT_PAGE_VLAN_H
+
+#include "nmt-editor-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 {
+	NmtEditorPageDevice parent;
+
+} NmtPageVlan;
+
+typedef struct {
+	NmtEditorPageDeviceClass parent;
+
+} NmtPageVlanClass;
+
+GType nmt_page_vlan_get_type (void);
+
+NmtEditorPage *nmt_page_vlan_new (NMConnection   *conn,
+                                  NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_VLAN_H */
diff --git a/clients/tui/nmt-page-wifi.c b/clients/tui/nmt-page-wifi.c
new file mode 100644
index 00000000..9cd20abf
--- /dev/null
+++ b/clients/tui/nmt-page-wifi.c
@@ -0,0 +1,400 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 "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_EDITOR_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;
+
+NmtEditorPage *
+nmt_page_wifi_new (NMConnection   *conn,
+                   NmtDeviceEntry *deventry)
+{
+	return g_object_new (NMT_TYPE_PAGE_WIFI,
+	                     "connection", conn,
+	                     "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)
+{
+	GBytes *ssid;
+	char *utf8;
+
+	ssid = g_value_get_boxed (source_value);
+	if (ssid)
+		utf8 = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
+		                              g_bytes_get_size (ssid));
+	else
+		utf8 = g_strdup ("");
+	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;
+	GBytes *old_ssid, *ssid;
+	char *utf8;
+
+	text = g_value_get_string (source_value);
+
+	old_ssid = nm_setting_wireless_get_ssid (s_wireless);
+	if (old_ssid)
+		utf8 = nm_utils_ssid_to_utf8 (g_bytes_get_data (old_ssid, NULL),
+		                              g_bytes_get_size (old_ssid));
+	else
+		utf8 = g_strdup ("");
+
+	if (!g_strcmp0 (text, utf8)) {
+		g_free (utf8);
+		return FALSE;
+	}
+	g_free (utf8);
+
+	ssid = g_bytes_new (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;
+	NmtEditorSection *section;
+	NmtEditorGrid *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_editor_page_device_get_device_entry (NMT_EDITOR_PAGE_DEVICE (object));
+	g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS,
+	                        deventry, "mac-address",
+	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+	section = nmt_editor_section_new (_("WI-FI"), NULL, TRUE);
+	grid = nmt_editor_section_get_body (section);
+
+	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_editor_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_editor_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_editor_grid_append (grid, _("Channel"), hbox, NULL);
+
+	nmt_editor_grid_append (grid, NULL, nmt_newt_separator_new (), NULL);
+
+	widget = nmt_newt_popup_new (wifi_security);
+	nmt_editor_grid_append (grid, _("Security"), widget, NULL);
+	security = widget;
+
+	widget = nmt_newt_stack_new ();
+	stack = NMT_NEWT_STACK (widget);
+
+	/* none */
+	subgrid = nmt_editor_grid_new ();
+	nmt_newt_stack_add (stack, "none", subgrid);
+
+	/* wpa-personal */
+	subgrid = nmt_editor_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_editor_grid_append (NMT_EDITOR_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_editor_grid_new ();
+
+	widget = entry = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD);
+	nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Key"), widget, NULL);
+
+	widget = nmt_newt_popup_new (wep_index);
+	nmt_editor_grid_append (NMT_EDITOR_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_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Authentication"), widget, NULL);
+
+	nmt_newt_stack_add (stack, "wep-key", subgrid);
+
+	/* wep-passphrase */
+	subgrid = nmt_editor_grid_new ();
+
+	widget = entry = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD);
+	nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Password"), widget, NULL);
+
+	widget = nmt_newt_popup_new (wep_index);
+	nmt_editor_grid_append (NMT_EDITOR_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_editor_grid_append (NMT_EDITOR_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_editor_grid_new ();
+
+	widget = nmt_newt_entry_new (40, NMT_NEWT_ENTRY_NONEMPTY);
+	nmt_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Username"), widget, NULL);
+	g_object_bind_property (s_wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_USERNAME,
+	                        widget, "text",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+	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_editor_grid_append (NMT_EDITOR_GRID (subgrid), _("Password"), widget, NULL);
+
+	nmt_newt_stack_add (stack, "leap", subgrid);
+
+	nmt_editor_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_editor_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_editor_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_editor_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_editor_grid_append (grid, _("MTU"), widget, NULL);
+
+	nmt_editor_page_add_section (NMT_EDITOR_PAGE (wifi), section);
+
+	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)->finalize (object);
+}
+
+
+static void
+nmt_page_wifi_class_init (NmtPageWifiClass *wifi_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (wifi_class);
+
+	g_type_class_add_private (wifi_class, sizeof (NmtPageWifiPrivate));
+
+	object_class->constructed = nmt_page_wifi_constructed;
+	object_class->finalize    = nmt_page_wifi_finalize;
+}
diff --git a/clients/tui/nmt-page-wifi.h b/clients/tui/nmt-page-wifi.h
new file mode 100644
index 00000000..7ce50584
--- /dev/null
+++ b/clients/tui/nmt-page-wifi.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PAGE_WIFI_H
+#define NMT_PAGE_WIFI_H
+
+#include "nmt-editor-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 {
+	NmtEditorPageDevice parent;
+
+} NmtPageWifi;
+
+typedef struct {
+	NmtEditorPageDeviceClass parent;
+
+} NmtPageWifiClass;
+
+GType nmt_page_wifi_get_type (void);
+
+NmtEditorPage *nmt_page_wifi_new (NMConnection   *conn,
+                                  NmtDeviceEntry *deventry);
+
+G_END_DECLS
+
+#endif /* NMT_PAGE_WIFI_H */
diff --git a/clients/tui/nmt-password-dialog.c b/clients/tui/nmt-password-dialog.c
new file mode 100644
index 00000000..aeece121
--- /dev/null
+++ b/clients/tui/nmt-password-dialog.c
@@ -0,0 +1,345 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-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 "nm-secret-agent-simple.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 #NMSecretAgentSimple
+ * @title: the dialog title
+ * @prompt: the prompt text to display
+ * @secrets: (element-type #NMSecretAgentSimpleSecret): the secrets requested
+ *
+ * Creates a new #NmtPasswordDialog to request passwords from
+ * the user.
+ *
+ * Returns: a new #NmtPasswordDialog.
+ */
+NmtNewtForm *
+nmt_password_dialog_new (const char *request_id,
+                         const char *title,
+                         const char *prompt,
+                         GPtrArray  *secrets)
+{
+	return g_object_new (NMT_TYPE_PASSWORD_DIALOG,
+	                     "request-id", request_id,
+	                     "title", title,
+	                     "prompt", prompt,
+	                     "secrets", secrets,
+	                     "escape-exits", TRUE,
+	                     NULL);
+}
+
+static void
+nmt_password_dialog_init (NmtPasswordDialog *dialog)
+{
+	NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+	priv->entries = g_ptr_array_new ();
+}
+
+static void
+maybe_save_input_and_exit (NmtNewtWidget *widget,
+                           gpointer       dialog)
+{
+	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++) {
+		NMSecretAgentSimpleSecret *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++) {
+		NMSecretAgentSimpleSecret *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 #NMSecretAgentSimple
+	 */
+	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: #NMSecretAgentSimpleSecret.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_SECRETS,
+		 g_param_spec_boxed ("secrets", "", "",
+		                     G_TYPE_PTR_ARRAY,
+		                     G_PARAM_READWRITE |
+		                     G_PARAM_CONSTRUCT_ONLY |
+		                     G_PARAM_STATIC_STRINGS));
+}
+
+/**
+ * nmt_password_dialog_succeeded:
+ * @dialog: the #NmtPasswordDialog
+ *
+ * After the dialog has exited, returns %TRUE if the user clicked
+ * "OK", %FALSE if "Cancel".
+ *
+ * Returns: whether the dialog succeeded.
+ */
+gboolean
+nmt_password_dialog_succeeded (NmtPasswordDialog *dialog)
+{
+	NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+	return priv->succeeded;
+}
+
+/**
+ * nmt_password_dialog_get_request_id:
+ * @dialog: the #NmtPasswordDialog
+ *
+ * Gets the dialog's request ID.
+ *
+ * Returns: the dialog's request ID.
+ */
+const char *
+nmt_password_dialog_get_request_id (NmtPasswordDialog *dialog)
+{
+	NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+	return priv->request_id;
+}
+
+/**
+ * nmt_password_dialog_get_secrets:
+ * @dialog: the #NmtPasswordDialog
+ *
+ * Gets the dialog's secrets array.
+ *
+ * Returns: (transfer none): the dialog's secrets array.
+ */
+GPtrArray *
+nmt_password_dialog_get_secrets (NmtPasswordDialog *dialog)
+{
+	NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog);
+
+	return priv->secrets;
+}
diff --git a/clients/tui/nmt-password-dialog.h b/clients/tui/nmt-password-dialog.h
new file mode 100644
index 00000000..36c9f51e
--- /dev/null
+++ b/clients/tui/nmt-password-dialog.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PASSWORD_DIALOG_H
+#define NMT_PASSWORD_DIALOG_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PASSWORD_DIALOG            (nmt_password_dialog_get_type ())
+#define NMT_PASSWORD_DIALOG(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialog))
+#define NMT_PASSWORD_DIALOG_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass))
+#define NMT_IS_PASSWORD_DIALOG(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PASSWORD_DIALOG))
+#define NMT_IS_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PASSWORD_DIALOG))
+#define NMT_PASSWORD_DIALOG_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass))
+
+typedef struct {
+	NmtNewtForm parent;
+
+} NmtPasswordDialog;
+
+typedef struct {
+	NmtNewtFormClass parent;
+
+} NmtPasswordDialogClass;
+
+GType nmt_password_dialog_get_type (void);
+
+NmtNewtForm *nmt_password_dialog_new            (const char        *request_id,
+                                                 const char        *title,
+                                                 const char        *prompt,
+                                                 GPtrArray         *secrets);
+
+gboolean     nmt_password_dialog_succeeded      (NmtPasswordDialog *dialog);
+
+const char  *nmt_password_dialog_get_request_id (NmtPasswordDialog *dialog);
+GPtrArray   *nmt_password_dialog_get_secrets    (NmtPasswordDialog *dialog);
+
+G_END_DECLS
+
+#endif /* NMT_PASSWORD_DIALOG_H */
diff --git a/clients/tui/nmt-password-fields.c b/clients/tui/nmt-password-fields.c
new file mode 100644
index 00000000..7ef037b9
--- /dev/null
+++ b/clients/tui/nmt-password-fields.c
@@ -0,0 +1,319 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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_bind_property (priv->entry, "text",
+	                        object, "password",
+	                        G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+	G_OBJECT_CLASS (nmt_password_fields_parent_class)->constructed (object);
+}
+
+static void
+nmt_password_fields_finalize (GObject *object)
+{
+	NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (object);
+
+	if (priv->always_ask) {
+		g_signal_handlers_disconnect_by_func (priv->always_ask,
+		                                      G_CALLBACK (always_ask_changed), object);
+	}
+	if (priv->show_password) {
+		g_signal_handlers_disconnect_by_func (priv->show_password,
+		                                      G_CALLBACK (show_password_changed), object);
+	}
+
+	G_OBJECT_CLASS (nmt_password_fields_parent_class)->finalize (object);
+}
+
+static void
+nmt_password_fields_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+	NmtPasswordFields *fields = NMT_PASSWORD_FIELDS (object);
+	NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields);
+
+	switch (prop_id) {
+	case PROP_WIDTH:
+		nmt_newt_entry_set_width (priv->entry, g_value_get_int (value));
+		break;
+	case PROP_EXTRAS:
+		priv->extras = g_value_get_uint (value);
+		nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (fields));
+		break;
+	case PROP_PASSWORD:
+		nmt_password_fields_set_password (fields, g_value_get_string (value));
+		break;
+	case PROP_ALWAYS_ASK:
+		if (priv->always_ask)
+			nmt_newt_checkbox_set_active (priv->always_ask, g_value_get_boolean (value));
+		break;
+	case PROP_SHOW_PASSWORD:
+		if (priv->show_password)
+			nmt_newt_checkbox_set_active (priv->show_password, g_value_get_boolean (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_password_fields_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+	NmtPasswordFields *entry = NMT_PASSWORD_FIELDS (object);
+	NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (entry);
+
+	switch (prop_id) {
+	case PROP_WIDTH:
+		g_value_set_int (value, nmt_newt_entry_get_width (priv->entry));
+		break;
+	case PROP_EXTRAS:
+		g_value_set_uint (value, priv->extras);
+		break;
+	case PROP_PASSWORD:
+		g_value_set_string (value, nmt_password_fields_get_password (entry));
+		break;
+	case PROP_ALWAYS_ASK:
+		if (priv->always_ask)
+			g_value_set_boolean (value, nmt_newt_checkbox_get_active (priv->always_ask));
+		break;
+	case PROP_SHOW_PASSWORD:
+		if (priv->show_password)
+			g_value_set_boolean (value, nmt_newt_checkbox_get_active (priv->show_password));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_password_fields_class_init (NmtPasswordFieldsClass *entry_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+	g_type_class_add_private (entry_class, sizeof (NmtPasswordFieldsPrivate));
+
+	/* virtual methods */
+	object_class->constructed  = nmt_password_fields_constructed;
+	object_class->set_property = nmt_password_fields_set_property;
+	object_class->get_property = nmt_password_fields_get_property;
+	object_class->finalize     = nmt_password_fields_finalize;
+
+	/**
+	 * NmtPasswordFields:width:
+	 *
+	 * The width in characters of the password entry
+	 */
+	g_object_class_install_property
+		(object_class, PROP_WIDTH,
+		 g_param_spec_int ("width", "", "",
+		                   -1, 80, -1,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtPasswordFields:extras:
+	 *
+	 * The extra widgets to show
+	 */
+	g_object_class_install_property
+		(object_class, PROP_EXTRAS,
+		 g_param_spec_uint ("extras", "", "",
+		                    0, 0xFFFF, 0,
+		                    G_PARAM_READWRITE |
+		                    G_PARAM_CONSTRUCT_ONLY |
+		                    G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtPasswordFields:password:
+	 *
+	 * The entered password.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_PASSWORD,
+		 g_param_spec_string ("password", "", "",
+		                      NULL,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtPasswordFields:always-ask:
+	 *
+	 * The current state of the "Always ask" checkbox.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ALWAYS_ASK,
+		 g_param_spec_boolean ("always-ask", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtPasswordFields:show-password:
+	 *
+	 * The current state of the "Show password" checkbox.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_SHOW_PASSWORD,
+		 g_param_spec_boolean ("show-password", "", "",
+		                       FALSE,
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-password-fields.h b/clients/tui/nmt-password-fields.h
new file mode 100644
index 00000000..2ef830aa
--- /dev/null
+++ b/clients/tui/nmt-password-fields.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_PASSWORD_FIELDS_H
+#define NMT_PASSWORD_FIELDS_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_PASSWORD_FIELDS            (nmt_password_fields_get_type ())
+#define NMT_PASSWORD_FIELDS(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFields))
+#define NMT_PASSWORD_FIELDS_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass))
+#define NMT_IS_PASSWORD_FIELDS(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PASSWORD_FIELDS))
+#define NMT_IS_PASSWORD_FIELDS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PASSWORD_FIELDS))
+#define NMT_PASSWORD_FIELDS_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass))
+
+typedef struct {
+	NmtNewtGrid parent;
+
+} NmtPasswordFields;
+
+typedef struct {
+	NmtNewtGridClass parent;
+
+} NmtPasswordFieldsClass;
+
+GType nmt_password_fields_get_type (void);
+
+typedef enum {
+	NMT_PASSWORD_FIELDS_ALWAYS_ASK    = (1 << 0),
+	NMT_PASSWORD_FIELDS_SHOW_PASSWORD = (1 << 1),
+} NmtPasswordFieldsExtras;
+
+NmtNewtWidget *nmt_password_fields_new (int                     width,
+                                        NmtPasswordFieldsExtras extras);
+
+G_END_DECLS
+
+#endif /* NMT_PASSWORD_FIELDS_H */
diff --git a/clients/tui/nmt-route-editor.c b/clients/tui/nmt-route-editor.c
new file mode 100644
index 00000000..77bbec76
--- /dev/null
+++ b/clients/tui/nmt-route-editor.c
@@ -0,0 +1,210 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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);
+	GPtrArray *routes;
+
+	g_object_get (priv->edit_setting,
+	              NM_SETTING_IP_CONFIG_ROUTES, &routes,
+	              NULL);
+	g_object_set (priv->orig_setting,
+	              NM_SETTING_IP_CONFIG_ROUTES, routes,
+	              NULL);
+	g_ptr_array_unref (routes);
+
+	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);
+	else
+		routes = nmt_route_table_new (AF_INET6);
+	g_object_bind_property (priv->edit_setting, NM_SETTING_IP_CONFIG_ROUTES,
+	                        routes, "routes",
+	                        G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
+
+	vbox = nmt_newt_grid_new ();
+	nmt_newt_grid_add (NMT_NEWT_GRID (vbox), routes, 0, 0);
+
+	buttons = nmt_newt_grid_new ();
+	nmt_newt_grid_add (NMT_NEWT_GRID (vbox), buttons, 0, 1);
+	nmt_newt_widget_set_padding (buttons, 0, 1, 0, 0);
+
+	cancel = g_object_ref_sink (nmt_newt_button_new (_("Cancel")));
+	nmt_newt_widget_set_exit_on_activate (cancel, TRUE);
+	nmt_newt_grid_add (NMT_NEWT_GRID (buttons), cancel, 0, 0);
+	nmt_newt_grid_set_flags (NMT_NEWT_GRID (buttons), cancel,
+	                         NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_ANCHOR_RIGHT |
+	                         NMT_NEWT_GRID_FILL_Y);
+
+	ok = g_object_ref_sink (nmt_newt_button_new (_("OK")));
+	g_signal_connect (ok, "clicked", G_CALLBACK (save_routes_and_exit), editor);
+	nmt_newt_grid_add (NMT_NEWT_GRID (buttons), ok, 1, 0);
+	nmt_newt_widget_set_padding (ok, 1, 0, 0, 0);
+	g_object_bind_property (routes, "valid",
+	                        ok, "sensitive",
+	                        G_BINDING_SYNC_CREATE);
+
+	nmt_newt_form_set_content (NMT_NEWT_FORM (editor), vbox);
+}
+
+static void
+nmt_route_editor_finalize (GObject *object)
+{
+	NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object);
+
+	g_clear_object (&priv->orig_setting);
+	g_clear_object (&priv->edit_setting);
+
+	G_OBJECT_CLASS (nmt_route_editor_parent_class)->finalize (object);
+}
+
+static void
+nmt_route_editor_set_property (GObject      *object,
+                               guint         prop_id,
+                               const GValue *value,
+                               GParamSpec   *pspec)
+{
+	NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_SETTING:
+		priv->orig_setting = g_value_dup_object (value);
+		priv->edit_setting = nm_setting_duplicate (priv->orig_setting);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_route_editor_get_property (GObject    *object,
+                               guint       prop_id,
+                               GValue     *value,
+                               GParamSpec *pspec)
+{
+	NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_SETTING:
+		g_value_set_object (value, priv->edit_setting);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_route_editor_class_init (NmtRouteEditorClass *entry_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (entry_class);
+
+	g_type_class_add_private (entry_class, sizeof (NmtRouteEditorPrivate));
+
+	/* virtual methods */
+	object_class->constructed  = nmt_route_editor_constructed;
+	object_class->set_property = nmt_route_editor_set_property;
+	object_class->get_property = nmt_route_editor_get_property;
+	object_class->finalize     = nmt_route_editor_finalize;
+
+	/**
+	 * NmtRouteEditor:setting:
+	 *
+	 * The #NMSettingIP4Config or #NMSettingIP6Config whose routes are
+	 * being edited.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_SETTING,
+		 g_param_spec_object ("setting", "", "",
+		                      NM_TYPE_SETTING,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-route-editor.h b/clients/tui/nmt-route-editor.h
new file mode 100644
index 00000000..9882c713
--- /dev/null
+++ b/clients/tui/nmt-route-editor.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_ROUTE_EDITOR_H
+#define NMT_ROUTE_EDITOR_H
+
+#include <NetworkManager.h>
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_ROUTE_EDITOR            (nmt_route_editor_get_type ())
+#define NMT_ROUTE_EDITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditor))
+#define NMT_ROUTE_EDITOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass))
+#define NMT_IS_ROUTE_EDITOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_EDITOR))
+#define NMT_IS_ROUTE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_EDITOR))
+#define NMT_ROUTE_EDITOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass))
+
+typedef struct {
+	NmtNewtForm parent;
+
+} NmtRouteEditor;
+
+typedef struct {
+	NmtNewtFormClass parent;
+
+} NmtRouteEditorClass;
+
+GType nmt_route_editor_get_type (void);
+
+NmtNewtForm *nmt_route_editor_new (NMSetting *setting);
+
+G_END_DECLS
+
+#endif /* NMT_ROUTE_EDITOR_H */
diff --git a/clients/tui/nmt-route-entry.c b/clients/tui/nmt-route-entry.c
new file mode 100644
index 00000000..3a875520
--- /dev/null
+++ b/clients/tui/nmt-route-entry.c
@@ -0,0 +1,291 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 <NetworkManager.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;
+	NMIPRoute *route;
+} NmtRouteEntryPrivate;
+
+enum {
+	PROP_0,
+	PROP_FAMILY,
+	PROP_IP_ENTRY_WIDTH,
+	PROP_METRIC_ENTRY_WIDTH,
+	PROP_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);
+
+	nm_editor_bind_ip_route_to_strings (priv->family,
+	                                    object, "route",
+	                                    priv->dest, "text",
+	                                    priv->next_hop, "text",
+	                                    priv->metric, "text",
+	                                    G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+	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->route, nm_ip_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_ROUTE:
+		if (priv->route)
+			nm_ip_route_unref (priv->route);
+		priv->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_ROUTE:
+		g_value_set_boxed (value, priv->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:route:
+	 *
+	 * The contents of the entries, as an #NMIPRoute.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ROUTE,
+		 g_param_spec_boxed ("route", "", "",
+		                     nm_ip_route_get_type (),
+		                     G_PARAM_READWRITE |
+		                     G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-route-entry.h b/clients/tui/nmt-route-entry.h
new file mode 100644
index 00000000..d37b14cd
--- /dev/null
+++ b/clients/tui/nmt-route-entry.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_ROUTE_ENTRY_H
+#define NMT_ROUTE_ENTRY_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_ROUTE_ENTRY            (nmt_route_entry_get_type ())
+#define NMT_ROUTE_ENTRY(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntry))
+#define NMT_ROUTE_ENTRY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass))
+#define NMT_IS_ROUTE_ENTRY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_ENTRY))
+#define NMT_IS_ROUTE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_ENTRY))
+#define NMT_ROUTE_ENTRY_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass))
+
+typedef struct {
+	NmtNewtGrid parent;
+
+} NmtRouteEntry;
+
+typedef struct {
+	NmtNewtGridClass parent;
+
+} NmtRouteEntryClass;
+
+GType nmt_route_entry_get_type (void);
+
+NmtNewtWidget *nmt_route_entry_new (int family,
+                                    int ip_entry_width,
+                                    int metric_entry_width);
+
+G_END_DECLS
+
+#endif /* NMT_ROUTE_ENTRY_H */
diff --git a/clients/tui/nmt-route-table.c b/clients/tui/nmt-route-table.c
new file mode 100644
index 00000000..523d8ee3
--- /dev/null
+++ b/clients/tui/nmt-route-table.c
@@ -0,0 +1,334 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 <NetworkManager.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;
+
+	GPtrArray *routes;
+	NmtNewtWidget *list;
+} NmtRouteTablePrivate;
+
+enum {
+	PROP_0,
+	PROP_FAMILY,
+	PROP_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);
+	NMIPRoute *route;
+
+	if (n >= priv->routes->len)
+		return FALSE;
+
+	route = priv->routes->pdata[n];
+	g_value_set_boxed (target_value, route);
+	return TRUE;
+}
+
+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);
+	GPtrArray *routes;
+	NMIPRoute *route;
+
+	if (n >= priv->routes->len)
+		return FALSE;
+	route = priv->routes->pdata[n];
+
+	routes = priv->routes;
+	priv->routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_route_unref);
+
+	if (route)
+		nm_ip_route_unref (route);
+	routes->pdata[n] = 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);
+
+	g_object_bind_property_full (table, "routes",
+	                             entry, "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);
+	NMIPRoute *route;
+
+	if (priv->family == AF_INET)
+		route = nm_ip_route_new (AF_INET, "0.0.0.0", 32, NULL, 0, NULL);
+	else
+		route = nm_ip_route_new (AF_INET6, "::", 128, NULL, 0, NULL);
+	g_ptr_array_add (priv->routes, route);
+	nmt_widget_list_set_length (list, priv->routes->len);
+	g_object_notify (table, "routes");
+}
+
+static void
+remove_route (NmtWidgetList *list,
+              int            num,
+              gpointer       table)
+{
+	NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table);
+	NMIPRoute *route;
+
+	if (num >= priv->routes->len)
+		return;
+
+	route = priv->routes->pdata[num];
+	g_ptr_array_remove_index (priv->routes, num);
+	nmt_widget_list_set_length (list, priv->routes->len);
+
+	g_object_notify (table, "routes");
+}
+
+static void
+nmt_route_table_init (NmtRouteTable *table)
+{
+	NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table);
+	NmtNewtWidget *header, *empty;
+	NmtNewtWidget *dest_prefix_label, *next_hop_label, *metric_label;
+	int dest_prefix_width, next_hop_width, metric_width;
+	char *text;
+
+	priv->routes = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_ip_route_unref);
+
+	header = nmt_newt_grid_new ();
+
+	text = g_strdup_printf ("%s/%s", _("Destination"), _("Prefix"));
+	dest_prefix_width = nmt_newt_text_width (text);
+	dest_prefix_label = g_object_new (NMT_TYPE_NEWT_LABEL,
+	                                  "text", text,
+	                                  "style", NMT_NEWT_LABEL_PLAIN,
+	                                  NULL);
+	g_free (text);
+	nmt_newt_grid_add (NMT_NEWT_GRID (header), dest_prefix_label, 0, 0);
+
+	text = _("Next Hop");
+	next_hop_label = g_object_new (NMT_TYPE_NEWT_LABEL,
+	                               "text", text,
+	                               "style", NMT_NEWT_LABEL_PLAIN,
+	                               NULL);
+	next_hop_width = nmt_newt_text_width (text);
+	nmt_newt_grid_add (NMT_NEWT_GRID (header), next_hop_label, 1, 0);
+
+	text = _("Metric");
+	metric_label = g_object_new (NMT_TYPE_NEWT_LABEL,
+	                             "text", text,
+	                             "style", NMT_NEWT_LABEL_PLAIN,
+	                             NULL);
+	metric_width = nmt_newt_text_width (text);
+	nmt_newt_grid_add (NMT_NEWT_GRID (header), metric_label, 2, 0);
+
+	priv->ip_entry_width = MAX (20, MAX (dest_prefix_width, next_hop_width));
+	priv->metric_entry_width = MAX (7, metric_width);
+
+	nmt_newt_widget_set_padding (dest_prefix_label,
+	                           0, 0, priv->ip_entry_width - dest_prefix_width, 0);
+	nmt_newt_widget_set_padding (next_hop_label,
+	                           2, 0, priv->ip_entry_width - next_hop_width, 0);
+	nmt_newt_widget_set_padding (metric_label,
+	                           2, 0, priv->metric_entry_width - metric_width, 0);
+
+	nmt_newt_grid_add (NMT_NEWT_GRID (table), header, 0, 0);
+
+	empty = nmt_newt_label_new (_("No custom routes are defined."));
+	priv->list = nmt_widget_list_new (create_route_entry, table, NULL, empty);
+	g_signal_connect (priv->list, "add-clicked", G_CALLBACK (add_route), table);
+	g_signal_connect (priv->list, "remove-clicked", G_CALLBACK (remove_route), table);
+	nmt_newt_grid_add (NMT_NEWT_GRID (table), priv->list, 0, 1);
+}
+
+static void
+nmt_route_table_finalize (GObject *object)
+{
+	NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (object);
+
+	g_ptr_array_unref (priv->routes);
+
+	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);
+	GPtrArray *array;
+	int i;
+
+	switch (prop_id) {
+	case PROP_FAMILY:
+		priv->family = g_value_get_int (value);
+		break;
+	case PROP_ROUTES:
+		array = g_value_get_boxed (value);
+		g_ptr_array_set_size (priv->routes, 0);
+		for (i = 0; i < array->len; i++) {
+			nm_ip_route_ref (array->pdata[i]);
+			g_ptr_array_add (priv->routes, array->pdata[i]);
+		}
+		nmt_widget_list_set_length (NMT_WIDGET_LIST (priv->list), priv->routes->len);
+		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_ROUTES:
+		g_value_set_boxed (value, priv->routes);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+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:routes:
+	 *
+	 * The array of routes, suitable for binding to #NMSettingIP4Config:routes
+	 * or #NMSettingIP6Config:routes.
+	 *
+	 * Element-type: NMIPRoute
+	 */
+	g_object_class_install_property
+		(object_class, PROP_ROUTES,
+		 g_param_spec_boxed ("routes", "", "",
+		                     G_TYPE_PTR_ARRAY,
+		                     G_PARAM_READWRITE |
+		                     G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-route-table.h b/clients/tui/nmt-route-table.h
new file mode 100644
index 00000000..217bf40d
--- /dev/null
+++ b/clients/tui/nmt-route-table.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_ROUTE_TABLE_H
+#define NMT_ROUTE_TABLE_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_ROUTE_TABLE            (nmt_route_table_get_type ())
+#define NMT_ROUTE_TABLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTable))
+#define NMT_ROUTE_TABLE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass))
+#define NMT_IS_ROUTE_TABLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_TABLE))
+#define NMT_IS_ROUTE_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_TABLE))
+#define NMT_ROUTE_TABLE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass))
+
+typedef struct {
+	NmtNewtGrid parent;
+
+} NmtRouteTable;
+
+typedef struct {
+	NmtNewtGridClass parent;
+
+} NmtRouteTableClass;
+
+GType nmt_route_table_get_type (void);
+
+NmtNewtWidget *nmt_route_table_new (int family);
+
+G_END_DECLS
+
+#endif /* NMT_ROUTE_TABLE_H */
diff --git a/clients/tui/nmt-slave-list.c b/clients/tui/nmt-slave-list.c
new file mode 100644
index 00000000..9c8596f7
--- /dev/null
+++ b/clients/tui/nmt-slave-list.c
@@ -0,0 +1,266 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 <NetworkManager.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_interface_name (priv->master);
+	if (g_strcmp0 (master, master_ifname) != 0 && g_strcmp0 (master, priv->master_uuid) != 0)
+		return FALSE;
+
+	return TRUE;
+}
+
+static void
+nmt_slave_list_add_connection (NmtEditConnectionList *list)
+{
+	NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (list);
+
+	nmt_add_connection_full (_("Select the type of slave connection you wish to add."), NULL,
+	                         priv->master, priv->type_filter, priv->type_filter_data);
+}
+
+static void
+nmt_slave_list_edit_connection (NmtEditConnectionList *list,
+                                NMConnection          *connection)
+{
+	nmt_edit_connection (connection);
+}
+
+static void
+nmt_slave_list_remove_connection (NmtEditConnectionList  *list,
+                                  NMRemoteConnection     *connection)
+{
+	nmt_remove_connection (connection);
+}
+
+static void
+nmt_slave_list_set_property (GObject      *object,
+                             guint         prop_id,
+                             const GValue *value,
+                             GParamSpec   *pspec)
+{
+	NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MASTER:
+		priv->master = g_value_dup_object (value);
+		if (priv->master) {
+			NMSettingConnection *s_con = nm_connection_get_setting_connection (priv->master);
+
+			priv->master_type = nm_setting_connection_get_connection_type (s_con);
+			priv->master_uuid = nm_setting_connection_get_uuid (s_con);
+		}
+		break;
+	case PROP_TYPE_FILTER:
+		priv->type_filter = g_value_get_pointer (value);
+		break;
+	case PROP_TYPE_FILTER_DATA:
+		priv->type_filter_data = g_value_get_pointer (value);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_slave_list_get_property (GObject    *object,
+                             guint       prop_id,
+                             GValue     *value,
+                             GParamSpec *pspec)
+{
+	NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_MASTER:
+		g_value_set_object (value, priv->master);
+		break;
+	case PROP_TYPE_FILTER:
+		g_value_set_pointer (value, priv->type_filter);
+		break;
+	case PROP_TYPE_FILTER_DATA:
+		g_value_set_pointer (value, priv->type_filter_data);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_slave_list_class_init (NmtSlaveListClass *list_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+	NmtEditConnectionListClass *connection_list_class = NMT_EDIT_CONNECTION_LIST_CLASS (list_class);
+
+	g_type_class_add_private (list_class, sizeof (NmtSlaveListPrivate));
+
+	/* virtual methods */
+	object_class->set_property = nmt_slave_list_set_property;
+	object_class->get_property = nmt_slave_list_get_property;
+	object_class->finalize     = nmt_slave_list_finalize;
+
+	connection_list_class->add_connection    = nmt_slave_list_add_connection;
+	connection_list_class->edit_connection   = nmt_slave_list_edit_connection;
+	connection_list_class->remove_connection = nmt_slave_list_remove_connection;
+
+	/**
+	 * NmtSlaveList:master:
+	 *
+	 * The master #NMConnection whose slaves are being displayed.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_MASTER,
+		 g_param_spec_object ("master", "", "",
+		                      NM_TYPE_CONNECTION,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtSlaveList:type-filter:
+	 *
+	 * If non-%NULL, this will be used to limit the connection types
+	 * that are available when the user clicks on the "Add" button to
+	 * add a new slave. If the filter filters the list down to only a
+	 * single connection type, then the user will not be presented
+	 * with a connection-type dialog, and will instead be immediately
+	 * taken to an editor window for the new slave after clicking
+	 * "Add".
+	 */
+	g_object_class_install_property
+		(object_class, PROP_TYPE_FILTER,
+		 g_param_spec_pointer ("type-filter", "", "",
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtSlaveList:type-filter-data:
+	 *
+	 * User data passed to #NmtSlaveList:type-filter
+	 */
+	g_object_class_install_property
+		(object_class, PROP_TYPE_FILTER_DATA,
+		 g_param_spec_pointer ("type-filter-data", "", "",
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_CONSTRUCT_ONLY |
+		                       G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-slave-list.h b/clients/tui/nmt-slave-list.h
new file mode 100644
index 00000000..d10cd3a7
--- /dev/null
+++ b/clients/tui/nmt-slave-list.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_SLAVE_LIST_H
+#define NMT_SLAVE_LIST_H
+
+#include "nmt-edit-connection-list.h"
+#include "nmtui-edit.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_SLAVE_LIST            (nmt_slave_list_get_type ())
+#define NMT_SLAVE_LIST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveList))
+#define NMT_SLAVE_LIST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass))
+#define NMT_IS_SLAVE_LIST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_SLAVE_LIST))
+#define NMT_IS_SLAVE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_SLAVE_LIST))
+#define NMT_SLAVE_LIST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass))
+
+typedef struct {
+	NmtEditConnectionList parent;
+
+} NmtSlaveList;
+
+typedef struct {
+	NmtEditConnectionListClass parent;
+
+} NmtSlaveListClass;
+
+GType nmt_slave_list_get_type (void);
+
+NmtNewtWidget *nmt_slave_list_new (NMConnection               *master,
+                                   NmtAddConnectionTypeFilter  type_filter,
+                                   gpointer                    type_filter_data);
+
+G_END_DECLS
+
+#endif /* NMT_SLAVE_LIST_H */
diff --git a/clients/tui/nmt-utils.c b/clients/tui/nmt-utils.c
new file mode 100644
index 00000000..f2bf70db
--- /dev/null
+++ b/clients/tui/nmt-utils.c
@@ -0,0 +1,151 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-utils
+ * @short_description: Miscellaneous nmtui-specific utilities
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "nmt-utils.h"
+
+/**
+ * NmtSyncOp:
+ *
+ * A helper object used when synchronously waiting for an asynchronous
+ * operation to complete.
+ *
+ * The caller first does:
+ *
+ * |[
+ *     NmtSyncOp op;
+ *
+ *     nmt_sync_op_init (&op);
+ * ]|
+ *
+ * It then passes the op as the user_data to the async operation's
+ * callback function, and then calls nmt_sync_op_wait_boolean() or
+ * nmt_sync_op_wait_pointer() to wait for the result.
+ *
+ * When the async callback is invoked, it should call
+ * nmt_sync_op_complete_boolean() or nmt_sync_op_complete_pointer() to
+ * return a result or an error to the caller. 
+ *
+ * There is no free/clear function; any memory that needs to be freed
+ * will have been returned to the caller from
+ * nmt_sync_op_wait_boolean() or nmt_sync_op_wait_pointer(), so there
+ * is nothing left that needs to be freed.
+ */
+
+typedef struct {
+	gpointer result;
+	GError *error;
+	gpointer complete;
+} NmtSyncOpReal;
+
+/**
+ * nmt_sync_op_init:
+ * @op: pointer to a stack-allocated #NmtSyncOp
+ *
+ * Initializes @op before use.
+ */
+void
+nmt_sync_op_init (NmtSyncOp *op)
+{
+	memset (op, 0, sizeof (*op));
+}
+
+/**
+ * nmt_sync_op_wait_boolean:
+ * @op: the #NmtSyncOp
+ * @error: return location for a #GError
+ *
+ * This runs the main loop until @op's operation returns, and then
+ * returns the result or error.
+ *
+ * Returns: the result of the operation.
+ */
+gboolean
+nmt_sync_op_wait_boolean (NmtSyncOp  *op,
+                          GError    **error)
+{
+	return GPOINTER_TO_UINT (nmt_sync_op_wait_pointer (op, error));
+}
+
+/**
+ * nmt_sync_op_complete_boolean:
+ * @op: the #NmtSyncOp
+ * @result: the result of the operation
+ * @error: (allow-none): the error, or %NULL
+ *
+ * Completes @op and returns @result and/or @error to the caller.
+ */
+void
+nmt_sync_op_complete_boolean (NmtSyncOp  *op,
+                              gboolean    result,
+                              GError     *error)
+{
+	nmt_sync_op_complete_pointer (op, GUINT_TO_POINTER (result), error);
+}
+
+/**
+ * nmt_sync_op_wait_pointer:
+ * @op: the #NmtSyncOp
+ * @error: return location for a #GError
+ *
+ * This runs the main loop until @op's operation returns, and then
+ * returns the result or error.
+ *
+ * Returns: the result of the operation.
+ */
+gpointer
+nmt_sync_op_wait_pointer (NmtSyncOp  *op,
+                          GError    **error)
+{
+	NmtSyncOpReal *real = (NmtSyncOpReal *)op;
+
+	while (!real->complete)
+		g_main_context_iteration (NULL, TRUE);
+
+	if (real->error)
+		g_propagate_error (error, real->error);
+	return real->result;
+}
+
+/**
+ * nmt_sync_op_complete_pointer:
+ * @op: the #NmtSyncOp
+ * @result: the result of the operation
+ * @error: (allow-none): the error, or %NULL
+ *
+ * Completes @op and returns @result and/or @error to the caller.
+ */
+void
+nmt_sync_op_complete_pointer (NmtSyncOp  *op,
+                              gpointer    result,
+                              GError     *error)
+{
+	NmtSyncOpReal *real = (NmtSyncOpReal *)op;
+
+	real->result = result;
+	real->error = error ? g_error_copy (error) : NULL;
+	real->complete = GUINT_TO_POINTER (TRUE);
+}
diff --git a/clients/tui/nmt-utils.h b/clients/tui/nmt-utils.h
new file mode 100644
index 00000000..7e24fcab
--- /dev/null
+++ b/clients/tui/nmt-utils.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_UTILS_H
+#define NMT_UTILS_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+	gpointer private[3];
+} NmtSyncOp;
+
+void      nmt_sync_op_init             (NmtSyncOp  *op);
+
+gboolean  nmt_sync_op_wait_boolean     (NmtSyncOp  *op,
+                                        GError    **error);
+void      nmt_sync_op_complete_boolean (NmtSyncOp  *op,
+                                        gboolean    result,
+                                        GError     *error);
+
+gpointer  nmt_sync_op_wait_pointer     (NmtSyncOp  *op,
+                                        GError    **error);
+void      nmt_sync_op_complete_pointer (NmtSyncOp  *op,
+                                        gpointer    result,
+                                        GError     *error);
+
+G_END_DECLS
+
+#endif /* NMT_UTILS_H */
diff --git a/clients/tui/nmt-widget-list.c b/clients/tui/nmt-widget-list.c
new file mode 100644
index 00000000..9159ed98
--- /dev/null
+++ b/clients/tui/nmt-widget-list.c
@@ -0,0 +1,496 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-widget-list
+ * @short_description: A list of widgets, with Add and Remove buttons
+ *
+ * #NmtWidgetList presents a homogeneous list of widgets, with "Remove"
+ * buttons next to each one, and an "Add" button at the button to add
+ * new ones.
+ *
+ * It is the base class for #NmtAddressList, and is used internally by
+ * #NmtRouteTable.
+ *
+ * FIXME: The way this works is sort of weird.
+ */
+
+#include "config.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <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);
+	} else
+		nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->add_button), TRUE);
+}
+
+static void
+add_clicked (NmtNewtButton *button, gpointer list)
+{
+	g_signal_emit (G_OBJECT (list), signals[ADD_CLICKED], 0, NULL);
+}
+
+static void
+remove_clicked (NmtNewtButton *button, gpointer list)
+{
+	NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list);
+	int i;
+
+	for (i = 0; i < priv->remove_buttons->len; i++) {
+		if (priv->remove_buttons->pdata[i] == (gpointer)button)
+			break;
+	}
+	g_return_if_fail (i < priv->remove_buttons->len);
+
+	g_signal_emit (G_OBJECT (list), signals[REMOVE_CLICKED], 0, i, NULL);
+}
+
+static NmtNewtWidget *
+nmt_widget_list_real_create_widget (NmtWidgetList *list,
+                                    int            n)
+{
+	NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list);
+
+	g_return_val_if_fail (priv->create_callback != NULL, NULL);
+
+	return priv->create_callback (list, n, priv->user_data);
+}
+
+/**
+ * nmt_widget_list_get_length:
+ * @list: the #NmtNewtWidgetList
+ *
+ * Gets the number of widgets in the list.
+ *
+ * Returns: the number of widgets in the list.
+ */
+int
+nmt_widget_list_get_length (NmtWidgetList *list)
+{
+	NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list);
+
+	return priv->length;
+}
+
+/**
+ * nmt_widget_list_set_length:
+ * @list: the #NmtNewtWidgetList
+ * @length: the new length
+ *
+ * Changes the number of widgets in the list. Widgets will be added or
+ * deleted as necessary.
+ */
+void
+nmt_widget_list_set_length (NmtWidgetList *list,
+                            int            length)
+{
+	NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list);
+
+	if (priv->length != length) {
+		priv->length = length;
+		g_object_notify (G_OBJECT (list), "length");
+	}
+
+	ensure_widgets (list);
+}
+
+static void
+nmt_widget_list_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+	NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_CREATE_CALLBACK:
+		priv->create_callback = g_value_get_pointer (value);
+		break;
+	case PROP_USER_DATA:
+		priv->user_data = g_value_get_pointer (value);
+		break;
+	case PROP_DESTROY_NOTIFY:
+		priv->destroy_notify = g_value_get_pointer (value);
+		break;
+	case PROP_LENGTH:
+		priv->length = g_value_get_int (value);
+		ensure_widgets (NMT_WIDGET_LIST (object));
+		break;
+	case PROP_EMPTY_WIDGET:
+		priv->empty_widget = g_value_get_object (value);
+		if (priv->empty_widget) {
+			g_object_ref_sink (priv->empty_widget);
+			nmt_newt_grid_add (NMT_NEWT_GRID (object), priv->empty_widget, 0, 0);
+		}
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_widget_list_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+	NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object);
+
+	switch (prop_id) {
+	case PROP_CREATE_CALLBACK:
+		g_value_set_pointer (value, priv->create_callback);
+		break;
+	case PROP_USER_DATA:
+		g_value_set_pointer (value, priv->user_data);
+		break;
+	case PROP_DESTROY_NOTIFY:
+		g_value_set_pointer (value, priv->destroy_notify);
+		break;
+	case PROP_LENGTH:
+		g_value_set_int (value, priv->length);
+		break;
+	case PROP_EMPTY_WIDGET:
+		g_value_set_object (value, priv->empty_widget);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+nmt_widget_list_class_init (NmtWidgetListClass *list_class)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (list_class);
+
+	g_type_class_add_private (list_class, sizeof (NmtWidgetListPrivate));
+
+	/* virtual methods */
+	object_class->constructed  = nmt_widget_list_constructed;
+	object_class->set_property = nmt_widget_list_set_property;
+	object_class->get_property = nmt_widget_list_get_property;
+	object_class->finalize     = nmt_widget_list_finalize;
+
+	list_class->create_widget = nmt_widget_list_real_create_widget;
+
+	/* signals */
+
+	/**
+	 * NmtNewtWidget::add-clicked:
+	 * @list: the #NmtNewtWidgetList
+	 *
+	 * Emitted when the user clicks the "Add" button. The caller can
+	 * decide whether or not to add a new widget, and call
+	 * nmt_widget_list_set_length() with the new length if so.
+	 *
+	 * FIXME: the "Add" button should be insensitive if it's
+	 * not going to work.
+	 */
+	signals[ADD_CLICKED] =
+		g_signal_new ("add-clicked",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NmtWidgetListClass, add_clicked),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 0);
+	/**
+	 * NmtNewtWidget::remove-clicked:
+	 * @list: the #NmtNewtWidgetList
+	 * @n: the widget being removed
+	 *
+	 * Emitted when the user clicks one of the "Remove" buttons. The
+	 * caller can decide whether or not to remove the widget, and
+	 * call nmt_widget_list_set_length() with the new length if so.
+	 *
+	 * FIXME: the "Remove" button should be insensitive if it's not
+	 * going to work.
+	 */
+	signals[REMOVE_CLICKED] =
+		g_signal_new ("remove-clicked",
+		              G_OBJECT_CLASS_TYPE (object_class),
+		              G_SIGNAL_RUN_FIRST,
+		              G_STRUCT_OFFSET (NmtWidgetListClass, remove_clicked),
+		              NULL, NULL, NULL,
+		              G_TYPE_NONE, 1, G_TYPE_INT);
+
+	/* properties */
+
+	/**
+	 * NmtWidgetList:create-callback:
+	 *
+	 * Callback called to create a new widget.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_CREATE_CALLBACK,
+		 g_param_spec_pointer ("create-callback", "", "",
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtWidgetList:user-data:
+	 *
+	 * User data for #NmtWidgetList:create-callback
+	 */
+	g_object_class_install_property
+		(object_class, PROP_USER_DATA,
+		 g_param_spec_pointer ("user-data", "", "",
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtWidgetList:destroy-notify:
+	 *
+	 * #GDestroyNotify for #NmtWidgetList:user-data
+	 */
+	g_object_class_install_property
+		(object_class, PROP_DESTROY_NOTIFY,
+		 g_param_spec_pointer ("destroy-notify", "", "",
+		                       G_PARAM_READWRITE |
+		                       G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtWidgetList:length:
+	 *
+	 * The length of the widget list; changing this value will add or
+	 * remove widgets from the list.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_LENGTH,
+		 g_param_spec_int ("length", "", "",
+		                   0, G_MAXINT, 0,
+		                   G_PARAM_READWRITE |
+		                   G_PARAM_STATIC_STRINGS));
+	/**
+	 * NmtWidgetList:empty-widget:
+	 *
+	 * If non-%NULL, this widget will be displayed when there are
+	 * no "real" widgets in the list.
+	 */
+	g_object_class_install_property
+		(object_class, PROP_EMPTY_WIDGET,
+		 g_param_spec_object ("empty-widget", "", "",
+		                      NMT_TYPE_NEWT_WIDGET,
+		                      G_PARAM_READWRITE |
+		                      G_PARAM_CONSTRUCT_ONLY |
+		                      G_PARAM_STATIC_STRINGS));
+}
diff --git a/clients/tui/nmt-widget-list.h b/clients/tui/nmt-widget-list.h
new file mode 100644
index 00000000..7dbeb215
--- /dev/null
+++ b/clients/tui/nmt-widget-list.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMT_WIDGET_LIST_H
+#define NMT_WIDGET_LIST_H
+
+#include "nmt-newt-grid.h"
+
+G_BEGIN_DECLS
+
+#define NMT_TYPE_WIDGET_LIST            (nmt_widget_list_get_type ())
+#define NMT_WIDGET_LIST(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetList))
+#define NMT_WIDGET_LIST_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass))
+#define NMT_IS_WIDGET_LIST(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_WIDGET_LIST))
+#define NMT_IS_WIDGET_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_WIDGET_LIST))
+#define NMT_WIDGET_LIST_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass))
+
+typedef struct {
+	NmtNewtGrid parent;
+
+} NmtWidgetList;
+
+typedef struct {
+	NmtNewtGridClass parent;
+
+	/* signals */
+	void            (*add_clicked)    (NmtWidgetList *list);
+	void            (*remove_clicked) (NmtWidgetList *list,
+	                                   int            n);
+
+	/* methods */
+	NmtNewtWidget * (*create_widget)  (NmtWidgetList *list,
+	                                   int            n);
+
+} NmtWidgetListClass;
+
+GType nmt_widget_list_get_type (void);
+
+typedef NmtNewtWidget * (*NmtWidgetListCallback) (NmtWidgetList *list,
+                                                  int            n,
+                                                  gpointer       user_data);
+
+NmtNewtWidget *nmt_widget_list_new        (NmtWidgetListCallback  create_callback,
+                                           gpointer               user_data,
+                                           GDestroyNotify         destroy_notify,
+                                           NmtNewtWidget         *empty_widget);
+
+int            nmt_widget_list_get_length (NmtWidgetList         *list);
+void           nmt_widget_list_set_length (NmtWidgetList         *list,
+                                           int                    length);
+
+G_END_DECLS
+
+#endif /* NMT_WIDGET_LIST_H */
diff --git a/clients/tui/nmtui-connect.c b/clients/tui/nmtui-connect.c
new file mode 100644
index 00000000..438915d2
--- /dev/null
+++ b/clients/tui/nmtui-connect.c
@@ -0,0 +1,365 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 and deactivating #NMConnections,
+ * including presenting a password dialog if necessary.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "nmt-newt.h"
+
+#include "nmtui.h"
+#include "nmtui-connect.h"
+#include "nmt-connect-connection-list.h"
+#include "nmt-password-dialog.h"
+#include "nm-secret-agent-simple.h"
+#include "nmt-utils.h"
+
+static void
+secrets_requested (NMSecretAgentSimple *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)))
+		nm_secret_agent_simple_response (agent, request_id, secrets);
+	else
+		nm_secret_agent_simple_response (agent, request_id, NULL);
+
+	g_object_unref (form);
+}
+
+static void
+connect_cancelled (NmtNewtForm *form,
+                   gpointer     user_data)
+{
+	NmtSyncOp *op = user_data;
+	GError *error = NULL;
+
+	error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_CANCELLED, "Cancelled");
+	nmt_sync_op_complete_boolean (op, FALSE, error);
+	g_clear_error (&error);
+}
+
+static void
+activate_ac_state_changed (GObject    *object,
+                           GParamSpec *pspec,
+                           gpointer    user_data)
+{
+	NmtSyncOp *op = user_data;
+	NMActiveConnectionState state;
+	GError *error = NULL;
+
+	state = nm_active_connection_get_state (NM_ACTIVE_CONNECTION (object));
+	if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING)
+		return;
+
+	if (state != NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
+		error = g_error_new_literal (NM_CLIENT_ERROR, NM_CLIENT_ERROR_FAILED,
+		                             _("Activation failed"));
+	}
+
+	nmt_sync_op_complete_boolean (op, error == NULL, error);
+	g_clear_error (&error);
+}
+
+static void
+activate_callback (GObject      *client,
+                   GAsyncResult *result,
+                   gpointer      user_data)
+{
+	NmtSyncOp *op = user_data;
+	NMActiveConnection *ac;
+	GError *error = NULL;
+
+	ac = nm_client_activate_connection_finish (NM_CLIENT (client), result, &error);
+	if (error)
+		nmt_sync_op_complete_pointer (op, NULL, error);
+	else
+		nmt_sync_op_complete_pointer (op, ac, NULL);
+}
+
+static void
+add_and_activate_callback (GObject      *client,
+                           GAsyncResult *result,
+                           gpointer      user_data)
+{
+	NmtSyncOp *op = user_data;
+	NMActiveConnection *ac;
+	GError *error = NULL;
+
+	ac = nm_client_add_and_activate_connection_finish (NM_CLIENT (client), result, &error);
+	if (error)
+		nmt_sync_op_complete_pointer (op, NULL, error);
+	else
+		nmt_sync_op_complete_pointer (op, ac, NULL);
+}
+
+static void
+activate_connection (NMConnection *connection,
+                     NMDevice     *device,
+                     NMObject     *specific_object)
+{
+	NmtNewtForm *form;
+	NMSecretAgentOld *agent;
+	NmtNewtWidget *label;
+	NmtSyncOp op;
+	const char *specific_object_path;
+	NMActiveConnection *ac;
+	GError *error = NULL;
+
+	form = g_object_new (NMT_TYPE_NEWT_FORM,
+	                     "escape-exits", TRUE,
+	                     NULL);
+	label = nmt_newt_label_new (_("Connecting..."));
+	nmt_newt_form_set_content (form, label);
+
+	agent = nm_secret_agent_simple_new ("nmtui");
+	if (agent) {
+		if (connection) {
+			nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (agent),
+			                               nm_object_get_path (NM_OBJECT (connection)));
+		}
+		g_signal_connect (agent, "request-secrets", G_CALLBACK (secrets_requested), NULL);
+	}
+
+	specific_object_path = specific_object ? nm_object_get_path (specific_object) : NULL;
+
+	/* There's no way to cancel an nm_client_activate_connection() /
+	 * nm_client_add_and_activate_connection() call, so we always let them
+	 * complete, even if the user hits Esc; they shouldn't normally take long
+	 * to complete anyway.
+	 */
+
+	nmt_sync_op_init (&op);
+	if (connection) {
+		nm_client_activate_connection_async (nm_client,
+		                                     connection, device, specific_object_path,
+		                                     NULL, activate_callback, &op);
+	} else {
+		nm_client_add_and_activate_connection_async (nm_client,
+		                                             NULL, device, specific_object_path,
+		                                             NULL, add_and_activate_callback, &op);
+	}
+
+	nmt_newt_form_show (form);
+
+	ac = nmt_sync_op_wait_pointer (&op, &error);
+	if (!ac) {
+		nmt_newt_message_dialog (_("Could not activate connection: %s"), error->message);
+		g_clear_error (&error);
+		goto done;
+	} else if (nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) {
+		/* Already active */
+		goto done;
+	} else if (!nmt_newt_widget_get_realized (NMT_NEWT_WIDGET (form))) {
+		/* User already hit Esc */
+		goto done;
+	}
+
+	if (!connection) {
+		connection = NM_CONNECTION (nm_active_connection_get_connection (ac));
+		if (connection) {
+			nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (agent),
+			                               nm_object_get_path (NM_OBJECT (connection)));
+		}
+	}
+
+	/* Now wait for the connection to actually reach the ACTIVATED state,
+	 * allowing the user to cancel if it takes too long.
+	 */
+
+	nmt_sync_op_init (&op);
+
+	g_signal_connect (form, "quit", G_CALLBACK (connect_cancelled), &op);
+	g_signal_connect (ac, "notify::" NM_ACTIVE_CONNECTION_STATE,
+	                  G_CALLBACK (activate_ac_state_changed), &op);
+
+	if (!nmt_sync_op_wait_boolean (&op, &error)) {
+		if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+			nmt_newt_message_dialog (_("Could not activate connection: %s"), error->message);
+		g_clear_error (&error);
+	}
+
+	g_signal_handlers_disconnect_by_func (form, G_CALLBACK (connect_cancelled), &op);
+	g_signal_handlers_disconnect_by_func (ac, G_CALLBACK (activate_ac_state_changed), &op);
+
+ done:
+	if (nmt_newt_widget_get_realized (NMT_NEWT_WIDGET (form)))
+		nmt_newt_form_quit (form);
+	g_object_unref (form);
+
+	nm_secret_agent_old_unregister (agent, NULL, NULL);
+	g_object_unref (agent);
+}
+
+static void
+listbox_activated (NmtNewtListbox *listbox,
+                   gpointer        user_data)
+{
+	NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST (listbox);
+	NMConnection *connection;
+	NMDevice *device;
+	NMObject *specific_object;
+	NMActiveConnection *ac;
+
+	if (!nmt_connect_connection_list_get_selection (list,
+	                                                &connection,
+	                                                &device,
+	                                                &specific_object,
+	                                                &ac))
+		return;
+
+	if (ac)
+		nm_client_deactivate_connection (nm_client, ac, NULL, NULL);
+	else
+		activate_connection (connection, device, specific_object);
+}
+
+static void
+activate_clicked (NmtNewtButton *button,
+                  gpointer       listbox)
+{
+	listbox_activated (listbox, NULL);
+}
+
+static void
+listbox_active_changed (GObject    *object,
+                        GParamSpec *pspec,
+                        gpointer    button)
+{
+	NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST (object);
+	static const char *activate, *deactivate;
+	static int deactivate_padding, activate_padding;
+	NMActiveConnection *ac;
+	gboolean has_selection;
+
+	if (G_UNLIKELY (activate == NULL)) {
+		int activate_width, deactivate_width;
+
+		activate = _("Activate");
+		activate_width = nmt_newt_text_width (activate);
+		deactivate = _("Deactivate");
+		deactivate_width = nmt_newt_text_width (deactivate);
+
+		activate_padding = MAX (0, deactivate_width - activate_width);
+		deactivate_padding = MAX (0, activate_width - deactivate_width);
+	}
+
+	has_selection = nmt_connect_connection_list_get_selection (list, NULL, NULL, NULL, &ac);
+
+	nmt_newt_component_set_sensitive (button, has_selection);
+	if (has_selection && ac) {
+		nmt_newt_button_set_label (button, deactivate);
+		nmt_newt_widget_set_padding (button, 0, 0, deactivate_padding, 0);
+	} else {
+		nmt_newt_button_set_label (button, activate);
+		nmt_newt_widget_set_padding (button, 0, 0, activate_padding, 0);
+	}
+}
+
+static NmtNewtForm *
+nmt_connect_connection_list (void)
+{
+	int screen_width, screen_height;
+	NmtNewtForm *form;
+	NmtNewtWidget *list, *activate, *quit, *bbox, *grid;
+
+	newtGetScreenSize (&screen_width, &screen_height);
+
+	form = g_object_new (NMT_TYPE_NEWT_FORM,
+	                     "y", 2,
+	                     "height", screen_height - 4,
+	                     "escape-exits", TRUE,
+	                     NULL);
+
+	grid = nmt_newt_grid_new ();
+
+	list = nmt_connect_connection_list_new ();
+	nmt_newt_grid_add (NMT_NEWT_GRID (grid), list, 0, 0);
+	nmt_newt_grid_set_flags (NMT_NEWT_GRID (grid), list,
+	                         NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y |
+	                         NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_EXPAND_Y);
+	g_signal_connect (list, "activated", G_CALLBACK (listbox_activated), NULL);
+
+	bbox = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_VERTICAL);
+	nmt_newt_grid_add (NMT_NEWT_GRID (grid), bbox, 1, 0);
+	nmt_newt_widget_set_padding (bbox, 1, 1, 0, 1);
+
+	activate = nmt_newt_button_box_add_start (NMT_NEWT_BUTTON_BOX (bbox), _("Activate"));
+	g_signal_connect (list, "notify::active", G_CALLBACK (listbox_active_changed), activate);
+	listbox_active_changed (G_OBJECT (list), NULL, activate);
+	g_signal_connect (activate, "clicked", G_CALLBACK (activate_clicked), list);
+
+	quit = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("Quit"));
+	nmt_newt_widget_set_exit_on_activate (quit, TRUE);
+
+	nmt_newt_form_set_content (form, grid);
+	return form;
+}
+
+static NmtNewtForm *
+nmt_connect_connection (const char *identifier)
+{
+	NmtNewtWidget *list;
+	NMConnection *connection;
+	NMDevice *device;
+	NMObject *specific_object;
+	NMActiveConnection *ac;
+
+	list = nmt_connect_connection_list_new ();
+	if (!nmt_connect_connection_list_get_connection (NMT_CONNECT_CONNECTION_LIST (list),
+	                                                 identifier,
+	                                                 &connection,
+	                                                 &device,
+	                                                 &specific_object,
+	                                                 &ac))
+		nmt_newt_message_dialog (_("No such connection '%s'"), identifier);
+	else if (ac)
+		nmt_newt_message_dialog (_("Connection is already active"));
+	else
+		activate_connection (connection, device, specific_object);
+	g_object_unref (list);
+
+	return NULL;
+}
+
+NmtNewtForm *
+nmtui_connect (int argc, char **argv)
+{
+	if (argc == 2)
+		return nmt_connect_connection (argv[1]);
+	else
+		return nmt_connect_connection_list ();
+}
diff --git a/clients/tui/nmtui-connect.h b/clients/tui/nmtui-connect.h
new file mode 100644
index 00000000..8310ac38
--- /dev/null
+++ b/clients/tui/nmtui-connect.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMTUI_CONNECT_H
+#define NMTUI_CONNECT_H
+
+G_BEGIN_DECLS
+
+NmtNewtForm *nmtui_connect (int argc, char **argv);
+
+G_END_DECLS
+
+#endif /* NMTUI_CONNECT_H */
diff --git a/clients/tui/nmtui-edit.c b/clients/tui/nmtui-edit.c
new file mode 100644
index 00000000..0c3206da
--- /dev/null
+++ b/clients/tui/nmtui-edit.c
@@ -0,0 +1,577 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 <NetworkManager.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;
+	const char *master, *slave_type;
+	const char *uuid, *ifname;
+	const GPtrArray *conns;
+	int i;
+	gboolean found_master = FALSE;
+
+	s_con = nm_connection_get_setting_connection (connection);
+	g_return_val_if_fail (s_con != NULL, FALSE);
+
+	master = nm_setting_connection_get_master (s_con);
+	if (!master)
+		return TRUE;
+	slave_type = nm_setting_connection_get_slave_type (s_con);
+	if (   g_strcmp0 (slave_type, NM_SETTING_BOND_SETTING_NAME) != 0
+	    && g_strcmp0 (slave_type, NM_SETTING_TEAM_SETTING_NAME) != 0
+	    && g_strcmp0 (slave_type, NM_SETTING_BRIDGE_SETTING_NAME) != 0)
+		return TRUE;
+
+	conns = nm_client_get_connections (nm_client);
+	for (i = 0; i < conns->len; i++) {
+		NMConnection *candidate = conns->pdata[i];
+
+		uuid = nm_connection_get_uuid (candidate);
+		ifname = nm_connection_get_interface_name (candidate);
+		if (!g_strcmp0 (master, uuid) || !g_strcmp0 (master, ifname)) {
+			found_master = TRUE;
+			break;
+		}
+	}
+
+	return !found_master;
+}
+
+static NmtNewtForm *
+nmt_edit_main_connection_list (void)
+{
+	int screen_width, screen_height;
+	NmtNewtForm *form;
+	NmtNewtWidget *quit, *list;
+
+	newtGetScreenSize (&screen_width, &screen_height);
+
+	form = g_object_new (NMT_TYPE_NEWT_FORM,
+	                     "y", 2,
+	                     "height", screen_height - 4,
+	                     "escape-exits", TRUE,
+	                     NULL);
+
+	quit = nmt_newt_button_new (_("Quit"));
+	nmt_newt_widget_set_exit_on_activate (quit, TRUE);
+
+	list = g_object_new (NMT_TYPE_EDIT_CONNECTION_LIST,
+	                     "extra-widget", quit,
+	                     "connection-filter", edit_connection_list_filter,
+	                     NULL);
+
+	g_signal_connect (list, "add-connection",
+	                  G_CALLBACK (list_add_connection), form);
+	g_signal_connect (list, "edit-connection",
+	                  G_CALLBACK (list_edit_connection), form);
+	g_signal_connect (list, "remove-connection",
+	                  G_CALLBACK (list_remove_connection), form);
+
+	nmt_newt_form_set_content (form, list);
+	return form;
+}
+
+#define NMT_TYPE_ADD_CONNECTION    (nmt_add_connection_get_type ())
+#define NMT_ADD_CONNECTION(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ADD_CONNECTION, NmtAddConnection))
+#define NMT_IS_ADD_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ADD_CONNECTION))
+
+typedef NmtNewtForm NmtAddConnection;
+typedef NmtNewtFormClass NmtAddConnectionClass;
+
+GType nmt_add_connection_get_type (void);
+
+G_DEFINE_TYPE (NmtAddConnection, nmt_add_connection, NMT_TYPE_NEWT_FORM)
+
+#define NMT_ADD_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ADD_CONNECTION, NmtAddConnectionPrivate))
+
+typedef struct {
+	NmtNewtTextbox *textbox;
+	NmtNewtListbox *listbox;
+
+	char *primary_text;
+	char *secondary_text;
+	NMConnection *master;
+	NmtAddConnectionTypeFilter type_filter;
+	gpointer type_filter_data;
+
+	gboolean single_type;
+} NmtAddConnectionPrivate;
+
+enum {
+	PROP_0,
+
+	PROP_PRIMARY_TEXT,
+	PROP_SECONDARY_TEXT,
+	PROP_MASTER,
+	PROP_TYPE_FILTER,
+	PROP_TYPE_FILTER_DATA,
+
+	LAST_PROP
+};
+
+static void
+create_connection (NmtNewtWidget *widget, gpointer list)
+{
+	NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (list);
+	GType type = (GType) GPOINTER_TO_SIZE (nmt_newt_listbox_get_active_key (priv->listbox));
+	NMConnection *connection;
+
+	connection = nm_editor_utils_create_connection (type, priv->master, nm_client);
+	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;
+	NMRemoteConnection *connection;
+} ConnectionDeleteData;
+
+static void
+connection_deleted_callback (GObject      *connection,
+                             GAsyncResult *result,
+                             gpointer      user_data)
+{
+	ConnectionDeleteData *data = user_data;
+	GError *error = NULL;
+
+	if (!nm_remote_connection_delete_finish (data->connection, result, &error)) {
+		nmt_newt_message_dialog (_("Unable to delete connection: %s"),
+		                         error->message);
+	} else
+		data->got_callback = TRUE;
+
+	if (error || (data->got_callback && data->got_signal))
+		nmt_sync_op_complete_boolean (&data->op, error == NULL, error);
+	g_clear_error (&error);
+}
+
+static void
+connection_removed_signal (NMClient           *client,
+                           NMRemoteConnection *connection,
+                           gpointer            user_data)
+{
+	ConnectionDeleteData *data = user_data;
+
+	if (connection == data->connection) {
+		data->got_signal = TRUE;
+		if (data->got_callback && data->got_signal)
+			nmt_sync_op_complete_boolean (&data->op, TRUE, NULL);
+	}
+}
+
+static void
+remove_one_connection (NMRemoteConnection *connection)
+{
+	ConnectionDeleteData data;
+	GError *error = NULL;
+
+	data.got_callback = data.got_signal = FALSE;
+	nmt_sync_op_init (&data.op);
+
+	data.connection = connection;
+	g_signal_connect (nm_client, NM_CLIENT_CONNECTION_REMOVED,
+	                  G_CALLBACK (connection_removed_signal), &data);
+	nm_remote_connection_delete_async (connection, NULL, connection_deleted_callback, &data);
+
+	if (!nmt_sync_op_wait_boolean (&data.op, &error)) {
+		nmt_newt_message_dialog (_("Could not delete connection '%s': %s"),
+		                         nm_connection_get_id (NM_CONNECTION (connection)),
+		                         error->message);
+		g_error_free (error);
+	}
+
+	g_signal_handlers_disconnect_by_func (nm_client, G_CALLBACK (connection_removed_signal), &data);
+}
+
+void
+nmt_remove_connection (NMRemoteConnection *connection)
+{
+	const GPtrArray *all_conns;
+	GSList *slaves, *iter;
+	int i;
+	NMRemoteConnection *slave;
+	NMSettingConnection *s_con;
+	const char *uuid, *iface, *master;
+	int choice;
+
+	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;
+
+	g_object_ref (connection);
+	remove_one_connection (connection);
+
+	uuid = nm_connection_get_uuid (NM_CONNECTION (connection));
+	iface = nm_connection_get_interface_name (NM_CONNECTION (connection));
+
+	all_conns = nm_client_get_connections (nm_client);
+	slaves = NULL;
+	for (i = 0; i < all_conns->len; i++) {
+		slave = all_conns->pdata[i];
+		s_con = nm_connection_get_setting_connection (NM_CONNECTION (slave));
+		master = nm_setting_connection_get_master (s_con);
+		if (master) {
+			if (!g_strcmp0 (master, uuid) || !g_strcmp0 (master, iface))
+				slaves = g_slist_prepend (slaves, g_object_ref (slave));
+		}
+	}
+
+	for (iter = slaves; iter; iter = iter->next)
+		remove_one_connection (iter->data);
+	g_slist_free_full (slaves, g_object_unref);
+
+	g_object_unref (connection);
+}
+
+NmtNewtForm *
+nmtui_edit (int argc, char **argv)
+{
+	NMConnection *conn = NULL;
+
+	if (argc == 2) {
+		if (nm_utils_is_uuid (argv[1]))
+			conn = NM_CONNECTION (nm_client_get_connection_by_uuid (nm_client, argv[1]));
+		if (!conn)
+			conn = NM_CONNECTION (nm_client_get_connection_by_id (nm_client, argv[1]));
+
+		if (!conn) {
+			nmt_newt_message_dialog ("%s: no such connection '%s'\n", argv[0], argv[1]);
+			return NULL;
+		}
+
+		return nmt_editor_new (conn);
+	} else
+		return nmt_edit_main_connection_list ();
+}
diff --git a/clients/tui/nmtui-edit.h b/clients/tui/nmtui-edit.h
new file mode 100644
index 00000000..dae91cfa
--- /dev/null
+++ b/clients/tui/nmtui-edit.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMTUI_EDIT_H
+#define NMTUI_EDIT_H
+
+#include "nmt-newt.h"
+
+G_BEGIN_DECLS
+
+typedef gboolean (*NmtAddConnectionTypeFilter) (GType    connection_type,
+                                                gpointer user_data);
+
+NmtNewtForm *nmtui_edit (int argc, char **argv);
+
+void nmt_add_connection      (void);
+void nmt_add_connection_full (const char                 *primary_text,
+                              const char                 *secondary_text,
+                              NMConnection               *master,
+                              NmtAddConnectionTypeFilter  type_filter,
+                              gpointer                    type_filter_data);
+
+void nmt_edit_connection     (NMConnection               *connection);
+
+void nmt_remove_connection   (NMRemoteConnection         *connection);
+
+G_END_DECLS
+
+#endif /* NMTUI_EDIT_H */
diff --git a/clients/tui/nmtui-hostname.c b/clients/tui/nmtui-hostname.c
new file mode 100644
index 00000000..23de95b8
--- /dev/null
+++ b/clients/tui/nmtui-hostname.c
@@ -0,0 +1,128 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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_client),
+	              NM_CLIENT_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 (GObject      *object,
+              GAsyncResult *result,
+              gpointer      op)
+{
+	GError *error = NULL;
+
+	nm_client_save_hostname_finish (NM_CLIENT (object), result, &error);
+	nmt_sync_op_complete_boolean (op, error == NULL, error);
+	g_clear_error (&error);
+}
+
+NmtNewtForm *
+nmtui_hostname (int argc, char **argv)
+{
+	const char *hostname;
+	char *tmp = NULL;
+	NmtSyncOp op;
+	GError *error = NULL;
+
+	if (argc == 2)
+		hostname = argv[1];
+	else
+		hostname = tmp = nmtui_hostname_run_dialog ();
+
+	if (hostname) {
+		nmt_sync_op_init (&op);
+		nm_client_save_hostname_async (nm_client, hostname, NULL, hostname_set, &op);
+		if (nmt_sync_op_wait_boolean (&op, &error)) {
+			/* Translators: this indicates the result. ie, "I have set the hostname to ..." */
+			nmt_newt_message_dialog (_("Set hostname to '%s'"), hostname);
+		} else {
+			nmt_newt_message_dialog (_("Unable to set hostname: %s"), error->message);
+			g_error_free (error);
+		}
+
+		g_free (tmp);
+	}
+
+	return NULL;
+}
diff --git a/clients/tui/nmtui-hostname.h b/clients/tui/nmtui-hostname.h
new file mode 100644
index 00000000..a14bc699
--- /dev/null
+++ b/clients/tui/nmtui-hostname.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef NMTUI_HOSTNAME_H
+#define NMTUI_HOSTNAME_H
+
+G_BEGIN_DECLS
+
+NmtNewtForm *nmtui_hostname (int argc, char **argv);
+
+G_END_DECLS
+
+#endif /* NMTUI_HOSTNAME_H */
diff --git a/clients/tui/nmtui.c b/clients/tui/nmtui.c
new file mode 100644
index 00000000..0fac98eb
--- /dev/null
+++ b/clients/tui/nmtui.c
@@ -0,0 +1,293 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 <NetworkManager.h>
+
+#include "nmt-newt.h"
+#include "nm-editor-bindings.h"
+
+#include "nmtui.h"
+#include "nmtui-edit.h"
+#include "nmtui-connect.h"
+#include "nmtui-hostname.h"
+
+NMClient *nm_client;
+static GMainLoop *loop;
+
+typedef NmtNewtForm * (*NmtuiSubprogram) (int argc, char **argv);
+
+static const struct {
+	const char *name, *shortcut, *arg;
+	const char *display_name;
+	NmtuiSubprogram func;
+} subprograms[] = {
+	{ "edit",     "nmtui-edit",     N_("connection"),
+	  N_("Edit a connection"),
+	  nmtui_edit },
+	{ "connect",  "nmtui-connect",  N_("connection"),
+	  N_("Activate a connection"),
+	  nmtui_connect },
+	{ "hostname", "nmtui-hostname", N_("new hostname"),
+	  N_("Set system hostname"),
+	  nmtui_hostname }
+};
+static const int num_subprograms = G_N_ELEMENTS (subprograms);
+
+static void
+quit_func (int argc, char **argv)
+{
+	nmtui_quit ();
+}
+
+static NmtNewtForm *
+nmtui_main (int argc, char **argv)
+{
+	NmtNewtForm *form;
+	NmtNewtWidget *widget, *ok;
+	NmtNewtGrid *grid;
+	NmtNewtListbox *listbox;
+	NmtNewtButtonBox *bbox;
+	NmtuiSubprogram subprogram = NULL;
+	int i;
+
+	form = g_object_new (NMT_TYPE_NEWT_FORM,
+	                     "title", _("NetworkManager TUI"),
+	                     "escape-exits", TRUE,
+	                     NULL);
+
+	widget = nmt_newt_grid_new ();
+	nmt_newt_form_set_content (form, widget);
+	grid = NMT_NEWT_GRID (widget);
+
+	widget = nmt_newt_label_new (_("Please select an option"));
+	nmt_newt_grid_add (grid, widget, 0, 0);
+
+	widget = g_object_new (NMT_TYPE_NEWT_LISTBOX,
+	                       "height", num_subprograms + 2,
+	                       "skip-null-keys", TRUE,
+	                       NULL);
+	nmt_newt_grid_add (grid, widget, 0, 1);
+	nmt_newt_widget_set_padding (widget, 0, 1, 0, 1);
+	nmt_newt_widget_set_exit_on_activate (widget, TRUE);
+	listbox = NMT_NEWT_LISTBOX (widget);
+
+	for (i = 0; i < num_subprograms; i++) {
+		nmt_newt_listbox_append (listbox, _(subprograms[i].display_name),
+		                         subprograms[i].func);
+	}
+	nmt_newt_listbox_append (listbox, "", NULL);
+	nmt_newt_listbox_append (listbox, _("Quit"), quit_func);
+
+	widget = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL);
+	nmt_newt_grid_add (grid, widget, 0, 2);
+	bbox = NMT_NEWT_BUTTON_BOX (widget);
+
+	ok = nmt_newt_button_box_add_end (bbox, _("OK"));
+	nmt_newt_widget_set_exit_on_activate (ok, TRUE);
+
+	widget = nmt_newt_form_run_sync (form);
+	if (widget)
+		subprogram = nmt_newt_listbox_get_active_key (listbox);
+	g_object_unref (form);
+
+	if (subprogram)
+		return subprogram (argc, argv);
+	else
+		return NULL;
+}
+
+/**
+ * nmtui_quit:
+ *
+ * Causes nmtui to exit.
+ */
+void
+nmtui_quit (void)
+{
+	g_main_loop_quit (loop);
+}
+
+static void
+usage (void)
+{
+	const char *argv0 = g_get_prgname ();
+	const char *usage_str = _("Usage");
+	int i;
+
+	for (i = 0; i < num_subprograms; i++) {
+		if (!strcmp (argv0, subprograms[i].shortcut)) {
+			g_printerr ("%s: %s [%s]\n", usage_str, argv0, _(subprograms[i].arg));
+			exit (1);
+		}
+	}
+
+	g_printerr ("%s: nmtui\n", usage_str);
+	for (i = 0; i < num_subprograms; i++) {
+		g_printerr ("%*s  nmtui %s [%s]\n",
+		            nmt_newt_text_width (usage_str), " ",
+		            subprograms[i].name,
+		            _(subprograms[i].arg));
+	}
+	exit (1);
+}
+
+typedef struct {
+	NmtuiSubprogram subprogram;
+	int argc;
+	char **argv;
+} NmtuiStartupData;
+
+static void
+toplevel_form_quit (NmtNewtForm *form,
+                    gpointer     user_data)
+{
+       nmtui_quit ();
+}
+
+static gboolean
+idle_run_subprogram (gpointer user_data)
+{
+	NmtuiStartupData *data = user_data;
+	NmtNewtForm *form;
+
+	form = data->subprogram (data->argc, data->argv);
+	if (form) {
+		g_signal_connect (form, "quit", G_CALLBACK (toplevel_form_quit), NULL);
+		nmt_newt_form_show (form);
+		g_object_unref (form);
+	} else
+		nmtui_quit ();
+
+	return FALSE;
+}
+
+gboolean sleep_on_startup = FALSE;
+gboolean noinit = FALSE;
+
+GOptionEntry entries[] = {
+	{ "sleep", 's', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &sleep_on_startup,
+	  "Sleep on startup", NULL },
+	{ "noinit", 'n', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &noinit,
+	  "Don't initialize newt", NULL },
+	{ NULL }
+};
+
+int
+main (int argc, char **argv)
+{
+	GOptionContext *opts;
+	GError *error = NULL;
+	NmtuiStartupData startup_data;
+	const char *prgname;
+	int i;
+
+	setlocale (LC_ALL, "");
+	bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+	bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+	textdomain (GETTEXT_PACKAGE);
+
+	opts = g_option_context_new (NULL);
+	g_option_context_add_main_entries (opts, entries, NULL);
+
+	if (!g_option_context_parse (opts, &argc, &argv, &error)) {
+		g_printerr ("%s: %s: %s\n",
+		            argv[0],
+		            _("Could not parse arguments"),
+		            error->message);
+		exit (1);
+	}
+	g_option_context_free (opts);
+
+	nm_editor_bindings_init ();
+
+	nm_client = nm_client_new (NULL, &error);
+	if (!nm_client) {
+		g_printerr (_("Could not contact NetworkManager: %s.\n"), error->message);
+		g_error_free (error);
+		exit (1);
+	}
+	if (!nm_client_get_nm_running (nm_client)) {
+		g_printerr ("%s\n", _("NetworkManager is not running."));
+		exit (1);
+	}
+
+	if (sleep_on_startup)
+		sleep (5);
+
+	startup_data.subprogram = NULL;
+	prgname = g_get_prgname ();
+	if (g_str_has_prefix (prgname, "lt-"))
+		prgname += 3;
+	if (!strcmp (prgname, "nmtui")) {
+		if (argc > 1) {
+			for (i = 0; i < num_subprograms; i++) {
+				if (!strcmp (argv[1], subprograms[i].name)) {
+					argc--;
+					argv[0] = (char *) subprograms[i].shortcut;
+					memmove (&argv[1], &argv[2], argc * sizeof (char *));
+					startup_data.subprogram = subprograms[i].func;
+					break;
+				}
+			}
+		} else
+			startup_data.subprogram = nmtui_main;
+	} else {
+		for (i = 0; i < num_subprograms; i++) {
+			if (!strcmp (prgname, subprograms[i].shortcut)) {
+				startup_data.subprogram = subprograms[i].func;
+				break;
+			}
+		}
+	}
+	if (!startup_data.subprogram)
+		usage ();
+
+	if (!noinit)
+		nmt_newt_init ();
+
+	startup_data.argc = argc;
+	startup_data.argv = argv;
+	g_idle_add (idle_run_subprogram, &startup_data);
+	loop = g_main_loop_new (NULL, FALSE);
+	g_main_loop_run (loop);
+	g_main_loop_unref (loop);
+
+	if (!noinit)
+		nmt_newt_finished ();
+
+	g_object_unref (nm_client);
+
+	return 0;
+}
diff --git a/clients/tui/nmtui.h b/clients/tui/nmtui.h
new file mode 100644
index 00000000..aada9719
--- /dev/null
+++ b/clients/tui/nmtui.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a 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 <NetworkManager.h>
+
+G_BEGIN_DECLS
+
+extern NMClient *nm_client;
+
+void nmtui_quit (void);
+
+G_END_DECLS
+
+#endif /* NMTUI_H */