diff options
129 files changed, 24528 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore index 50d14854cf..71f7e5333f 100644 --- a/.gitignore +++ b/.gitignore @@ -215,6 +215,12 @@ valgrind-*.log /cli/src/nmcli +/tui/newt/libnmt-newt.a +/tui/nmtui +/tui/nmtui-connect +/tui/nmtui-edit +/tui/nmtui-hostname + /tools/generate-settings-spec /vapi/*.vapi diff --git a/Makefile.am b/Makefile.am index ebaad4bd55..ab2941cc6c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,6 +9,7 @@ SUBDIRS = \ src \ callouts \ cli \ + tui \ tools \ policy \ data \ diff --git a/configure.ac b/configure.ac index 41bdb08a37..f19b9adea3 100644 --- a/configure.ac +++ b/configure.ac @@ -653,6 +653,17 @@ else libndp_location=system fi +AC_ARG_WITH(nmtui, AS_HELP_STRING([--with-nmtui=yes|no], [Build nmtui])) +if test "$with_nmtui" != no; then + PKG_CHECK_MODULES(NEWT, [libnewt >= 0.52.15], [build_nmtui=yes], [build_nmtui=no]) +else + build_nmtui=no +fi +if test "$with_nmtui" = yes -a "$build_nmtui" = no; then + AC_MSG_ERROR([You must have libnewt installed to build nmtui.]) +fi +AM_CONDITIONAL(BUILD_NMTUI, test "$build_nmtui" = yes) + NM_COMPILER_WARNINGS @@ -741,6 +752,8 @@ tools/Makefile cli/Makefile cli/src/Makefile cli/completion/Makefile +tui/Makefile +tui/newt/Makefile test/Makefile initscript/RedHat/NetworkManager initscript/Debian/NetworkManager @@ -817,6 +830,7 @@ echo " modemmanager-1: $with_modem_manager_1" echo " concheck: $enable_concheck" echo " libndp: $libndp_location" echo " libteamdctl: $enable_teamdctl" +echo " nmtui: $build_nmtui" echo echo "Configuration plugins" diff --git a/po/POTFILES.in b/po/POTFILES.in index 3be5735052..98516c1972 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -61,4 +61,34 @@ src/nm-sleep-monitor-systemd.c src/settings/plugins/ifcfg-rh/reader.c src/settings/nm-settings-utils.c test/nm-online.c - +tui/newt/nmt-newt-utils.c +tui/nm-editor-utils.c +tui/nm-ui-utils.c +tui/nmt-connect-connection-list.c +tui/nmt-device-entry.c +tui/nmt-edit-connection-list.c +tui/nmt-editor.c +tui/nmt-mtu-entry.c +tui/nmt-page-bond.c +tui/nmt-page-bridge-port.c +tui/nmt-page-bridge.c +tui/nmt-page-ethernet.c +tui/nmt-page-infiniband.c +tui/nmt-page-ip4.c +tui/nmt-page-ip6.c +tui/nmt-page-main.c +tui/nmt-page-team-port.c +tui/nmt-page-team.c +tui/nmt-page-vlan.c +tui/nmt-page-wifi.c +tui/nmt-password-dialog.c +tui/nmt-password-fields.c +tui/nmt-route-editor.c +tui/nmt-route-table.c +tui/nmt-secret-agent.c +tui/nmt-slave-list.c +tui/nmt-widget-list.c +tui/nmtui-connect.c +tui/nmtui-edit.c +tui/nmtui-hostname.c +tui/nmtui.c diff --git a/tui/Makefile.am b/tui/Makefile.am new file mode 100644 index 0000000000..6c02ffc2c9 --- /dev/null +++ b/tui/Makefile.am @@ -0,0 +1,122 @@ +if BUILD_NMTUI + +SUBDIRS = newt . + +AM_CPPFLAGS= \ + -I$(top_srcdir) \ + -I$(top_srcdir)/include \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/libnm-util \ + -I$(top_builddir)/libnm-util \ + -I$(top_srcdir)/libnm-glib \ + -I$(srcdir)/newt \ + $(GLIB_CFLAGS) \ + $(NEWT_CFLAGS) \ + $(DBUS_CFLAGS) \ + $(GUDEV_CFLAGS) \ + -DLOCALEDIR=\""$(localedir)"\" \ + $(NULL) + +bin_PROGRAMS = nmtui +bin_SCRIPTS = nmtui-edit nmtui-connect nmtui-hostname + +nmtui_SOURCES = \ + nmtui.c \ + nmtui.h \ + nmtui-connect.c \ + nmtui-connect.h \ + nmtui-edit.c \ + nmtui-edit.h \ + nmtui-hostname.c \ + nmtui-hostname.h \ + \ + nm-editor-bindings.c \ + nm-editor-bindings.h \ + nm-editor-utils.c \ + nm-editor-utils.h \ + nm-gvaluearray-compat.h \ + nm-ui-utils.c \ + nm-ui-utils.h \ + \ + nmt-address-list.c \ + nmt-address-list.h \ + nmt-connect-connection-list.c \ + nmt-connect-connection-list.h \ + nmt-device-entry.c \ + nmt-device-entry.h \ + nmt-edit-connection-list.c \ + nmt-edit-connection-list.h \ + nmt-editor-page.c \ + nmt-editor-page.h \ + nmt-editor.c \ + nmt-editor.h \ + nmt-ip-entry.c \ + nmt-ip-entry.h \ + nmt-mac-entry.c \ + nmt-mac-entry.h \ + nmt-mtu-entry.c \ + nmt-mtu-entry.h \ + nmt-page-bond.c \ + nmt-page-bond.h \ + nmt-page-bridge.c \ + nmt-page-bridge.h \ + nmt-page-bridge-port.c \ + nmt-page-bridge-port.h \ + nmt-page-device.c \ + nmt-page-device.h \ + nmt-page-ethernet.c \ + nmt-page-ethernet.h \ + nmt-page-grid.c \ + nmt-page-grid.h \ + nmt-page-infiniband.c \ + nmt-page-infiniband.h \ + nmt-page-ip4.c \ + nmt-page-ip4.h \ + nmt-page-ip6.c \ + nmt-page-ip6.h \ + nmt-page-main.c \ + nmt-page-main.h \ + nmt-page-team.c \ + nmt-page-team.h \ + nmt-page-team-port.c \ + nmt-page-team-port.h \ + nmt-page-vlan.c \ + nmt-page-vlan.h \ + nmt-page-wifi.c \ + nmt-page-wifi.h \ + nmt-password-dialog.c \ + nmt-password-dialog.h \ + nmt-password-fields.c \ + nmt-password-fields.h \ + nmt-route-editor.c \ + nmt-route-editor.h \ + nmt-route-entry.c \ + nmt-route-entry.h \ + nmt-route-table.c \ + nmt-route-table.h \ + nmt-secret-agent.c \ + nmt-secret-agent.h \ + nmt-slave-list.c \ + nmt-slave-list.h \ + nmt-utils.c \ + nmt-utils.h \ + nmt-widget-list.c \ + nmt-widget-list.h \ + $(NULL) + +nmtui_LDADD = \ + $(top_builddir)/libnm-util/libnm-util.la \ + $(top_builddir)/libnm-glib/libnm-glib.la \ + $(builddir)/newt/libnmt-newt.a \ + $(GUDEV_LIBS) \ + $(DBUS_LIBS) \ + $(NEWT_LIBS) \ + $(GLIB_LIBS) \ + $(NULL) + +$(bin_SCRIPTS): + ln -s nmtui $@ + +CLEANFILES = $(bin_SCRIPTS) + +endif diff --git a/tui/newt/Makefile.am b/tui/newt/Makefile.am new file mode 100644 index 0000000000..c5b295cade --- /dev/null +++ b/tui/newt/Makefile.am @@ -0,0 +1,51 @@ +AM_CPPFLAGS= \ + $(GLIB_CFLAGS) \ + $(NEWT_CFLAGS) \ + $(NULL) + +noinst_LIBRARIES = libnmt-newt.a + +libnmt_newt_a_SOURCES = \ + nmt-newt.h \ + nmt-newt-types.h \ + nmt-newt-button.c \ + nmt-newt-button.h \ + nmt-newt-button-box.c \ + nmt-newt-button-box.h \ + nmt-newt-checkbox.c \ + nmt-newt-checkbox.h \ + nmt-newt-component.c \ + nmt-newt-component.h \ + nmt-newt-container.c \ + nmt-newt-container.h \ + nmt-newt-entry.c \ + nmt-newt-entry.h \ + nmt-newt-entry-numeric.c \ + nmt-newt-entry-numeric.h \ + nmt-newt-form.c \ + nmt-newt-form.h \ + nmt-newt-grid.c \ + nmt-newt-grid.h \ + nmt-newt-hacks.c \ + nmt-newt-hacks.h \ + nmt-newt-label.c \ + nmt-newt-label.h \ + nmt-newt-listbox.c \ + nmt-newt-listbox.h \ + nmt-newt-popup.c \ + nmt-newt-popup.h \ + nmt-newt-section.c \ + nmt-newt-section.h \ + nmt-newt-separator.c \ + nmt-newt-separator.h \ + nmt-newt-stack.c \ + nmt-newt-stack.h \ + nmt-newt-textbox.c \ + nmt-newt-textbox.h \ + nmt-newt-toggle-button.c \ + nmt-newt-toggle-button.h \ + nmt-newt-utils.c \ + nmt-newt-utils.h \ + nmt-newt-widget.c \ + nmt-newt-widget.h \ + $(NULL) diff --git a/tui/newt/nmt-newt-button-box.c b/tui/newt/nmt-newt-button-box.c new file mode 100644 index 0000000000..d38ed10d1a --- /dev/null +++ b/tui/newt/nmt-newt-button-box.c @@ -0,0 +1,391 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-button-box + * @short_description: A container for #NmtNewtButtons + * + * #NmtNewtButtonBox is a container for creating and holding + * #NmtNewtButtons. + * + * A button box can be either horizontally or vertically laid out, and + * has two sections within it: the "start" (left or top) and "end" + * (right or bottom). Buttons are added from left to right or top to bottom + * within each of the two sections. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-button-box.h" +#include "nmt-newt-button.h" + +G_DEFINE_TYPE (NmtNewtButtonBox, nmt_newt_button_box, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_BUTTON_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxPrivate)) + +typedef struct { + NmtNewtButtonBoxOrientation orientation; + GPtrArray *start_buttons, *end_buttons; +} NmtNewtButtonBoxPrivate; + +enum { + PROP_0, + + PROP_ORIENTATION, + + LAST_PROP +}; + +/** + * NmtNewtButtonBoxOrientation: + * @NMT_NEWT_BUTTON_BOX_HORIZONTAL: horizontal + * @NMT_NEWT_BUTTON_BOX_VERTICAL: vertical + * + * The orientation of an #NmtNewtButtonBox + */ + +/** + * nmt_newt_button_box_new: + * @orientation: the orientation + * + * Creates a new #NmtNewtButtonBox + * + * Returns: a new #NmtNewtButtonBox + */ +NmtNewtWidget * +nmt_newt_button_box_new (NmtNewtButtonBoxOrientation orientation) +{ + return g_object_new (NMT_TYPE_NEWT_BUTTON_BOX, + "orientation", orientation, + NULL); +} + +static void +nmt_newt_button_box_init (NmtNewtButtonBox *bbox) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox); + + priv->start_buttons = g_ptr_array_new (); + priv->end_buttons = g_ptr_array_new (); +} + +/** + * nmt_newt_button_box_add_start: + * @bbox: an #NmtNewtButtonBox + * @label: the label for the newt button + * + * Creates a new #NmtNewtButton with the given @label, adds it + * to the "start" section of @bbox, and returns the newly-created + * button. + * + * Returns: the newly-created button, already added to @bbox + */ +NmtNewtWidget * +nmt_newt_button_box_add_start (NmtNewtButtonBox *bbox, + const char *label) +{ + NmtNewtWidget *button; + + button = nmt_newt_button_new (label); + nmt_newt_button_box_add_widget_start (bbox, button); + return button; +} + +/** + * nmt_newt_button_box_add_widget_start: + * @bbox: an #NmtNewtButtonBox + * @widget: the #NmtNewtWidget to add + * + * Adds the given widget to the "start" section of @bbox. + */ +void +nmt_newt_button_box_add_widget_start (NmtNewtButtonBox *bbox, + NmtNewtWidget *widget) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox); + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)-> + add (NMT_NEWT_CONTAINER (bbox), widget); + g_ptr_array_add (priv->start_buttons, widget); +} + +/** + * nmt_newt_button_box_add_end: + * @bbox: an #NmtNewtButtonBox + * @label: the label for the newt button + * + * Creates a new #NmtNewtButton with the given @label, adds it + * to the "end" section of @bbox, and returns the newly-created + * button. + * + * Returns: the newly-created button, already added to @bbox + */ +NmtNewtWidget * +nmt_newt_button_box_add_end (NmtNewtButtonBox *bbox, + const char *label) +{ + NmtNewtWidget *button; + + button = nmt_newt_button_new (label); + nmt_newt_button_box_add_widget_end (bbox, button); + return button; +} + +/** + * nmt_newt_button_box_add_widget_end: + * @bbox: an #NmtNewtButtonBox + * @widget: the #NmtNewtWidget to add + * + * Adds the given widget to the "end" section of @bbox. + */ +void +nmt_newt_button_box_add_widget_end (NmtNewtButtonBox *bbox, + NmtNewtWidget *widget) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox); + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)-> + add (NMT_NEWT_CONTAINER (bbox), widget); + g_ptr_array_add (priv->end_buttons, widget); +} + +static void +nmt_newt_button_box_remove (NmtNewtContainer *container, + NmtNewtWidget *child) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (container); + int i; + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_button_box_parent_class)-> + remove (container, child); + + for (i = 0; i < priv->start_buttons->len; i++) { + if (priv->start_buttons->pdata[i] == (gpointer) child) { + g_ptr_array_remove_index (priv->start_buttons, i); + return; + } + } + for (i = 0; i < priv->end_buttons->len; i++) { + if (priv->end_buttons->pdata[i] == (gpointer) child) { + g_ptr_array_remove_index (priv->end_buttons, i); + return; + } + } +} + +static void +add_buttons (GPtrArray *buttons, GPtrArray *cos) +{ + NmtNewtWidget *child; + newtComponent *child_cos; + int i, c; + + for (i = 0; i < buttons->len; i++) { + child = buttons->pdata[i]; + + if (!nmt_newt_widget_get_visible (child)) + continue; + + child_cos = nmt_newt_widget_get_components (child); + for (c = 0; child_cos[c]; c++) + g_ptr_array_add (cos, child_cos[c]); + g_free (child_cos); + } +} + +static newtComponent * +nmt_newt_button_box_get_components (NmtNewtWidget *widget) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget); + GPtrArray *cos; + + cos = g_ptr_array_new (); + add_buttons (priv->start_buttons, cos); + add_buttons (priv->end_buttons, cos); + g_ptr_array_add (cos, NULL); + + return (newtComponent *) g_ptr_array_free (cos, FALSE); +} + +static void +size_request_buttons (NmtNewtButtonBox *bbox, + GPtrArray *buttons, + int *width, + int *height) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (bbox); + int child_width, child_height; + int i; + + for (i = 0; i < buttons->len; i++) { + NmtNewtWidget *child = buttons->pdata[i]; + + nmt_newt_widget_size_request (child, &child_width, &child_height); + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) { + *width += child_width; + if (i > 0) + *width += 1; + *height = MAX (*height, child_height); + } else { + *height += child_height; + if (i > 0) + *height += 1; + *width = MAX (*width, child_width); + } + } +} + +static void +nmt_newt_button_box_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtButtonBox *bbox = NMT_NEWT_BUTTON_BOX (widget); + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget); + + *width = *height = 0; + size_request_buttons (bbox, priv->start_buttons, width, height); + size_request_buttons (bbox, priv->end_buttons, width, height); + + if (priv->start_buttons && priv->end_buttons) { + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) + *width += 1; + else + *height += 1; + } +} + +static void +nmt_newt_button_box_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (widget); + NmtNewtWidget *child; + int child_x, child_y, child_width, child_height; + int i; + + child_x = x; + child_y = y; + for (i = 0; i < priv->start_buttons->len; i++) { + child = priv->start_buttons->pdata[i]; + nmt_newt_widget_size_request (child, &child_width, &child_height); + + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) { + nmt_newt_widget_size_allocate (child, + child_x, child_y + (height - child_height) / 2, + child_width, child_height); + child_x += child_width + 1; + } else { + nmt_newt_widget_size_allocate (child, + child_x + (width - child_width) / 2, child_y, + child_width, child_height); + child_y += child_height + 1; + } + } + + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) + child_x = x + width; + else + child_y = y + height; + + for (i = priv->end_buttons->len - 1; i >= 0; i--) { + child = priv->end_buttons->pdata[i]; + nmt_newt_widget_size_request (child, &child_width, &child_height); + + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) { + nmt_newt_widget_size_allocate (child, + child_x - child_width, + child_y + (height - child_height) / 2, + child_width, child_height); + child_x -= child_width + 1; + } else { + nmt_newt_widget_size_allocate (child, + child_x + (width - child_width) / 2, + child_y - child_height, + child_width, child_height); + child_y -= child_height + 1; + } + } +} + +static void +nmt_newt_button_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ORIENTATION: + priv->orientation = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ORIENTATION: + g_value_set_int (value, priv->orientation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_box_class_init (NmtNewtButtonBoxClass *bbox_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (bbox_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (bbox_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (bbox_class); + + g_type_class_add_private (bbox_class, sizeof (NmtNewtButtonBoxPrivate)); + + object_class->get_property = nmt_newt_button_box_get_property; + object_class->set_property = nmt_newt_button_box_set_property; + + widget_class->get_components = nmt_newt_button_box_get_components; + widget_class->size_request = nmt_newt_button_box_size_request; + widget_class->size_allocate = nmt_newt_button_box_size_allocate; + + container_class->remove = nmt_newt_button_box_remove; + + g_object_class_install_property (object_class, PROP_ORIENTATION, + g_param_spec_int ("orientation", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-button-box.h b/tui/newt/nmt-newt-button-box.h new file mode 100644 index 0000000000..970588a4d0 --- /dev/null +++ b/tui/newt/nmt-newt-button-box.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_BUTTON_BOX_H +#define NMT_NEWT_BUTTON_BOX_H + +#include "nmt-newt-grid.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_BUTTON_BOX (nmt_newt_button_box_get_type ()) +#define NMT_NEWT_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBox)) +#define NMT_NEWT_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxClass)) +#define NMT_IS_NEWT_BUTTON_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_BUTTON_BOX)) +#define NMT_IS_NEWT_BUTTON_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_BUTTON_BOX)) +#define NMT_NEWT_BUTTON_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_BUTTON_BOX, NmtNewtButtonBoxClass)) + +struct _NmtNewtButtonBox { + NmtNewtContainer parent; + +}; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtNewtButtonBoxClass; + +GType nmt_newt_button_box_get_type (void); + +typedef enum { + NMT_NEWT_BUTTON_BOX_HORIZONTAL, + NMT_NEWT_BUTTON_BOX_VERTICAL +} NmtNewtButtonBoxOrientation; + +NmtNewtWidget *nmt_newt_button_box_new (NmtNewtButtonBoxOrientation orientation); + +NmtNewtWidget *nmt_newt_button_box_add_start (NmtNewtButtonBox *bbox, + const char *label); +NmtNewtWidget *nmt_newt_button_box_add_end (NmtNewtButtonBox *bbox, + const char *label); + +void nmt_newt_button_box_add_widget_start (NmtNewtButtonBox *bbox, + NmtNewtWidget *widget); +void nmt_newt_button_box_add_widget_end (NmtNewtButtonBox *bbox, + NmtNewtWidget *widget); + + +G_END_DECLS + +#endif /* NMT_NEWT_BUTTON_BOX_H */ diff --git a/tui/newt/nmt-newt-button.c b/tui/newt/nmt-newt-button.c new file mode 100644 index 0000000000..cdec789814 --- /dev/null +++ b/tui/newt/nmt-newt-button.c @@ -0,0 +1,260 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-button + * @short_description: Push buttons + * + * #NmtNewtButton implements a button widget. + */ + +#include "config.h" + +#include "nmt-newt-button.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtButton, nmt_newt_button, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonPrivate)) + +typedef struct { + char *label; +} NmtNewtButtonPrivate; + +enum { + PROP_0, + PROP_LABEL, + + LAST_PROP +}; + +enum { + CLICKED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/** + * nmt_newt_button_new: + * @label: the (initial) button label + * + * Creates a new button. + * + * Returns: a new #NmtNewtButton + */ +NmtNewtWidget * +nmt_newt_button_new (const char *label) +{ + return g_object_new (NMT_TYPE_NEWT_BUTTON, + "label", label, + NULL); +} + +/** + * nmt_newt_button_set_label: + * @button: an #NmtNewtButton + * @label: the new label + * + * Updates @button's label. + */ +void +nmt_newt_button_set_label (NmtNewtButton *button, + const char *label) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (button); + + if (!g_strcmp0 (priv->label, label)) + return; + + g_free (priv->label); + priv->label = g_strdup (label); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (button)); +} + +/** + * nmt_newt_button_get_label: + * @button: an #NmtNewtButton + * + * Gets @button's label. + * + * Returns: @button's label. + */ +const char * +nmt_newt_button_get_label (NmtNewtButton *button) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (button); + + return priv->label; +} + +static void +nmt_newt_button_init (NmtNewtButton *button) +{ +} + +static void +nmt_newt_button_finalize (GObject *object) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (object); + + g_free (priv->label); + + G_OBJECT_CLASS (nmt_newt_button_parent_class)->finalize (object); +} + +static newtComponent +nmt_newt_button_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (component); + newtComponent co; + char *label = NULL, *label_lc; + + if (sensitive) { + label_lc = nmt_newt_locale_from_utf8 (priv->label); + co = newtCompactButton (-1, -1, label_lc); + g_free (label_lc); + } else { + label = g_strdup_printf (" <%s>", priv->label); + label_lc = nmt_newt_locale_from_utf8 (label); + co = newtLabel (-1, -1, label_lc); + g_free (label_lc); + newtLabelSetColors (co, NMT_NEWT_COLORSET_DISABLED_BUTTON); + } + + return co; +} + +static void +nmt_newt_button_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->size_request (widget, width, height); + + /* remove the automatically-added left padding */ + (*width)--; +} + +static void +nmt_newt_button_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + /* account for the automatically-added left padding */ + x--; + width++; + + NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->size_allocate (widget, x, y, width, height); +} + +static void +nmt_newt_button_activated (NmtNewtWidget *widget) +{ + g_signal_emit (widget, signals[CLICKED], 0); + + NMT_NEWT_WIDGET_CLASS (nmt_newt_button_parent_class)->activated (widget); +} + +static void +nmt_newt_button_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_LABEL: + nmt_newt_button_set_label (NMT_NEWT_BUTTON (object), + g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LABEL: + g_value_set_string (value, priv->label); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_class_init (NmtNewtButtonClass *button_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (button_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (button_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (button_class); + + g_type_class_add_private (button_class, sizeof (NmtNewtButtonPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_button_set_property; + object_class->get_property = nmt_newt_button_get_property; + object_class->finalize = nmt_newt_button_finalize; + + widget_class->size_request = nmt_newt_button_size_request; + widget_class->size_allocate = nmt_newt_button_size_allocate; + widget_class->activated = nmt_newt_button_activated; + + component_class->build_component = nmt_newt_button_build_component; + + /* signals */ + + /** + * NmtNewtButton::clicked: + * @button: the #NmtNewtButton + * + * Emitted when the button is clicked. + */ + signals[CLICKED] = + g_signal_new ("clicked", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /* properties */ + + /** + * NmtNewtButton:label: + * + * The button's label + */ + g_object_class_install_property (object_class, PROP_LABEL, + g_param_spec_string ("label", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-button.h b/tui/newt/nmt-newt-button.h new file mode 100644 index 0000000000..52e95c5d13 --- /dev/null +++ b/tui/newt/nmt-newt-button.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_BUTTON_H +#define NMT_NEWT_BUTTON_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_BUTTON (nmt_newt_button_get_type ()) +#define NMT_NEWT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButton)) +#define NMT_NEWT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonClass)) +#define NMT_IS_NEWT_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_BUTTON)) +#define NMT_IS_NEWT_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_BUTTON)) +#define NMT_NEWT_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonClass)) + +struct _NmtNewtButton { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtButtonClass; + +GType nmt_newt_button_get_type (void); + +NmtNewtWidget *nmt_newt_button_new (const char *label); + +void nmt_newt_button_set_label (NmtNewtButton *button, + const char *label); +const char *nmt_newt_button_get_label (NmtNewtButton *button); + +G_END_DECLS + +#endif /* NMT_NEWT_BUTTON_H */ diff --git a/tui/newt/nmt-newt-checkbox.c b/tui/newt/nmt-newt-checkbox.c new file mode 100644 index 0000000000..168fada20e --- /dev/null +++ b/tui/newt/nmt-newt-checkbox.c @@ -0,0 +1,235 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-checkbox + * @short_description: Checkboxes + * + * #NmtNewtCheckbox implements a checkbox widget. + */ + +#include "config.h" + +#include "nmt-newt-checkbox.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtCheckbox, nmt_newt_checkbox, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_CHECKBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxPrivate)) + +typedef struct { + char *label_lc; + gboolean active; +} NmtNewtCheckboxPrivate; + +enum { + PROP_0, + PROP_LABEL, + PROP_ACTIVE, + + LAST_PROP +}; + +#define CHECKBOX_INACTIVE ' ' +#define CHECKBOX_ACTIVE 'X' +#define CHECKBOX_STATES " X" + +/** + * nmt_newt_checkbox_new: + * @label: the (initial) checkbox label + * + * Creates a new checkbox. + * + * Returns: a new #NmtNewtCheckbox + */ +NmtNewtWidget * +nmt_newt_checkbox_new (const char *label) +{ + return g_object_new (NMT_TYPE_NEWT_CHECKBOX, + "label", label, + NULL); +} + +/** + * nmt_newt_checkbox_set_active: + * @checkbox: an #NmtNewtCheckbox + * @active: whether @checkbox should be checked + * + * Updates @checkbox's checked state + */ +void +nmt_newt_checkbox_set_active (NmtNewtCheckbox *checkbox, + gboolean active) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox); + newtComponent co; + + active = !!active; + if (active == priv->active) + return; + + priv->active = active; + + co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (checkbox)); + if (co) + newtCheckboxSetValue (co, priv->active ? CHECKBOX_ACTIVE : CHECKBOX_INACTIVE); + + g_object_notify (G_OBJECT (checkbox), "active"); +} + +/** + * nmt_newt_checkbox_get_active: + * @checkbox: an #NmtNewtCheckbox + * + * Gets @checkbox's checked state + * + * Returns: @checkbox's checked state + */ +gboolean +nmt_newt_checkbox_get_active (NmtNewtCheckbox *checkbox) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox); + + return priv->active; +} + +static void +nmt_newt_checkbox_init (NmtNewtCheckbox *checkbox) +{ +} + +static void +nmt_newt_checkbox_finalize (GObject *object) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object); + + g_free (priv->label_lc); + + G_OBJECT_CLASS (nmt_newt_checkbox_parent_class)->finalize (object); +} + +static void +checkbox_toggled_callback (newtComponent co, + void *checkbox) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (checkbox); + gboolean active; + + active = (newtCheckboxGetValue (co) == CHECKBOX_ACTIVE); + if (active != priv->active) { + priv->active = active; + g_object_notify (checkbox, "active"); + } +} + +static newtComponent +nmt_newt_checkbox_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (component); + newtComponent co; + + co = newtCheckbox (-1, -1, priv->label_lc, + priv->active ? CHECKBOX_ACTIVE : CHECKBOX_INACTIVE, + CHECKBOX_STATES, NULL); + if (!sensitive) + newtCheckboxSetFlags (co, NEWT_FLAG_DISABLED, NEWT_FLAGS_SET); + newtComponentAddCallback (co, checkbox_toggled_callback, component); + return co; +} + +static void +nmt_newt_checkbox_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtCheckbox *checkbox = NMT_NEWT_CHECKBOX (object); + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LABEL: + g_free (priv->label_lc); + priv->label_lc = nmt_newt_locale_from_utf8 (g_value_get_string (value)); + break; + case PROP_ACTIVE: + nmt_newt_checkbox_set_active (checkbox, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_checkbox_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtCheckboxPrivate *priv = NMT_NEWT_CHECKBOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LABEL: + g_value_take_string (value, nmt_newt_locale_to_utf8 (priv->label_lc)); + break; + case PROP_ACTIVE: + g_value_set_boolean (value, priv->active); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_checkbox_class_init (NmtNewtCheckboxClass *checkbox_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (checkbox_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (checkbox_class); + + g_type_class_add_private (checkbox_class, sizeof (NmtNewtCheckboxPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_checkbox_set_property; + object_class->get_property = nmt_newt_checkbox_get_property; + object_class->finalize = nmt_newt_checkbox_finalize; + + component_class->build_component = nmt_newt_checkbox_build_component; + + /** + * NmtNewtCheckbox:label: + * + * The checkbox's label + */ + g_object_class_install_property (object_class, PROP_LABEL, + g_param_spec_string ("label", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtCheckbox:active: + * + * The checkbox's checked state + */ + g_object_class_install_property (object_class, PROP_ACTIVE, + g_param_spec_boolean ("active", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-checkbox.h b/tui/newt/nmt-newt-checkbox.h new file mode 100644 index 0000000000..c277386b18 --- /dev/null +++ b/tui/newt/nmt-newt-checkbox.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_CHECKBOX_H +#define NMT_NEWT_CHECKBOX_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_CHECKBOX (nmt_newt_checkbox_get_type ()) +#define NMT_NEWT_CHECKBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckbox)) +#define NMT_NEWT_CHECKBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxClass)) +#define NMT_IS_NEWT_CHECKBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_CHECKBOX)) +#define NMT_IS_NEWT_CHECKBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_CHECKBOX)) +#define NMT_NEWT_CHECKBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_CHECKBOX, NmtNewtCheckboxClass)) + +struct _NmtNewtCheckbox { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtCheckboxClass; + +GType nmt_newt_checkbox_get_type (void); + +NmtNewtWidget *nmt_newt_checkbox_new (const char *label); + +void nmt_newt_checkbox_set_active (NmtNewtCheckbox *checkbox, + gboolean active); +gboolean nmt_newt_checkbox_get_active (NmtNewtCheckbox *checkbox); + +G_END_DECLS + +#endif /* NMT_NEWT_CHECKBOX_H */ diff --git a/tui/newt/nmt-newt-component.c b/tui/newt/nmt-newt-component.c new file mode 100644 index 0000000000..877b53dbf1 --- /dev/null +++ b/tui/newt/nmt-newt-component.c @@ -0,0 +1,307 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-component + * @short_description: Base class for widgets that wrap #newtComponents + * + * #NmtNewtComponent is the abstract class for #NmtNewtWidgets that + * wrap a (single) #newtComponent. + */ + +#include "config.h" + +#include "nmt-newt-component.h" +#include "nmt-newt-form.h" +#include "nmt-newt-hacks.h" + +G_DEFINE_ABSTRACT_TYPE (NmtNewtComponent, nmt_newt_component, NMT_TYPE_NEWT_WIDGET) + +#define NMT_NEWT_COMPONENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentPrivate)) + +typedef struct { + newtComponent co; + gboolean own_component; + gboolean sensitive; +} NmtNewtComponentPrivate; + +enum { + PROP_0, + + PROP_COMPONENT, + PROP_SENSITIVE, + + LAST_PROP +}; + +static void +nmt_newt_component_init (NmtNewtComponent *component) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component); + + priv->sensitive = TRUE; +} + +static void +nmt_newt_component_unrealize (NmtNewtWidget *widget) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + + if (!priv->co) + return; + + newtComponentAddCallback (priv->co, NULL, NULL); + newtComponentAddDestroyCallback (priv->co, NULL, NULL); + + if (priv->own_component) + newtComponentDestroy (priv->co); + priv->co = NULL; +} + +static void +component_destroy_callback (newtComponent co, + void *component) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component); + + priv->own_component = FALSE; + nmt_newt_widget_unrealize (component); + nmt_newt_widget_needs_rebuild (component); +} + +static void +nmt_newt_component_realize (NmtNewtWidget *widget) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + + nmt_newt_component_unrealize (widget); + priv->co = NMT_NEWT_COMPONENT_GET_CLASS (widget)-> + build_component (NMT_NEWT_COMPONENT (widget), priv->sensitive); + priv->own_component = TRUE; + if (!priv->sensitive) + newtComponentTakesFocus (priv->co, FALSE); + newtComponentAddDestroyCallback (priv->co, component_destroy_callback, widget); +} + +static newtComponent * +nmt_newt_component_get_components (NmtNewtWidget *widget) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + newtComponent *cos; + + priv->own_component = FALSE; + cos = g_new0 (newtComponent, 2); + cos[0] = priv->co; + return cos; +} + +static NmtNewtWidget * +nmt_newt_component_find_component (NmtNewtWidget *widget, + newtComponent co) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + + if (co == priv->co) + return widget; + else + return NULL; +} + +static void +nmt_newt_component_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + + newtComponentGetSize (priv->co, width, height); +} + +static void +nmt_newt_component_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + newtGrid grid; + + /* You can't directly place a newtComponent, so we create a newtGrid, + * position the component within that, and then place the grid. + */ + grid = newtCreateGrid (1, 1); + newtGridSetField (grid, 0, 0, + NEWT_GRID_COMPONENT, priv->co, + x, y, 0, 0, + NEWT_ANCHOR_LEFT | NEWT_ANCHOR_TOP, 0); + newtGridPlace (grid, 0, 0); + newtGridFree (grid, FALSE); +} + +static newtComponent +nmt_newt_component_get_focus_component (NmtNewtWidget *widget) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (widget); + + return priv->co; +} + +/** + * nmt_newt_component_get_component: + * @component: an #NmtNewtComponent + * + * A simpler version of nmt_newt_widget_get_components() for the + * single-component case. Also, unlike + * nmt_newt_widget_get_component(), this does not realize the widget + * if it isn't already realized. FIXME: why? + * + * Returns: @component's #newtComponent + */ +newtComponent +nmt_newt_component_get_component (NmtNewtComponent *component) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component); + + return priv->co; +} + +/** + * nmt_newt_component_get_sensitive: + * @component: an #NmtNewtComponent + * + * Gets @component's #NmtNewtComponent:sensitive property, indicating + * whether the widget is available for manipulation. Insensitive + * components will be skipped over in the keyboard tab chain, and may + * be displayed differently. + * + * Returns: @component's #NmtNewtComponent:sensitive property + */ +gboolean +nmt_newt_component_get_sensitive (NmtNewtComponent *component) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component); + + return priv->sensitive; +} + +/** + * nmt_newt_component_set_sensitive: + * @component: an #NmtNewtComponent + * @sensitive: whether @component should be sensitive + * + * Sets @component's #NmtNewtComponent:sensitive property. + */ +void +nmt_newt_component_set_sensitive (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtComponentPrivate *priv = NMT_NEWT_COMPONENT_GET_PRIVATE (component); + + sensitive = !!sensitive; + if (priv->sensitive == sensitive) + return; + + priv->sensitive = sensitive; + g_object_notify (G_OBJECT (component), "sensitive"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (component)); +} + +static void +nmt_newt_component_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtComponent *component = NMT_NEWT_COMPONENT (object); + + switch (prop_id) { + case PROP_SENSITIVE: + nmt_newt_component_set_sensitive (component, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_component_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtComponent *component = NMT_NEWT_COMPONENT (object); + + switch (prop_id) { + case PROP_COMPONENT: + g_value_set_pointer (value, nmt_newt_component_get_component (component)); + break; + case PROP_SENSITIVE: + g_value_set_boolean (value, nmt_newt_component_get_sensitive (component)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_component_class_init (NmtNewtComponentClass *component_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (component_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (component_class); + + g_type_class_add_private (component_class, sizeof (NmtNewtComponentPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_component_set_property; + object_class->get_property = nmt_newt_component_get_property; + + widget_class->realize = nmt_newt_component_realize; + widget_class->unrealize = nmt_newt_component_unrealize; + widget_class->get_components = nmt_newt_component_get_components; + widget_class->find_component = nmt_newt_component_find_component; + widget_class->size_request = nmt_newt_component_size_request; + widget_class->size_allocate = nmt_newt_component_size_allocate; + widget_class->get_focus_component = nmt_newt_component_get_focus_component; + + /* properties */ + + /** + * NmtNewtComponent:component: + * + * The component's #newtComponent + */ + g_object_class_install_property (object_class, PROP_COMPONENT, + g_param_spec_pointer ("component", "", "", + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtComponent:sensitive: + * + * Whether the component is sensitive. Insensitive components will + * be skipped over in the keyboard tab chain, and may be displayed + * differently. + */ + g_object_class_install_property (object_class, PROP_SENSITIVE, + g_param_spec_boolean ("sensitive", "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-component.h b/tui/newt/nmt-newt-component.h new file mode 100644 index 0000000000..2ffa018e1a --- /dev/null +++ b/tui/newt/nmt-newt-component.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_COMPONENT_H +#define NMT_NEWT_COMPONENT_H + +#include "nmt-newt-widget.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_COMPONENT (nmt_newt_component_get_type ()) +#define NMT_NEWT_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponent)) +#define NMT_NEWT_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentClass)) +#define NMT_IS_NEWT_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_COMPONENT)) +#define NMT_IS_NEWT_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_COMPONENT)) +#define NMT_NEWT_COMPONENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_COMPONENT, NmtNewtComponentClass)) + +struct _NmtNewtComponent { + NmtNewtWidget parent; + +}; + +typedef struct { + NmtNewtWidgetClass parent; + + /* methods */ + newtComponent (*build_component) (NmtNewtComponent *component, + gboolean sensitive); + +} NmtNewtComponentClass; + +GType nmt_newt_component_get_type (void); + +newtComponent nmt_newt_component_get_component (NmtNewtComponent *component); + +gboolean nmt_newt_component_get_sensitive (NmtNewtComponent *component); +void nmt_newt_component_set_sensitive (NmtNewtComponent *component, + gboolean sensitive); + +G_END_DECLS + +#endif /* NMT_NEWT_COMPONENT_H */ diff --git a/tui/newt/nmt-newt-container.c b/tui/newt/nmt-newt-container.c new file mode 100644 index 0000000000..03f677fbdc --- /dev/null +++ b/tui/newt/nmt-newt-container.c @@ -0,0 +1,252 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-container + * @short_description: Base class for containers + * + * #NmtNewtContainer is the base class for #NmtNewtWidgets that + * contain other widgets. + * + * #NmtNewtGrid is the most generic container type. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-container.h" +#include "nmt-newt-component.h" + +G_DEFINE_ABSTRACT_TYPE (NmtNewtContainer, nmt_newt_container, NMT_TYPE_NEWT_WIDGET) + +#define NMT_NEWT_CONTAINER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerPrivate)) + +typedef struct { + GPtrArray *children; + +} NmtNewtContainerPrivate; + +static void child_needs_rebuild (NmtNewtWidget *widget, gpointer user_data); + +static void +nmt_newt_container_init (NmtNewtContainer *container) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container); + + priv->children = g_ptr_array_new (); +} + +static void +nmt_newt_container_finalize (GObject *object) +{ + NmtNewtContainer *container = NMT_NEWT_CONTAINER (object); + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (object); + + while (priv->children->len) + nmt_newt_container_remove (container, priv->children->pdata[0]); + + G_OBJECT_CLASS (nmt_newt_container_parent_class)->finalize (object); +} + +static void +nmt_newt_container_realize (NmtNewtWidget *widget) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget); + int i; + + for (i = 0; i < priv->children->len; i++) + nmt_newt_widget_realize (priv->children->pdata[i]); +} + +static void +nmt_newt_container_unrealize (NmtNewtWidget *widget) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget); + int i; + + for (i = 0; i < priv->children->len; i++) + nmt_newt_widget_unrealize (priv->children->pdata[i]); +} + +static void +child_needs_rebuild (NmtNewtWidget *widget, + gpointer user_data) +{ + NmtNewtWidget *container = user_data; + + nmt_newt_widget_needs_rebuild (container); +} + +static void +nmt_newt_container_real_child_validity_changed (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtContainerPrivate *priv; + int i; + + if (widget) { + if (!nmt_newt_widget_get_visible (widget)) + return; + if (!nmt_newt_widget_get_valid (widget)) { + nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), FALSE); + return; + } + } + + priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container); + for (i = 0; i < priv->children->len; i++) { + widget = priv->children->pdata[i]; + if ( nmt_newt_widget_get_visible (widget) + && !nmt_newt_widget_get_valid (widget)) { + nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), FALSE); + return; + } + } + + nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (container), TRUE); +} + +static void +nmt_newt_container_child_validity_changed (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NMT_NEWT_CONTAINER_GET_CLASS (container)->child_validity_changed (container, widget); +} + +static void +child_validity_notify (GObject *object, + GParamSpec *pspec, + gpointer container) +{ + nmt_newt_container_child_validity_changed (container, NMT_NEWT_WIDGET (object)); +} + +static void +nmt_newt_container_real_add (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container); + + g_signal_connect (widget, "needs-rebuild", G_CALLBACK (child_needs_rebuild), container); + g_signal_connect (widget, "notify::valid", G_CALLBACK (child_validity_notify), container); + g_ptr_array_add (priv->children, g_object_ref_sink (widget)); + nmt_newt_widget_set_parent (widget, NMT_NEWT_WIDGET (container)); + + nmt_newt_container_child_validity_changed (container, widget); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (container)); +} + +static void +nmt_newt_container_real_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container); + int i; + + for (i = 0; i < priv->children->len; i++) { + if (widget == priv->children->pdata[i]) { + g_ptr_array_remove_index (priv->children, i); + g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (child_needs_rebuild), container); + g_signal_handlers_disconnect_by_func (widget, G_CALLBACK (child_validity_notify), container); + nmt_newt_widget_set_parent (widget, NULL); + g_object_unref (widget); + + nmt_newt_container_child_validity_changed (container, NULL); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (container)); + return; + } + } +} + +/** + * nmt_newt_container_remove: + * @container: the #NmtNewtContainer + * @widget: the child to remove + * + * Removes @widget from @container. + * + * Note that there is not a corresponding + * <literal>nmt_newt_container_add ()</literal>; you must use + * container-type-specific methods to add widgets to containers. + */ +void +nmt_newt_container_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NMT_NEWT_CONTAINER_GET_CLASS (container)->remove (container, widget); +} + +static NmtNewtWidget * +nmt_newt_container_find_component (NmtNewtWidget *widget, + newtComponent co) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (widget); + NmtNewtWidget *found, *child; + int i; + + for (i = 0; i < priv->children->len; i++) { + child = priv->children->pdata[i]; + + found = nmt_newt_widget_find_component (child, co); + if (found) + return found; + } + + return NULL; +} + +/** + * nmt_newt_container_get_children: + * @container: an #NmtNewtContainer + * + * Gets a list of @container's children. + * + * Returns: (transfer full): a list of @container's children. + */ +GSList * +nmt_newt_container_get_children (NmtNewtContainer *container) +{ + NmtNewtContainerPrivate *priv = NMT_NEWT_CONTAINER_GET_PRIVATE (container); + GSList *ret; + int i; + + for (i = 0, ret = NULL; i < priv->children->len; i++) + ret = g_slist_prepend (ret, g_object_ref (priv->children->pdata[i])); + return g_slist_reverse (ret); +} + +static void +nmt_newt_container_class_init (NmtNewtContainerClass *container_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (container_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (container_class); + + g_type_class_add_private (container_class, sizeof (NmtNewtContainerPrivate)); + + /* virtual methods */ + object_class->finalize = nmt_newt_container_finalize; + + widget_class->realize = nmt_newt_container_realize; + widget_class->unrealize = nmt_newt_container_unrealize; + widget_class->find_component = nmt_newt_container_find_component; + + container_class->add = nmt_newt_container_real_add; + container_class->remove = nmt_newt_container_real_remove; + container_class->child_validity_changed = nmt_newt_container_real_child_validity_changed; +} diff --git a/tui/newt/nmt-newt-container.h b/tui/newt/nmt-newt-container.h new file mode 100644 index 0000000000..6f182925d5 --- /dev/null +++ b/tui/newt/nmt-newt-container.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_CONTAINER_H +#define NMT_NEWT_CONTAINER_H + +#include "nmt-newt-widget.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_CONTAINER (nmt_newt_container_get_type ()) +#define NMT_NEWT_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainer)) +#define NMT_NEWT_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerClass)) +#define NMT_IS_NEWT_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_CONTAINER)) +#define NMT_IS_NEWT_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_CONTAINER)) +#define NMT_NEWT_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_CONTAINER, NmtNewtContainerClass)) + +struct _NmtNewtContainer { + NmtNewtWidget parent; + +}; + +typedef struct { + NmtNewtWidgetClass parent; + + /* methods */ + void (*add) (NmtNewtContainer *container, + NmtNewtWidget *child); + void (*remove) (NmtNewtContainer *container, + NmtNewtWidget *child); + + void (*child_validity_changed) (NmtNewtContainer *container, + NmtNewtWidget *child); + +} NmtNewtContainerClass; + +GType nmt_newt_container_get_type (void); + +void nmt_newt_container_remove (NmtNewtContainer *container, + NmtNewtWidget *widget); + +GSList *nmt_newt_container_get_children (NmtNewtContainer *container); + +G_END_DECLS + +#endif /* NMT_NEWT_CONTAINER_H */ diff --git a/tui/newt/nmt-newt-entry-numeric.c b/tui/newt/nmt-newt-entry-numeric.c new file mode 100644 index 0000000000..76ca1c37a9 --- /dev/null +++ b/tui/newt/nmt-newt-entry-numeric.c @@ -0,0 +1,213 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-entry-numeric + * @short_description: Numeric text entry + * + * #NmtNewtEntryNumeric implements a numeric-only #NmtNewtEntry. + * + * #NmtNewtEntryNumeric provides its own #NmtNewtEntryFilter and + * #NmtNewtEntryValidator functions, so you should not set your own. + */ + +#include "config.h" + +#include <stdlib.h> + +#include "nmt-newt-entry-numeric.h" + +G_DEFINE_TYPE (NmtNewtEntryNumeric, nmt_newt_entry_numeric, NMT_TYPE_NEWT_ENTRY) + +#define NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericPrivate)) + +typedef struct { + int min, max; +} NmtNewtEntryNumericPrivate; + +enum { + PROP_0, + PROP_MINIMUM, + PROP_MAXIMUM, + + LAST_PROP +}; + +/** + * nmt_newt_entry_numeric_new: + * @width: the entry's width in characters + * @min: the minimum valid value + * @max: the maximum valid value + * + * Creates a new #NmtNewtEntryNumeric, accepting values in pthe + * indicated range. + * + * Returns: a new #NmtNewtEntryNumeric + */ +NmtNewtWidget * +nmt_newt_entry_numeric_new (int width, + int min, + int max) +{ + return g_object_new (NMT_TYPE_NEWT_ENTRY_NUMERIC, + "width", width, + "minimum", min, + "maximum", max, + NULL); +} + +static gboolean +newt_entry_numeric_filter (NmtNewtEntry *entry, + const char *text, + int ch, + int position, + gpointer user_data) +{ + NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (entry); + + if (g_ascii_isdigit (ch)) + return TRUE; + + if (ch == '-' && position == 0 && priv->min < 0) + return TRUE; + + return FALSE; +} + +static gboolean +newt_entry_numeric_validate (NmtNewtEntry *entry, + const char *text, + gpointer user_data) +{ + NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (entry); + int val; + char *end; + + if (!*text) + return FALSE; + + val = strtoul (text, &end, 10); + if (*end) + return FALSE; + if (val < priv->min || val > priv->max) + return FALSE; + + return TRUE; +} + +static void +nmt_newt_entry_numeric_init (NmtNewtEntryNumeric *entry) +{ + nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), newt_entry_numeric_filter, NULL); + nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), newt_entry_numeric_validate, NULL); +} + +static void +nmt_newt_entry_numeric_constructed (GObject *object) +{ + NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object); + + if (!*nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object))) { + char buf[32]; + + g_snprintf (buf, sizeof (buf), "%d", priv->min); + nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), buf); + } + + G_OBJECT_CLASS (nmt_newt_entry_numeric_parent_class)->constructed (object); +} + +static void +nmt_newt_entry_numeric_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MINIMUM: + priv->min = g_value_get_int (value); + break; + case PROP_MAXIMUM: + priv->max = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_entry_numeric_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtEntryNumericPrivate *priv = NMT_NEWT_ENTRY_NUMERIC_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MINIMUM: + g_value_set_int (value, priv->min); + break; + case PROP_MAXIMUM: + g_value_set_int (value, priv->max); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_entry_numeric_class_init (NmtNewtEntryNumericClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtNewtEntryNumericPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_newt_entry_numeric_constructed; + object_class->set_property = nmt_newt_entry_numeric_set_property; + object_class->get_property = nmt_newt_entry_numeric_get_property; + + /** + * NmtNewtEntryNumeric:minimum: + * + * The minimum #NmtNewtWidget:valid value for the entry. If this + * is non-negative, then the entry will not allow negative numbers + * to be entered. + */ + g_object_class_install_property (object_class, PROP_MINIMUM, + g_param_spec_int ("minimum", "", "", + G_MININT, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtEntryNumeric:maximum: + * + * The maximum #NmtNewtWidget:valid value for the entry. + */ + g_object_class_install_property (object_class, PROP_MAXIMUM, + g_param_spec_int ("maximum", "", "", + G_MININT, G_MAXINT, G_MAXINT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-entry-numeric.h b/tui/newt/nmt-newt-entry-numeric.h new file mode 100644 index 0000000000..43ac34490a --- /dev/null +++ b/tui/newt/nmt-newt-entry-numeric.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_ENTRY_NUMERIC_H +#define NMT_NEWT_ENTRY_NUMERIC_H + +#include "nmt-newt-entry.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_ENTRY_NUMERIC (nmt_newt_entry_numeric_get_type ()) +#define NMT_NEWT_ENTRY_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumeric)) +#define NMT_NEWT_ENTRY_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericClass)) +#define NMT_IS_NEWT_ENTRY_NUMERIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC)) +#define NMT_IS_NEWT_ENTRY_NUMERIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_ENTRY_NUMERIC)) +#define NMT_NEWT_ENTRY_NUMERIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_ENTRY_NUMERIC, NmtNewtEntryNumericClass)) + +struct _NmtNewtEntryNumeric { + NmtNewtEntry parent; + +}; + +typedef struct { + NmtNewtEntryClass parent; + +} NmtNewtEntryNumericClass; + +GType nmt_newt_entry_numeric_get_type (void); + +NmtNewtWidget *nmt_newt_entry_numeric_new (int width, + int min, + int max); + +G_END_DECLS + +#endif /* NMT_NEWT_ENTRY_NUMERIC_H */ diff --git a/tui/newt/nmt-newt-entry.c b/tui/newt/nmt-newt-entry.c new file mode 100644 index 0000000000..c98fcee8a4 --- /dev/null +++ b/tui/newt/nmt-newt-entry.c @@ -0,0 +1,536 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-entry + * @short_description: Text entries + * + * #NmtNewtEntry implements entry widgets, with optional filtering and + * validation. + * + * See also #NmtNewtEntryNumeric, for numeric-only entries. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-entry.h" +#include "nmt-newt-form.h" +#include "nmt-newt-hacks.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtEntry, nmt_newt_entry, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryPrivate)) + +typedef struct { + int width; + NmtNewtEntryFlags flags; + char *text; + int last_cursor_pos; + guint idle_update; + + NmtNewtEntryFilter filter; + gpointer filter_data; + + NmtNewtEntryValidator validator; + gpointer validator_data; +} NmtNewtEntryPrivate; + +enum { + PROP_0, + PROP_TEXT, + PROP_WIDTH, + PROP_FLAGS, + PROP_PASSWORD, + + LAST_PROP +}; + +/** + * NmtNewtEntryFlags: + * @NMT_NEWT_ENTRY_NOSCROLL: the entry content should not scroll left + * and right + * @NMT_NEWT_ENTRY_PASSWORD: the entry should show '*'s instead of its + * actual contents + * @NMT_NEWT_ENTRY_NONEMPTY: the entry should be considered not + * #NmtNewtWidget:valid if it is empty. + * + * Flags describing an #NmtNewtEntry + */ + +/** + * nmt_newt_entry_new: + * @width: the width in characters for the entry + * @flags: flags describing the entry + * + * Creates a new #NmtNewtEntry. + * + * Returns: a new #NmtNewtEntry + */ +NmtNewtWidget * +nmt_newt_entry_new (int width, + NmtNewtEntryFlags flags) +{ + return g_object_new (NMT_TYPE_NEWT_ENTRY, + "width", width, + "flags", flags, + NULL); +} + +/** + * NmtNewtEntryFilter: + * @entry: the #NmtNewtEntry + * @text: the current contents of @entry + * @ch: the character just typed + * @position: the position of the cursor in @entry + * @user_data: the data passed to nmt_newt_entry_set_filter() + * + * Callback function used to filter the contents of an entry. + * + * Returns: %TRUE if @ch should be accepted, %FALSE if not + */ + +/** + * nmt_newt_entry_set_filter: + * @entry: an #NmtNewtEntry + * @filter: the function to use to filter the entry + * @user_data: data for @filter + * + * Sets a #NmtNewtEntryFilter on @entry, to allow filtering out + * certain characters from the entry. + * + * Note that @filter will only be called for printable characters (eg, + * not for cursor-control characters or the like), and that it will + * only be called for user input, not, eg, for + * nmt_newt_entry_set_text(). + */ +void +nmt_newt_entry_set_filter (NmtNewtEntry *entry, + NmtNewtEntryFilter filter, + gpointer user_data) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + priv->filter = filter; + priv->filter_data = user_data; +} + +static void +nmt_newt_entry_check_valid (NmtNewtEntry *entry) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + gboolean valid; + + if ( (priv->flags & NMT_NEWT_ENTRY_NONEMPTY) + && *priv->text == '\0') + valid = FALSE; + else if (priv->validator) + valid = !!priv->validator (entry, priv->text, priv->validator_data); + else + valid = TRUE; + + nmt_newt_widget_set_valid (NMT_NEWT_WIDGET (entry), valid); +} + +/** + * NmtNewtEntryValidator: + * @entry: the #NmtNewtEntry + * @text: the current contents of @entry + * @user_data: the data passed to nmt_newt_entry_set_validator() + * + * Callback function used to validate the contents of an entry. + * + * Returns: whether the entry is #NmtNewtWidget:valid + */ + +/** + * nmt_newt_entry_set_validator: + * @entry: an #NmtNewtEntry + * @validator: the function to use to validate the entry + * @user_data: data for @validator + * + * Sets a #NmtNewtEntryValidator on @entry, to allow validation of + * the entry contents. If @validator returns %FALSE, then the entry + * will not be considered #NmtNewtWidget:valid. + */ +void +nmt_newt_entry_set_validator (NmtNewtEntry *entry, + NmtNewtEntryValidator validator, + gpointer user_data) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + priv->validator = validator; + priv->validator_data = user_data; + + nmt_newt_entry_check_valid (entry); +} + +static void +nmt_newt_entry_set_text_internal (NmtNewtEntry *entry, + const char *text, + newtComponent co) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + if (!text) + text = ""; + + if (!strcmp (priv->text, text)) + return; + + g_free (priv->text); + priv->text = g_strdup (text); + + if (co) { + char *text_lc; + + text_lc = priv->text ? nmt_newt_locale_from_utf8 (priv->text) : NULL; + newtEntrySet (co, text_lc, TRUE); + g_free (text_lc); + priv->last_cursor_pos = -1; + } + + g_object_freeze_notify (G_OBJECT (entry)); + nmt_newt_entry_check_valid (entry); + g_object_notify (G_OBJECT (entry), "text"); + g_object_thaw_notify (G_OBJECT (entry)); +} + +/** + * nmt_newt_entry_set_text: + * @entry: an #NmtNewtEntry + * @text: the new text + * + * Updates @entry's text. Note that this skips the entry's + * #NmtNewtEntryFilter, but will cause its #NmtNewtEntryValidator to + * be re-run. + */ +void +nmt_newt_entry_set_text (NmtNewtEntry *entry, + const char *text) +{ + newtComponent co; + + co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (entry)); + nmt_newt_entry_set_text_internal (entry, text, co); +} + +/** + * nmt_newt_entry_get_text: + * @entry: an #NmtNewtEntry + * + * Gets @entry's text + * + * Returns: @entry's text + */ +const char * +nmt_newt_entry_get_text (NmtNewtEntry *entry) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + return priv->text; +} + +/** + * nmt_newt_entry_set_width: + * @entry: an #NmtNewtEntpry + * @widget: the new width + * + * Updates @entry's width + */ +void +nmt_newt_entry_set_width (NmtNewtEntry *entry, + int width) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + if (priv->width == width) + return; + + priv->width = width; + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry)); + + g_object_notify (G_OBJECT (entry), "width"); +} + +/** + * nmt_newt_entry_get_width: + * @entry: an #NmtNewtEntry + * + * Gets @entry's width + * + * Returns: @entry's width + */ +int +nmt_newt_entry_get_width (NmtNewtEntry *entry) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + return priv->width; +} + +static void +nmt_newt_entry_init (NmtNewtEntry *entry) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + priv->text = g_strdup (""); + priv->last_cursor_pos = -1; +} + +static void +nmt_newt_entry_constructed (GObject *object) +{ + nmt_newt_entry_check_valid (NMT_NEWT_ENTRY (object)); + + G_OBJECT_CLASS (nmt_newt_entry_parent_class)->constructed (object); +} + +static void +nmt_newt_entry_finalize (GObject *object) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (object); + + g_free (priv->text); + if (priv->idle_update) + g_source_remove (priv->idle_update); + + G_OBJECT_CLASS (nmt_newt_entry_parent_class)->finalize (object); +} + +static gboolean +idle_update_entry (gpointer entry) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + newtComponent co = nmt_newt_component_get_component (entry); + char *text; + + priv->idle_update = 0; + if (!co) + return FALSE; + + priv->last_cursor_pos = newtEntryGetCursorPosition (co); + + text = nmt_newt_locale_to_utf8 (newtEntryGetValue (co)); + nmt_newt_entry_set_text_internal (entry, text, NULL); + g_free (text); + + return FALSE; +} + +static int +entry_filter (newtComponent entry, + void *self, + int ch, + int cursor) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (self); + + if (g_ascii_isprint (ch)) { + if (priv->filter) { + char *text = nmt_newt_locale_to_utf8 (newtEntryGetValue (entry)); + + if (!priv->filter (self, text, ch, cursor, priv->filter_data)) { + g_free (text); + return 0; + } + g_free (text); + } + } + + if (!priv->idle_update) + priv->idle_update = g_idle_add (idle_update_entry, self); + return ch; +} + +static guint +convert_flags (NmtNewtEntryFlags flags) +{ + guint newt_flags = NEWT_FLAG_RETURNEXIT; + + if (!(flags & NMT_NEWT_ENTRY_NOSCROLL)) + newt_flags |= NEWT_FLAG_SCROLL; + if (flags & NMT_NEWT_ENTRY_PASSWORD) + newt_flags |= NEWT_FLAG_PASSWORD; + + return newt_flags; +} + +static newtComponent +nmt_newt_entry_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (component); + newtComponent co; + char *text_lc; + int flags; + + flags = convert_flags (priv->flags); + if (!sensitive) + flags |= NEWT_FLAG_DISABLED; + + text_lc = priv->text ? nmt_newt_locale_from_utf8 (priv->text) : NULL; + co = newtEntry (-1, -1, text_lc, priv->width, NULL, flags); + g_free (text_lc); + + if (priv->last_cursor_pos != -1) + newtEntrySetCursorPosition (co, priv->last_cursor_pos); + + newtEntrySetFilter (co, entry_filter, component); + return co; +} + +static void +nmt_newt_entry_activated (NmtNewtWidget *widget) +{ + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (widget); + + if (priv->idle_update) { + g_source_remove (priv->idle_update); + idle_update_entry (widget); + } + + NMT_NEWT_WIDGET_CLASS (nmt_newt_entry_parent_class)->activated (widget); +} + +static void +nmt_newt_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtEntry *entry = NMT_NEWT_ENTRY (object); + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + switch (prop_id) { + case PROP_TEXT: + nmt_newt_entry_set_text (entry, g_value_get_string (value)); + break; + case PROP_WIDTH: + nmt_newt_entry_set_width (entry, g_value_get_int (value)); + break; + case PROP_FLAGS: + priv->flags = g_value_get_uint (value); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry)); + break; + case PROP_PASSWORD: + if (g_value_get_boolean (value)) + priv->flags |= NMT_NEWT_ENTRY_PASSWORD; + else + priv->flags &= ~NMT_NEWT_ENTRY_PASSWORD; + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (entry)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtEntry *entry = NMT_NEWT_ENTRY (object); + NmtNewtEntryPrivate *priv = NMT_NEWT_ENTRY_GET_PRIVATE (entry); + + switch (prop_id) { + case PROP_TEXT: + g_value_set_string (value, nmt_newt_entry_get_text (entry)); + break; + case PROP_WIDTH: + g_value_set_int (value, priv->width); + break; + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_PASSWORD: + g_value_set_boolean (value, (priv->flags & NMT_NEWT_ENTRY_PASSWORD) != 0); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_entry_class_init (NmtNewtEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (entry_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtNewtEntryPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_newt_entry_constructed; + object_class->set_property = nmt_newt_entry_set_property; + object_class->get_property = nmt_newt_entry_get_property; + object_class->finalize = nmt_newt_entry_finalize; + + widget_class->activated = nmt_newt_entry_activated; + + component_class->build_component = nmt_newt_entry_build_component; + + /** + * NmtNewtEntry:text + * + * The entry's text + */ + g_object_class_install_property (object_class, PROP_TEXT, + g_param_spec_string ("text", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtEntry:width + * + * The entry's width in characters + */ + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_int ("width", "", "", + -1, 80, -1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtEntry:flags + * + * The entry's #NmtNewtEntryFlags + */ + g_object_class_install_property (object_class, PROP_FLAGS, + g_param_spec_uint ("flags", "", "", + 0, 0xFFFF, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtEntry:password + * + * %TRUE if #NmtNewtEntry:flags contains %NMT_NEWT_ENTRY_PASSWORD, + * %FALSE if not. + */ + g_object_class_install_property (object_class, PROP_PASSWORD, + g_param_spec_boolean ("password", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-entry.h b/tui/newt/nmt-newt-entry.h new file mode 100644 index 0000000000..8df0b13701 --- /dev/null +++ b/tui/newt/nmt-newt-entry.h @@ -0,0 +1,74 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_ENTRY_H +#define NMT_NEWT_ENTRY_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_ENTRY (nmt_newt_entry_get_type ()) +#define NMT_NEWT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntry)) +#define NMT_NEWT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryClass)) +#define NMT_IS_NEWT_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_ENTRY)) +#define NMT_IS_NEWT_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_ENTRY)) +#define NMT_NEWT_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_ENTRY, NmtNewtEntryClass)) + +struct _NmtNewtEntry { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtEntryClass; + +GType nmt_newt_entry_get_type (void); + +typedef gboolean (*NmtNewtEntryFilter) (NmtNewtEntry *, const char *text, int ch, int position, gpointer); +typedef gboolean (*NmtNewtEntryValidator) (NmtNewtEntry *, const char *text, gpointer); + +typedef enum { + NMT_NEWT_ENTRY_NOSCROLL = (1 << 0), + NMT_NEWT_ENTRY_PASSWORD = (1 << 1), + NMT_NEWT_ENTRY_NONEMPTY = (1 << 2) +} NmtNewtEntryFlags; + +NmtNewtWidget *nmt_newt_entry_new (int width, + NmtNewtEntryFlags flags); + +void nmt_newt_entry_set_filter (NmtNewtEntry *entry, + NmtNewtEntryFilter filter, + gpointer user_data); +void nmt_newt_entry_set_validator (NmtNewtEntry *entry, + NmtNewtEntryValidator validator, + gpointer user_data); + +void nmt_newt_entry_set_text (NmtNewtEntry *entry, + const char *text); +const char *nmt_newt_entry_get_text (NmtNewtEntry *entry); + +void nmt_newt_entry_set_width (NmtNewtEntry *entry, + int width); +int nmt_newt_entry_get_width (NmtNewtEntry *entry); + +G_END_DECLS + +#endif /* NMT_NEWT_ENTRY_H */ diff --git a/tui/newt/nmt-newt-form.c b/tui/newt/nmt-newt-form.c new file mode 100644 index 0000000000..ff8eb83609 --- /dev/null +++ b/tui/newt/nmt-newt-form.c @@ -0,0 +1,656 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-form + * @short_description: The top-level NmtNewt widget + * + * #NmtNewtForm is the top-level widget that contains and presents a + * "form" (aka dialog) to the user. + */ + +#include "config.h" + +#include <fcntl.h> +#include <string.h> +#include <unistd.h> + +#include <glib/gi18n-lib.h> + +#include "nmt-newt-form.h" +#include "nmt-newt-button.h" +#include "nmt-newt-grid.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtForm, nmt_newt_form, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_FORM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_FORM, NmtNewtFormPrivate)) + +typedef struct { + newtComponent form; + NmtNewtWidget *content; + + guint x, y, width, height; + guint padding; + gboolean fixed_x, fixed_y; + gboolean fixed_width, fixed_height; + char *title_lc; + + gboolean dirty, escape_exits; + NmtNewtWidget *focus; +#ifdef HAVE_NEWTFORMGETSCROLLPOSITION + int scroll_position = 0; +#endif +} NmtNewtFormPrivate; + +enum { + PROP_0, + PROP_TITLE, + PROP_FULLSCREEN, + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT, + PROP_PADDING, + PROP_ESCAPE_EXITS, + + LAST_PROP +}; + +static void nmt_newt_form_redraw (NmtNewtForm *form); + +/** + * nmt_newt_form_new: + * @title: (allow-none): the form title + * + * Creates a new form, which will be shown centered on the screen. + * Compare nmt_newt_form_new_fullscreen(). You can also position a + * form manually by setting its #NmtNewtForm:x and #NmtNewtForm:y + * properties, either at construct time or later. + * + * If @title is NULL, the form will have no title. + * + * Returns: a new #NmtNewtForm + */ +NmtNewtForm * +nmt_newt_form_new (const char *title) +{ + return g_object_new (NMT_TYPE_NEWT_FORM, + "title", title, + NULL); +} + +/** + * nmt_newt_form_new_fullscreen: + * @title: (allow-none): the form title + * + * Creates a new fullscreen form. Compare nmt_newt_form_new(). + * + * If @title is NULL, the form will have no title. + * + * Returns: a new #NmtNewtForm + */ +NmtNewtForm * +nmt_newt_form_new_fullscreen (const char *title) +{ + return g_object_new (NMT_TYPE_NEWT_FORM, + "title", title, + "fullscreen", TRUE, + NULL); +} + +static void +nmt_newt_form_init (NmtNewtForm *form) +{ + g_object_ref_sink (form); +} + +static void +nmt_newt_form_finalize (GObject *object) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object); + + g_free (priv->title_lc); + g_clear_object (&priv->focus); + + G_OBJECT_CLASS (nmt_newt_form_parent_class)->finalize (object); +} + +static void +nmt_newt_form_needs_rebuild (NmtNewtWidget *widget) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (widget); + + if (!priv->dirty) { + priv->dirty = TRUE; + nmt_newt_form_redraw (NMT_NEWT_FORM (widget)); + } +} + +static void +nmt_newt_form_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (container); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_form_parent_class); + + g_return_if_fail (widget == priv->content); + + parent_class->remove (container, widget); + priv->content = NULL; +} + +/** + * nmt_newt_form_set_content: + * @form: the #NmtNewtForm + * @content: the form's content + * + * Sets @form's content to be @content. + */ +void +nmt_newt_form_set_content (NmtNewtForm *form, + NmtNewtWidget *content) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_form_parent_class); + + if (priv->content) + nmt_newt_form_remove (NMT_NEWT_CONTAINER (form), priv->content); + + priv->content = content; + + if (priv->content) + parent_class->add (NMT_NEWT_CONTAINER (form), content); +} + +static void +nmt_newt_form_build (NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + int screen_height, screen_width, form_height, form_width; + newtComponent *cos; + int i; + + priv->dirty = FALSE; + nmt_newt_widget_realize (priv->content); + + nmt_newt_widget_size_request (priv->content, &form_width, &form_height); + newtGetScreenSize (&screen_width, &screen_height); + + if (!priv->fixed_width) + priv->width = MIN (form_width + 2 * priv->padding, screen_width - 2); + if (!priv->fixed_height) + priv->height = MIN (form_height + 2 * priv->padding, screen_height - 2); + + if (!priv->fixed_x) + priv->x = (screen_width - form_width) / 2; + if (!priv->fixed_y) + priv->y = (screen_height - form_height) / 2; + + nmt_newt_widget_size_allocate (priv->content, + priv->padding, + priv->padding, + priv->width - 2 * priv->padding, + priv->height - 2 * priv->padding); + + if (priv->height - 2 * priv->padding < form_height) { + newtComponent scroll_bar = + newtVerticalScrollbar (priv->width - 1, 0, priv->height, + NEWT_COLORSET_WINDOW, + NEWT_COLORSET_ACTCHECKBOX); + + priv->form = newtForm (scroll_bar, NULL, NEWT_FLAG_NOF12); + newtFormAddComponent (priv->form, scroll_bar); + newtFormSetHeight (priv->form, priv->height - 2); + } else + priv->form = newtForm (NULL, NULL, NEWT_FLAG_NOF12); + + if (priv->escape_exits) + newtFormAddHotKey (priv->form, NEWT_KEY_ESCAPE); + + cos = nmt_newt_widget_get_components (priv->content); + for (i = 0; cos[i]; i++) + newtFormAddComponent (priv->form, cos[i]); + g_free (cos); + + if (priv->focus) { + newtComponent fco; + + fco = nmt_newt_widget_get_focus_component (priv->focus); + if (fco) + newtFormSetCurrent (priv->form, fco); + } +#ifdef HAVE_NEWTFORMGETSCROLLPOSITION + if (priv->scroll_position) + newtFormSetScrollPosition (priv->form, priv->scroll_position); +#endif + + newtOpenWindow (priv->x, priv->y, priv->width, priv->height, priv->title_lc); +} + +static void +nmt_newt_form_destroy (NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + +#ifdef HAVE_NEWTFORMGETSCROLLPOSITION + priv->scroll_position = newtFormGetScrollPosition (priv->form); +#endif + + newtFormDestroy (priv->form); + priv->form = NULL; + newtPopWindow (); + + nmt_newt_widget_unrealize (priv->content); +} + +static void +nmt_newt_form_iterate (NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + NmtNewtWidget *focus; + struct newtExitStruct es; + + if (priv->dirty) { + nmt_newt_form_destroy (form); + nmt_newt_form_build (form); + } + + newtFormSetTimer (priv->form, 1); + newtFormRun (priv->form, &es); + if (es.reason == NEWT_EXIT_TIMER) + return; + + if ( es.reason == NEWT_EXIT_HOTKEY + || es.reason == NEWT_EXIT_ERROR) { + g_clear_object (&priv->focus); + nmt_newt_form_quit (form); + return; + } + + if (es.reason == NEWT_EXIT_COMPONENT) { + focus = nmt_newt_widget_find_component (priv->content, es.u.co); + if (focus) { + nmt_newt_form_set_focus (form, focus); + nmt_newt_widget_activated (focus); + } + } else { + focus = nmt_newt_widget_find_component (priv->content, + newtFormGetCurrent (priv->form)); + if (focus) + nmt_newt_form_set_focus (form, focus); + } +} + +static GSList *form_stack; +static GSource *keypress_source; + +static gboolean +nmt_newt_form_keypress_callback (int fd, + GIOCondition condition, + gpointer user_data) +{ + g_return_val_if_fail (form_stack != NULL, FALSE); + + nmt_newt_form_iterate (form_stack->data); + return TRUE; +} + +static gboolean +nmt_newt_form_timeout_callback (gpointer user_data) +{ + if (form_stack) + nmt_newt_form_iterate (form_stack->data); + return FALSE; +} + +static void +nmt_newt_form_redraw (NmtNewtForm *form) +{ + g_timeout_add (0, nmt_newt_form_timeout_callback, NULL); +} + +static void +nmt_newt_form_real_show (NmtNewtForm *form) +{ + if (!keypress_source) { + GIOChannel *io; + + io = g_io_channel_unix_new (STDIN_FILENO); + keypress_source = g_io_create_watch (io, G_IO_IN); + g_source_set_can_recurse (keypress_source, TRUE); + g_source_set_callback (keypress_source, + (GSourceFunc) nmt_newt_form_keypress_callback, + NULL, NULL); + g_source_attach (keypress_source, NULL); + g_io_channel_unref (io); + } + + nmt_newt_form_build (form); + form_stack = g_slist_prepend (form_stack, g_object_ref (form)); + nmt_newt_form_redraw (form); +} + +/** + * nmt_newt_form_show: + * @form: an #NmtNewtForm + * + * Displays @form and begins running it asynchronously in the default + * #GMainContext. If another form is currently running, it will remain + * visible in the background, but will not be able to receive keyboard + * input until @form exits. + * + * Call nmt_newt_form_quit() to quit the form. + */ +void +nmt_newt_form_show (NmtNewtForm *form) +{ + NMT_NEWT_FORM_GET_CLASS (form)->show (form); +} + +/** + * nmt_newt_form_run_sync: + * @form: an #NmtNewtForm + * + * Displays @form as with nmt_newt_form_show(), but then iterates the + * #GMainContext internally until @form exits. + * + * Returns: the widget whose activation caused @form to exit, or + * %NULL if it was not caused by a widget. FIXME: this exit value is + * sort of weird and may not be 100% accurate anyway. + */ +NmtNewtWidget * +nmt_newt_form_run_sync (NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + + nmt_newt_form_show (form); + while (priv->form) + g_main_context_iteration (NULL, TRUE); + + return priv->focus; +} + +/** + * nmt_newt_form_quit: + * @form: an #NmtNewtForm + * + * Causes @form to exit. + */ +void +nmt_newt_form_quit (NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + + g_return_if_fail (priv->form != NULL); + + nmt_newt_form_destroy (form); + + form_stack = g_slist_remove (form_stack, form); + g_object_unref (form); + + if (form_stack) + nmt_newt_form_iterate (form_stack->data); + else if (keypress_source) { + g_source_destroy (keypress_source); + g_clear_pointer (&keypress_source, g_source_unref); + } +} + +/** + * nmt_newt_form_set_focus: + * @form: an #NmtNewtForm + * @widget: the widget to focus + * + * Focuses @widget in @form. + */ +void +nmt_newt_form_set_focus (NmtNewtForm *form, + NmtNewtWidget *widget) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (form); + + g_return_if_fail (priv->form != NULL); + + if (priv->focus == widget) + return; + + if (priv->focus) + g_object_unref (priv->focus); + priv->focus = widget; + if (priv->focus) + g_object_ref (priv->focus); +} + +static void +nmt_newt_form_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object); + int screen_width, screen_height; + + switch (prop_id) { + case PROP_TITLE: + if (g_value_get_string (value)) { + priv->title_lc = nmt_newt_locale_from_utf8 (g_value_get_string (value)); + } else + priv->title_lc = NULL; + break; + case PROP_FULLSCREEN: + if (g_value_get_boolean (value)) { + newtGetScreenSize (&screen_width, &screen_height); + priv->x = priv->y = 2; + priv->fixed_x = priv->fixed_y = TRUE; + priv->width = screen_width - 4; + priv->height = screen_height - 4; + priv->fixed_width = priv->fixed_height = TRUE; + } + break; + case PROP_X: + if (g_value_get_uint (value)) { + priv->x = g_value_get_uint (value); + priv->fixed_x = TRUE; + } + break; + case PROP_Y: + if (g_value_get_uint (value)) { + priv->y = g_value_get_uint (value); + priv->fixed_y = TRUE; + } + break; + case PROP_WIDTH: + if (g_value_get_uint (value)) { + priv->width = g_value_get_uint (value); + priv->fixed_width = TRUE; + } + break; + case PROP_HEIGHT: + if (g_value_get_uint (value)) { + priv->height = g_value_get_uint (value); + priv->fixed_height = TRUE; + } + break; + case PROP_PADDING: + priv->padding = g_value_get_uint (value); + break; + case PROP_ESCAPE_EXITS: + priv->escape_exits = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_form_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_TITLE: + if (priv->title_lc) { + g_value_take_string (value, nmt_newt_locale_to_utf8 (priv->title_lc)); + } else + g_value_set_string (value, NULL); + break; + case PROP_X: + g_value_set_uint (value, priv->x); + break; + case PROP_Y: + g_value_set_uint (value, priv->y); + break; + case PROP_WIDTH: + g_value_set_uint (value, priv->width); + break; + case PROP_HEIGHT: + g_value_set_uint (value, priv->height); + break; + case PROP_PADDING: + g_value_set_uint (value, priv->padding); + break; + case PROP_ESCAPE_EXITS: + g_value_set_boolean (value, priv->escape_exits); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_form_class_init (NmtNewtFormClass *form_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (form_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (form_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (form_class); + + g_type_class_add_private (form_class, sizeof (NmtNewtFormPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_form_set_property; + object_class->get_property = nmt_newt_form_get_property; + object_class->finalize = nmt_newt_form_finalize; + + widget_class->needs_rebuild = nmt_newt_form_needs_rebuild; + + container_class->remove = nmt_newt_form_remove; + + form_class->show = nmt_newt_form_real_show; + + /** + * NmtNewtForm:title: + * + * The form's title. If non-%NULL, this will be displayed above + * the form in its border. + */ + g_object_class_install_property (object_class, PROP_TITLE, + g_param_spec_string ("title", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:fullscreen: + * + * If %TRUE, the form will fill the entire "screen" (ie, terminal + * window). + */ + g_object_class_install_property (object_class, PROP_FULLSCREEN, + g_param_spec_boolean ("fullscreen", "", "", + FALSE, + G_PARAM_WRITABLE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:x: + * + * The form's x coordinate. By default, the form will be centered + * on the screen. + */ + g_object_class_install_property (object_class, PROP_X, + g_param_spec_uint ("x", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:y: + * + * The form's y coordinate. By default, the form will be centered + * on the screen. + */ + g_object_class_install_property (object_class, PROP_Y, + g_param_spec_uint ("y", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:width: + * + * The form's width. By default, this will be determined by the + * width of the form's content. + */ + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_uint ("width", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:height: + * + * The form's height. By default, this will be determined by the + * height of the form's content. + */ + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_uint ("height", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:padding: + * + * The padding between the form's content and its border. + */ + g_object_class_install_property (object_class, PROP_PADDING, + g_param_spec_uint ("padding", "", "", + 0, G_MAXUINT, 1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:escape-exits: + * + * If %TRUE, then hitting the Escape key will cause the form to + * exit. + */ + g_object_class_install_property (object_class, PROP_ESCAPE_EXITS, + g_param_spec_boolean ("escape-exits", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + G_PARAM_CONSTRUCT_ONLY)); +} diff --git a/tui/newt/nmt-newt-form.h b/tui/newt/nmt-newt-form.h new file mode 100644 index 0000000000..e320d8723c --- /dev/null +++ b/tui/newt/nmt-newt-form.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_FORM_H +#define NMT_NEWT_FORM_H + +#include "nmt-newt-container.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_FORM (nmt_newt_form_get_type ()) +#define NMT_NEWT_FORM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_FORM, NmtNewtForm)) +#define NMT_NEWT_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_FORM, NmtNewtFormClass)) +#define NMT_IS_NEWT_FORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_FORM)) +#define NMT_IS_NEWT_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_FORM)) +#define NMT_NEWT_FORM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_FORM, NmtNewtFormClass)) + +struct _NmtNewtForm { + NmtNewtContainer parent; + +}; + +typedef struct { + NmtNewtContainerClass parent; + + void (*show) (NmtNewtForm *form); + +} NmtNewtFormClass; + +GType nmt_newt_form_get_type (void); + +NmtNewtForm *nmt_newt_form_new (const char *title); +NmtNewtForm *nmt_newt_form_new_fullscreen (const char *title); + +void nmt_newt_form_set_content (NmtNewtForm *form, + NmtNewtWidget *content); + +void nmt_newt_form_show (NmtNewtForm *form); +NmtNewtWidget *nmt_newt_form_run_sync (NmtNewtForm *form); +void nmt_newt_form_quit (NmtNewtForm *form); + +void nmt_newt_form_set_focus (NmtNewtForm *form, + NmtNewtWidget *widget); + +G_END_DECLS + +#endif /* NMT_NEWT_FORM_H */ diff --git a/tui/newt/nmt-newt-grid.c b/tui/newt/nmt-newt-grid.c new file mode 100644 index 0000000000..ac96733452 --- /dev/null +++ b/tui/newt/nmt-newt-grid.c @@ -0,0 +1,472 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-grid + * @short_description: Grid container + * + * #NmtNewtGrid is the most general-purpose container widget in NmtNewt. + * + * An #NmtNewtGrid consists of a number of rows and columns. There is + * no pre-established maximum row or columns. Rather, rows and columns + * exist if and only if there are widgets in them. + * + * The width of each column is the width of the widest widget in that + * column, and the height of each row is the height of the tallest + * widget in that row. Empty rows and empty columns take up no space, + * so a grid with a single widget at 0,0 would look exactly the same + * if the widget was at 5,10 instead. + * + * If a widget's cell ends up being larger than the widget's requested + * size, then by default the widget will be centered in its cell. + * However, this can be modified by changing its #NmtNewtGridFlags. + * FIXME: the FILL/ANCHOR flags can be implemented in #NmtNewtWidget + * and so should move there. Less clear about the EXPAND flags, which + * must be implemented by the container... + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-grid.h" + +G_DEFINE_TYPE (NmtNewtGrid, nmt_newt_grid, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_GRID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_GRID, NmtNewtGridPrivate)) + +typedef struct { + NmtNewtWidget *widget; + int x, y; + NmtNewtGridFlags flags; + int req_height, req_width; +} NmtNewtGridChild; + +typedef struct { + GArray *children; + int max_x, max_y; + int *row_heights, *col_widths; + gboolean *expand_rows, *expand_cols; + int n_expand_rows, n_expand_cols; + int req_height, req_width; +} NmtNewtGridPrivate; + +/** + * nmt_newt_grid_new: + * + * Creates a new #NmtNewtGrid + * + * Returns: a new #NmtNewtGrid + */ +NmtNewtWidget * +nmt_newt_grid_new (void) +{ + return g_object_new (NMT_TYPE_NEWT_GRID, NULL); +} + +static void +nmt_newt_grid_init (NmtNewtGrid *grid) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + + priv->children = g_array_new (FALSE, FALSE, sizeof (NmtNewtGridChild)); +} + +static void +nmt_newt_grid_finalize (GObject *object) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (object); + + g_array_unref (priv->children); + g_clear_pointer (&priv->row_heights, g_free); + g_clear_pointer (&priv->col_widths, g_free); + g_clear_pointer (&priv->expand_rows, g_free); + g_clear_pointer (&priv->expand_cols, g_free); + + G_OBJECT_CLASS (nmt_newt_grid_parent_class)->finalize (object); +} + +static int +child_sort_func (gconstpointer a, + gconstpointer b) +{ + NmtNewtGridChild *child_a = (NmtNewtGridChild *)a; + NmtNewtGridChild *child_b = (NmtNewtGridChild *)b; + + if (child_a->y != child_b->y) + return child_a->y - child_b->y; + else + return child_a->x - child_b->x; +} + +static newtComponent * +nmt_newt_grid_get_components (NmtNewtWidget *widget) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (widget); + NmtNewtGridChild *children; + GPtrArray *cos; + newtComponent *child_cos; + int i, c; + + g_array_sort (priv->children, child_sort_func); + children = (NmtNewtGridChild *)priv->children->data; + + cos = g_ptr_array_new (); + + for (i = 0; i < priv->children->len; i++) { + if (!nmt_newt_widget_get_visible (children[i].widget)) + continue; + + child_cos = nmt_newt_widget_get_components (children[i].widget); + for (c = 0; child_cos[c]; c++) + g_ptr_array_add (cos, child_cos[c]); + g_free (child_cos); + } + g_ptr_array_add (cos, NULL); + + return (newtComponent *) g_ptr_array_free (cos, FALSE); +} + +static void +nmt_newt_grid_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtGrid *grid = NMT_NEWT_GRID (widget); + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data; + int row, col, i; + + g_free (priv->row_heights); + g_free (priv->col_widths); + g_free (priv->expand_rows); + g_free (priv->expand_cols); + + priv->row_heights = g_new0 (int, priv->max_y + 1); + priv->col_widths = g_new0 (int, priv->max_x + 1); + priv->expand_rows = g_new0 (gboolean, priv->max_y + 1); + priv->expand_cols = g_new0 (gboolean, priv->max_x + 1); + priv->n_expand_rows = priv->n_expand_cols = 0; + + for (row = 0; row < priv->max_y + 1; row++) { + for (col = 0; col < priv->max_x + 1; col++) { + for (i = 0; i < priv->children->len; i++) { + if (children[i].x != col || children[i].y != row) + continue; + if (!nmt_newt_widget_get_visible (children[i].widget)) + continue; + + nmt_newt_widget_size_request (children[i].widget, + &children[i].req_width, + &children[i].req_height); + if (children[i].req_height > priv->row_heights[row]) + priv->row_heights[row] = children[i].req_height; + if (children[i].req_width > priv->col_widths[col]) + priv->col_widths[col] = children[i].req_width; + + if ( (children[i].flags & NMT_NEWT_GRID_EXPAND_X) + && !priv->expand_cols[children[i].x]) { + priv->expand_cols[children[i].x] = TRUE; + priv->n_expand_cols++; + } + if ( (children[i].flags & NMT_NEWT_GRID_EXPAND_Y) + && !priv->expand_rows[children[i].y]) { + priv->expand_rows[children[i].y] = TRUE; + priv->n_expand_rows++; + } + } + } + } + + priv->req_height = priv->req_width = 0; + for (row = 0; row < priv->max_y + 1; row++) + priv->req_height += priv->row_heights[row]; + for (col = 0; col < priv->max_x + 1; col++) + priv->req_width += priv->col_widths[col]; + + *height = priv->req_height; + *width = priv->req_width; +} + +static void +nmt_newt_grid_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (widget); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data, *child; + int i, row, col; + int child_x, child_y, child_width, child_height; + int extra, extra_all, extra_some; + + extra = width - priv->req_width; + if (extra > 0 && priv->n_expand_cols) { + extra_all = extra / priv->n_expand_cols; + extra_some = extra % priv->n_expand_cols; + + for (col = 0; col < priv->max_x + 1; col++) { + if (!priv->expand_cols[col]) + continue; + priv->col_widths[col] += extra_all; + if (extra_some) { + priv->col_widths[col]++; + extra_some--; + } + } + } + + extra = height - priv->req_height; + if (extra > 0 && priv->n_expand_rows) { + extra_all = extra / priv->n_expand_rows; + extra_some = extra % priv->n_expand_rows; + + for (row = 0; row < priv->max_y + 1; row++) { + if (!priv->expand_rows[row]) + continue; + priv->row_heights[row] += extra_all; + if (extra_some) { + priv->row_heights[row]++; + extra_some--; + } + } + } + + for (i = 0; i < priv->children->len; i++) { + child = &children[i]; + if (!nmt_newt_widget_get_visible (child->widget)) + continue; + + child_x = x; + for (col = 0; col < child->x; col++) + child_x += priv->col_widths[col]; + + if ((child->flags & NMT_NEWT_GRID_FILL_X) == NMT_NEWT_GRID_FILL_X) { + child_width = priv->col_widths[child->x]; + } else { + child_width = child->req_width; + if (child->flags & NMT_NEWT_GRID_ANCHOR_RIGHT) + child_x += priv->col_widths[child->x] - child->req_width; + else if (!(child->flags & NMT_NEWT_GRID_ANCHOR_LEFT)) + child_x += (priv->col_widths[child->x] - child->req_width) / 2; + } + + child_y = y; + for (row = 0; row < child->y; row++) + child_y += priv->row_heights[row]; + + if ((child->flags & NMT_NEWT_GRID_FILL_Y) == NMT_NEWT_GRID_FILL_Y) { + child_height = priv->row_heights[child->y]; + } else { + child_height = child->req_height; + if (child->flags & NMT_NEWT_GRID_ANCHOR_BOTTOM) + child_y += priv->row_heights[child->y] - child->req_height; + else if (!(child->flags & NMT_NEWT_GRID_ANCHOR_TOP)) + child_y += (priv->row_heights[child->y] - child->req_height) / 2; + } + + nmt_newt_widget_size_allocate (child->widget, + child_x, child_y, + child_width, child_height); + } +} + +static void +nmt_newt_grid_find_size (NmtNewtGrid *grid) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data; + int i; + + priv->max_x = priv->max_y = 0; + for (i = 0; i < priv->children->len; i++) { + if (children[i].x > priv->max_x) + priv->max_x = children[i].x; + if (children[i].y > priv->max_y) + priv->max_y = children[i].y; + } +} + +/** + * nmt_newt_grid_add: + * @grid: an #NmtNewtGrid + * @widget: the widget to add + * @x: x coordinate + * @y: y coordinate + * + * Adds @widget to @grid at @x, @y. See the discussion above for more + * details of exactly how this works. + */ +void +nmt_newt_grid_add (NmtNewtGrid *grid, + NmtNewtWidget *widget, + int x, + int y) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild child; + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_grid_parent_class)->add (NMT_NEWT_CONTAINER (grid), widget); + + memset (&child, 0, sizeof (child)); + child.widget = widget; + child.x = x; + child.y = y; + child.flags = NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y; + g_array_append_val (priv->children, child); + + if (x > priv->max_x) + priv->max_x = x; + if (y > priv->max_y) + priv->max_y = y; +} + +static int +find_child (NmtNewtGrid *grid, + NmtNewtWidget *widget) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data; + int i; + + for (i = 0; i < priv->children->len; i++) { + if (children[i].widget == widget) + return i; + } + + return -1; +} + +static void +nmt_newt_grid_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtGrid *grid = NMT_NEWT_GRID (container); + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + int i; + + i = find_child (grid, widget); + if (i != -1) { + g_array_remove_index (priv->children, i); + nmt_newt_grid_find_size (grid); + } + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_grid_parent_class)->remove (container, widget); +} + +/** + * nmt_newt_grid_move: + * @grid: an #NmtNewtGrid + * @widget: a child of @grid + * @x: x coordinate + * @y: y coordinate + * + * Moves @widget to the given new coordinates. + */ +void +nmt_newt_grid_move (NmtNewtGrid *grid, + NmtNewtWidget *widget, + int x, + int y) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data; + int i; + + i = find_child (grid, widget); + if (i != -1 && (children[i].x != x || children[i].y != y)) { + children[i].x = x; + children[i].y = y; + nmt_newt_grid_find_size (grid); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (grid)); + } +} + +/** + * NmtNewtGridFlags: + * @NMT_NEWT_GRID_EXPAND_X: The widget's cell should expand + * horizontally if the grid as a whole is given more width than + * it requested. + * @NMT_NEWT_GRID_EXPAND_Y: The widget's cell should expand + * vertically if the grid as a whole is given more height than + * it requested. + * @NMT_NEWT_GRID_ANCHOR_LEFT: If the widget's cell is wider than + * the widget requested, the widget should be anchored to the + * left of its cell rather than being centered. + * @NMT_NEWT_GRID_ANCHOR_RIGHT: If the widget's cell is wider than + * the widget requested, the widget should be anchored to the + * right of its cell rather than being centered. + * @NMT_NEWT_GRID_FILL_X: If the widget's cell is wider than + * the widget requested, the widget should be allocated the + * full width of the cell; this is equivalent to specifying + * both %NMT_NEWT_GRID_ANCHOR_LEFT and %NMT_NEWT_GRID_ANCHOR_RIGHT. + * @NMT_NEWT_GRID_ANCHOR_TOP: If the widget's cell is taller than + * the widget requested, the widget should be anchored to the + * top of its cell rather than being centered. + * @NMT_NEWT_GRID_ANCHOR_BOTTOM: If the widget's cell is taller than + * the widget requested, the widget should be anchored to the + * bottom of its cell rather than being centered. + * @NMT_NEWT_GRID_FILL_Y: If the widget's cell is taller than + * the widget requested, the widget should be allocated the + * full height of the cell; this is equivalent to specifying + * both %NMT_NEWT_GRID_ANCHOR_TOP and %NMT_NEWT_GRID_ANCHOR_BOTTOM. + * + * Flags describing how a widget is placed within its grid cell. + */ + +/** + * nmt_newt_grid_set_flags: + * @grid: an #NmtNewtGrid + * @widget: a child of @grid + * @flags: #NmtNewtGridFlags for @widget + * + * Sets the #NmtNewtGridFlags on @widget + */ +void +nmt_newt_grid_set_flags (NmtNewtGrid *grid, + NmtNewtWidget *widget, + NmtNewtGridFlags flags) +{ + NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid); + NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data; + int i; + + i = find_child (grid, widget); + if (i != -1) + children[i].flags = flags; +} + +static void +nmt_newt_grid_class_init (NmtNewtGridClass *grid_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (grid_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (grid_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (grid_class); + + g_type_class_add_private (grid_class, sizeof (NmtNewtGridPrivate)); + + /* virtual methods */ + object_class->finalize = nmt_newt_grid_finalize; + + widget_class->get_components = nmt_newt_grid_get_components; + widget_class->size_request = nmt_newt_grid_size_request; + widget_class->size_allocate = nmt_newt_grid_size_allocate; + + container_class->remove = nmt_newt_grid_remove; +} diff --git a/tui/newt/nmt-newt-grid.h b/tui/newt/nmt-newt-grid.h new file mode 100644 index 0000000000..f36a38ee8f --- /dev/null +++ b/tui/newt/nmt-newt-grid.h @@ -0,0 +1,72 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_GRID_H +#define NMT_NEWT_GRID_H + +#include "nmt-newt-container.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_GRID (nmt_newt_grid_get_type ()) +#define NMT_NEWT_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_GRID, NmtNewtGrid)) +#define NMT_NEWT_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_GRID, NmtNewtGridClass)) +#define NMT_IS_NEWT_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_GRID)) +#define NMT_IS_NEWT_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_GRID)) +#define NMT_NEWT_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_GRID, NmtNewtGridClass)) + +struct _NmtNewtGrid { + NmtNewtContainer parent; + +}; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtNewtGridClass; + +GType nmt_newt_grid_get_type (void); + +typedef enum { + NMT_NEWT_GRID_EXPAND_X = (1 << 0), + NMT_NEWT_GRID_EXPAND_Y = (1 << 1), + NMT_NEWT_GRID_ANCHOR_LEFT = (1 << 2), + NMT_NEWT_GRID_ANCHOR_RIGHT = (1 << 3), + NMT_NEWT_GRID_FILL_X = NMT_NEWT_GRID_ANCHOR_LEFT | NMT_NEWT_GRID_ANCHOR_RIGHT, + NMT_NEWT_GRID_ANCHOR_TOP = (1 << 4), + NMT_NEWT_GRID_ANCHOR_BOTTOM = (1 << 5), + NMT_NEWT_GRID_FILL_Y = NMT_NEWT_GRID_ANCHOR_TOP | NMT_NEWT_GRID_ANCHOR_BOTTOM, +} NmtNewtGridFlags; + +NmtNewtWidget *nmt_newt_grid_new (void); + +void nmt_newt_grid_add (NmtNewtGrid *grid, + NmtNewtWidget *widget, + int x, + int y); +void nmt_newt_grid_move (NmtNewtGrid *grid, + NmtNewtWidget *widget, + int x, + int y); +void nmt_newt_grid_set_flags (NmtNewtGrid *grid, + NmtNewtWidget *widget, + NmtNewtGridFlags flags); + +G_END_DECLS + +#endif /* NMT_NEWT_GRID_H */ diff --git a/tui/newt/nmt-newt-hacks.c b/tui/newt/nmt-newt-hacks.c new file mode 100644 index 0000000000..2d9b1725ae --- /dev/null +++ b/tui/newt/nmt-newt-hacks.c @@ -0,0 +1,103 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-hacks + * @short_description: Hacks! + * + * This contains hacky cheating implementations of certain newt + * functions that were added after 0.52.15. + */ + +#include "config.h" + +#include "nmt-newt-hacks.h" + +#if !defined (HAVE_NEWTCOMPONENTGETSIZE) || !defined (HAVE_NEWTENTRYGETCURSORPOSITION) +struct newtComponent_0_52_15_struct_hack { + int height, width; + int top, left; + int takesFocus; + int isMapped; + + struct componentOps *ops; + + newtCallback callback; + void *callbackData; + + newtCallback destroyCallback; + void *destroyCallbackData; + + void *data; +}; +#endif + +#ifndef HAVE_NEWTCOMPONENTGETSIZE +void +newtComponentGetSize (newtComponent component, + int *width, + int *height) +{ + struct newtComponent_0_52_15_struct_hack *hack = (void *) component; + + *width = hack->width; + *height = hack->height; +} + +void +newtComponentGetPosition (newtComponent component, + int *left, + int *top) +{ + struct newtComponent_0_52_15_struct_hack *hack = (void *) component; + + *left = hack->left; + *top = hack->top; +} +#endif + +#ifndef HAVE_NEWTENTRYGETCURSORPOSITION +struct newtEntry_0_52_15_struct_hack { + int flags; + char *buf; + const char **resultPtr; + int bufAlloced; + int bufUsed; + int cursorPosition; + /* ... */ +}; + +int +newtEntryGetCursorPosition (newtComponent component) +{ + struct newtComponent_0_52_15_struct_hack *co_hack = (void *) component; + struct newtEntry_0_52_15_struct_hack *entry_hack = co_hack->data; + + return entry_hack->cursorPosition; +} + +void +newtEntrySetCursorPosition (newtComponent component, + int position) +{ + struct newtComponent_0_52_15_struct_hack *co_hack = (void *) component; + struct newtEntry_0_52_15_struct_hack *entry_hack = co_hack->data; + + entry_hack->cursorPosition = position; +} +#endif diff --git a/tui/newt/nmt-newt-hacks.h b/tui/newt/nmt-newt-hacks.h new file mode 100644 index 0000000000..ba8464af0d --- /dev/null +++ b/tui/newt/nmt-newt-hacks.h @@ -0,0 +1,42 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_HACKS_H +#define NMT_NEWT_HACKS_H + +#include "config.h" + +#include <newt.h> + +#ifndef HAVE_NEWTCOMPONENTGETSIZE +void newtComponentGetSize (newtComponent component, + int *width, + int *height); + +void newtComponentGetPosition (newtComponent component, + int *left, + int *top); +#endif + +#ifndef HAVE_NEWTENTRYGETCURSORPOSITION +int newtEntryGetCursorPosition (newtComponent component); +void newtEntrySetCursorPosition (newtComponent component, + int position); +#endif + +#endif /* NMT_NEWT_HACKS_H */ diff --git a/tui/newt/nmt-newt-label.c b/tui/newt/nmt-newt-label.c new file mode 100644 index 0000000000..a9d44b04f1 --- /dev/null +++ b/tui/newt/nmt-newt-label.c @@ -0,0 +1,323 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-label + * @short_description: Labels + * + * #NmtNewtLabel implements a single-line label. + * + * See also #NmtNewtTextbox, for multiline. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-label.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtLabel, nmt_newt_label, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_LABEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_LABEL, NmtNewtLabelPrivate)) + +typedef struct { + char *text; + NmtNewtLabelStyle style; + gboolean highlight; +} NmtNewtLabelPrivate; + +enum { + PROP_0, + PROP_TEXT, + PROP_STYLE, + PROP_HIGHLIGHT, + + LAST_PROP +}; + +/** + * nmt_newt_label_new: + * @text: the initial label text + * + * Creates a new #NmtNewtLabel + * + * Returns: a new #NmtNewtLabel + */ +NmtNewtWidget * +nmt_newt_label_new (const char *text) +{ + return g_object_new (NMT_TYPE_NEWT_LABEL, + "text", text, + NULL); +} + +/** + * nmt_newt_label_set_text: + * @label: an #NmtNewtLabel + * @text: the new text + * + * Updates @label's text. + */ +void +nmt_newt_label_set_text (NmtNewtLabel *label, + const char *text) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + if (!g_strcmp0 (priv->text, text)) + return; + + g_free (priv->text); + priv->text = g_strdup (text); + + g_object_notify (G_OBJECT (label), "text"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label)); +} + +/** + * nmt_newt_label_get_text: + * @label: an #NmtNewtLabel + * + * Gets @label's text + * + * Returns: @label's text + */ +const char * +nmt_newt_label_get_text (NmtNewtLabel *label) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + return priv->text; +} + +/** + * NmtNewtLabelStyle: + * @NMT_NEWT_LABEL_NORMAL: a normal label + * @NMT_NEWT_LABEL_PLAIN: a "plain-looking" label + * + * The label style. Normal labels are blue. Plain labels are black, + * making them look more like they are text in their own right rather + * than just being a label for something else. + */ + +/** + * nmt_newt_label_set_style: + * @label: an #NmtNewtLabel + * @style: the #NmtNewtLabelStyle + * + * Sets the style of @label + */ +void +nmt_newt_label_set_style (NmtNewtLabel *label, + NmtNewtLabelStyle style) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + if (priv->style == style) + return; + + priv->style = style; + g_object_notify (G_OBJECT (label), "style"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label)); +} + +/** + * nmt_newt_label_get_style: + * @label: an #NmtNewtLabel + * + * Gets the style of @label + * + * Returns: the style of @label + */ +NmtNewtLabelStyle +nmt_newt_label_get_style (NmtNewtLabel *label) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + return priv->style; +} + +/** + * nmt_newt_label_set_highlight: + * @label: an #NmtNewtLabel + * @highlight: %TRUE if @label should be highlighted + * + * Sets whether @label is highlighted. Highlighted labels are red; + * this is generally used to highlight invalid widgets. + */ +void +nmt_newt_label_set_highlight (NmtNewtLabel *label, + gboolean highlight) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + highlight = !!highlight; + if (priv->highlight == highlight) + return; + + priv->highlight = highlight; + g_object_notify (G_OBJECT (label), "highlight"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (label)); +} + +/** + * nmt_newt_label_get_highlight: + * @label: an #NmtNewtLabel + * + * Gets whether @label is highlighted. + * + * Returns: whether @label is highlighted. + */ +gboolean +nmt_newt_label_get_highlight (NmtNewtLabel *label) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (label); + + return priv->highlight; +} + +static void +nmt_newt_label_init (NmtNewtLabel *label) +{ +} + +static void +nmt_newt_label_finalize (GObject *object) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (object); + + g_free (priv->text); + + G_OBJECT_CLASS (nmt_newt_label_parent_class)->finalize (object); +} + +static newtComponent +nmt_newt_label_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (component); + newtComponent co; + char *text_lc; + + text_lc = nmt_newt_locale_from_utf8 (priv->text); + co = newtLabel (-1, -1, text_lc); + g_free (text_lc); + + if (priv->highlight) + newtLabelSetColors (co, NMT_NEWT_COLORSET_BAD_LABEL); + else if (priv->style == NMT_NEWT_LABEL_PLAIN) + newtLabelSetColors (co, NMT_NEWT_COLORSET_PLAIN_LABEL); + + return co; +} + +static void +nmt_newt_label_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtLabel *label = NMT_NEWT_LABEL (object); + + switch (prop_id) { + case PROP_TEXT: + nmt_newt_label_set_text (label, g_value_get_string (value)); + break; + case PROP_STYLE: + nmt_newt_label_set_style (label, g_value_get_int (value)); + break; + case PROP_HIGHLIGHT: + nmt_newt_label_set_highlight (label, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_label_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtLabelPrivate *priv = NMT_NEWT_LABEL_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_TEXT: + g_value_set_string (value, priv->text); + break; + case PROP_STYLE: + g_value_set_int (value, priv->style); + break; + case PROP_HIGHLIGHT: + g_value_set_boolean (value, priv->highlight); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_label_class_init (NmtNewtLabelClass *label_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (label_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (label_class); + + g_type_class_add_private (label_class, sizeof (NmtNewtLabelPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_label_set_property; + object_class->get_property = nmt_newt_label_get_property; + object_class->finalize = nmt_newt_label_finalize; + + component_class->build_component = nmt_newt_label_build_component; + + /** + * NmtNewtLabel:text: + * + * The label's text + */ + g_object_class_install_property (object_class, PROP_TEXT, + g_param_spec_string ("text", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtLabel:style: + * + * The label's #NmtNewtLabelStyle + */ + g_object_class_install_property (object_class, PROP_STYLE, + g_param_spec_int ("style", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtLabel:highlight: + * + * Whether the label is highlighted. + */ + g_object_class_install_property (object_class, PROP_HIGHLIGHT, + g_param_spec_boolean ("highlight", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-label.h b/tui/newt/nmt-newt-label.h new file mode 100644 index 0000000000..a4e0dcca5c --- /dev/null +++ b/tui/newt/nmt-newt-label.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_LABEL_H +#define NMT_NEWT_LABEL_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_LABEL (nmt_newt_label_get_type ()) +#define NMT_NEWT_LABEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabel)) +#define NMT_NEWT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_LABEL, NmtNewtLabelClass)) +#define NMT_IS_NEWT_LABEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_LABEL)) +#define NMT_IS_NEWT_LABEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_LABEL)) +#define NMT_NEWT_LABEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_LABEL, NmtNewtLabelClass)) + +struct _NmtNewtLabel { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtLabelClass; + +GType nmt_newt_label_get_type (void); + +typedef enum { + NMT_NEWT_LABEL_NORMAL, + NMT_NEWT_LABEL_PLAIN +} NmtNewtLabelStyle; + +NmtNewtWidget *nmt_newt_label_new (const char *text); + +void nmt_newt_label_set_text (NmtNewtLabel *label, + const char *text); +const char *nmt_newt_label_get_text (NmtNewtLabel *label); + +void nmt_newt_label_set_style (NmtNewtLabel *label, + NmtNewtLabelStyle style); +NmtNewtLabelStyle nmt_newt_label_get_style (NmtNewtLabel *label); + +void nmt_newt_label_set_highlight (NmtNewtLabel *label, + gboolean highlight); +gboolean nmt_newt_label_get_highlight (NmtNewtLabel *label); + +G_END_DECLS + +#endif /* NMT_NEWT_LABEL_H */ diff --git a/tui/newt/nmt-newt-listbox.c b/tui/newt/nmt-newt-listbox.c new file mode 100644 index 0000000000..2e3655aaa4 --- /dev/null +++ b/tui/newt/nmt-newt-listbox.c @@ -0,0 +1,539 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-listbox + * @short_description: Single-choice listboxes + * + * #NmtNewtListbox implements a single-choice listbox. + * + * A listbox has some number of rows, each associated with an + * arbitrary pointer value. The pointer values do not need to be + * unique, but some APIs will not be usable if they aren't. You + * can also cause rows with %NULL keys to be treated specially. + * + * The listbox will emit #NmtNewtWidget::activate when the user + * presses Return on a selection. + */ + +#include "config.h" + +#include "nmt-newt-listbox.h" +#include "nmt-newt-form.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtListbox, nmt_newt_listbox, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_LISTBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxPrivate)) + +typedef struct { + int height, alloc_height, width; + gboolean fixed_height; + NmtNewtListboxFlags flags; + + GPtrArray *entries; + GPtrArray *keys; + + int active; + gpointer active_key; + gboolean skip_null_keys; + +} NmtNewtListboxPrivate; + +enum { + PROP_0, + PROP_HEIGHT, + PROP_FLAGS, + PROP_ACTIVE, + PROP_ACTIVE_KEY, + PROP_SKIP_NULL_KEYS, + + LAST_PROP +}; + +/** + * NmtNewtListboxFlags: + * @NMT_NEWT_LISTBOX_SCROLL: the listbox should have a scroll bar. + * @NMT_NEWT_LISTBOX_BORDER: the listbox should have a border around it. + * + * Flags describing an #NmtNewtListbox + */ + +/** + * nmt_newt_listbox_new: + * @height: the height of the listbox, or -1 for no fixed height + * @flags: the listbox flags + * + * Creates a new #NmtNewtListbox + * + * Returns: a new #NmtNewtListbox + */ +NmtNewtWidget * +nmt_newt_listbox_new (int height, + NmtNewtListboxFlags flags) +{ + return g_object_new (NMT_TYPE_NEWT_LISTBOX, + "height", height, + "flags", flags, + NULL); +} + +/** + * nmt_newt_listbox_append: + * @listbox: an #NmtNewtListbox + * @entry: the text for the new row + * @key: (allow-none): the key associated with @entry + * + * Adds a row to @listbox. + */ +void +nmt_newt_listbox_append (NmtNewtListbox *listbox, + const char *entry, + gpointer key) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + g_ptr_array_add (priv->entries, nmt_newt_locale_from_utf8 (entry)); + g_ptr_array_add (priv->keys, key); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (listbox)); +} + +/** + * nmt_newt_listbox_clear: + * @listbox: an #NmtNewtListbox + * + * Clears the contents of @listbox. + */ +void +nmt_newt_listbox_clear (NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + g_ptr_array_set_size (priv->entries, 0); + g_ptr_array_set_size (priv->keys, 0); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (listbox)); +} + +/** + * nmt_newt_listbox_set_active: + * @listbox: an #NmtNewtListbox + * @active: the row to make active + * + * Sets @active to be the currently-selected row in @listbox, + * scrolling it into view if needed. + */ +void +nmt_newt_listbox_set_active (NmtNewtListbox *listbox, + int active) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + if (active == priv->active) + return; + + g_return_if_fail (active >= 0 && active < priv->entries->len); + g_return_if_fail (!priv->skip_null_keys || priv->keys->pdata[active]); + + priv->active = active; + priv->active_key = priv->keys->pdata[active]; + + g_object_notify (G_OBJECT (listbox), "active"); + g_object_notify (G_OBJECT (listbox), "active-key"); +} + +/** + * nmt_newt_listbox_set_active_key: + * @listbox: an #NmtNewtListbox + * @active_key: the key for the row to make active + * + * Selects the (first) row in @listbox with @active_key as its key, + * scrolling it into view if needed. + */ +void +nmt_newt_listbox_set_active_key (NmtNewtListbox *listbox, + gpointer active_key) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + int i; + + if (active_key == priv->active_key) + return; + + g_return_if_fail (!priv->skip_null_keys || active_key); + + for (i = 0; i < priv->keys->len; i++) { + if (priv->keys->pdata[i] == active_key) { + priv->active = i; + priv->active_key = active_key; + + g_object_notify (G_OBJECT (listbox), "active"); + g_object_notify (G_OBJECT (listbox), "active-key"); + return; + } + } +} + +/** + * nmt_newt_listbox_get_active: + * @listbox: an #NmtNewtListbox + * + * Gets the currently-selected row in @listbox. + * + * Returns: the currently-selected row in @listbox. + */ +int +nmt_newt_listbox_get_active (NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + return priv->active; +} + +/** + * nmt_newt_listbox_get_active_key: + * @listbox: an #NmtNewtListbox + * + * Gets the key of the currently-selected row in @listbox. + * + * Returns: the key of the currently-selected row in @listbox. + */ +gpointer +nmt_newt_listbox_get_active_key (NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + return priv->active_key; +} + +/** + * nmt_newt_listbox_set_height: + * @listbox: an #NmtNewtListbox + * @height: the new height, or -1 for no fixed heigh + * + * Updates @listbox's height. + */ +void +nmt_newt_listbox_set_height (NmtNewtListbox *listbox, + int height) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + priv->height = height; + priv->fixed_height = priv->height != 0; + g_object_notify (G_OBJECT (listbox), "height"); +} + +static void +nmt_newt_listbox_init (NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + priv->entries = g_ptr_array_new_with_free_func (g_free); + priv->keys = g_ptr_array_new (); + + priv->active = -1; +} + +static void +nmt_newt_listbox_finalize (GObject *object) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object); + + g_ptr_array_unref (priv->entries); + g_ptr_array_unref (priv->keys); + + G_OBJECT_CLASS (nmt_newt_listbox_parent_class)->finalize (object); +} + +static void +nmt_newt_listbox_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (widget); + + NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)-> + size_request (widget, width, height); + + priv->alloc_height = -1; + if (!priv->fixed_height) + *height = 1; + priv->width = *width; +} + +static void +nmt_newt_listbox_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (widget); + + if (width > priv->width) { + newtListboxSetWidth (nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget)), + width); + } + + NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)-> + size_allocate (widget, x, y, width, height); + + priv->alloc_height = height; + + if (!priv->fixed_height && height != priv->height) { + priv->height = height; + nmt_newt_widget_needs_rebuild (widget); + } +} + +static void +update_active_internal (NmtNewtListbox *listbox, + int new_active) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + + if (priv->active == new_active) + return; + if (new_active >= priv->keys->len) + return; + + if (priv->skip_null_keys && !priv->keys->pdata[new_active]) { + if (new_active > priv->active) { + while ( new_active < priv->entries->len + && !priv->keys->pdata[new_active]) + new_active++; + } else { + while ( new_active >= 0 + && !priv->keys->pdata[new_active]) + new_active--; + } + + if ( new_active < 0 + || new_active >= priv->entries->len + || !priv->keys->pdata[new_active]) { + g_assert (priv->active >= 0 && priv->active < priv->entries->len); + return; + } + } + + nmt_newt_listbox_set_active (listbox, new_active); +} + +static void +selection_changed_callback (newtComponent co, + void *user_data) +{ + NmtNewtListbox *listbox = user_data; + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (listbox); + int new_active; + + new_active = GPOINTER_TO_UINT (newtListboxGetCurrent (co)); + update_active_internal (listbox, new_active); + + if (priv->active != new_active) + newtListboxSetCurrent (co, priv->active); +} + +static guint +convert_flags (NmtNewtListboxFlags flags) +{ + guint newt_flags = NEWT_FLAG_RETURNEXIT; + + if (flags & NMT_NEWT_LISTBOX_SCROLL) + newt_flags |= NEWT_FLAG_SCROLL; + if (flags & NMT_NEWT_LISTBOX_BORDER) + newt_flags |= NEWT_FLAG_BORDER; + + return newt_flags; +} + +static newtComponent +nmt_newt_listbox_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (component); + newtComponent co; + int i, active; + + if (priv->active == -1) + update_active_internal (NMT_NEWT_LISTBOX (component), 0); + active = priv->active; + + co = newtListbox (-1, -1, priv->height, convert_flags (priv->flags)); + newtComponentAddCallback (co, selection_changed_callback, component); + + for (i = 0; i < priv->entries->len; i++) { + newtListboxAppendEntry (co, priv->entries->pdata[i], GUINT_TO_POINTER (i)); + if (active == -1 && priv->keys->pdata[i] == priv->active_key) + active = i; + } + + if (active != -1) + newtListboxSetCurrent (co, active); + + return co; +} + +static void +nmt_newt_listbox_activated (NmtNewtWidget *widget) +{ + NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (widget); + newtComponent co = nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget)); + + nmt_newt_listbox_set_active (listbox, GPOINTER_TO_UINT (newtListboxGetCurrent (co))); + + NMT_NEWT_WIDGET_CLASS (nmt_newt_listbox_parent_class)->activated (widget); +} + +static void +nmt_newt_listbox_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (object); + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_HEIGHT: + priv->height = g_value_get_int (value); + priv->fixed_height = (priv->height != 0); + break; + case PROP_FLAGS: + priv->flags = g_value_get_uint (value); + break; + case PROP_ACTIVE: + nmt_newt_listbox_set_active (listbox, g_value_get_int (value)); + break; + case PROP_ACTIVE_KEY: + nmt_newt_listbox_set_active_key (listbox, g_value_get_pointer (value)); + break; + case PROP_SKIP_NULL_KEYS: + priv->skip_null_keys = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_listbox_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_HEIGHT: + g_value_set_int (value, priv->height); + break; + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_ACTIVE: + g_value_set_int (value, priv->active); + break; + case PROP_ACTIVE_KEY: + g_value_set_pointer (value, priv->active_key); + break; + case PROP_SKIP_NULL_KEYS: + g_value_set_boolean (value, priv->skip_null_keys); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_listbox_class_init (NmtNewtListboxClass *listbox_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (listbox_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (listbox_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (listbox_class); + + g_type_class_add_private (listbox_class, sizeof (NmtNewtListboxPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_listbox_set_property; + object_class->get_property = nmt_newt_listbox_get_property; + object_class->finalize = nmt_newt_listbox_finalize; + + widget_class->size_request = nmt_newt_listbox_size_request; + widget_class->size_allocate = nmt_newt_listbox_size_allocate; + widget_class->activated = nmt_newt_listbox_activated; + + component_class->build_component = nmt_newt_listbox_build_component; + + /* properties */ + + /** + * NmtNewtListbox:height: + * + * The listbox's height, or -1 if it has no fixed height. + */ + g_object_class_install_property (object_class, PROP_HEIGHT, + g_param_spec_int ("height", "", "", + -1, 255, -1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtListbox:flags: + * + * The listbox's #NmtNewtListboxFlags. + */ + g_object_class_install_property (object_class, PROP_FLAGS, + g_param_spec_uint ("flags", "", "", + 0, 0xFFFF, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtListbox:active: + * + * The currently-selected row. + */ + g_object_class_install_property (object_class, PROP_ACTIVE, + g_param_spec_int ("active", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtListbox:active-key: + * + * The key of the currently-selected row. + */ + g_object_class_install_property (object_class, PROP_ACTIVE_KEY, + g_param_spec_pointer ("active-key", "", "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtListbox:skip-null-keys: + * + * If %TRUE, rows with %NULL key values will be skipped over when + * navigating the list with the arrow keys. + */ + g_object_class_install_property (object_class, PROP_SKIP_NULL_KEYS, + g_param_spec_boolean ("skip-null-keys", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-listbox.h b/tui/newt/nmt-newt-listbox.h new file mode 100644 index 0000000000..da29207418 --- /dev/null +++ b/tui/newt/nmt-newt-listbox.h @@ -0,0 +1,71 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_LISTBOX_H +#define NMT_NEWT_LISTBOX_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_LISTBOX (nmt_newt_listbox_get_type ()) +#define NMT_NEWT_LISTBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListbox)) +#define NMT_NEWT_LISTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass)) +#define NMT_IS_NEWT_LISTBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_LISTBOX)) +#define NMT_IS_NEWT_LISTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_LISTBOX)) +#define NMT_NEWT_LISTBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass)) + +struct _NmtNewtListbox { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtListboxClass; + +GType nmt_newt_listbox_get_type (void); + +typedef enum { + NMT_NEWT_LISTBOX_SCROLL = (1 << 0), + NMT_NEWT_LISTBOX_BORDER = (1 << 1) +} NmtNewtListboxFlags; + +NmtNewtWidget *nmt_newt_listbox_new (int height, + NmtNewtListboxFlags flags); + +void nmt_newt_listbox_set_height (NmtNewtListbox *listbox, + int active); + +void nmt_newt_listbox_append (NmtNewtListbox *listbox, + const char *entry, + gpointer key); +void nmt_newt_listbox_clear (NmtNewtListbox *listbox); + +void nmt_newt_listbox_set_active (NmtNewtListbox *listbox, + int active); +void nmt_newt_listbox_set_active_key (NmtNewtListbox *listbox, + gpointer active_key); + +int nmt_newt_listbox_get_active (NmtNewtListbox *listbox); +gpointer nmt_newt_listbox_get_active_key (NmtNewtListbox *listbox); + +G_END_DECLS + +#endif /* NMT_NEWT_LISTBOX_H */ diff --git a/tui/newt/nmt-newt-popup.c b/tui/newt/nmt-newt-popup.c new file mode 100644 index 0000000000..e9757eac20 --- /dev/null +++ b/tui/newt/nmt-newt-popup.c @@ -0,0 +1,343 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-popup + * @short_description: Pop-up menus + * + * #NmtNewtPopup implements a pop-up menu. When inactive, they appear + * the same as #NmtNewtButtons, displaying the label from the + * #NmtNewtPopup:active entry. When activated, they pop up a temporary + * #NmtNewtForm containing an #NmtNewtListbox to select from. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-newt-popup.h" +#include "nmt-newt-form.h" +#include "nmt-newt-hacks.h" +#include "nmt-newt-listbox.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtPopup, nmt_newt_popup, NMT_TYPE_NEWT_BUTTON) + +#define NMT_NEWT_POPUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_POPUP, NmtNewtPopupPrivate)) + +typedef struct { + GArray *entries; + int active; +} NmtNewtPopupPrivate; + +enum { + PROP_0, + PROP_ACTIVE, + PROP_ACTIVE_ID, + + LAST_PROP +}; + +/** + * NmtNewtPopupEntry: + * @label: the user-visible label for the entry + * @id: the internal ID of the entry + * + * A single entry in a pop-up menu. + */ + +/** + * nmt_newt_popup_new: + * @entries: an array of #NmtNewtPopupEntry, terminated by an + * entry with a %NULL label + * + * Creates a new #NmtNewtPopup with the given entries. + * + * Returns: a new #NmtNewtPopup + */ +NmtNewtWidget * +nmt_newt_popup_new (NmtNewtPopupEntry *entries) +{ + NmtNewtWidget *widget; + NmtNewtPopupPrivate *priv; + int i; + + widget = g_object_new (NMT_TYPE_NEWT_POPUP, NULL); + priv = NMT_NEWT_POPUP_GET_PRIVATE (widget); + + for (i = 0; entries[i].label; i++) { + NmtNewtPopupEntry entry; + + entry.label = nmt_newt_locale_from_utf8 (_(entries[i].label)); + entry.id = g_strdup (entries[i].id); + g_array_append_val (priv->entries, entry); + } + + return widget; +} + +static void +popup_entry_clear_func (NmtNewtPopupEntry *entry) +{ + g_free (entry->label); + g_free (entry->id); +} + +static void +nmt_newt_popup_init (NmtNewtPopup *popup) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup); + + priv->entries = g_array_sized_new (FALSE, FALSE, sizeof (NmtNewtPopupEntry), 10); + g_array_set_clear_func (priv->entries, (GDestroyNotify) popup_entry_clear_func); +} + +static void +nmt_newt_popup_finalize (GObject *object) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (object); + + g_array_unref (priv->entries); + + G_OBJECT_CLASS (nmt_newt_popup_parent_class)->finalize (object); +} + +static newtComponent +nmt_newt_popup_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (component); + NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data; + + nmt_newt_button_set_label (NMT_NEWT_BUTTON (component), + entries[priv->active].label); + return NMT_NEWT_COMPONENT_CLASS (nmt_newt_popup_parent_class)-> + build_component (component, sensitive); +} + +static void +nmt_newt_popup_activated (NmtNewtWidget *widget) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (widget); + NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data; + NmtNewtForm *form; + NmtNewtWidget *listbox, *ret; + int button_x, button_y; + int window_x, window_y; + int list_w, list_h; + int i, active; + + listbox = nmt_newt_listbox_new (priv->entries->len, 0); + nmt_newt_widget_set_exit_on_activate (listbox, TRUE); + for (i = 0; i < priv->entries->len; i++) + nmt_newt_listbox_append (NMT_NEWT_LISTBOX (listbox), entries[i].label, NULL); + nmt_newt_listbox_set_active (NMT_NEWT_LISTBOX (listbox), priv->active); + + nmt_newt_widget_size_request (listbox, &list_w, &list_h); + newtComponentGetPosition (nmt_newt_component_get_component (NMT_NEWT_COMPONENT (widget)), + &button_x, &button_y); + window_x = button_x + 4; + window_y = button_y + 2 - priv->active; + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "x", window_x, + "y", window_y, + "width", list_w, + "height", list_h, + "padding", 0, + "escape-exits", TRUE, + NULL); + nmt_newt_form_set_content (form, listbox); + + ret = nmt_newt_form_run_sync (form); + if (ret == listbox) + active = nmt_newt_listbox_get_active (NMT_NEWT_LISTBOX (listbox)); + else + active = priv->active; + + g_object_unref (form); + + if (active != priv->active) { + priv->active = active; + g_object_notify (G_OBJECT (widget), "active"); + g_object_notify (G_OBJECT (widget), "active-id"); + nmt_newt_widget_needs_rebuild (widget); + } + + NMT_NEWT_WIDGET_CLASS (nmt_newt_popup_parent_class)->activated (widget); +} + +/** + * nmt_newt_popup_get_active: + * @popup: a #NmtNewtPopup + * + * Gets the index of the active entry in @popup. + * + * Returns: the index of the active entry in @popup. + */ +int +nmt_newt_popup_get_active (NmtNewtPopup *popup) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup); + + return priv->active; +} + +/** + * nmt_newt_popup_set_active: + * @popup: a #NmtNewtPopup + * @active: the index of the new active entry + * + * Sets the active entry in @popup. + */ +void +nmt_newt_popup_set_active (NmtNewtPopup *popup, + int active) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup); + + active = CLAMP (active, 0, priv->entries->len - 1); + + if (active != priv->active) { + priv->active = active; + g_object_notify (G_OBJECT (popup), "active"); + g_object_notify (G_OBJECT (popup), "active-id"); + } +} + +/** + * nmt_newt_popup_get_active_id: + * @popup: a #NmtNewtPopup + * + * Gets the textual ID of the active entry in @popup. + * + * Returns: the ID of the active entry in @popup. + */ +const char * +nmt_newt_popup_get_active_id (NmtNewtPopup *popup) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup); + NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data; + + return entries[priv->active].id; +} + +/** + * nmt_newt_popup_set_active_id: + * @popup: a #NmtNewtPopup + * @active_id: the ID of the new active entry + * + * Sets the active entry in @popup. + */ +void +nmt_newt_popup_set_active_id (NmtNewtPopup *popup, + const char *active_id) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE (popup); + NmtNewtPopupEntry *entries = (NmtNewtPopupEntry *)priv->entries->data; + int i; + + for (i = 0; i < priv->entries->len; i++) { + if (!g_strcmp0 (active_id, entries[i].id)) { + nmt_newt_popup_set_active (popup, i); + return; + } + } +} + +static void +nmt_newt_popup_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtPopup *popup = NMT_NEWT_POPUP (object); + + switch (prop_id) { + case PROP_ACTIVE: + nmt_newt_popup_set_active (popup, g_value_get_uint (value)); + break; + case PROP_ACTIVE_ID: + nmt_newt_popup_set_active_id (popup, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_popup_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtPopup *popup = NMT_NEWT_POPUP (object); + + switch (prop_id) { + case PROP_ACTIVE: + g_value_set_uint (value, nmt_newt_popup_get_active (popup)); + break; + case PROP_ACTIVE_ID: + g_value_set_string (value, nmt_newt_popup_get_active_id (popup)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_popup_class_init (NmtNewtPopupClass *popup_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (popup_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (popup_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (popup_class); + + g_type_class_add_private (popup_class, sizeof (NmtNewtPopupPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_popup_set_property; + object_class->get_property = nmt_newt_popup_get_property; + object_class->finalize = nmt_newt_popup_finalize; + + widget_class->activated = nmt_newt_popup_activated; + + component_class->build_component = nmt_newt_popup_build_component; + + /** + * NmtNewtPopup:active: + * + * The index of the currently-active entry. + */ + g_object_class_install_property (object_class, PROP_ACTIVE, + g_param_spec_uint ("active", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtPopup:active-id: + * + * The textual ID of the currently-active entry. + */ + g_object_class_install_property (object_class, PROP_ACTIVE_ID, + g_param_spec_string ("active-id", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-popup.h b/tui/newt/nmt-newt-popup.h new file mode 100644 index 0000000000..643ea2d8c4 --- /dev/null +++ b/tui/newt/nmt-newt-popup.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_POPUP_H +#define NMT_NEWT_POPUP_H + +#include "nmt-newt-button.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_POPUP (nmt_newt_popup_get_type ()) +#define NMT_NEWT_POPUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopup)) +#define NMT_NEWT_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_POPUP, NmtNewtPopupClass)) +#define NMT_IS_NEWT_POPUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_POPUP)) +#define NMT_IS_NEWT_POPUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_POPUP)) +#define NMT_NEWT_POPUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_POPUP, NmtNewtPopupClass)) + +struct _NmtNewtPopup { + NmtNewtButton parent; + +}; + +typedef struct { + NmtNewtButtonClass parent; + +} NmtNewtPopupClass; + +GType nmt_newt_popup_get_type (void); + +typedef struct { + char *label; + char *id; +} NmtNewtPopupEntry; + +NmtNewtWidget *nmt_newt_popup_new (NmtNewtPopupEntry *entries); + +int nmt_newt_popup_get_active (NmtNewtPopup *popup); +void nmt_newt_popup_set_active (NmtNewtPopup *popup, + int active); + +const char *nmt_newt_popup_get_active_id (NmtNewtPopup *popup); +void nmt_newt_popup_set_active_id (NmtNewtPopup *popup, + const char *active_id); + +G_END_DECLS + +#endif /* NMT_NEWT_POPUP_H */ diff --git a/tui/newt/nmt-newt-section.c b/tui/newt/nmt-newt-section.c new file mode 100644 index 0000000000..094b41b731 --- /dev/null +++ b/tui/newt/nmt-newt-section.c @@ -0,0 +1,408 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-section + * @short_description: A collapsible section + * + * #NmtNewtSection is a container with two children; the header and + * the body. The header is always visible, but the body is only + * visible when the container is #NmtNewtSection:open. + * + * Note that there is no default way to open and close an + * #NmtNewtSection. You need to implement this yourself. (Eg, by + * binding the #NmtToggleButton:active property of an #NmtToggleButton + * in the section's header to the section's #NmtNewtSection:open + * property.) + * + * In addition to the header and body, the #NmtNewtSection also draws + * a border along the left side, indicating the extent of the section. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-section.h" +#include "nmt-newt-grid.h" +#include "nmt-newt-label.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtSection, nmt_newt_section, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_SECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_SECTION, NmtNewtSectionPrivate)) + +typedef struct { + NmtNewtWidget *header; + int hheight_req, hwidth_req; + + NmtNewtWidget *body; + int bheight_req, bwidth_req; + + NmtNewtWidget *border_grid; + NmtNewtWidget *border_open_label; + NmtNewtWidget *border_closed_label; + NmtNewtWidget *border_end_label; + GPtrArray *border_line_labels; + + gboolean open; +} NmtNewtSectionPrivate; + +static char *closed_glyph, *open_glyph, *line_glyph, *end_glyph; + +enum { + PROP_0, + + PROP_OPEN, + + LAST_PROP +}; + +/** + * nmt_newt_section_new: + * + * Creates a new #NmtNewtSection + * + * Returns: a new #NmtNewtSection + */ +NmtNewtWidget * +nmt_newt_section_new (void) +{ + return g_object_new (NMT_TYPE_NEWT_SECTION, + NULL); +} + +static void +nmt_newt_section_init (NmtNewtSection *section) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class); + + priv->border_grid = nmt_newt_grid_new (); + parent_class->add (NMT_NEWT_CONTAINER (section), priv->border_grid); + + priv->border_open_label = nmt_newt_label_new (open_glyph); + nmt_newt_widget_set_visible (priv->border_open_label, FALSE); + nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_open_label, 0, 0); + + priv->border_closed_label = nmt_newt_label_new (closed_glyph); + nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_closed_label, 0, 0); + + priv->border_end_label = nmt_newt_label_new (end_glyph); + nmt_newt_widget_set_visible (priv->border_open_label, FALSE); + nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), priv->border_end_label, 0, 1); + + priv->border_line_labels = g_ptr_array_new (); +} + +static void +nmt_newt_section_finalize (GObject *object) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object); + + g_ptr_array_unref (priv->border_line_labels); + + G_OBJECT_CLASS (nmt_newt_section_parent_class)->finalize (object); +} + +/** + * nmt_newt_section_set_header: + * @section: an #NmtNewtSection + * @header: the header widget + * + * Sets @section's header widget. + */ +void +nmt_newt_section_set_header (NmtNewtSection *section, + NmtNewtWidget *header) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class); + NmtNewtContainer *container = NMT_NEWT_CONTAINER (section); + + if (priv->header) + parent_class->remove (container, priv->header); + priv->header = header; + parent_class->add (container, header); +} + +/** + * nmt_newt_section_get_header: + * @section: an #NmtNewtSection + * + * Gets @section's header widget. + * + * Returns: @section's header widget. + */ +NmtNewtWidget * +nmt_newt_section_get_header (NmtNewtSection *section) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + + return priv->header; +} + +/** + * nmt_newt_section_set_body: + * @section: an #NmtNewtSection + * @body: the body widget + * + * Sets @section's body widget. + */ +void +nmt_newt_section_set_body (NmtNewtSection *section, + NmtNewtWidget *body) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class); + NmtNewtContainer *container = NMT_NEWT_CONTAINER (section); + + if (priv->body) + parent_class->remove (container, priv->body); + priv->body = body; + parent_class->add (container, body); +} + +/** + * nmt_newt_section_get_body: + * @section: an #NmtNewtSection + * + * Gets @section's body widget. + * + * Returns: @section's body widget. + */ +NmtNewtWidget * +nmt_newt_section_get_body (NmtNewtSection *section) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + + return priv->body; +} + +static void +nmt_newt_section_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtSection *section = NMT_NEWT_SECTION (container); + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_newt_section_parent_class); + + if (widget == priv->header) + priv->header = NULL; + else if (widget == priv->body) + priv->body = NULL; + else if (widget == priv->border_grid) + priv->border_grid = NULL; + + parent_class->remove (container, widget); +} + +static newtComponent * +nmt_newt_section_get_components (NmtNewtWidget *widget) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget); + newtComponent *child_cos; + GPtrArray *cos; + int i; + + g_return_val_if_fail (priv->header != NULL && priv->body != NULL, NULL); + + cos = g_ptr_array_new (); + + child_cos = nmt_newt_widget_get_components (priv->border_grid); + for (i = 0; child_cos[i]; i++) + g_ptr_array_add (cos, child_cos[i]); + g_free (child_cos); + + child_cos = nmt_newt_widget_get_components (priv->header); + for (i = 0; child_cos[i]; i++) + g_ptr_array_add (cos, child_cos[i]); + g_free (child_cos); + + if (priv->open) { + child_cos = nmt_newt_widget_get_components (priv->body); + for (i = 0; child_cos[i]; i++) + g_ptr_array_add (cos, child_cos[i]); + g_free (child_cos); + } + + g_ptr_array_add (cos, NULL); + return (newtComponent *) g_ptr_array_free (cos, FALSE); +} + +static void +nmt_newt_section_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget); + int border_width, border_height; + + g_return_if_fail (priv->header != NULL && priv->body != NULL); + + nmt_newt_widget_size_request (priv->border_grid, &border_width, &border_height); + nmt_newt_widget_size_request (priv->header, &priv->hwidth_req, &priv->hheight_req); + nmt_newt_widget_size_request (priv->body, &priv->bwidth_req, &priv->bheight_req); + + *width = MAX (priv->hwidth_req, priv->bwidth_req) + 2; + *height = priv->open ? priv->hheight_req + priv->bheight_req + 1 : priv->hheight_req; +} + +static void +adjust_border_for_allocation (NmtNewtSectionPrivate *priv, + int height) +{ + int i; + + /* We have to use a series of one-line labels rather than a multi-line + * textbox, because newt will hide any component that's partially offscreen, + * but we want the on-screen portion of the border to show even if part of + * it is offscreen. + */ + + if (height == 1) { + nmt_newt_widget_set_visible (priv->border_closed_label, TRUE); + nmt_newt_widget_set_visible (priv->border_open_label, FALSE); + for (i = 0; i < priv->border_line_labels->len; i++) + nmt_newt_widget_set_visible (priv->border_line_labels->pdata[i], FALSE); + nmt_newt_widget_set_visible (priv->border_end_label, FALSE); + } else { + nmt_newt_widget_set_visible (priv->border_closed_label, FALSE); + nmt_newt_widget_set_visible (priv->border_open_label, TRUE); + for (i = 0; i < height - 2; i++) { + if (i >= priv->border_line_labels->len) { + NmtNewtWidget *label; + + label = nmt_newt_label_new (line_glyph); + g_ptr_array_add (priv->border_line_labels, label); + nmt_newt_grid_add (NMT_NEWT_GRID (priv->border_grid), label, 0, i + 1); + } else + nmt_newt_widget_set_visible (priv->border_line_labels->pdata[i], TRUE); + } + nmt_newt_widget_set_visible (priv->border_end_label, TRUE); + nmt_newt_grid_move (NMT_NEWT_GRID (priv->border_grid), priv->border_end_label, 0, height - 1); + } +} + +static void +nmt_newt_section_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (widget); + + adjust_border_for_allocation (priv, height); + + nmt_newt_widget_size_allocate (priv->border_grid, x, y, 1, height); + nmt_newt_widget_size_allocate (priv->header, x + 2, y, width, priv->hheight_req); + if (priv->open) { + nmt_newt_widget_size_allocate (priv->body, x + 2, y + priv->hheight_req, + width, height - priv->hheight_req); + } +} + +static void +nmt_newt_section_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_OPEN: + priv->open = g_value_get_boolean (value); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (object)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_section_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_OPEN: + g_value_set_boolean (value, priv->open); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_section_class_init (NmtNewtSectionClass *section_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (section_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (section_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (section_class); + + g_type_class_add_private (section_class, sizeof (NmtNewtSectionPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_section_set_property; + object_class->get_property = nmt_newt_section_get_property; + object_class->finalize = nmt_newt_section_finalize; + + widget_class->get_components = nmt_newt_section_get_components; + widget_class->size_request = nmt_newt_section_size_request; + widget_class->size_allocate = nmt_newt_section_size_allocate; + + container_class->remove = nmt_newt_section_remove; + + /* properties */ + + /** + * NmtNewtSection:open: + * + * %TRUE if the section is open (ie, its body is visible), %FALSE + * if not. + */ + g_object_class_install_property (object_class, PROP_OPEN, + g_param_spec_boolean ("open", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /* globals */ + closed_glyph = nmt_newt_locale_from_utf8 ("\342\225\220"); /* ═ */ + open_glyph = nmt_newt_locale_from_utf8 ("\342\225\244"); /* ╤ */ + line_glyph = nmt_newt_locale_from_utf8 ("\342\224\202"); /* │ */ + end_glyph = nmt_newt_locale_from_utf8 ("\342\224\224"); /* └ */ + if (!closed_glyph || !open_glyph || !line_glyph || !end_glyph) { + g_clear_pointer (&closed_glyph, g_free); + g_clear_pointer (&open_glyph, g_free); + g_clear_pointer (&line_glyph, g_free); + g_clear_pointer (&end_glyph, g_free); + + closed_glyph = g_strdup ("-"); + open_glyph = g_strdup ("+"); + line_glyph = g_strdup ("|"); + end_glyph = g_strdup ("\\"); + } +} diff --git a/tui/newt/nmt-newt-section.h b/tui/newt/nmt-newt-section.h new file mode 100644 index 0000000000..a943ba2000 --- /dev/null +++ b/tui/newt/nmt-newt-section.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_SECTION_H +#define NMT_NEWT_SECTION_H + +#include "nmt-newt-container.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_SECTION (nmt_newt_section_get_type ()) +#define NMT_NEWT_SECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSection)) +#define NMT_NEWT_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass)) +#define NMT_IS_NEWT_SECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_SECTION)) +#define NMT_IS_NEWT_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_SECTION)) +#define NMT_NEWT_SECTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass)) + +struct _NmtNewtSection { + NmtNewtContainer parent; + +}; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtNewtSectionClass; + +GType nmt_newt_section_get_type (void); + +NmtNewtWidget *nmt_newt_section_new (void); + +void nmt_newt_section_set_header (NmtNewtSection *section, + NmtNewtWidget *header); +NmtNewtWidget *nmt_newt_section_get_header (NmtNewtSection *section); + +void nmt_newt_section_set_body (NmtNewtSection *section, + NmtNewtWidget *body); +NmtNewtWidget *nmt_newt_section_get_body (NmtNewtSection *section); + +G_END_DECLS + +#endif /* NMT_NEWT_SECTION_H */ diff --git a/tui/newt/nmt-newt-separator.c b/tui/newt/nmt-newt-separator.c new file mode 100644 index 0000000000..07deb1af90 --- /dev/null +++ b/tui/newt/nmt-newt-separator.c @@ -0,0 +1,66 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-separator + * @short_description: Separator + * + * #NmtNewtSeparator is just a blank label, which is used in a few places + * where a widget is needed but none is desired, or to add blank space + * between widgets in containers that don't implement padding. + */ + +#include "config.h" + +#include "nmt-newt-separator.h" + +G_DEFINE_TYPE (NmtNewtSeparator, nmt_newt_separator, NMT_TYPE_NEWT_COMPONENT) + +/** + * nmt_newt_separator_new: + * + * Creates a new #NmtNewtSeparator. + * + * Returns: a new #NmtNewtSeparator + */ +NmtNewtWidget * +nmt_newt_separator_new (void) +{ + return g_object_new (NMT_TYPE_NEWT_SEPARATOR, NULL); +} + +static void +nmt_newt_separator_init (NmtNewtSeparator *separator) +{ +} + +static newtComponent +nmt_newt_separator_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + return newtLabel (-1, -1, " "); +} + +static void +nmt_newt_separator_class_init (NmtNewtSeparatorClass *separator_class) +{ + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (separator_class); + + /* virtual methods */ + component_class->build_component = nmt_newt_separator_build_component; +} diff --git a/tui/newt/nmt-newt-separator.h b/tui/newt/nmt-newt-separator.h new file mode 100644 index 0000000000..3f4183d588 --- /dev/null +++ b/tui/newt/nmt-newt-separator.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_SEPARATOR_H +#define NMT_NEWT_SEPARATOR_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_SEPARATOR (nmt_newt_separator_get_type ()) +#define NMT_NEWT_SEPARATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparator)) +#define NMT_NEWT_SEPARATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparatorClass)) +#define NMT_IS_NEWT_SEPARATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_SEPARATOR)) +#define NMT_IS_NEWT_SEPARATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_SEPARATOR)) +#define NMT_NEWT_SEPARATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_SEPARATOR, NmtNewtSeparatorClass)) + +struct _NmtNewtSeparator { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtSeparatorClass; + +GType nmt_newt_separator_get_type (void); + +NmtNewtWidget *nmt_newt_separator_new (void); + +G_END_DECLS + +#endif /* NMT_NEWT_SEPARATOR_H */ diff --git a/tui/newt/nmt-newt-stack.c b/tui/newt/nmt-newt-stack.c new file mode 100644 index 0000000000..1b31d58fe2 --- /dev/null +++ b/tui/newt/nmt-newt-stack.c @@ -0,0 +1,366 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-stack + * @short_description: A stack of alternative widgets + * + * #NmtNewtStack implements a stack of widgets, only one of which is + * visible at any time. + * + * The height and width of the widget is determined only by its + * visible child. Likewise, the widget's #NmtNewtWidget:valid is + * determined only by the validity of its visible child, not its other + * children. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-stack.h" + +G_DEFINE_TYPE (NmtNewtStack, nmt_newt_stack, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_STACK_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_STACK, NmtNewtStackPrivate)) + +typedef struct { + GPtrArray *children; + GPtrArray *ids; + + guint active; +} NmtNewtStackPrivate; + +enum { + PROP_0, + PROP_ACTIVE, + PROP_ACTIVE_ID, + + LAST_PROP +}; + +/** + * nmt_newt_stack_new: + * + * Creates a new #NmtNewtStack + * + * Returns: a new #NmtNewtStack + */ +NmtNewtWidget * +nmt_newt_stack_new (void) +{ + return g_object_new (NMT_TYPE_NEWT_STACK, NULL); +} + +static void +nmt_newt_stack_init (NmtNewtStack *stack) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + + priv->children = g_ptr_array_new (); + priv->ids = g_ptr_array_new_with_free_func (g_free); +} + +static void +nmt_newt_stack_finalize (GObject *object) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (object); + + g_ptr_array_unref (priv->children); + g_ptr_array_unref (priv->ids); + + G_OBJECT_CLASS (nmt_newt_stack_parent_class)->finalize (object); +} + +static newtComponent * +nmt_newt_stack_get_components (NmtNewtWidget *widget) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (widget); + + if (priv->active > priv->children->len) + return NULL; + + return nmt_newt_widget_get_components (priv->children->pdata[priv->active]); +} + +static void +nmt_newt_stack_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtNewtStack *stack = NMT_NEWT_STACK (widget); + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + int i, child_width, child_height; + + if (priv->active > priv->children->len) { + *width = *height = 0; + return; + } + + /* We size-request all pages so that embedded NmtPageGrids will + * participate in their size-grouping (so that switching pages + * won't result in the column widths changing). + */ + for (i = 0; i < priv->children->len; i++) { + nmt_newt_widget_size_request (priv->children->pdata[i], &child_width, &child_height); + if (i == priv->active) { + *width = child_width; + *height = child_height; + } + } +} + +static void +nmt_newt_stack_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (widget); + + if (priv->active > priv->children->len) + return; + + nmt_newt_widget_size_allocate (priv->children->pdata[priv->active], x, y, width, height); +} + +/** + * nmt_newt_stack_add: + * @stack: an #NmtNewtStack + * @id: the ID for the new page + * @widget: the widget to add + * + * Adds @widget to @stack with the given @id. + */ +void +nmt_newt_stack_add (NmtNewtStack *stack, + const char *id, + NmtNewtWidget *widget) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + + g_ptr_array_add (priv->children, widget); + g_ptr_array_add (priv->ids, g_strdup (id)); + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)->add (NMT_NEWT_CONTAINER (stack), widget); +} + +static void +nmt_newt_stack_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtStack *stack = NMT_NEWT_STACK (container); + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + int i; + + NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)->remove (container, widget); + + for (i = 0; i < priv->children->len; i++) { + if (priv->children->pdata[i] == widget) { + g_ptr_array_remove_index (priv->children, i); + g_ptr_array_remove_index (priv->ids, i); + return; + } + } +} + +static void +nmt_newt_stack_child_validity_changed (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (container); + + if (priv->active > priv->children->len) + return; + + if (priv->children->pdata[priv->active] == (gpointer) widget) { + NMT_NEWT_CONTAINER_CLASS (nmt_newt_stack_parent_class)-> + child_validity_changed (container, widget); + } +} + +/** + * nmt_newt_stack_set_active: + * @stack: an #NmtNewtStack + * @active: the index of the new active page + * + * Sets the active page on @stack to @active. + */ +void +nmt_newt_stack_set_active (NmtNewtStack *stack, + guint active) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + + if (priv->active == active) + return; + + priv->active = active; + g_object_notify (G_OBJECT (stack), "active"); + g_object_notify (G_OBJECT (stack), "active-id"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (stack)); +} + +/** + * nmt_newt_stack_get_active: + * @stack: an #NmtNewtStack + * + * Gets the index of the active page on @stack + * + * Returns: the index of the active page on @stack + */ +guint +nmt_newt_stack_get_active (NmtNewtStack *stack) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + + return priv->active; +} + +/** + * nmt_newt_stack_set_active_id: + * @stack: an #NmtNewtStack + * @active_id: the ID of the new active page + * + * Sets the active page on @stack to @active_id. + */ +void +nmt_newt_stack_set_active_id (NmtNewtStack *stack, + const char *id) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + int i; + + if (!g_strcmp0 (priv->ids->pdata[priv->active], id)) + return; + + for (i = 0; i < priv->ids->len; i++) { + if (!g_strcmp0 (priv->ids->pdata[i], id)) { + priv->active = i; + g_object_notify (G_OBJECT (stack), "active"); + g_object_notify (G_OBJECT (stack), "active-id"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (stack)); + return; + } + } +} + +/** + * nmt_newt_stack_get_active_id: + * @stack: an #NmtNewtStack + * + * Gets the ID of the active page on @stack + * + * Returns: the ID of the active page on @stack + */ +const char * +nmt_newt_stack_get_active_id (NmtNewtStack *stack) +{ + NmtNewtStackPrivate *priv = NMT_NEWT_STACK_GET_PRIVATE (stack); + + if (priv->active > priv->children->len) + return NULL; + + return priv->ids->pdata[priv->active]; +} + +static void +nmt_newt_stack_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtStack *stack = NMT_NEWT_STACK (object); + + switch (prop_id) { + case PROP_ACTIVE: + nmt_newt_stack_set_active (stack, g_value_get_uint (value)); + break; + case PROP_ACTIVE_ID: + nmt_newt_stack_set_active_id (stack, g_value_get_string (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_stack_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtStack *stack = NMT_NEWT_STACK (object); + + switch (prop_id) { + case PROP_ACTIVE: + g_value_set_uint (value, nmt_newt_stack_get_active (stack)); + break; + case PROP_ACTIVE_ID: + g_value_set_string (value, nmt_newt_stack_get_active_id (stack)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_stack_class_init (NmtNewtStackClass *stack_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (stack_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (stack_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (stack_class); + + g_type_class_add_private (stack_class, sizeof (NmtNewtStackPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_stack_set_property; + object_class->get_property = nmt_newt_stack_get_property; + object_class->finalize = nmt_newt_stack_finalize; + + widget_class->get_components = nmt_newt_stack_get_components; + widget_class->size_request = nmt_newt_stack_size_request; + widget_class->size_allocate = nmt_newt_stack_size_allocate; + + container_class->remove = nmt_newt_stack_remove; + container_class->child_validity_changed = nmt_newt_stack_child_validity_changed; + + /** + * NmtNewtStack:active: + * + * The index of the active page + */ + g_object_class_install_property (object_class, PROP_ACTIVE, + g_param_spec_uint ("active", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtStack:active-id: + * + * The ID of the active page + */ + g_object_class_install_property (object_class, PROP_ACTIVE_ID, + g_param_spec_string ("active-id", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-stack.h b/tui/newt/nmt-newt-stack.h new file mode 100644 index 0000000000..09afe4baf6 --- /dev/null +++ b/tui/newt/nmt-newt-stack.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_STACK_H +#define NMT_NEWT_STACK_H + +#include "nmt-newt-container.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_STACK (nmt_newt_stack_get_type ()) +#define NMT_NEWT_STACK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_STACK, NmtNewtStack)) +#define NMT_NEWT_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_STACK, NmtNewtStackClass)) +#define NMT_IS_NEWT_STACK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_STACK)) +#define NMT_IS_NEWT_STACK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_STACK)) +#define NMT_NEWT_STACK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_STACK, NmtNewtStackClass)) + +struct _NmtNewtStack { + NmtNewtContainer parent; + +}; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtNewtStackClass; + +GType nmt_newt_stack_get_type (void); + +NmtNewtWidget *nmt_newt_stack_new (void); + +void nmt_newt_stack_add (NmtNewtStack *stack, + const char *id, + NmtNewtWidget *widget); + +void nmt_newt_stack_set_active (NmtNewtStack *stack, + guint active); +guint nmt_newt_stack_get_active (NmtNewtStack *stack); + +void nmt_newt_stack_set_active_id (NmtNewtStack *stack, + const char *id); +const char * nmt_newt_stack_get_active_id (NmtNewtStack *stack); + +G_END_DECLS + +#endif /* NMT_NEWT_STACK_H */ diff --git a/tui/newt/nmt-newt-textbox.c b/tui/newt/nmt-newt-textbox.c new file mode 100644 index 0000000000..888bde28f9 --- /dev/null +++ b/tui/newt/nmt-newt-textbox.c @@ -0,0 +1,292 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-textbox + * @short_description: Multi-line text box + * + * #NmtNewtTextbox implements a multi-line text, optionally with + * word-wrapping. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-newt-textbox.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE (NmtNewtTextbox, nmt_newt_textbox, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_TEXTBOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxPrivate)) + +typedef struct { + int wrap_width; + NmtNewtTextboxFlags flags; + + char *text; + int width, height; +} NmtNewtTextboxPrivate; + +enum { + PROP_0, + PROP_TEXT, + PROP_FLAGS, + PROP_WRAP_WIDTH, + + LAST_PROP +}; + +/** + * NmtNewtTextboxFlags: + * @NMT_NEWT_TEXTBOX_SCROLLABLE: the textbox should be scollable. + * @NMT_NEWT_TEXTBOX_SET_BACKGROUND: the textbox should have a + * white background + * + * Flags for an #NmtNewtTextbox + */ + +/** + * nmt_newt_textbox_new: + * @flags: the textbox's flags + * @wrap_width: width in characters at which to word-wrap, or + * 0 to not wrap. + * + * Creates a new #NmtNewtTextbox. + * + * Returns: a new #NmtNewtTextbox + */ +NmtNewtWidget * +nmt_newt_textbox_new (NmtNewtTextboxFlags flags, + int wrap_width) +{ + return g_object_new (NMT_TYPE_NEWT_TEXTBOX, + "flags", flags, + "wrap-width", wrap_width, + NULL); +} + +/** + * nmt_newt_textbox_get_text: + * @textbox: an #NmtNewtTextbox + * + * Gets @textbox's text + * + * Returns: @textbox's text + */ +void +nmt_newt_textbox_set_text (NmtNewtTextbox *textbox, + const char *text) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox); + char **lines; + int i, len; + + if (!text) + text = ""; + if (!strcmp (priv->text, text)) + return; + + g_free (priv->text); + priv->text = g_strdup (text); + + priv->width = priv->height = 0; + lines = g_strsplit (priv->text, "\n", -1); + for (i = 0; lines[i]; i++) { + len = g_utf8_strlen (lines[i], -1); + if (len > priv->width) + priv->width = len; + } + g_free (lines); + priv->height = MIN (i, 1); + + g_object_notify (G_OBJECT (textbox), "text"); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (textbox)); +} + +/** + * nmt_newt_textbox_get_text: + * @textbox: an #NmtNewtTextbox + * + * Gets @textbox's text + * + * Returns: @textbox's text + */ +const char * +nmt_newt_textbox_get_text (NmtNewtTextbox *textbox) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox); + + return priv->text; +} + +static void +nmt_newt_textbox_init (NmtNewtTextbox *textbox) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox); + + priv->text = g_strdup (""); +} + +static void +nmt_newt_textbox_finalize (GObject *object) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (object); + + g_free (priv->text); + + G_OBJECT_CLASS (nmt_newt_textbox_parent_class)->finalize (object); +} + +static guint +convert_flags (NmtNewtTextboxFlags flags) +{ + guint newt_flags = 0; + + if (flags & NMT_NEWT_TEXTBOX_SCROLLABLE) + newt_flags |= NEWT_FLAG_SCROLL; + + return newt_flags; +} + +static newtComponent +nmt_newt_textbox_build_component (NmtNewtComponent *component, + gboolean sensitive) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (component); + newtComponent co; + const char *text; + char *text_lc; + + text = priv->text; + if (!*text) + text = "\n"; + + text_lc = nmt_newt_locale_from_utf8 (text); + if (priv->wrap_width > 0) { + co = newtTextboxReflowed (-1, -1, text_lc, priv->wrap_width, 0, 0, 0); + } else { + co = newtTextbox (-1, -1, priv->width, priv->height, convert_flags (priv->flags)); + newtTextboxSetText (co, text_lc); + } + g_free (text_lc); + + if (priv->flags & NMT_NEWT_TEXTBOX_SET_BACKGROUND) + newtTextboxSetColors (co, NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, NEWT_COLORSET_ACTTEXTBOX); + + return co; +} + +static void +nmt_newt_textbox_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtTextbox *textbox = NMT_NEWT_TEXTBOX (object); + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (textbox); + + switch (prop_id) { + case PROP_TEXT: + nmt_newt_textbox_set_text (textbox, g_value_get_string (value)); + break; + case PROP_FLAGS: + priv->flags = g_value_get_uint (value); + break; + case PROP_WRAP_WIDTH: + priv->wrap_width = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_textbox_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_TEXT: + g_value_set_string (value, priv->text); + break; + case PROP_FLAGS: + g_value_set_uint (value, priv->flags); + break; + case PROP_WRAP_WIDTH: + g_value_set_int (value, priv->wrap_width); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_textbox_class_init (NmtNewtTextboxClass *textbox_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (textbox_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS (textbox_class); + + g_type_class_add_private (textbox_class, sizeof (NmtNewtTextboxPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_textbox_set_property; + object_class->get_property = nmt_newt_textbox_get_property; + object_class->finalize = nmt_newt_textbox_finalize; + + component_class->build_component = nmt_newt_textbox_build_component; + + /** + * NmtNewtTextbox:text: + * + * The textbox's text + */ + g_object_class_install_property (object_class, PROP_TEXT, + g_param_spec_string ("text", "", "", + "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtTextbox:flags: + * + * The textbox's flags + */ + g_object_class_install_property (object_class, PROP_FLAGS, + g_param_spec_uint ("flags", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtTextbox:wrap-width: + * + * The width in characters at which the textbox's text + * will wrap, or 0 if it does not wrap. + */ + g_object_class_install_property (object_class, PROP_WRAP_WIDTH, + g_param_spec_int ("wrap-width", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-textbox.h b/tui/newt/nmt-newt-textbox.h new file mode 100644 index 0000000000..b3743aea05 --- /dev/null +++ b/tui/newt/nmt-newt-textbox.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_TEXTBOX_H +#define NMT_NEWT_TEXTBOX_H + +#include "nmt-newt-component.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_TEXTBOX (nmt_newt_textbox_get_type ()) +#define NMT_NEWT_TEXTBOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextbox)) +#define NMT_NEWT_TEXTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxClass)) +#define NMT_IS_NEWT_TEXTBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_TEXTBOX)) +#define NMT_IS_NEWT_TEXTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_TEXTBOX)) +#define NMT_NEWT_TEXTBOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_TEXTBOX, NmtNewtTextboxClass)) + +struct _NmtNewtTextbox { + NmtNewtComponent parent; + +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtTextboxClass; + +GType nmt_newt_textbox_get_type (void); + +typedef enum { + NMT_NEWT_TEXTBOX_SCROLLABLE = (1 << 0), + NMT_NEWT_TEXTBOX_SET_BACKGROUND = (1 << 1) +} NmtNewtTextboxFlags; + +NmtNewtWidget *nmt_newt_textbox_new (NmtNewtTextboxFlags flags, + int wrap_width); + +void nmt_newt_textbox_set_text (NmtNewtTextbox *textbox, + const char *text); +const char *nmt_newt_textbox_get_text (NmtNewtTextbox *textbox); + +G_END_DECLS + +#endif /* NMT_NEWT_TEXTBOX_H */ diff --git a/tui/newt/nmt-newt-toggle-button.c b/tui/newt/nmt-newt-toggle-button.c new file mode 100644 index 0000000000..d435e459e5 --- /dev/null +++ b/tui/newt/nmt-newt-toggle-button.c @@ -0,0 +1,234 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-toggle-button + * @short_description: Toggle buttons + * + * #NmtNewtToggleButton implements a two-state toggle button. + */ + +#include "config.h" + +#include "nmt-newt-toggle-button.h" + +G_DEFINE_TYPE (NmtNewtToggleButton, nmt_newt_toggle_button, NMT_TYPE_NEWT_BUTTON) + +#define NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonPrivate)) + +typedef struct { + char *on_label, *off_label; + gboolean active; +} NmtNewtToggleButtonPrivate; + +enum { + PROP_0, + PROP_ON_LABEL, + PROP_OFF_LABEL, + PROP_ACTIVE, + + LAST_PROP +}; + +/** + * nmt_newt_toggle_button_new: + * @on_label: the button's label when it is in the "on" state + * @off_label: the button's label when it is in the "off" state + * + * Creates a new #NmtNewtToggleButton + * + * Returns: a new #NmtNewtToggleButton + */ +NmtNewtWidget * +nmt_newt_toggle_button_new (const char *on_label, + const char *off_label) +{ + return g_object_new (NMT_TYPE_NEWT_TOGGLE_BUTTON, + "on-label", on_label, + "off-label", off_label, + NULL); +} + +/** + * nmt_newt_toggle_button_get_active: + * @button: an #NmtNewtToggleButton + * + * Gets whether @button is currently "on" or "off" + * + * Returns: whether @button is currently "on" (%TRUE) or "off" (%FALSE) + */ +gboolean +nmt_newt_toggle_button_get_active (NmtNewtToggleButton *button) +{ + NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (button); + + return priv->active; +} + +/** + * nmt_newt_toggle_button_set_active: + * @button: an #NmtNewtToggleButton + * @active: whether @button should be "on" or "off" + * + * Sets whether @button is currently "on" or "off" + */ +void +nmt_newt_toggle_button_set_active (NmtNewtToggleButton *button, + gboolean active) +{ + NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (button); + + if (priv->active == active) + return; + + priv->active = active; + g_object_set (G_OBJECT (button), + "label", active ? priv->on_label : priv->off_label, + NULL); + g_object_notify (G_OBJECT (button), "active"); +} + +static void +nmt_newt_toggle_button_init (NmtNewtToggleButton *button) +{ +} + +static void +nmt_newt_toggle_button_finalize (GObject *object) +{ + NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object); + + g_free (priv->on_label); + g_free (priv->off_label); + + G_OBJECT_CLASS (nmt_newt_toggle_button_parent_class)->finalize (object); +} + +static void +nmt_newt_toggle_button_activated (NmtNewtWidget *widget) +{ + NmtNewtToggleButton *button = NMT_NEWT_TOGGLE_BUTTON (widget); + + nmt_newt_toggle_button_set_active (button, !nmt_newt_toggle_button_get_active (button)); + + NMT_NEWT_WIDGET_CLASS (nmt_newt_toggle_button_parent_class)->activated (widget); +} + +static void +nmt_newt_toggle_button_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ON_LABEL: + g_free (priv->on_label); + priv->on_label = g_value_dup_string (value); + if (priv->active) + g_object_set (object, "label", priv->on_label, NULL); + break; + case PROP_OFF_LABEL: + g_free (priv->off_label); + priv->off_label = g_value_dup_string (value); + if (!priv->active) + g_object_set (object, "label", priv->off_label, NULL); + break; + case PROP_ACTIVE: + priv->active = g_value_get_boolean (value); + g_object_set (object, + "label", priv->active ? priv->on_label : priv->off_label, + NULL); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_toggle_button_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtToggleButtonPrivate *priv = NMT_NEWT_TOGGLE_BUTTON_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_ON_LABEL: + g_value_set_string (value, priv->on_label); + break; + case PROP_OFF_LABEL: + g_value_set_string (value, priv->off_label); + break; + case PROP_ACTIVE: + g_value_set_boolean (value, priv->active); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_toggle_button_class_init (NmtNewtToggleButtonClass *button_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (button_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (button_class); + + g_type_class_add_private (button_class, sizeof (NmtNewtToggleButtonPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_toggle_button_set_property; + object_class->get_property = nmt_newt_toggle_button_get_property; + object_class->finalize = nmt_newt_toggle_button_finalize; + + widget_class->activated = nmt_newt_toggle_button_activated; + + /** + * NmtNewtToggleButton:on-label: + * + * The label the button displays when it is "on". + */ + g_object_class_install_property (object_class, PROP_ON_LABEL, + g_param_spec_string ("on-label", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtToggleButton:off-label: + * + * The label the button displays when it is "off". + */ + g_object_class_install_property (object_class, PROP_OFF_LABEL, + g_param_spec_string ("off-label", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtToggleButton:active: + * + * Whether the button is currently "on" (%TRUE) or "off" (%FALSE) + */ + g_object_class_install_property (object_class, PROP_ACTIVE, + g_param_spec_boolean ("active", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-toggle-button.h b/tui/newt/nmt-newt-toggle-button.h new file mode 100644 index 0000000000..e54c6d3fa1 --- /dev/null +++ b/tui/newt/nmt-newt-toggle-button.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_TOGGLE_BUTTON_H +#define NMT_NEWT_TOGGLE_BUTTON_H + +#include "nmt-newt-button.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_TOGGLE_BUTTON (nmt_newt_toggle_button_get_type ()) +#define NMT_NEWT_TOGGLE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButton)) +#define NMT_NEWT_TOGGLE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonClass)) +#define NMT_IS_NEWT_TOGGLE_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON)) +#define NMT_IS_NEWT_TOGGLE_BUTTON_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_TOGGLE_BUTTON)) +#define NMT_NEWT_TOGGLE_BUTTON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_TOGGLE_BUTTON, NmtNewtToggleButtonClass)) + +struct _NmtNewtToggleButton { + NmtNewtButton parent; + +}; + +typedef struct { + NmtNewtButtonClass parent; + +} NmtNewtToggleButtonClass; + +GType nmt_newt_toggle_button_get_type (void); + +NmtNewtWidget *nmt_newt_toggle_button_new (const char *on_label, + const char *off_label); + +gboolean nmt_newt_toggle_button_get_active (NmtNewtToggleButton *button); +void nmt_newt_toggle_button_set_active (NmtNewtToggleButton *button, + gboolean active); + +G_END_DECLS + +#endif /* NMT_NEWT_TOGGLE_BUTTON_H */ diff --git a/tui/newt/nmt-newt-types.h b/tui/newt/nmt-newt-types.h new file mode 100644 index 0000000000..c26ff900ef --- /dev/null +++ b/tui/newt/nmt-newt-types.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_TYPES_H +#define NMT_NEWT_TYPES_H + +#include <glib-object.h> +#include <newt.h> + +G_BEGIN_DECLS + +typedef struct _NmtNewtButton NmtNewtButton; +typedef struct _NmtNewtButtonBox NmtNewtButtonBox; +typedef struct _NmtNewtCheckbox NmtNewtCheckbox; +typedef struct _NmtNewtComponent NmtNewtComponent; +typedef struct _NmtNewtContainer NmtNewtContainer; +typedef struct _NmtNewtEntry NmtNewtEntry; +typedef struct _NmtNewtEntryNumeric NmtNewtEntryNumeric; +typedef struct _NmtNewtForm NmtNewtForm; +typedef struct _NmtNewtGrid NmtNewtGrid; +typedef struct _NmtNewtLabel NmtNewtLabel; +typedef struct _NmtNewtListbox NmtNewtListbox; +typedef struct _NmtNewtPopup NmtNewtPopup; +typedef struct _NmtNewtSection NmtNewtSection; +typedef struct _NmtNewtSectionBorder NmtNewtSectionBorder; +typedef struct _NmtNewtSeparator NmtNewtSeparator; +typedef struct _NmtNewtStack NmtNewtStack; +typedef struct _NmtNewtTextbox NmtNewtTextbox; +typedef struct _NmtNewtToggleButton NmtNewtToggleButton; +typedef struct _NmtNewtWidget NmtNewtWidget; + +G_END_DECLS + +#endif /* NMT_NEWT_COMPONENT_H */ diff --git a/tui/newt/nmt-newt-utils.c b/tui/newt/nmt-newt-utils.c new file mode 100644 index 0000000000..f2148d2b02 --- /dev/null +++ b/tui/newt/nmt-newt-utils.c @@ -0,0 +1,333 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-utils + * @short_description: Utility functions + */ + +#include "config.h" + +#include <errno.h> +#include <stdarg.h> +#include <unistd.h> + +#include <glib/gi18n-lib.h> + +#include "nmt-newt-utils.h" + +static void +nmt_newt_g_log_handler (const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer user_data) +{ + const char *level_name; + char *full_message; + int screen_width, screen_height; + newtComponent text, ok, form; + newtGrid grid; + + g_assert (!(log_level & G_LOG_FLAG_RECURSION)); + + if (log_level & G_LOG_LEVEL_DEBUG) + return; + + switch (log_level & G_LOG_LEVEL_MASK) { + case G_LOG_LEVEL_ERROR: + level_name = "ERROR"; + break; + case G_LOG_LEVEL_CRITICAL: + level_name = "CRITICAL"; + break; + case G_LOG_LEVEL_WARNING: + level_name = "WARNING"; + break; + case G_LOG_LEVEL_MESSAGE: + level_name = "Message"; + break; + default: + level_name = NULL; + } + + full_message = g_strdup_printf ("%s%s%s%s%s", + log_domain ? log_domain : "", + log_domain && level_name ? " " : "", + level_name ? level_name : "", + log_domain || level_name ? ": " : "", + message); + + /* newtWinMessage() wraps the window too narrowly by default, so + * we don't want to use that. But we intentionally avoid using any + * NmtNewt classes, to avoid possible error recursion. + */ + + newtGetScreenSize (&screen_width, &screen_height); + text = newtTextboxReflowed (-1, -1, full_message, MAX (70, screen_width - 10), 0, 0, 0); + g_free (full_message); + + ok = newtButton (-1, -1, "OK"); + + grid = newtCreateGrid (1, 2); + newtGridSetField (grid, 0, 0, NEWT_GRID_COMPONENT, text, 0, 0, 0, 0, 0, 0); + newtGridSetField (grid, 0, 1, NEWT_GRID_COMPONENT, ok, 0, 1, 0, 0, + NEWT_ANCHOR_RIGHT, 0); + + newtGridWrappedWindow (grid, (char *) (level_name ? level_name : "")); + newtGridFree (grid, TRUE); + + form = newtForm (NULL, NULL, 0); + newtFormAddComponents (form, text, ok, NULL); + newtRunForm (form); + newtFormDestroy (form); + newtPopWindow (); +} + +/** + * nmt_newt_init: + * + * Wrapper for newtInit() that also does some nmt-newt-internal setup. + * This should be called once, before any other nmt-newt functions. + * + * FIXME: Currently this also calls g_log_set_default_handler() to set + * up a log handler that displays g_warning()s and the like as pop-up + * windows, but in the long run that should only happen for + * debug/developer builds. + */ +void +nmt_newt_init (void) +{ + newtInit (); + newtCls (); + + newtSetColor (NEWT_COLORSET_CHECKBOX, "black", "lightgray"); + newtSetColor (NMT_NEWT_COLORSET_BAD_LABEL, "red", "lightgray"); + newtSetColor (NMT_NEWT_COLORSET_PLAIN_LABEL, "black", "lightgray"); + newtSetColor (NMT_NEWT_COLORSET_DISABLED_BUTTON, "blue", "lightgray"); + newtSetColor (NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, "black", "white"); + + g_log_set_default_handler (nmt_newt_g_log_handler, NULL); +} + +/** + * nmt_newt_finished: + * + * Wrapper for newtFinished(). Should be called at the end of the program. + */ +void +nmt_newt_finished (void) +{ + newtFinished (); + g_log_set_default_handler (nmt_newt_g_log_handler, g_log_default_handler); +} + +/** + * nmt_newt_error_dialog: + * @message: a printf()-style message format + * @...: arguments + * + * Displays the given message in a dialog box with a single "OK" + * button, and returns after the user clicks "OK". + * + * FIXME: it's not just for errors any more! + */ +void +nmt_newt_error_dialog (const char *message, + ...) +{ + va_list ap; + char *msg, *msg_lc, *ok_lc; + + va_start (ap, message); + msg = g_strdup_vprintf (message, ap); + va_end (ap); + + msg_lc = nmt_newt_locale_from_utf8 (msg); + ok_lc = nmt_newt_locale_from_utf8 (_("OK")); + newtWinMessage (NULL, ok_lc, "%s", msg_lc); + + g_free (ok_lc); + g_free (msg_lc); + g_free (msg); +} + +/** + * nmt_newt_choice_dialog: + * @button1: the label for the first button + * @button2: the label for the second button + * @message: a printf()-style message format + * @...: arguments + * + * Displays the given message in a dialog box with two buttons with + * the indicated labels, and waits for the user to click one. + * + * Returns: which button was clicked: 0 for @button1 or 1 for @button2 + */ +int +nmt_newt_choice_dialog (const char *button1, + const char *button2, + const char *message, + ...) +{ + va_list ap; + char *msg, *msg_lc, *button1_lc, *button2_lc; + int choice; + + va_start (ap, message); + msg = g_strdup_vprintf (message, ap); + va_end (ap); + + msg_lc = nmt_newt_locale_from_utf8 (msg); + button1_lc = nmt_newt_locale_from_utf8 (button1); + button2_lc = nmt_newt_locale_from_utf8 (button2); + choice = newtWinChoice (NULL, button1_lc, button2_lc, "%s", msg_lc); + + g_free (button1_lc); + g_free (button2_lc); + g_free (msg_lc); + g_free (msg); + + return choice; +} + +/** + * nmt_newt_locale_to_utf8: + * @str_lc: a string in the user's locale encoding + * + * Convenience wrapper around g_locale_to_utf8(). + * + * Note that libnewt works in terms of the user's locale character + * set, NOT UTF-8, so all strings received from libnewt must be + * converted back to UTF-8 before being returned to the caller or used + * in other APIs. + * + * Returns: @str_lc, converted to UTF-8. + */ +char * +nmt_newt_locale_to_utf8 (const char *str_lc) +{ + char *str_utf8; + + str_utf8 = g_locale_to_utf8 (str_lc, -1, NULL, NULL, NULL); + if (!str_utf8) + str_utf8 = g_strdup (""); + return str_utf8; +} + +/** + * nmt_newt_locale_from_utf8: + * @str_utf8: a UTF-8 string + * + * Convenience wrapper around g_locale_from_utf8(). + * + * Note that libnewt works in terms of the user's locale character + * set, NOT UTF-8, so all strings from nmt-newt must be converted to + * locale encoding before being passed to libnewt. + * + * Returns: @str_utf8, converted to the user's locale encoding. + */ +char * +nmt_newt_locale_from_utf8 (const char *str_utf8) +{ + char *str_lc; + + str_lc = g_locale_from_utf8 (str_utf8, -1, NULL, NULL, NULL); + if (!str_lc) + str_lc = g_strdup (""); + return str_lc; +} + +/** + * nmt_newt_edit_string: + * @data: data to edit + * + * libnewt does not have a multi-line editable text component, so + * nmt-newt provides this function instead, which will open the user's + * editor to edit a file containing the given @data (ensuring that the + * current screen state is saved before starting the editor and + * restored after it returns). + * + * Returns: the edited data, or %NULL if an error occurred. + */ +char * +nmt_newt_edit_string (const char *data) +{ + gssize len, nwrote; + char *filename, *argv[3]; + GError *error = NULL; + int fd, status; + char *new_data = NULL; + + fd = g_file_open_tmp ("XXXXXX.json", &filename, &error); + if (fd == -1) { + nmt_newt_error_dialog (_("Could not create temporary file: %s"), error->message); + g_error_free (error); + return NULL; + } + + len = data ? strlen (data) : 0; + while (len) { + do + nwrote = write (fd, data, len); + while (nwrote == -1 && errno == EINTR); + + len -= nwrote; + data += nwrote; + } + close (fd); + + argv[0] = (char *) g_getenv ("VISUAL"); + if (!argv[0]) + argv[0] = (char *) g_getenv ("EDITOR"); + if (!argv[0]) + argv[0] = (char *) "vi"; + argv[1] = filename; + argv[2] = NULL; + + newtSuspend (); + g_spawn_sync (NULL, argv, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, + NULL, NULL, NULL, NULL, + &status, &error); + newtResume (); + + if (error) { + nmt_newt_error_dialog (_("Could not create temporary file: %s"), error->message); + g_error_free (error); + goto done; + } + + if (!g_spawn_check_exit_status (status, &error)) { + nmt_newt_error_dialog (_("Editor failed: %s"), error->message); + g_error_free (error); + goto done; + } + + if (!g_file_get_contents (filename, &new_data, NULL, &error)) { + nmt_newt_error_dialog (_("Could not re-read file: %s"), error->message); + g_error_free (error); + goto done; + } + + done: + unlink (filename); + g_free (filename); + + return new_data; +} + diff --git a/tui/newt/nmt-newt-utils.h b/tui/newt/nmt-newt-utils.h new file mode 100644 index 0000000000..1823e3c2a7 --- /dev/null +++ b/tui/newt/nmt-newt-utils.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_UTILS_H +#define NMT_NEWT_UTILS_H + +#include <newt.h> +#include <glib.h> + +G_BEGIN_DECLS + +void nmt_newt_init (void); +void nmt_newt_finished (void); + +typedef enum { + NMT_NEWT_COLORSET_BAD_LABEL = NEWT_COLORSET_CUSTOM (0), + NMT_NEWT_COLORSET_PLAIN_LABEL, + NMT_NEWT_COLORSET_DISABLED_BUTTON, + NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND +} NmtNewtColorsets; + +char *nmt_newt_locale_to_utf8 (const char *str_lc); +char *nmt_newt_locale_from_utf8 (const char *str_utf8); + +void nmt_newt_error_dialog (const char *message, + ...); +int nmt_newt_choice_dialog (const char *button1, + const char *button2, + const char *message, + ...); + +char *nmt_newt_edit_string (const char *data); + +G_END_DECLS + +#endif /* NMT_NEWT_UTILS_H */ diff --git a/tui/newt/nmt-newt-widget.c b/tui/newt/nmt-newt-widget.c new file mode 100644 index 0000000000..f7c8d20fe5 --- /dev/null +++ b/tui/newt/nmt-newt-widget.c @@ -0,0 +1,648 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-widget + * @short_description: Base TUI Widget class + * + * #NmtNewtWidget is the abstract base class for nmt-newt. All widgets + * inherit from one of its two subclasses: #NmtNewtComponent, for + * widgets that wrap a (single) #newtComponent, and #NmtNewtContainer, + * for widgets consisting of multiple components. See those classes + * for more details. + * + * With the exception of #NmtNewtForm, all widgets start out with a + * floating reference, which will be sunk by the container they are + * added to. #NmtNewtForm is the "top-level" widget type, and so does + * not have a floating reference. + * + * FIXME: need RTL support + */ + +#include "config.h" + +#include "nmt-newt-widget.h" +#include "nmt-newt-form.h" + +G_DEFINE_ABSTRACT_TYPE (NmtNewtWidget, nmt_newt_widget, G_TYPE_INITIALLY_UNOWNED) + +#define NMT_NEWT_WIDGET_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetPrivate)) + +typedef struct { + NmtNewtWidget *parent; + gboolean visible, realized, valid; + gboolean exit_on_activate; + + int pad_left, pad_top, pad_right, pad_bottom; +} NmtNewtWidgetPrivate; + +enum { + PROP_0, + + PROP_PARENT, + PROP_VISIBLE, + PROP_VALID, + PROP_EXIT_ON_ACTIVATE, + + LAST_PROP +}; + +enum { + NEEDS_REBUILD, + ACTIVATED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void +nmt_newt_widget_init (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + priv->visible = TRUE; + priv->valid = TRUE; +} + +static void +nmt_newt_widget_finalize (GObject *object) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (object); + + nmt_newt_widget_unrealize (NMT_NEWT_WIDGET (object)); + g_clear_object (&priv->parent); + + G_OBJECT_CLASS (nmt_newt_widget_parent_class)->finalize (object); +} + +/** + * nmt_newt_widget_realize: + * @widget: an #NmtNewtWidget + * + * "Realizes" @widget. That is, creates #newtComponents corresponding + * to @widget and its children. + * + * You should not need to call this yourself; an #NmtNewtForm will + * cause its children to be realized and unrealized as needed. + */ +void +nmt_newt_widget_realize (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + if (!priv->realized) { + NMT_NEWT_WIDGET_GET_CLASS (widget)->realize (widget); + priv->realized = TRUE; + } +} + +/** + * nmt_newt_widget_unrealize: + * @widget: an #NmtNewtWidget + * + * "Unrealizes" @widget, destroying its #newtComponents. + * + * You should not need to call this yourself; an #NmtNewtForm will + * cause its children to be realized and unrealized as needed. + */ +void +nmt_newt_widget_unrealize (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + if (priv->realized) { + NMT_NEWT_WIDGET_GET_CLASS (widget)->unrealize (widget); + priv->realized = FALSE; + } +} + +/** + * nmt_newt_widget_get_components: + * @widget: an #NmtNewtWidget + * + * Gets the #newtComponents that make up @widget, if @widget is + * visible. If @widget has not yet been realized, it will be realized + * first. + * + * If this function is called on a widget, then the widget will assume + * that someone else is now responsible for destroying the components, + * and so it will not destroy them itself when the widget is + * destroyed. Normally, components will end up being destroyed by the + * #NmtNewtForm they are added to. + * + * Returns: a %NULL-terminated array of components, in focus-chain + * order. You must free the array with g_free() when you are done + * with it. + */ +newtComponent * +nmt_newt_widget_get_components (NmtNewtWidget *widget) +{ + if (nmt_newt_widget_get_visible (widget)) { + nmt_newt_widget_realize (widget); + return NMT_NEWT_WIDGET_GET_CLASS (widget)->get_components (widget); + } else + return NULL; +} + +/** + * nmt_newt_widget_find_component: + * @widget: an #NmtNewtWidget + * @co: a #newtComponent + * + * Finds the widget inside @widget that owns @co. + * + * Return value: @co's owner, or %NULL if it was not found. + */ +NmtNewtWidget * +nmt_newt_widget_find_component (NmtNewtWidget *widget, + newtComponent co) +{ + return NMT_NEWT_WIDGET_GET_CLASS (widget)->find_component (widget, co); +} + +/** + * nmt_newt_widget_set_padding: + * @widget: an #NmtNewtWidget + * @pad_left: padding on the left of @widget + * @pad_top: padding on the top of @widget + * @pad_right: padding on the right of @widget + * @pad_bottom: padding on the bottom of @widget + * + * Sets the padding on @widget. + */ +void +nmt_newt_widget_set_padding (NmtNewtWidget *widget, + int pad_left, + int pad_top, + int pad_right, + int pad_bottom) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + priv->pad_left = pad_left; + priv->pad_top = pad_top; + priv->pad_right = pad_right; + priv->pad_bottom = pad_bottom; +} + +/** + * nmt_newt_widget_size_request: + * @widget: an #NmtNewtWidget + * @width: (out): on output, the widget's requested width + * @height: (out): on output, the widget's requested height + * + * Asks @widget for its requested size. If @widget is not visible, + * this will return 0, 0. If @widget has not yet been realized, it + * will be realized first. + */ +void +nmt_newt_widget_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + if (nmt_newt_widget_get_visible (widget)) { + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + nmt_newt_widget_realize (widget); + NMT_NEWT_WIDGET_GET_CLASS (widget)->size_request (widget, width, height); + + *width += priv->pad_left + priv->pad_right; + *height += priv->pad_top + priv->pad_bottom; + } else + *width = *height = 0; +} + +/** + * nmt_newt_widget_size_allocate: + * @widget: an #NmtNewtWidget + * @x: the widget's (absolute) X coordinate + * @y: the widget's (absolute) Y coordinate + * @width: the widget's allocated width + * @height: the widget's allocated height + * + * Positions @widget at the given coordinates, with the given size. If + * @widget is not visible, this has no effect. If @widget has not yet + * been realized, it will be realized first. + * + * @x and @y are absolute coordinates (ie, relative to the screen / + * terminal window, not relative to @widget's parent). + * + * In general, the results are undefined if @width or @height is less + * than the widget's requested size. If @width or @height is larger + * than the requested size, most #NmtNewtComponents will ignore the + * extra space, but some components and most containers will expand to + * fit. + */ +void +nmt_newt_widget_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + if (nmt_newt_widget_get_visible (widget)) { + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + nmt_newt_widget_realize (widget); + x += priv->pad_left; + y += priv->pad_top; + width -= priv->pad_left + priv->pad_right; + height -= priv->pad_top + priv->pad_bottom; + + NMT_NEWT_WIDGET_GET_CLASS (widget)->size_allocate (widget, x, y, width, height); + } +} + +/** + * nmt_newt_widget_get_focus_component: + * @widget: an #NmtNewtWidget + * + * Gets the #newtComponent that should be given the keyboard focus when + * @widget is focused. + * + * Returns: the #newtComponent to focus, or %NULL if @widget can't + * take the focus. + */ +newtComponent +nmt_newt_widget_get_focus_component (NmtNewtWidget *widget) +{ + if (!NMT_NEWT_WIDGET_GET_CLASS (widget)->get_focus_component) + return NULL; + + return NMT_NEWT_WIDGET_GET_CLASS (widget)->get_focus_component (widget); +} + +static void +nmt_newt_widget_real_activated (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + if (priv->exit_on_activate) + nmt_newt_form_quit (nmt_newt_widget_get_form (widget)); +} + +/** + * nmt_newt_widget_activated: + * @widget: an #NmtNewtWidget + * + * Tells @widget that its #newtComponent has been activated (ie, the + * user hit "Return" on it) and emits #NmtNewtWidget::activated. + * + * If #NmtNewtWidget:exit-on-activate is set on @widget, then this + * will call nmt_newt_form_quit() on the widget's form. + */ +void +nmt_newt_widget_activated (NmtNewtWidget *widget) +{ + g_signal_emit (widget, signals[ACTIVATED], 0); +} + +/** + * nmt_newt_widget_get_exit_on_activate: + * @widget: an #NmtNewtWidget + * + * Gets @widget's #NmtNewtWidget:exit-on-activate flag, qv. + * + * Returns: @widget's #NmtNewtWidget:exit-on-activate flag + */ +gboolean +nmt_newt_widget_get_exit_on_activate (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + return priv->exit_on_activate; +} + +/** + * nmt_newt_widget_set_exit_on_activate: + * @widget: an #NmtNewtWidget + * @exit_on_activate: whether @widget should exit on activate. + * + * Sets @widget's #NmtNewtWidget:exit-on-activate flag, qv. + */ +void +nmt_newt_widget_set_exit_on_activate (NmtNewtWidget *widget, + gboolean exit_on_activate) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + exit_on_activate = !!exit_on_activate; + if (priv->exit_on_activate != exit_on_activate) { + priv->exit_on_activate = exit_on_activate; + g_object_notify (G_OBJECT (widget), "exit-on-activate"); + } +} + +/** + * nmt_newt_widget_get_visible: + * @widget: an #NmtNewtWidget + * + * Gets @widget's #NmtNewtWidget:visible flag, qv. + * + * Returns: @widget's #NmtNewtWidget:visible flag + */ +gboolean +nmt_newt_widget_get_visible (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + return priv->visible; +} + +/** + * nmt_newt_widget_set_visible: + * @widget: an #NmtNewtWidget + * @visible: whether @widget should be visible + * + * Sets @widget's #NmtNewtWidget:visible flag, qv. + */ +void +nmt_newt_widget_set_visible (NmtNewtWidget *widget, + gboolean visible) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + visible = !!visible; + if (priv->visible != visible) { + priv->visible = visible; + g_object_notify (G_OBJECT (widget), "visible"); + nmt_newt_widget_needs_rebuild (widget); + } +} + +/** + * nmt_newt_widget_set_parent: + * @widget: an #NmtNewtWidget + * @parent: @widget's parent + * + * Sets @widget's parent to @parent. This is used internally by + * #NmtNewtContainer implementations; you must use an appropriate + * container-specific method to actually add a widget to a container. + */ +void +nmt_newt_widget_set_parent (NmtNewtWidget *widget, + NmtNewtWidget *parent) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + g_clear_object (&priv->parent); + priv->parent = parent ? g_object_ref (parent) : NULL; + g_object_notify (G_OBJECT (widget), "parent"); +} + +/** + * nmt_newt_widget_get_parent: + * @widget: an #NmtNewtWidget + * + * Gets @widget's parent + * + * Returns: @widget's parent + */ +NmtNewtWidget * +nmt_newt_widget_get_parent (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + return priv->parent; +} + +/** + * nmt_newt_widget_get_form: + * @widget: an #NmtNewtWidget + * + * Gets @widget's top-level form. + * + * Returns: @widget's #NmtNewtForm + */ +NmtNewtForm * +nmt_newt_widget_get_form (NmtNewtWidget *widget) +{ + while (widget) { + if (NMT_IS_NEWT_FORM (widget)) + return NMT_NEWT_FORM (widget); + widget = nmt_newt_widget_get_parent (widget); + } + + return NULL; +} + +/** + * nmt_newt_widget_get_valid: + * @widget: an #NmtNewtWidget + * + * Gets @widget's #NmtNewtWidget:valid flag, indicating whether its + * content is valid. + * + * Returns: @widget's #NmtNewtWidget:valid flag + */ +gboolean +nmt_newt_widget_get_valid (NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + return priv->valid; +} + +/** + * nmt_newt_widget_set_valid: + * @widget: an #NmtNewtWidget + * @valid: whether @widget is valid + * + * Sets @widget's #NmtNewtWidget:valid flag, indicating whether its + * content is valid. + * + * This method should be considered "protected"; if you change it, the + * widget implementation will likely just change it back at some + * point. + */ +void +nmt_newt_widget_set_valid (NmtNewtWidget *widget, + gboolean valid) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (widget); + + valid = !!valid; + if (priv->valid == valid) + return; + + priv->valid = valid; + g_object_notify (G_OBJECT (widget), "valid"); +} + +/** + * nmt_newt_widget_needs_rebuilds: + * @widget: an #NmtNewtWidget + * + * Marks @widget as needing to be "rebuilt" (ie, re-realized). This is + * called automatically in some cases (such as when adding a widget to + * or removing it from a container). #NmtNewtComponent implementations + * should also call this if they need to make some change that can + * only be done by destroying their current #newtComponent and + * creating a new one. + */ +void +nmt_newt_widget_needs_rebuild (NmtNewtWidget *widget) +{ + g_signal_emit (widget, signals[NEEDS_REBUILD], 0); +} + +static void +nmt_newt_widget_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtNewtWidget *widget = NMT_NEWT_WIDGET (object); + + switch (prop_id) { + case PROP_PARENT: + nmt_newt_widget_set_parent (widget, g_value_get_object (value)); + break; + case PROP_VISIBLE: + nmt_newt_widget_set_visible (widget, g_value_get_boolean (value)); + break; + case PROP_EXIT_ON_ACTIVATE: + nmt_newt_widget_set_exit_on_activate (widget, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_widget_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object (value, priv->parent); + break; + case PROP_VISIBLE: + g_value_set_boolean (value, priv->visible); + break; + case PROP_VALID: + g_value_set_boolean (value, priv->valid); + break; + case PROP_EXIT_ON_ACTIVATE: + g_value_set_boolean (value, priv->exit_on_activate); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_widget_class_init (NmtNewtWidgetClass *widget_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (widget_class); + + g_type_class_add_private (widget_class, sizeof (NmtNewtWidgetPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_widget_set_property; + object_class->get_property = nmt_newt_widget_get_property; + object_class->finalize = nmt_newt_widget_finalize; + + widget_class->activated = nmt_newt_widget_real_activated; + + /* signals */ + + /** + * NmtNewtWidget::needs-rebuild: + * @widget: the #NmtNewtWidget + * + * Emitted when nmt_newt_widget_need_rebuild() is called on @widget + * or any of its children. This signal propagates up the container + * hierarchy, eventually reaching the top-level #NmtNewtForm. + */ + signals[NEEDS_REBUILD] = + g_signal_new ("needs-rebuild", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NmtNewtWidgetClass, needs_rebuild), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /** + * NmtNewtWidget::activated: + * @widget: the #NmtNewtWidget + * + * Emitted when the widget's #newtComponent is activated. + */ + signals[ACTIVATED] = + g_signal_new ("activated", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NmtNewtWidgetClass, activated), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /* properties */ + + /** + * NmtNewtWidget:parent: + * + * The widget's parent widget, or %NULL if it has no parent. + */ + g_object_class_install_property (object_class, PROP_PARENT, + g_param_spec_object ("parent", "", "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtWidget:visible: + * + * Whether the widget is visible. Invisible widgets do not get + * realized or sized. + */ + g_object_class_install_property (object_class, PROP_VISIBLE, + g_param_spec_boolean ("visible", "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtWidget:valid: + * + * Whether the widget's content is considered valid. Components + * determine their own validity. A container, by default, is + * considered valid if all of its children are valid. + */ + g_object_class_install_property (object_class, PROP_VALID, + g_param_spec_boolean ("valid", "", "", + TRUE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtWidget:exit-on-activate: + * + * If %TRUE, the widget will call nmt_newt_form_quit() on its form + * when it is activated. + */ + g_object_class_install_property (object_class, PROP_EXIT_ON_ACTIVATE, + g_param_spec_boolean ("exit-on-activate", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/newt/nmt-newt-widget.h b/tui/newt/nmt-newt-widget.h new file mode 100644 index 0000000000..265f2455d1 --- /dev/null +++ b/tui/newt/nmt-newt-widget.h @@ -0,0 +1,117 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_WIDGET_H +#define NMT_NEWT_WIDGET_H + +#include "nmt-newt-types.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_NEWT_WIDGET (nmt_newt_widget_get_type ()) +#define NMT_NEWT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidget)) +#define NMT_NEWT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass)) +#define NMT_IS_NEWT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_NEWT_WIDGET)) +#define NMT_IS_NEWT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_NEWT_WIDGET)) +#define NMT_NEWT_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass)) + +struct _NmtNewtWidget { + GInitiallyUnowned parent; + +}; + +typedef struct { + GInitiallyUnownedClass parent; + + /* signals */ + void (*needs_rebuild) (NmtNewtWidget *widget); + void (*activated) (NmtNewtWidget *widget); + + /* methods */ + void (*realize) (NmtNewtWidget *widget); + void (*unrealize) (NmtNewtWidget *widget); + + newtComponent * (*get_components) (NmtNewtWidget *widget); + NmtNewtWidget * (*find_component) (NmtNewtWidget *widget, + newtComponent co); + + void (*size_request) (NmtNewtWidget *widget, + int *width, + int *height); + void (*size_allocate) (NmtNewtWidget *widget, + int x, + int y, + int width, + int height); + + newtComponent (*get_focus_component) (NmtNewtWidget *widget); + +} NmtNewtWidgetClass; + +GType nmt_newt_widget_get_type (void); + +void nmt_newt_widget_realize (NmtNewtWidget *widget); +void nmt_newt_widget_unrealize (NmtNewtWidget *widget); + +newtComponent *nmt_newt_widget_get_components (NmtNewtWidget *widget); + +void nmt_newt_widget_set_padding (NmtNewtWidget *widget, + int pad_left, + int pad_top, + int pad_right, + int pad_bottom); + +void nmt_newt_widget_size_request (NmtNewtWidget *widget, + int *width, + int *height); +void nmt_newt_widget_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height); + +void nmt_newt_widget_set_parent (NmtNewtWidget *widget, + NmtNewtWidget *parent); +NmtNewtWidget *nmt_newt_widget_get_parent (NmtNewtWidget *widget); + +NmtNewtForm *nmt_newt_widget_get_form (NmtNewtWidget *widget); + +gboolean nmt_newt_widget_get_visible (NmtNewtWidget *widget); +void nmt_newt_widget_set_visible (NmtNewtWidget *widget, + gboolean visible); + +newtComponent nmt_newt_widget_get_focus_component (NmtNewtWidget *widget); + +void nmt_newt_widget_activated (NmtNewtWidget *widget); +gboolean nmt_newt_widget_get_exit_on_activate (NmtNewtWidget *widget); +void nmt_newt_widget_set_exit_on_activate (NmtNewtWidget *widget, + gboolean exit_on_activate); + +gboolean nmt_newt_widget_get_valid (NmtNewtWidget *widget); + +NmtNewtWidget *nmt_newt_widget_find_component (NmtNewtWidget *widget, + newtComponent co); + +/* protected */ +void nmt_newt_widget_needs_rebuild (NmtNewtWidget *widget); +void nmt_newt_widget_set_valid (NmtNewtWidget *widget, + gboolean valid); + +G_END_DECLS + +#endif /* NMT_NEWT_WIDGET_H */ diff --git a/tui/newt/nmt-newt.h b/tui/newt/nmt-newt.h new file mode 100644 index 0000000000..6a9c8d9fb5 --- /dev/null +++ b/tui/newt/nmt-newt.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_H +#define NMT_NEWT_H + +#include "nmt-newt-button.h" +#include "nmt-newt-button-box.h" +#include "nmt-newt-checkbox.h" +#include "nmt-newt-entry.h" +#include "nmt-newt-entry-numeric.h" +#include "nmt-newt-form.h" +#include "nmt-newt-grid.h" +#include "nmt-newt-label.h" +#include "nmt-newt-listbox.h" +#include "nmt-newt-popup.h" +#include "nmt-newt-section.h" +#include "nmt-newt-separator.h" +#include "nmt-newt-stack.h" +#include "nmt-newt-textbox.h" +#include "nmt-newt-toggle-button.h" +#include "nmt-newt-utils.h" + +#endif /* NMT_NEWT_H */ diff --git a/tui/nm-editor-bindings.c b/tui/nm-editor-bindings.c new file mode 100644 index 0000000000..949dcd7d2a --- /dev/null +++ b/tui/nm-editor-bindings.c @@ -0,0 +1,1666 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nm-editor-bindings + * @short_description: #GBinding-based NM connection editor helpers + * + * nm-editor-bindings contains helper functions to bind NMSettings objects + * to connection editing widgets. The goal is that this should eventually be + * shared between nmtui, nm-connection-editor, and gnome-control-center. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> + +#include <dbus/dbus-glib.h> + +#include "nm-editor-bindings.h" +#include "nm-gvaluearray-compat.h" + +static gboolean +ip_string_parse (const char *text, + int family, + gpointer addr, + guint32 *prefix) +{ + const char *slash; + char *addrstr, *end; + gboolean valid; + + slash = strchr (text, '/'); + + if (slash) { + if (!prefix) + return FALSE; + addrstr = g_strndup (text, slash - text); + } else + addrstr = g_strdup (text); + valid = (inet_pton (family, addrstr, addr) == 1); + g_free (addrstr); + + if (!valid) + return FALSE; + + if (slash) { + *prefix = strtoul (slash + 1, &end, 10); + if ( *end + || *prefix == 0 + || (family == AF_INET && *prefix > 32) + || (family == AF_INET6 && *prefix > 128)) + valid = FALSE; + } else if (prefix) + *prefix = 32; + + return valid; +} + +static gboolean +ip4_addresses_with_prefix_to_strv (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *addrs; + GArray *addr; + guint32 addrbytes, prefix; + char buf[INET_ADDRSTRLEN], **strings; + int i; + + addrs = g_value_get_boxed (source_value); + strings = g_new0 (char *, addrs->len + 1); + + for (i = 0; i < addrs->len; i++) { + addr = addrs->pdata[i]; + addrbytes = g_array_index (addr, guint32, 0); + prefix = g_array_index (addr, guint32, 1); + + if (addrbytes) { + strings[i] = g_strdup_printf ("%s/%d", + inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)), + (int) prefix); + } else + strings[i] = g_strdup (""); + } + + g_value_take_boxed (target_value, strings); + return TRUE; +} + +static gboolean +ip4_addresses_with_prefix_from_strv (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + char **strings; + GPtrArray *addrs; + GArray *addr; + guint32 *addrvals; + int i; + + strings = g_value_get_boxed (source_value); + /* Fetch the original property value, so as to preserve the gateway elements */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &addrs, + NULL); + + for (i = 0; strings[i]; i++) { + if (i >= addrs->len) { + guint32 val; + + addr = g_array_sized_new (FALSE, FALSE, sizeof (guint32), 3); + val = 0; + g_array_append_val (addr, val); + val = 32; + g_array_append_val (addr, val); + val = 0; + g_array_append_val (addr, val); + g_ptr_array_add (addrs, addr); + } else + addr = addrs->pdata[i]; + addrvals = (guint32 *)addr->data; + + if (!ip_string_parse (strings[i], AF_INET, &addrvals[0], &addrvals[1])) { + g_ptr_array_unref (addrs); + return FALSE; + } + } + + g_ptr_array_set_size (addrs, i); + g_value_take_boxed (target_value, addrs); + return TRUE; +} + +/** + * nm_editor_bind_ip4_addresses_with_prefix_to_strv: + * @source: the source object (eg, an #NMSettingIP4Config) + * @source_property: the property on @source to bind (eg, + * %NM_SETTING_IP4_CONFIG_ADDRESSES) + * @target: the target object (eg, an #NmtAddressList) + * @target_property: the property on @target to bind + * (eg, "strings") + * @flags: %GBindingFlags + * + * Binds the %DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT property + * @source_property on @source to the %G_TYPE_STRV property + * @target_property on @target. + * + * Each address/prefix/gateway triplet in @source_property will be + * converted to a string of the form "ip.ad.dr.ess/prefix" in + * @target_property (and vice versa if %G_BINDING_BIDIRECTIONAL) is + * specified. The "gateway" fields in @source_property are ignored + * when converting to strings, and unmodified when converting from + * strings. + */ +void +nm_editor_bind_ip4_addresses_with_prefix_to_strv (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + target, target_property, + flags, + ip4_addresses_with_prefix_to_strv, + ip4_addresses_with_prefix_from_strv, + NULL, NULL); +} + +static gboolean +ip4_addresses_to_strv (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GArray *addrs; + guint32 addrbytes; + char buf[INET_ADDRSTRLEN], **strings; + int i; + + addrs = g_value_get_boxed (source_value); + strings = g_new0 (char *, addrs->len + 1); + + for (i = 0; i < addrs->len; i++) { + addrbytes = g_array_index (addrs, guint32, i); + if (addrbytes) + inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)); + else + buf[0] = '\0'; + strings[i] = g_strdup (buf); + } + + g_value_take_boxed (target_value, strings); + return TRUE; +} + +static gboolean +ip4_addresses_from_strv (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + char **strings; + GArray *addrs; + guint32 addr; + int i; + + strings = g_value_get_boxed (source_value); + addrs = g_array_new (FALSE, FALSE, sizeof (guint32)); + + for (i = 0; strings[i]; i++) { + if (!ip_string_parse (strings[i], AF_INET, &addr, NULL)) { + g_array_unref (addrs); + return FALSE; + } + g_array_append_val (addrs, addr); + } + + g_value_take_boxed (target_value, addrs); + return TRUE; +} + +/** + * nm_editor_bind_ip4_addresses_to_strv: + * @source: the source object (eg, an #NMSettingIP4Config) + * @source_property: the property on @source to bind (eg, + * %NM_SETTING_IP4_CONFIG_DNS) + * @target: the target object (eg, an #NmtAddressList) + * @target_property: the property on @target to bind + * (eg, "strings") + * @flags: %GBindingFlags + * + * Binds the %DBUS_TYPE_G_UINT_ARRAY property @source_property on + * @source to the %G_TYPE_STRV property @target_property on @target. + * + * Each address in @source_property will be converted to a string of + * the form "ip.ad.dr.ess" in @target_property (and vice versa if + * %G_BINDING_BIDIRECTIONAL) is specified. + */ +void +nm_editor_bind_ip4_addresses_to_strv (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + target, target_property, + flags, + ip4_addresses_to_strv, + ip4_addresses_from_strv, + NULL, NULL); +} + +static gboolean +ip4_gateway_to_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *addrs; + GArray *addr; + guint32 gateway = 0; + const char *str; + char buf[INET_ADDRSTRLEN]; + int i; + + addrs = g_value_get_boxed (source_value); + for (i = 0; i < addrs->len; i++) { + addr = addrs->pdata[i]; + gateway = g_array_index (addr, guint32, 2); + if (gateway) + break; + } + + if (gateway) + str = inet_ntop (AF_INET, &gateway, buf, sizeof (buf)); + else + str = ""; + g_value_set_string (target_value, str); + return TRUE; +} + +static gboolean +ip4_gateway_from_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + const char *text; + GPtrArray *addrs; + GArray *addr; + guint32 addrbytes, *addrvals; + int i; + + text = g_value_get_string (source_value); + if (!ip_string_parse (text, AF_INET, &addrbytes, NULL)) + return FALSE; + + /* Fetch the original property value, so as to preserve the IP address elements */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &addrs, + NULL); + if (!addrs->len) { + g_ptr_array_unref (addrs); + return FALSE; + } + addr = addrs->pdata[0]; + addrvals = (guint32 *)addr->data; + if (addrbytes == addrvals[2]) { + g_ptr_array_unref (addrs); + return FALSE; + } + addrvals[2] = addrbytes; + + for (i = 1; i < addrs->len; i++) { + addr = addrs->pdata[i]; + addrvals = (guint32 *)addr->data; + addrvals[2] = 0; + } + + g_value_take_boxed (target_value, addrs); + return TRUE; +} + +/** + * nm_editor_bind_ip4_gateway_to_string: + * @source: the source object (eg, an #NMSettingIP4Config) + * @source_property: the property on @source to bind (eg, + * %NM_SETTING_IP4_CONFIG_ADDRESSES) + * @target: the target object (eg, an #NmtNewtEntry) + * @target_property: the property on @target to bind + * (eg, "text") + * @flags: %GBindingFlags + * + * Binds the %DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT property + * @source_property on @source to the %G_TYPE_STRING property + * @target_property on @target. + * + * Specifically, this binds the "gateway" field of the first address + * in @source_property; all other addresses in @source_property are + * ignored, and its "address" and "prefix" fields are unmodified. + */ +void +nm_editor_bind_ip4_gateway_to_string (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + target, target_property, + flags, + ip4_gateway_to_string, + ip4_gateway_from_string, + NULL, NULL); +} + +static gboolean +ip4_route_transform_to_dest_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP4Route *route; + char buf[INET_ADDRSTRLEN], *string; + guint32 addrbytes; + + route = g_value_get_boxed (source_value); + if (route) + addrbytes = nm_ip4_route_get_dest (route); + else + addrbytes = 0; + + if (addrbytes) { + string = g_strdup_printf ("%s/%d", + inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)), + (int) nm_ip4_route_get_prefix (route)); + g_value_take_string (target_value, string); + } else + g_value_set_string (target_value, ""); + return TRUE; +} + +static gboolean +ip4_route_transform_to_next_hop_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP4Route *route; + char buf[INET_ADDRSTRLEN]; + guint32 addrbytes; + + route = g_value_get_boxed (source_value); + if (route) + addrbytes = nm_ip4_route_get_next_hop (route); + else + addrbytes = 0; + + if (addrbytes) + inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)); + else + buf[0] = '\0'; + g_value_set_string (target_value, buf); + return TRUE; +} + +static gboolean +ip4_route_transform_to_metric_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP4Route *route; + char *string; + + route = g_value_get_boxed (source_value); + if (route && nm_ip4_route_get_dest (route)) { + string = g_strdup_printf ("%lu", (gulong) nm_ip4_route_get_metric (route)); + g_value_take_string (target_value, string); + } else + g_value_set_string (target_value, ""); + return TRUE; +} + +static gboolean +ip4_route_transform_from_dest_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP4Route *route; + const char *text; + guint32 addrbytes, prefix; + + text = g_value_get_string (source_value); + if (!ip_string_parse (text, AF_INET, &addrbytes, &prefix)) + return FALSE; + + /* Fetch the original property value */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &route, + NULL); + + nm_ip4_route_set_dest (route, addrbytes); + nm_ip4_route_set_prefix (route, prefix); + + g_value_take_boxed (target_value, route); + return TRUE; +} + +static gboolean +ip4_route_transform_from_next_hop_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP4Route *route; + const char *text; + guint32 addrbytes; + + text = g_value_get_string (source_value); + if (*text) { + if (!ip_string_parse (text, AF_INET, &addrbytes, NULL)) + return FALSE; + } else + addrbytes = 0; + + /* Fetch the original property value */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &route, + NULL); + + nm_ip4_route_set_next_hop (route, addrbytes); + + g_value_take_boxed (target_value, route); + return TRUE; +} + +static gboolean +ip4_route_transform_from_metric_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP4Route *route; + const char *text; + guint32 metric; + + text = g_value_get_string (source_value); + metric = strtoul (text, NULL, 10); + + /* Fetch the original property value */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &route, + NULL); + + nm_ip4_route_set_metric (route, metric); + + g_value_take_boxed (target_value, route); + return TRUE; +} + +/** + * nm_editor_bind_ip4_route_to_strings: + * @source: the source object + * @source_property: the source property + * @dest_target: the target object for the route's destionation + * @dest_target_property: the property on @dest_target + * @next_hop_target: the target object for the route's next hop + * @next_hop_target_property: the property on @next_hop_target + * @metric_target: the target object for the route's metric + * @metric_target_property: the property on @metric_target + * @flags: %GBindingFlags + * + * Binds the #NMIP4Route-valued property @source_property on @source + * to the three indicated string-valued target properties (and vice + * versa if %G_BINDING_BIDIRECTIONAL is specified). + * + * @dest_target_property should be an "address/prefix" string, as with + * nm_editor_bind_ip4_addresses_with_prefix_to_strv(). @next_hop_target + * is a plain IP address, and @metric_target is a number. + */ +void +nm_editor_bind_ip4_route_to_strings (gpointer source, + const gchar *source_property, + gpointer dest_target, + const gchar *dest_target_property, + gpointer next_hop_target, + const gchar *next_hop_target_property, + gpointer metric_target, + const gchar *metric_target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + dest_target, dest_target_property, + flags, + ip4_route_transform_to_dest_string, + ip4_route_transform_from_dest_string, + NULL, NULL); + g_object_bind_property_full (source, source_property, + next_hop_target, next_hop_target_property, + flags, + ip4_route_transform_to_next_hop_string, + ip4_route_transform_from_next_hop_string, + NULL, NULL); + g_object_bind_property_full (source, source_property, + metric_target, metric_target_property, + flags, + ip4_route_transform_to_metric_string, + ip4_route_transform_from_metric_string, + NULL, NULL); +} + +#define IP6_ADDRESS_SET(addr) ( addr \ + && addr->len == sizeof (struct in6_addr) \ + && memcmp (addr->data, &in6addr_any, addr->len) != 0) + +static gboolean +ip6_addresses_with_prefix_to_strv (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *addrs; + GValueArray *addr; + GValue *val; + GByteArray *addrbytes; + guint prefix; + char **strings, buf[INET6_ADDRSTRLEN]; + int i; + + addrs = g_value_get_boxed (source_value); + strings = g_new0 (char *, addrs->len + 1); + + for (i = 0; i < addrs->len; i++) { + addr = addrs->pdata[i]; + val = g_value_array_get_nth (addr, 0); + addrbytes = g_value_get_boxed (val); + val = g_value_array_get_nth (addr, 1); + prefix = g_value_get_uint (val); + + if (IP6_ADDRESS_SET (addrbytes)) { + strings[i] = g_strdup_printf ("%s/%d", + inet_ntop (AF_INET6, addrbytes->data, buf, sizeof (buf)), + prefix); + } else + strings[i] = g_strdup (""); + } + + g_value_take_boxed (target_value, strings); + return TRUE; +} + +static gboolean +ip6_addresses_with_prefix_from_strv (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + char **strings; + GPtrArray *addrs; + GValueArray *addr; + guint32 prefix; + GValue val = G_VALUE_INIT, *valp; + GByteArray *ba; + int i; + + strings = g_value_get_boxed (source_value); + + /* Fetch the original property value, so as to preserve the gateway elements */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &addrs, + NULL); + + for (i = 0; strings[i]; i++) { + if (i > addrs->len) { + addr = g_value_array_new (3); + + g_value_init (&val, DBUS_TYPE_G_UCHAR_ARRAY); + ba = g_byte_array_sized_new (sizeof (struct in6_addr)); + g_byte_array_append (ba, (guint8 *) &in6addr_any, sizeof (struct in6_addr)); + g_value_take_boxed (&val, ba); + g_value_array_append (addr, &val); + g_value_unset (&val); + + g_value_init (&val, G_TYPE_UINT); + g_value_set_uint (&val, 128); + g_value_array_append (addr, &val); + g_value_unset (&val); + + g_value_init (&val, DBUS_TYPE_G_UCHAR_ARRAY); + ba = g_byte_array_sized_new (sizeof (struct in6_addr)); + g_byte_array_append (ba, (guint8 *) &in6addr_any, sizeof (struct in6_addr)); + g_value_take_boxed (&val, ba); + g_value_array_append (addr, &val); + g_value_unset (&val); + + g_ptr_array_add (addrs, addr); + } else + addr = addrs->pdata[i]; + + valp = g_value_array_get_nth (addr, 0); + ba = g_value_get_boxed (valp); + g_assert (ba->len == sizeof (struct in6_addr)); + + if (!ip_string_parse (strings[i], AF_INET6, ba->data, &prefix)) { + g_ptr_array_unref (addrs); + return FALSE; + } + + valp = g_value_array_get_nth (addr, 1); + g_value_set_uint (valp, prefix); + } + + g_ptr_array_set_size (addrs, i); + g_value_set_boxed (target_value, addrs); + return TRUE; +} + +/** + * nm_editor_bind_ip6_addresses_with_prefix_to_strv: + * @source: the source object (eg, an #NMSettingIP6Config) + * @source_property: the property on @source to bind (eg, + * %NM_SETTING_IP6_CONFIG_ADDRESSES) + * @target: the target object (eg, an #NmtAddressList) + * @target_property: the property on @target to bind + * (eg, "strings") + * @flags: %GBindingFlags + * + * Binds the %DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS property + * @source_property on @source to the %G_TYPE_STRV property + * @target_property on @target. + * + * Each address/prefix/gateway triplet in @source_property will be + * converted to a string of the form "ip::ad:dr:ess/prefix" in + * @target_property (and vice versa if %G_BINDING_BIDIRECTIONAL) is + * specified. The "gateway" fields in @source_property are ignored + * when converting to strings, and unmodified when converting from + * strings. + */ +void +nm_editor_bind_ip6_addresses_with_prefix_to_strv (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + target, target_property, + flags, + ip6_addresses_with_prefix_to_strv, + ip6_addresses_with_prefix_from_strv, + NULL, NULL); +} + +static gboolean +ip6_addresses_to_strv (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *addrs; + GByteArray *addrbytes; + char buf[INET6_ADDRSTRLEN], **strings; + int i; + + addrs = g_value_get_boxed (source_value); + strings = g_new0 (char *, addrs->len + 1); + + for (i = 0; i < addrs->len; i++) { + addrbytes = addrs->pdata[i]; + if (IP6_ADDRESS_SET (addrbytes)) + inet_ntop (AF_INET, addrbytes->data, buf, sizeof (buf)); + else + buf[0] = '\0'; + strings[i] = g_strdup (buf); + } + + g_value_take_boxed (target_value, strings); + return TRUE; +} + +static gboolean +ip6_addresses_from_strv (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + char **strings; + GPtrArray *addrs; + GByteArray *addr; + struct in6_addr addrbytes; + int i; + + strings = g_value_get_boxed (source_value); + addrs = g_ptr_array_new (); + + for (i = 0; strings[i]; i++) { + if (!ip_string_parse (strings[i], AF_INET6, &addrbytes, NULL)) { + while (i--) + g_byte_array_unref (addrs->pdata[i]); + g_ptr_array_unref (addrs); + return FALSE; + } + + addr = g_byte_array_sized_new (sizeof (addrbytes)); + g_byte_array_append (addr, (guint8 *)&addrbytes, sizeof (addrbytes)); + g_ptr_array_add (addrs, addr); + } + + g_value_take_boxed (target_value, addrs); + return TRUE; +} + +/** + * nm_editor_bind_ip6_addresses_to_strv: + * @source: the source object (eg, an #NMSettingIP6Config) + * @source_property: the property on @source to bind (eg, + * %NM_SETTING_IP6_CONFIG_DNS) + * @target: the target object (eg, an #NmtAddressList) + * @target_property: the property on @target to bind + * (eg, "strings") + * @flags: %GBindingFlags + * + * Binds the %DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UCHAR property + * @source_property on @source to the %G_TYPE_STRV property + * @target_property on @target. + * + * Each address in @source_property will be converted to a string of + * the form "ip::ad:dr:ess" in @target_property (and vice versa if + * %G_BINDING_BIDIRECTIONAL) is specified. + */ +void +nm_editor_bind_ip6_addresses_to_strv (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + target, target_property, + flags, + ip6_addresses_to_strv, + ip6_addresses_from_strv, + NULL, NULL); +} + +static gboolean +ip6_gateway_to_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *addrs; + GValueArray *addr; + GValue *val; + GByteArray *gateway; + char buf[INET6_ADDRSTRLEN]; + const char *str; + + addrs = g_value_get_boxed (source_value); + if (addrs->len == 0) + return FALSE; + + addr = addrs->pdata[0]; + val = g_value_array_get_nth (addr, 2); + gateway = g_value_get_boxed (val); + + if (IP6_ADDRESS_SET (gateway)) + str = inet_ntop (AF_INET6, gateway->data, buf, sizeof (buf)); + else + str = ""; + g_value_set_string (target_value, str); + return TRUE; +} + +static gboolean +ip6_gateway_from_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *addrs; + const char *text; + GValueArray *addr; + struct in6_addr gateway; + GValue *val; + GByteArray *ba; + int i; + + text = g_value_get_string (source_value); + if (!ip_string_parse (text, AF_INET6, &gateway, NULL)) + return FALSE; + + /* Fetch the original property value, so as to preserve the IP address elements */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &addrs, + NULL); + if (!addrs->len) { + g_ptr_array_unref (addrs); + return FALSE; + } + + addr = addrs->pdata[0]; + + ba = g_byte_array_sized_new (sizeof (gateway)); + g_byte_array_append (ba, (guint8 *) &gateway, sizeof (gateway)); + + val = g_value_array_get_nth (addr, 2); + g_value_take_boxed (val, ba); + + for (i = 1; i < addrs->len; i++) { + addr = addrs->pdata[i]; + val = g_value_array_get_nth (addr, 2); + ba = g_value_get_boxed (val); + + if (ba) + memset (ba->data, 0, ba->len); + } + + g_value_take_boxed (target_value, addrs); + return TRUE; +} + +/** + * nm_editor_bind_ip6_gateway_to_string: + * @source: the source object (eg, an #NMSettingIP6Config) + * @source_property: the property on @source to bind (eg, + * %NM_SETTING_IP6_CONFIG_ADDRESSES) + * @target: the target object (eg, an #NmtNewtEntry) + * @target_property: the property on @target to bind + * (eg, "text") + * @flags: %GBindingFlags + * + * Binds the %DBUS_TYPE_G_ARRAY_OF_IP6_ADDRESS property + * @source_property on @source to the %G_TYPE_STRING property + * @target_property on @target. + * + * Specifically, this binds the "gateway" field of the first address + * in @source_property; all other addresses in @source_property are + * ignored, and its "address" and "prefix" fields are unmodified. + */ +void +nm_editor_bind_ip6_gateway_to_string (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + target, target_property, + flags, + ip6_gateway_to_string, + ip6_gateway_from_string, + NULL, NULL); +} + +#define IN6_ADDR_SET(bytes) (memcmp (bytes, &in6addr_any, sizeof (struct in6_addr)) != 0) + +static gboolean +ip6_route_transform_to_dest_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP6Route *route; + char buf[INET6_ADDRSTRLEN], *string; + const struct in6_addr *addrbytes; + + route = g_value_get_boxed (source_value); + if (route) + addrbytes = nm_ip6_route_get_dest (route); + else + addrbytes = &in6addr_any; + + if (IN6_ADDR_SET (addrbytes)) { + string = g_strdup_printf ("%s/%d", + inet_ntop (AF_INET6, addrbytes, buf, sizeof (buf)), + (int) nm_ip6_route_get_prefix (route)); + g_value_take_string (target_value, string); + } else + g_value_set_string (target_value, ""); + return TRUE; +} + +static gboolean +ip6_route_transform_to_next_hop_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP6Route *route; + char buf[INET6_ADDRSTRLEN]; + const struct in6_addr *addrbytes; + + route = g_value_get_boxed (source_value); + if (route) + addrbytes = nm_ip6_route_get_next_hop (route); + else + addrbytes = &in6addr_any; + + if (IN6_ADDR_SET (addrbytes)) + inet_ntop (AF_INET, &addrbytes, buf, sizeof (buf)); + else + buf[0] = '\0'; + g_value_set_string (target_value, buf); + return TRUE; +} + +static gboolean +ip6_route_transform_to_metric_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP6Route *route; + char *string; + + route = g_value_get_boxed (source_value); + if (route && IN6_ADDR_SET (nm_ip6_route_get_dest (route))) { + string = g_strdup_printf ("%lu", (gulong) nm_ip6_route_get_metric (route)); + g_value_take_string (target_value, string); + } else + g_value_set_string (target_value, ""); + return TRUE; +} + +static gboolean +ip6_route_transform_from_dest_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP6Route *route; + const char *text; + const struct in6_addr *addrbytes; + guint32 prefix; + + text = g_value_get_string (source_value); + if (!ip_string_parse (text, AF_INET6, &addrbytes, &prefix)) + return FALSE; + + /* Fetch the original property value */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &route, + NULL); + + nm_ip6_route_set_dest (route, addrbytes); + nm_ip6_route_set_prefix (route, prefix); + + g_value_take_boxed (target_value, route); + return TRUE; +} + +static gboolean +ip6_route_transform_from_next_hop_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP6Route *route; + const char *text; + const struct in6_addr *addrbytes; + + text = g_value_get_string (source_value); + if (*text) { + if (!ip_string_parse (text, AF_INET6, &addrbytes, NULL)) + return FALSE; + } else + addrbytes = 0; + + /* Fetch the original property value */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &route, + NULL); + + nm_ip6_route_set_next_hop (route, addrbytes); + + g_value_take_boxed (target_value, route); + return TRUE; +} + +static gboolean +ip6_route_transform_from_metric_string (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMIP6Route *route; + const char *text; + guint32 metric; + + text = g_value_get_string (source_value); + metric = strtoul (text, NULL, 10); + + /* Fetch the original property value */ + g_object_get (g_binding_get_source (binding), + g_binding_get_source_property (binding), &route, + NULL); + + nm_ip6_route_set_metric (route, metric); + + g_value_take_boxed (target_value, route); + return TRUE; +} + +/** + * nm_editor_bind_ip6_route_to_strings: + * @source: the source object + * @source_property: the source property + * @dest_target: the target object for the route's destionation + * @dest_target_property: the property on @dest_target + * @next_hop_target: the target object for the route's next hop + * @next_hop_target_property: the property on @next_hop_target + * @metric_target: the target object for the route's metric + * @metric_target_property: the property on @metric_target + * @flags: %GBindingFlags + * + * Binds the #NMIP6Route-valued property @source_property on @source + * to the three indicated string-valued target properties (and vice + * versa if %G_BINDING_BIDIRECTIONAL is specified). + * + * @dest_target_property should be an "address/prefix" string, as with + * nm_editor_bind_ip6_addresses_with_prefix_to_strv(). @next_hop_target + * is a plain IP address, and @metric_target is a number. + */ +void +nm_editor_bind_ip6_route_to_strings (gpointer source, + const gchar *source_property, + gpointer dest_target, + const gchar *dest_target_property, + gpointer next_hop_target, + const gchar *next_hop_target_property, + gpointer metric_target, + const gchar *metric_target_property, + GBindingFlags flags) +{ + g_object_bind_property_full (source, source_property, + dest_target, dest_target_property, + flags, + ip6_route_transform_to_dest_string, + ip6_route_transform_from_dest_string, + NULL, NULL); + g_object_bind_property_full (source, source_property, + next_hop_target, next_hop_target_property, + flags, + ip6_route_transform_to_next_hop_string, + ip6_route_transform_from_next_hop_string, + NULL, NULL); + g_object_bind_property_full (source, source_property, + metric_target, metric_target_property, + flags, + ip6_route_transform_to_metric_string, + ip6_route_transform_from_metric_string, + NULL, NULL); +} + +/* Wireless security method binding */ +typedef struct { + NMConnection *connection; + NMSettingWirelessSecurity *s_wsec; + gboolean s_wsec_in_use; + + GObject *target; + char *target_property; + + gboolean updating; +} NMEditorWirelessSecurityMethodBinding; + +static const char * +get_security_type (NMEditorWirelessSecurityMethodBinding *binding) +{ + const char *key_mgmt, *auth_alg; + + if (!binding->s_wsec_in_use) + return "none"; + + key_mgmt = nm_setting_wireless_security_get_key_mgmt (binding->s_wsec); + auth_alg = nm_setting_wireless_security_get_auth_alg (binding->s_wsec); + + /* No IEEE 802.1x */ + if (!strcmp (key_mgmt, "none")) { + NMWepKeyType wep_type = nm_setting_wireless_security_get_wep_key_type (binding->s_wsec); + + if (wep_type == NM_WEP_KEY_TYPE_KEY) + return "wep-key"; + else + return "wep-passphrase"; + } + + if (!strcmp (key_mgmt, "ieee8021x")) { + if (auth_alg && !strcmp (auth_alg, "leap")) + return "leap"; + return "dynamic-wep"; + } + + if ( !strcmp (key_mgmt, "wpa-none") + || !strcmp (key_mgmt, "wpa-psk")) + return "wpa-personal"; + + if (!strcmp (key_mgmt, "wpa-eap")) + return "wpa-enterprise"; + + return NULL; +} + +static void +wireless_security_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMEditorWirelessSecurityMethodBinding *binding = user_data; + + if (binding->updating) + return; + + binding->updating = TRUE; + g_object_set (binding->target, + binding->target_property, get_security_type (binding), + NULL); + binding->updating = FALSE; +} + +static void +wireless_connection_changed (NMConnection *connection, + gpointer user_data) +{ + NMEditorWirelessSecurityMethodBinding *binding = user_data; + NMSettingWirelessSecurity *s_wsec; + + if (binding->updating) + return; + + s_wsec = nm_connection_get_setting_wireless_security (connection); + if ( (s_wsec && binding->s_wsec_in_use) + || (!s_wsec && !binding->s_wsec_in_use)) + return; + + binding->s_wsec_in_use = !binding->s_wsec_in_use; + wireless_security_changed (NULL, NULL, binding); +} + +static void +wireless_security_target_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMEditorWirelessSecurityMethodBinding *binding = user_data; + char *method; + + if (binding->updating) + return; + + g_object_get (binding->target, + binding->target_property, &method, + NULL); + + binding->updating = TRUE; + + if (!strcmp (method, "none")) { + if (!binding->s_wsec_in_use) + return; + binding->s_wsec_in_use = FALSE; + nm_connection_remove_setting (binding->connection, NM_TYPE_SETTING_WIRELESS_SECURITY); + + binding->updating = FALSE; + return; + } + + if (!binding->s_wsec_in_use) { + binding->s_wsec_in_use = TRUE; + nm_connection_add_setting (binding->connection, NM_SETTING (binding->s_wsec)); + } + + if (!strcmp (method, "wep-key")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_KEY, + NULL); + } else if (!strcmp (method, "wep-passphrase")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "none", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_PASSPHRASE, + NULL); + } else if (!strcmp (method, "leap")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "leap", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, + NULL); + } else if (!strcmp (method, "dynamic-wep")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "ieee8021x", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, "open", + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, + NULL); + } else if (!strcmp (method, "wpa-personal")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-psk", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, NULL, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, + NULL); + } else if (!strcmp (method, "wpa-enterprise")) { + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, "wpa-eap", + NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, NULL, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, NM_WEP_KEY_TYPE_UNKNOWN, + NULL); + } else + g_warn_if_reached (); + + binding->updating = FALSE; +} + +static void +wireless_security_target_destroyed (gpointer user_data, + GObject *ex_target) +{ + NMEditorWirelessSecurityMethodBinding *binding = user_data; + + g_signal_handlers_disconnect_by_func (binding->s_wsec, G_CALLBACK (wireless_security_changed), binding); + g_object_unref (binding->s_wsec); + g_object_unref (binding->connection); + + g_free (binding->target_property); + + g_slice_free (NMEditorWirelessSecurityMethodBinding, binding); +} + +/** + * nm_editor_bind_wireless_security_method: + * @connection: an #NMConnection + * @s_wsec: an #NMSettingWirelessSecurity + * @target: the target widget + * @target_property: the string-valued property on @target to bind + * @flags: %GBindingFlags + * + * Binds the wireless security method on @connection to + * @target_property on @target (and vice versa if + * %G_BINDING_BIDIRECTIONAL). + * + * @target_property will be of the values "none", "wpa-personal", + * "wpa-enterprise", "wep-key", "wep-passphrase", "dynamic-wep", or + * "leap". + * + * If binding bidirectionally, @s_wsec will be automatically added to + * or removed from @connection as needed when @target_property + * changes. + */ +void +nm_editor_bind_wireless_security_method (NMConnection *connection, + NMSettingWirelessSecurity *s_wsec, + gpointer target, + const char *target_property, + GBindingFlags flags) +{ + NMEditorWirelessSecurityMethodBinding *binding; + char *notify; + + binding = g_slice_new0 (NMEditorWirelessSecurityMethodBinding); + + binding->target = target; + binding->target_property = g_strdup (target_property); + if (flags & G_BINDING_BIDIRECTIONAL) { + notify = g_strdup_printf ("notify::%s", target_property); + g_signal_connect (target, notify, G_CALLBACK (wireless_security_target_changed), binding); + g_free (notify); + } + g_object_weak_ref (target, wireless_security_target_destroyed, binding); + + binding->connection = g_object_ref (connection); + g_signal_connect (connection, NM_CONNECTION_CHANGED, + G_CALLBACK (wireless_connection_changed), binding); + binding->s_wsec_in_use = (nm_connection_get_setting_wireless_security (connection) != NULL); + + binding->s_wsec = g_object_ref (s_wsec); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_KEY_MGMT, + G_CALLBACK (wireless_security_changed), binding); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_AUTH_ALG, + G_CALLBACK (wireless_security_changed), binding); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY_TYPE, + G_CALLBACK (wireless_security_changed), binding); + + if (flags & G_BINDING_SYNC_CREATE) + wireless_security_changed (NULL, NULL, binding); +} + +/* WEP key binding */ + +typedef struct { + NMSettingWirelessSecurity *s_wsec; + GObject *entry, *key_selector; + char *entry_property, *key_selector_property; + + gboolean updating; +} NMEditorWepKeyBinding; + +static void +wep_key_setting_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMEditorWepKeyBinding *binding = user_data; + const char *key; + int index; + + if (binding->updating) + return; + + index = nm_setting_wireless_security_get_wep_tx_keyidx (binding->s_wsec); + key = nm_setting_wireless_security_get_wep_key (binding->s_wsec, index); + + binding->updating = TRUE; + g_object_set (binding->key_selector, + binding->key_selector_property, index, + NULL); + g_object_set (binding->entry, + binding->entry_property, key, + NULL); + binding->updating = FALSE; +} + +static void +wep_key_ui_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMEditorWepKeyBinding *binding = user_data; + char *key; + int index; + + if (binding->updating) + return; + + g_object_get (binding->key_selector, + binding->key_selector_property, &index, + NULL); + g_object_get (binding->entry, + binding->entry_property, &key, + NULL); + + binding->updating = TRUE; + g_object_set (binding->s_wsec, + NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, index, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, index == 0 ? key : NULL, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, index == 1 ? key : NULL, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, index == 2 ? key : NULL, + NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, index == 3 ? key : NULL, + NULL); + binding->updating = FALSE; + + g_free (key); +} + +static void +wep_key_target_destroyed (gpointer user_data, + GObject *ex_target) +{ + NMEditorWepKeyBinding *binding = user_data; + + g_signal_handlers_disconnect_by_func (binding->s_wsec, G_CALLBACK (wep_key_setting_changed), binding); + + if (ex_target != binding->entry) { + g_signal_handlers_disconnect_by_func (binding->entry, G_CALLBACK (wep_key_ui_changed), binding); + g_object_weak_unref (binding->entry, wep_key_target_destroyed, binding); + } else { + g_signal_handlers_disconnect_by_func (binding->key_selector, G_CALLBACK (wep_key_ui_changed), binding); + g_object_weak_unref (binding->key_selector, wep_key_target_destroyed, binding); + } + + g_object_unref (binding->s_wsec); + g_free (binding->entry_property); + g_free (binding->key_selector_property); + + g_slice_free (NMEditorWepKeyBinding, binding); +} + +/** + * nm_editor_bind_wireless_security_wep_key: + * @s_wsec: an #NMSettingWirelessSecurity + * @entry: an entry widget + * @entry_property: the string-valued property on @entry to bind + * @key_selector: a pop-up widget of some sort + * @key_selector_property: the integer-valued property on + * @key_selector to bind + * @flags: %GBindingFlags + * + * Binds the "wep-tx-keyidx" property on @s_wsec to + * @key_selector_property on @key_selector, and the corresponding + * "wep-keyN" property to @entry_property on @entry (and vice versa if + * %G_BINDING_BIDIRECTIONAL). + */ +void +nm_editor_bind_wireless_security_wep_key (NMSettingWirelessSecurity *s_wsec, + gpointer entry, + const char *entry_property, + gpointer key_selector, + const char *key_selector_property, + GBindingFlags flags) +{ + NMEditorWepKeyBinding *binding; + char *notify; + + binding = g_slice_new0 (NMEditorWepKeyBinding); + binding->s_wsec = g_object_ref (s_wsec); + binding->entry = entry; + binding->entry_property = g_strdup (entry_property); + binding->key_selector = key_selector; + binding->key_selector_property = g_strdup (key_selector_property); + + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY0, + G_CALLBACK (wep_key_setting_changed), binding); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY1, + G_CALLBACK (wep_key_setting_changed), binding); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY2, + G_CALLBACK (wep_key_setting_changed), binding); + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_KEY3, + G_CALLBACK (wep_key_setting_changed), binding); + + g_signal_connect (s_wsec, "notify::" NM_SETTING_WIRELESS_SECURITY_WEP_TX_KEYIDX, + G_CALLBACK (wep_key_setting_changed), binding); + + if (flags & G_BINDING_BIDIRECTIONAL) { + notify = g_strdup_printf ("notify::%s", entry_property); + g_signal_connect (entry, notify, G_CALLBACK (wep_key_ui_changed), binding); + g_free (notify); + + notify = g_strdup_printf ("notify::%s", key_selector_property); + g_signal_connect (key_selector, notify, G_CALLBACK (wep_key_ui_changed), binding); + g_free (notify); + } + + g_object_weak_ref (entry, wep_key_target_destroyed, binding); + g_object_weak_ref (key_selector, wep_key_target_destroyed, binding); + + if (flags & G_BINDING_SYNC_CREATE) + wep_key_setting_changed (NULL, NULL, binding); +} + +/* VLAN binding */ + +typedef struct { + NMSettingVlan *s_vlan; + + char *last_ifname_parent; + int last_ifname_id; + + gboolean updating; +} NMEditorVlanWidgetBinding; + +static gboolean +parse_interface_name (const char *ifname, + char **parent_ifname, + int *id) +{ + const char *ifname_end; + char *end; + + if (!ifname || !*ifname) + return FALSE; + + if (g_str_has_prefix (ifname, "vlan")) { + ifname_end = ifname + 4; + *id = strtoul (ifname_end, &end, 10); + if (*end || end == (char *)ifname_end || *id < 0) + return FALSE; + *parent_ifname = NULL; + return TRUE; + } + + ifname_end = strchr (ifname, '.'); + if (ifname_end) { + *id = strtoul (ifname_end + 1, &end, 10); + if (*end || end == (char *)ifname_end + 1 || *id < 0) + return FALSE; + *parent_ifname = g_strndup (ifname, ifname_end - ifname); + return TRUE; + } + + return FALSE; +} + +static void +vlan_settings_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMEditorVlanWidgetBinding *binding = user_data; + const char *ifname, *parent; + char *ifname_parent; + int ifname_id, id; + + if (binding->updating) + return; + + ifname = nm_setting_vlan_get_interface_name (binding->s_vlan); + parent = nm_setting_vlan_get_parent (binding->s_vlan); + id = nm_setting_vlan_get_id (binding->s_vlan); + + if (!parse_interface_name (ifname, &ifname_parent, &ifname_id)) + return; + + /* If the id in INTERFACE_NAME changed, and ID is either unset, or was previously + * in sync with INTERFACE_NAME, then update ID. + */ + if ( id != ifname_id + && (id == binding->last_ifname_id || id == 0)) { + binding->updating = TRUE; + g_object_set (G_OBJECT (binding->s_vlan), + NM_SETTING_VLAN_ID, ifname_id, + NULL); + binding->updating = FALSE; + } + + /* If the PARENT in INTERFACE_NAME changed, and PARENT is either unset, or was + * previously in sync with INTERFACE_NAME, then update PARENT. + */ + if ( g_strcmp0 (parent, ifname_parent) != 0 + && ( g_strcmp0 (parent, binding->last_ifname_parent) == 0 + || !parent || !*parent)) { + binding->updating = TRUE; + g_object_set (G_OBJECT (binding->s_vlan), + NM_SETTING_VLAN_PARENT, ifname_parent, + NULL); + binding->updating = FALSE; + } + + g_free (binding->last_ifname_parent); + binding->last_ifname_parent = ifname_parent; + binding->last_ifname_id = ifname_id; +} + +static void +vlan_target_destroyed (gpointer user_data, + GObject *ex_target) +{ + NMEditorVlanWidgetBinding *binding = user_data; + + g_free (binding->last_ifname_parent); + g_slice_free (NMEditorVlanWidgetBinding, binding); +} + +/** + * nm_editor_bind_vlan_name: + * @s_vlan: an #NMSettingVlan + * + * Binds together several properties on @s_vlan, so that if the + * %NM_SETTING_VLAN_INTERFACE_NAME matches %NM_SETTING_VLAN_PARENT + * and %NM_SETTING_VLAN_ID in the obvious way, then changes to + * %NM_SETTING_VLAN_INTERFACE_NAME will propagate to the other + * two properties automatically. + */ +void +nm_editor_bind_vlan_name (NMSettingVlan *s_vlan) +{ + NMEditorVlanWidgetBinding *binding; + const char *ifname; + + binding = g_slice_new0 (NMEditorVlanWidgetBinding); + binding->s_vlan = s_vlan; + + g_signal_connect (s_vlan, "notify::" NM_SETTING_VLAN_INTERFACE_NAME, + G_CALLBACK (vlan_settings_changed), binding); + + g_object_weak_ref (G_OBJECT (s_vlan), vlan_target_destroyed, binding); + + ifname = nm_setting_vlan_get_interface_name (s_vlan); + if (!parse_interface_name (ifname, &binding->last_ifname_parent, &binding->last_ifname_id)) { + binding->last_ifname_parent = NULL; + binding->last_ifname_id = 0; + } +} + + +#define DBUS_TYPE_G_LIST_OF_STRING (dbus_g_type_get_collection ("GSList", G_TYPE_STRING)) + +static void +convert_slist_to_strv (const GValue *src_value, GValue *dest_value) +{ + GSList *slist; + char **strv; + int len, i = 0; + + slist = g_value_get_boxed (src_value); + len = g_slist_length (slist); + + strv = g_new (char *, len + 1); + for (i = 0; slist; slist = slist->next, i++) + strv[i] = g_strdup (slist->data); + strv[i] = NULL; + + g_value_take_boxed (dest_value, strv); +} + +void +nm_editor_bindings_init (void) +{ + /* FIXME: libnm registers strv->list, but not list->strv */ + g_value_register_transform_func (DBUS_TYPE_G_LIST_OF_STRING, + G_TYPE_STRV, + convert_slist_to_strv); +} diff --git a/tui/nm-editor-bindings.h b/tui/nm-editor-bindings.h new file mode 100644 index 0000000000..a7a32dd142 --- /dev/null +++ b/tui/nm-editor-bindings.h @@ -0,0 +1,99 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NM_EDITOR_BINDINGS_H +#define NM_EDITOR_BINDINGS_H + +#include <glib-object.h> +#include <nm-connection.h> +#include <nm-setting-wireless-security.h> +#include <nm-setting-vlan.h> + +G_BEGIN_DECLS + +void nm_editor_bindings_init (void); + +void nm_editor_bind_ip4_addresses_with_prefix_to_strv (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags); +void nm_editor_bind_ip4_addresses_to_strv (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags); +void nm_editor_bind_ip4_gateway_to_string (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags); + +void nm_editor_bind_ip4_route_to_strings (gpointer source, + const gchar *source_property, + gpointer dest_target, + const gchar *dest_target_property, + gpointer next_hop_target, + const gchar *next_hop_target_property, + gpointer metric_target, + const gchar *metric_target_property, + GBindingFlags flags); + +void nm_editor_bind_ip6_addresses_with_prefix_to_strv (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags); +void nm_editor_bind_ip6_addresses_to_strv (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags); +void nm_editor_bind_ip6_gateway_to_string (gpointer source, + const gchar *source_property, + gpointer target, + const gchar *target_property, + GBindingFlags flags); + +void nm_editor_bind_ip6_route_to_strings (gpointer source, + const gchar *source_property, + gpointer dest_target, + const gchar *dest_target_property, + gpointer next_hop_target, + const gchar *next_hop_target_property, + gpointer metric_target, + const gchar *metric_target_property, + GBindingFlags flags); + +void nm_editor_bind_wireless_security_method (NMConnection *connection, + NMSettingWirelessSecurity *s_wsec, + gpointer target, + const char *target_property, + GBindingFlags flags); +void nm_editor_bind_wireless_security_wep_key (NMSettingWirelessSecurity *s_wsec, + gpointer entry, + const char *entry_property, + gpointer key_selector, + const char *key_selector_property, + GBindingFlags flags); + +void nm_editor_bind_vlan_name (NMSettingVlan *s_vlan); + +G_END_DECLS + +#endif /* NM_EDITOR_BINDINGS_H */ diff --git a/tui/nm-editor-utils.c b/tui/nm-editor-utils.c new file mode 100644 index 0000000000..7989eac84b --- /dev/null +++ b/tui/nm-editor-utils.c @@ -0,0 +1,420 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2012, 2013 Red Hat, Inc. + */ + +/** + * SECTION:nm-editor-utils + * @short_description: Miscellaneous connection editor utilities + * + * nm-editor-utils contains helper functions for connection editors. + * The goal is that this should eventually be shared between nmtui, + * nm-connection-editor, and gnome-control-center. + */ + +#include "config.h" + +#include <glib/gi18n.h> +#include <nm-utils.h> + +#include <nm-device-bond.h> +#include <nm-device-bridge.h> +#include <nm-device-team.h> +#include <nm-device-ethernet.h> +#include <nm-device-infiniband.h> +#include <nm-device-team.h> +#include <nm-device-vlan.h> +#include <nm-device-wifi.h> + +#include "nm-editor-utils.h" +#if 0 +#include "vpn-helpers.h" + +static GSList *vpn_plugins; + +static gint +sort_vpn_plugins (gconstpointer a, gconstpointer b) +{ + NMVpnPluginUiInterface *aa = NM_VPN_PLUGIN_UI_INTERFACE (a); + NMVpnPluginUiInterface *bb = NM_VPN_PLUGIN_UI_INTERFACE (b); + char *aa_desc = NULL, *bb_desc = NULL; + int ret; + + g_object_get (aa, NM_VPN_PLUGIN_UI_INTERFACE_NAME, &aa_desc, NULL); + g_object_get (bb, NM_VPN_PLUGIN_UI_INTERFACE_NAME, &bb_desc, NULL); + + ret = g_strcmp0 (aa_desc, bb_desc); + + g_free (aa_desc); + g_free (bb_desc); + + return ret; +} +#endif + +static void +wifi_connection_setup_func (NMConnection *connection, + NMSettingConnection *s_con, + NMSetting *s_hw) +{ + g_object_set (G_OBJECT (s_hw), + NM_SETTING_WIRELESS_MODE, NM_SETTING_WIRELESS_MODE_INFRA, + NULL); +} + +static void +bond_connection_setup_func (NMConnection *connection, + NMSettingConnection *s_con, + NMSetting *s_hw) +{ + NMSettingBond *s_bond = NM_SETTING_BOND (s_hw); + const char **options, *def, *cur; + int i; + + options = nm_setting_bond_get_valid_options (s_bond); + for (i = 0; options[i]; i++) { + def = nm_setting_bond_get_option_default (s_bond, options[i]); + cur = nm_setting_bond_get_option_by_name (s_bond, options[i]); + if (g_strcmp0 (def, cur) != 0) + nm_setting_bond_add_option (s_bond, options[i], def); + } +} + +typedef void (*NMEditorNewConnectionSetupFunc) (NMConnection *connection, + NMSettingConnection *s_con, + NMSetting *s_hw); + +typedef struct { + NMEditorConnectionTypeData data; + + const char *id_format; + NMEditorNewConnectionSetupFunc connection_setup_func; + gboolean no_autoconnect; +} NMEditorConnectionTypeDataReal; + +static gint +sort_types (gconstpointer a, gconstpointer b) +{ + NMEditorConnectionTypeData *typea = *(NMEditorConnectionTypeData **)a; + NMEditorConnectionTypeData *typeb = *(NMEditorConnectionTypeData **)b; + + if (typea->virtual && !typeb->virtual) + return 1; + else if (typeb->virtual && !typea->virtual) + return -1; + + if (typea->setting_type == NM_TYPE_SETTING_VPN && + typeb->setting_type != NM_TYPE_SETTING_VPN) + return 1; + else if (typeb->setting_type == NM_TYPE_SETTING_VPN && + typea->setting_type != NM_TYPE_SETTING_VPN) + return -1; + + return g_utf8_collate (typea->name, typeb->name); +} + +/** + * nm_editor_utils_get_connection_type_list: + * + * Gets an array of information about supported connection types. The + * array is sorted in a standard presentation order (hardware types + * first, alphabetized, then virtual types, alphabetized, then VPN + * types, alphabetized). + * + * Returns: the array of connection type information + */ +NMEditorConnectionTypeData ** +nm_editor_utils_get_connection_type_list (void) +{ + GPtrArray *array; + NMEditorConnectionTypeDataReal *item; + static NMEditorConnectionTypeData **list; +#if 0 + GHashTable *vpn_plugins_hash; + gboolean have_vpn_plugins; +#endif + + if (list) + return list; + + array = g_ptr_array_new (); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Ethernet"); + item->data.setting_type = NM_TYPE_SETTING_WIRED; + item->data.device_type = NM_TYPE_DEVICE_ETHERNET; + item->data.virtual = FALSE; + item->id_format = _("Ethernet connection %d"); + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Wi-Fi"); + item->data.setting_type = NM_TYPE_SETTING_WIRELESS; + item->data.device_type = NM_TYPE_DEVICE_WIFI; + item->data.virtual = FALSE; + item->id_format = _("Wi-Fi connection %d"); + item->connection_setup_func = wifi_connection_setup_func; + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("InfiniBand"); + item->data.setting_type = NM_TYPE_SETTING_INFINIBAND; + item->data.device_type = NM_TYPE_DEVICE_INFINIBAND; + item->data.virtual = FALSE; + item->id_format = _("InfiniBand connection %d"); + g_ptr_array_add (array, item); + +#if 0 + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Mobile Broadband"); + item->data.setting_type = NM_TYPE_SETTING_GSM; + item->data.virtual = FALSE; + item->id_format = _("Mobile broadband connection %d"); + item->no_autoconnect = TRUE; + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("DSL"); + item->data.setting_type = NM_TYPE_SETTING_PPPOE; + item->data.device_type = NM_TYPE_DEVICE_ETHERNET; + item->data.virtual = FALSE; + item->id_format = _("DSL connection %d"); + item->no_autoconnect = TRUE; + g_ptr_array_add (array, item); +#endif + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Bond"); + item->data.setting_type = NM_TYPE_SETTING_BOND; + item->data.device_type = NM_TYPE_DEVICE_BOND; + item->data.virtual = TRUE; + item->id_format = _("Bond connection %d"); + item->connection_setup_func = bond_connection_setup_func; + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Bridge"); + item->data.setting_type = NM_TYPE_SETTING_BRIDGE; + item->data.slave_setting_type = NM_TYPE_SETTING_BRIDGE_PORT; + item->data.device_type = NM_TYPE_DEVICE_BRIDGE; + item->data.virtual = TRUE; + item->id_format = _("Bridge connection %d"); + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("Team"); + item->data.setting_type = NM_TYPE_SETTING_TEAM; + item->data.slave_setting_type = NM_TYPE_SETTING_TEAM_PORT; + item->data.device_type = NM_TYPE_DEVICE_TEAM; + item->data.virtual = TRUE; + item->id_format = _("Team connection %d"); + g_ptr_array_add (array, item); + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("VLAN"); + item->data.setting_type = NM_TYPE_SETTING_VLAN; + item->data.device_type = NM_TYPE_DEVICE_VLAN; + item->data.virtual = TRUE; + item->id_format = _("VLAN connection %d"); + g_ptr_array_add (array, item); + +#if 0 + /* Add "VPN" only if there are plugins */ + vpn_plugins_hash = vpn_get_plugins (NULL); + have_vpn_plugins = vpn_plugins_hash && g_hash_table_size (vpn_plugins_hash); + if (have_vpn_plugins) { + GHashTableIter iter; + gpointer name, plugin; + + item = g_new0 (NMEditorConnectionTypeDataReal, 1); + item->data.name = _("VPN"); + item->data.setting_type = NM_TYPE_SETTING_VPN; + item->data.virtual = TRUE; + item->id_format = _("VPN connection %d"); + item->no_autoconnect = TRUE; + g_ptr_array_add (array, item); + + vpn_plugins = NULL; + g_hash_table_iter_init (&iter, vpn_plugins_hash); + while (g_hash_table_iter_next (&iter, &name, &plugin)) + vpn_plugins = g_slist_prepend (vpn_plugins, plugin); + vpn_plugins = g_slist_sort (vpn_plugins, sort_vpn_plugins); + } +#endif + + g_ptr_array_sort (array, sort_types); + g_ptr_array_add (array, NULL); + + list = (NMEditorConnectionTypeData **)g_ptr_array_free (array, FALSE); + return list; +} + +static char * +get_available_connection_name (const char *format, + NMRemoteSettings *settings) +{ + GSList *connections, *iter, *names = NULL; + char *cname = NULL; + int i = 0; + + connections = nm_remote_settings_list_connections (settings); + for (iter = connections; iter; iter = iter->next) { + const char *id; + + id = nm_connection_get_id (NM_CONNECTION (iter->data)); + g_assert (id); + names = g_slist_append (names, (gpointer) id); + } + g_slist_free (connections); + + /* Find the next available unique connection name */ + while (!cname && (i++ < 10000)) { + char *temp; + gboolean found = FALSE; + + temp = g_strdup_printf (format, i); + for (iter = names; iter; iter = g_slist_next (iter)) { + if (!strcmp (iter->data, temp)) { + found = TRUE; + break; + } + } + if (!found) + cname = temp; + else + g_free (temp); + } + + g_slist_free (names); + return cname; +} + +/** + * nm_editor_utils_create_connection: + * @type: the type of the connection's primary #NMSetting + * @master: (allow-none): the connection's master, if any + * @settings: an #NMRemoteSettings + * + * Creates a new #NMConnection of the given type, automatically + * creating a UUID and an appropriate not-currently-in-use connection + * name, setting #NMSettingConnection:autoconnect appropriately for + * the connection type, filling in slave-related information if + * @master is not %NULL, and initializing any other mandatory-to-set + * properties to reasonable initial values. + * + * Returns: a new #NMConnection + */ +NMConnection * +nm_editor_utils_create_connection (GType type, + NMConnection *master, + NMRemoteSettings *settings) +{ + NMEditorConnectionTypeData **types; + NMEditorConnectionTypeDataReal *type_data = NULL; + const char *master_setting_type = NULL, *master_uuid = NULL; + GType master_type = G_TYPE_INVALID, slave_setting_type = G_TYPE_INVALID; + NMConnection *connection; + NMSettingConnection *s_con; + NMSetting *s_hw, *s_slave; + char *uuid, *id; + int i; + + if (master) { + NMSettingConnection *master_s_con; + + master_s_con = nm_connection_get_setting_connection (master); + master_setting_type = nm_setting_connection_get_connection_type (master_s_con); + master_uuid = nm_setting_connection_get_uuid (master_s_con); + master_type = nm_connection_lookup_setting_type (master_setting_type); + } + + types = nm_editor_utils_get_connection_type_list (); + for (i = 0; types[i]; i++) { + if (types[i]->setting_type == type) + type_data = (NMEditorConnectionTypeDataReal *)types[i]; + if (types[i]->setting_type == master_type) + slave_setting_type = types[i]->slave_setting_type; + + } + g_return_val_if_fail (type_data != NULL, NULL); + + connection = nm_connection_new (); + + s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); + nm_connection_add_setting (connection, NM_SETTING (s_con)); + + s_hw = g_object_new (type, NULL); + nm_connection_add_setting (connection, s_hw); + + if (slave_setting_type != G_TYPE_INVALID) { + s_slave = g_object_new (slave_setting_type, NULL); + nm_connection_add_setting (connection, s_slave); + } + + uuid = nm_utils_uuid_generate (); + id = get_available_connection_name (type_data->id_format, settings); + + g_object_set (s_con, + NM_SETTING_CONNECTION_UUID, uuid, + NM_SETTING_CONNECTION_ID, id, + NM_SETTING_CONNECTION_TYPE, nm_setting_get_name (s_hw), + NM_SETTING_CONNECTION_AUTOCONNECT, !type_data->no_autoconnect, + NM_SETTING_CONNECTION_MASTER, master_uuid, + NM_SETTING_CONNECTION_SLAVE_TYPE, master_setting_type, + NULL); + + g_free (uuid); + g_free (id); + + if (type_data->connection_setup_func) + type_data->connection_setup_func (connection, s_con, s_hw); + + return connection; +} + +/** + * nm_editor_utils_get_connection_type_data: + * @conn: an #NMConnection + * + * Gets the #NMEditorConnectionTypeData corresponding to + * @conn's connection type. + * + * Returns: the #NMEditorConnectionTypeData + */ +NMEditorConnectionTypeData * +nm_editor_utils_get_connection_type_data (NMConnection *conn) +{ + NMSettingConnection *s_con; + const char *conn_type; + GType conn_gtype; + NMEditorConnectionTypeData **types; + int i; + + s_con = nm_connection_get_setting_connection (conn); + g_return_val_if_fail (s_con != NULL, NULL); + + conn_type = nm_setting_connection_get_connection_type (s_con); + conn_gtype = nm_connection_lookup_setting_type (conn_type); + g_return_val_if_fail (conn_gtype != G_TYPE_INVALID, NULL); + + types = nm_editor_utils_get_connection_type_list (); + for (i = 0; types[i]; i++) { + if (types[i]->setting_type == conn_gtype) + return types[i]; + } + + return NULL; +} diff --git a/tui/nm-editor-utils.h b/tui/nm-editor-utils.h new file mode 100644 index 0000000000..4b9ad5edbb --- /dev/null +++ b/tui/nm-editor-utils.h @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2012, 2013 Red Hat, Inc. + */ + +#ifndef NM_EDITOR_UTILS_H +#define NM_EDITOR_UTILS_H + +#include <glib-object.h> +#include <nm-remote-settings.h> + +G_BEGIN_DECLS + +typedef struct { + const char *name; + GType setting_type; + GType slave_setting_type; + GType device_type; + gboolean virtual; +} NMEditorConnectionTypeData; + +NMEditorConnectionTypeData **nm_editor_utils_get_connection_type_list (void); +NMEditorConnectionTypeData *nm_editor_utils_get_connection_type_data (NMConnection *conn); + +NMConnection *nm_editor_utils_create_connection (GType type, + NMConnection *master, + NMRemoteSettings *settings); + +G_END_DECLS + +#endif /* NM_EDITOR_UTILS_H */ diff --git a/tui/nm-gvaluearray-compat.h b/tui/nm-gvaluearray-compat.h new file mode 100644 index 0000000000..017d336af5 --- /dev/null +++ b/tui/nm-gvaluearray-compat.h @@ -0,0 +1,101 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NM_GVALUEARRAY_COMPAT_H +#define NM_GVALUEARRAY_COMPAT_H + +#include <glib.h> + +#define g_value_array_get_type() \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_get_type (); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#define g_value_array_get_nth(value_array, index_) \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_get_nth (value_array, index_); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#define g_value_array_new(n_prealloced) \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_new (n_prealloced); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#define g_value_array_free(value_array) \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_free (value_array); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#define g_value_array_copy(value_array) \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_copy (value_array); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#define g_value_array_prepend(value_array, value) \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_prepend (value_array, value); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#define g_value_array_append(value_array, value) \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_append (value_array, value); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#define g_value_array_insert(value_array, index_, value) \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_insert (value_array, index_, value); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#define g_value_array_remove(value_array, index_) \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_remove (value_array, index_); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#define g_value_array_sort(value_array, compare_func) \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_sort (value_array, compare_func); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#define g_value_array_sort_with_data(value_array, compare_func, user_data) \ + G_GNUC_EXTENSION ({ \ + G_GNUC_BEGIN_IGNORE_DEPRECATIONS \ + g_value_array_sort_with_data (value_array, compare_func, user_data); \ + G_GNUC_END_IGNORE_DEPRECATIONS \ + }) + +#endif /* NM_GVALUEARRAY_COMPAT_H */ diff --git a/tui/nm-ui-utils.c b/tui/nm-ui-utils.c new file mode 100644 index 0000000000..3609c91ab0 --- /dev/null +++ b/tui/nm-ui-utils.c @@ -0,0 +1,587 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager Applet -- allow user control over networking + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2007 - 2012 Red Hat, Inc. + */ + +/** + * SECTION:nm-ui-utils + * @short_description: Applet/Connection editor utilities + * + * This is stolen directly from libnm-gtk and should probably + * eventually migrate into libnm-glib. FIXME. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> + +#include <glib/gi18n.h> +#include <gudev/gudev.h> + +#include <nm-device.h> + +#include "nm-ui-utils.h" + +static char *ignored_words[] = { + "Semiconductor", + "Components", + "Corporation", + "Communications", + "Company", + "Corp.", + "Corp", + "Co.", + "Inc.", + "Inc", + "Incorporated", + "Ltd.", + "Limited.", + "Intel?", + "chipset", + "adapter", + "[hex]", + "NDIS", + "Module", + NULL +}; + +static char *ignored_phrases[] = { + "Multiprotocol MAC/baseband processor", + "Wireless LAN Controller", + "Wireless LAN Adapter", + "Wireless Adapter", + "Network Connection", + "Wireless Cardbus Adapter", + "Wireless CardBus Adapter", + "54 Mbps Wireless PC Card", + "Wireless PC Card", + "Wireless PC", + "PC Card with XJACK(r) Antenna", + "Wireless cardbus", + "Wireless LAN PC Card", + "Technology Group Ltd.", + "Communication S.p.A.", + "Business Mobile Networks BV", + "Mobile Broadband Minicard Composite Device", + "Mobile Communications AB", + "(PC-Suite Mode)", + NULL +}; + +static char * +fixup_desc_string (const char *desc) +{ + char *p, *temp; + char **words, **item; + GString *str; + + p = temp = g_strdup (desc); + while (*p) { + if (*p == '_' || *p == ',') + *p = ' '; + p++; + } + + /* Attempt to shorten ID by ignoring certain phrases */ + for (item = ignored_phrases; *item; item++) { + guint32 ignored_len = strlen (*item); + + p = strstr (temp, *item); + if (p) + memmove (p, p + ignored_len, strlen (p + ignored_len) + 1); /* +1 for the \0 */ + } + + /* Attmept to shorten ID by ignoring certain individual words */ + words = g_strsplit (temp, " ", 0); + str = g_string_new_len (NULL, strlen (temp)); + g_free (temp); + + for (item = words; *item; item++) { + int i = 0; + gboolean ignore = FALSE; + + if (g_ascii_isspace (**item) || (**item == '\0')) + continue; + + while (ignored_words[i] && !ignore) { + if (!strcmp (*item, ignored_words[i])) + ignore = TRUE; + i++; + } + + if (!ignore) { + if (str->len) + g_string_append_c (str, ' '); + g_string_append (str, *item); + } + } + g_strfreev (words); + + temp = str->str; + g_string_free (str, FALSE); + + return temp; +} + +#define VENDOR_TAG "nma_utils_get_device_vendor" +#define PRODUCT_TAG "nma_utils_get_device_product" +#define DESCRIPTION_TAG "nma_utils_get_device_description" + +static void +get_description (NMDevice *device) +{ + char *description = NULL; + const char *dev_product; + const char *dev_vendor; + char *product, *pdown; + char *vendor, *vdown; + GString *str; + + dev_product = nm_device_get_product (device); + dev_vendor = nm_device_get_vendor (device); + if (!dev_product || !dev_vendor) { + g_object_set_data (G_OBJECT (device), + DESCRIPTION_TAG, + (char *) nm_device_get_iface (device)); + return; + } + + product = fixup_desc_string (dev_product); + vendor = fixup_desc_string (dev_vendor); + + str = g_string_new_len (NULL, strlen (vendor) + strlen (product) + 1); + + /* Another quick hack; if all of the fixed up vendor string + * is found in product, ignore the vendor. + */ + pdown = g_ascii_strdown (product, -1); + vdown = g_ascii_strdown (vendor, -1); + if (!strstr (pdown, vdown)) { + g_string_append (str, vendor); + g_string_append_c (str, ' '); + } + g_free (pdown); + g_free (vdown); + + g_string_append (str, product); + + description = g_string_free (str, FALSE); + + g_object_set_data_full (G_OBJECT (device), + VENDOR_TAG, vendor, + (GDestroyNotify) g_free); + g_object_set_data_full (G_OBJECT (device), + PRODUCT_TAG, product, + (GDestroyNotify) g_free); + g_object_set_data_full (G_OBJECT (device), + DESCRIPTION_TAG, description, + (GDestroyNotify) g_free); +} + +/** + * nma_utils_get_device_vendor: + * @device: an #NMDevice + * + * Gets a cleaned-up version of #NMDevice:vendor for @device. This + * removes strings like "Inc." that would just take up unnecessary + * space in the UI. + * + * Returns: a cleaned-up vendor string, or %NULL if the vendor is + * not known + */ +const char * +nma_utils_get_device_vendor (NMDevice *device) +{ + const char *vendor; + + g_return_val_if_fail (device != NULL, NULL); + + vendor = g_object_get_data (G_OBJECT (device), VENDOR_TAG); + if (!vendor) { + get_description (device); + vendor = g_object_get_data (G_OBJECT (device), VENDOR_TAG); + } + + return vendor; +} + +/** + * nma_utils_get_device_product: + * @device: an #NMDevice + * + * Gets a cleaned-up version of #NMDevice:product for @device. This + * removes strings like "Wireless LAN Adapter" that would just take up + * unnecessary space in the UI. + * + * Returns: a cleaned-up product string, or %NULL if the product name + * is not known + */ +const char * +nma_utils_get_device_product (NMDevice *device) +{ + const char *product; + + g_return_val_if_fail (device != NULL, NULL); + + product = g_object_get_data (G_OBJECT (device), PRODUCT_TAG); + if (!product) { + get_description (device); + product = g_object_get_data (G_OBJECT (device), PRODUCT_TAG); + } + + return product; +} + +/** + * nma_utils_get_device_description: + * @device: an #NMDevice + * + * Gets a description of @device, incorporating the results of + * nma_utils_get_device_vendor() and + * nma_utils_get_device_product(). + * + * Returns: a description of @device. If either the vendor or the + * product name is unknown, this returns the interface name. + */ +const char * +nma_utils_get_device_description (NMDevice *device) +{ + const char *description; + + g_return_val_if_fail (device != NULL, NULL); + + description = g_object_get_data (G_OBJECT (device), DESCRIPTION_TAG); + if (!description) { + get_description (device); + description = g_object_get_data (G_OBJECT (device), DESCRIPTION_TAG); + } + + return description; +} + +static gboolean +find_duplicates (char **names, + gboolean *duplicates, + int num_devices) +{ + int i, j; + gboolean found_any = FALSE; + + memset (duplicates, 0, num_devices * sizeof (gboolean)); + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) + continue; + for (j = i + 1; j < num_devices; j++) { + if (duplicates[j]) + continue; + if (!strcmp (names[i], names[j])) + duplicates[i] = duplicates[j] = found_any = TRUE; + } + } + + return found_any; +} + +/** + * nma_utils_get_device_generic_type_name: + * @device: an #NMDevice + * + * Gets a "generic" name for the type of @device. + * + * Returns: @device's generic type name + */ +const char * +nma_utils_get_device_generic_type_name (NMDevice *device) +{ + switch (nm_device_get_device_type (device)) { + case NM_DEVICE_TYPE_ETHERNET: + case NM_DEVICE_TYPE_INFINIBAND: + return _("Wired"); + default: + return nma_utils_get_device_type_name (device); + } +} + +/** + * nma_utils_get_device_type_name: + * @device: an #NMDevice + * + * Gets a specific name for the type of @device. + * + * Returns: @device's generic type name + */ +const char * +nma_utils_get_device_type_name (NMDevice *device) +{ + switch (nm_device_get_device_type (device)) { + case NM_DEVICE_TYPE_ETHERNET: + return _("Ethernet"); + case NM_DEVICE_TYPE_WIFI: + return _("Wi-Fi"); + case NM_DEVICE_TYPE_BT: + return _("Bluetooth"); + case NM_DEVICE_TYPE_OLPC_MESH: + return _("OLPC Mesh"); + case NM_DEVICE_TYPE_WIMAX: + return _("WiMAX"); + case NM_DEVICE_TYPE_MODEM: + return _("Mobile Broadband"); + case NM_DEVICE_TYPE_INFINIBAND: + return _("InfiniBand"); + case NM_DEVICE_TYPE_BOND: + return _("Bond"); + case NM_DEVICE_TYPE_TEAM: + return _("Team"); + case NM_DEVICE_TYPE_BRIDGE: + return _("Bridge"); + case NM_DEVICE_TYPE_VLAN: + return _("VLAN"); + case NM_DEVICE_TYPE_ADSL: + return _("ADSL"); + default: + return _("Unknown"); + } +} + +static char * +get_device_type_name_with_iface (NMDevice *device) +{ + const char *type_name = nma_utils_get_device_type_name (device); + + switch (nm_device_get_device_type (device)) { + case NM_DEVICE_TYPE_BOND: + case NM_DEVICE_TYPE_TEAM: + case NM_DEVICE_TYPE_BRIDGE: + case NM_DEVICE_TYPE_VLAN: + return g_strdup_printf ("%s (%s)", type_name, nm_device_get_iface (device)); + default: + return g_strdup (type_name); + } +} + +static char * +get_device_generic_type_name_with_iface (NMDevice *device) +{ + switch (nm_device_get_device_type (device)) { + case NM_DEVICE_TYPE_ETHERNET: + case NM_DEVICE_TYPE_INFINIBAND: + return g_strdup (_("Wired")); + default: + return get_device_type_name_with_iface (device); + } +} + +#define BUS_TAG "nm-ui-utils.c:get_bus_name" + +static const char * +get_bus_name (GUdevClient *uclient, NMDevice *device) +{ + GUdevDevice *udevice; + const char *ifname, *bus; + char *display_bus; + + bus = g_object_get_data (G_OBJECT (device), BUS_TAG); + if (bus) { + if (*bus) + return bus; + else + return NULL; + } + + ifname = nm_device_get_iface (device); + if (!ifname) + return NULL; + + udevice = g_udev_client_query_by_subsystem_and_name (uclient, "net", ifname); + if (!udevice) + udevice = g_udev_client_query_by_subsystem_and_name (uclient, "tty", ifname); + if (!udevice) + return NULL; + + bus = g_udev_device_get_property (udevice, "ID_BUS"); + if (!g_strcmp0 (bus, "pci")) + display_bus = g_strdup (_("PCI")); + else if (!g_strcmp0 (bus, "usb")) + display_bus = g_strdup (_("USB")); + else { + /* Use "" instead of NULL so we can tell later that we've + * already tried. + */ + display_bus = g_strdup (""); + } + + g_object_set_data_full (G_OBJECT (device), + BUS_TAG, display_bus, + (GDestroyNotify) g_free); + if (*display_bus) + return display_bus; + else + return NULL; +} + +/** + * nma_utils_disambiguate_device_names: + * @devices: (array length=num_devices): a set of #NMDevice + * @num_devices: length of @devices + * + * Generates a list of short-ish unique presentation names for the + * devices in @devices. + * + * Returns: (transfer full) (array zero-terminated=1): the device names + */ +char ** +nma_utils_disambiguate_device_names (NMDevice **devices, + int num_devices) +{ + static const char *subsys[3] = { "net", "tty", NULL }; + GUdevClient *uclient; + char **names; + gboolean *duplicates; + int i; + + names = g_new (char *, num_devices + 1); + duplicates = g_new (gboolean, num_devices); + + /* Generic device name */ + for (i = 0; i < num_devices; i++) + names[i] = get_device_generic_type_name_with_iface (devices[i]); + if (!find_duplicates (names, duplicates, num_devices)) + goto done; + + /* Try specific names (eg, "Ethernet" and "InfiniBand" rather + * than "Wired") + */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + g_free (names[i]); + names[i] = get_device_type_name_with_iface (devices[i]); + } + } + if (!find_duplicates (names, duplicates, num_devices)) + goto done; + + /* Try prefixing bus name (eg, "PCI Ethernet" vs "USB Ethernet") */ + uclient = g_udev_client_new (subsys); + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + const char *bus = get_bus_name (uclient, devices[i]); + char *name; + + if (!bus) + continue; + + g_free (names[i]); + name = get_device_type_name_with_iface (devices[i]); + /* Translators: the first %s is a bus name (eg, "USB") or + * product name, the second is a device type (eg, + * "Ethernet"). You can change this to something like + * "%2$s (%1$s)" if there's no grammatical way to combine + * the strings otherwise. + */ + names[i] = g_strdup_printf (C_("long device name", "%s %s"), + bus, name); + g_free (name); + } + } + g_object_unref (uclient); + if (!find_duplicates (names, duplicates, num_devices)) + goto done; + + /* Try prefixing vendor name */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + const char *vendor = nma_utils_get_device_vendor (devices[i]); + char *name; + + if (!vendor) + continue; + + g_free (names[i]); + name = get_device_type_name_with_iface (devices[i]); + names[i] = g_strdup_printf (C_("long device name", "%s %s"), + vendor, + nma_utils_get_device_type_name (devices[i])); + g_free (name); + } + } + if (!find_duplicates (names, duplicates, num_devices)) + goto done; + + /* We have multiple identical network cards, so we have to differentiate + * them by interface name. + */ + for (i = 0; i < num_devices; i++) { + if (duplicates[i]) { + const char *interface = nm_device_get_iface (devices[i]); + + if (!interface) + continue; + + g_free (names[i]); + names[i] = g_strdup_printf ("%s (%s)", + nma_utils_get_device_type_name (devices[i]), + interface); + } + } + + done: + g_free (duplicates); + names[num_devices] = NULL; + return names; +} + +/** + * nma_utils_get_connection_device_name: + * @connection: an #NMConnection for a virtual device type + * + * Returns the name that nma_utils_disambiguate_device_names() would + * return for the virtual device that would be created for @connection. + * Eg, "VLAN (eth1.1)". + * + * Returns: (transfer full): the name of @connection's device + */ +char * +nma_utils_get_connection_device_name (NMConnection *connection) +{ + const char *iface, *type, *display_type; + NMSettingConnection *s_con; + + iface = nm_connection_get_virtual_iface_name (connection); + g_return_val_if_fail (iface != NULL, NULL); + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con != NULL, NULL); + type = nm_setting_connection_get_connection_type (s_con); + + if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME)) + display_type = _("Bond"); + else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME)) + display_type = _("Team"); + else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME)) + display_type = _("Bridge"); + else if (!strcmp (type, NM_SETTING_VLAN_SETTING_NAME)) + display_type = _("VLAN"); + else { + g_warning ("Unrecognized virtual device type '%s'", type); + display_type = type; + } + + return g_strdup_printf ("%s (%s)", display_type, iface); +} diff --git a/tui/nm-ui-utils.h b/tui/nm-ui-utils.h new file mode 100644 index 0000000000..693df441c8 --- /dev/null +++ b/tui/nm-ui-utils.h @@ -0,0 +1,41 @@ +/* NetworkManager Wireless Applet -- Display wireless access points and allow user control + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + * (C) Copyright 2007 - 2012 Red Hat, Inc. + */ + + +/* WARNING: this file is private API between nm-applet and various GNOME + * bits; it may change without notice and is not guaranteed to be stable. + */ + +#ifndef NMA_UI_UTILS_H +#define NMA_UI_UTILS_H + +#include <nm-device.h> + +const char *nma_utils_get_device_vendor (NMDevice *device); +const char *nma_utils_get_device_product (NMDevice *device); +const char *nma_utils_get_device_description (NMDevice *device); +const char *nma_utils_get_device_generic_type_name (NMDevice *device); +const char *nma_utils_get_device_type_name (NMDevice *device); + +char **nma_utils_disambiguate_device_names (NMDevice **devices, + int num_devices); +char *nma_utils_get_connection_device_name (NMConnection *connection); + +#endif /* NMA_UI_UTILS_H */ + diff --git a/tui/nmt-address-list.c b/tui/nmt-address-list.c new file mode 100644 index 0000000000..da301a2fda --- /dev/null +++ b/tui/nmt-address-list.c @@ -0,0 +1,284 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-address-list + * @short_description: An editable list of IP addresses or hostnames + * + * #NmtAddressList is a subclass of #NmtWidgetList that contains + * entries displaying IP addresses, address/prefix strings, or + * hostnames. This is designed for binding its #NmtAddressList:strings + * property to an appropriate #NMSettingIP4Config or + * #NMSettingIP6Config property via one of the nm-editor-bindings + * functions. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> + +#include <dbus/dbus-glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-address-list.h" +#include "nmt-ip-entry.h" + +G_DEFINE_TYPE (NmtAddressList, nmt_address_list, NMT_TYPE_WIDGET_LIST) + +#define NMT_ADDRESS_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ADDRESS_LIST, NmtAddressListPrivate)) + +typedef struct { + NmtAddressListType list_type; + char **strings; +} NmtAddressListPrivate; + +enum { + PROP_0, + PROP_LIST_TYPE, + PROP_STRINGS, + + LAST_PROP +}; + +/** + * NmtAddressListType: + * @NMT_ADDRESS_LIST_IP4_WITH_PREFIX: IPv4 address/prefix strings + * @NMT_ADDRESS_LIST_IP4: IPv4 addresses + * @NMT_ADDRESS_LIST_IP6_WITH_PREFIX: IPv6 address/prefix strings + * @NMT_ADDRESS_LIST_IP6: IPv6 addresses + * @NMT_ADDRESS_LIST_HOSTNAME: hostnames + * + * The type of address in an #NmtAddressList + */ + +/** + * nmt_address_list_new: + * @list_type: the type of address the list will contain + * + * Creates a new #NmtAddressList + * + * Returns: a new #NmtAddressList + */ +NmtNewtWidget * +nmt_address_list_new (NmtAddressListType list_type) +{ + return g_object_new (NMT_TYPE_ADDRESS_LIST, + "list-type", list_type, + NULL); +} + +static void +nmt_address_list_init (NmtAddressList *list) +{ +} + +static gboolean +strings_transform_to_entry (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + int n = GPOINTER_TO_INT (user_data); + char **strings; + + strings = g_value_get_boxed (source_value); + if (n >= g_strv_length (strings)) + return FALSE; + + g_value_set_string (target_value, strings[n]); + return TRUE; +} + +static gboolean +strings_transform_from_entry (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NmtAddressList *list = NMT_ADDRESS_LIST (g_binding_get_source (binding)); + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list); + int n = GPOINTER_TO_INT (user_data); + + if (n >= g_strv_length (priv->strings)) + return FALSE; + + g_free (priv->strings[n]); + priv->strings[n] = g_value_dup_string (source_value); + + g_value_set_boxed (target_value, priv->strings); + return TRUE; +} + +static gboolean +hostname_filter (NmtNewtEntry *entry, + const char *text, + int ch, + int position, + gpointer user_data) +{ + return g_ascii_isalnum (ch) || ch == '.' || ch == '-'; +} + +static NmtNewtWidget * +nmt_address_list_create_widget (NmtWidgetList *list, + int num) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list); + NmtNewtWidget *entry; + + if (priv->list_type == NMT_ADDRESS_LIST_IP4_WITH_PREFIX) + entry = nmt_ip_entry_new (25, AF_INET, TRUE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_IP4) + entry = nmt_ip_entry_new (25, AF_INET, FALSE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_IP6_WITH_PREFIX) + entry = nmt_ip_entry_new (25, AF_INET6, TRUE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_IP6) + entry = nmt_ip_entry_new (25, AF_INET6, FALSE, FALSE); + else if (priv->list_type == NMT_ADDRESS_LIST_HOSTNAME) { + entry = nmt_newt_entry_new (25, NMT_NEWT_ENTRY_NONEMPTY); + nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), hostname_filter, list); + } else + g_assert_not_reached (); + + g_object_bind_property_full (list, "strings", entry, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + strings_transform_to_entry, + strings_transform_from_entry, + GINT_TO_POINTER (num), NULL); + + return entry; +} + +static void +nmt_address_list_add_clicked (NmtWidgetList *list) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list); + int len; + + len = g_strv_length (priv->strings); + priv->strings = g_renew (char *, priv->strings, len + 2); + priv->strings[len] = g_strdup (""); + priv->strings[len + 1] = NULL; + + nmt_widget_list_set_length (list, len + 1); + g_object_notify (G_OBJECT (list), "strings"); +} + +static void +nmt_address_list_remove_clicked (NmtWidgetList *list, + int num) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (list); + int len; + + len = g_strv_length (priv->strings); + g_free (priv->strings[num]); + memmove (priv->strings + num, priv->strings + num + 1, (len - num) * sizeof (char *)); + + nmt_widget_list_set_length (list, len - 1); + g_object_notify (G_OBJECT (list), "strings"); +} + +static void +nmt_address_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LIST_TYPE: + priv->list_type = g_value_get_uint (value); + break; + case PROP_STRINGS: + g_strfreev (priv->strings); + priv->strings = g_value_dup_boxed (value); + if (!priv->strings) + priv->strings = g_new0 (char *, 1); + nmt_widget_list_set_length (NMT_WIDGET_LIST (object), + g_strv_length (priv->strings)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_address_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtAddressListPrivate *priv = NMT_ADDRESS_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LIST_TYPE: + g_value_set_uint (value, priv->list_type); + break; + case PROP_STRINGS: + g_value_set_boxed (value, priv->strings); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_address_list_class_init (NmtAddressListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (list_class); + NmtWidgetListClass *widget_list_class = NMT_WIDGET_LIST_CLASS (list_class); + + g_type_class_add_private (list_class, sizeof (NmtAddressListPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_address_list_set_property; + object_class->get_property = nmt_address_list_get_property; + + widget_list_class->create_widget = nmt_address_list_create_widget; + widget_list_class->add_clicked = nmt_address_list_add_clicked; + widget_list_class->remove_clicked = nmt_address_list_remove_clicked; + + /** + * NmtAddressList:list-type: + * + * The type of address the list holds. + */ + g_object_class_install_property (object_class, PROP_LIST_TYPE, + g_param_spec_uint ("list-type", "", "", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtAddressList:strings: + * + * The strings in the list's entries. + */ + g_object_class_install_property (object_class, PROP_STRINGS, + g_param_spec_boxed ("strings", "", "", + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-address-list.h b/tui/nmt-address-list.h new file mode 100644 index 0000000000..df7a4f7962 --- /dev/null +++ b/tui/nmt-address-list.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_ADDRESS_LIST_H +#define NMT_ADDRESS_LIST_H + +#include "nmt-widget-list.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_ADDRESS_LIST (nmt_address_list_get_type ()) +#define NMT_ADDRESS_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressList)) +#define NMT_ADDRESS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass)) +#define NMT_IS_ADDRESS_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ADDRESS_LIST)) +#define NMT_IS_ADDRESS_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ADDRESS_LIST)) +#define NMT_ADDRESS_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ADDRESS_LIST, NmtAddressListClass)) + +typedef struct { + NmtWidgetList parent; + +} NmtAddressList; + +typedef struct { + NmtWidgetListClass parent; + +} NmtAddressListClass; + +GType nmt_address_list_get_type (void); + +typedef enum { + NMT_ADDRESS_LIST_IP4_WITH_PREFIX, + NMT_ADDRESS_LIST_IP4, + NMT_ADDRESS_LIST_IP6_WITH_PREFIX, + NMT_ADDRESS_LIST_IP6, + NMT_ADDRESS_LIST_HOSTNAME +} NmtAddressListType; + +NmtNewtWidget *nmt_address_list_new (NmtAddressListType list_type); + +G_END_DECLS + +#endif /* NMT_ADDRESS_LIST_H */ diff --git a/tui/nmt-connect-connection-list.c b/tui/nmt-connect-connection-list.c new file mode 100644 index 0000000000..12d16da54e --- /dev/null +++ b/tui/nmt-connect-connection-list.c @@ -0,0 +1,820 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-connect-connection-list + * @short_description: Connection list for "nmtui connect" + * + * #NmtConnectConnectionList is the list of devices, connections, and + * access points displayed by "nmtui connect". + */ + +#include "config.h" + +#include <stdlib.h> +#include <glib/gi18n-lib.h> + +#include <nm-access-point.h> +#include <nm-device-wifi.h> +#include <nm-utils.h> + +#include "nmtui.h" +#include "nmt-connect-connection-list.h" +#include "nmt-utils.h" +#include "nmt-password-dialog.h" +#include "nmt-secret-agent.h" +#include "nm-ui-utils.h" + +G_DEFINE_TYPE (NmtConnectConnectionList, nmt_connect_connection_list, NMT_TYPE_NEWT_LISTBOX) + +#define NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListPrivate)) + +typedef struct { + char *name; + NMDevice *device; + + int sort_order; + + GSList *conns; +} NmtConnectDevice; + +typedef struct { + const char *name; + char *ssid; + + NMConnection *conn; + NMAccessPoint *ap; + NMDevice *device; +} NmtConnectConnection; + +typedef struct { + GSList *nmt_devices; +} NmtConnectConnectionListPrivate; + +/** + * nmt_connect_connection_list_new: + * + * Creates a new #NmtConnectConnectionList + * + * Returns: a new #NmtConnectConnectionList + */ +NmtNewtWidget * +nmt_connect_connection_list_new (void) +{ + return g_object_new (NMT_TYPE_CONNECT_CONNECTION_LIST, + "flags", NMT_NEWT_LISTBOX_SCROLL | NMT_NEWT_LISTBOX_BORDER, + "skip-null-keys", TRUE, + NULL); +} + +static void +nmt_connect_connection_list_init (NmtConnectConnectionList *list) +{ +} + +static void +nmt_connect_connection_free (NmtConnectConnection *nmtconn) +{ + g_clear_object (&nmtconn->conn); + g_clear_object (&nmtconn->ap); + g_free (nmtconn->ssid); +} + +static void +nmt_connect_device_free (NmtConnectDevice *nmtdev) +{ + g_clear_pointer (&nmtdev->name, g_free); + g_clear_object (&nmtdev->device); + + g_slist_free_full (nmtdev->conns, (GDestroyNotify) nmt_connect_connection_free); +} + +static const char *device_sort_order[] = { + "NMDeviceEthernet", + "NMDeviceInfiniband", + "NMDeviceWifi", + NM_SETTING_VLAN_SETTING_NAME, + NM_SETTING_BOND_SETTING_NAME, + NM_SETTING_TEAM_SETTING_NAME, + NM_SETTING_BRIDGE_SETTING_NAME, + "NMDeviceModem", + "NMDeviceBt" +}; +static const int device_sort_order_len = G_N_ELEMENTS (device_sort_order); + +static int +get_sort_order_for_device (NMDevice *device) +{ + const char *type; + int i; + + type = G_OBJECT_TYPE_NAME (device); + for (i = 0; i < device_sort_order_len; i++) { + if (!strcmp (type, device_sort_order[i])) + return i; + } + + return -1; +} + +static int +get_sort_order_for_connection (NMConnection *conn) +{ + NMSettingConnection *s_con; + const char *type; + int i; + + s_con = nm_connection_get_setting_connection (conn); + type = nm_setting_connection_get_connection_type (s_con); + + for (i = 0; i < device_sort_order_len; i++) { + if (!strcmp (type, device_sort_order[i])) + return i; + } + + return -1; +} + +static int +sort_connections (gconstpointer a, + gconstpointer b) +{ + NmtConnectConnection *nmta = (NmtConnectConnection *)a; + NmtConnectConnection *nmtb = (NmtConnectConnection *)b; + + /* If nmta and nmtb both have NMConnections, sort them by timestamp */ + if (nmta->conn && nmtb->conn) { + NMSettingConnection *s_con_a, *s_con_b; + guint64 time_a, time_b; + + s_con_a = nm_connection_get_setting_connection (nmta->conn); + s_con_b = nm_connection_get_setting_connection (nmtb->conn); + + time_a = nm_setting_connection_get_timestamp (s_con_a); + time_b = nm_setting_connection_get_timestamp (s_con_b); + + return (int) (time_b - time_a); + } + + /* If one is an NMConnection and the other is an NMAccessPoint, the + * connection comes first. + */ + if (nmta->conn) + return -1; + else if (nmtb->conn) + return 1; + + g_return_val_if_fail (nmta->ap && nmtb->ap, 0); + + /* If both are access points, then sort by strength */ + return nm_access_point_get_strength (nmtb->ap) - nm_access_point_get_strength (nmta->ap); +} + +static void +add_connections_for_device (NmtConnectDevice *nmtdev, + GSList *connections) +{ + GSList *iter; + + for (iter = connections; iter; iter = iter->next) { + NMConnection *conn = iter->data; + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (conn); + if (nm_setting_connection_get_master (s_con)) + continue; + + if (nm_device_connection_valid (nmtdev->device, conn)) { + NmtConnectConnection *nmtconn = g_slice_new0 (NmtConnectConnection); + + nmtconn->name = nm_connection_get_id (conn); + nmtconn->device = nmtdev->device; + nmtconn->conn = g_object_ref (conn); + nmtdev->conns = g_slist_prepend (nmtdev->conns, nmtconn); + } + } +} + +static void +add_connections_for_aps (NmtConnectDevice *nmtdev, + GSList *connections) +{ + NmtConnectConnection *nmtconn; + NMConnection *conn; + NMAccessPoint *ap; + const GPtrArray *aps; + GSList *iter; + int i; + + aps = nm_device_wifi_get_access_points (NM_DEVICE_WIFI (nmtdev->device)); + if (!aps) + return; + + for (i = 0; i < aps->len; i++) { + ap = aps->pdata[i]; + + if (!nm_access_point_get_ssid (ap)) + continue; + + nmtconn = g_slice_new0 (NmtConnectConnection); + nmtconn->device = nmtdev->device; + nmtconn->ap = g_object_ref (ap); + nmtconn->ssid = nm_utils_ssid_to_utf8 (nm_access_point_get_ssid (ap)); + + for (iter = connections; iter; iter = iter->next) { + conn = iter->data; + if ( nm_device_connection_valid (nmtdev->device, conn) + && nm_access_point_connection_valid (ap, conn)) { + nmtconn->name = nm_connection_get_id (conn); + nmtconn->conn = g_object_ref (conn); + break; + } + } + + if (!iter) + nmtconn->name = nmtconn->ssid; + + nmtdev->conns = g_slist_prepend (nmtdev->conns, nmtconn); + } +} + +static GSList * +append_nmt_devices_for_devices (GSList *nmt_devices, + const GPtrArray *devices, + char **names, + GSList *connections) +{ + NmtConnectDevice *nmtdev; + NMDevice *device; + int i, sort_order; + + for (i = 0; i < devices->len; i++) { + device = devices->pdata[i]; + + sort_order = get_sort_order_for_device (device); + if (sort_order == -1) + continue; + + nmtdev = g_slice_new0 (NmtConnectDevice); + nmtdev->name = g_strdup (names[i]); + nmtdev->device = g_object_ref (device); + nmtdev->sort_order = sort_order; + + if (NM_IS_DEVICE_WIFI (device)) + add_connections_for_aps (nmtdev, connections); + else + add_connections_for_device (nmtdev, connections); + nmtdev->conns = g_slist_sort (nmtdev->conns, sort_connections); + + nmt_devices = g_slist_prepend (nmt_devices, nmtdev); + } + + return nmt_devices; +} + +static GSList * +append_nmt_devices_for_virtual_devices (GSList *nmt_devices, + GSList *connections) +{ + NmtConnectDevice *nmtdev; + GSList *iter; + GHashTable *devices_by_name; + char *name; + NMConnection *conn; + NmtConnectConnection *nmtconn; + int sort_order; + + devices_by_name = g_hash_table_new (g_str_hash, g_str_equal); + + for (iter = connections; iter; iter = iter->next) { + conn = iter->data; + sort_order = get_sort_order_for_connection (conn); + if (sort_order == -1) + continue; + + name = nma_utils_get_connection_device_name (conn); + nmtdev = g_hash_table_lookup (devices_by_name, name); + if (nmtdev) + g_free (name); + else { + nmtdev = g_slice_new0 (NmtConnectDevice); + nmtdev->name = name; + nmtdev->sort_order = sort_order; + + g_hash_table_insert (devices_by_name, nmtdev->name, nmtdev); + nmt_devices = g_slist_prepend (nmt_devices, nmtdev); + } + + nmtconn = g_slice_new0 (NmtConnectConnection); + nmtconn->name = nm_connection_get_id (conn); + nmtconn->conn = g_object_ref (conn); + + nmtdev->conns = g_slist_insert_sorted (nmtdev->conns, nmtconn, sort_connections); + } + + g_hash_table_destroy (devices_by_name); + return nmt_devices; +} + +static GSList * +append_nmt_devices_for_vpns (GSList *nmt_devices, + GSList *connections) +{ + NmtConnectDevice *nmtdev; + GSList *iter; + NMConnection *conn; + NmtConnectConnection *nmtconn; + + nmtdev = g_slice_new0 (NmtConnectDevice); + nmtdev->name = g_strdup (_("VPN")); + nmtdev->sort_order = 100; + + for (iter = connections; iter; iter = iter->next) { + conn = iter->data; + if (!nm_connection_is_type (conn, NM_SETTING_VPN_SETTING_NAME)) + continue; + + nmtconn = g_slice_new0 (NmtConnectConnection); + nmtconn->name = nm_connection_get_id (conn); + nmtconn->conn = g_object_ref (conn); + + nmtdev->conns = g_slist_insert_sorted (nmtdev->conns, nmtconn, sort_connections); + } + + if (nmtdev->conns) + nmt_devices = g_slist_prepend (nmt_devices, nmtdev); + else + nmt_connect_device_free (nmtdev); + + return nmt_devices; +} + +static int +sort_nmt_devices (gconstpointer a, + gconstpointer b) +{ + NmtConnectDevice *nmta = (NmtConnectDevice *)a; + NmtConnectDevice *nmtb = (NmtConnectDevice *)b; + + if (nmta->sort_order != nmtb->sort_order) + return nmta->sort_order - nmtb->sort_order; + + return strcmp (nmta->name, nmtb->name); +} + +static gboolean +connection_is_active (NMConnection *conn, + const GPtrArray *acs) +{ + NMActiveConnection *ac; + const char *path, *ac_path; + int i; + + path = nm_connection_get_path (conn); + for (i = 0; acs && i < acs->len; i++) { + ac = acs->pdata[i]; + ac_path = nm_active_connection_get_connection (ac); + + if (!strcmp (path, ac_path)) + return TRUE; + } + + return FALSE; +} + +static void +nmt_connect_connection_list_rebuild (NmtConnectConnectionList *list) +{ + NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (list); + NmtNewtListbox *listbox = NMT_NEWT_LISTBOX (list); + const GPtrArray *devices, *acs; + int max_width; + char **names, *row, active_col; + const char *strength_col; + GSList *connections; + GSList *nmt_devices, *diter, *citer; + NmtConnectDevice *nmtdev; + NmtConnectConnection *nmtconn; + + g_slist_free_full (priv->nmt_devices, (GDestroyNotify) nmt_connect_device_free); + priv->nmt_devices = NULL; + nmt_newt_listbox_clear (listbox); + + devices = nm_client_get_devices (nm_client); + acs = nm_client_get_active_connections (nm_client); + connections = nm_remote_settings_list_connections (nm_settings); + + nmt_devices = NULL; + if (devices) { + names = nma_utils_disambiguate_device_names ((NMDevice **) devices->pdata, devices->len); + nmt_devices = append_nmt_devices_for_devices (nmt_devices, devices, names, connections); + g_strfreev (names); + } + nmt_devices = append_nmt_devices_for_virtual_devices (nmt_devices, connections); + nmt_devices = append_nmt_devices_for_vpns (nmt_devices, connections); + + nmt_devices = g_slist_sort (nmt_devices, sort_nmt_devices); + g_slist_free (connections); + + max_width = 0; + for (diter = nmt_devices; diter; diter = diter->next) { + nmtdev = diter->data; + for (citer = nmtdev->conns; citer; citer = citer->next) { + nmtconn = citer->data; + + max_width = MAX (max_width, g_utf8_strlen (nmtconn->name, -1)); + } + } + + for (diter = nmt_devices; diter; diter = diter->next) { + nmtdev = diter->data; + + if (diter != nmt_devices) + nmt_newt_listbox_append (listbox, "", NULL); + nmt_newt_listbox_append (listbox, nmtdev->name, NULL); + + for (citer = nmtdev->conns; citer; citer = citer->next) { + nmtconn = citer->data; + + if (nmtconn->conn && connection_is_active (nmtconn->conn, acs)) + active_col = '*'; + else + active_col = ' '; + + if (nmtconn->ap) { + guint8 strength = nm_access_point_get_strength (nmtconn->ap); + + if (strength > 80) + strength_col = " ▂▄▆█"; + else if (strength > 55) + strength_col = " ▂▄▆_"; + else if (strength > 30) + strength_col = " ▂▄__"; + else if (strength > 5) + strength_col = " ▂___"; + else + strength_col = " ____"; + } else + strength_col = ""; + + row = g_strdup_printf ("%c %s%-*s%s", + active_col, + nmtconn->name, + (int)(max_width - g_utf8_strlen (nmtconn->name, -1)), "", + strength_col); + + nmt_newt_listbox_append (listbox, row, nmtconn); + g_free (row); + } + } + + priv->nmt_devices = nmt_devices; +} + +static void +rebuild_on_acs_changed (GObject *object, + GParamSpec *spec, + gpointer list) +{ + nmt_connect_connection_list_rebuild (list); +} + +static void +rebuild_on_devices_changed (NMClient *client, + NMDevice *device, + gpointer list) +{ + nmt_connect_connection_list_rebuild (list); +} + +static void +nmt_connect_connection_list_constructed (GObject *object) +{ + NmtConnectConnectionList *list = NMT_CONNECT_CONNECTION_LIST (object); + + g_signal_connect (nm_client, "notify::" NM_CLIENT_ACTIVE_CONNECTIONS, + G_CALLBACK (rebuild_on_acs_changed), list); + g_signal_connect (nm_client, "device-added", + G_CALLBACK (rebuild_on_devices_changed), list); + g_signal_connect (nm_client, "device-removed", + G_CALLBACK (rebuild_on_devices_changed), list); + + nmt_connect_connection_list_rebuild (list); + + G_OBJECT_CLASS (nmt_connect_connection_list_parent_class)->constructed (object); +} + +static void +nmt_connect_connection_list_finalize (GObject *object) +{ + NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (object); + + g_slist_free_full (priv->nmt_devices, (GDestroyNotify) nmt_connect_device_free); + + g_signal_handlers_disconnect_by_func (nm_client, G_CALLBACK (rebuild_on_acs_changed), object); + g_signal_handlers_disconnect_by_func (nm_client, G_CALLBACK (rebuild_on_devices_changed), object); + + G_OBJECT_CLASS (nmt_connect_connection_list_parent_class)->finalize (object); +} + +typedef struct { + NmtNewtForm *form; + NMSecretAgent *agent; +} NmtActivationData; + +static void +nmt_activation_data_free (NmtActivationData *data) +{ + g_object_unref (data->form); + g_object_unref (data->agent); + g_slice_free (NmtActivationData, data); +} + +static void +secrets_requested (NmtSecretAgent *agent, + const char *request_id, + const char *title, + const char *msg, + GPtrArray *secrets, + gpointer user_data) +{ + NmtNewtForm *form; + + form = nmt_password_dialog_new (request_id, title, msg, secrets); + nmt_newt_form_run_sync (form); + + if (nmt_password_dialog_succeeded (NMT_PASSWORD_DIALOG (form))) + nmt_secret_agent_response (agent, request_id, secrets); + else + nmt_secret_agent_response (agent, request_id, NULL); + + g_object_unref (form); +} + + +static gboolean +idle_unref_ac (gpointer ac) +{ + g_object_unref (ac); + return FALSE; +} + +static void +activation_complete (GSimpleAsyncResult *simple, + GError *error) +{ + NmtActivationData *data = g_object_get_data (G_OBJECT (simple), "NmtActivationData"); + + if (error) + g_simple_async_result_set_from_error (simple, error); + else + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete (simple); + g_object_unref (simple); + + nmt_newt_form_quit (data->form); + /* If we bail out too soon, the agent won't have completed registering, + * and nm_secret_agent_unregister() would complain. + */ + if (nm_secret_agent_get_registered (data->agent)) + nm_secret_agent_unregister (data->agent); +} + +static void +activate_ac_state_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + NMActiveConnectionState state; + GError *error = NULL; + + state = nm_active_connection_get_state (NM_ACTIVE_CONNECTION (object)); + if (state == NM_ACTIVE_CONNECTION_STATE_ACTIVATING) + return; + + if (state != NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + error = g_error_new_literal (NM_CLIENT_ERROR, NM_CLIENT_ERROR_UNKNOWN, + _("Activation failed")); + } + + g_signal_handlers_disconnect_by_func (object, G_CALLBACK (activate_ac_state_changed), simple); + + /* Work around NMObject bug for now: fix is 1981323b */ + g_idle_add (idle_unref_ac, object); + + activation_complete (simple, error); + g_clear_error (&error); +} + +static void +activation_callback (NMClient *client, + NMActiveConnection *ac, + GError *error, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + + if (error || nm_active_connection_get_state (ac) == NM_ACTIVE_CONNECTION_STATE_ACTIVATED) { + activation_complete (simple, error); + return; + } + + g_object_ref (ac); + g_signal_connect (ac, "notify::" NM_ACTIVE_CONNECTION_STATE, + G_CALLBACK (activate_ac_state_changed), simple); +} + +static void +activate_nmt_connection_async (NmtConnectConnectionList *list, + NmtConnectConnection *nmtconn, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NmtActivationData *data; + NmtNewtWidget *label; + GSimpleAsyncResult *simple; + + data = g_slice_new (NmtActivationData); + + data->form = g_object_new (NMT_TYPE_NEWT_FORM, NULL); + label = nmt_newt_label_new (_("Connecting...")); + nmt_newt_form_set_content (data->form, label); + + data->agent = nmt_secret_agent_new (); + nm_secret_agent_register (data->agent); + g_signal_connect (data->agent, "request-secrets", + G_CALLBACK (secrets_requested), NULL); + + simple = g_simple_async_result_new (G_OBJECT (list), callback, user_data, + activate_nmt_connection_async); + g_object_set_data_full (G_OBJECT (simple), "NmtActivationData", data, + (GDestroyNotify)nmt_activation_data_free); + + /* FIXME: cancel button */ + nm_client_activate_connection (nm_client, + nmtconn->conn, nmtconn->device, + nmtconn->ap ? nm_object_get_path (NM_OBJECT (nmtconn->ap)) : NULL, + activation_callback, simple); + nmt_newt_form_show (data->form); +} + +static gboolean +activate_nmt_connection_finish (NmtConnectConnectionList *list, + GAsyncResult *result, + GError **error) +{ + return g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); +} + +static void +activate_complete (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + NmtSyncOp *op = user_data; + GError *error = NULL; + + if (activate_nmt_connection_finish (NMT_CONNECT_CONNECTION_LIST (object), result, &error)) + nmt_sync_op_complete_boolean (op, TRUE, NULL); + else + nmt_sync_op_complete_boolean (op, FALSE, error); + g_clear_error (&error); +} + +static void +nmt_connect_connection_list_activated (NmtNewtWidget *widget) +{ + NmtConnectConnection *nmtconn; + NmtSyncOp op; + GError *error = NULL; + + nmtconn = nmt_newt_listbox_get_active_key (NMT_NEWT_LISTBOX (widget)); + g_return_if_fail (nmtconn != NULL); + + nmt_sync_op_init (&op); + activate_nmt_connection_async (NMT_CONNECT_CONNECTION_LIST (widget), nmtconn, + activate_complete, &op); + if (!nmt_sync_op_wait_boolean (&op, &error)) { + nmt_newt_error_dialog (_("Could not activate connection: %s"), error->message); + g_error_free (error); + } +} + +static void +nmt_connect_connection_list_class_init (NmtConnectConnectionListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (list_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (list_class); + + g_type_class_add_private (list_class, sizeof (NmtConnectConnectionListPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_connect_connection_list_constructed; + object_class->finalize = nmt_connect_connection_list_finalize; + + widget_class->activated = nmt_connect_connection_list_activated; +} + +/** + * nmt_connect_connection_list_activate_async: + * @list: an #NmtConnectConnectionList + * @identifier: a connection ID or UUID + * @callback: #GAsyncReadyCallback + * @user_data: data for @callback + * + * Asynchronously begins to activate the connection identified by + * @identifier, and invokes @callback when it completes. + */ +void +nmt_connect_connection_list_activate_async (NmtConnectConnectionList *list, + const char *identifier, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NmtConnectConnectionListPrivate *priv = NMT_CONNECT_CONNECTION_LIST_GET_PRIVATE (list); + GSList *diter, *citer; + NmtConnectDevice *nmtdev; + NmtConnectConnection *nmtconn = NULL; + NMConnection *conn = NULL; + + if (nm_utils_is_uuid (identifier)) + conn = NM_CONNECTION (nm_remote_settings_get_connection_by_uuid (nm_settings, identifier)); + else { + GSList *conns, *iter; + + conns = nm_remote_settings_list_connections (nm_settings); + for (iter = conns; iter; iter = iter->next) { + NMConnection *candidate = iter->data; + + if (!strcmp (identifier, nm_connection_get_id (candidate))) { + conn = candidate; + break; + } + } + g_slist_free (conns); + } + + for (diter = priv->nmt_devices; diter; diter = diter->next) { + nmtdev = diter->data; + if (!nmtdev->conns) + continue; + + for (citer = nmtdev->conns; citer; citer = citer->next) { + nmtconn = citer->data; + if (conn) { + if (conn == nmtconn->conn) + goto activate; + else + continue; + } + + if (nmtconn->ssid && !strcmp (identifier, nmtconn->ssid)) + goto activate; + } + + if (!conn && !nmtdev->device && !strcmp (identifier, nm_device_get_ip_iface (nmtdev->device))) { + nmtconn = nmtdev->conns->data; + goto activate; + } + } + + g_printerr ("%s: no such connection '%s'\n", g_get_prgname (), identifier); + exit (1); + + activate: + activate_nmt_connection_async (list, nmtconn, callback, user_data); +} + +/** + * nmt_connect_connection_list_activate_finish: + * @list: an #NmtConnectConnectionList + * @result: the #GAsyncResult passed to the callback + * @error: return location for a #GError + * + * Gets the result of an nmt_connect_connection_list_activate_async() call. + * + * Returns: success or failure + */ +gboolean +nmt_connect_connection_list_activate_finish (NmtConnectConnectionList *list, + GAsyncResult *result, + GError **error) +{ + return g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error); +} diff --git a/tui/nmt-connect-connection-list.h b/tui/nmt-connect-connection-list.h new file mode 100644 index 0000000000..14e94ec528 --- /dev/null +++ b/tui/nmt-connect-connection-list.h @@ -0,0 +1,58 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_CONNECT_CONNECTION_LIST_H +#define NMT_CONNECT_CONNECTION_LIST_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_CONNECT_CONNECTION_LIST (nmt_connect_connection_list_get_type ()) +#define NMT_CONNECT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionList)) +#define NMT_CONNECT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListClass)) +#define NMT_IS_CONNECT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST)) +#define NMT_IS_CONNECT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_CONNECT_CONNECTION_LIST)) +#define NMT_CONNECT_CONNECTION_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_CONNECT_CONNECTION_LIST, NmtConnectConnectionListClass)) + +typedef struct { + NmtNewtListbox parent; + +} NmtConnectConnectionList; + +typedef struct { + NmtNewtListboxClass parent; + +} NmtConnectConnectionListClass; + +GType nmt_connect_connection_list_get_type (void); + +NmtNewtWidget *nmt_connect_connection_list_new (void); + +void nmt_connect_connection_list_activate_async (NmtConnectConnectionList *list, + const char *identifier, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean nmt_connect_connection_list_activate_finish (NmtConnectConnectionList *list, + GAsyncResult *result, + GError **error); + + +G_END_DECLS + +#endif /* NMT_CONNECT_CONNECTION_LIST_H */ diff --git a/tui/nmt-device-entry.c b/tui/nmt-device-entry.c new file mode 100644 index 0000000000..3a418fd13a --- /dev/null +++ b/tui/nmt-device-entry.c @@ -0,0 +1,589 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-device-entry + * @short_description: #NmtNewtEntry for identifying a device + * + * #NmtDeviceEntry provides a widget for identifying a device, either + * by interface name or by hardware address. The user can enter either + * value, and the entry's #NmtDeviceEntry:interface-name or + * #NmtDeviceEntry:mac-address property will be set accordingly. If + * the entry recognizes the interface name or mac address typed in as + * matching a known #NMDevice, then it will also display the other + * property in parentheses. + * + * FIXME: #NmtDeviceEntry is currently an #NmtPageGrid object, so that + * we can possibly eventually add a button to its "extra" field, that + * would pop up a form for selecting a device. But if we're not going + * to implement that then we should make it just an #NmtNewtEntry. + */ + +#include "config.h" + +#include <string.h> +#include <sys/socket.h> +#include <linux/if_arp.h> + +#include <glib/gi18n-lib.h> +#include <nm-device.h> +#include <nm-device-infiniband.h> +#include <nm-utils.h> + +#include "nmtui.h" +#include "nmt-device-entry.h" + +G_DEFINE_TYPE (NmtDeviceEntry, nmt_device_entry, NMT_TYPE_PAGE_GRID) + +#define NMT_DEVICE_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryPrivate)) + +typedef struct { + GType hardware_type; + NmtDeviceEntryDeviceFilter device_filter; + gpointer device_filter_data; + int arptype; + + char *interface_name; + GByteArray *mac_address; + + char *label; + NmtNewtEntry *entry; + NmtNewtWidget *button; + + gboolean updating; +} NmtDeviceEntryPrivate; + +enum { + PROP_0, + PROP_LABEL, + PROP_WIDTH, + PROP_HARDWARE_TYPE, + PROP_INTERFACE_NAME, + PROP_MAC_ADDRESS, + + LAST_PROP +}; + +/** + * nmt_device_entry_new: + * @label: the label for the entry + * @width: the width of the entry + * @hardware_type: the type of #NMDevice to be selected, or + * %G_TYPE_NONE if this is for a virtual device type. + * + * Creates a new #NmtDeviceEntry, for identifying a device of type + * @hardware_type. If @hardware_type is %G_TYPE_NONE (and you do not + * set a #NmtDeviceEntryDeviceFilter), then this will only allow + * specifying an interface name, not a hardware address. + * + * Returns: a new #NmtDeviceEntry. + */ +NmtNewtWidget * +nmt_device_entry_new (const char *label, + int width, + GType hardware_type) +{ + return g_object_new (NMT_TYPE_DEVICE_ENTRY, + "label", label, + "width", width, + "hardware-type", hardware_type, + NULL); +} + +static gboolean +device_entry_parse (NmtDeviceEntry *deventry, + const char *text, + char **interface_name, + char **mac_address) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; + char **words; + int len; + + *interface_name = *mac_address = NULL; + if (!*text) + return TRUE; + + if (priv->hardware_type == G_TYPE_NONE && !priv->device_filter) { + if (nm_utils_iface_valid_name (text)) { + *interface_name = g_strdup (text); + return TRUE; + } else + return FALSE; + } + + words = g_strsplit (text, " ", -1); + if (g_strv_length (words) > 2) { + g_strfreev (words); + return FALSE; + } + + if (words[1]) { + len = strlen (words[1]); + if (len < 3 || words[1][0] != '(' || words[1][len - 1] != ')') + goto fail; + + memmove (words[1], words[1] + 1, len - 2); + words[1][len - 2] = '\0'; + } + + if ( nm_utils_hwaddr_aton (words[0], priv->arptype, buf) + && (!words[1] || nm_utils_iface_valid_name (words[1]))) { + *mac_address = words[0]; + *interface_name = NULL; + g_free (words); + return TRUE; + } else if ( nm_utils_iface_valid_name (words[0]) + && (!words[1] || nm_utils_hwaddr_aton (words[1], priv->arptype, buf))) { + *interface_name = words[0]; + *mac_address = NULL; + g_free (words); + return TRUE; + } + + fail: + g_strfreev (words); + return FALSE; +} + +static gboolean +device_entry_validate (NmtNewtEntry *entry, + const char *text, + gpointer user_data) +{ + NmtDeviceEntry *deventry = user_data; + char *ifname, *mac; + + if (!device_entry_parse (deventry, text, &ifname, &mac)) + return FALSE; + + g_free (ifname); + g_free (mac); + return TRUE; +} + +static NMDevice * +find_device_by_interface_name (NmtDeviceEntry *deventry, + const char *interface_name) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + const GPtrArray *devices; + NMDevice *device = NULL; + int i; + + devices = nm_client_get_devices (nm_client); + if (!devices) + return NULL; + + for (i = 0; i < devices->len && !device; i++) { + NMDevice *candidate = devices->pdata[i]; + + if ( priv->hardware_type != G_TYPE_NONE + && !G_TYPE_CHECK_INSTANCE_TYPE (candidate, priv->hardware_type)) + continue; + + if ( priv->device_filter + && !priv->device_filter (deventry, candidate, priv->device_filter_data)) + continue; + + if (!g_strcmp0 (interface_name, nm_device_get_iface (candidate))) + device = candidate; + } + + return device; +} + +static NMDevice * +find_device_by_mac_address (NmtDeviceEntry *deventry, + const char *mac_address) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + const GPtrArray *devices; + NMDevice *device = NULL; + int i; + + devices = nm_client_get_devices (nm_client); + if (!devices) + return NULL; + + for (i = 0; i < devices->len && !device; i++) { + NMDevice *candidate = devices->pdata[i]; + char *hwaddr; + + if ( priv->hardware_type != G_TYPE_NONE + && !G_TYPE_CHECK_INSTANCE_TYPE (candidate, priv->hardware_type)) + continue; + + if ( priv->device_filter + && !priv->device_filter (deventry, candidate, priv->device_filter_data)) + continue; + + g_object_get (G_OBJECT (candidate), "hw-address", &hwaddr, NULL); + if (hwaddr && !g_ascii_strcasecmp (mac_address, hwaddr)) + device = candidate; + g_free (hwaddr); + } + + return device; +} + +static void +update_entry (NmtDeviceEntry *deventry) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + const char *ifname; + char *mac, *text; + NMDevice *ifname_device, *mac_device; + + if (priv->interface_name) { + ifname = priv->interface_name; + ifname_device = find_device_by_interface_name (deventry, priv->interface_name); + } else { + ifname = NULL; + ifname_device = NULL; + } + + if (priv->mac_address) { + mac = nm_utils_hwaddr_ntoa (priv->mac_address->data, priv->arptype); + mac_device = find_device_by_mac_address (deventry, mac); + } else { + mac = NULL; + mac_device = NULL; + } + + if (!ifname && mac_device) + ifname = nm_device_get_iface (mac_device); + if (!mac && ifname_device) + g_object_get (G_OBJECT (ifname_device), "hw-address", &mac, NULL); + + if (ifname_device && mac_device && ifname_device != mac_device) { + /* Mismatch! */ + text = g_strdup_printf ("%s != %s", priv->interface_name, mac); + } else if (ifname && mac) { + if (ifname_device) + text = g_strdup_printf ("%s (%s)", ifname, mac); + else + text = g_strdup_printf ("%s (%s)", mac, ifname); + } else if (ifname) + text = g_strdup (ifname); + else if (mac) + text = g_strdup (mac); + else + text = g_strdup (""); + + priv->updating = TRUE; + g_object_set (G_OBJECT (priv->entry), "text", text, NULL); + priv->updating = FALSE; + g_free (text); + + g_free (mac); +} + +static gboolean +nmt_device_entry_set_interface_name (NmtDeviceEntry *deventry, + const char *interface_name) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + + if (g_strcmp0 (interface_name, priv->interface_name) != 0) { + g_free (priv->interface_name); + priv->interface_name = g_strdup (interface_name); + + g_object_notify (G_OBJECT (deventry), "interface-name"); + return TRUE; + } else + return FALSE; +} + +static gboolean +nmt_device_entry_set_mac_address (NmtDeviceEntry *deventry, + GByteArray *mac_address) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + gboolean changed; + + if (mac_address) + g_return_val_if_fail (mac_address->len == nm_utils_hwaddr_len (priv->arptype), FALSE); + + if (mac_address && !priv->mac_address) { + priv->mac_address = g_boxed_copy (DBUS_TYPE_G_UCHAR_ARRAY, mac_address); + changed = TRUE; + } else if (!mac_address && priv->mac_address) { + g_clear_pointer (&priv->mac_address, g_byte_array_unref); + changed = TRUE; + } else if ( mac_address && priv->mac_address + && memcmp (mac_address->data, priv->mac_address->data, mac_address->len) != 0) { + g_byte_array_unref (priv->mac_address); + priv->mac_address = g_boxed_copy (DBUS_TYPE_G_UCHAR_ARRAY, mac_address); + changed = TRUE; + } else + changed = FALSE; + + if (changed) + g_object_notify (G_OBJECT (deventry), "mac-address"); + return changed; +} + +static void +entry_text_changed (GObject *object, + GParamSpec *pspec, + gpointer deventry) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + const char *text; + char *ifname, *mac; + + if (priv->updating) + return; + + text = nmt_newt_entry_get_text (priv->entry); + if (!device_entry_parse (deventry, text, &ifname, &mac)) + return; + + if (ifname) { + nmt_device_entry_set_interface_name (deventry, ifname); + g_free (ifname); + } else + nmt_device_entry_set_interface_name (deventry, NULL); + + if (mac) { + GByteArray *mac_address; + + mac_address = nm_utils_hwaddr_atoba (mac, priv->arptype); + nmt_device_entry_set_mac_address (deventry, mac_address); + g_byte_array_unref (mac_address); + g_free (mac); + } else + nmt_device_entry_set_mac_address (deventry, NULL); +} + +static void +nmt_device_entry_init (NmtDeviceEntry *deventry) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + NmtNewtWidget *entry; + + priv->hardware_type = G_TYPE_NONE; + + entry = nmt_newt_entry_new (-1, 0); + priv->entry = NMT_NEWT_ENTRY (entry); + nmt_newt_entry_set_validator (priv->entry, device_entry_validate, deventry); + g_signal_connect (priv->entry, "notify::text", + G_CALLBACK (entry_text_changed), deventry); + +#if 0 + priv->button = nmt_newt_button_new (_("Select...")); + g_signal_connect (priv->button, "clicked", + G_CALLBACK (do_select_dialog), deventry); +#endif +} + +static void +nmt_device_entry_constructed (GObject *object) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object); + + nmt_page_grid_append (NMT_PAGE_GRID (object), priv->label, NMT_NEWT_WIDGET (priv->entry), NULL); + + G_OBJECT_CLASS (nmt_device_entry_parent_class)->constructed (object); +} + +static void +nmt_device_entry_finalize (GObject *object) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object); + + g_free (priv->interface_name); + if (priv->mac_address) + g_byte_array_unref (priv->mac_address); + + G_OBJECT_CLASS (nmt_device_entry_parent_class)->finalize (object); +} + +/** + * NmtDeviceEntryDeviceFilter: + * @deventry: the #NmtDeviceEntry + * @device: an #NMDevice + * @user_data: user data + * + * Filter function for determining which devices can be specified + * on an entry. + * + * Returns: %TRUE if @device is acceptable for @deventry + */ + +/** + * nmt_device_entry_set_device_filter: + * @deventry: the #NmtDeviceEntry + * @filter: the filter + * @user_data: data for @filter + * + * Sets a device filter on @deventry. Only devices that pass @filter + * will be recognized by @deventry. + * + * If the entry's #NmtDeviceEntry:hardware-type is not %G_TYPE_NONE, + * then only devices that both match the hardware type and are + * accepted by the filter will be allowed. + */ +void +nmt_device_entry_set_device_filter (NmtDeviceEntry *deventry, + NmtDeviceEntryDeviceFilter filter, + gpointer user_data) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + + priv->device_filter = filter; + priv->device_filter_data = user_data; +} + +static void +nmt_device_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtDeviceEntry *deventry = NMT_DEVICE_ENTRY (object); + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (deventry); + const char *interface_name; + GByteArray *mac_address; + + switch (prop_id) { + case PROP_LABEL: + priv->label = g_value_dup_string (value); + break; + case PROP_WIDTH: + nmt_newt_entry_set_width (priv->entry, g_value_get_int (value)); + break; + case PROP_HARDWARE_TYPE: + priv->hardware_type = g_value_get_gtype (value); + priv->arptype = (priv->hardware_type == NM_TYPE_DEVICE_INFINIBAND) ? ARPHRD_INFINIBAND : ARPHRD_ETHER; + break; + case PROP_INTERFACE_NAME: + interface_name = g_value_get_string (value); + if (nmt_device_entry_set_interface_name (deventry, interface_name)) + update_entry (deventry); + break; + case PROP_MAC_ADDRESS: + mac_address = g_value_get_boxed (value); + if (nmt_device_entry_set_mac_address (deventry, mac_address)) + update_entry (deventry); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_device_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtDeviceEntryPrivate *priv = NMT_DEVICE_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_LABEL: + g_value_set_string (value, priv->label); + break; + case PROP_WIDTH: + g_value_set_int (value, nmt_newt_entry_get_width (priv->entry)); + break; + case PROP_HARDWARE_TYPE: + g_value_set_gtype (value, priv->hardware_type); + break; + case PROP_INTERFACE_NAME: + g_value_set_string (value, priv->interface_name); + break; + case PROP_MAC_ADDRESS: + g_value_set_boxed (value, priv->mac_address); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_device_entry_class_init (NmtDeviceEntryClass *deventry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (deventry_class); + + g_type_class_add_private (deventry_class, sizeof (NmtDeviceEntryPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_device_entry_constructed; + object_class->set_property = nmt_device_entry_set_property; + object_class->get_property = nmt_device_entry_get_property; + object_class->finalize = nmt_device_entry_finalize; + + /** + * NmtDeviceEntry:label: + * + * The entry's label + */ + g_object_class_install_property (object_class, PROP_LABEL, + g_param_spec_string ("label", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:width: + * + * The entry's width in characters + */ + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_int ("width", "", "", + -1, 80, -1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:hardware-type: + * + * The type of #NMDevice to limit the entry to, or %G_TYPE_NONE + * if the entry is for a virtual device and should not accept + * hardware addresses. + */ + g_object_class_install_property (object_class, PROP_HARDWARE_TYPE, + g_param_spec_gtype ("hardware-type", "", "", + G_TYPE_NONE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:interface-name: + * + * The interface name of the device identified by the entry. + */ + g_object_class_install_property (object_class, PROP_INTERFACE_NAME, + g_param_spec_string ("interface-name", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtDeviceEntry:mac-address: + * + * The hardware address of the device identified by the entry. + */ + g_object_class_install_property (object_class, PROP_MAC_ADDRESS, + g_param_spec_boxed ("mac-address", "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-device-entry.h b/tui/nmt-device-entry.h new file mode 100644 index 0000000000..2b959aaa85 --- /dev/null +++ b/tui/nmt-device-entry.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_DEVICE_ENTRY_H +#define NMT_DEVICE_ENTRY_H + +#include "nmt-page-grid.h" + +#include <nm-connection.h> +#include <nm-device.h> + +G_BEGIN_DECLS + +#define NMT_TYPE_DEVICE_ENTRY (nmt_device_entry_get_type ()) +#define NMT_DEVICE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntry)) +#define NMT_DEVICE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryClass)) +#define NMT_IS_DEVICE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_DEVICE_ENTRY)) +#define NMT_IS_DEVICE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_DEVICE_ENTRY)) +#define NMT_DEVICE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_DEVICE_ENTRY, NmtDeviceEntryClass)) + +typedef struct { + NmtPageGrid parent; + +} NmtDeviceEntry; + +typedef struct { + NmtPageGridClass parent; + +} NmtDeviceEntryClass; + +GType nmt_device_entry_get_type (void); + +NmtNewtWidget *nmt_device_entry_new (const char *label, + int width, + GType hardware_type); + +typedef gboolean (*NmtDeviceEntryDeviceFilter) (NmtDeviceEntry *deventry, + NMDevice *device, + gpointer user_data); +void nmt_device_entry_set_device_filter (NmtDeviceEntry *deventry, + NmtDeviceEntryDeviceFilter filter, + gpointer user_data); + +G_END_DECLS + +#endif /* NMT_DEVICE_ENTRY_H */ diff --git a/tui/nmt-edit-connection-list.c b/tui/nmt-edit-connection-list.c new file mode 100644 index 0000000000..eeba78ddad --- /dev/null +++ b/tui/nmt-edit-connection-list.c @@ -0,0 +1,550 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-edit-connection-list + * @short_description: Connection list for "nmtui edit" + * + * #NmtEditConnectionList is the list of connections displayed by + * "nmtui edit". + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmtui.h" +#include "nmtui-edit.h" +#include "nmt-edit-connection-list.h" +#include "nmt-editor.h" + +#include "nm-editor-utils.h" + +G_DEFINE_TYPE (NmtEditConnectionList, nmt_edit_connection_list, NMT_TYPE_NEWT_GRID) + +#define NMT_EDIT_CONNECTION_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListPrivate)) + +typedef struct { + GSList *connections; + + gboolean grouped; + NmtEditConnectionListFilter connection_filter; + gpointer connection_filter_data; + + NmtNewtListbox *listbox; + NmtNewtButtonBox *buttons; + + NmtNewtWidget *add; + NmtNewtWidget *edit; + NmtNewtWidget *delete; + NmtNewtWidget *extra; +} NmtEditConnectionListPrivate; + +enum { + PROP_0, + + PROP_GROUPED, + PROP_CONNECTION_FILTER, + PROP_CONNECTION_FILTER_DATA, + PROP_EXTRA_WIDGET, + PROP_CONNECTIONS, + PROP_NUM_CONNECTIONS, + + LAST_PROP +}; + +enum { + ADD_CONNECTION, + EDIT_CONNECTION, + REMOVE_CONNECTION, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void add_clicked (NmtNewtButton *button, gpointer list); +static void edit_clicked (NmtNewtButton *button, gpointer list); +static void delete_clicked (NmtNewtButton *button, gpointer list); +static void listbox_activated (NmtNewtWidget *listbox, gpointer list); + +/** + * nmt_edit_connection_list_get_connections: + * @list: an #NmtEditConnectionList + * + * Gets the list's list of connections + * + * Returns: (transfer none) (element-type #NMConnection): the + * list of connections. + */ +GSList * +nmt_edit_connection_list_get_connections (NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + + return priv->connections; +} + +static void +nmt_edit_connection_list_init (NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + NmtNewtWidget *listbox, *buttons; + NmtNewtGrid *grid = NMT_NEWT_GRID (list); + + listbox = g_object_new (NMT_TYPE_NEWT_LISTBOX, + "flags", NMT_NEWT_LISTBOX_SCROLL | NMT_NEWT_LISTBOX_BORDER, + "skip-null-keys", TRUE, + NULL); + priv->listbox = NMT_NEWT_LISTBOX (listbox); + nmt_newt_grid_add (grid, listbox, 0, 0); + nmt_newt_grid_set_flags (grid, listbox, + NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | + NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_EXPAND_Y); + g_signal_connect (priv->listbox, "activated", G_CALLBACK (listbox_activated), list); + + buttons = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_VERTICAL); + priv->buttons = NMT_NEWT_BUTTON_BOX (buttons); + nmt_newt_grid_add (grid, buttons, 1, 0); + nmt_newt_widget_set_padding (buttons, 1, 1, 0, 1); + nmt_newt_grid_set_flags (grid, buttons, + NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | + NMT_NEWT_GRID_EXPAND_Y); + + priv->add = nmt_newt_button_box_add_start (priv->buttons, _("Add")); + g_signal_connect (priv->add, "clicked", G_CALLBACK (add_clicked), list); + + priv->edit = nmt_newt_button_box_add_start (priv->buttons, _("Edit...")); + g_signal_connect (priv->edit, "clicked", G_CALLBACK (edit_clicked), list); + + priv->delete = nmt_newt_button_box_add_start (priv->buttons, _("Delete")); + g_signal_connect (priv->delete, "clicked", G_CALLBACK (delete_clicked), list); +} + +static int +sort_by_timestamp (gconstpointer a, + gconstpointer b) +{ + NMSettingConnection *s_con_a, *s_con_b; + guint64 time_a, time_b; + + s_con_a = nm_connection_get_setting_connection ((NMConnection *) a); + s_con_b = nm_connection_get_setting_connection ((NMConnection *) b); + + time_a = nm_setting_connection_get_timestamp (s_con_a); + time_b = nm_setting_connection_get_timestamp (s_con_b); + + return (int) (time_b - time_a); +} + +static void nmt_edit_connection_list_rebuild (NmtEditConnectionList *list); + +static void +rebuild_on_connection_updated (NMRemoteConnection *connection, + gpointer list) +{ + nmt_edit_connection_list_rebuild (list); +} + +static void +free_connections (NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + NMConnection *conn; + GSList *iter; + + for (iter = priv->connections; iter; iter = iter->next) { + conn = iter->data; + + g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (rebuild_on_connection_updated), list); + g_object_unref (conn); + } + g_slist_free (priv->connections); +} + +static void +nmt_edit_connection_list_rebuild (NmtEditConnectionList *list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + NmtNewtListbox *listbox; + GSList *iter, *next; + gboolean did_header = FALSE, did_vpn = FALSE; + NMEditorConnectionTypeData **types; + NMConnection *conn; + int i; + + free_connections (list); + priv->connections = nm_remote_settings_list_connections (nm_settings); + for (iter = priv->connections; iter; iter = next) { + conn = iter->data; + next = iter->next; + + if ( priv->connection_filter + && !priv->connection_filter (list, conn, priv->connection_filter_data)) { + priv->connections = g_slist_delete_link (priv->connections, iter); + continue; + } + + g_signal_connect (conn, NM_REMOTE_CONNECTION_UPDATED, + G_CALLBACK (rebuild_on_connection_updated), list); + g_signal_connect (conn, NM_REMOTE_CONNECTION_REMOVED, + G_CALLBACK (rebuild_on_connection_updated), list); + g_object_ref (iter->data); + } + priv->connections = g_slist_sort (priv->connections, sort_by_timestamp); + g_object_notify (G_OBJECT (list), "connections"); + g_object_notify (G_OBJECT (list), "num-connections"); + + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->edit), + priv->connections != NULL); + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->delete), + priv->connections != NULL); + + listbox = NMT_NEWT_LISTBOX (priv->listbox); + nmt_newt_listbox_clear (listbox); + + if (!priv->grouped) { + /* Just add the connections in order */ + for (iter = priv->connections; iter; iter = iter->next) { + conn = iter->data; + nmt_newt_listbox_append (listbox, nm_connection_get_id (conn), conn); + } + return; + } + + types = nm_editor_utils_get_connection_type_list (); + for (i = 0; types[i]; i++) { + if (types[i]->setting_type == NM_TYPE_SETTING_VPN) { + if (did_vpn) + continue; + did_vpn = TRUE; + } + + did_header = FALSE; + + for (iter = priv->connections; iter; iter = iter->next) { + NMSetting *setting; + char *indented; + + conn = iter->data; + setting = nm_connection_get_setting (conn, types[i]->setting_type); + if (!setting) + continue; + if (!nm_connection_is_type (conn, nm_setting_get_name (setting))) + continue; + + if (!did_header) { + nmt_newt_listbox_append (listbox, types[i]->name, NULL); + did_header = TRUE; + } + + indented = g_strdup_printf (" %s", nm_connection_get_id (conn)); + nmt_newt_listbox_append (listbox, indented, conn); + g_free (indented); + } + } +} + +static void +rebuild_on_new_connection (NMRemoteSettings *settings, + NMRemoteConnection *connection, + gpointer list) +{ + nmt_edit_connection_list_rebuild (list); +} + +static void +nmt_edit_connection_list_constructed (GObject *object) +{ + NmtEditConnectionList *list = NMT_EDIT_CONNECTION_LIST (object); + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + + if (priv->extra) + nmt_newt_button_box_add_widget_end (priv->buttons, priv->extra); + + g_signal_connect (nm_settings, NM_REMOTE_SETTINGS_NEW_CONNECTION, + G_CALLBACK (rebuild_on_new_connection), list); + + nmt_edit_connection_list_rebuild (list); + + G_OBJECT_CLASS (nmt_edit_connection_list_parent_class)->constructed (object); +} + +static void +add_clicked (NmtNewtButton *button, gpointer list) +{ + g_signal_emit (list, signals[ADD_CONNECTION], 0); +} + +static void +edit_clicked (NmtNewtButton *button, gpointer list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + NMConnection *connection; + + connection = nmt_newt_listbox_get_active_key (priv->listbox); + g_return_if_fail (connection != NULL); + + g_signal_emit (list, signals[EDIT_CONNECTION], 0, connection); +} + +static void +delete_clicked (NmtNewtButton *button, gpointer list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + NMRemoteConnection *connection; + + connection = nmt_newt_listbox_get_active_key (priv->listbox); + g_return_if_fail (connection != NULL); + + g_signal_emit (list, signals[REMOVE_CONNECTION], 0, connection); +} + +static void +listbox_activated (NmtNewtWidget *listbox, gpointer list) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (list); + + edit_clicked (NMT_NEWT_BUTTON (priv->edit), list); +} + +static void +nmt_edit_connection_list_finalize (GObject *object) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object); + + free_connections (NMT_EDIT_CONNECTION_LIST (object)); + g_clear_object (&priv->extra); + + G_OBJECT_CLASS (nmt_edit_connection_list_parent_class)->finalize (object); +} + +static void +nmt_edit_connection_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_GROUPED: + priv->grouped = g_value_get_boolean (value); + break; + case PROP_CONNECTION_FILTER: + priv->connection_filter = g_value_get_pointer (value); + break; + case PROP_CONNECTION_FILTER_DATA: + priv->connection_filter_data = g_value_get_pointer (value); + break; + case PROP_EXTRA_WIDGET: + priv->extra = g_value_get_object (value); + if (priv->extra) + g_object_ref_sink (priv->extra); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_edit_connection_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtEditConnectionListPrivate *priv = NMT_EDIT_CONNECTION_LIST_GET_PRIVATE (object); + GPtrArray *connections; + GSList *iter; + + switch (prop_id) { + case PROP_GROUPED: + g_value_set_boolean (value, priv->grouped); + break; + case PROP_CONNECTION_FILTER: + g_value_set_pointer (value, priv->connection_filter); + break; + case PROP_CONNECTION_FILTER_DATA: + g_value_set_pointer (value, priv->connection_filter_data); + break; + case PROP_EXTRA_WIDGET: + g_value_set_object (value, priv->extra); + break; + case PROP_CONNECTIONS: + connections = g_ptr_array_new_with_free_func (g_object_unref); + for (iter = priv->connections; iter; iter = iter->next) + g_ptr_array_add (connections, g_object_ref (iter->data)); + g_value_take_boxed (value, connections); + break; + case PROP_NUM_CONNECTIONS: + g_value_set_int (value, g_slist_length (priv->connections)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_edit_connection_list_class_init (NmtEditConnectionListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (list_class); + + g_type_class_add_private (list_class, sizeof (NmtEditConnectionListPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_edit_connection_list_constructed; + object_class->set_property = nmt_edit_connection_list_set_property; + object_class->get_property = nmt_edit_connection_list_get_property; + object_class->finalize = nmt_edit_connection_list_finalize; + + /* signals */ + + /** + * NmtEditConnectionList::add-connection: + * @list: the #NmtEditConnectionList + * + * Emitted when the user clicks the list's "Add" button. + */ + signals[ADD_CONNECTION] = + g_signal_new ("add-connection", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NmtEditConnectionListClass, add_connection), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + + /** + * NmtEditConnectionList::edit-connection: + * @list: the #NmtEditConnectionList + * @connection: the connection to edit + * + * Emitted when the user clicks the list's "Edit" button, or + * hits "Return" on the listbox. + */ + signals[EDIT_CONNECTION] = + g_signal_new ("edit-connection", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NmtEditConnectionListClass, edit_connection), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + NM_TYPE_CONNECTION); + + /** + * NmtEditConnectionList::remove-connection: + * @list: the #NmtEditConnectionList + * @connection: the connection to remove + * + * Emitted when the user clicks the list's "Delete" button. + */ + signals[REMOVE_CONNECTION] = + g_signal_new ("remove-connection", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NmtEditConnectionListClass, remove_connection), + NULL, NULL, NULL, + G_TYPE_NONE, 1, + NM_TYPE_CONNECTION); + + /* properties */ + + /** + * NmtEditConnectionList:grouped: + * + * If %TRUE, connections should be grouped by type, with headers + * indicating the types (as in the main connection list). If %FALSE, + * they will not be grouped (as in slave connection lists). + */ + g_object_class_install_property (object_class, PROP_GROUPED, + g_param_spec_boolean ("grouped", "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionListFilter: + * @list: the #NmtEditConnectionList + * @connection: an #NMConnection + * @user_data: the user data + * + * Decides whether @connection should be displayed in @list. + * + * Returns: %TRUE or %FALSE + */ + /** + * NmtEditConnectionList:connection-filter: + * + * A callback function for filtering which connections appear in + * the list. + */ + g_object_class_install_property (object_class, PROP_CONNECTION_FILTER, + g_param_spec_pointer ("connection-filter", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtEditConnectionList:connection-filter-data: + * + * Data for the #NmtEditConnectionList:connection-filter. + */ + g_object_class_install_property (object_class, PROP_CONNECTION_FILTER_DATA, + g_param_spec_pointer ("connection-filter-data", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionList:extra-widget: + * + * An extra button widget to display at the bottom of the button + * box. + */ + g_object_class_install_property (object_class, PROP_EXTRA_WIDGET, + g_param_spec_object ("extra-widget", "", "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionList:connections: + * + * The list of connections in the widget. + * + * Element-Type: #NMConnection + */ + g_object_class_install_property (object_class, PROP_CONNECTIONS, + g_param_spec_boxed ("connections", "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NmtEditConnectionList:num-connections: + * + * The number of connections in the widget. + */ + g_object_class_install_property (object_class, PROP_NUM_CONNECTIONS, + g_param_spec_int ("num-connections", "", "", + 0, G_MAXINT, 0, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-edit-connection-list.h b/tui/nmt-edit-connection-list.h new file mode 100644 index 0000000000..35157e94dc --- /dev/null +++ b/tui/nmt-edit-connection-list.h @@ -0,0 +1,61 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDIT_CONNECTION_LIST_H +#define NMT_EDIT_CONNECTION_LIST_H + +#include "nmt-newt.h" + +#include <nm-remote-connection.h> + +G_BEGIN_DECLS + +#define NMT_TYPE_EDIT_CONNECTION_LIST (nmt_edit_connection_list_get_type ()) +#define NMT_EDIT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionList)) +#define NMT_EDIT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListClass)) +#define NMT_IS_EDIT_CONNECTION_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDIT_CONNECTION_LIST)) +#define NMT_IS_EDIT_CONNECTION_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDIT_CONNECTION_LIST)) +#define NMT_EDIT_CONNECTION_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDIT_CONNECTION_LIST, NmtEditConnectionListClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtEditConnectionList; + +typedef struct { + NmtNewtGridClass parent; + + /* signals */ + void (*add_connection) (NmtEditConnectionList *list); + void (*edit_connection) (NmtEditConnectionList *list, + NMConnection *connection); + void (*remove_connection) (NmtEditConnectionList *list, + NMRemoteConnection *connection); +} NmtEditConnectionListClass; + +GType nmt_edit_connection_list_get_type (void); + +typedef gboolean (*NmtEditConnectionListFilter) (NmtEditConnectionList *list, + NMConnection *connection, + gpointer user_data); + +GSList *nmt_edit_connection_list_get_connections (NmtEditConnectionList *list); + +G_END_DECLS + +#endif /* NMT_EDIT_CONNECTION_LIST_H */ diff --git a/tui/nmt-editor-page.c b/tui/nmt-editor-page.c new file mode 100644 index 0000000000..549a86a6f5 --- /dev/null +++ b/tui/nmt-editor-page.c @@ -0,0 +1,227 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor-page: + * @short_description: An #NmtEditor "page" + * + * #NmtEditorPage is the abstract base class for #NmtEditor "pages". + * Note that despite the name, currently all "page" types except + * #NmtPageMain are actually displayed as collapsible sections, not + * separate tabs/forms. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-editor-page.h" + +G_DEFINE_ABSTRACT_TYPE (NmtEditorPage, nmt_editor_page, NMT_TYPE_PAGE_GRID) + +#define NMT_EDITOR_PAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR_PAGE, NmtEditorPagePrivate)) + +typedef struct { + char *title; + NmtNewtWidget *header_widget; + NMConnection *connection; + +} NmtEditorPagePrivate; + +enum { + PROP_0, + + PROP_CONNECTION, + PROP_TITLE, + + LAST_PROP +}; + +static void +nmt_editor_page_init (NmtEditorPage *page) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page); + + priv->header_widget = g_object_ref_sink (nmt_newt_separator_new ()); +} + +static void +nmt_editor_page_finalize (GObject *object) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (object); + + g_free (priv->title); + g_clear_object (&priv->header_widget); + g_clear_object (&priv->connection); + + G_OBJECT_CLASS (nmt_editor_page_parent_class)->finalize (object); +} + +/** + * nmt_editor_page_get_connection: + * @page: the #NmtEditorPage + * + * Gets the page's #NMConnection. + * + * Returns: (transfer none): the page's #NMConnection. + */ +NMConnection * +nmt_editor_page_get_connection (NmtEditorPage *page) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page); + + return priv->connection; +} + +/** + * nmt_editor_page_set_header_widget: + * @page: the #NmtEditorPage + * @widget: an #NmtNewtWidget + * + * Sets the page's header widget. When displayed as a subpage of + * #NmtPageMain, this widget will be put into the corresponding + * #NmtNewtSection's header. + * + * FIXME: for consistency, this should be a property as well. + */ +void +nmt_editor_page_set_header_widget (NmtEditorPage *page, + NmtNewtWidget *widget) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page); + + g_clear_object (&priv->header_widget); + + if (!widget) + widget = nmt_newt_separator_new (); + priv->header_widget = g_object_ref_sink (widget); +} + +/** + * nmt_editor_page_get_header_widget: + * @page: the #NmtEditorPage + * + * Gets the page's header widget. When displayed as a subpage of + * #NmtPageMain, this widget will be put into the corresponding + * #NmtNewtSection's header. + * + * Returns: (transfer none): the page's header widget. + */ +NmtNewtWidget * +nmt_editor_page_get_header_widget (NmtEditorPage *page) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page); + + return priv->header_widget; +} + +/** + * nmt_editor_page_get_title: + * @page: the #NmtEditorPage + * + * Gets the page's title. + * + * Returns: the page's title + */ +const char * +nmt_editor_page_get_title (NmtEditorPage *page) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (page); + + return priv->title; +} + +static void +nmt_editor_page_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CONNECTION: + priv->connection = g_value_dup_object (value); + break; + case PROP_TITLE: + priv->title = g_value_dup_string (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_page_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtEditorPagePrivate *priv = NMT_EDITOR_PAGE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_object (value, priv->connection); + break; + case PROP_TITLE: + g_value_set_string (value, priv->title); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_page_class_init (NmtEditorPageClass *page_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (page_class); + + g_type_class_add_private (page_class, sizeof (NmtEditorPagePrivate)); + + /* virtual methods */ + object_class->set_property = nmt_editor_page_set_property; + object_class->get_property = nmt_editor_page_get_property; + object_class->finalize = nmt_editor_page_finalize; + + /* properties */ + + /** + * NmtEditorPage:connection: + * + * The page's #NMConnection. + */ + g_object_class_install_property (object_class, PROP_CONNECTION, + g_param_spec_object ("connection", "", "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtEditorPage:title: + * + * The page's title. + */ + g_object_class_install_property (object_class, PROP_TITLE, + g_param_spec_string ("title", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-editor-page.h b/tui/nmt-editor-page.h new file mode 100644 index 0000000000..d9a97102ee --- /dev/null +++ b/tui/nmt-editor-page.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_PAGE_H +#define NMT_EDITOR_PAGE_H + +#include <nm-connection.h> + +#include "nmt-page-grid.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_EDITOR_PAGE (nmt_editor_page_get_type ()) +#define NMT_EDITOR_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR_PAGE, NmtEditorPage)) +#define NMT_EDITOR_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR_PAGE, NmtEditorPageClass)) +#define NMT_IS_EDITOR_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR_PAGE)) +#define NMT_IS_EDITOR_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR_PAGE)) +#define NMT_EDITOR_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR_PAGE, NmtEditorPageClass)) + +typedef struct { + NmtPageGrid parent; + +} NmtEditorPage; + +typedef struct { + NmtPageGridClass parent; + +} NmtEditorPageClass; + +GType nmt_editor_page_get_type (void); + +NMConnection *nmt_editor_page_get_connection (NmtEditorPage *page); + +void nmt_editor_page_set_header_widget (NmtEditorPage *page, + NmtNewtWidget *widget); +NmtNewtWidget *nmt_editor_page_get_header_widget (NmtEditorPage *page); + +const char *nmt_editor_page_get_title (NmtEditorPage *page); + +G_END_DECLS + +#endif /* NMT_EDITOR_PAGE_H */ diff --git a/tui/nmt-editor.c b/tui/nmt-editor.c new file mode 100644 index 0000000000..7aa2276ab7 --- /dev/null +++ b/tui/nmt-editor.c @@ -0,0 +1,330 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-editor + * @short_description: Connection editing form + * + * #NmtEditor is the top-level form for editing a connection. + */ + +#include "config.h" + +#include "nmt-editor.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <nm-utils.h> + +#include "nmtui.h" + +#include "nm-editor-utils.h" +#include "nmt-page-main.h" +#include "nmt-utils.h" + +G_DEFINE_TYPE (NmtEditor, nmt_editor, NMT_TYPE_NEWT_FORM) + +#define NMT_EDITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_EDITOR, NmtEditorPrivate)) + +typedef struct { + NMConnection *orig_connection; + NMConnection *edit_connection; + + NMEditorConnectionTypeData *type_data; + + NmtNewtWidget *ok, *cancel; + gboolean running; +} NmtEditorPrivate; + +enum { + PROP_0, + PROP_CONNECTION, + PROP_TYPE_DATA, + + LAST_PROP +}; + +/** + * nmt_editor_new: + * @connection: the #NMConnection to edit + * + * Creates a new #NmtEditor to edit @connection. + * + * Returns: a new #NmtEditor + */ +NmtNewtForm * +nmt_editor_new (NMConnection *connection) +{ + NMEditorConnectionTypeData *type_data; + + type_data = nm_editor_utils_get_connection_type_data (connection); + if (!type_data) { + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (connection); + if (s_con) { + nmt_newt_error_dialog (_("Could not create editor for connection '%s' of type '%s'."), + nm_connection_get_id (connection), + nm_setting_connection_get_connection_type (s_con)); + } else { + nmt_newt_error_dialog (_("Could not create editor for invalid connection '%s'."), + nm_connection_get_id (connection)); + } + + return NULL; + } + + return g_object_new (NMT_TYPE_EDITOR, + "connection", connection, + "type-data", type_data, + "title", _("Edit connection"), + "fullscreen", TRUE, + NULL); +} + +static void +nmt_editor_init (NmtEditor *entry) +{ +} + +static void +connection_updated (NMRemoteConnection *connection, + GError *error, + gpointer op) +{ + nmt_sync_op_complete_boolean (op, error == NULL, error); +} + +static void +connection_added (NMRemoteSettings *settings, + NMRemoteConnection *connection, + GError *error, + gpointer op) +{ + nmt_sync_op_complete_boolean (op, error == NULL, error); +} + +static void +save_connection_and_exit (NmtNewtButton *button, + gpointer user_data) +{ + NmtEditor *editor = user_data; + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (editor); + NmtSyncOp op; + GError *error = NULL; + + if (!nm_connection_replace_settings_from_connection (priv->orig_connection, + priv->edit_connection, + &error)) { + nmt_newt_error_dialog (_("Error saving connection: %s"), error->message); + g_error_free (error); + return; + } + + nmt_sync_op_init (&op); + if (NM_IS_REMOTE_CONNECTION (priv->orig_connection)) { + nm_remote_connection_commit_changes (NM_REMOTE_CONNECTION (priv->orig_connection), + connection_updated, &op); + if (!nmt_sync_op_wait_boolean (&op, &error)) { + nmt_newt_error_dialog (_("Unable to save connection: %s"), + error->message); + g_error_free (error); + return; + } + + /* Clear secrets so they don't lay around in memory; they'll get + * requested again anyway next time the connection is edited. + */ + nm_connection_clear_secrets (priv->orig_connection); + } else { + nm_remote_settings_add_connection (nm_settings, priv->orig_connection, + connection_added, &op); + if (!nmt_sync_op_wait_boolean (&op, &error)) { + nmt_newt_error_dialog (_("Unable to add new connection: %s"), + error->message); + g_error_free (error); + return; + } + } + + nmt_newt_form_quit (NMT_NEWT_FORM (editor)); +} + +static void +got_secrets (NMRemoteConnection *connection, + GHashTable *secrets, + GError *error, + gpointer op) +{ + nmt_sync_op_complete_pointer (op, secrets, error); +} + +static NMConnection * +build_edit_connection (NMConnection *orig_connection) +{ + NMConnection *edit_connection; + GHashTable *settings, *secrets; + GHashTableIter iter; + const char *setting_name; + NmtSyncOp op; + + edit_connection = nm_connection_duplicate (orig_connection); + + if (!NM_IS_REMOTE_CONNECTION (orig_connection)) + return edit_connection; + + settings = nm_connection_to_hash (orig_connection, NM_SETTING_HASH_FLAG_NO_SECRETS); + g_hash_table_iter_init (&iter, settings); + while (g_hash_table_iter_next (&iter, (gpointer) &setting_name, NULL)) { + nmt_sync_op_init (&op); + nm_remote_connection_get_secrets (NM_REMOTE_CONNECTION (orig_connection), + setting_name, got_secrets, &op); + /* FIXME: error handling */ + secrets = nmt_sync_op_wait_pointer (&op, NULL); + if (secrets) + nm_connection_update_secrets (edit_connection, setting_name, secrets, NULL); + } + g_hash_table_unref (settings); + + return edit_connection; +} + +static void +nmt_editor_constructed (GObject *object) +{ + NmtEditor *editor = NMT_EDITOR (object); + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (editor); + NmtNewtWidget *vbox, *buttons, *page; + + if (G_OBJECT_CLASS (nmt_editor_parent_class)->constructed) + G_OBJECT_CLASS (nmt_editor_parent_class)->constructed (object); + + priv->edit_connection = build_edit_connection (priv->orig_connection); + + vbox = nmt_newt_grid_new (); + + page = nmt_page_main_new (priv->edit_connection, priv->type_data); + nmt_newt_grid_add (NMT_NEWT_GRID (vbox), page, 0, 0); + + buttons = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add (NMT_NEWT_GRID (vbox), buttons, 0, 1); + nmt_newt_widget_set_padding (buttons, 0, 1, 0, 0); + + priv->cancel = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (buttons), _("Cancel")); + nmt_newt_widget_set_exit_on_activate (priv->cancel, TRUE); + + priv->ok = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (buttons), _("OK")); + g_signal_connect (priv->ok, "clicked", G_CALLBACK (save_connection_and_exit), editor); + g_object_bind_property (page, "valid", + priv->ok, "sensitive", + G_BINDING_SYNC_CREATE); + + nmt_newt_form_set_content (NMT_NEWT_FORM (editor), vbox); +} + +static void +nmt_editor_finalize (GObject *object) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object); + + g_clear_object (&priv->orig_connection); + g_clear_object (&priv->edit_connection); + + g_clear_object (&priv->ok); + g_clear_object (&priv->cancel); + + G_OBJECT_CLASS (nmt_editor_parent_class)->finalize (object); +} + +static void +nmt_editor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CONNECTION: + priv->orig_connection = g_value_dup_object (value); + break; + case PROP_TYPE_DATA: + priv->type_data = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CONNECTION: + g_value_set_object (value, priv->orig_connection); + break; + case PROP_TYPE_DATA: + g_value_set_pointer (value, priv->type_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_editor_class_init (NmtEditorClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtEditorPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_editor_constructed; + object_class->set_property = nmt_editor_set_property; + object_class->get_property = nmt_editor_get_property; + object_class->finalize = nmt_editor_finalize; + + /** + * NmtEditor:connection: + * + * The connection being edited. + */ + g_object_class_install_property (object_class, PROP_CONNECTION, + g_param_spec_object ("connection", "", "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtEditor:type-data: + * + * The #NmEditorConnectionTypeData for #NmtEditor:connection. + */ + g_object_class_install_property (object_class, PROP_TYPE_DATA, + g_param_spec_pointer ("type-data", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-editor.h b/tui/nmt-editor.h new file mode 100644 index 0000000000..a991a7666a --- /dev/null +++ b/tui/nmt-editor.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_EDITOR_H +#define NMT_EDITOR_H + +#include <nm-connection.h> + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_EDITOR (nmt_editor_get_type ()) +#define NMT_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_EDITOR, NmtEditor)) +#define NMT_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_EDITOR, NmtEditorClass)) +#define NMT_IS_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_EDITOR)) +#define NMT_IS_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_EDITOR)) +#define NMT_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_EDITOR, NmtEditorClass)) + +typedef struct { + NmtNewtForm parent; + +} NmtEditor; + +typedef struct { + NmtNewtFormClass parent; + +} NmtEditorClass; + +GType nmt_editor_get_type (void); + +NmtNewtForm *nmt_editor_new (NMConnection *connection); + +G_END_DECLS + +#endif /* NMT_EDITOR_H */ diff --git a/tui/nmt-ip-entry.c b/tui/nmt-ip-entry.c new file mode 100644 index 0000000000..85a29fcce5 --- /dev/null +++ b/tui/nmt-ip-entry.c @@ -0,0 +1,265 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-ip-entry + * @short_description: #NmtNewtEntry for IP address entry + * + * #NmtIPEntry is an #NmtNewtEntry for entering IP addresses, or IP + * address/prefix combination. It will only allow typing characters + * that are valid in an IP address, and will set its + * #NmtNewtWidget:valid property depending on whether it currently + * contains a valid IP address. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include <glib/gi18n-lib.h> + +#include "nmt-ip-entry.h" + +G_DEFINE_TYPE (NmtIPEntry, nmt_ip_entry, NMT_TYPE_NEWT_ENTRY) + +#define NMT_IP_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_IP_ENTRY, NmtIPEntryPrivate)) + +typedef struct { + int family; + gboolean prefix; + gboolean optional; + +} NmtIPEntryPrivate; + +enum { + PROP_0, + PROP_FAMILY, + PROP_PREFIX, + PROP_OPTIONAL, + + LAST_PROP +}; + +/** + * nmt_ip_entry_new: + * @width: the width of the entry + * @family: the IP address family. Eg, %AF_INET + * @prefix: whether to require a trailing "/prefix" + * @optional: whether the address is optional + * + * Creates a new #NmtIPEntry, to accept IP addresses in the indicated + * @family, or (if @prefix is %TRUE), to accept IP address/prefix combos. + * + * If @optional is %TRUE then the address is considered optional, and + * so will still be #NmtNewtWidget:valid even when it is empty. If + * @optional is %FALSE, the entry will be invalid when it is empty. + */ +NmtNewtWidget * +nmt_ip_entry_new (int width, + int family, + gboolean prefix, + gboolean optional) +{ + return g_object_new (NMT_TYPE_IP_ENTRY, + "width", width, + "family", family, + "prefix", prefix, + "optional", optional, + NULL); +} + +static gboolean +ip_entry_filter (NmtNewtEntry *entry, + const char *text, + int ch, + int position, + gpointer user_data) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (entry); + const char *slash; + gboolean inaddr; + + if (g_ascii_isdigit (ch)) + return TRUE; + + slash = strchr (text, '/'); + if (ch == '/') + return priv->prefix && slash == NULL; + + inaddr = !slash || (position <= (slash - text)); + + if (priv->family == AF_INET) { + if (ch == '.') + return inaddr; + else + return FALSE; + } else if (priv->family == AF_INET6) { + if (g_ascii_isxdigit (ch) || ch == ':') + return inaddr; + else + return FALSE; + } else + g_return_val_if_reached (FALSE); +} + +static gboolean +ip_entry_validate (NmtNewtEntry *entry, + const char *text, + gpointer user_data) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (entry); + guchar buf[16]; + guint32 prefix; + const char *slash; + char *addrstr, *end; + gboolean valid; + + if (!*text) + return priv->optional; + + slash = strchr (text, '/'); + + if (slash) { + if (!priv->prefix) + return FALSE; + addrstr = g_strndup (text, slash - text); + } else + addrstr = g_strdup (text); + valid = (inet_pton (priv->family, addrstr, buf) == 1); + g_free (addrstr); + + if (!valid) + return FALSE; + + if (slash) { + prefix = strtoul (slash + 1, &end, 10); + if ( *end + || prefix == 0 + || (priv->family == AF_INET && prefix > 32) + || (priv->family == AF_INET6 && prefix > 128)) + valid = FALSE; + } + + return valid; +} + +static void +nmt_ip_entry_init (NmtIPEntry *entry) +{ + nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), ip_entry_filter, NULL); + nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), ip_entry_validate, NULL); +} + +static void +nmt_ip_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + priv->family = g_value_get_int (value); + break; + case PROP_PREFIX: + priv->prefix = g_value_get_boolean (value); + break; + case PROP_OPTIONAL: + priv->optional = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_ip_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtIPEntryPrivate *priv = NMT_IP_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int (value, priv->family); + break; + case PROP_PREFIX: + g_value_set_boolean (value, priv->prefix); + break; + case PROP_OPTIONAL: + g_value_set_boolean (value, priv->optional); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_ip_entry_class_init (NmtIPEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtIPEntryPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_ip_entry_set_property; + object_class->get_property = nmt_ip_entry_get_property; + + /** + * NmtIPEntry:family: + * + * The address family. Eg, %AF_INET + */ + g_object_class_install_property (object_class, PROP_FAMILY, + g_param_spec_int ("family", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtIPEntry:prefix: + * + * If %TRUE, the entry accepts address/prefix combinations. If + * %FALSE it accepts just addresses. + */ + g_object_class_install_property (object_class, PROP_PREFIX, + g_param_spec_boolean ("prefix", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtIPEntry:optional: + * + * If %TRUE, the entry will be #NmtNewtWidget:valid when it is + * empty. If %FALSE, it will only be valid when it contains a + * valid address or address/prefix. + */ + g_object_class_install_property (object_class, PROP_OPTIONAL, + g_param_spec_boolean ("optional", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-ip-entry.h b/tui/nmt-ip-entry.h new file mode 100644 index 0000000000..ceb355efcf --- /dev/null +++ b/tui/nmt-ip-entry.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_IP_ENTRY_H +#define NMT_IP_ENTRY_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_IP_ENTRY (nmt_ip_entry_get_type ()) +#define NMT_IP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_IP_ENTRY, NmtIPEntry)) +#define NMT_IP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_IP_ENTRY, NmtIPEntryClass)) +#define NMT_IS_IP_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_IP_ENTRY)) +#define NMT_IS_IP_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_IP_ENTRY)) +#define NMT_IP_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_IP_ENTRY, NmtIPEntryClass)) + +typedef struct { + NmtNewtEntry parent; + +} NmtIPEntry; + +typedef struct { + NmtNewtEntryClass parent; + +} NmtIPEntryClass; + +GType nmt_ip_entry_get_type (void); + +NmtNewtWidget *nmt_ip_entry_new (int width, + int family, + gboolean prefix, + gboolean optional); + +G_END_DECLS + +#endif /* NMT_IP_ENTRY_H */ diff --git a/tui/nmt-mac-entry.c b/tui/nmt-mac-entry.c new file mode 100644 index 0000000000..d76c097f20 --- /dev/null +++ b/tui/nmt-mac-entry.c @@ -0,0 +1,217 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-mac-entry + * @short_description: #NmtNewtEntry for hardware address entry + * + * #NmtMacEntry is an #NmtNewtEntry for entering hardware addresses. + * It will only allow typing characters that are valid in a hardware + * address, and will set its #NmtNewtWidget:valid property depending + * on whether it currently contains a valid hardware address. + */ + +#include "config.h" + +#include <dbus/dbus-glib.h> +#include <nm-utils.h> + +#include "nmt-mac-entry.h" + +G_DEFINE_TYPE (NmtMacEntry, nmt_mac_entry, NMT_TYPE_NEWT_ENTRY) + +#define NMT_MAC_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_MAC_ENTRY, NmtMacEntryPrivate)) + +typedef struct { + int mac_length; + int mac_str_length; + +} NmtMacEntryPrivate; + +enum { + PROP_0, + PROP_MAC_LENGTH, + PROP_MAC_ADDRESS, + + LAST_PROP +}; + +/** + * nmt_mac_entry_new: + * @width: the width in characters of the entry + * @mac_length: the length in bytes of the hardware address + * (either %ETH_ALEN or %INFINIBAND_ALEN) + * + * Creates a new #NmtMacEntry. + * + * Returns: a new #NmtMacEntry. + */ +NmtNewtWidget * +nmt_mac_entry_new (int width, + int mac_length) +{ + return g_object_new (NMT_TYPE_MAC_ENTRY, + "width", width, + "mac-length", mac_length, + NULL); +} + +static gboolean +mac_filter (NmtNewtEntry *entry, + const char *text, + int ch, + int position, + gpointer user_data) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry); + + if (position > priv->mac_str_length) + return FALSE; + + return g_ascii_isxdigit (ch) || ch == ':'; +} + +static gboolean +mac_validator (NmtNewtEntry *entry, + const char *text, + gpointer user_data) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (entry); + const char *p; + + if (!*text) + return TRUE; + + p = text; + while ( g_ascii_isxdigit (p[0]) + && g_ascii_isxdigit (p[1]) + && p[2] == ':') + p += 3; + + if ( !g_ascii_isxdigit (p[0]) + || !g_ascii_isxdigit (p[1])) + return FALSE; + p += 2; + + if (!*p) + return (p - text == priv->mac_str_length); + + if (g_ascii_isxdigit (p[0]) && !p[1]) { + char *fixed = g_strdup_printf ("%.*s:%c", (int)(p - text), text, *p); + + g_object_set (G_OBJECT (entry), "text", fixed, NULL); + return TRUE; + } + + return FALSE; +} + +static void +nmt_mac_entry_init (NmtMacEntry *entry) +{ + nmt_newt_entry_set_filter (NMT_NEWT_ENTRY (entry), mac_filter, NULL); + nmt_newt_entry_set_validator (NMT_NEWT_ENTRY (entry), mac_validator, NULL); +} + +static void +nmt_mac_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (object); + GByteArray *addr; + char *addr_str; + + switch (prop_id) { + case PROP_MAC_LENGTH: + priv->mac_length = g_value_get_int (value); + priv->mac_str_length = priv->mac_length * 3 - 1; + break; + case PROP_MAC_ADDRESS: + addr = g_value_get_boxed (value); + if (addr) { + addr_str = nm_utils_hwaddr_ntoa_len (addr->data, addr->len); + nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), addr_str); + g_free (addr_str); + } else + nmt_newt_entry_set_text (NMT_NEWT_ENTRY (object), ""); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_mac_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtMacEntryPrivate *priv = NMT_MAC_ENTRY_GET_PRIVATE (object); + GByteArray *addr; + + switch (prop_id) { + case PROP_MAC_LENGTH: + g_value_set_int (value, priv->mac_length); + break; + case PROP_MAC_ADDRESS: + addr = nm_utils_hwaddr_atoba (nmt_newt_entry_get_text (NMT_NEWT_ENTRY (object)), ARPHRD_ETHER); + g_value_take_boxed (value, addr); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_mac_entry_class_init (NmtMacEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtMacEntryPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_mac_entry_set_property; + object_class->get_property = nmt_mac_entry_get_property; + + /** + * NmtMacEntry:mac-length: + * + * The length in bytes of the hardware address type the entry + * accepts: either %ETH_ALEN or %INFINIBAND_ALEN. + */ + g_object_class_install_property (object_class, PROP_MAC_LENGTH, + g_param_spec_int ("mac-length", "", "", + 0, INFINIBAND_ALEN, ETH_ALEN, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtMacEntry:mac-address: + * + * The MAC address, in binary (in the same format used by the various + * #NMSetting "mac-address" properties). + */ + g_object_class_install_property (object_class, PROP_MAC_ADDRESS, + g_param_spec_boxed ("mac-address", "", "", + DBUS_TYPE_G_UCHAR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-mac-entry.h b/tui/nmt-mac-entry.h new file mode 100644 index 0000000000..33a3232986 --- /dev/null +++ b/tui/nmt-mac-entry.h @@ -0,0 +1,54 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_MAC_ENTRY_H +#define NMT_MAC_ENTRY_H + +#include <linux/if_ether.h> +#include <linux/if_infiniband.h> +#include <linux/if_arp.h> + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_MAC_ENTRY (nmt_mac_entry_get_type ()) +#define NMT_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntry)) +#define NMT_MAC_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass)) +#define NMT_IS_MAC_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_MAC_ENTRY)) +#define NMT_IS_MAC_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_MAC_ENTRY)) +#define NMT_MAC_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_MAC_ENTRY, NmtMacEntryClass)) + +typedef struct { + NmtNewtEntry parent; + +} NmtMacEntry; + +typedef struct { + NmtNewtEntryClass parent; + +} NmtMacEntryClass; + +GType nmt_mac_entry_get_type (void); + +NmtNewtWidget *nmt_mac_entry_new (int width, + int mac_length); + +G_END_DECLS + +#endif /* NMT_MAC_ENTRY_H */ diff --git a/tui/nmt-mtu-entry.c b/tui/nmt-mtu-entry.c new file mode 100644 index 0000000000..da3746ccbb --- /dev/null +++ b/tui/nmt-mtu-entry.c @@ -0,0 +1,191 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-mtu-entry + * @short_description: #NmtNewtEntry for MTU entry + * + * #NmtMtuEntry is an #NmtNewtEntry for entering MTU values. It will + * only allow typing numeric characters, and will set its + * #NmtNewtWidget:valid property depending on whether it currently + * contains a valid MTU. + * + * The entry also has an attached #NmtNewtLabel. When the entry value + * is "0", the label will read "(default)". Otherwise it reads "bytes", + * indicating the units used by the entry. + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib/gi18n-lib.h> + +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE (NmtMtuEntry, nmt_mtu_entry, NMT_TYPE_NEWT_GRID) + +#define NMT_MTU_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_MTU_ENTRY, NmtMtuEntryPrivate)) + +typedef struct { + int mtu; + + NmtNewtEntry *entry; + NmtNewtLabel *label; + +} NmtMtuEntryPrivate; + +enum { + PROP_0, + PROP_MTU, + + LAST_PROP +}; + +/** + * nmt_mtu_entry_new: + * + * Creates a new #NmtMtuEntry + * + * Returns: a new #NmtMtuEntry + */ +NmtNewtWidget * +nmt_mtu_entry_new (void) +{ + return g_object_new (NMT_TYPE_MTU_ENTRY, NULL); +} + +static gboolean +mtu_validator (NmtNewtEntry *entry, + const char *text, + gpointer user_data) +{ + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (user_data); + + if (*text && !atoi (text)) { + nmt_newt_entry_set_text (entry, ""); + text = ""; + } + + if (!*text) + nmt_newt_label_set_text (priv->label, _("(default)")); + else + nmt_newt_label_set_text (priv->label, _("bytes")); + + return TRUE; +} + +static gboolean +mtu_transform_to_text (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + int mtu = g_value_get_int (source_value); + + if (mtu) + g_value_transform (source_value, target_value); + else + g_value_set_string (target_value, ""); + return TRUE; +} + +static void +nmt_mtu_entry_init (NmtMtuEntry *entry) +{ + + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (entry); + NmtNewtGrid *grid = NMT_NEWT_GRID (entry); + NmtNewtWidget *real_entry, *label; + + real_entry = nmt_newt_entry_numeric_new (10, 0, 65535); + priv->entry = NMT_NEWT_ENTRY (real_entry); + + label = nmt_newt_label_new (_("bytes")); + priv->label = NMT_NEWT_LABEL (label); + + nmt_newt_grid_add (grid, real_entry, 0, 0); + nmt_newt_grid_add (grid, label, 1, 0); + nmt_newt_widget_set_padding (label, 1, 0, 0, 0); + + nmt_newt_entry_set_validator (priv->entry, mtu_validator, entry); + g_object_bind_property_full (entry, "mtu", real_entry, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + mtu_transform_to_text, + NULL, + NULL, NULL); +} + +static void +nmt_mtu_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MTU: + priv->mtu = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_mtu_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtMtuEntryPrivate *priv = NMT_MTU_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MTU: + g_value_set_int (value, priv->mtu); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_mtu_entry_class_init (NmtMtuEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtMtuEntryPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_mtu_entry_set_property; + object_class->get_property = nmt_mtu_entry_get_property; + + /** + * NmtMtuEntry:mtu: + * + * The contents of the entry, as a number. + */ + g_object_class_install_property (object_class, PROP_MTU, + g_param_spec_int ("mtu", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-mtu-entry.h b/tui/nmt-mtu-entry.h new file mode 100644 index 0000000000..dfe416d0e7 --- /dev/null +++ b/tui/nmt-mtu-entry.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_MTU_ENTRY_H +#define NMT_MTU_ENTRY_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_MTU_ENTRY (nmt_mtu_entry_get_type ()) +#define NMT_MTU_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntry)) +#define NMT_MTU_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass)) +#define NMT_IS_MTU_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_MTU_ENTRY)) +#define NMT_IS_MTU_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_MTU_ENTRY)) +#define NMT_MTU_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_MTU_ENTRY, NmtMtuEntryClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtMtuEntry; + +typedef struct { + NmtNewtGridClass parent; + +} NmtMtuEntryClass; + +GType nmt_mtu_entry_get_type (void); + +NmtNewtWidget *nmt_mtu_entry_new (void); + +G_END_DECLS + +#endif /* NMT_MTU_ENTRY_H */ diff --git a/tui/nmt-page-bond.c b/tui/nmt-page-bond.c new file mode 100644 index 0000000000..55a6ffeebb --- /dev/null +++ b/tui/nmt-page-bond.c @@ -0,0 +1,436 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-bond + * @short_description: The editor page for Bond connections + * + * Note that this is fairly different from most of the other editor + * pages, because #NMSettingBond doesn't have properties, so we + * can't just use #GBinding. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-bond.h" + +#include "nmt-address-list.h" +#include "nmt-slave-list.h" + +G_DEFINE_TYPE (NmtPageBond, nmt_page_bond, NMT_TYPE_PAGE_DEVICE) + +#define NMT_PAGE_BOND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_BOND, NmtPageBondPrivate)) + +typedef enum { + NMT_PAGE_BOND_MONITORING_UNKNOWN = -1, + NMT_PAGE_BOND_MONITORING_MII = 0, + NMT_PAGE_BOND_MONITORING_ARP = 1, +} NmtPageBondMonitoringMode; + +typedef struct { + NmtSlaveList *slaves; + + NmtNewtPopup *mode; + NmtNewtEntry *primary; + NmtNewtPopup *monitoring; + NmtNewtEntry *miimon; + NmtNewtEntry *updelay; + NmtNewtEntry *downdelay; + NmtNewtEntry *arp_interval; + NmtAddressList *arp_ip_target; + + NmtPageBondMonitoringMode monitoring_mode; + + NMSettingBond *s_bond; + GType slave_type; + gboolean updating; +} NmtPageBondPrivate; + +NmtNewtWidget * +nmt_page_bond_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_BOND, + "connection", conn, + "title", _("BOND"), + "device-entry", deventry, + NULL); +} + +static void +nmt_page_bond_init (NmtPageBond *bond) +{ + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + + priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_UNKNOWN; + priv->slave_type = G_TYPE_NONE; +} + +static NmtNewtPopupEntry bond_mode[] = { + { N_("Round-robin"), "balance-rr" }, + { N_("Active Backup"), "active-backup" }, + { N_("XOR"), "balance-xor" }, + { N_("Broadcast"), "broadcast" }, + { N_("802.3ad"), "802.3ad" }, + { N_("Adaptive Transmit Load Balancing (tlb)"), "balance-tlb" }, + { N_("Adaptive Load Balancing (alb)"), "balance-alb" }, + { NULL, NULL } +}; + +/* NB: the ordering/numbering here corresponds to NmtPageBondMonitoringMode */ +static NmtNewtPopupEntry bond_monitoring[] = { + { N_("MII (recommended)"), "mii" }, + { N_("ARP"), "arp" }, + { NULL, NULL } +}; + +static void +bond_options_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NMSettingBond *s_bond = NM_SETTING_BOND (object); + NmtPageBond *bond = NMT_PAGE_BOND (user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + const char *val; + char **ips; + + if (priv->updating) + return; + + priv->updating = TRUE; + + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE); + nmt_newt_popup_set_active_id (priv->mode, val); + + if (!strcmp (val, "active-backup")) { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_PRIMARY); + nmt_newt_entry_set_text (priv->primary, val); + } else + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), FALSE); + + if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_UNKNOWN) { + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + if (val && strcmp (val, "0") != 0) + priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_ARP; + else + priv->monitoring_mode = NMT_PAGE_BOND_MONITORING_MII; + } + nmt_newt_popup_set_active (priv->monitoring, priv->monitoring_mode); + + if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_MII) { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MIIMON); + nmt_newt_entry_set_text (priv->miimon, val); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_UPDELAY); + nmt_newt_entry_set_text (priv->updelay, val); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_DOWNDELAY); + nmt_newt_entry_set_text (priv->downdelay, val); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), FALSE); + } else { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_INTERVAL); + nmt_newt_entry_set_text (priv->arp_interval, val); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), TRUE); + val = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET); + ips = g_strsplit (val, ",", -1); + g_object_set (G_OBJECT (priv->arp_ip_target), + "string", ips, + NULL); + g_strfreev (ips); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), FALSE); + } + + priv->updating = FALSE; +} + +static void +slaves_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtPageBond *bond = NMT_PAGE_BOND (user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + GPtrArray *slaves; + + g_object_get (object, "connections", &slaves, NULL); + if (slaves->len == 0) { + if (priv->slave_type == G_TYPE_NONE) + return; + priv->slave_type = G_TYPE_NONE; + } else { + NMConnection *slave = slaves->pdata[0]; + + if (priv->slave_type != G_TYPE_NONE) + return; + + if (nm_connection_is_type (slave, NM_SETTING_INFINIBAND_SETTING_NAME)) + priv->slave_type = NM_TYPE_SETTING_INFINIBAND; + else + priv->slave_type = NM_TYPE_SETTING_WIRED; + } + + if (priv->slave_type == NM_TYPE_SETTING_INFINIBAND) { + nmt_newt_popup_set_active_id (priv->mode, "active-backup"); + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->mode), FALSE); + } else + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->mode), TRUE); +} + +#define WIDGET_CHANGED_FUNC(widget, func, option) \ +static void \ +widget ## _widget_changed (GObject *object, \ + GParamSpec *pspec, \ + gpointer user_data) \ +{ \ + NmtPageBond *bond = NMT_PAGE_BOND (user_data); \ + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); \ + \ + if (priv->updating) \ + return; \ + \ + priv->updating = TRUE; \ + nm_setting_bond_add_option (priv->s_bond, option, func (priv->widget)); \ + priv->updating = FALSE; \ +} + +WIDGET_CHANGED_FUNC (primary, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_PRIMARY) +WIDGET_CHANGED_FUNC (miimon, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_MIIMON) +WIDGET_CHANGED_FUNC (updelay, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_UPDELAY) +WIDGET_CHANGED_FUNC (downdelay, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_DOWNDELAY) +WIDGET_CHANGED_FUNC (arp_interval, nmt_newt_entry_get_text, NM_SETTING_BOND_OPTION_ARP_INTERVAL) + +static void +mode_widget_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtPageBond *bond = NMT_PAGE_BOND (user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + const char *mode; + + if (priv->updating) + return; + + mode = nmt_newt_popup_get_active_id (priv->mode); + priv->updating = TRUE; + nm_setting_bond_add_option (priv->s_bond, NM_SETTING_BOND_OPTION_MODE, mode); + priv->updating = FALSE; + + if (!strcmp (mode, "balance-tlb") || !strcmp (mode, "balance-alb")) { + nmt_newt_popup_set_active (priv->monitoring, NMT_PAGE_BOND_MONITORING_MII); + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->monitoring), FALSE); + } else + nmt_newt_component_set_sensitive (NMT_NEWT_COMPONENT (priv->monitoring), TRUE); + + if (!strcmp (mode, "active-backup")) + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), TRUE); + else + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->primary), FALSE); +} + +static void +monitoring_widget_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtPageBond *bond = NMT_PAGE_BOND (user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + + if (priv->updating) + return; + + priv->monitoring_mode = nmt_newt_popup_get_active (priv->monitoring); + if (priv->monitoring_mode == NMT_PAGE_BOND_MONITORING_MII) { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), TRUE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), TRUE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), TRUE); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), FALSE); + } else { + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_interval), TRUE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->arp_ip_target), TRUE); + + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->miimon), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->updelay), FALSE); + nmt_newt_widget_set_visible (NMT_NEWT_WIDGET (priv->downdelay), FALSE); + } +} + +static void +arp_ip_target_widget_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtPageBond *bond = NMT_PAGE_BOND (user_data); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + char **ips, *target; + + if (priv->updating) + return; + + g_object_get (G_OBJECT (priv->arp_ip_target), + "strings", &ips, + NULL); + target = g_strjoinv (",", ips); + + priv->updating = TRUE; + nm_setting_bond_add_option (priv->s_bond, NM_SETTING_BOND_OPTION_ARP_IP_TARGET, target); + priv->updating = FALSE; + + g_free (target); + g_strfreev (ips); +} + +static gboolean +bond_connection_type_filter (GType connection_type, + gpointer user_data) +{ + NmtPageBond *bond = user_data; + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + + if ( priv->slave_type != NM_TYPE_SETTING_WIRED + && connection_type == NM_TYPE_SETTING_INFINIBAND) + return TRUE; + if ( priv->slave_type != NM_TYPE_SETTING_INFINIBAND + && connection_type == NM_TYPE_SETTING_WIRED) + return TRUE; + + return FALSE; +} + +static void +nmt_page_bond_constructed (GObject *object) +{ + NmtPageBond *bond = NMT_PAGE_BOND (object); + NmtPageBondPrivate *priv = NMT_PAGE_BOND_GET_PRIVATE (bond); + NmtDeviceEntry *deventry; + NmtPageGrid *grid; + NMSettingBond *s_bond; + NmtNewtWidget *widget, *label; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (bond)); + s_bond = nm_connection_get_setting_bond (conn); + if (!s_bond) { + nm_connection_add_setting (conn, nm_setting_bond_new ()); + s_bond = nm_connection_get_setting_bond (conn); + } + priv->s_bond = s_bond; + + deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object)); + g_object_bind_property (s_bond, NM_SETTING_BOND_INTERFACE_NAME, + deventry, "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + grid = NMT_PAGE_GRID (bond); + + widget = nmt_newt_separator_new (); + nmt_page_grid_append (grid, _("Slaves"), widget, NULL); + nmt_page_grid_set_row_flags (grid, widget, NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT); + + widget = nmt_slave_list_new (conn, bond_connection_type_filter, bond); + g_signal_connect (widget, "notify::connections", + G_CALLBACK (slaves_changed), bond); + nmt_page_grid_append (grid, NULL, widget, NULL); + priv->slaves = NMT_SLAVE_LIST (widget); + + widget = nmt_newt_popup_new (bond_mode); + g_signal_connect (widget, "notify::active-id", + G_CALLBACK (mode_widget_changed), bond); + nmt_page_grid_append (grid, _("Mode"), widget, NULL); + priv->mode = NMT_NEWT_POPUP (widget); + + widget = nmt_newt_entry_new (40, 0); + g_signal_connect (widget, "notify::text", + G_CALLBACK (primary_widget_changed), bond); + nmt_page_grid_append (grid, _("Primary"), widget, NULL); + priv->primary = NMT_NEWT_ENTRY (widget); + + widget = nmt_newt_popup_new (bond_monitoring); + g_signal_connect (widget, "notify::active", + G_CALLBACK (monitoring_widget_changed), bond); + nmt_page_grid_append (grid, _("Link monitoring"), widget, NULL); + priv->monitoring = NMT_NEWT_POPUP (widget); + + widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT); + g_signal_connect (widget, "notify::text", + G_CALLBACK (miimon_widget_changed), bond); + label = nmt_newt_label_new (C_("milliseconds", "ms")); + nmt_page_grid_append (grid, _("Monitoring frequency"), widget, label); + priv->miimon = NMT_NEWT_ENTRY (widget); + + widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT); + g_signal_connect (widget, "notify::text", + G_CALLBACK (updelay_widget_changed), bond); + label = nmt_newt_label_new (C_("milliseconds", "ms")); + nmt_page_grid_append (grid, _("Link up delay"), widget, label); + priv->updelay = NMT_NEWT_ENTRY (widget); + + widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT); + g_signal_connect (widget, "notify::text", + G_CALLBACK (downdelay_widget_changed), bond); + label = nmt_newt_label_new (C_("milliseconds", "ms")); + nmt_page_grid_append (grid, _("Link down delay"), widget, label); + priv->downdelay = NMT_NEWT_ENTRY (widget); + + widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT); + g_signal_connect (widget, "notify::text", + G_CALLBACK (arp_interval_widget_changed), bond); + label = nmt_newt_label_new (C_("milliseconds", "ms")); + nmt_page_grid_append (grid, _("Monitoring frequency"), widget, label); + priv->arp_interval = NMT_NEWT_ENTRY (widget); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4); + g_signal_connect (widget, "notify::strings", + G_CALLBACK (arp_ip_target_widget_changed), bond); + nmt_page_grid_append (grid, _("ARP targets"), widget, NULL); + priv->arp_ip_target = NMT_ADDRESS_LIST (widget); + + g_signal_connect (s_bond, "notify::" NM_SETTING_BOND_OPTIONS, + G_CALLBACK (bond_options_changed), bond); + bond_options_changed (G_OBJECT (s_bond), NULL, bond); + slaves_changed (G_OBJECT (priv->slaves), NULL, bond); + + G_OBJECT_CLASS (nmt_page_bond_parent_class)->constructed (object); +} + +static void +nmt_page_bond_class_init (NmtPageBondClass *bond_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (bond_class); + + g_type_class_add_private (bond_class, sizeof (NmtPageBondPrivate)); + + object_class->constructed = nmt_page_bond_constructed; +} diff --git a/tui/nmt-page-bond.h b/tui/nmt-page-bond.h new file mode 100644 index 0000000000..c8e69dd5ee --- /dev/null +++ b/tui/nmt-page-bond.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_BOND_H +#define NMT_PAGE_BOND_H + +#include "nmt-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_BOND (nmt_page_bond_get_type ()) +#define NMT_PAGE_BOND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BOND, NmtPageBond)) +#define NMT_PAGE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BOND, NmtPageBondClass)) +#define NMT_IS_PAGE_BOND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_BOND)) +#define NMT_IS_PAGE_BOND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_BOND)) +#define NMT_PAGE_BOND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_BOND, NmtPageBondClass)) + +typedef struct { + NmtPageDevice parent; + +} NmtPageBond; + +typedef struct { + NmtPageDeviceClass parent; + +} NmtPageBondClass; + +GType nmt_page_bond_get_type (void); + +NmtNewtWidget *nmt_page_bond_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_BOND_H */ diff --git a/tui/nmt-page-bridge-port.c b/tui/nmt-page-bridge-port.c new file mode 100644 index 0000000000..ab7a78887f --- /dev/null +++ b/tui/nmt-page-bridge-port.c @@ -0,0 +1,92 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-bridge-port + * @short_description: The editor page for Bridge ports + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-bridge-port.h" + +G_DEFINE_TYPE (NmtPageBridgePort, nmt_page_bridge_port, NMT_TYPE_EDITOR_PAGE) + +NmtNewtWidget * +nmt_page_bridge_port_new (NMConnection *conn) +{ + return g_object_new (NMT_TYPE_PAGE_BRIDGE_PORT, + "connection", conn, + "title", _("BRIDGE PORT"), + NULL); +} + +static void +nmt_page_bridge_port_init (NmtPageBridgePort *bridge) +{ +} + +static void +nmt_page_bridge_port_constructed (GObject *object) +{ + NmtPageBridgePort *bridge = NMT_PAGE_BRIDGE_PORT (object); + NmtPageGrid *grid; + NMSettingBridgePort *s_port; + NmtNewtWidget *widget; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (bridge)); + s_port = nm_connection_get_setting_bridge_port (conn); + if (!s_port) { + nm_connection_add_setting (conn, nm_setting_bridge_port_new ()); + s_port = nm_connection_get_setting_bridge_port (conn); + } + + grid = NMT_PAGE_GRID (bridge); + + widget = nmt_newt_entry_numeric_new (10, 0, 63); + g_object_bind_property (s_port, NM_SETTING_BRIDGE_PORT_PRIORITY, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Priority"), widget, NULL); + + widget = nmt_newt_entry_numeric_new (10, 1, 65535); + g_object_bind_property (s_port, NM_SETTING_BRIDGE_PORT_PATH_COST, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Path cost"), widget, NULL); + + widget = nmt_newt_checkbox_new (_("Hairpin mode")); + g_object_bind_property (s_port, NM_SETTING_BRIDGE_PORT_HAIRPIN_MODE, + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, NULL, widget, NULL); + + G_OBJECT_CLASS (nmt_page_bridge_port_parent_class)->constructed (object); +} + +static void +nmt_page_bridge_port_class_init (NmtPageBridgePortClass *bridge_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (bridge_class); + + object_class->constructed = nmt_page_bridge_port_constructed; +} diff --git a/tui/nmt-page-bridge-port.h b/tui/nmt-page-bridge-port.h new file mode 100644 index 0000000000..7fe3a92c43 --- /dev/null +++ b/tui/nmt-page-bridge-port.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_BRIDGE_PORT_H +#define NMT_PAGE_BRIDGE_PORT_H + +#include "nmt-editor-page.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_BRIDGE_PORT (nmt_page_bridge_port_get_type ()) +#define NMT_PAGE_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePort)) +#define NMT_PAGE_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass)) +#define NMT_IS_PAGE_BRIDGE_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_BRIDGE_PORT)) +#define NMT_IS_PAGE_BRIDGE_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_BRIDGE_PORT)) +#define NMT_PAGE_BRIDGE_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_BRIDGE_PORT, NmtPageBridgePortClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageBridgePort; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageBridgePortClass; + +GType nmt_page_bridge_port_get_type (void); + +NmtNewtWidget *nmt_page_bridge_port_new (NMConnection *conn); + +G_END_DECLS + +#endif /* NMT_PAGE_BRIDGE_PORT_H */ diff --git a/tui/nmt-page-bridge.c b/tui/nmt-page-bridge.c new file mode 100644 index 0000000000..713d83507e --- /dev/null +++ b/tui/nmt-page-bridge.c @@ -0,0 +1,153 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-bridge + * @short_description: The editor page for Bridge connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-bridge.h" + +#include "nmt-address-list.h" +#include "nmt-slave-list.h" + +G_DEFINE_TYPE (NmtPageBridge, nmt_page_bridge, NMT_TYPE_PAGE_DEVICE) + +NmtNewtWidget * +nmt_page_bridge_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_BRIDGE, + "connection", conn, + "title", _("BRIDGE"), + "device-entry", deventry, + NULL); +} + +static void +nmt_page_bridge_init (NmtPageBridge *bridge) +{ +} + +static gboolean +bridge_connection_type_filter (GType connection_type, + gpointer user_data) +{ + return ( connection_type == NM_TYPE_SETTING_WIRED + || connection_type == NM_TYPE_SETTING_WIRELESS + || connection_type == NM_TYPE_SETTING_VLAN); +} + +static void +nmt_page_bridge_constructed (GObject *object) +{ + NmtPageBridge *bridge = NMT_PAGE_BRIDGE (object); + NmtDeviceEntry *deventry; + NmtPageGrid *grid; + NMSettingBridge *s_bridge; + NmtNewtWidget *widget, *label, *stp; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (bridge)); + s_bridge = nm_connection_get_setting_bridge (conn); + if (!s_bridge) { + nm_connection_add_setting (conn, nm_setting_bridge_new ()); + s_bridge = nm_connection_get_setting_bridge (conn); + } + + deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object)); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_INTERFACE_NAME, + deventry, "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + grid = NMT_PAGE_GRID (bridge); + + widget = nmt_newt_separator_new (); + nmt_page_grid_append (grid, _("Slaves"), widget, NULL); + nmt_page_grid_set_row_flags (grid, widget, NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT); + + widget = nmt_slave_list_new (conn, bridge_connection_type_filter, bridge); + nmt_page_grid_append (grid, NULL, widget, NULL); + + widget = nmt_newt_entry_numeric_new (10, 0, 1000000); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_AGEING_TIME, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new (_("seconds")); + nmt_page_grid_append (grid, _("Aging time"), widget, label); + + widget = stp = nmt_newt_checkbox_new (_("Enable STP (Spanning Tree Protocol)")); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP, + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, NULL, widget, NULL); + + widget = nmt_newt_entry_numeric_new (10, 0, G_MAXINT); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_PRIORITY, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP, + widget, "sensitive", + G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Priority"), widget, NULL); + + widget = nmt_newt_entry_numeric_new (10, 2, 30); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_FORWARD_DELAY, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP, + widget, "sensitive", + G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new (_("seconds")); + nmt_page_grid_append (grid, _("Forward delay"), widget, label); + + widget = nmt_newt_entry_numeric_new (10, 1, 10); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_HELLO_TIME, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP, + widget, "sensitive", + G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new (_("seconds")); + nmt_page_grid_append (grid, _("Hello time"), widget, label); + + widget = nmt_newt_entry_numeric_new (10, 6, 40); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_MAX_AGE, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (s_bridge, NM_SETTING_BRIDGE_STP, + widget, "sensitive", + G_BINDING_SYNC_CREATE); + label = nmt_newt_label_new (_("seconds")); + nmt_page_grid_append (grid, _("Max age"), widget, label); + + G_OBJECT_CLASS (nmt_page_bridge_parent_class)->constructed (object); +} + +static void +nmt_page_bridge_class_init (NmtPageBridgeClass *bridge_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (bridge_class); + + object_class->constructed = nmt_page_bridge_constructed; +} diff --git a/tui/nmt-page-bridge.h b/tui/nmt-page-bridge.h new file mode 100644 index 0000000000..b11d350291 --- /dev/null +++ b/tui/nmt-page-bridge.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_BRIDGE_H +#define NMT_PAGE_BRIDGE_H + +#include "nmt-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_BRIDGE (nmt_page_bridge_get_type ()) +#define NMT_PAGE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_BRIDGE, NmtPageBridge)) +#define NMT_PAGE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_BRIDGE, NmtPageBridgeClass)) +#define NMT_IS_PAGE_BRIDGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_BRIDGE)) +#define NMT_IS_PAGE_BRIDGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_BRIDGE)) +#define NMT_PAGE_BRIDGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_BRIDGE, NmtPageBridgeClass)) + +typedef struct { + NmtPageDevice parent; + +} NmtPageBridge; + +typedef struct { + NmtPageDeviceClass parent; + +} NmtPageBridgeClass; + +GType nmt_page_bridge_get_type (void); + +NmtNewtWidget *nmt_page_bridge_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_BRIDGE_H */ diff --git a/tui/nmt-page-device.c b/tui/nmt-page-device.c new file mode 100644 index 0000000000..8ab0747e59 --- /dev/null +++ b/tui/nmt-page-device.c @@ -0,0 +1,151 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-device + * @short_description: Abstract base class for "device" editor pages + * + * #NmtPageDevice is the base class for #NmtEditorPage subclasses + * representing device-type-specific data. (Eg, #NmtPageEthernet, + * #NmtPageVlan, etc). + * + * FIXME: rename to NmtEditorPageDevice, so it doesn't sound like it's + * an actual page type. + */ + +#include "config.h" + +#include "nmt-page-device.h" + +G_DEFINE_TYPE (NmtPageDevice, nmt_page_device, NMT_TYPE_EDITOR_PAGE) + +#define NMT_PAGE_DEVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_DEVICE, NmtPageDevicePrivate)) + +typedef struct { + NmtDeviceEntry *device_entry; + gboolean show_by_default; +} NmtPageDevicePrivate; + +enum { + PROP_0, + + PROP_DEVICE_ENTRY, + PROP_SHOW_BY_DEFAULT, + + LAST_PROP +}; + +static void +nmt_page_device_init (NmtPageDevice *device) +{ +} + +static void +nmt_page_device_finalize (GObject *object) +{ + NmtPageDevicePrivate *priv = NMT_PAGE_DEVICE_GET_PRIVATE (object); + + g_clear_object (&priv->device_entry); + + G_OBJECT_CLASS (nmt_page_device_parent_class)->finalize (object); +} + +NmtDeviceEntry * +nmt_page_device_get_device_entry (NmtPageDevice *page) +{ + NmtPageDevicePrivate *priv = NMT_PAGE_DEVICE_GET_PRIVATE (page); + + return priv->device_entry; +} + +gboolean +nmt_page_device_get_show_by_default (NmtPageDevice *page) +{ + NmtPageDevicePrivate *priv = NMT_PAGE_DEVICE_GET_PRIVATE (page); + + return priv->show_by_default; +} + +static void +nmt_page_device_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtPageDevicePrivate *priv = NMT_PAGE_DEVICE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DEVICE_ENTRY: + priv->device_entry = g_value_dup_object (value); + break; + case PROP_SHOW_BY_DEFAULT: + priv->show_by_default = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_page_device_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtPageDevicePrivate *priv = NMT_PAGE_DEVICE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_DEVICE_ENTRY: + g_value_set_object (value, priv->device_entry); + break; + case PROP_SHOW_BY_DEFAULT: + g_value_set_boolean (value, priv->show_by_default); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_page_device_class_init (NmtPageDeviceClass *page_device_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (page_device_class); + + g_type_class_add_private (page_device_class, sizeof (NmtPageDevicePrivate)); + + /* virtual methods */ + object_class->set_property = nmt_page_device_set_property; + object_class->get_property = nmt_page_device_get_property; + object_class->finalize = nmt_page_device_finalize; + + /* properties */ + g_object_class_install_property (object_class, PROP_DEVICE_ENTRY, + g_param_spec_object ("device-entry", "", "", + NMT_TYPE_DEVICE_ENTRY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_SHOW_BY_DEFAULT, + g_param_spec_boolean ("show-by-default", "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-page-device.h b/tui/nmt-page-device.h new file mode 100644 index 0000000000..8c90a67a08 --- /dev/null +++ b/tui/nmt-page-device.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_DEVICE_H +#define NMT_PAGE_DEVICE_H + +#include "nmt-editor-page.h" +#include "nmt-device-entry.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_DEVICE (nmt_page_device_get_type ()) +#define NMT_PAGE_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_DEVICE, NmtPageDevice)) +#define NMT_PAGE_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_DEVICE, NmtPageDeviceClass)) +#define NMT_IS_PAGE_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_DEVICE)) +#define NMT_IS_PAGE_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_DEVICE)) +#define NMT_PAGE_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_DEVICE, NmtPageDeviceClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageDevice; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageDeviceClass; + +GType nmt_page_device_get_type (void); + +NmtDeviceEntry *nmt_page_device_get_device_entry (NmtPageDevice *page); +gboolean nmt_page_device_get_show_by_default (NmtPageDevice *page); + +G_END_DECLS + +#endif /* NMT_PAGE_DEVICE_H */ diff --git a/tui/nmt-page-ethernet.c b/tui/nmt-page-ethernet.c new file mode 100644 index 0000000000..9eb08fe5b3 --- /dev/null +++ b/tui/nmt-page-ethernet.c @@ -0,0 +1,97 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ethernet + * @short_description: The editor page for Ethernet connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-ethernet.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE (NmtPageEthernet, nmt_page_ethernet, NMT_TYPE_PAGE_DEVICE) + +NmtNewtWidget * +nmt_page_ethernet_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_ETHERNET, + "connection", conn, + "title", _("ETHERNET"), + "device-entry", deventry, + "show-by-default", FALSE, + NULL); +} + +static void +nmt_page_ethernet_init (NmtPageEthernet *ethernet) +{ +} + +static void +nmt_page_ethernet_constructed (GObject *object) +{ + NmtPageEthernet *ethernet = NMT_PAGE_ETHERNET (object); + NmtDeviceEntry *deventry; + NmtPageGrid *grid; + NMSettingWired *s_wired; + NmtNewtWidget *widget; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ethernet)); + s_wired = nm_connection_get_setting_wired (conn); + if (!s_wired) { + nm_connection_add_setting (conn, nm_setting_wired_new ()); + s_wired = nm_connection_get_setting_wired (conn); + } + + deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object)); + g_object_bind_property (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, + deventry, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + grid = NMT_PAGE_GRID (ethernet); + + widget = nmt_mac_entry_new (40, ETH_ALEN); + g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + widget, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Cloned MAC address"), widget, NULL); + + widget = nmt_mtu_entry_new (); + g_object_bind_property (s_wired, NM_SETTING_WIRED_MTU, + widget, "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("MTU"), widget, NULL); + + G_OBJECT_CLASS (nmt_page_ethernet_parent_class)->constructed (object); +} + +static void +nmt_page_ethernet_class_init (NmtPageEthernetClass *ethernet_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ethernet_class); + + object_class->constructed = nmt_page_ethernet_constructed; +} diff --git a/tui/nmt-page-ethernet.h b/tui/nmt-page-ethernet.h new file mode 100644 index 0000000000..5e001562f7 --- /dev/null +++ b/tui/nmt-page-ethernet.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_ETHERNET_H +#define NMT_PAGE_ETHERNET_H + +#include "nmt-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_ETHERNET (nmt_page_ethernet_get_type ()) +#define NMT_PAGE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernet)) +#define NMT_PAGE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernetClass)) +#define NMT_IS_PAGE_ETHERNET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_ETHERNET)) +#define NMT_IS_PAGE_ETHERNET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_ETHERNET)) +#define NMT_PAGE_ETHERNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_ETHERNET, NmtPageEthernetClass)) + +typedef struct { + NmtPageDevice parent; + +} NmtPageEthernet; + +typedef struct { + NmtPageDeviceClass parent; + +} NmtPageEthernetClass; + +GType nmt_page_ethernet_get_type (void); + +NmtNewtWidget *nmt_page_ethernet_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_ETHERNET_H */ diff --git a/tui/nmt-page-grid.c b/tui/nmt-page-grid.c new file mode 100644 index 0000000000..89ba9330f4 --- /dev/null +++ b/tui/nmt-page-grid.c @@ -0,0 +1,458 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-grid + * @short_description: Grid widget for #NmtEditorPages + * + * #NmtPageGrid is the layout grid used by #NmtEditorPages. It + * consists of a number of rows, each containing either a single + * widget that spans the entire width of the row, or else containing a + * label, a widget, and an optional extra widget. + * + * Each row of the grid can take up multiple on-screen rows, if + * its main widget is multiple rows high. The label and extra widgets + * will be top-aligned if the row is taller than they are. + * + * The #NmtPageGrids in a form behave as though they are all in a + * "size group" together; they will all use the same column widths, + * which will be wide enough for the widest labels/widgets in any of + * the grids. #NmtPageGrid is also specially aware of #NmtNewtSection, + * and grids inside sections will automatically take the size of the + * section border into account as well. + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-page-grid.h" + +G_DEFINE_TYPE (NmtPageGrid, nmt_page_grid, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_PAGE_GRID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_GRID, NmtPageGridPrivate)) + +typedef struct { + GArray *rows; + int *row_heights; + int indent; +} NmtPageGridPrivate; + +typedef struct { + NmtNewtWidget *label; + NmtNewtWidget *widget; + NmtNewtWidget *extra; + NmtPageGridRowFlags flags; +} NmtPageGridRow; + +typedef struct { + int col_widths[3]; +} NmtPageGridFormState; + +/** + * nmt_page_grid_new: + * + * Creates a new #NmtPageGrid + * + * Returns: a new #NmtPageGrid + */ +NmtNewtWidget * +nmt_page_grid_new (void) +{ + return g_object_new (NMT_TYPE_PAGE_GRID, + NULL); +} + +static void +nmt_page_grid_init (NmtPageGrid *grid) +{ + NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (grid); + + priv->rows = g_array_new (FALSE, TRUE, sizeof (NmtPageGridRow)); +} + +static void +nmt_page_grid_finalize (GObject *object) +{ + NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (object); + + g_array_unref (priv->rows); + g_clear_pointer (&priv->row_heights, g_free); + + G_OBJECT_CLASS (nmt_page_grid_parent_class)->finalize (object); +} + +/** + * nmt_page_grid_append: + * @grid: the #NmtPageGrid + * @label: (allow-none): the label text for @widget, or %NULL + * @widget: the (main) widget + * @extra: (allow-none): optional extra widget + * + * Adds a row to @grid. + * + * If @label is non-%NULL, this will add a three-column row, + * containing a right-aligned #NmtNewtLabel in the first column, + * @widget in the second column, and @extra (if non-%NULL) in + * the third column. + * + * If @label is %NULL, then this will add a row with a single + * grid-spanning column, containing @widget. + * + * FIXME: That's sort of weird. + * + * See also nmt_page_grid_set_row_flags(). + */ +void +nmt_page_grid_append (NmtPageGrid *grid, + const char *label, + NmtNewtWidget *widget, + NmtNewtWidget *extra) +{ + NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (grid); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_page_grid_parent_class); + NmtNewtContainer *container = NMT_NEWT_CONTAINER (grid); + NmtPageGridRow row; + + memset (&row, 0, sizeof (row)); + + if (label) { + row.label = nmt_newt_label_new (label); + parent_class->add (container, row.label); + } + + row.widget = widget; + parent_class->add (container, widget); + if (row.label) { + g_object_bind_property (row.widget, "valid", + row.label, "highlight", + G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE); + } + + if (extra) { + row.extra = extra; + parent_class->add (container, extra); + } + + g_array_append_val (priv->rows, row); +} + +static int +nmt_page_grid_find_widget (NmtPageGrid *grid, + NmtNewtWidget *widget) +{ + NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (grid); + NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data; + int i; + + for (i = 0; i < priv->rows->len; i++) { + if (rows[i].label == widget || rows[i].widget == widget || rows[i].extra == widget) + return i; + } + + return -1; +} + +/** + * NmtPageGridRowFlags: + * @NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT: the row's label should be + * aligned left instead of right. + * @NMT_PAGE_GRID_ROW_EXTRA_ALIGN_RIGHT: the row's extra widget + * should be aligned right instead of left. + * + * Flags to alter an #NmtPageGrid row's layout. + */ + +/** + * nmt_page_grid_set_row_flags: + * @grid: an #NmtPageGrid + * @widget: the widget whose row you want to adjust + * @flags: the flags to set + * + * Sets flags to adjust the layout of @widget's row in @grid. + */ +void +nmt_page_grid_set_row_flags (NmtPageGrid *grid, + NmtNewtWidget *widget, + NmtPageGridRowFlags flags) +{ + NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (grid); + NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data; + int i; + + i = nmt_page_grid_find_widget (grid, widget); + if (i != -1) + rows[i].flags = flags; +} + +static void +nmt_page_grid_remove (NmtNewtContainer *container, + NmtNewtWidget *widget) +{ + NmtPageGrid *grid = NMT_PAGE_GRID (container); + NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (grid); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS (nmt_page_grid_parent_class); + NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data; + int i; + + i = nmt_page_grid_find_widget (grid, widget); + if (i != -1) { + if (rows[i].label) + parent_class->remove (container, rows[i].label); + parent_class->remove (container, rows[i].widget); + if (rows[i].extra) + parent_class->remove (container, rows[i].extra); + + g_array_remove_index (priv->rows, i); + return; + } + + // FIXME: shouldn't happen + parent_class->remove (container, widget); +} + +static newtComponent * +nmt_page_grid_get_components (NmtNewtWidget *widget) +{ + NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (widget); + NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data; + newtComponent *child_cos; + GPtrArray *cos; + int i, c; + + cos = g_ptr_array_new (); + + for (i = 0; i < priv->rows->len; i++) { + if (!nmt_newt_widget_get_visible (rows[i].widget)) + continue; + + if (rows[i].label) { + child_cos = nmt_newt_widget_get_components (rows[i].label); + g_assert (child_cos[0] && !child_cos[1]); + g_ptr_array_add (cos, child_cos[0]); + g_free (child_cos); + } + + child_cos = nmt_newt_widget_get_components (rows[i].widget); + for (c = 0; child_cos[c]; c++) + g_ptr_array_add (cos, child_cos[c]); + g_free (child_cos); + + if (rows[i].extra) { + child_cos = nmt_newt_widget_get_components (rows[i].extra); + for (c = 0; child_cos[c]; c++) + g_ptr_array_add (cos, child_cos[c]); + g_free (child_cos); + } + } + + g_ptr_array_add (cos, NULL); + return (newtComponent *) g_ptr_array_free (cos, FALSE); +} + +static NmtPageGridFormState * +get_form_state (NmtNewtWidget *widget) +{ + NmtNewtForm *form = nmt_newt_widget_get_form (widget); + NmtPageGridFormState *state; + + if (!form) + return NULL; + + state = g_object_get_data (G_OBJECT (form), "NmtPageGridFormState"); + if (state) + return state; + + state = g_new0 (NmtPageGridFormState, 1); + g_object_set_data_full (G_OBJECT (form), "NmtPageGridFormState", state, g_free); + return state; +} + +static void +nmt_page_grid_realize (NmtNewtWidget *widget) +{ + NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (widget); + NmtNewtWidget *parent; + + NMT_NEWT_WIDGET_CLASS (nmt_page_grid_parent_class)->realize (widget); + + /* This is a hack, but it's the simplest way to make it work... */ + priv->indent = 0; + + parent = nmt_newt_widget_get_parent (widget); + while (parent) { + if (NMT_IS_NEWT_SECTION (parent)) { + priv->indent = 2; + break; + } + parent = nmt_newt_widget_get_parent (parent); + } +} + +static void +nmt_page_grid_unrealize (NmtNewtWidget *widget) +{ + NmtPageGridFormState *state = get_form_state (widget); + + if (state) + memset (state->col_widths, 0, sizeof (state->col_widths)); + + NMT_NEWT_WIDGET_CLASS (nmt_page_grid_parent_class)->unrealize (widget); +} + +static void +nmt_page_grid_size_request (NmtNewtWidget *widget, + int *width, + int *height) +{ + NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (widget); + NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data; + NmtPageGridFormState *state = get_form_state (widget); + gboolean add_padding = FALSE; + int i; + + g_free (priv->row_heights); + priv->row_heights = g_new0 (int, priv->rows->len); + + *height = 0; + for (i = 0; i < priv->rows->len; i++) { + int lwidth, lheight, wwidth, wheight, ewidth, eheight; + + if (!nmt_newt_widget_get_visible (rows[i].widget)) + continue; + + if (rows[i].label) { + nmt_newt_widget_size_request (rows[i].label, &lwidth, &lheight); + lwidth += priv->indent; + state->col_widths[0] = MAX (state->col_widths[0], lwidth); + + nmt_newt_widget_size_request (rows[i].widget, &wwidth, &wheight); + state->col_widths[1] = MAX (state->col_widths[1], wwidth); + priv->row_heights[i] = wheight; + + add_padding = TRUE; + } else { + nmt_newt_widget_size_request (rows[i].widget, &wwidth, &wheight); + priv->row_heights[i] = wheight; + } + + if (rows[i].extra) { + nmt_newt_widget_size_request (rows[i].extra, &ewidth, &eheight); + state->col_widths[2] = MAX (state->col_widths[2], ewidth); + priv->row_heights[i] = MAX (priv->row_heights[i], eheight); + } + + *height += priv->row_heights[i]; + } + + *width = state->col_widths[0] + state->col_widths[1] + state->col_widths[2]; + if (add_padding) + *width += 2; +} + + +static void +nmt_page_grid_size_allocate (NmtNewtWidget *widget, + int x, + int y, + int width, + int height) +{ + NmtPageGridPrivate *priv = NMT_PAGE_GRID_GET_PRIVATE (widget); + NmtPageGridRow *rows = (NmtPageGridRow *) priv->rows->data; + NmtPageGridFormState *state = get_form_state (widget); + int col0_width, col1_width, col2_width; + int i, row; + + col0_width = state->col_widths[0] - priv->indent; + col1_width = state->col_widths[1]; + col2_width = state->col_widths[2]; + + for (i = row = 0; i < priv->rows->len; i++) { + if (!nmt_newt_widget_get_visible (rows[i].widget)) + continue; + + if (rows[i].label) { + int lwidth, lheight, lx; + + if (rows[i].flags & NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT) + lx = x; + else { + nmt_newt_widget_size_request (rows[i].label, &lwidth, &lheight); + lx = x + col0_width - lwidth; + } + + nmt_newt_widget_size_allocate (rows[i].label, + lx, + y + row, + col0_width, + priv->row_heights[i]); + + nmt_newt_widget_size_allocate (rows[i].widget, + x + col0_width + 1, + y + row, + col1_width, + priv->row_heights[i]); + if (rows[i].extra) { + int wwidth, wheight, ex; + + if (rows[i].flags & NMT_PAGE_GRID_ROW_EXTRA_ALIGN_RIGHT) + ex = x + col0_width + col1_width + 2; + else { + nmt_newt_widget_size_request (rows[i].widget, &wwidth, &wheight); + ex = x + col0_width + wwidth + 2; + } + + nmt_newt_widget_size_allocate (rows[i].extra, + ex, + y + row, + col2_width, + priv->row_heights[i]); + } + } else { + nmt_newt_widget_size_allocate (rows[i].widget, + x, + y + row, + col0_width + col1_width + col2_width + 2, + priv->row_heights[i]); + } + + row += priv->row_heights[i]; + } +} + +static void +nmt_page_grid_class_init (NmtPageGridClass *grid_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (grid_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (grid_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (grid_class); + + g_type_class_add_private (grid_class, sizeof (NmtPageGridPrivate)); + + /* virtual methods */ + object_class->finalize = nmt_page_grid_finalize; + + widget_class->realize = nmt_page_grid_realize; + widget_class->unrealize = nmt_page_grid_unrealize; + widget_class->get_components = nmt_page_grid_get_components; + widget_class->size_request = nmt_page_grid_size_request; + widget_class->size_allocate = nmt_page_grid_size_allocate; + + container_class->remove = nmt_page_grid_remove; +} diff --git a/tui/nmt-page-grid.h b/tui/nmt-page-grid.h new file mode 100644 index 0000000000..40ff96c410 --- /dev/null +++ b/tui/nmt-page-grid.h @@ -0,0 +1,62 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_GRID_H +#define NMT_PAGE_GRID_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_GRID (nmt_page_grid_get_type ()) +#define NMT_PAGE_GRID(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_GRID, NmtPageGrid)) +#define NMT_PAGE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_GRID, NmtPageGridClass)) +#define NMT_IS_PAGE_GRID(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_GRID)) +#define NMT_IS_PAGE_GRID_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_GRID)) +#define NMT_PAGE_GRID_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_GRID, NmtPageGridClass)) + +typedef struct { + NmtNewtContainer parent; + +} NmtPageGrid; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtPageGridClass; + +GType nmt_page_grid_get_type (void); + +typedef enum { + NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT = (1 << 0), + NMT_PAGE_GRID_ROW_EXTRA_ALIGN_RIGHT = (1 << 1) +} NmtPageGridRowFlags; + +NmtNewtWidget *nmt_page_grid_new (void); + +void nmt_page_grid_append (NmtPageGrid *grid, + const char *label, + NmtNewtWidget *widget, + NmtNewtWidget *extra); +void nmt_page_grid_set_row_flags (NmtPageGrid *grid, + NmtNewtWidget *widget, + NmtPageGridRowFlags flags); + +G_END_DECLS + +#endif /* NMT_PAGE_GRID_H */ diff --git a/tui/nmt-page-infiniband.c b/tui/nmt-page-infiniband.c new file mode 100644 index 0000000000..c1238ab678 --- /dev/null +++ b/tui/nmt-page-infiniband.c @@ -0,0 +1,101 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-infiniband + * @short_description: The editor page for InfiniBand connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-infiniband.h" +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE (NmtPageInfiniband, nmt_page_infiniband, NMT_TYPE_PAGE_DEVICE) + +NmtNewtWidget * +nmt_page_infiniband_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_INFINIBAND, + "connection", conn, + "title", _("INFINIBAND"), + "device-entry", deventry, + NULL); +} + +static void +nmt_page_infiniband_init (NmtPageInfiniband *infiniband) +{ +} + +static NmtNewtPopupEntry transport_mode[] = { + { N_("Datagram"), "datagram" }, + { N_("Connected"), "connected" }, + { NULL, NULL } +}; + +static void +nmt_page_infiniband_constructed (GObject *object) +{ + NmtPageInfiniband *infiniband = NMT_PAGE_INFINIBAND (object); + NmtDeviceEntry *deventry; + NmtPageGrid *grid; + NMSettingInfiniband *s_ib; + NmtNewtWidget *widget; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (infiniband)); + s_ib = nm_connection_get_setting_infiniband (conn); + if (!s_ib) { + nm_connection_add_setting (conn, nm_setting_infiniband_new ()); + s_ib = nm_connection_get_setting_infiniband (conn); + } + + deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object)); + g_object_bind_property (s_ib, NM_SETTING_INFINIBAND_MAC_ADDRESS, + deventry, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + grid = NMT_PAGE_GRID (infiniband); + + widget = nmt_newt_popup_new (transport_mode); + g_object_bind_property (s_ib, NM_SETTING_INFINIBAND_TRANSPORT_MODE, + widget, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Transport mode"), widget, NULL); + + widget = nmt_mtu_entry_new (); + g_object_bind_property (s_ib, NM_SETTING_INFINIBAND_MTU, + widget, "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("MTU"), widget, NULL); + + G_OBJECT_CLASS (nmt_page_infiniband_parent_class)->constructed (object); +} + +static void +nmt_page_infiniband_class_init (NmtPageInfinibandClass *infiniband_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (infiniband_class); + + object_class->constructed = nmt_page_infiniband_constructed; +} diff --git a/tui/nmt-page-infiniband.h b/tui/nmt-page-infiniband.h new file mode 100644 index 0000000000..706b7b60c3 --- /dev/null +++ b/tui/nmt-page-infiniband.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_INFINIBAND_H +#define NMT_PAGE_INFINIBAND_H + +#include "nmt-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_INFINIBAND (nmt_page_infiniband_get_type ()) +#define NMT_PAGE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfiniband)) +#define NMT_PAGE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfinibandClass)) +#define NMT_IS_PAGE_INFINIBAND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_INFINIBAND)) +#define NMT_IS_PAGE_INFINIBAND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_INFINIBAND)) +#define NMT_PAGE_INFINIBAND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_INFINIBAND, NmtPageInfinibandClass)) + +typedef struct { + NmtPageDevice parent; + +} NmtPageInfiniband; + +typedef struct { + NmtPageDeviceClass parent; + +} NmtPageInfinibandClass; + +GType nmt_page_infiniband_get_type (void); + +NmtNewtWidget *nmt_page_infiniband_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_INFINIBAND_H */ diff --git a/tui/nmt-page-ip4.c b/tui/nmt-page-ip4.c new file mode 100644 index 0000000000..210387c511 --- /dev/null +++ b/tui/nmt-page-ip4.c @@ -0,0 +1,199 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ip4 + * @short_description: The editor page for IP4 configuration + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-ip4.h" +#include "nmt-ip-entry.h" +#include "nmt-address-list.h" +#include "nmt-route-editor.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE (NmtPageIP4, nmt_page_ip4, NMT_TYPE_EDITOR_PAGE) + +static NmtNewtPopupEntry ip4methods[] = { + { N_("Disabled"), NM_SETTING_IP4_CONFIG_METHOD_DISABLED }, + { N_("Automatic"), NM_SETTING_IP4_CONFIG_METHOD_AUTO }, + { N_("Link-Local"), NM_SETTING_IP4_CONFIG_METHOD_LINK_LOCAL }, + { N_("Manual"), NM_SETTING_IP4_CONFIG_METHOD_MANUAL }, + { N_("Shared"), NM_SETTING_IP4_CONFIG_METHOD_SHARED }, + { NULL, NULL } +}; + +NmtNewtWidget * +nmt_page_ip4_new (NMConnection *conn) +{ + return g_object_new (NMT_TYPE_PAGE_IP4, + "connection", conn, + "title", _("IPv4 CONFIGURATION"), + NULL); +} + +gboolean +nmt_page_ip4_is_non_empty (NmtPageIP4 *ip4) +{ + NMConnection *conn; + NMSettingIP4Config *s_ip4; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ip4)); + s_ip4 = nm_connection_get_setting_ip4_config (conn); + if ( !g_strcmp0 (nm_setting_ip4_config_get_method (s_ip4), NM_SETTING_IP4_CONFIG_METHOD_MANUAL) + || nm_setting_ip4_config_get_num_addresses (s_ip4)) + return TRUE; + return FALSE; +} + +static void +nmt_page_ip4_init (NmtPageIP4 *ip4) +{ +} + +static void +edit_routes (NmtNewtButton *button, + gpointer user_data) +{ + NMSetting *s_ip4 = user_data; + NmtNewtForm *form; + + form = nmt_route_editor_new (s_ip4); + nmt_newt_form_run_sync (form); + g_object_unref (form); +} + +static gboolean +ip4_routes_transform_to_description (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *routes; + char *text; + + routes = g_value_get_boxed (source_value); + if (!routes || !routes->len) + text = g_strdup (_("(No custom routes)")); + else { + text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, + "One custom route", + "%d custom routes", + routes->len), + routes->len); + } + + g_value_take_string (target_value, text); + return TRUE; +} + +static void +nmt_page_ip4_constructed (GObject *object) +{ + NmtPageIP4 *ip4 = NMT_PAGE_IP4 (object); + NmtPageGrid *grid; + NMSettingIP4Config *s_ip4; + NmtNewtWidget *widget, *button; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ip4)); + s_ip4 = nm_connection_get_setting_ip4_config (conn); + if (!s_ip4) { + s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new (); + nm_connection_add_setting (conn, (NMSetting *) s_ip4); + } + + widget = nmt_newt_popup_new (ip4methods); + g_object_bind_property (s_ip4, NM_SETTING_IP4_CONFIG_METHOD, + widget, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_page_set_header_widget (NMT_EDITOR_PAGE (ip4), widget); + + grid = NMT_PAGE_GRID (ip4); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4_WITH_PREFIX); + nm_editor_bind_ip4_addresses_with_prefix_to_strv (s_ip4, NM_SETTING_IP4_CONFIG_ADDRESSES, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Addresses"), widget, NULL); + + widget = nmt_ip_entry_new (25, AF_INET, FALSE, TRUE); + nm_editor_bind_ip4_gateway_to_string (s_ip4, NM_SETTING_IP4_CONFIG_ADDRESSES, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Gateway"), widget, NULL); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP4); + nm_editor_bind_ip4_addresses_to_strv (s_ip4, NM_SETTING_IP4_CONFIG_DNS, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("DNS servers"), widget, NULL); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_HOSTNAME); + g_object_bind_property (s_ip4, NM_SETTING_IP4_CONFIG_DNS_SEARCH, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Search domains"), widget, NULL); + + nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = g_object_new (NMT_TYPE_NEWT_LABEL, + "text", "", + "style", NMT_NEWT_LABEL_PLAIN, + NULL); + g_object_bind_property_full (s_ip4, NM_SETTING_IP4_CONFIG_ROUTES, + widget, "text", + G_BINDING_SYNC_CREATE, + ip4_routes_transform_to_description, + NULL, NULL, NULL); + button = nmt_newt_button_new (_("Edit...")); + g_signal_connect (button, "clicked", G_CALLBACK (edit_routes), s_ip4); + nmt_page_grid_append (grid, _("Routing"), widget, button); + + widget = nmt_newt_checkbox_new (_("Never use this network for default route")); + g_object_bind_property (s_ip4, NM_SETTING_IP4_CONFIG_NEVER_DEFAULT, + widget, "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_page_grid_append (grid, NULL, widget, NULL); + + nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_newt_checkbox_new (_("Require IPv4 addressing for this connection")); + g_object_bind_property (s_ip4, NM_SETTING_IP4_CONFIG_MAY_FAIL, + widget, "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_page_grid_append (grid, NULL, widget, NULL); + + G_OBJECT_CLASS (nmt_page_ip4_parent_class)->constructed (object); +} + +static void +nmt_page_ip4_class_init (NmtPageIP4Class *ip4_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ip4_class); + + object_class->constructed = nmt_page_ip4_constructed; +} diff --git a/tui/nmt-page-ip4.h b/tui/nmt-page-ip4.h new file mode 100644 index 0000000000..f3bb933e0e --- /dev/null +++ b/tui/nmt-page-ip4.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_IP4_H +#define NMT_PAGE_IP4_H + +#include "nmt-editor-page.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_IP4 (nmt_page_ip4_get_type ()) +#define NMT_PAGE_IP4(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_IP4, NmtPageIP4)) +#define NMT_PAGE_IP4_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_IP4, NmtPageIP4Class)) +#define NMT_IS_PAGE_IP4(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_IP4)) +#define NMT_IS_PAGE_IP4_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_IP4)) +#define NMT_PAGE_IP4_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_IP4, NmtPageIP4Class)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageIP4; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageIP4Class; + +GType nmt_page_ip4_get_type (void); + +NmtNewtWidget *nmt_page_ip4_new (NMConnection *conn); + +gboolean nmt_page_ip4_is_non_empty (NmtPageIP4 *ip4); + +G_END_DECLS + +#endif /* NMT_PAGE_IP4_H */ diff --git a/tui/nmt-page-ip6.c b/tui/nmt-page-ip6.c new file mode 100644 index 0000000000..b40c00fdf8 --- /dev/null +++ b/tui/nmt-page-ip6.c @@ -0,0 +1,197 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-ip6 + * @short_description: The editor page for IP6 configuration + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-ip6.h" +#include "nmt-ip-entry.h" +#include "nmt-address-list.h" +#include "nmt-route-editor.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE (NmtPageIP6, nmt_page_ip6, NMT_TYPE_EDITOR_PAGE) + +static NmtNewtPopupEntry ip6methods[] = { + { N_("Ignore"), NM_SETTING_IP6_CONFIG_METHOD_IGNORE }, + { N_("Automatic"), NM_SETTING_IP6_CONFIG_METHOD_AUTO }, + { N_("Automatic (DHCP-only)"), NM_SETTING_IP6_CONFIG_METHOD_DHCP }, + { N_("Link-Local"), NM_SETTING_IP6_CONFIG_METHOD_LINK_LOCAL }, + { N_("Manual"), NM_SETTING_IP6_CONFIG_METHOD_MANUAL }, + { NULL, NULL } +}; + +NmtNewtWidget * +nmt_page_ip6_new (NMConnection *conn) +{ + return g_object_new (NMT_TYPE_PAGE_IP6, + "connection", conn, + "title", _("IPv6 CONFIGURATION"), + NULL); +} + +gboolean +nmt_page_ip6_is_non_empty (NmtPageIP6 *ip6) +{ + NMConnection *conn; + NMSettingIP6Config *s_ip6; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ip6)); + s_ip6 = nm_connection_get_setting_ip6_config (conn); + if ( !g_strcmp0 (nm_setting_ip6_config_get_method (s_ip6), NM_SETTING_IP6_CONFIG_METHOD_MANUAL) + || nm_setting_ip6_config_get_num_addresses (s_ip6)) + return TRUE; + return FALSE; +} + +static void +nmt_page_ip6_init (NmtPageIP6 *ip6) +{ +} + +static void +edit_routes (NmtNewtButton *button, + gpointer user_data) +{ + NMSetting *s_ip6 = user_data; + NmtNewtForm *form; + + form = nmt_route_editor_new (s_ip6); + nmt_newt_form_run_sync (form); + g_object_unref (form); +} + +static gboolean +ip6_routes_transform_to_description (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GPtrArray *routes; + char *text; + + routes = g_value_get_boxed (source_value); + if (!routes || !routes->len) + text = g_strdup (_("(No custom routes)")); + else { + text = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, + "One custom route", + "%d custom routes", + routes->len), + routes->len); + } + + g_value_take_string (target_value, text); + return TRUE; +} + +static void +nmt_page_ip6_constructed (GObject *object) +{ + NmtPageIP6 *ip6 = NMT_PAGE_IP6 (object); + NmtPageGrid *grid; + NMSettingIP6Config *s_ip6; + NmtNewtWidget *widget, *button; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (ip6)); + s_ip6 = nm_connection_get_setting_ip6_config (conn); + if (!s_ip6) { + s_ip6 = (NMSettingIP6Config *) nm_setting_ip6_config_new (); + nm_connection_add_setting (conn, (NMSetting *) s_ip6); + } + + widget = nmt_newt_popup_new (ip6methods); + g_object_bind_property (s_ip6, NM_SETTING_IP6_CONFIG_METHOD, + widget, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_editor_page_set_header_widget (NMT_EDITOR_PAGE (ip6), widget); + + grid = NMT_PAGE_GRID (ip6); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP6_WITH_PREFIX); + nm_editor_bind_ip6_addresses_with_prefix_to_strv (s_ip6, NM_SETTING_IP6_CONFIG_ADDRESSES, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Addresses"), widget, NULL); + + widget = nmt_ip_entry_new (25, AF_INET6, FALSE, TRUE); + nm_editor_bind_ip6_gateway_to_string (s_ip6, NM_SETTING_IP6_CONFIG_ADDRESSES, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Gateway"), widget, NULL); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_IP6); + nm_editor_bind_ip6_addresses_to_strv (s_ip6, NM_SETTING_IP6_CONFIG_DNS, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("DNS servers"), widget, NULL); + + widget = nmt_address_list_new (NMT_ADDRESS_LIST_HOSTNAME); + g_object_bind_property (s_ip6, NM_SETTING_IP6_CONFIG_DNS_SEARCH, + widget, "strings", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Search domains"), widget, NULL); + + widget = g_object_new (NMT_TYPE_NEWT_LABEL, + "text", "", + "style", NMT_NEWT_LABEL_PLAIN, + NULL); + g_object_bind_property_full (s_ip6, NM_SETTING_IP6_CONFIG_ROUTES, + widget, "text", + G_BINDING_SYNC_CREATE, + ip6_routes_transform_to_description, + NULL, NULL, NULL); + button = nmt_newt_button_new (_("Edit...")); + g_signal_connect (button, "clicked", G_CALLBACK (edit_routes), s_ip6); + nmt_page_grid_append (grid, _("Routing"), widget, button); + + widget = nmt_newt_checkbox_new (_("Never use this network for default route")); + g_object_bind_property (s_ip6, NM_SETTING_IP6_CONFIG_NEVER_DEFAULT, + widget, "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_page_grid_append (grid, NULL, widget, NULL); + + nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_newt_checkbox_new (_("Require IPv6 addressing for this connection")); + g_object_bind_property (s_ip6, NM_SETTING_IP6_CONFIG_MAY_FAIL, + widget, "active", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_page_grid_append (grid, NULL, widget, NULL); + + G_OBJECT_CLASS (nmt_page_ip6_parent_class)->constructed (object); +} + +static void +nmt_page_ip6_class_init (NmtPageIP6Class *ip6_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (ip6_class); + + object_class->constructed = nmt_page_ip6_constructed; +} diff --git a/tui/nmt-page-ip6.h b/tui/nmt-page-ip6.h new file mode 100644 index 0000000000..d0d2bfa298 --- /dev/null +++ b/tui/nmt-page-ip6.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_IP6_H +#define NMT_PAGE_IP6_H + +#include "nmt-editor-page.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_IP6 (nmt_page_ip6_get_type ()) +#define NMT_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_IP6, NmtPageIP6)) +#define NMT_PAGE_IP6_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_IP6, NmtPageIP6Class)) +#define NMT_IS_PAGE_IP6(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_IP6)) +#define NMT_IS_PAGE_IP6_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_IP6)) +#define NMT_PAGE_IP6_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_IP6, NmtPageIP6Class)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageIP6; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageIP6Class; + +GType nmt_page_ip6_get_type (void); + +NmtNewtWidget *nmt_page_ip6_new (NMConnection *conn); + +gboolean nmt_page_ip6_is_non_empty (NmtPageIP6 *ip6); + +G_END_DECLS + +#endif /* NMT_PAGE_IP6_H */ diff --git a/tui/nmt-page-main.c b/tui/nmt-page-main.c new file mode 100644 index 0000000000..f49b155b3c --- /dev/null +++ b/tui/nmt-page-main.c @@ -0,0 +1,327 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-main + * @short_description: The top-level #NmtEditorPage for a connection + * + * #NmtPageMain is the top-level #NmtEditorPage for a connection. It + * handles #NMSettingConnection properties, and embeds the other pages + * within itself. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include <nm-device.h> +#include <nm-utils.h> + +#include "nmt-page-main.h" +#include "nmt-device-entry.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" +#include "nmtui.h" + +#include "nmt-page-bond.h" +#include "nmt-page-bridge.h" +#include "nmt-page-bridge-port.h" +#include "nmt-page-ethernet.h" +#include "nmt-page-infiniband.h" +#include "nmt-page-ip4.h" +#include "nmt-page-ip6.h" +#include "nmt-page-team.h" +#include "nmt-page-team-port.h" +#include "nmt-page-vlan.h" +#include "nmt-page-wifi.h" + +G_DEFINE_TYPE (NmtPageMain, nmt_page_main, NMT_TYPE_EDITOR_PAGE) + +#define NMT_PAGE_MAIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_MAIN, NmtPageMainPrivate)) + +typedef struct { + NMEditorConnectionTypeData *type_data; +} NmtPageMainPrivate; + +enum { + PROP_0, + + PROP_TYPE_DATA, + + LAST_PROP +}; + +/** + * nmt_page_main_new: + * @conn: the #NMConnection to display + * @type_data: @conn's #NMEditorConnectionTypeData + * + * Creates a new #NmtPageMain + * + * Returns: a new #NmtPageMain + */ +NmtNewtWidget * +nmt_page_main_new (NMConnection *conn, + NMEditorConnectionTypeData *type_data) +{ + return g_object_new (NMT_TYPE_PAGE_MAIN, + "connection", conn, + "type-data", type_data, + NULL); +} + +static void +nmt_page_main_init (NmtPageMain *page) +{ +} + +static gboolean +permissions_transform_to_allusers (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GSList *perms = g_value_get_boxed (source_value); + + g_value_set_boolean (target_value, perms == NULL); + return TRUE; +} + +static gboolean +permissions_transform_from_allusers (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + gboolean allusers = g_value_get_boolean (source_value); + GSList *perms = NULL; + + if (allusers) { + char *perm = g_strdup_printf ("user:%s:", g_get_user_name ()); + + perms = g_slist_prepend (perms, perm); + } + g_value_take_boxed (target_value, perms); + return TRUE; +} + +static NmtNewtWidget * +build_section_for_page (NmtEditorPage *page, + gboolean open) +{ + NmtNewtWidget *section, *header, *toggle; + + g_return_val_if_fail (nmt_newt_widget_get_parent (NMT_NEWT_WIDGET (page)) == NULL, NULL); + + section = nmt_newt_section_new (); + + toggle = nmt_newt_toggle_button_new (_("Hide"), _("Show")); + + header = nmt_page_grid_new (); + nmt_page_grid_append (NMT_PAGE_GRID (header), + nmt_editor_page_get_title (page), + nmt_editor_page_get_header_widget (page), + toggle); + nmt_page_grid_set_row_flags (NMT_PAGE_GRID (header), + nmt_editor_page_get_header_widget (page), + NMT_PAGE_GRID_ROW_LABEL_ALIGN_LEFT | + NMT_PAGE_GRID_ROW_EXTRA_ALIGN_RIGHT); + nmt_newt_section_set_header (NMT_NEWT_SECTION (section), header); + + nmt_newt_section_set_body (NMT_NEWT_SECTION (section), NMT_NEWT_WIDGET (page)); + + g_object_bind_property (toggle, "active", + section, "open", + G_BINDING_SYNC_CREATE); + + if (open || !nmt_newt_widget_get_valid (section)) + nmt_newt_toggle_button_set_active (NMT_NEWT_TOGGLE_BUTTON (toggle), TRUE); + + return section; +} + +static void +nmt_page_main_constructed (GObject *object) +{ + NmtPageMain *page_main = NMT_PAGE_MAIN (object); + NmtPageMainPrivate *priv = NMT_PAGE_MAIN_GET_PRIVATE (page_main); + NmtPageGrid *grid; + NMConnection *conn; + NMSettingConnection *s_con; + NmtNewtWidget *widget, *section, *page, *separator; + NmtDeviceEntry *deventry; + GType hardware_type; + const char *slave_type; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (page_main)); + s_con = nm_connection_get_setting_connection (conn); + + grid = NMT_PAGE_GRID (page_main); + + widget = nmt_newt_entry_new (40, NMT_NEWT_ENTRY_NONEMPTY); + g_object_bind_property (s_con, NM_SETTING_CONNECTION_ID, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Profile name"), widget, NULL); + + if (priv->type_data->virtual) + hardware_type = G_TYPE_NONE; + else + hardware_type = priv->type_data->device_type; + + widget = nmt_device_entry_new (_("Device"), 40, hardware_type); + nmt_page_grid_append (grid, NULL, widget, NULL); + deventry = NMT_DEVICE_ENTRY (widget); + g_object_bind_property (s_con, NM_SETTING_CONNECTION_INTERFACE_NAME, + deventry, "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + if (nm_connection_is_type (conn, NM_SETTING_BOND_SETTING_NAME)) + page = nmt_page_bond_new (conn, deventry); + else if (nm_connection_is_type (conn, NM_SETTING_BRIDGE_SETTING_NAME)) + page = nmt_page_bridge_new (conn, deventry); + else if (nm_connection_is_type (conn, NM_SETTING_INFINIBAND_SETTING_NAME)) + page = nmt_page_infiniband_new (conn, deventry); + else if (nm_connection_is_type (conn, NM_SETTING_TEAM_SETTING_NAME)) + page = nmt_page_team_new (conn, deventry); + else if (nm_connection_is_type (conn, NM_SETTING_VLAN_SETTING_NAME)) + page = nmt_page_vlan_new (conn, deventry); + else if (nm_connection_is_type (conn, NM_SETTING_WIRED_SETTING_NAME)) + page = nmt_page_ethernet_new (conn, deventry); + else if (nm_connection_is_type (conn, NM_SETTING_WIRELESS_SETTING_NAME)) + page = nmt_page_wifi_new (conn, deventry); + else + page = NULL; + + if (page) { + gboolean show_by_default = nmt_page_device_get_show_by_default (NMT_PAGE_DEVICE (page)); + + section = build_section_for_page (NMT_EDITOR_PAGE (page), show_by_default); + nmt_page_grid_append (grid, NULL, section, NULL); + } + + nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + slave_type = nm_setting_connection_get_slave_type (s_con); + if (slave_type) { + if (!strcmp (slave_type, NM_SETTING_BRIDGE_SETTING_NAME)) { + page = nmt_page_bridge_port_new (conn); + section = build_section_for_page (NMT_EDITOR_PAGE (page), TRUE); + nmt_page_grid_append (grid, NULL, section, NULL); + } else if (!strcmp (slave_type, NM_SETTING_TEAM_SETTING_NAME)) { + page = nmt_page_team_port_new (conn); + section = build_section_for_page (NMT_EDITOR_PAGE (page), TRUE); + nmt_page_grid_append (grid, NULL, section, NULL); + } + } else { + page = nmt_page_ip4_new (conn); + section = build_section_for_page (NMT_EDITOR_PAGE (page), + nmt_page_ip4_is_non_empty (NMT_PAGE_IP4 (page))); + nmt_page_grid_append (grid, NULL, section, NULL); + + /* Add a separator between ip4 and ip6 that's only visible if ip4 is open */ + separator = nmt_newt_separator_new (); + g_object_bind_property (section, "open", separator, "visible", G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, NULL, separator, NULL); + + page = nmt_page_ip6_new (conn); + section = build_section_for_page (NMT_EDITOR_PAGE (page), + nmt_page_ip6_is_non_empty (NMT_PAGE_IP6 (page))); + nmt_page_grid_append (grid, NULL, section, NULL); + nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + } + + widget = nmt_newt_checkbox_new (_("Automatically connect")); + g_object_bind_property (s_con, NM_SETTING_CONNECTION_AUTOCONNECT, + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, NULL, widget, NULL); + + widget = nmt_newt_checkbox_new (_("Available to all users")); + g_object_bind_property_full (s_con, NM_SETTING_CONNECTION_PERMISSIONS, + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + permissions_transform_to_allusers, + permissions_transform_from_allusers, + NULL, NULL); + nmt_page_grid_append (grid, NULL, widget, NULL); + + G_OBJECT_CLASS (nmt_page_main_parent_class)->constructed (object); +} + +static void +nmt_page_main_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtPageMainPrivate *priv = NMT_PAGE_MAIN_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_TYPE_DATA: + priv->type_data = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_page_main_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtPageMainPrivate *priv = NMT_PAGE_MAIN_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_TYPE_DATA: + g_value_set_pointer (value, priv->type_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_page_main_class_init (NmtPageMainClass *main_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (main_class); + + g_type_class_add_private (main_class, sizeof (NmtPageMainPrivate)); + + object_class->constructed = nmt_page_main_constructed; + object_class->set_property = nmt_page_main_set_property; + object_class->get_property = nmt_page_main_get_property; + + /** + * NmtPageMain:type-data: + * + * The page's connection's #NMEditorConnectionTypeData + */ + g_object_class_install_property (object_class, PROP_TYPE_DATA, + g_param_spec_pointer ("type-data", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-page-main.h b/tui/nmt-page-main.h new file mode 100644 index 0000000000..20de14f36a --- /dev/null +++ b/tui/nmt-page-main.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_MAIN_H +#define NMT_PAGE_MAIN_H + +#include "nmt-editor-page.h" +#include "nm-editor-utils.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_MAIN (nmt_page_main_get_type ()) +#define NMT_PAGE_MAIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_MAIN, NmtPageMain)) +#define NMT_PAGE_MAIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_MAIN, NmtPageMainClass)) +#define NMT_IS_PAGE_MAIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_MAIN)) +#define NMT_IS_PAGE_MAIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_MAIN)) +#define NMT_PAGE_MAIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_MAIN, NmtPageMainClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageMain; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageMainClass; + +GType nmt_page_main_get_type (void); + +NmtNewtWidget *nmt_page_main_new (NMConnection *conn, + NMEditorConnectionTypeData *type_data); + +G_END_DECLS + +#endif /* NMT_PAGE_MAIN_H */ diff --git a/tui/nmt-page-team-port.c b/tui/nmt-page-team-port.c new file mode 100644 index 0000000000..aec6b80394 --- /dev/null +++ b/tui/nmt-page-team-port.c @@ -0,0 +1,125 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-team-port + * @short_description: The editor page for Team ports. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-team-port.h" + +G_DEFINE_TYPE (NmtPageTeamPort, nmt_page_team_port, NMT_TYPE_EDITOR_PAGE) + +#define NMT_PAGE_TEAM_PORT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortPrivate)) + +typedef struct { + NMSettingTeamPort *s_port; + +} NmtPageTeamPortPrivate; + +NmtNewtWidget * +nmt_page_team_port_new (NMConnection *conn) +{ + return g_object_new (NMT_TYPE_PAGE_TEAM_PORT, + "connection", conn, + "title", _("TEAM PORT"), + NULL); +} + +static void +nmt_page_team_port_init (NmtPageTeamPort *team) +{ +} + +static void +edit_clicked (NmtNewtButton *button, + gpointer user_data) +{ + NmtPageTeamPort *team = user_data; + NmtPageTeamPortPrivate *priv = NMT_PAGE_TEAM_PORT_GET_PRIVATE (team); + const char *config; + char *new_config; + + config = nm_setting_team_port_get_config (priv->s_port); + if (!config) + config = ""; + + new_config = nmt_newt_edit_string (config); + + if (new_config && !*new_config) + g_clear_pointer (&new_config, g_free); + g_object_set (G_OBJECT (priv->s_port), + NM_SETTING_TEAM_PORT_CONFIG, new_config, + NULL); + g_free (new_config); +} + +static void +nmt_page_team_port_constructed (GObject *object) +{ + NmtPageTeamPort *team = NMT_PAGE_TEAM_PORT (object); + NmtPageTeamPortPrivate *priv = NMT_PAGE_TEAM_PORT_GET_PRIVATE (team); + NmtNewtGrid *grid; + NMSettingTeamPort *s_port; + NmtNewtWidget *widget; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (team)); + s_port = nm_connection_get_setting_team_port (conn); + if (!s_port) { + nm_connection_add_setting (conn, nm_setting_team_port_new ()); + s_port = nm_connection_get_setting_team_port (conn); + } + priv->s_port = s_port; + + widget = nmt_newt_grid_new (); + nmt_page_grid_append (NMT_PAGE_GRID (team), NULL, widget, NULL); + + grid = NMT_NEWT_GRID (widget); + + widget = nmt_newt_label_new (_("JSON configuration")); + nmt_newt_grid_add (grid, widget, 0, 2); + + widget = nmt_newt_textbox_new (NMT_NEWT_TEXTBOX_SCROLLABLE | NMT_NEWT_TEXTBOX_SET_BACKGROUND, 60); + g_object_bind_property (s_port, NM_SETTING_TEAM_PORT_CONFIG, + widget, "text", + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (grid, widget, 0, 3); + nmt_newt_widget_set_padding (widget, 2, 0, 2, 1); + + widget = nmt_newt_button_new (_("Edit...")); + g_signal_connect (widget, "clicked", G_CALLBACK (edit_clicked), team); + nmt_newt_grid_add (grid, widget, 0, 4); + + G_OBJECT_CLASS (nmt_page_team_port_parent_class)->constructed (object); +} + +static void +nmt_page_team_port_class_init (NmtPageTeamPortClass *team_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (team_class); + + g_type_class_add_private (team_class, sizeof (NmtPageTeamPortPrivate)); + + object_class->constructed = nmt_page_team_port_constructed; +} diff --git a/tui/nmt-page-team-port.h b/tui/nmt-page-team-port.h new file mode 100644 index 0000000000..d4ec7a557f --- /dev/null +++ b/tui/nmt-page-team-port.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_TEAM_PORT_H +#define NMT_PAGE_TEAM_PORT_H + +#include "nmt-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_TEAM_PORT (nmt_page_team_port_get_type ()) +#define NMT_PAGE_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPort)) +#define NMT_PAGE_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortClass)) +#define NMT_IS_PAGE_TEAM_PORT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_TEAM_PORT)) +#define NMT_IS_PAGE_TEAM_PORT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_TEAM_PORT)) +#define NMT_PAGE_TEAM_PORT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_TEAM_PORT, NmtPageTeamPortClass)) + +typedef struct { + NmtEditorPage parent; + +} NmtPageTeamPort; + +typedef struct { + NmtEditorPageClass parent; + +} NmtPageTeamPortClass; + +GType nmt_page_team_port_get_type (void); + +NmtNewtWidget *nmt_page_team_port_new (NMConnection *conn); + +G_END_DECLS + +#endif /* NMT_PAGE_TEAM_PORT_H */ diff --git a/tui/nmt-page-team.c b/tui/nmt-page-team.c new file mode 100644 index 0000000000..81db3d33f7 --- /dev/null +++ b/tui/nmt-page-team.c @@ -0,0 +1,195 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-team + * @short_description: The editor page for Team connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-page-team.h" + +#include "nmt-slave-list.h" + +G_DEFINE_TYPE (NmtPageTeam, nmt_page_team, NMT_TYPE_PAGE_DEVICE) + +#define NMT_PAGE_TEAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_TEAM, NmtPageTeamPrivate)) + +typedef struct { + NmtSlaveList *slaves; + + NMSettingTeam *s_team; + GType slave_type; + +} NmtPageTeamPrivate; + +NmtNewtWidget * +nmt_page_team_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_TEAM, + "connection", conn, + "title", _("TEAM"), + "device-entry", deventry, + NULL); +} + +static void +nmt_page_team_init (NmtPageTeam *team) +{ + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team); + + priv->slave_type = G_TYPE_NONE; +} + +static void +slaves_changed (GObject *object, + GParamSpec *pspec, + gpointer user_data) +{ + NmtPageTeam *team = NMT_PAGE_TEAM (user_data); + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team); + GPtrArray *slaves; + + g_object_get (object, "connections", &slaves, NULL); + if (slaves->len == 0) { + priv->slave_type = G_TYPE_NONE; + } else if (priv->slave_type == G_TYPE_NONE) { + NMConnection *slave = slaves->pdata[0]; + + if (nm_connection_is_type (slave, NM_SETTING_INFINIBAND_SETTING_NAME)) + priv->slave_type = NM_TYPE_SETTING_INFINIBAND; + else + priv->slave_type = NM_TYPE_SETTING_WIRED; + } +} + +static gboolean +team_connection_type_filter (GType connection_type, + gpointer user_data) +{ + NmtPageTeam *team = user_data; + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team); + + if (priv->slave_type != NM_TYPE_SETTING_WIRED) { + if (connection_type == NM_TYPE_SETTING_INFINIBAND) + return TRUE; + } + if (priv->slave_type != NM_TYPE_SETTING_INFINIBAND) { + if ( connection_type == NM_TYPE_SETTING_WIRED + || connection_type == NM_TYPE_SETTING_WIRELESS + || connection_type == NM_TYPE_SETTING_VLAN) + return TRUE; + } + + return FALSE; +} + +static void +edit_clicked (NmtNewtButton *button, + gpointer user_data) +{ + NmtPageTeam *team = user_data; + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team); + const char *config; + char *new_config; + + config = nm_setting_team_get_config (priv->s_team); + if (!config) + config = ""; + + new_config = nmt_newt_edit_string (config); + + if (new_config && !*new_config) + g_clear_pointer (&new_config, g_free); + g_object_set (G_OBJECT (priv->s_team), + NM_SETTING_TEAM_CONFIG, new_config, + NULL); + g_free (new_config); +} + +static void +nmt_page_team_constructed (GObject *object) +{ + NmtPageTeam *team = NMT_PAGE_TEAM (object); + NmtPageTeamPrivate *priv = NMT_PAGE_TEAM_GET_PRIVATE (team); + NmtDeviceEntry *deventry; + NmtNewtGrid *grid; + NMSettingTeam *s_team; + NmtNewtWidget *widget; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (team)); + s_team = nm_connection_get_setting_team (conn); + if (!s_team) { + nm_connection_add_setting (conn, nm_setting_team_new ()); + s_team = nm_connection_get_setting_team (conn); + } + priv->s_team = s_team; + + deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object)); + g_object_bind_property (s_team, NM_SETTING_TEAM_INTERFACE_NAME, + deventry, "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + widget = nmt_newt_grid_new (); + nmt_page_grid_append (NMT_PAGE_GRID (team), NULL, widget, NULL); + + grid = NMT_NEWT_GRID (widget); + + widget = nmt_newt_label_new (_("Slaves")); + nmt_newt_grid_add (grid, widget, 0, 0); + + widget = nmt_slave_list_new (conn, team_connection_type_filter, team); + g_signal_connect (widget, "notify::connections", + G_CALLBACK (slaves_changed), team); + nmt_newt_grid_add (grid, widget, 0, 1); + nmt_newt_widget_set_padding (widget, 0, 0, 0, 1); + priv->slaves = NMT_SLAVE_LIST (widget); + slaves_changed (G_OBJECT (priv->slaves), NULL, team); + + widget = nmt_newt_label_new (_("JSON configuration")); + nmt_newt_grid_add (grid, widget, 0, 2); + + widget = nmt_newt_textbox_new (NMT_NEWT_TEXTBOX_SCROLLABLE | NMT_NEWT_TEXTBOX_SET_BACKGROUND, 60); + g_object_bind_property (s_team, NM_SETTING_TEAM_CONFIG, + widget, "text", + G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (grid, widget, 0, 3); + nmt_newt_widget_set_padding (widget, 2, 0, 2, 1); + + widget = nmt_newt_button_new (_("Edit...")); + g_signal_connect (widget, "clicked", G_CALLBACK (edit_clicked), team); + nmt_newt_grid_add (grid, widget, 0, 4); + + G_OBJECT_CLASS (nmt_page_team_parent_class)->constructed (object); +} + +static void +nmt_page_team_class_init (NmtPageTeamClass *team_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (team_class); + + g_type_class_add_private (team_class, sizeof (NmtPageTeamPrivate)); + + object_class->constructed = nmt_page_team_constructed; +} diff --git a/tui/nmt-page-team.h b/tui/nmt-page-team.h new file mode 100644 index 0000000000..49a81dd55e --- /dev/null +++ b/tui/nmt-page-team.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_TEAM_H +#define NMT_PAGE_TEAM_H + +#include "nmt-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_TEAM (nmt_page_team_get_type ()) +#define NMT_PAGE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_TEAM, NmtPageTeam)) +#define NMT_PAGE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_TEAM, NmtPageTeamClass)) +#define NMT_IS_PAGE_TEAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_TEAM)) +#define NMT_IS_PAGE_TEAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_TEAM)) +#define NMT_PAGE_TEAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_TEAM, NmtPageTeamClass)) + +typedef struct { + NmtPageDevice parent; + +} NmtPageTeam; + +typedef struct { + NmtPageDeviceClass parent; + +} NmtPageTeamClass; + +GType nmt_page_team_get_type (void); + +NmtNewtWidget *nmt_page_team_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_TEAM_H */ diff --git a/tui/nmt-page-vlan.c b/tui/nmt-page-vlan.c new file mode 100644 index 0000000000..f6de671c85 --- /dev/null +++ b/tui/nmt-page-vlan.c @@ -0,0 +1,163 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-vlan + * @short_description: The editor page for VLAN connections + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include <nm-device-ethernet.h> + +#include "nm-editor-bindings.h" + +#include "nmt-page-vlan.h" +#include "nmt-device-entry.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" + +G_DEFINE_TYPE (NmtPageVlan, nmt_page_vlan, NMT_TYPE_PAGE_DEVICE) + +#define NMT_PAGE_VLAN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_VLAN, NmtPageVlanPrivate)) + +typedef struct { + NMSettingWired *s_wired; + +} NmtPageVlanPrivate; + +NmtNewtWidget * +nmt_page_vlan_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_VLAN, + "connection", conn, + "title", _("VLAN"), + "device-entry", deventry, + NULL); +} + +static void +nmt_page_vlan_init (NmtPageVlan *vlan) +{ +} + +static gboolean +vlan_device_filter (NmtDeviceEntry *deventry, + NMDevice *device, + gpointer user_data) +{ + // FIXME + return NM_IS_DEVICE_ETHERNET (device); +} + +static void +nmt_page_vlan_constructed (GObject *object) +{ + NmtPageVlan *vlan = NMT_PAGE_VLAN (object); + NmtPageVlanPrivate *priv = NMT_PAGE_VLAN_GET_PRIVATE (vlan); + NmtDeviceEntry *deventry; + NmtPageGrid *grid; + NMSettingWired *s_wired; + NMSettingVlan *s_vlan; + NmtNewtWidget *widget, *parent, *id_entry; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (vlan)); + s_vlan = nm_connection_get_setting_vlan (conn); + if (!s_vlan) { + nm_connection_add_setting (conn, nm_setting_vlan_new ()); + s_vlan = nm_connection_get_setting_vlan (conn); + } + s_wired = nm_connection_get_setting_wired (conn); + if (!s_wired) { + /* It makes things simpler if we always have a NMSettingWired; + * we'll hold a ref on one, and add it to and remove it from + * the connection as needed. + */ + s_wired = NM_SETTING_WIRED (nm_setting_wired_new ()); + } + priv->s_wired = g_object_ref_sink (s_wired); + + deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object)); + g_object_bind_property (s_vlan, NM_SETTING_VLAN_INTERFACE_NAME, + deventry, "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + grid = NMT_PAGE_GRID (vlan); + + nm_editor_bind_vlan_name (s_vlan); + + widget = parent = nmt_device_entry_new (_("Parent"), 40, G_TYPE_NONE); + nmt_device_entry_set_device_filter (NMT_DEVICE_ENTRY (widget), + vlan_device_filter, vlan); + g_object_bind_property (s_vlan, NM_SETTING_VLAN_PARENT, + widget, "interface-name", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + g_object_bind_property (s_wired, NM_SETTING_WIRED_MAC_ADDRESS, + widget, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, NULL, widget, NULL); + + widget = id_entry = nmt_newt_entry_numeric_new (8, 0, 4095); + g_object_bind_property (s_vlan, NM_SETTING_VLAN_ID, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("VLAN id"), widget, NULL); + + nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_mac_entry_new (40, ETH_ALEN); + g_object_bind_property (s_wired, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + widget, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Cloned MAC address"), widget, NULL); + + widget = nmt_mtu_entry_new (); + g_object_bind_property (s_wired, NM_SETTING_WIRED_MTU, + widget, "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("MTU"), widget, NULL); + + G_OBJECT_CLASS (nmt_page_vlan_parent_class)->constructed (object); +} + +static void +nmt_page_vlan_finalize (GObject *object) +{ + NmtPageVlanPrivate *priv = NMT_PAGE_VLAN_GET_PRIVATE (object); + + g_clear_object (&priv->s_wired); + + G_OBJECT_CLASS (nmt_page_vlan_parent_class)->finalize (object); +} + +static void +nmt_page_vlan_class_init (NmtPageVlanClass *vlan_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (vlan_class); + + g_type_class_add_private (vlan_class, sizeof (NmtPageVlanPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_page_vlan_constructed; + object_class->finalize = nmt_page_vlan_finalize; +} diff --git a/tui/nmt-page-vlan.h b/tui/nmt-page-vlan.h new file mode 100644 index 0000000000..57d0024388 --- /dev/null +++ b/tui/nmt-page-vlan.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_VLAN_H +#define NMT_PAGE_VLAN_H + +#include "nmt-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_VLAN (nmt_page_vlan_get_type ()) +#define NMT_PAGE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_VLAN, NmtPageVlan)) +#define NMT_PAGE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_VLAN, NmtPageVlanClass)) +#define NMT_IS_PAGE_VLAN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_VLAN)) +#define NMT_IS_PAGE_VLAN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_VLAN)) +#define NMT_PAGE_VLAN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_VLAN, NmtPageVlanClass)) + +typedef struct { + NmtPageDevice parent; + +} NmtPageVlan; + +typedef struct { + NmtPageDeviceClass parent; + +} NmtPageVlanClass; + +GType nmt_page_vlan_get_type (void); + +NmtNewtWidget *nmt_page_vlan_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_VLAN_H */ diff --git a/tui/nmt-page-wifi.c b/tui/nmt-page-wifi.c new file mode 100644 index 0000000000..153aee7ca5 --- /dev/null +++ b/tui/nmt-page-wifi.c @@ -0,0 +1,389 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-page-wifi + * @short_description: The editor page for Wi-Fi connections + * + * #NmtPageWifi is the editor page for Wi-Fi connections, which + * includes both #NMSettingWireless and #NMSettingWirelessSecurity + * properties. + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> +#include <nm-utils.h> + +#include "nmt-page-wifi.h" +#include "nmt-mac-entry.h" +#include "nmt-mtu-entry.h" +#include "nmt-password-fields.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE (NmtPageWifi, nmt_page_wifi, NMT_TYPE_PAGE_DEVICE) + +#define NMT_PAGE_WIFI_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PAGE_WIFI, NmtPageWifiPrivate)) + +typedef struct { + NMSettingWirelessSecurity *s_wsec; + +} NmtPageWifiPrivate; + +NmtNewtWidget * +nmt_page_wifi_new (NMConnection *conn, + NmtDeviceEntry *deventry) +{ + return g_object_new (NMT_TYPE_PAGE_WIFI, + "connection", conn, + "title", _("WI-FI"), + "device-entry", deventry, + NULL); +} + +static void +nmt_page_wifi_init (NmtPageWifi *wifi) +{ +} + +static NmtNewtPopupEntry wifi_mode[] = { + { NC_("Wi-Fi", "Client"), NM_SETTING_WIRELESS_MODE_INFRA }, + { N_("Access Point"), NM_SETTING_WIRELESS_MODE_AP }, + { N_("Ad-Hoc Network"), NM_SETTING_WIRELESS_MODE_ADHOC }, + { NULL, NULL } +}; + +static NmtNewtPopupEntry wifi_band[] = { + { NC_("Wi-Fi", "Automatic"), NULL }, + /* 802.11a Wi-Fi network */ + { N_("A (5 GHz)"), "a" }, + /* 802.11b / 802.11g Wi-Fi network */ + { N_("B/G (2.4 GHz)"), "bg" }, + { NULL, NULL } +}; + +static NmtNewtPopupEntry wifi_security[] = { + { NC_("Wi-Fi security", "None"), "none" }, + { N_("WPA & WPA2 Personal"), "wpa-personal" }, + { N_("WPA & WPA2 Enterprise"), "wpa-enterprise" }, + { N_("WEP 40/128-bit Key (Hex or ASCII)"), "wep-key" }, + { N_("WEP 128-bit Passphrase"), "wep-passphrase" }, + { N_("Dynamic WEP (802.1x)"), "dynamic-wep" }, + { N_("LEAP"), "leap" }, + { NULL, NULL } +}; + +static NmtNewtPopupEntry wep_index[] = { + { NC_("WEP key index", "1 (Default)"), "1" }, + { NC_("WEP key index", "2"), "2" }, + { NC_("WEP key index", "3"), "3" }, + { NC_("WEP key index", "4"), "4" }, + { NULL, NULL } +}; + +static NmtNewtPopupEntry wep_auth[] = { + { N_("Open System"), "open" }, + { N_("Shared Key"), "shared" }, + { NULL, NULL } +}; + +static gboolean +mode_transform_to_band_visibility (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + if (!g_strcmp0 (g_value_get_string (source_value), NM_SETTING_WIRELESS_MODE_INFRA)) + g_value_set_boolean (target_value, FALSE); + else + g_value_set_boolean (target_value, TRUE); + return TRUE; +} + +static gboolean +band_transform_to_channel_visibility (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + g_value_set_boolean (target_value, g_value_get_string (source_value) != NULL); + return TRUE; +} + +static gboolean +ssid_transform_to_entry (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GByteArray *ssid; + char *utf8; + + ssid = g_value_get_boxed (source_value); + utf8 = nm_utils_ssid_to_utf8 (ssid); + g_value_take_string (target_value, utf8); + return TRUE; +} + +static gboolean +ssid_transform_from_entry (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NMSettingWireless *s_wireless = user_data; + const char *text; + const GByteArray *old_ssid; + GByteArray *ssid; + char *utf8; + + text = g_value_get_string (source_value); + + old_ssid = nm_setting_wireless_get_ssid (s_wireless); + utf8 = nm_utils_ssid_to_utf8 (old_ssid); + + if (!g_strcmp0 (text, utf8)) { + g_free (utf8); + return FALSE; + } + g_free (utf8); + + ssid = g_byte_array_new (); + g_byte_array_append (ssid, (guint8 *)text, strlen (text)); + g_value_take_boxed (target_value, ssid); + return TRUE; +} + +static void +nmt_page_wifi_constructed (GObject *object) +{ + NmtPageWifiPrivate *priv = NMT_PAGE_WIFI_GET_PRIVATE (object); + NmtPageWifi *wifi = NMT_PAGE_WIFI (object); + NmtDeviceEntry *deventry; + NmtPageGrid *grid; + NMSettingWireless *s_wireless; + NMSettingWirelessSecurity *s_wsec; + NmtNewtWidget *widget, *hbox, *subgrid; + NmtNewtWidget *mode, *band, *security, *entry; + NmtNewtStack *stack; + NMConnection *conn; + + conn = nmt_editor_page_get_connection (NMT_EDITOR_PAGE (wifi)); + s_wireless = nm_connection_get_setting_wireless (conn); + if (!s_wireless) { + nm_connection_add_setting (conn, nm_setting_wireless_new ()); + s_wireless = nm_connection_get_setting_wireless (conn); + } + + s_wsec = nm_connection_get_setting_wireless_security (conn); + if (!s_wsec) { + /* It makes things simpler if we always have a + * NMSettingWirelessSecurity; we'll hold a ref on one, and add + * it to and remove it from the connection as needed. + */ + s_wsec = NM_SETTING_WIRELESS_SECURITY (nm_setting_wireless_security_new ()); + } + priv->s_wsec = g_object_ref_sink (s_wsec); + + deventry = nmt_page_device_get_device_entry (NMT_PAGE_DEVICE (object)); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MAC_ADDRESS, + deventry, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + grid = NMT_PAGE_GRID (wifi); + + widget = nmt_newt_entry_new (40, NMT_NEWT_ENTRY_NONEMPTY); + g_object_bind_property_full (s_wireless, NM_SETTING_WIRELESS_SSID, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + ssid_transform_to_entry, + ssid_transform_from_entry, + s_wireless, NULL); + nmt_page_grid_append (grid, _("SSID"), widget, NULL); + + widget = nmt_newt_popup_new (wifi_mode); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MODE, + widget, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Mode"), widget, NULL); + mode = widget; + + hbox = nmt_newt_grid_new (); + widget = nmt_newt_popup_new (wifi_band); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_BAND, + widget, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (NMT_NEWT_GRID (hbox), widget, 0, 0); + band = widget; + + widget = nmt_newt_entry_numeric_new (10, 0, 255); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_CHANNEL, + widget, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_newt_grid_add (NMT_NEWT_GRID (hbox), widget, 1, 0); + nmt_newt_widget_set_padding (widget, 1, 0, 0, 0); + + g_object_bind_property_full (band, "active-id", widget, "visible", + G_BINDING_SYNC_CREATE, + band_transform_to_channel_visibility, + NULL, NULL, NULL); + g_object_bind_property_full (mode, "active-id", hbox, "visible", + G_BINDING_SYNC_CREATE, + mode_transform_to_band_visibility, + NULL, NULL, NULL); + nmt_page_grid_append (grid, _("Channel"), hbox, NULL); + + nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_newt_popup_new (wifi_security); + nmt_page_grid_append (grid, _("Security"), widget, NULL); + security = widget; + + widget = nmt_newt_stack_new (); + stack = NMT_NEWT_STACK (widget); + + /* none */ + subgrid = nmt_page_grid_new (); + nmt_newt_stack_add (stack, "none", subgrid); + + /* wpa-personal */ + subgrid = nmt_page_grid_new (); + widget = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + g_object_bind_property (s_wsec, NM_SETTING_WIRELESS_SECURITY_PSK, + widget, "password", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Password"), widget, NULL); + nmt_newt_stack_add (stack, "wpa-personal", subgrid); + + /* "wpa-enterprise" */ + // FIXME + widget = nmt_newt_label_new (_("(No support for wpa-enterprise yet...)")); + nmt_newt_stack_add (stack, "wpa-enterprise", widget); + + /* wep-key */ + subgrid = nmt_page_grid_new (); + + widget = entry = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Key"), widget, NULL); + + widget = nmt_newt_popup_new (wep_index); + nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("WEP index"), widget, NULL); + + nm_editor_bind_wireless_security_wep_key (s_wsec, + entry, "password", + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + widget = nmt_newt_popup_new (wep_auth); + nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Authentication"), widget, NULL); + + nmt_newt_stack_add (stack, "wep-key", subgrid); + + /* wep-passphrase */ + subgrid = nmt_page_grid_new (); + + widget = entry = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Password"), widget, NULL); + + widget = nmt_newt_popup_new (wep_index); + nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("WEP index"), widget, NULL); + + nm_editor_bind_wireless_security_wep_key (s_wsec, + entry, "password", + widget, "active", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + widget = nmt_newt_popup_new (wep_auth); + nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Authentication"), widget, NULL); + + nmt_newt_stack_add (stack, "wep-passphrase", subgrid); + + /* "dynamic-wep" */ + // FIXME + widget = nmt_newt_label_new (_("(No support for dynamic-wep yet...)")); + nmt_newt_stack_add (stack, "dynamic-wep", widget); + + /* leap */ + subgrid = nmt_page_grid_new (); + + widget = nmt_newt_entry_new (40, NMT_NEWT_ENTRY_NONEMPTY); + nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Username"), widget, NULL); + + widget = nmt_password_fields_new (40, NMT_PASSWORD_FIELDS_SHOW_PASSWORD); + g_object_bind_property (s_wsec, NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + widget, "password", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + nmt_page_grid_append (NMT_PAGE_GRID (subgrid), _("Password"), widget, NULL); + + nmt_newt_stack_add (stack, "leap", subgrid); + + nmt_page_grid_append (grid, NULL, NMT_NEWT_WIDGET (stack), NULL); + g_object_bind_property (security, "active-id", + stack, "active-id", + G_BINDING_SYNC_CREATE); + nm_editor_bind_wireless_security_method (conn, s_wsec, security, "active-id", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + + nmt_page_grid_append (grid, NULL, nmt_newt_separator_new (), NULL); + + widget = nmt_mac_entry_new (40, ETH_ALEN); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_BSSID, + widget, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("BSSID"), widget, NULL); + + widget = nmt_mac_entry_new (40, ETH_ALEN); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, + widget, "mac-address", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("Cloned MAC address"), widget, NULL); + + widget = nmt_mtu_entry_new (); + g_object_bind_property (s_wireless, NM_SETTING_WIRELESS_MTU, + widget, "mtu", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + nmt_page_grid_append (grid, _("MTU"), widget, NULL); + + G_OBJECT_CLASS (nmt_page_wifi_parent_class)->constructed (object); +} + +static void +nmt_page_wifi_finalize (GObject *object) +{ + NmtPageWifiPrivate *priv = NMT_PAGE_WIFI_GET_PRIVATE (object); + + g_clear_object (&priv->s_wsec); + + G_OBJECT_CLASS (nmt_page_wifi_parent_class)->constructed (object); +} + + +static void +nmt_page_wifi_class_init (NmtPageWifiClass *wifi_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (wifi_class); + + g_type_class_add_private (wifi_class, sizeof (NmtPageWifiPrivate)); + + object_class->constructed = nmt_page_wifi_constructed; + object_class->finalize = nmt_page_wifi_finalize; +} diff --git a/tui/nmt-page-wifi.h b/tui/nmt-page-wifi.h new file mode 100644 index 0000000000..06cb2a9805 --- /dev/null +++ b/tui/nmt-page-wifi.h @@ -0,0 +1,50 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PAGE_WIFI_H +#define NMT_PAGE_WIFI_H + +#include "nmt-page-device.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PAGE_WIFI (nmt_page_wifi_get_type ()) +#define NMT_PAGE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PAGE_WIFI, NmtPageWifi)) +#define NMT_PAGE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PAGE_WIFI, NmtPageWifiClass)) +#define NMT_IS_PAGE_WIFI(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PAGE_WIFI)) +#define NMT_IS_PAGE_WIFI_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PAGE_WIFI)) +#define NMT_PAGE_WIFI_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PAGE_WIFI, NmtPageWifiClass)) + +typedef struct { + NmtPageDevice parent; + +} NmtPageWifi; + +typedef struct { + NmtPageDeviceClass parent; + +} NmtPageWifiClass; + +GType nmt_page_wifi_get_type (void); + +NmtNewtWidget *nmt_page_wifi_new (NMConnection *conn, + NmtDeviceEntry *deventry); + +G_END_DECLS + +#endif /* NMT_PAGE_WIFI_H */ diff --git a/tui/nmt-password-dialog.c b/tui/nmt-password-dialog.c new file mode 100644 index 0000000000..00c9a17e04 --- /dev/null +++ b/tui/nmt-password-dialog.c @@ -0,0 +1,343 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-password-dialog + * @short_description: A password dialog + * + * #NmtPasswordDialog is the password dialog used to get connection + * secrets when activating a connection. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-password-dialog.h" +#include "nmt-secret-agent.h" +#include "nmtui.h" + +G_DEFINE_TYPE (NmtPasswordDialog, nmt_password_dialog, NMT_TYPE_NEWT_FORM) + +#define NMT_PASSWORD_DIALOG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogPrivate)) + +typedef struct { + char *request_id; + char *prompt; + GPtrArray *secrets; + GPtrArray *entries; + + NmtNewtWidget *ok, *cancel; + NmtNewtWidget *last_entry; + NmtNewtWidget *secret_grid; + + gboolean succeeded; +} NmtPasswordDialogPrivate; + +enum { + PROP_0, + PROP_REQUEST_ID, + PROP_PROMPT, + PROP_SECRETS, + + LAST_PROP +}; + +/** + * nmt_password_dialog_new: + * @request_id: the request ID from the #NmtSecretAgent + * @title: the dialog title + * @prompt: the prompt text to display + * @secrets: (element-type #NmtSecretAgentSecret): the secrets requested + * + * Creates a new #NmtPasswordDialog to request passwords from + * the user. + * + * Returns: a new #NmtPasswordDialog. + */ +NmtNewtForm * +nmt_password_dialog_new (const char *request_id, + const char *title, + const char *prompt, + GPtrArray *secrets) +{ + return g_object_new (NMT_TYPE_PASSWORD_DIALOG, + "request-id", request_id, + "title", title, + "prompt", prompt, + "secrets", secrets, + "escape-exits", TRUE, + NULL); +} + +static void +nmt_password_dialog_init (NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + + priv->entries = g_ptr_array_new (); +} + +static void +maybe_save_input_and_exit (NmtNewtWidget *widget, + newtComponent co, + gpointer dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + int i; + + /* This gets invoked when the user types Return in the final entry, + * but the form may not be fully valid in that case. + */ + if (!nmt_newt_widget_get_valid (priv->secret_grid)) + return; + + priv->succeeded = TRUE; + + for (i = 0; i < priv->secrets->len; i++) { + NmtSecretAgentSecret *secret = priv->secrets->pdata[i]; + + g_free (secret->value); + g_object_get (priv->entries->pdata[i], "text", &secret->value, NULL); + } + + nmt_newt_form_quit (nmt_newt_widget_get_form (widget)); +} + +static void +nmt_password_dialog_constructed (GObject *object) +{ + NmtPasswordDialog *dialog = NMT_PASSWORD_DIALOG (object); + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + NmtNewtWidget *widget; + NmtNewtGrid *grid, *secret_grid; + NmtNewtButtonBox *bbox; + int i; + + widget = nmt_newt_grid_new (); + nmt_newt_form_set_content (NMT_NEWT_FORM (dialog), widget); + grid = NMT_NEWT_GRID (widget); + + widget = nmt_newt_textbox_new (0, 60); + nmt_newt_textbox_set_text (NMT_NEWT_TEXTBOX (widget), priv->prompt); + nmt_newt_grid_add (grid, widget, 0, 0); + + widget = nmt_newt_grid_new (); + nmt_newt_grid_add (grid, widget, 0, 1); + nmt_newt_widget_set_padding (widget, 0, 1, 0, 1); + priv->secret_grid = widget; + secret_grid = NMT_NEWT_GRID (widget); + + for (i = 0; i < priv->secrets->len; i++) { + NmtSecretAgentSecret *secret = priv->secrets->pdata[i]; + NmtNewtEntryFlags flags; + + widget = nmt_newt_label_new (secret->name); + nmt_newt_grid_add (secret_grid, widget, 0, i); + nmt_newt_widget_set_padding (widget, 4, 0, 1, 0); + + flags = NMT_NEWT_ENTRY_NONEMPTY; + if (secret->password) + flags |= NMT_NEWT_ENTRY_PASSWORD; + widget = nmt_newt_entry_new (30, flags); + nmt_newt_grid_add (secret_grid, widget, 1, i); + g_ptr_array_add (priv->entries, widget); + + if (i == priv->secrets->len - 1) { + priv->last_entry = widget; + g_signal_connect (widget, "activated", + G_CALLBACK (maybe_save_input_and_exit), dialog); + } + } + + widget = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add (grid, widget, 0, 2); + bbox = NMT_NEWT_BUTTON_BOX (widget); + + priv->cancel = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("Cancel")); + nmt_newt_widget_set_exit_on_activate (priv->cancel, TRUE); + + priv->ok = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("OK")); + g_signal_connect (priv->ok, "activated", + G_CALLBACK (maybe_save_input_and_exit), dialog); + g_object_bind_property (priv->secret_grid, "valid", + priv->ok, "sensitive", + G_BINDING_SYNC_CREATE); + + G_OBJECT_CLASS (nmt_password_dialog_parent_class)->constructed (object); +} + +static void +nmt_password_dialog_finalize (GObject *object) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (object); + + g_free (priv->request_id); + g_free (priv->prompt); + g_clear_pointer (&priv->entries, g_ptr_array_unref); + g_clear_pointer (&priv->secrets, g_ptr_array_unref); + + G_OBJECT_CLASS (nmt_password_dialog_parent_class)->finalize (object); +} + +static void +nmt_password_dialog_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_REQUEST_ID: + priv->request_id = g_value_dup_string (value); + break; + case PROP_PROMPT: + priv->prompt = g_value_dup_string (value); + break; + case PROP_SECRETS: + priv->secrets = g_value_dup_boxed (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_password_dialog_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_REQUEST_ID: + g_value_set_string (value, priv->request_id); + break; + case PROP_PROMPT: + g_value_set_string (value, priv->prompt); + break; + case PROP_SECRETS: + g_value_set_boxed (value, priv->secrets); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_password_dialog_class_init (NmtPasswordDialogClass *dialog_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (dialog_class); + + g_type_class_add_private (dialog_class, sizeof (NmtPasswordDialogPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_password_dialog_constructed; + object_class->set_property = nmt_password_dialog_set_property; + object_class->get_property = nmt_password_dialog_get_property; + object_class->finalize = nmt_password_dialog_finalize; + + /** + * NmtPasswordDialog:request-id: + * + * The request ID from the #NmtSecretAgent + */ + g_object_class_install_property (object_class, PROP_REQUEST_ID, + g_param_spec_string ("request-id", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordDialog:prompt: + * + * The prompt text. + */ + g_object_class_install_property (object_class, PROP_PROMPT, + g_param_spec_string ("prompt", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordDialog:secrets: + * + * The array of request secrets + * + * Element-Type: #NmtSecretAgentSecret. + */ + g_object_class_install_property (object_class, PROP_SECRETS, + g_param_spec_boxed ("secrets", "", "", + G_TYPE_PTR_ARRAY, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +/** + * nmt_password_dialog_succeeded: + * @dialog: the #NmtPasswordDialog + * + * After the dialog has exited, returns %TRUE if the user clicked + * "OK", %FALSE if "Cancel". + * + * Returns: whether the dialog succeeded. + */ +gboolean +nmt_password_dialog_succeeded (NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + + return priv->succeeded; +} + +/** + * nmt_password_dialog_get_request_id: + * @dialog: the #NmtPasswordDialog + * + * Gets the dialog's request ID. + * + * Returns: the dialog's request ID. + */ +const char * +nmt_password_dialog_get_request_id (NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + + return priv->request_id; +} + +/** + * nmt_password_dialog_get_secrets: + * @dialog: the #NmtPasswordDialog + * + * Gets the dialog's secrets array. + * + * Returns: (transfer none): the dialog's secrets array. + */ +GPtrArray * +nmt_password_dialog_get_secrets (NmtPasswordDialog *dialog) +{ + NmtPasswordDialogPrivate *priv = NMT_PASSWORD_DIALOG_GET_PRIVATE (dialog); + + return priv->secrets; +} diff --git a/tui/nmt-password-dialog.h b/tui/nmt-password-dialog.h new file mode 100644 index 0000000000..36c9f51e19 --- /dev/null +++ b/tui/nmt-password-dialog.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PASSWORD_DIALOG_H +#define NMT_PASSWORD_DIALOG_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PASSWORD_DIALOG (nmt_password_dialog_get_type ()) +#define NMT_PASSWORD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialog)) +#define NMT_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass)) +#define NMT_IS_PASSWORD_DIALOG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PASSWORD_DIALOG)) +#define NMT_IS_PASSWORD_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PASSWORD_DIALOG)) +#define NMT_PASSWORD_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PASSWORD_DIALOG, NmtPasswordDialogClass)) + +typedef struct { + NmtNewtForm parent; + +} NmtPasswordDialog; + +typedef struct { + NmtNewtFormClass parent; + +} NmtPasswordDialogClass; + +GType nmt_password_dialog_get_type (void); + +NmtNewtForm *nmt_password_dialog_new (const char *request_id, + const char *title, + const char *prompt, + GPtrArray *secrets); + +gboolean nmt_password_dialog_succeeded (NmtPasswordDialog *dialog); + +const char *nmt_password_dialog_get_request_id (NmtPasswordDialog *dialog); +GPtrArray *nmt_password_dialog_get_secrets (NmtPasswordDialog *dialog); + +G_END_DECLS + +#endif /* NMT_PASSWORD_DIALOG_H */ diff --git a/tui/nmt-password-fields.c b/tui/nmt-password-fields.c new file mode 100644 index 0000000000..ec8b521825 --- /dev/null +++ b/tui/nmt-password-fields.c @@ -0,0 +1,310 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-password-fields + * @short_description: Widgets for password-related data + * + * #NmtPasswordFields provides an entry to type a password into, followed + * optionally by an "Ask for this password every time" checkbox and/or a + * "Show password" checkbox that toggles whether the password is visible. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-password-fields.h" + +G_DEFINE_TYPE (NmtPasswordFields, nmt_password_fields, NMT_TYPE_NEWT_GRID) + +#define NMT_PASSWORD_FIELDS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsPrivate)) + +typedef struct { + NmtPasswordFieldsExtras extras; + + NmtNewtEntry *entry; + NmtNewtCheckbox *always_ask; + NmtNewtCheckbox *show_password; + + char *init_password; + +} NmtPasswordFieldsPrivate; + +enum { + PROP_0, + PROP_WIDTH, + PROP_EXTRAS, + PROP_PASSWORD, + PROP_ALWAYS_ASK, + PROP_SHOW_PASSWORD, + + LAST_PROP +}; + +/** + * NmtPasswordFieldsExtras: + * @NMT_PASSWORD_FIELDS_ALWAYS_ASK: show an "Always ask" checkbox + * @NMT_PASSWORD_FIELDS_SHOW_PASSWORD: show a "Show password" checkbox + * + * Extra widgets to include in an #NmtPasswordFields + */ + +/** + * nmt_password_fields_new: + * @width: width in characters of the password entry + * @extras: extra widgets to show + * + * Creates a new #NmtPasswordFields + * + * Returns: a new #NmtPasswordFields + */ +NmtNewtWidget * +nmt_password_fields_new (int width, + NmtPasswordFieldsExtras extras) +{ + return g_object_new (NMT_TYPE_PASSWORD_FIELDS, + "width", width, + "extras", extras, + NULL); +} + +static void +nmt_password_fields_set_password (NmtPasswordFields *fields, + const char *password) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields); + + if (!g_strcmp0 (password, nmt_newt_entry_get_text (priv->entry))) + return; + + nmt_newt_entry_set_text (priv->entry, password); + g_object_notify (G_OBJECT (fields), "password"); +} + +static const char * +nmt_password_fields_get_password (NmtPasswordFields *fields) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields); + + return nmt_newt_entry_get_text (priv->entry); +} + +static void +always_ask_changed (GObject *object, + GParamSpec *pspec, + gpointer fields) +{ + g_object_notify (fields, "always-ask"); +} + +static void +show_password_changed (GObject *object, + GParamSpec *pspec, + gpointer fields) +{ + g_object_notify (fields, "show-password"); +} + +static void +nmt_password_fields_init (NmtPasswordFields *fields) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields); + + priv->entry = NMT_NEWT_ENTRY (nmt_newt_entry_new (-1, 0)); + priv->always_ask = NMT_NEWT_CHECKBOX (nmt_newt_checkbox_new (_("Ask for this password every time"))); + priv->show_password = NMT_NEWT_CHECKBOX (nmt_newt_checkbox_new (_("Show password"))); +} + +static void +nmt_password_fields_constructed (GObject *object) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (object); + NmtNewtGrid *grid = NMT_NEWT_GRID (object); + + nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (priv->entry), 0, 0); + + if (priv->extras & NMT_PASSWORD_FIELDS_ALWAYS_ASK) { + nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (priv->always_ask), 0, 1); + g_signal_connect (priv->always_ask, "notify::active", + G_CALLBACK (always_ask_changed), object); + } else + g_clear_object (&priv->always_ask); + + if (priv->extras & NMT_PASSWORD_FIELDS_SHOW_PASSWORD) { + nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (priv->show_password), 0, 2); + g_signal_connect (priv->show_password, "notify::active", + G_CALLBACK (show_password_changed), object); + g_object_bind_property (priv->show_password, "active", + priv->entry, "password", + G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE); + } else + g_clear_object (&priv->show_password); + + G_OBJECT_CLASS (nmt_password_fields_parent_class)->constructed (object); +} + +static void +nmt_password_fields_finalize (GObject *object) +{ + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (object); + + if (priv->always_ask) { + g_signal_handlers_disconnect_by_func (priv->always_ask, + G_CALLBACK (always_ask_changed), object); + } + if (priv->show_password) { + g_signal_handlers_disconnect_by_func (priv->show_password, + G_CALLBACK (show_password_changed), object); + } + + G_OBJECT_CLASS (nmt_password_fields_parent_class)->finalize (object); +} + +static void +nmt_password_fields_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtPasswordFields *fields = NMT_PASSWORD_FIELDS (object); + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (fields); + + switch (prop_id) { + case PROP_WIDTH: + nmt_newt_entry_set_width (priv->entry, g_value_get_int (value)); + break; + case PROP_EXTRAS: + priv->extras = g_value_get_uint (value); + nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (fields)); + break; + case PROP_PASSWORD: + nmt_password_fields_set_password (fields, g_value_get_string (value)); + break; + case PROP_ALWAYS_ASK: + if (priv->always_ask) + nmt_newt_checkbox_set_active (priv->always_ask, g_value_get_boolean (value)); + break; + case PROP_SHOW_PASSWORD: + if (priv->show_password) + nmt_newt_checkbox_set_active (priv->show_password, g_value_get_boolean (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_password_fields_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtPasswordFields *entry = NMT_PASSWORD_FIELDS (object); + NmtPasswordFieldsPrivate *priv = NMT_PASSWORD_FIELDS_GET_PRIVATE (entry); + + switch (prop_id) { + case PROP_WIDTH: + g_value_set_int (value, nmt_newt_entry_get_width (priv->entry)); + break; + case PROP_EXTRAS: + g_value_set_uint (value, priv->extras); + break; + case PROP_PASSWORD: + g_value_set_string (value, nmt_password_fields_get_password (entry)); + break; + case PROP_ALWAYS_ASK: + if (priv->always_ask) + g_value_set_boolean (value, nmt_newt_checkbox_get_active (priv->always_ask)); + break; + case PROP_SHOW_PASSWORD: + if (priv->show_password) + g_value_set_boolean (value, nmt_newt_checkbox_get_active (priv->show_password)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_password_fields_class_init (NmtPasswordFieldsClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtPasswordFieldsPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_password_fields_constructed; + object_class->set_property = nmt_password_fields_set_property; + object_class->get_property = nmt_password_fields_get_property; + object_class->finalize = nmt_password_fields_finalize; + + /** + * NmtPasswordFields:width: + * + * The width in characters of the password entry + */ + g_object_class_install_property (object_class, PROP_WIDTH, + g_param_spec_int ("width", "", "", + -1, 80, -1, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:extras: + * + * The extra widgets to show + */ + g_object_class_install_property (object_class, PROP_EXTRAS, + g_param_spec_uint ("extras", "", "", + 0, 0xFFFF, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:password: + * + * The entered password. + */ + g_object_class_install_property (object_class, PROP_PASSWORD, + g_param_spec_string ("password", "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:always-ask: + * + * The current state of the "Always ask" checkbox. + */ + g_object_class_install_property (object_class, PROP_ALWAYS_ASK, + g_param_spec_boolean ("always-ask", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtPasswordFields:show-password: + * + * The current state of the "Show password" checkbox. + */ + g_object_class_install_property (object_class, PROP_SHOW_PASSWORD, + g_param_spec_boolean ("show-password", "", "", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-password-fields.h b/tui/nmt-password-fields.h new file mode 100644 index 0000000000..2ef830aa24 --- /dev/null +++ b/tui/nmt-password-fields.h @@ -0,0 +1,55 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_PASSWORD_FIELDS_H +#define NMT_PASSWORD_FIELDS_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_PASSWORD_FIELDS (nmt_password_fields_get_type ()) +#define NMT_PASSWORD_FIELDS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFields)) +#define NMT_PASSWORD_FIELDS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass)) +#define NMT_IS_PASSWORD_FIELDS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_PASSWORD_FIELDS)) +#define NMT_IS_PASSWORD_FIELDS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_PASSWORD_FIELDS)) +#define NMT_PASSWORD_FIELDS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_PASSWORD_FIELDS, NmtPasswordFieldsClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtPasswordFields; + +typedef struct { + NmtNewtGridClass parent; + +} NmtPasswordFieldsClass; + +GType nmt_password_fields_get_type (void); + +typedef enum { + NMT_PASSWORD_FIELDS_ALWAYS_ASK = (1 << 0), + NMT_PASSWORD_FIELDS_SHOW_PASSWORD = (1 << 1), +} NmtPasswordFieldsExtras; + +NmtNewtWidget *nmt_password_fields_new (int width, + NmtPasswordFieldsExtras extras); + +G_END_DECLS + +#endif /* NMT_PASSWORD_FIELDS_H */ diff --git a/tui/nmt-route-editor.c b/tui/nmt-route-editor.c new file mode 100644 index 0000000000..053cdb0275 --- /dev/null +++ b/tui/nmt-route-editor.c @@ -0,0 +1,219 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-route-editor + * @short_description: Route editing dialog + * + * #NmtRouteEditor implements a form for editing IPv4 or IPv6 routes. + * This was implemented as a separate dialog because it seemed too + * wide to fit into the main window. + */ + +#include "config.h" + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-route-editor.h" +#include "nmt-route-table.h" + +G_DEFINE_TYPE (NmtRouteEditor, nmt_route_editor, NMT_TYPE_NEWT_FORM) + +#define NMT_ROUTE_EDITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorPrivate)) + +typedef struct { + NMSetting *orig_setting; + NMSetting *edit_setting; + +} NmtRouteEditorPrivate; + +enum { + PROP_0, + PROP_SETTING, + + LAST_PROP +}; + +/** + * nmt_route_editor_new: + * @setting: the #NMSettingIP4Config or #NMSettingIP6Config to edit + * + * Creates a new #NmtRouteEditor to edit the routes in @setting + * + * Returns: a new #NmtRouteEditor + */ +NmtNewtForm * +nmt_route_editor_new (NMSetting *setting) +{ + return g_object_new (NMT_TYPE_ROUTE_EDITOR, + "setting", setting, + NULL); +} + +static void +nmt_route_editor_init (NmtRouteEditor *entry) +{ +} + +static void +save_routes_and_exit (NmtNewtButton *button, + gpointer user_data) +{ + NmtRouteEditor *editor = user_data; + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (editor); + const char *property; + GBinding *binding; + + if (NM_IS_SETTING_IP4_CONFIG (priv->edit_setting)) + property = NM_SETTING_IP4_CONFIG_ROUTES; + else + property = NM_SETTING_IP6_CONFIG_ROUTES; + + /* Because of the complicated dbus-glib GTypes, it's easier to cheat + * and use GBinding to do this than it is to copy the value by hand. + */ + binding = g_object_bind_property (priv->edit_setting, property, + priv->orig_setting, property, + G_BINDING_SYNC_CREATE); + g_object_unref (binding); + + nmt_newt_form_quit (NMT_NEWT_FORM (editor)); +} + +static void +nmt_route_editor_constructed (GObject *object) +{ + NmtRouteEditor *editor = NMT_ROUTE_EDITOR (object); + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (editor); + NmtNewtWidget *vbox, *routes, *buttons, *ok, *cancel; + + if (G_OBJECT_CLASS (nmt_route_editor_parent_class)->constructed) + G_OBJECT_CLASS (nmt_route_editor_parent_class)->constructed (object); + + if (NM_IS_SETTING_IP4_CONFIG (priv->edit_setting)) { + routes = nmt_route_table_new (AF_INET); + g_object_bind_property (priv->edit_setting, NM_SETTING_IP4_CONFIG_ROUTES, + routes, "ip4-routes", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + } else { + routes = nmt_route_table_new (AF_INET6); + g_object_bind_property (priv->edit_setting, NM_SETTING_IP6_CONFIG_ROUTES, + routes, "ip6-routes", + G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL); + } + + vbox = nmt_newt_grid_new (); + nmt_newt_grid_add (NMT_NEWT_GRID (vbox), routes, 0, 0); + + buttons = nmt_newt_grid_new (); + nmt_newt_grid_add (NMT_NEWT_GRID (vbox), buttons, 0, 1); + nmt_newt_widget_set_padding (buttons, 0, 1, 0, 0); + + cancel = g_object_ref_sink (nmt_newt_button_new (_("Cancel"))); + nmt_newt_widget_set_exit_on_activate (cancel, TRUE); + nmt_newt_grid_add (NMT_NEWT_GRID (buttons), cancel, 0, 0); + nmt_newt_grid_set_flags (NMT_NEWT_GRID (buttons), cancel, + NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_ANCHOR_RIGHT | + NMT_NEWT_GRID_FILL_Y); + + ok = g_object_ref_sink (nmt_newt_button_new (_("OK"))); + g_signal_connect (ok, "clicked", G_CALLBACK (save_routes_and_exit), editor); + nmt_newt_grid_add (NMT_NEWT_GRID (buttons), ok, 1, 0); + nmt_newt_widget_set_padding (ok, 1, 0, 0, 0); + g_object_bind_property (routes, "valid", + ok, "sensitive", + G_BINDING_SYNC_CREATE); + + nmt_newt_form_set_content (NMT_NEWT_FORM (editor), vbox); +} + +static void +nmt_route_editor_finalize (GObject *object) +{ + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object); + + g_clear_object (&priv->orig_setting); + g_clear_object (&priv->edit_setting); + + G_OBJECT_CLASS (nmt_route_editor_parent_class)->finalize (object); +} + +static void +nmt_route_editor_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SETTING: + priv->orig_setting = g_value_dup_object (value); + priv->edit_setting = nm_setting_duplicate (priv->orig_setting); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_editor_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtRouteEditorPrivate *priv = NMT_ROUTE_EDITOR_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_SETTING: + g_value_set_object (value, priv->edit_setting); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_editor_class_init (NmtRouteEditorClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtRouteEditorPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_route_editor_constructed; + object_class->set_property = nmt_route_editor_set_property; + object_class->get_property = nmt_route_editor_get_property; + object_class->finalize = nmt_route_editor_finalize; + + /** + * NmtRouteEditor:setting: + * + * The #NMSettingIP4Config or #NMSettingIP6Config whose routes are + * being edited. + */ + g_object_class_install_property (object_class, PROP_SETTING, + g_param_spec_object ("setting", "", "", + NM_TYPE_SETTING, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-route-editor.h b/tui/nmt-route-editor.h new file mode 100644 index 0000000000..e1040dffd9 --- /dev/null +++ b/tui/nmt-route-editor.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_ROUTE_EDITOR_H +#define NMT_ROUTE_EDITOR_H + +#include <nm-connection.h> + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_ROUTE_EDITOR (nmt_route_editor_get_type ()) +#define NMT_ROUTE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditor)) +#define NMT_ROUTE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass)) +#define NMT_IS_ROUTE_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_EDITOR)) +#define NMT_IS_ROUTE_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_EDITOR)) +#define NMT_ROUTE_EDITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_EDITOR, NmtRouteEditorClass)) + +typedef struct { + NmtNewtForm parent; + +} NmtRouteEditor; + +typedef struct { + NmtNewtFormClass parent; + +} NmtRouteEditorClass; + +GType nmt_route_editor_get_type (void); + +NmtNewtForm *nmt_route_editor_new (NMSetting *setting); + +G_END_DECLS + +#endif /* NMT_ROUTE_EDITOR_H */ diff --git a/tui/nmt-route-entry.c b/tui/nmt-route-entry.c new file mode 100644 index 0000000000..db8c254d74 --- /dev/null +++ b/tui/nmt-route-entry.c @@ -0,0 +1,323 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-route-entry + * @short_description: A set of widgets describing a single route. + * + * #NmtRouteEntry provides a set of three entry widgets, for entering + * a network/prefix, a next hop, and a metric. + * + * This is used as a building block by #NmtRouteTable. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include <glib/gi18n-lib.h> +#include <nm-setting-ip4-config.h> +#include <nm-setting-ip6-config.h> + +#include "nmt-route-entry.h" +#include "nmt-ip-entry.h" + +#include "nm-editor-bindings.h" + +G_DEFINE_TYPE (NmtRouteEntry, nmt_route_entry, NMT_TYPE_NEWT_GRID) + +#define NMT_ROUTE_ENTRY_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryPrivate)) + +typedef struct { + NmtNewtWidget *dest, *next_hop, *metric; + + int family; + int ip_entry_width, metric_entry_width; + NMIP4Route *ip4_route; + NMIP6Route *ip6_route; +} NmtRouteEntryPrivate; + +enum { + PROP_0, + PROP_FAMILY, + PROP_IP_ENTRY_WIDTH, + PROP_METRIC_ENTRY_WIDTH, + PROP_IP4_ROUTE, + PROP_IP6_ROUTE, + + LAST_PROP +}; + +/** + * nmt_route_entry_new: + * @family: the address family, eg %AF_INET + * @ip_entry_width: the width in characters for the IP address entries + * @metric_entry_width: the width in characters for the metric entry + * + * Creates a new #NmtRouteEntry + * + * Returns: a new #NmtRouteEntry + */ +NmtNewtWidget * +nmt_route_entry_new (int family, + int ip_entry_width, + int metric_entry_width) +{ + return g_object_new (NMT_TYPE_ROUTE_ENTRY, + "family", family, + "ip-entry-width", ip_entry_width, + "metric-entry-width", metric_entry_width, + NULL); +} + +static void +nmt_route_entry_init (NmtRouteEntry *entry) +{ +} + +static gboolean +entry_validity_transform_to_warning_label (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + if (g_value_get_boolean (source_value)) + g_value_set_string (target_value, " "); + else + g_value_set_string (target_value, "!"); + return TRUE; +} + +static NmtNewtWidget * +create_warning_label (NmtNewtWidget *entry) +{ + NmtNewtWidget *label; + + label = g_object_new (NMT_TYPE_NEWT_LABEL, + "highlight", TRUE, + NULL); + g_object_bind_property_full (entry, "valid", + label, "text", + G_BINDING_SYNC_CREATE, + entry_validity_transform_to_warning_label, + NULL, NULL, NULL); + return label; +} + +static void +nmt_route_entry_constructed (GObject *object) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object); + NmtNewtGrid *grid = NMT_NEWT_GRID (object); + NmtNewtWidget *warning_label; + + priv->dest = nmt_ip_entry_new (priv->ip_entry_width, priv->family, TRUE, FALSE); + priv->next_hop = nmt_ip_entry_new (priv->ip_entry_width, priv->family, FALSE, TRUE); + priv->metric = nmt_newt_entry_numeric_new (priv->metric_entry_width, 0, 65535); + + nmt_newt_grid_add (grid, priv->dest, 0, 0); + warning_label = create_warning_label (priv->dest); + nmt_newt_grid_add (grid, warning_label, 1, 0); + + nmt_newt_grid_add (grid, priv->next_hop, 2, 0); + nmt_newt_widget_set_padding (priv->next_hop, 1, 0, 0, 0); + warning_label = create_warning_label (priv->next_hop); + nmt_newt_grid_add (grid, warning_label, 3, 0); + + nmt_newt_grid_add (grid, priv->metric, 4, 0); + nmt_newt_widget_set_padding (priv->metric, 1, 0, 0, 0); + + if (priv->family == AF_INET) { + nm_editor_bind_ip4_route_to_strings (object, "ip4-route", + priv->dest, "text", + priv->next_hop, "text", + priv->metric, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + } else if (priv->family == AF_INET6) { + nm_editor_bind_ip6_route_to_strings (object, "ip6-route", + priv->dest, "text", + priv->next_hop, "text", + priv->metric, "text", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); + } else + g_assert_not_reached (); + + G_OBJECT_CLASS (nmt_route_entry_parent_class)->constructed (object); +} + +static newtComponent +nmt_route_entry_get_focus_component (NmtNewtWidget *widget) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (widget); + + return nmt_newt_widget_get_focus_component (priv->dest); +} + +static void +nmt_route_entry_finalize (GObject *object) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object); + + g_clear_pointer (&priv->ip4_route, nm_ip4_route_unref); + g_clear_pointer (&priv->ip6_route, nm_ip6_route_unref); + + G_OBJECT_CLASS (nmt_route_entry_parent_class)->finalize (object); +} + +static void +nmt_route_entry_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + priv->family = g_value_get_int (value); + break; + case PROP_IP_ENTRY_WIDTH: + priv->ip_entry_width = g_value_get_int (value); + break; + case PROP_METRIC_ENTRY_WIDTH: + priv->metric_entry_width = g_value_get_int (value); + break; + case PROP_IP4_ROUTE: + g_return_if_fail (priv->family == AF_INET); + if (priv->ip4_route) + nm_ip4_route_unref (priv->ip4_route); + priv->ip4_route = g_value_dup_boxed (value); + break; + case PROP_IP6_ROUTE: + g_return_if_fail (priv->family == AF_INET); + if (priv->ip6_route) + nm_ip6_route_unref (priv->ip6_route); + priv->ip6_route = g_value_dup_boxed (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_entry_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtRouteEntryPrivate *priv = NMT_ROUTE_ENTRY_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int (value, priv->family); + break; + case PROP_IP_ENTRY_WIDTH: + g_value_set_int (value, priv->ip_entry_width); + break; + case PROP_METRIC_ENTRY_WIDTH: + g_value_set_int (value, priv->metric_entry_width); + break; + case PROP_IP4_ROUTE: + g_return_if_fail (priv->family == AF_INET); + g_value_set_boxed (value, priv->ip4_route); + break; + case PROP_IP6_ROUTE: + g_return_if_fail (priv->family == AF_INET6); + g_value_set_boxed (value, priv->ip6_route); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_entry_class_init (NmtRouteEntryClass *entry_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (entry_class); + NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (entry_class); + + g_type_class_add_private (entry_class, sizeof (NmtRouteEntryPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_route_entry_constructed; + object_class->set_property = nmt_route_entry_set_property; + object_class->get_property = nmt_route_entry_get_property; + object_class->finalize = nmt_route_entry_finalize; + + widget_class->get_focus_component = nmt_route_entry_get_focus_component; + + /** + * NmtRouteEntry:family: + * + * The address family of the route, eg, %AF_INET + */ + g_object_class_install_property (object_class, PROP_FAMILY, + g_param_spec_int ("family", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteEntry:ip-entry-width: + * + * The width in characters of the IP address entries + */ + g_object_class_install_property (object_class, PROP_IP_ENTRY_WIDTH, + g_param_spec_int ("ip-entry-width", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteEntry:metric-entry-width: + * + * The width in characters of the metric entry + */ + g_object_class_install_property (object_class, PROP_METRIC_ENTRY_WIDTH, + g_param_spec_int ("metric-entry-width", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteEntry:ip4-route: + * + * The contents of the entries, as an #NMIP4Route. Only valid + * if #NmtRouteEntry:family is %AF_INET. + */ + g_object_class_install_property (object_class, PROP_IP4_ROUTE, + g_param_spec_boxed ("ip4-route", "", "", + nm_ip4_route_get_type (), + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteEntry:ip6-route: + * + * The contents of the entries, as an #NMIP6Route. Only valid + * if #NmtRouteEntry:family is %AF_INET6. + */ + g_object_class_install_property (object_class, PROP_IP6_ROUTE, + g_param_spec_boxed ("ip6-route", "", "", + nm_ip6_route_get_type (), + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-route-entry.h b/tui/nmt-route-entry.h new file mode 100644 index 0000000000..d37b14cd5b --- /dev/null +++ b/tui/nmt-route-entry.h @@ -0,0 +1,51 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_ROUTE_ENTRY_H +#define NMT_ROUTE_ENTRY_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_ROUTE_ENTRY (nmt_route_entry_get_type ()) +#define NMT_ROUTE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntry)) +#define NMT_ROUTE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass)) +#define NMT_IS_ROUTE_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_ENTRY)) +#define NMT_IS_ROUTE_ENTRY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_ENTRY)) +#define NMT_ROUTE_ENTRY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_ENTRY, NmtRouteEntryClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtRouteEntry; + +typedef struct { + NmtNewtGridClass parent; + +} NmtRouteEntryClass; + +GType nmt_route_entry_get_type (void); + +NmtNewtWidget *nmt_route_entry_new (int family, + int ip_entry_width, + int metric_entry_width); + +G_END_DECLS + +#endif /* NMT_ROUTE_ENTRY_H */ diff --git a/tui/nmt-route-table.c b/tui/nmt-route-table.c new file mode 100644 index 0000000000..8f9cb24dfe --- /dev/null +++ b/tui/nmt-route-table.c @@ -0,0 +1,388 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-route-table + * @short_description: An editable list of IP4 or IP6 routes + * + * #NmtRouteTable implements a list of #NmtRouteEntry, plus headers, + * and buttons to add and remove entries. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> + +#include <glib/gi18n-lib.h> +#include <dbus/dbus-glib.h> +#include <nm-utils.h> + +#include "nmt-route-table.h" +#include "nmt-route-entry.h" +#include "nmt-widget-list.h" + +G_DEFINE_TYPE (NmtRouteTable, nmt_route_table, NMT_TYPE_NEWT_GRID) + +#define NMT_ROUTE_TABLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ROUTE_TABLE, NmtRouteTablePrivate)) + +typedef struct { + int family; + + int ip_entry_width; + int metric_entry_width; + + GSList *routes; + NmtNewtWidget *list; +} NmtRouteTablePrivate; + +enum { + PROP_0, + PROP_FAMILY, + PROP_IP4_ROUTES, + PROP_IP6_ROUTES, + + LAST_PROP +}; + +/** + * nmt_route_table_new: + * @family: the address family, eg %AF_INET + * + * Creates a new #NmtRouteTable + * + * Returns: a new #NmtRouteTable + */ +NmtNewtWidget * +nmt_route_table_new (int family) +{ + return g_object_new (NMT_TYPE_ROUTE_TABLE, + "family", family, + NULL); +} + +static gboolean +route_list_transform_to_route (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NmtRouteTable *table = NMT_ROUTE_TABLE (g_binding_get_source (binding)); + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + int n = GPOINTER_TO_INT (user_data); + gpointer route; + + route = g_slist_nth_data (priv->routes, n); + if (route) + g_value_set_boxed (target_value, route); + return route != NULL; +} + +static gboolean +route_list_transform_from_route (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + NmtRouteTable *table = NMT_ROUTE_TABLE (g_binding_get_source (binding)); + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + int n = GPOINTER_TO_INT (user_data); + GSList *routes, *nth; + + nth = g_slist_nth (priv->routes, n); + if (!nth) + return FALSE; + + routes = priv->routes; + priv->routes = NULL; + + if (nth->data) { + if (priv->family == AF_INET) + nm_ip4_route_unref (nth->data); + else if (priv->family == AF_INET6) + nm_ip6_route_unref (nth->data); + } + nth->data = g_value_dup_boxed (source_value); + g_value_take_boxed (target_value, routes); + + return TRUE; +} + +static NmtNewtWidget * +create_route_entry (NmtWidgetList *list, + int num, + gpointer table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + NmtNewtWidget *entry; + + entry = nmt_route_entry_new (priv->family, + priv->ip_entry_width, + priv->metric_entry_width); + + if (priv->family == AF_INET) { + g_object_bind_property_full (table, "ip4-routes", + entry, "ip4-route", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + route_list_transform_to_route, + route_list_transform_from_route, + GINT_TO_POINTER (num), NULL); + } else { + g_object_bind_property_full (table, "ip6-routes", + entry, "ip6-route", + G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE, + route_list_transform_to_route, + route_list_transform_from_route, + GINT_TO_POINTER (num), NULL); + } + return entry; +} + +static void +add_route (NmtWidgetList *list, + gpointer table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + + if (priv->family == AF_INET) { + NMIP4Route *route; + + route = nm_ip4_route_new (); + nm_ip4_route_set_prefix (route, 32); + priv->routes = g_slist_append (priv->routes, route); + nmt_widget_list_set_length (list, g_slist_length (priv->routes)); + g_object_notify (table, "ip4-routes"); + } else { + NMIP6Route *route; + + route = nm_ip6_route_new (); + nm_ip6_route_set_prefix (route, 128); + priv->routes = g_slist_append (priv->routes, route); + nmt_widget_list_set_length (list, g_slist_length (priv->routes)); + g_object_notify (table, "ip6-routes"); + } +} + +static void +remove_route (NmtWidgetList *list, + int num, + gpointer table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + GSList *nth; + gpointer route; + + nth = g_slist_nth (priv->routes, num); + if (!nth) + return; + + route = nth->data; + priv->routes = g_slist_delete_link (priv->routes, nth); + nmt_widget_list_set_length (list, g_slist_length (priv->routes)); + + if (priv->family == AF_INET) { + nm_ip4_route_unref (route); + g_object_notify (table, "ip4-routes"); + } else { + nm_ip6_route_unref (route); + g_object_notify (table, "ip6-routes"); + } +} + +static void +nmt_route_table_init (NmtRouteTable *table) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (table); + NmtNewtWidget *header, *empty; + NmtNewtWidget *dest_prefix_label, *next_hop_label, *metric_label; + int dest_prefix_len, next_hop_len, metric_len; + char *text; + + header = nmt_newt_grid_new (); + + text = g_strdup_printf ("%s/%s", _("Destination"), _("Prefix")); + dest_prefix_len = g_utf8_strlen (text, -1); + dest_prefix_label = g_object_new (NMT_TYPE_NEWT_LABEL, + "text", text, + "style", NMT_NEWT_LABEL_PLAIN, + NULL); + g_free (text); + nmt_newt_grid_add (NMT_NEWT_GRID (header), dest_prefix_label, 0, 0); + + text = _("Next Hop"); + next_hop_label = g_object_new (NMT_TYPE_NEWT_LABEL, + "text", text, + "style", NMT_NEWT_LABEL_PLAIN, + NULL); + next_hop_len = g_utf8_strlen (text, -1); + nmt_newt_grid_add (NMT_NEWT_GRID (header), next_hop_label, 1, 0); + + text = _("Metric"); + metric_label = g_object_new (NMT_TYPE_NEWT_LABEL, + "text", text, + "style", NMT_NEWT_LABEL_PLAIN, + NULL); + metric_len = g_utf8_strlen (text, -1); + nmt_newt_grid_add (NMT_NEWT_GRID (header), metric_label, 2, 0); + + priv->ip_entry_width = MAX (20, MAX (dest_prefix_len, next_hop_len)); + priv->metric_entry_width = MAX (7, metric_len); + + nmt_newt_widget_set_padding (dest_prefix_label, + 0, 0, priv->ip_entry_width - dest_prefix_len, 0); + nmt_newt_widget_set_padding (next_hop_label, + 2, 0, priv->ip_entry_width - next_hop_len, 0); + nmt_newt_widget_set_padding (metric_label, + 2, 0, priv->metric_entry_width - metric_len, 0); + + nmt_newt_grid_add (NMT_NEWT_GRID (table), header, 0, 0); + + empty = nmt_newt_label_new (_("No custom routes are defined.")); + priv->list = nmt_widget_list_new (create_route_entry, table, NULL, empty); + g_signal_connect (priv->list, "add-clicked", G_CALLBACK (add_route), table); + g_signal_connect (priv->list, "remove-clicked", G_CALLBACK (remove_route), table); + nmt_newt_grid_add (NMT_NEWT_GRID (table), priv->list, 0, 1); +} + +static void +nmt_route_table_finalize (GObject *object) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (object); + + if (priv->family == AF_INET) + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref); + else if (priv->family == AF_INET6) + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip6_route_unref); + + G_OBJECT_CLASS (nmt_route_table_parent_class)->finalize (object); +} + +static void +nmt_route_table_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + priv->family = g_value_get_int (value); + break; + case PROP_IP4_ROUTES: + g_return_if_fail (priv->family == AF_INET); + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip4_route_unref); + priv->routes = nm_utils_ip4_routes_from_gvalue (value); + nmt_widget_list_set_length (NMT_WIDGET_LIST (priv->list), + g_slist_length (priv->routes)); + break; + case PROP_IP6_ROUTES: + g_return_if_fail (priv->family == AF_INET6); + g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip6_route_unref); + priv->routes = nm_utils_ip6_routes_from_gvalue (value); + nmt_widget_list_set_length (NMT_WIDGET_LIST (priv->list), + g_slist_length (priv->routes)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_route_table_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtRouteTablePrivate *priv = NMT_ROUTE_TABLE_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_FAMILY: + g_value_set_int (value, priv->family); + break; + case PROP_IP4_ROUTES: + g_return_if_fail (priv->family == AF_INET); + nm_utils_ip4_routes_to_gvalue (priv->routes, value); + break; + case PROP_IP6_ROUTES: + g_return_if_fail (priv->family == AF_INET6); + nm_utils_ip6_routes_to_gvalue (priv->routes, value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +#define DBUS_TYPE_G_ARRAY_OF_UINT (dbus_g_type_get_collection ("GArray", G_TYPE_UINT)) +#define DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_ARRAY_OF_UINT)) +#define DBUS_TYPE_G_IP6_ROUTE (dbus_g_type_get_struct ("GValueArray", DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, DBUS_TYPE_G_UCHAR_ARRAY, G_TYPE_UINT, G_TYPE_INVALID)) +#define DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_IP6_ROUTE)) + +static void +nmt_route_table_class_init (NmtRouteTableClass *table_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (table_class); + + g_type_class_add_private (table_class, sizeof (NmtRouteTablePrivate)); + + /* virtual methods */ + object_class->set_property = nmt_route_table_set_property; + object_class->get_property = nmt_route_table_get_property; + object_class->finalize = nmt_route_table_finalize; + + /** + * NmtRouteTable:family: + * + * The network address family of the routes, eg %AF_INET + */ + g_object_class_install_property (object_class, PROP_FAMILY, + g_param_spec_int ("family", "", "", + -1, G_MAXINT, -1, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteTable:ip4-routes: + * + * The array of routes, suitable for binding to + * #NMSettingIP4Config:routes. + * + * Only valid if #NmtRouteTable:family is %AF_INET + */ + g_object_class_install_property (object_class, PROP_IP4_ROUTES, + g_param_spec_boxed ("ip4-routes", "", "", + DBUS_TYPE_G_ARRAY_OF_ARRAY_OF_UINT, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtRouteTable:ip6-routes: + * + * The array of routes, suitable for binding to + * #NMSettingIP6Config:routes. + * + * Only valid if #NmtRouteTable:family is %AF_INET6 + */ + g_object_class_install_property (object_class, PROP_IP6_ROUTES, + g_param_spec_boxed ("ip6-routes", "", "", + DBUS_TYPE_G_ARRAY_OF_IP6_ROUTE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-route-table.h b/tui/nmt-route-table.h new file mode 100644 index 0000000000..217bf40da0 --- /dev/null +++ b/tui/nmt-route-table.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_ROUTE_TABLE_H +#define NMT_ROUTE_TABLE_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_ROUTE_TABLE (nmt_route_table_get_type ()) +#define NMT_ROUTE_TABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTable)) +#define NMT_ROUTE_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass)) +#define NMT_IS_ROUTE_TABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ROUTE_TABLE)) +#define NMT_IS_ROUTE_TABLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_ROUTE_TABLE)) +#define NMT_ROUTE_TABLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_ROUTE_TABLE, NmtRouteTableClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtRouteTable; + +typedef struct { + NmtNewtGridClass parent; + +} NmtRouteTableClass; + +GType nmt_route_table_get_type (void); + +NmtNewtWidget *nmt_route_table_new (int family); + +G_END_DECLS + +#endif /* NMT_ROUTE_TABLE_H */ diff --git a/tui/nmt-secret-agent.c b/tui/nmt-secret-agent.c new file mode 100644 index 0000000000..f0a01cda0a --- /dev/null +++ b/tui/nmt-secret-agent.c @@ -0,0 +1,645 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2011-2013 Red Hat, Inc. + * Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com> + */ + +/** + * SECTION:nmt-secret-agent + * @short_description: A secret agent + * + * #NmtSecretAgent is the secret agent used by nmtui-connect. + * + * This is a stripped-down version of gnome-shell's ShellNetworkAgent, + * with bits of the corresponding JavaScript code squished down into + * it. It is intended to eventually be generic enough that it could + * replace ShellNetworkAgent. + */ + +#include "config.h" + +#include <string.h> +#include <dbus/dbus-glib.h> +#include <glib/gi18n-lib.h> + +#include <nm-utils.h> + +#include "nmt-secret-agent.h" +#include "nmt-newt.h" + +G_DEFINE_TYPE (NmtSecretAgent, nmt_secret_agent, NM_TYPE_SECRET_AGENT) + +#define NMT_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_SECRET_AGENT, NmtSecretAgentPrivate)) + +enum { + REQUEST_SECRETS, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +typedef struct { + NmtSecretAgent *self; + + gchar *request_id; + NMConnection *connection; + gchar **hints; + NMSecretAgentGetSecretsFunc callback; + gpointer callback_data; +} NmtSecretAgentRequest; + +typedef struct { + /* <char *request_id, NmtSecretAgentRequest *request> */ + GHashTable *requests; +} NmtSecretAgentPrivate; + +static void +nmt_secret_agent_request_free (gpointer data) +{ + NmtSecretAgentRequest *request = data; + + g_object_unref (request->self); + g_object_unref (request->connection); + g_strfreev (request->hints); + + g_slice_free (NmtSecretAgentRequest, request); +} + +static void +nmt_secret_agent_init (NmtSecretAgent *agent) +{ + NmtSecretAgentPrivate *priv = NMT_SECRET_AGENT_GET_PRIVATE (agent); + + priv->requests = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, nmt_secret_agent_request_free); +} + +static void +nmt_secret_agent_finalize (GObject *object) +{ + NmtSecretAgentPrivate *priv = NMT_SECRET_AGENT_GET_PRIVATE (object); + GError *error; + GHashTableIter iter; + gpointer key; + gpointer value; + + error = g_error_new (NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_AGENT_CANCELED, + "The secret agent is going away"); + + g_hash_table_iter_init (&iter, priv->requests); + while (g_hash_table_iter_next (&iter, &key, &value)) { + NmtSecretAgentRequest *request = value; + + request->callback (NM_SECRET_AGENT (object), + request->connection, + NULL, error, + request->callback_data); + } + + g_hash_table_destroy (priv->requests); + g_error_free (error); + + G_OBJECT_CLASS (nmt_secret_agent_parent_class)->finalize (object); +} + +static gboolean +strv_has (gchar **haystack, + gchar *needle) +{ + gchar *iter; + + for (iter = *haystack; iter; iter++) { + if (g_strcmp0 (iter, needle) == 0) + return TRUE; + } + + return FALSE; +} + +/** + * NmtSecretAgentSecret: + * @name: the user-visible name of the secret. Eg, "WEP Passphrase". + * @value: the value of the secret + * @password: %TRUE if this secret represents a password, %FALSE + * if it represents non-secret data. + * + * A single "secret" being requested. + */ + +typedef struct { + NmtSecretAgentSecret base; + + NMSetting *setting; + char *property; + + NmtNewtEntryValidator validator; + gpointer validator_data; +} NmtSecretAgentSecretReal; + +static void +nmt_secret_agent_secret_free (NmtSecretAgentSecret *secret) +{ + NmtSecretAgentSecretReal *real = (NmtSecretAgentSecretReal *)secret; + + g_free (secret->name); + g_free (secret->value); + g_free (real->property); + g_clear_object (&real->setting); + + g_slice_free (NmtSecretAgentSecretReal, real); +} + +static NmtSecretAgentSecret * +nmt_secret_agent_secret_new (const char *name, + NMSetting *setting, + const char *property, + gboolean password) +{ + NmtSecretAgentSecretReal *real; + + real = g_slice_new0 (NmtSecretAgentSecretReal); + real->base.name = g_strdup (name); + real->base.password = password; + + if (setting) { + real->setting = g_object_ref (setting); + real->property = g_strdup (property); + + g_object_get (setting, property, &real->base.value, NULL); + } + + return &real->base; +} + +static gboolean +add_8021x_secrets (NmtSecretAgentRequest *request, + GPtrArray *secrets) +{ + NMSetting8021x *s_8021x = nm_connection_get_setting_802_1x (request->connection); + const char *eap_method; + NmtSecretAgentSecret *secret; + + eap_method = nm_setting_802_1x_get_eap_method (s_8021x, 0); + if (!eap_method) + return FALSE; + + if ( !strcmp (eap_method, "md5") + || !strcmp (eap_method, "leap") + || !strcmp (eap_method, "ttls") + || !strcmp (eap_method, "peap")) { + /* TTLS and PEAP are actually much more complicated, but this complication + * is not visible here since we only care about phase2 authentication + * (and don't even care of which one) + */ + secret = nmt_secret_agent_secret_new (_("Username"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_IDENTITY, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nmt_secret_agent_secret_new (_("Password"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } + + if (!strcmp (eap_method, "tls")) { + secret = nmt_secret_agent_secret_new (_("Identity"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_IDENTITY, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nmt_secret_agent_secret_new (_("Private key password"), + NM_SETTING (s_8021x), + NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } + + return FALSE; +} + +static gboolean +add_wireless_secrets (NmtSecretAgentRequest *request, + GPtrArray *secrets) +{ + NMSettingWirelessSecurity *s_wsec = nm_connection_get_setting_wireless_security (request->connection); + const char *key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec); + NmtSecretAgentSecret *secret; + + if (!key_mgmt) + return FALSE; + + if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) { + secret = nmt_secret_agent_secret_new (_("Password"), + NM_SETTING (s_wsec), + NM_SETTING_WIRELESS_SECURITY_PSK, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } + + if (!strcmp (key_mgmt, "none")) { + int index; + char *key; + + index = nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec); + key = g_strdup_printf ("wep-key%d", index); + secret = nmt_secret_agent_secret_new (_("Key"), + NM_SETTING (s_wsec), + key, + TRUE); + g_free (key); + +#if 0 + nmt_secret_agent_secret_set_validator (secret, static_wep_key_validate, + nm_setting_wireless_security_get_wep_key_type (s_wsec)); +#endif + g_ptr_array_add (secrets, secret); + return TRUE; + } + + if (!strcmp (key_mgmt, "iee8021x")) { + if (!g_strcmp0 (nm_setting_wireless_security_get_auth_alg (s_wsec), "leap")) { + secret = nmt_secret_agent_secret_new (_("Password"), + NM_SETTING (s_wsec), + NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; + } else + return add_8021x_secrets (request, secrets); + } + + if (!strcmp (key_mgmt, "wpa-eap")) + return add_8021x_secrets (request, secrets); + + return FALSE; +} + +static gboolean +add_pppoe_secrets (NmtSecretAgentRequest *request, + GPtrArray *secrets) +{ + NMSettingPPPOE *s_pppoe = nm_connection_get_setting_pppoe (request->connection); + NmtSecretAgentSecret *secret; + + secret = nmt_secret_agent_secret_new (_("Username"), + NM_SETTING (s_pppoe), + NM_SETTING_PPPOE_USERNAME, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nmt_secret_agent_secret_new (_("Service"), + NM_SETTING (s_pppoe), + NM_SETTING_PPPOE_SERVICE, + FALSE); + g_ptr_array_add (secrets, secret); + secret = nmt_secret_agent_secret_new (_("Password"), + NM_SETTING (s_pppoe), + NM_SETTING_PPPOE_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + return TRUE; +} + +static void +request_secrets_from_ui (NmtSecretAgentRequest *request) +{ + GPtrArray *secrets; + NmtSecretAgentSecret *secret; + const char *title; + char *msg; + gboolean ok = TRUE; + + secrets = g_ptr_array_new_with_free_func ((GDestroyNotify) nmt_secret_agent_secret_free); + + if (nm_connection_is_type (request->connection, NM_SETTING_WIRELESS_SETTING_NAME)) { + NMSettingWireless *s_wireless; + char *ssid; + + s_wireless = nm_connection_get_setting_wireless (request->connection); + ssid = nm_utils_ssid_to_utf8 (nm_setting_wireless_get_ssid (s_wireless)); + + title = _("Authentication required by wireless network"); + msg = g_strdup_printf (_("Passwords or encryption keys are required to access the wireless network '%s'."), ssid); + + ok = add_wireless_secrets (request, secrets); + } else if (nm_connection_is_type (request->connection, NM_SETTING_WIRED_SETTING_NAME)) { + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (request->connection); + + title = _("Wired 802.1X authentication"); + msg = NULL; + + secret = nmt_secret_agent_secret_new (_("Network name"), + NM_SETTING (s_con), + NM_SETTING_CONNECTION_ID, + FALSE); + g_ptr_array_add (secrets, secret); + ok = add_8021x_secrets (request, secrets); + } else if (nm_connection_is_type (request->connection, NM_SETTING_PPPOE_SETTING_NAME)) { + title = _("DSL authentication"); + msg = NULL; + + ok = add_pppoe_secrets (request, secrets); + } else if (nm_connection_is_type (request->connection, NM_SETTING_GSM_SETTING_NAME)) { + NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (request->connection); + + if (strv_has (request->hints, "pin")) { + title = _("PIN code required"); + msg = g_strdup (_("PIN code is needed for the mobile broadband device")); + + secret = nmt_secret_agent_secret_new (_("PIN"), + NM_SETTING (s_gsm), + NM_SETTING_GSM_PIN, + FALSE); + g_ptr_array_add (secrets, secret); + } else { + title = _("Mobile broadband network password"); + msg = g_strdup_printf (_("A password is required to connect to '%s'."), + nm_connection_get_id (request->connection)); + + secret = nmt_secret_agent_secret_new (_("Password"), + NM_SETTING (s_gsm), + NM_SETTING_GSM_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + } + } else if (nm_connection_is_type (request->connection, NM_SETTING_CDMA_SETTING_NAME)) { + NMSettingCdma *s_cdma = nm_connection_get_setting_cdma (request->connection); + + title = _("Mobile broadband network password"); + msg = g_strdup_printf (_("A password is required to connect to '%s'."), + nm_connection_get_id (request->connection)); + + secret = nmt_secret_agent_secret_new (_("Password"), + NM_SETTING (s_cdma), + NM_SETTING_CDMA_PASSWORD, + TRUE); + g_ptr_array_add (secrets, secret); + } else if (nm_connection_is_type (request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) { + NMSetting *setting; + + setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_GSM_SETTING_NAME); + if (!setting) + setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_CDMA_SETTING_NAME); + + title = _("Mobile broadband network password"); + msg = g_strdup_printf (_("A password is required to connect to '%s'."), + nm_connection_get_id (request->connection)); + + secret = nmt_secret_agent_secret_new (_("Password"), + setting, + "password", + TRUE); + g_ptr_array_add (secrets, secret); + } else + ok = FALSE; + + if (!ok) { + g_ptr_array_unref (secrets); + return; + } + + g_signal_emit (request->self, signals[REQUEST_SECRETS], 0, + request->request_id, title, msg, secrets); +} + +static void +nmt_secret_agent_get_secrets (NMSecretAgent *agent, + NMConnection *connection, + const gchar *connection_path, + const gchar *setting_name, + const gchar **hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentGetSecretsFunc callback, + gpointer callback_data) +{ + NmtSecretAgent *self = NMT_SECRET_AGENT (agent); + NmtSecretAgentPrivate *priv = NMT_SECRET_AGENT_GET_PRIVATE (self); + NmtSecretAgentRequest *request; + NMSettingConnection *s_con; + const char *connection_type; + char *request_id; + GError *error; + + request_id = g_strdup_printf ("%s/%s", connection_path, setting_name); + if ((request = g_hash_table_lookup (priv->requests, request_id)) != NULL) { + /* We already have a request pending for this (connection, setting) */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INTERNAL_ERROR, + "Request for %s secrets already pending", request_id); + nope: + callback (agent, connection, NULL, NULL, callback_data); + g_free (request_id); + return; + } + + s_con = nm_connection_get_setting_connection (connection); + connection_type = nm_setting_connection_get_connection_type (s_con); + + if (!strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME)) { + /* We don't support VPN secrets yet */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, + "VPN secrets not supported"); + goto nope; + } + + if (!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)) { + /* We don't do stored passwords */ + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS, + "Stored passwords not supported"); + goto nope; + } + + request = g_slice_new (NmtSecretAgentRequest); + request->self = g_object_ref (self); + request->connection = g_object_ref (connection); + request->hints = g_strdupv ((gchar **)hints); + request->callback = callback; + request->callback_data = callback_data; + request->request_id = request_id; + g_hash_table_replace (priv->requests, request->request_id, request); + + request_secrets_from_ui (request); +} + +static void +gvalue_destroy_notify (gpointer data) +{ + GValue *value = data; + g_value_unset (value); + g_slice_free (GValue, value); +} + +/** + * nmt_secret_agent_response: + * @self: the #NmtSecretAgent + * @request_id: the request ID being responded to + * @secrets: (allow-none): the array of secrets, or %NULL + * + * Response to a #NmtSecretAgent::get-secrets signal. + * + * If the user provided secrets, the caller should set the + * corresponding <literal>value</literal> fields in the + * #NmtSecretAgentSecrets (freeing any initial values they had), and + * pass the array to nmt_secret_agent_response(). If the user + * cancelled the request, @secrets should be NULL. + */ +void +nmt_secret_agent_response (NmtSecretAgent *self, + const char *request_id, + GPtrArray *secrets) +{ + NmtSecretAgentPrivate *priv; + NmtSecretAgentRequest *request; + GHashTable *hash = NULL, *setting_hash; + GValue *value; + GError *error = NULL; + int i; + + g_return_if_fail (NMT_IS_SECRET_AGENT (self)); + + priv = NMT_SECRET_AGENT_GET_PRIVATE (self); + request = g_hash_table_lookup (priv->requests, request_id); + g_return_if_fail (request != NULL); + + if (secrets) { + hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) g_hash_table_unref); + for (i = 0; i < secrets->len; i++) { + NmtSecretAgentSecretReal *secret = secrets->pdata[i]; + + setting_hash = g_hash_table_lookup (hash, nm_setting_get_name (secret->setting)); + if (!setting_hash) { + setting_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, gvalue_destroy_notify); + g_hash_table_insert (hash, (char *)nm_setting_get_name (secret->setting), + setting_hash); + } + + value = g_slice_new0 (GValue); + g_value_init (value, G_TYPE_STRING); + g_value_set_string (value, secret->base.value); + + g_hash_table_insert (setting_hash, g_strdup (secret->property), value); + } + } else { + error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED, + "User cancelled"); + } + + request->callback (NM_SECRET_AGENT (self), request->connection, hash, error, request->callback_data); + + g_clear_pointer (&hash, g_hash_table_unref); + g_clear_error (&error); + g_hash_table_remove (priv->requests, request_id); +} + +static void +nmt_secret_agent_cancel_get_secrets (NMSecretAgent *agent, + const gchar *connection_path, + const gchar *setting_name) +{ + /* We don't support cancellation. Sorry! */ +} + +static void +nmt_secret_agent_save_secrets (NMSecretAgent *agent, + NMConnection *connection, + const gchar *connection_path, + NMSecretAgentSaveSecretsFunc callback, + gpointer callback_data) +{ + /* We don't support secret storage */ + callback (agent, connection, NULL, callback_data);} + +static void +nmt_secret_agent_delete_secrets (NMSecretAgent *agent, + NMConnection *connection, + const gchar *connection_path, + NMSecretAgentDeleteSecretsFunc callback, + gpointer callback_data) +{ + /* We don't support secret storage, so there's nothing to delete. */ + callback (agent, connection, NULL, callback_data); +} + +void +nmt_secret_agent_class_init (NmtSecretAgentClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + NMSecretAgentClass *agent_class = NM_SECRET_AGENT_CLASS (klass); + + g_type_class_add_private (klass, sizeof (NmtSecretAgentPrivate)); + + gobject_class->finalize = nmt_secret_agent_finalize; + + agent_class->get_secrets = nmt_secret_agent_get_secrets; + agent_class->cancel_get_secrets = nmt_secret_agent_cancel_get_secrets; + agent_class->save_secrets = nmt_secret_agent_save_secrets; + agent_class->delete_secrets = nmt_secret_agent_delete_secrets; + + /** + * NmtSecretAgent::request-secrets: + * @agent: the #NmtSecretAgent + * @request_id: request ID, to eventually pass to + * nmt_secret_agent_response(). + * @title: a title for the password dialog + * @prompt: a prompt message for the password dialog + * @secrets: (element-type #NmtSecretAgentSecret): array of secrets + * being requested. + * + * Emitted when the agent requires secrets from the user. + * + * The application should create a password dialog (eg, + * #NmtPasswordDialog) with the given title and prompt, and an + * entry for each element of @secrets. If any of the secrets + * already have a <literal>value</literal> filled in, the + * corresponding entry should be initialized to that value. + * + * When the dialog is complete, the app must call + * nmt_secret_agent_response() with the results. + */ + signals[REQUEST_SECRETS] = g_signal_new ("request-secrets", + G_TYPE_FROM_CLASS (klass), + 0, 0, NULL, NULL, NULL, + G_TYPE_NONE, + 4, + G_TYPE_STRING, /* request_id */ + G_TYPE_STRING, /* title */ + G_TYPE_STRING, /* prompt */ + G_TYPE_PTR_ARRAY); +} + +/** + * nmt_secret_agent_new: + * + * Creates a new #NmtSecretAgent. + * + * Returns: a new #NmtSecretAgent + */ +NMSecretAgent * +nmt_secret_agent_new (void) +{ + return g_object_new (NMT_TYPE_SECRET_AGENT, + NM_SECRET_AGENT_IDENTIFIER, "nmtui", + NM_SECRET_AGENT_AUTO_REGISTER, FALSE, + NULL); +} diff --git a/tui/nmt-secret-agent.h b/tui/nmt-secret-agent.h new file mode 100644 index 0000000000..04b1581946 --- /dev/null +++ b/tui/nmt-secret-agent.h @@ -0,0 +1,57 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_SECRET_AGENT_H +#define NMT_SECRET_AGENT_H + +#include <nm-secret-agent.h> + +G_BEGIN_DECLS + +#define NMT_TYPE_SECRET_AGENT (nmt_secret_agent_get_type ()) +#define NMT_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_SECRET_AGENT, NmtSecretAgent)) +#define NMT_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_SECRET_AGENT, NmtSecretAgentClass)) +#define NMT_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_SECRET_AGENT)) +#define NMT_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_SECRET_AGENT)) +#define NMT_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_SECRET_AGENT, NmtSecretAgentClass)) + +typedef struct { + NMSecretAgent parent; + +} NmtSecretAgent; + +typedef struct { + NMSecretAgentClass parent; + +} NmtSecretAgentClass; + +typedef struct { + char *name, *value; + gboolean password; +} NmtSecretAgentSecret; + +GType nmt_secret_agent_get_type (void); + +NMSecretAgent *nmt_secret_agent_new (void); +void nmt_secret_agent_response (NmtSecretAgent *self, + const char *request_id, + GPtrArray *secrets); + +G_END_DECLS + +#endif /* NMT_SECRET_AGENT_H */ diff --git a/tui/nmt-slave-list.c b/tui/nmt-slave-list.c new file mode 100644 index 0000000000..284614aa2f --- /dev/null +++ b/tui/nmt-slave-list.c @@ -0,0 +1,264 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-slave-list: + * @short_description: An editable list of a connection's slaves + * + * #NmtSlaveList implements an #NmtEditConnectionList for the + * slaves of a connection. + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include <nm-remote-connection.h> + +#include "nmt-slave-list.h" + +G_DEFINE_TYPE (NmtSlaveList, nmt_slave_list, NMT_TYPE_EDIT_CONNECTION_LIST) + +#define NMT_SLAVE_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_SLAVE_LIST, NmtSlaveListPrivate)) + +typedef struct { + NMConnection *master; + const char *master_type, *master_uuid; + + NmtAddConnectionTypeFilter type_filter; + gpointer type_filter_data; +} NmtSlaveListPrivate; + +enum { + PROP_0, + PROP_MASTER, + PROP_TYPE_FILTER, + PROP_TYPE_FILTER_DATA, + + LAST_PROP +}; + +static gboolean nmt_slave_list_connection_filter (NmtEditConnectionList *list, + NMConnection *connection, + gpointer user_data); + +/** + * nmt_slave_list_new: + * @master: the master #NMConnection whose slaves are being listed + * @type_filter: (allow-none): a function to limit the availble slave types + * @type_filter_data: (allow-none): data for @type_filter. + * + * Creates a new #NmtSlaveList. + * + * If @type_filter is non-%NULL, it will be used to limit the connection + * types that are available when the user clicks on the "Add" button to add + * a new slave. If the @type_filter filters the list down to only a single + * connection type, then the user will not be presented with a connection-type + * dialog, and will instead be immediately taken to an editor window for the + * new slave after clicking "Add". + * + * Returns: a new #NmtSlaveList. + */ +NmtNewtWidget * +nmt_slave_list_new (NMConnection *master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data) +{ + return g_object_new (NMT_TYPE_SLAVE_LIST, + "master", master, + "type-filter", type_filter, + "type-filter-data", type_filter_data, + "grouped", FALSE, + "connection-filter", nmt_slave_list_connection_filter, + NULL); +} + +static void +nmt_slave_list_init (NmtSlaveList *list) +{ +} + +static void +nmt_slave_list_finalize (GObject *object) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object); + + g_object_unref (priv->master); + + G_OBJECT_CLASS (nmt_slave_list_parent_class)->finalize (object); +} + +static gboolean +nmt_slave_list_connection_filter (NmtEditConnectionList *list, + NMConnection *connection, + gpointer user_data) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (list); + NMSettingConnection *s_con; + const char *master, *master_ifname, *slave_type; + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con != NULL, FALSE); + + slave_type = nm_setting_connection_get_slave_type (s_con); + if (g_strcmp0 (slave_type, priv->master_type) != 0) + return FALSE; + + master = nm_setting_connection_get_master (s_con); + if (!master) + return FALSE; + + master_ifname = nm_connection_get_virtual_iface_name (priv->master); + if (g_strcmp0 (master, master_ifname) != 0 && g_strcmp0 (master, priv->master_uuid) != 0) + return FALSE; + + return TRUE; +} + +static void +nmt_slave_list_add_connection (NmtEditConnectionList *list) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (list); + + nmt_add_connection_full (_("Select the type of slave connection you wish to add."), NULL, + priv->master, priv->type_filter, priv->type_filter_data); +} + +static void +nmt_slave_list_edit_connection (NmtEditConnectionList *list, + NMConnection *connection) +{ + nmt_edit_connection (connection); +} + +static void +nmt_slave_list_remove_connection (NmtEditConnectionList *list, + NMRemoteConnection *connection) +{ + nmt_remove_connection (connection); +} + +static void +nmt_slave_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MASTER: + priv->master = g_value_dup_object (value); + if (priv->master) { + NMSettingConnection *s_con = nm_connection_get_setting_connection (priv->master); + + priv->master_type = nm_setting_connection_get_connection_type (s_con); + priv->master_uuid = nm_setting_connection_get_uuid (s_con); + } + break; + case PROP_TYPE_FILTER: + priv->type_filter = g_value_get_pointer (value); + break; + case PROP_TYPE_FILTER_DATA: + priv->type_filter_data = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_slave_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtSlaveListPrivate *priv = NMT_SLAVE_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_MASTER: + g_value_set_object (value, priv->master); + break; + case PROP_TYPE_FILTER: + g_value_set_pointer (value, priv->type_filter); + break; + case PROP_TYPE_FILTER_DATA: + g_value_set_pointer (value, priv->type_filter_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_slave_list_class_init (NmtSlaveListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (list_class); + NmtEditConnectionListClass *connection_list_class = NMT_EDIT_CONNECTION_LIST_CLASS (list_class); + + g_type_class_add_private (list_class, sizeof (NmtSlaveListPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_slave_list_set_property; + object_class->get_property = nmt_slave_list_get_property; + object_class->finalize = nmt_slave_list_finalize; + + connection_list_class->add_connection = nmt_slave_list_add_connection; + connection_list_class->edit_connection = nmt_slave_list_edit_connection; + connection_list_class->remove_connection = nmt_slave_list_remove_connection; + + /** + * NmtSlaveList:master: + * + * The master #NMConnection whose slaves are being displayed. + */ + g_object_class_install_property (object_class, PROP_MASTER, + g_param_spec_object ("master", "", "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtSlaveList:type-filter: + * + * If non-%NULL, this will be used to limit the connection types + * that are available when the user clicks on the "Add" button to + * add a new slave. If the filter filters the list down to only a + * single connection type, then the user will not be presented + * with a connection-type dialog, and will instead be immediately + * taken to an editor window for the new slave after clicking + * "Add". + */ + g_object_class_install_property (object_class, PROP_TYPE_FILTER, + g_param_spec_pointer ("type-filter", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + /** + * NmtSlaveList:type-filter-data: + * + * User data passed to #NmtSlaveList:type-filter + */ + g_object_class_install_property (object_class, PROP_TYPE_FILTER_DATA, + g_param_spec_pointer ("type-filter-data", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-slave-list.h b/tui/nmt-slave-list.h new file mode 100644 index 0000000000..d10cd3a73a --- /dev/null +++ b/tui/nmt-slave-list.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_SLAVE_LIST_H +#define NMT_SLAVE_LIST_H + +#include "nmt-edit-connection-list.h" +#include "nmtui-edit.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_SLAVE_LIST (nmt_slave_list_get_type ()) +#define NMT_SLAVE_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveList)) +#define NMT_SLAVE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass)) +#define NMT_IS_SLAVE_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_SLAVE_LIST)) +#define NMT_IS_SLAVE_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_SLAVE_LIST)) +#define NMT_SLAVE_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_SLAVE_LIST, NmtSlaveListClass)) + +typedef struct { + NmtEditConnectionList parent; + +} NmtSlaveList; + +typedef struct { + NmtEditConnectionListClass parent; + +} NmtSlaveListClass; + +GType nmt_slave_list_get_type (void); + +NmtNewtWidget *nmt_slave_list_new (NMConnection *master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data); + +G_END_DECLS + +#endif /* NMT_SLAVE_LIST_H */ diff --git a/tui/nmt-utils.c b/tui/nmt-utils.c new file mode 100644 index 0000000000..f2bf70db82 --- /dev/null +++ b/tui/nmt-utils.c @@ -0,0 +1,151 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-utils + * @short_description: Miscellaneous nmtui-specific utilities + */ + +#include "config.h" + +#include <string.h> + +#include "nmt-utils.h" + +/** + * NmtSyncOp: + * + * A helper object used when synchronously waiting for an asynchronous + * operation to complete. + * + * The caller first does: + * + * |[ + * NmtSyncOp op; + * + * nmt_sync_op_init (&op); + * ]| + * + * It then passes the op as the user_data to the async operation's + * callback function, and then calls nmt_sync_op_wait_boolean() or + * nmt_sync_op_wait_pointer() to wait for the result. + * + * When the async callback is invoked, it should call + * nmt_sync_op_complete_boolean() or nmt_sync_op_complete_pointer() to + * return a result or an error to the caller. + * + * There is no free/clear function; any memory that needs to be freed + * will have been returned to the caller from + * nmt_sync_op_wait_boolean() or nmt_sync_op_wait_pointer(), so there + * is nothing left that needs to be freed. + */ + +typedef struct { + gpointer result; + GError *error; + gpointer complete; +} NmtSyncOpReal; + +/** + * nmt_sync_op_init: + * @op: pointer to a stack-allocated #NmtSyncOp + * + * Initializes @op before use. + */ +void +nmt_sync_op_init (NmtSyncOp *op) +{ + memset (op, 0, sizeof (*op)); +} + +/** + * nmt_sync_op_wait_boolean: + * @op: the #NmtSyncOp + * @error: return location for a #GError + * + * This runs the main loop until @op's operation returns, and then + * returns the result or error. + * + * Returns: the result of the operation. + */ +gboolean +nmt_sync_op_wait_boolean (NmtSyncOp *op, + GError **error) +{ + return GPOINTER_TO_UINT (nmt_sync_op_wait_pointer (op, error)); +} + +/** + * nmt_sync_op_complete_boolean: + * @op: the #NmtSyncOp + * @result: the result of the operation + * @error: (allow-none): the error, or %NULL + * + * Completes @op and returns @result and/or @error to the caller. + */ +void +nmt_sync_op_complete_boolean (NmtSyncOp *op, + gboolean result, + GError *error) +{ + nmt_sync_op_complete_pointer (op, GUINT_TO_POINTER (result), error); +} + +/** + * nmt_sync_op_wait_pointer: + * @op: the #NmtSyncOp + * @error: return location for a #GError + * + * This runs the main loop until @op's operation returns, and then + * returns the result or error. + * + * Returns: the result of the operation. + */ +gpointer +nmt_sync_op_wait_pointer (NmtSyncOp *op, + GError **error) +{ + NmtSyncOpReal *real = (NmtSyncOpReal *)op; + + while (!real->complete) + g_main_context_iteration (NULL, TRUE); + + if (real->error) + g_propagate_error (error, real->error); + return real->result; +} + +/** + * nmt_sync_op_complete_pointer: + * @op: the #NmtSyncOp + * @result: the result of the operation + * @error: (allow-none): the error, or %NULL + * + * Completes @op and returns @result and/or @error to the caller. + */ +void +nmt_sync_op_complete_pointer (NmtSyncOp *op, + gpointer result, + GError *error) +{ + NmtSyncOpReal *real = (NmtSyncOpReal *)op; + + real->result = result; + real->error = error ? g_error_copy (error) : NULL; + real->complete = GUINT_TO_POINTER (TRUE); +} diff --git a/tui/nmt-utils.h b/tui/nmt-utils.h new file mode 100644 index 0000000000..7e24fcab19 --- /dev/null +++ b/tui/nmt-utils.h @@ -0,0 +1,46 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_UTILS_H +#define NMT_UTILS_H + +#include <glib-object.h> + +G_BEGIN_DECLS + +typedef struct { + gpointer private[3]; +} NmtSyncOp; + +void nmt_sync_op_init (NmtSyncOp *op); + +gboolean nmt_sync_op_wait_boolean (NmtSyncOp *op, + GError **error); +void nmt_sync_op_complete_boolean (NmtSyncOp *op, + gboolean result, + GError *error); + +gpointer nmt_sync_op_wait_pointer (NmtSyncOp *op, + GError **error); +void nmt_sync_op_complete_pointer (NmtSyncOp *op, + gpointer result, + GError *error); + +G_END_DECLS + +#endif /* NMT_UTILS_H */ diff --git a/tui/nmt-widget-list.c b/tui/nmt-widget-list.c new file mode 100644 index 0000000000..eb3e850fca --- /dev/null +++ b/tui/nmt-widget-list.c @@ -0,0 +1,491 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-widget-list + * @short_description: A list of widgets, with Add and Remove buttons + * + * #NmtWidgetList presents a homogeneous list of widgets, with "Remove" + * buttons next to each one, and an "Add" button at the button to add + * new ones. + * + * It is the base class for #NmtAddressList, and is used internally by + * #NmtRouteTable. + * + * FIXME: The way this works is sort of weird. + */ + +#include "config.h" + +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdlib.h> +#include <string.h> + +#include <dbus/dbus-glib.h> +#include <glib/gi18n-lib.h> + +#include "nmt-widget-list.h" +#include "nmt-newt.h" + +G_DEFINE_TYPE (NmtWidgetList, nmt_widget_list, NMT_TYPE_NEWT_GRID) + +#define NMT_WIDGET_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_WIDGET_LIST, NmtWidgetListPrivate)) + +typedef struct { + int length; + + NmtWidgetListCallback create_callback; + gpointer user_data; + GDestroyNotify destroy_notify; + + NmtNewtWidget *empty_widget; + + GPtrArray *widgets; + GPtrArray *remove_buttons; + + NmtNewtWidget *add_button; + GBinding *add_sensitivity; +} NmtWidgetListPrivate; + +enum { + PROP_0, + PROP_CREATE_CALLBACK, + PROP_USER_DATA, + PROP_DESTROY_NOTIFY, + PROP_EMPTY_WIDGET, + PROP_LENGTH, + + LAST_PROP +}; + +enum { + ADD_CLICKED, + REMOVE_CLICKED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void add_clicked (NmtNewtButton *button, gpointer user_data); +static void remove_clicked (NmtNewtButton *button, gpointer user_data); + +/** + * NmtWidgetListCallback: + * @list: the #NmtWidgetList + * @n: the number of the widget being added + * @user_data: the callback's user data + * + * Called by #NmtWidgetList to ask for a new widget to be created. + * + * Note that the widget is not created to go with any particular + * value, but rather is created to be at a certain spot in the list. + * When an element is deleted from the list, it is actually always + * the last widget in the list that is removed, but it is assumed + * that the widget list is bound to some array-valued property, and + * so when an element is deleted from that array, the widgets will + * all update themselves automatically when the array changes. + * + * Returns: a new widget for the list + */ + +/** + * nmt_widget_list_new: + * @create_callback: callback to create new widgets + * @user_data: user data for @create_callback + * @destroy_notify: #GDestroyNotify for @user_data + * @empty_widget: (allow-none): a widget to display when there are + * no "real" widgets in the list. + * + * Creates a new #NmtWidgetList. + * + * Returns: a new #NmtWidgetList. + */ +NmtNewtWidget * +nmt_widget_list_new (NmtWidgetListCallback create_callback, + gpointer user_data, + GDestroyNotify destroy_notify, + NmtNewtWidget *empty_widget) +{ + return g_object_new (NMT_TYPE_WIDGET_LIST, + "create-callback", create_callback, + "user-data", user_data, + "destroy-notify", destroy_notify, + "empty-widget", empty_widget, + NULL); +} + +static void +nmt_widget_list_init (NmtWidgetList *list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + + priv->widgets = g_ptr_array_new (); + priv->remove_buttons = g_ptr_array_new (); + + priv->add_button = nmt_newt_button_new (_("Add...")); + g_signal_connect (priv->add_button, "clicked", + G_CALLBACK (add_clicked), list); + nmt_newt_grid_add (NMT_NEWT_GRID (list), priv->add_button, 0, 0); +} + +static void +nmt_widget_list_constructed (GObject *object) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object); + + if (priv->length == 0 && priv->empty_widget) { + nmt_newt_widget_set_visible (priv->empty_widget, TRUE); + nmt_newt_grid_move (NMT_NEWT_GRID (object), priv->add_button, 0, 1); + } + + G_OBJECT_CLASS (nmt_widget_list_parent_class)->constructed (object); +} + +static void +nmt_widget_list_finalize (GObject *object) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object); + + g_ptr_array_unref (priv->widgets); + g_ptr_array_unref (priv->remove_buttons); + + if (priv->user_data && priv->destroy_notify) + priv->destroy_notify (priv->user_data); + + g_clear_object (&priv->empty_widget); + + G_OBJECT_CLASS (nmt_widget_list_parent_class)->finalize (object); +} + +static void +ensure_widgets (NmtWidgetList *list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + NmtNewtWidget *widget, *button, *focus; + gboolean was_empty; + NmtNewtForm *form; + int i; + + was_empty = priv->widgets->len == 0; + + if (priv->length < priv->widgets->len) { + /* remove excess widgets */ + for (i = priv->length; i < priv->widgets->len; i++) { + nmt_newt_container_remove (NMT_NEWT_CONTAINER (list), priv->widgets->pdata[i]); + nmt_newt_container_remove (NMT_NEWT_CONTAINER (list), priv->remove_buttons->pdata[i]); + } + g_ptr_array_set_size (priv->widgets, priv->length); + g_ptr_array_set_size (priv->remove_buttons, priv->length); + + } else if (priv->length > priv->widgets->len) { + /* add new widgets */ + for (i = priv->widgets->len; i < priv->length; i++) { + widget = NMT_WIDGET_LIST_GET_CLASS (list)->create_widget (list, i); + + nmt_newt_grid_add (NMT_NEWT_GRID (list), widget, 0, i); + g_ptr_array_add (priv->widgets, widget); + + button = nmt_newt_button_new (_("Remove")); + g_signal_connect (button, "clicked", + G_CALLBACK (remove_clicked), list); + + nmt_newt_grid_add (NMT_NEWT_GRID (list), button, 1, i); + nmt_newt_widget_set_padding (button, 1, 0, 0, 0); + g_ptr_array_add (priv->remove_buttons, button); + } + + } else + return; + + if (priv->widgets->len == 0 && priv->empty_widget) { + nmt_newt_widget_set_visible (priv->empty_widget, TRUE); + nmt_newt_grid_move (NMT_NEWT_GRID (list), priv->add_button, 0, 1); + } else { + if (was_empty && priv->empty_widget) + nmt_newt_widget_set_visible (priv->empty_widget, FALSE); + nmt_newt_grid_move (NMT_NEWT_GRID (list), priv->add_button, 0, priv->length); + } + + form = nmt_newt_widget_get_form (NMT_NEWT_WIDGET (list)); + if (form) { + if (priv->widgets->len) { + if (was_empty) + focus = priv->widgets->pdata[0]; + else + focus = priv->widgets->pdata[priv->widgets->len - 1]; + } else + focus = priv->add_button; + nmt_newt_form_set_focus (form, focus); + } + + g_clear_object (&priv->add_sensitivity); + if (priv->widgets->len) { + widget = priv->widgets->pdata[priv->widgets->len - 1]; + priv->add_sensitivity = g_object_bind_property (widget, "valid", + priv->add_button, "sensitive", + G_BINDING_SYNC_CREATE); + g_object_add_weak_pointer (G_OBJECT (priv->add_sensitivity), + (gpointer *)&priv->add_sensitivity); + } +} + +static void +add_clicked (NmtNewtButton *button, gpointer list) +{ + g_signal_emit (G_OBJECT (list), signals[ADD_CLICKED], 0, NULL); +} + +static void +remove_clicked (NmtNewtButton *button, gpointer list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + int i; + + for (i = 0; i < priv->remove_buttons->len; i++) { + if (priv->remove_buttons->pdata[i] == (gpointer)button) + break; + } + g_return_if_fail (i < priv->remove_buttons->len); + + g_signal_emit (G_OBJECT (list), signals[REMOVE_CLICKED], 0, i, NULL); +} + +static NmtNewtWidget * +nmt_widget_list_real_create_widget (NmtWidgetList *list, + int n) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + + g_return_val_if_fail (priv->create_callback != NULL, NULL); + + return priv->create_callback (list, n, priv->user_data); +} + +/** + * nmt_widget_list_get_length: + * @list: the #NmtNewtWidgetList + * + * Gets the number of widgets in the list. + * + * Returns: the number of widgets in the list. + */ +int +nmt_widget_list_get_length (NmtWidgetList *list) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + + return priv->length; +} + +/** + * nmt_widget_list_set_length: + * @list: the #NmtNewtWidgetList + * @length: the new length + * + * Changes the number of widgets in the list. Widgets will be added or + * deleted as necessary. + */ +void +nmt_widget_list_set_length (NmtWidgetList *list, + int length) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (list); + + if (priv->length != length) { + priv->length = length; + g_object_notify (G_OBJECT (list), "length"); + } + + ensure_widgets (list); +} + +static void +nmt_widget_list_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CREATE_CALLBACK: + priv->create_callback = g_value_get_pointer (value); + break; + case PROP_USER_DATA: + priv->user_data = g_value_get_pointer (value); + break; + case PROP_DESTROY_NOTIFY: + priv->destroy_notify = g_value_get_pointer (value); + break; + case PROP_LENGTH: + priv->length = g_value_get_int (value); + ensure_widgets (NMT_WIDGET_LIST (object)); + break; + case PROP_EMPTY_WIDGET: + priv->empty_widget = g_value_get_object (value); + if (priv->empty_widget) { + g_object_ref_sink (priv->empty_widget); + nmt_newt_grid_add (NMT_NEWT_GRID (object), priv->empty_widget, 0, 0); + } + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_widget_list_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtWidgetListPrivate *priv = NMT_WIDGET_LIST_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_CREATE_CALLBACK: + g_value_set_pointer (value, priv->create_callback); + break; + case PROP_USER_DATA: + g_value_set_pointer (value, priv->user_data); + break; + case PROP_DESTROY_NOTIFY: + g_value_set_pointer (value, priv->destroy_notify); + break; + case PROP_LENGTH: + g_value_set_int (value, priv->length); + break; + case PROP_EMPTY_WIDGET: + g_value_set_object (value, priv->empty_widget); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_widget_list_class_init (NmtWidgetListClass *list_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (list_class); + + g_type_class_add_private (list_class, sizeof (NmtWidgetListPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_widget_list_constructed; + object_class->set_property = nmt_widget_list_set_property; + object_class->get_property = nmt_widget_list_get_property; + object_class->finalize = nmt_widget_list_finalize; + + list_class->create_widget = nmt_widget_list_real_create_widget; + + /* signals */ + + /** + * NmtNewtWidget::add-clicked: + * @list: the #NmtNewtWidgetList + * + * Emitted when the user clicks the "Add" button. The caller can + * decide whether or not to add a new widget, and call + * nmt_widget_list_set_length() with the new length if so. + * + * FIXME: the "Add" button should be insensitive if it's + * not going to work. + */ + signals[ADD_CLICKED] = + g_signal_new ("add-clicked", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NmtWidgetListClass, add_clicked), + NULL, NULL, NULL, + G_TYPE_NONE, 0); + /** + * NmtNewtWidget::remove-clicked: + * @list: the #NmtNewtWidgetList + * @n: the widget being removed + * + * Emitted when the user clicks one of the "Remove" buttons. The + * caller can decide whether or not to remove the widget, and + * call nmt_widget_list_set_length() with the new length if so. + * + * FIXME: the "Remove" button should be insensitive if it's not + * going to work. + */ + signals[REMOVE_CLICKED] = + g_signal_new ("remove-clicked", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (NmtWidgetListClass, remove_clicked), + NULL, NULL, NULL, + G_TYPE_NONE, 1, G_TYPE_INT); + + /* properties */ + + /** + * NmtWidgetList:create-callback: + * + * Callback called to create a new widget. + */ + g_object_class_install_property (object_class, PROP_CREATE_CALLBACK, + g_param_spec_pointer ("create-callback", "", "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:user-data: + * + * User data for #NmtWidgetList:create-callback + */ + g_object_class_install_property (object_class, PROP_USER_DATA, + g_param_spec_pointer ("user-data", "", "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:destroy-notify: + * + * #GDestroyNotify for #NmtWidgetList:user-data + */ + g_object_class_install_property (object_class, PROP_DESTROY_NOTIFY, + g_param_spec_pointer ("destroy-notify", "", "", + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:length: + * + * The length of the widget list; changing this value will add or + * remove widgets from the list. + */ + g_object_class_install_property (object_class, PROP_LENGTH, + g_param_spec_int ("length", "", "", + 0, G_MAXINT, 0, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + /** + * NmtWidgetList:empty-widget: + * + * If non-%NULL, this widget will be displayed when there are + * no "real" widgets in the list. + */ + g_object_class_install_property (object_class, PROP_EMPTY_WIDGET, + g_param_spec_object ("empty-widget", "", "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/tui/nmt-widget-list.h b/tui/nmt-widget-list.h new file mode 100644 index 0000000000..7dbeb21588 --- /dev/null +++ b/tui/nmt-widget-list.h @@ -0,0 +1,69 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMT_WIDGET_LIST_H +#define NMT_WIDGET_LIST_H + +#include "nmt-newt-grid.h" + +G_BEGIN_DECLS + +#define NMT_TYPE_WIDGET_LIST (nmt_widget_list_get_type ()) +#define NMT_WIDGET_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetList)) +#define NMT_WIDGET_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass)) +#define NMT_IS_WIDGET_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_WIDGET_LIST)) +#define NMT_IS_WIDGET_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NMT_TYPE_WIDGET_LIST)) +#define NMT_WIDGET_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NMT_TYPE_WIDGET_LIST, NmtWidgetListClass)) + +typedef struct { + NmtNewtGrid parent; + +} NmtWidgetList; + +typedef struct { + NmtNewtGridClass parent; + + /* signals */ + void (*add_clicked) (NmtWidgetList *list); + void (*remove_clicked) (NmtWidgetList *list, + int n); + + /* methods */ + NmtNewtWidget * (*create_widget) (NmtWidgetList *list, + int n); + +} NmtWidgetListClass; + +GType nmt_widget_list_get_type (void); + +typedef NmtNewtWidget * (*NmtWidgetListCallback) (NmtWidgetList *list, + int n, + gpointer user_data); + +NmtNewtWidget *nmt_widget_list_new (NmtWidgetListCallback create_callback, + gpointer user_data, + GDestroyNotify destroy_notify, + NmtNewtWidget *empty_widget); + +int nmt_widget_list_get_length (NmtWidgetList *list); +void nmt_widget_list_set_length (NmtWidgetList *list, + int length); + +G_END_DECLS + +#endif /* NMT_WIDGET_LIST_H */ diff --git a/tui/nmtui-connect.c b/tui/nmtui-connect.c new file mode 100644 index 0000000000..9307100a25 --- /dev/null +++ b/tui/nmtui-connect.c @@ -0,0 +1,130 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui-connect + * @short_description: nm-applet-like functionality + * + * nmtui-connect implements activating #NMConnections, including + * presenting a password dialog if necessary. + * + * It's supposed to also implement deactivating them, but it doesn't. + * FIXME. + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib/gi18n-lib.h> +#include <nm-utils.h> + +#include "nmt-newt.h" + +#include "nmtui.h" +#include "nmtui-connect.h" +#include "nmt-connect-connection-list.h" +#include "nmt-utils.h" + +static void +connect_complete (GObject *object, + GAsyncResult *result, + gpointer user_data) +{ + NmtSyncOp *op = user_data; + GError *error = NULL; + + if (nmt_connect_connection_list_activate_finish (NMT_CONNECT_CONNECTION_LIST (object), + result, &error)) + nmt_sync_op_complete_boolean (op, TRUE, NULL); + else + nmt_sync_op_complete_boolean (op, FALSE, error); + g_clear_error (&error); +} + +static void +nmt_connect_connection (const char *identifier) +{ + NmtNewtWidget *list; + NmtSyncOp op; + GError *error = NULL; + + nmt_sync_op_init (&op); + list = nmt_connect_connection_list_new (); + nmt_connect_connection_list_activate_async (NMT_CONNECT_CONNECTION_LIST (list), identifier, + connect_complete, &op); + if (!nmt_sync_op_wait_boolean (&op, &error)) { + nmt_newt_error_dialog (_("Could not activate connection: %s"), error->message); + g_error_free (error); + } + g_object_unref (list); +} + +static void +quit_clicked (NmtNewtButton *button, + gpointer user_data) +{ + nmtui_quit (); +} + +static void +nmt_connect_connection_list (void) +{ + int screen_width, screen_height; + NmtNewtForm *form; + NmtNewtWidget *list, *activate, *quit, *bbox, *grid; + + newtGetScreenSize (&screen_width, &screen_height); + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "y", 2, + "height", screen_height - 4, + "escape-exits", TRUE, + NULL); + + grid = nmt_newt_grid_new (); + + list = nmt_connect_connection_list_new (); + nmt_newt_grid_add (NMT_NEWT_GRID (grid), list, 0, 0); + nmt_newt_grid_set_flags (NMT_NEWT_GRID (grid), list, + NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y | + NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_EXPAND_Y); + + bbox = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_VERTICAL); + nmt_newt_grid_add (NMT_NEWT_GRID (grid), bbox, 1, 0); + nmt_newt_widget_set_padding (bbox, 1, 1, 0, 1); + + // FIXME: the activate button doesn't do anything + activate = nmt_newt_button_box_add_start (NMT_NEWT_BUTTON_BOX (bbox), _("Activate")); + quit = nmt_newt_button_box_add_end (NMT_NEWT_BUTTON_BOX (bbox), _("Quit")); + nmt_newt_widget_set_exit_on_activate (quit, TRUE); + g_signal_connect (quit, "clicked", G_CALLBACK (quit_clicked), NULL); + + nmt_newt_form_set_content (form, grid); + nmt_newt_form_show (form); + g_object_unref (form); +} + +void +nmtui_connect (int argc, char **argv) +{ + if (argc == 2) + nmt_connect_connection (argv[1]); + else + nmt_connect_connection_list (); +} diff --git a/tui/nmtui-connect.h b/tui/nmtui-connect.h new file mode 100644 index 0000000000..818b860ed6 --- /dev/null +++ b/tui/nmtui-connect.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_CONNECT_H +#define NMTUI_CONNECT_H + +G_BEGIN_DECLS + +void nmtui_connect (int argc, char **argv); + +G_END_DECLS + +#endif /* NMTUI_CONNECT_H */ diff --git a/tui/nmtui-edit.c b/tui/nmtui-edit.c new file mode 100644 index 0000000000..f7ca4caa0d --- /dev/null +++ b/tui/nmtui-edit.c @@ -0,0 +1,527 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui-edit + * @short_description: nm-connection-editor-like functionality + * + * nmtui-edit implements editing #NMConnections. + */ + +#include "config.h" + +#include <stdlib.h> + +#include <glib/gi18n-lib.h> +#include <nm-utils.h> + +#include "nmtui.h" +#include "nmtui-edit.h" +#include "nmt-edit-connection-list.h" +#include "nmt-editor.h" +#include "nmt-utils.h" + +#include "nm-editor-utils.h" + +static void +list_add_connection (NmtEditConnectionList *list, + gpointer form) +{ + nmt_add_connection (); + nmt_newt_form_set_focus (form, NMT_NEWT_WIDGET (list)); +} + +static void +list_edit_connection (NmtEditConnectionList *list, + NMConnection *connection, + gpointer form) +{ + nmt_edit_connection (connection); + nmt_newt_form_set_focus (form, NMT_NEWT_WIDGET (list)); +} + +static void +list_remove_connection (NmtEditConnectionList *list, + NMRemoteConnection *connection, + gpointer form) +{ + nmt_remove_connection (connection); + nmt_newt_form_set_focus (form, NMT_NEWT_WIDGET (list)); +} + +static gboolean +edit_connection_list_filter (NmtEditConnectionList *list, + NMConnection *connection, + gpointer user_data) +{ + NMSettingConnection *s_con; + + s_con = nm_connection_get_setting_connection (connection); + g_return_val_if_fail (s_con != NULL, FALSE); + + return (nm_setting_connection_get_slave_type (s_con) == NULL); +} + +static void +quit_clicked (NmtNewtButton *button, + gpointer user_data) +{ + nmtui_quit (); +} + +static void +nmt_edit_main_connection_list (void) +{ + int screen_width, screen_height; + NmtNewtForm *form; + NmtNewtWidget *quit, *list; + + newtGetScreenSize (&screen_width, &screen_height); + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "y", 2, + "height", screen_height - 4, + "escape-exits", TRUE, + NULL); + + quit = nmt_newt_button_new (_("Quit")); + nmt_newt_widget_set_exit_on_activate (quit, TRUE); + g_signal_connect (quit, "clicked", G_CALLBACK (quit_clicked), NULL); + + list = g_object_new (NMT_TYPE_EDIT_CONNECTION_LIST, + "extra-widget", quit, + "connection-filter", edit_connection_list_filter, + NULL); + + g_signal_connect (list, "add-connection", + G_CALLBACK (list_add_connection), form); + g_signal_connect (list, "edit-connection", + G_CALLBACK (list_edit_connection), form); + g_signal_connect (list, "remove-connection", + G_CALLBACK (list_remove_connection), form); + + nmt_newt_form_set_content (form, list); + nmt_newt_form_show (form); + g_object_unref (form); +} + +#define NMT_TYPE_ADD_CONNECTION (nmt_add_connection_get_type ()) +#define NMT_ADD_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NMT_TYPE_ADD_CONNECTION, NmtAddConnection)) +#define NMT_IS_ADD_CONNECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NMT_TYPE_ADD_CONNECTION)) + +typedef NmtNewtForm NmtAddConnection; +typedef NmtNewtFormClass NmtAddConnectionClass; + +GType nmt_add_connection_get_type (void); + +G_DEFINE_TYPE (NmtAddConnection, nmt_add_connection, NMT_TYPE_NEWT_FORM) + +#define NMT_ADD_CONNECTION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_ADD_CONNECTION, NmtAddConnectionPrivate)) + +typedef struct { + NmtNewtTextbox *textbox; + NmtNewtListbox *listbox; + + char *primary_text; + char *secondary_text; + NMConnection *master; + NmtAddConnectionTypeFilter type_filter; + gpointer type_filter_data; + + gboolean single_type; +} NmtAddConnectionPrivate; + +enum { + PROP_0, + + PROP_PRIMARY_TEXT, + PROP_SECONDARY_TEXT, + PROP_MASTER, + PROP_TYPE_FILTER, + PROP_TYPE_FILTER_DATA, + + LAST_PROP +}; + +static void +create_connection (NmtNewtWidget *widget, gpointer list) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (list); + GType type = (GType) GPOINTER_TO_SIZE (nmt_newt_listbox_get_active_key (priv->listbox)); + NMConnection *connection; + + connection = nm_editor_utils_create_connection (type, priv->master, nm_settings); + nmt_edit_connection (connection); + g_object_unref (connection); + + nmt_newt_form_quit (list); +} + +static void +nmt_add_connection_init (NmtAddConnection *form) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (form); + NmtNewtWidget *textbox, *listbox, *button; + NmtNewtGrid *grid, *buttons; + + grid = NMT_NEWT_GRID (nmt_newt_grid_new ()); + + textbox = nmt_newt_textbox_new (0, 60); + priv->textbox = NMT_NEWT_TEXTBOX (textbox); + nmt_newt_grid_add (grid, textbox, 0, 0); + + listbox = nmt_newt_listbox_new (5, NMT_NEWT_LISTBOX_SCROLL); + priv->listbox = NMT_NEWT_LISTBOX (listbox); + g_signal_connect (priv->listbox, "activated", G_CALLBACK (create_connection), form); + nmt_newt_grid_add (grid, listbox, 0, 1); + nmt_newt_widget_set_padding (listbox, 0, 1, 0, 0); + nmt_newt_grid_set_flags (grid, listbox, NMT_NEWT_GRID_EXPAND_X); + + // FIXME: VPN description textbox + + buttons = NMT_NEWT_GRID (nmt_newt_grid_new ()); + nmt_newt_grid_add (grid, NMT_NEWT_WIDGET (buttons), 0, 2); + nmt_newt_widget_set_padding (NMT_NEWT_WIDGET (buttons), 0, 1, 0, 0); + + button = g_object_ref_sink (nmt_newt_button_new (_("Cancel"))); + nmt_newt_widget_set_exit_on_activate (button, TRUE); + nmt_newt_grid_add (NMT_NEWT_GRID (buttons), button, 0, 0); + nmt_newt_widget_set_padding (button, 0, 0, 1, 0); + nmt_newt_grid_set_flags (NMT_NEWT_GRID (buttons), button, + NMT_NEWT_GRID_EXPAND_X | NMT_NEWT_GRID_ANCHOR_RIGHT | + NMT_NEWT_GRID_FILL_Y); + + button = g_object_ref_sink (nmt_newt_button_new (_("Create"))); + g_signal_connect (button, "clicked", G_CALLBACK (create_connection), form); + nmt_newt_grid_add (NMT_NEWT_GRID (buttons), button, 1, 0); + + nmt_newt_form_set_content (NMT_NEWT_FORM (form), NMT_NEWT_WIDGET (grid)); +} + +static void +nmt_add_connection_constructed (GObject *object) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object); + NMEditorConnectionTypeData **types; + char *text; + int i, num_types; + + if (priv->secondary_text) { + text = g_strdup_printf ("%s\n\n%s", + priv->primary_text, + priv->secondary_text); + } else + text = g_strdup (priv->primary_text); + nmt_newt_textbox_set_text (priv->textbox, text); + g_free (text); + + types = nm_editor_utils_get_connection_type_list (); + for (i = num_types = 0; types[i]; i++) { + if (priv->type_filter && !priv->type_filter (types[i]->setting_type, priv->type_filter_data)) + continue; + nmt_newt_listbox_append (priv->listbox, types[i]->name, + GSIZE_TO_POINTER (types[i]->setting_type)); + num_types++; + } + + if (num_types == 1) + priv->single_type = TRUE; + + G_OBJECT_CLASS (nmt_add_connection_parent_class)->constructed (object); +} + +static void +nmt_add_connection_show (NmtNewtForm *form) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (form); + + if (priv->single_type) { + nmt_newt_listbox_set_active (priv->listbox, 0); + create_connection (NMT_NEWT_WIDGET (priv->listbox), g_object_ref (form)); + } else + NMT_NEWT_FORM_CLASS (nmt_add_connection_parent_class)->show (form); +} + +static void +nmt_add_connection_finalize (GObject *object) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object); + + g_free (priv->primary_text); + g_free (priv->secondary_text); + g_clear_object (&priv->master); + + G_OBJECT_CLASS (nmt_add_connection_parent_class)->finalize (object); +} + +static void +nmt_add_connection_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PRIMARY_TEXT: + priv->primary_text = g_value_dup_string (value); + break; + case PROP_SECONDARY_TEXT: + priv->secondary_text = g_value_dup_string (value); + break; + case PROP_MASTER: + priv->master = g_value_dup_object (value); + break; + case PROP_TYPE_FILTER: + priv->type_filter = g_value_get_pointer (value); + break; + case PROP_TYPE_FILTER_DATA: + priv->type_filter_data = g_value_get_pointer (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_add_connection_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NmtAddConnectionPrivate *priv = NMT_ADD_CONNECTION_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_PRIMARY_TEXT: + g_value_set_string (value, priv->primary_text); + break; + case PROP_SECONDARY_TEXT: + g_value_set_string (value, priv->secondary_text); + break; + case PROP_MASTER: + g_value_set_object (value, priv->master); + break; + case PROP_TYPE_FILTER: + g_value_set_pointer (value, priv->type_filter); + break; + case PROP_TYPE_FILTER_DATA: + g_value_set_pointer (value, priv->type_filter_data); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +nmt_add_connection_class_init (NmtAddConnectionClass *add_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (add_class); + NmtNewtFormClass *form_class = NMT_NEWT_FORM_CLASS (add_class); + + g_type_class_add_private (add_class, sizeof (NmtAddConnectionPrivate)); + + /* virtual methods */ + object_class->constructed = nmt_add_connection_constructed; + object_class->set_property = nmt_add_connection_set_property; + object_class->get_property = nmt_add_connection_get_property; + object_class->finalize = nmt_add_connection_finalize; + + form_class->show = nmt_add_connection_show; + + g_object_class_install_property (object_class, PROP_PRIMARY_TEXT, + g_param_spec_string ("primary-text", "", "", + _("Select the type of connection you wish to create."), + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_SECONDARY_TEXT, + g_param_spec_string ("secondary-text", "", "", +#if 0 + _("If you are creating a VPN, and the VPN connection you " + "wish to create does not appear in the list, you may " + "not have the correct VPN plugin installed."), +#else + NULL, +#endif + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_MASTER, + g_param_spec_object ("master", "", "", + NM_TYPE_CONNECTION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_TYPE_FILTER, + g_param_spec_pointer ("type-filter", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_TYPE_FILTER_DATA, + g_param_spec_pointer ("type-filter-data", "", "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +void +nmt_add_connection (void) +{ + NmtNewtForm *form; + + form = g_object_new (NMT_TYPE_ADD_CONNECTION, + "title", _("New Connection"), + NULL); + nmt_newt_form_show (form); + g_object_unref (form); +} + +void +nmt_add_connection_full (const char *primary_text, + const char *secondary_text, + NMConnection *master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data) +{ + NmtNewtForm *form; + + form = g_object_new (NMT_TYPE_ADD_CONNECTION, + "title", _("New Connection"), + "primary-text", primary_text, + "secondary-text", secondary_text, + "master", master, + "type-filter", type_filter, + "type-filter-data", type_filter_data, + NULL); + nmt_newt_form_show (form); + g_object_unref (form); +} + +void +nmt_edit_connection (NMConnection *connection) +{ + NmtNewtForm *editor; + + editor = nmt_editor_new (connection); + if (!editor) + return; + + nmt_newt_form_show (editor); + g_object_unref (editor); +} + +typedef struct { + NmtSyncOp op; + gboolean got_callback, got_signal; +} ConnectionDeleteData; + +static void +connection_deleted_callback (NMRemoteConnection *connection, + GError *error, + gpointer user_data) +{ + ConnectionDeleteData *data = user_data; + + if (error) { + nmt_newt_error_dialog (_("OK"), + _("Unable to delete connection: %s"), + error->message); + } else + data->got_callback = TRUE; + + if (error || (data->got_callback && data->got_signal)) + nmt_sync_op_complete_boolean (&data->op, error == NULL, error); +} + +static void +connection_removed_signal (NMRemoteConnection *connection, + gpointer user_data) +{ + ConnectionDeleteData *data = user_data; + + data->got_signal = TRUE; + if (data->got_callback && data->got_signal) + nmt_sync_op_complete_boolean (&data->op, TRUE, NULL); +} + +void +nmt_remove_connection (NMRemoteConnection *connection) +{ + ConnectionDeleteData data; + int choice; + GError *error = NULL; + + choice = nmt_newt_choice_dialog (_("Cancel"), + _("Delete"), + _("Are you sure you want to delete the connection '%s'?"), + nm_connection_get_id (NM_CONNECTION (connection))); + if (choice == 1) + return; + + data.got_callback = data.got_signal = FALSE; + nmt_sync_op_init (&data.op); + + g_object_ref (connection); + g_signal_connect (connection, NM_REMOTE_CONNECTION_REMOVED, + G_CALLBACK (connection_removed_signal), &data); + nm_remote_connection_delete (connection, connection_deleted_callback, &data); + + if (!nmt_sync_op_wait_boolean (&data.op, &error)) { + nmt_newt_error_dialog (_("Could not delete connection: %s"), + error->message); + g_error_free (error); + } + + g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_removed_signal), &data); + g_object_unref (connection); +} + +void +nmtui_edit (int argc, char **argv) +{ + NMConnection *conn = NULL; + + if (argc == 2) { + if (nm_utils_is_uuid (argv[1])) + conn = NM_CONNECTION (nm_remote_settings_get_connection_by_uuid (nm_settings, argv[1])); + else { + GSList *conns, *iter; + + conns = nm_remote_settings_list_connections (nm_settings); + for (iter = conns; iter; iter = iter->next) { + NMConnection *candidate = iter->data; + + if (!strcmp (argv[1], nm_connection_get_id (candidate))) { + conn = candidate; + break; + } + } + g_slist_free (conns); + } + + if (!conn) { + g_printerr ("%s: no such connection '%s'\n", argv[0], argv[1]); + exit (1); + } + + nmt_edit_connection (conn); + } else + nmt_edit_main_connection_list (); +} diff --git a/tui/nmtui-edit.h b/tui/nmtui-edit.h new file mode 100644 index 0000000000..a28c8a6f4f --- /dev/null +++ b/tui/nmtui-edit.h @@ -0,0 +1,44 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_EDIT_H +#define NMTUI_EDIT_H + +#include "nmt-newt.h" + +G_BEGIN_DECLS + +typedef gboolean (*NmtAddConnectionTypeFilter) (GType connection_type, + gpointer user_data); + +void nmtui_edit (int argc, char **argv); + +void nmt_add_connection (void); +void nmt_add_connection_full (const char *primary_text, + const char *secondary_text, + NMConnection *master, + NmtAddConnectionTypeFilter type_filter, + gpointer type_filter_data); + +void nmt_edit_connection (NMConnection *connection); + +void nmt_remove_connection (NMRemoteConnection *connection); + +G_END_DECLS + +#endif /* NMTUI_EDIT_H */ diff --git a/tui/nmtui-hostname.c b/tui/nmtui-hostname.c new file mode 100644 index 0000000000..11216bc6ce --- /dev/null +++ b/tui/nmtui-hostname.c @@ -0,0 +1,124 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui-hostname + * @short_description: hostname-setting functionality + * + * nmtui-hostname implements the "set hostname" functionality + */ + +#include "config.h" + +#include <glib/gi18n-lib.h> + +#include "nmt-newt.h" + +#include "nmtui.h" +#include "nmtui-hostname.h" +#include "nmt-utils.h" + +static char * +nmtui_hostname_run_dialog (void) +{ + NmtNewtForm *form; + NmtNewtWidget *widget, *ok, *cancel; + NmtNewtGrid *grid; + NmtNewtEntry *entry; + NmtNewtButtonBox *bbox; + char *hostname, *ret = NULL; + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "title", _("Set Hostname"), + "escape-exits", TRUE, + NULL); + + widget = nmt_newt_grid_new (); + nmt_newt_form_set_content (form, widget); + grid = NMT_NEWT_GRID (widget); + + widget = nmt_newt_label_new (_("Hostname")); + nmt_newt_grid_add (grid, widget, 0, 0); + + widget = nmt_newt_entry_new (40, 0); + nmt_newt_widget_set_exit_on_activate (widget, TRUE); + nmt_newt_grid_add (grid, widget, 1, 0); + nmt_newt_widget_set_padding (widget, 1, 0, 0, 0); + entry = NMT_NEWT_ENTRY (widget); + + widget = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add (grid, widget, 1, 1); + nmt_newt_widget_set_padding (widget, 0, 1, 0, 0); + bbox = NMT_NEWT_BUTTON_BOX (widget); + + cancel = nmt_newt_button_box_add_end (bbox, _("Cancel")); + nmt_newt_widget_set_exit_on_activate (cancel, TRUE); + ok = nmt_newt_button_box_add_end (bbox, _("OK")); + nmt_newt_widget_set_exit_on_activate (ok, TRUE); + + g_object_get (G_OBJECT (nm_settings), + NM_REMOTE_SETTINGS_HOSTNAME, &hostname, + NULL); + nmt_newt_entry_set_text (entry, hostname); + g_free (hostname); + + widget = nmt_newt_form_run_sync (form); + if (widget == (NmtNewtWidget *)entry || widget == ok) + ret = g_strdup (nmt_newt_entry_get_text (entry)); + + g_object_unref (form); + return ret; +} + +static void +hostname_set (NMRemoteSettings *settings, + GError *error, + gpointer op) +{ + nmt_sync_op_complete_boolean (op, error == NULL, error); +} + +void +nmtui_hostname (int argc, char **argv) +{ + const char *hostname; + char *tmp = NULL; + NmtSyncOp op; + GError *error = NULL; + + if (argc == 2) + hostname = argv[1]; + else + hostname = tmp = nmtui_hostname_run_dialog (); + + if (hostname) { + nmt_sync_op_init (&op); + nm_remote_settings_save_hostname (nm_settings, hostname, hostname_set, &op); + if (nmt_sync_op_wait_boolean (&op, &error)) { + /* Translators: this indicates the result. ie, "I have set the hostname to ..." */ + nmt_newt_error_dialog (_("Set hostname to '%s'"), hostname); + } else { + nmt_newt_error_dialog (_("Unable to set hostname: %s"), error->message); + g_error_free (error); + } + + g_free (tmp); + } + + nmtui_quit (); +} diff --git a/tui/nmtui-hostname.h b/tui/nmtui-hostname.h new file mode 100644 index 0000000000..921b231b2f --- /dev/null +++ b/tui/nmtui-hostname.h @@ -0,0 +1,28 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_HOSTNAME_H +#define NMTUI_HOSTNAME_H + +G_BEGIN_DECLS + +void nmtui_hostname (int argc, char **argv); + +G_END_DECLS + +#endif /* NMTUI_HOSTNAME_H */ diff --git a/tui/nmtui.c b/tui/nmtui.c new file mode 100644 index 0000000000..ab4ee0fc72 --- /dev/null +++ b/tui/nmtui.c @@ -0,0 +1,274 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmtui + * @short_description: nmtui toplevel + * + * The top level of nmtui. Exists mostly just to call nmtui_connect(), + * nmtui_edit(), and nmtui_hostname(). + */ + +#include "config.h" + +#include <locale.h> +#include <stdlib.h> +#include <string.h> + +#include <glib.h> +#include <glib/gi18n-lib.h> + +#include <nm-client.h> +#include <nm-connection.h> +#include <nm-remote-settings.h> +#include <nm-utils.h> + +#include "nmt-newt.h" + +#include "nmtui.h" +#include "nmtui-edit.h" +#include "nmtui-connect.h" +#include "nmtui-hostname.h" +#include "nm-editor-bindings.h" + +NMClient *nm_client; +NMRemoteSettings *nm_settings; +static GMainLoop *loop; + +typedef void (*NmtuiSubprogram) (int argc, char **argv); + +static const struct { + const char *name, *shortcut, *usage; + const char *display_name; + NmtuiSubprogram func; +} subprograms[] = { + { "edit", "nmtui-edit", " [connection]", + N_("Edit a connection"), + nmtui_edit }, + { "connect", "nmtui-connect", " [connection]", + N_("Activate a connection"), + nmtui_connect }, + { "hostname", "nmtui-hostname", " [new hostname]", + N_("Set system hostname"), + nmtui_hostname } +}; +static const int num_subprograms = G_N_ELEMENTS (subprograms); + +static void +nmtui_main (int argc, char **argv) +{ + NmtNewtForm *form; + NmtNewtWidget *widget, *cancel, *ok; + NmtNewtGrid *grid; + NmtNewtListbox *listbox; + NmtNewtButtonBox *bbox; + int i; + + form = g_object_new (NMT_TYPE_NEWT_FORM, + "title", _("NetworkManager TUI"), + "escape-exits", TRUE, + NULL); + + widget = nmt_newt_grid_new (); + nmt_newt_form_set_content (form, widget); + grid = NMT_NEWT_GRID (widget); + + widget = nmt_newt_label_new (_("Please select an option")); + nmt_newt_grid_add (grid, widget, 0, 0); + + widget = nmt_newt_listbox_new (num_subprograms, 0); + nmt_newt_grid_add (grid, widget, 0, 1); + nmt_newt_widget_set_padding (widget, 0, 1, 0, 1); + nmt_newt_widget_set_exit_on_activate (widget, TRUE); + listbox = NMT_NEWT_LISTBOX (widget); + + for (i = 0; i < num_subprograms; i++) { + nmt_newt_listbox_append (listbox, _(subprograms[i].display_name), + subprograms[i].func); + } + + widget = nmt_newt_button_box_new (NMT_NEWT_BUTTON_BOX_HORIZONTAL); + nmt_newt_grid_add (grid, widget, 0, 2); + bbox = NMT_NEWT_BUTTON_BOX (widget); + + cancel = nmt_newt_button_box_add_end (bbox, _("Cancel")); + nmt_newt_widget_set_exit_on_activate (cancel, TRUE); + ok = nmt_newt_button_box_add_end (bbox, _("OK")); + nmt_newt_widget_set_exit_on_activate (ok, TRUE); + + widget = nmt_newt_form_run_sync (form); + if (widget == ok || widget == (NmtNewtWidget *)listbox) { + NmtuiSubprogram subprogram = nmt_newt_listbox_get_active_key (listbox); + + subprogram (argc, argv); + } else + nmtui_quit (); + g_object_unref (form); +} + +/** + * nmtui_quit: + * + * Causes nmtui to exit. + */ +void +nmtui_quit (void) +{ + g_main_loop_quit (loop); +} + +static void +connections_read (NMRemoteSettings *settings, + gpointer user_data) +{ + gboolean *got_connections = user_data; + + *got_connections = TRUE; +} + +static void +usage (void) +{ + const char *argv0 = g_get_prgname (); + int i; + + for (i = 0; i < num_subprograms; i++) { + if (!strcmp (argv0, subprograms[i].shortcut)) { + g_printerr ("Usage: %s%s\n", argv0, subprograms[i].usage); + exit (1); + } + } + + g_printerr ("Usage: nmtui\n"); + for (i = 0; i < num_subprograms; i++) { + g_printerr (" nmtui %s%s\n", subprograms[i].name, + subprograms[i].usage); + } + exit (1); +} + +typedef struct { + NmtuiSubprogram subprogram; + int argc; + char **argv; +} NmtuiStartupData; + +static gboolean +idle_run_subprogram (gpointer user_data) +{ + NmtuiStartupData *data = user_data; + + data->subprogram (data->argc, data->argv); + return FALSE; +} + +gboolean sleep_on_startup = FALSE; +gboolean noinit = FALSE; + +GOptionEntry entries[] = { + { "sleep", 's', 0, G_OPTION_ARG_NONE, &sleep_on_startup, + "Sleep on startup", NULL }, + { "noinit", 'n', 0, G_OPTION_ARG_NONE, &noinit, + "Don't initialize newt", NULL }, + { NULL } +}; + +int +main (int argc, char **argv) +{ + gboolean got_connections = FALSE; + GOptionContext *opts; + GError *error = NULL; + NmtuiStartupData startup_data; + const char *prgname; + int i; + + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + textdomain (GETTEXT_PACKAGE); + + opts = g_option_context_new (NULL); + g_option_context_add_main_entries (opts, entries, NULL); + + if (!g_option_context_parse (opts, &argc, &argv, &error)) { + g_printerr ("%s: Could not parse arguments: %s\n", + argv[0], error->message); + exit (1); + } + g_option_context_free (opts); + + nm_client = nm_client_new (); + + nm_settings = nm_remote_settings_new (NULL); + g_signal_connect (nm_settings, NM_REMOTE_SETTINGS_CONNECTIONS_READ, + G_CALLBACK (connections_read), &got_connections); + while (!got_connections) + g_main_context_iteration (NULL, TRUE); + + if (sleep_on_startup) + sleep (5); + + nm_editor_bindings_init (); + + startup_data.subprogram = NULL; + prgname = g_get_prgname (); + if (g_str_has_prefix (prgname, "lt-")) + prgname += 3; + if (!strcmp (prgname, "nmtui")) { + if (argc > 1) { + for (i = 0; i < num_subprograms; i++) { + if (!strcmp (argv[1], subprograms[i].name)) { + argc--; + argv[0] = (char *) subprograms[i].shortcut; + memmove (&argv[1], &argv[2], argc * sizeof (char *)); + startup_data.subprogram = subprograms[i].func; + break; + } + } + } else + startup_data.subprogram = nmtui_main; + } else { + for (i = 0; i < num_subprograms; i++) { + if (!strcmp (prgname, subprograms[i].shortcut)) { + startup_data.subprogram = subprograms[i].func; + break; + } + } + } + if (!startup_data.subprogram) + usage (); + + if (!noinit) + nmt_newt_init (); + + startup_data.argc = argc; + startup_data.argv = argv; + g_idle_add (idle_run_subprogram, &startup_data); + loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + if (!noinit) + nmt_newt_finished (); + + g_object_unref (nm_client); + g_object_unref (nm_settings); + + return 0; +} diff --git a/tui/nmtui.h b/tui/nmtui.h new file mode 100644 index 0000000000..3e462b1130 --- /dev/null +++ b/tui/nmtui.h @@ -0,0 +1,34 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef NMTUI_H +#define NMTUI_H + +#include <nm-client.h> +#include <nm-remote-settings.h> + +G_BEGIN_DECLS + +extern NMClient *nm_client; +extern NMRemoteSettings *nm_settings; + +void nmtui_quit (void); + +G_END_DECLS + +#endif /* NMTUI_H */ diff --git a/tui/vpn-helpers.c b/tui/vpn-helpers.c new file mode 100644 index 0000000000..f4e32837a8 --- /dev/null +++ b/tui/vpn-helpers.c @@ -0,0 +1,424 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +/** + * SECTION:vpn-helpers + * @short_description: VPN-related utilities + * + * This is copied directly from libnm-gtk and should probably + * eventually move into libnm-glib. + * + * It is also currently unused in nmtui. + * + * FIXME. + */ + +#include <string.h> +#include <glib.h> +#include <gmodule.h> +#include <glib/gi18n.h> + +#include <nm-connection.h> +#include <nm-setting-connection.h> +#include <nm-setting-vpn.h> + +#include "vpn-helpers.h" + +#define NM_VPN_API_SUBJECT_TO_CHANGE +#include "nm-vpn-plugin-ui-interface.h" + +#define VPN_NAME_FILES_DIR SYSCONFDIR"/NetworkManager/VPN" + +static GHashTable *plugins = NULL; + +G_DEFINE_QUARK (NMA_ERROR, nma_error) +#define NMA_ERROR nma_error_quark () +#define NMA_ERROR_GENERIC 0 + +NMVpnPluginUiInterface * +vpn_get_plugin_by_service (const char *service) +{ + g_return_val_if_fail (service != NULL, NULL); + + return g_hash_table_lookup (plugins, service); +} + +GHashTable * +vpn_get_plugins (GError **error) +{ + GDir *dir; + const char *f; + + if (error) + g_return_val_if_fail (*error == NULL, NULL); + + if (plugins) + return plugins; + + dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL); + if (!dir) { + g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "Couldn't read VPN .name files directory " VPN_NAME_FILES_DIR "."); + return NULL; + } + + plugins = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); + + while ((f = g_dir_read_name (dir))) { + char *path = NULL, *service = NULL; + char *so_path = NULL, *so_name = NULL; + GKeyFile *keyfile = NULL; + GModule *module; + NMVpnPluginUiFactory factory = NULL; + + if (!g_str_has_suffix (f, ".name")) + continue; + + path = g_strdup_printf ("%s/%s", VPN_NAME_FILES_DIR, f); + + keyfile = g_key_file_new (); + if (!g_key_file_load_from_file (keyfile, path, 0, NULL)) + goto next; + + service = g_key_file_get_string (keyfile, "VPN Connection", "service", NULL); + if (!service) + goto next; + + so_path = g_key_file_get_string (keyfile, "GNOME", "properties", NULL); + if (!so_path) + goto next; + + /* Remove any path and extension components, then reconstruct path + * to the SO in LIBDIR + */ + so_name = g_path_get_basename (so_path); + g_free (so_path); + so_path = g_strdup_printf ("%s/NetworkManager/%s", LIBDIR, so_name); + g_free (so_name); + + module = g_module_open (so_path, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL); + if (!module) { + g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "Cannot load the VPN plugin which provides the " + "service '%s'.", service); + goto next; + } + + if (g_module_symbol (module, "nm_vpn_plugin_ui_factory", (gpointer) &factory)) { + NMVpnPluginUiInterface *plugin; + GError *factory_error = NULL; + gboolean success = FALSE; + + plugin = factory (&factory_error); + if (plugin) { + char *plug_name = NULL, *plug_service = NULL; + + /* Validate plugin properties */ + g_object_get (G_OBJECT (plugin), + NM_VPN_PLUGIN_UI_INTERFACE_NAME, &plug_name, + NM_VPN_PLUGIN_UI_INTERFACE_SERVICE, &plug_service, + NULL); + if (!plug_name || !strlen (plug_name)) { + g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': missing plugin name", + g_module_name (module)); + } else if (!plug_service || strcmp (plug_service, service)) { + g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': invalid service name", + g_module_name (module)); + } else { + /* Success! */ + g_object_set_data_full (G_OBJECT (plugin), "gmodule", module, + (GDestroyNotify) g_module_close); + g_hash_table_insert (plugins, g_strdup (service), plugin); + success = TRUE; + } + g_free (plug_name); + g_free (plug_service); + } else { + g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot load VPN plugin in '%s': %s", + g_module_name (module), g_module_error ()); + } + + if (!success) + g_module_close (module); + } else { + g_set_error (error, NMA_ERROR, NMA_ERROR_GENERIC, "cannot locate nm_vpn_plugin_ui_factory() in '%s': %s", + g_module_name (module), g_module_error ()); + g_module_close (module); + } + + next: + g_free (so_path); + g_free (service); + g_key_file_free (keyfile); + g_free (path); + } + g_dir_close (dir); + + return plugins; +} + +#if 0 +typedef struct { + VpnImportSuccessCallback callback; + gpointer user_data; +} ActionInfo; + +static void +import_vpn_from_file_cb (GtkWidget *dialog, gint response, gpointer user_data) +{ + char *filename = NULL; + ActionInfo *info = (ActionInfo *) user_data; + GHashTableIter iter; + gpointer key; + NMVpnPluginUiInterface *plugin; + NMConnection *connection = NULL; + GError *error = NULL; + + if (response != GTK_RESPONSE_ACCEPT) + goto out; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + if (!filename) { + g_warning ("%s: didn't get a filename back from the chooser!", __func__); + goto out; + } + + g_hash_table_iter_init (&iter, plugins); + while (!connection && g_hash_table_iter_next (&iter, &key, (gpointer *)&plugin)) { + g_clear_error (&error); + connection = nm_vpn_plugin_ui_interface_import (plugin, filename, &error); + } + + if (connection) + info->callback (connection, info->user_data); + else { + GtkWidget *err_dialog; + char *bname = g_path_get_basename (filename); + + err_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot import VPN connection")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog), + _("The file '%s' could not be read or does not contain recognized VPN connection information\n\nError: %s."), + bname, error ? error->message : "unknown error"); + g_free (bname); + g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show_all (err_dialog); + gtk_window_present (GTK_WINDOW (err_dialog)); + } + + g_clear_error (&error); + g_free (filename); + +out: + gtk_widget_hide (dialog); + gtk_widget_destroy (dialog); + g_free (info); +} + +static void +destroy_import_chooser (GtkWidget *dialog, gpointer user_data) +{ + g_free (user_data); + gtk_widget_destroy (dialog); +} + +void +vpn_import (VpnImportSuccessCallback callback, gpointer user_data) +{ + GtkWidget *dialog; + ActionInfo *info; + const char *home_folder; + + dialog = gtk_file_chooser_dialog_new (_("Select file to import"), + NULL, + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + home_folder = g_get_home_dir (); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder); + + info = g_malloc0 (sizeof (ActionInfo)); + info->callback = callback; + info->user_data = user_data; + + g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (destroy_import_chooser), info); + g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (import_vpn_from_file_cb), info); + gtk_widget_show_all (dialog); + gtk_window_present (GTK_WINDOW (dialog)); +} + +static void +export_vpn_to_file_cb (GtkWidget *dialog, gint response, gpointer user_data) +{ + NMConnection *connection = NM_CONNECTION (user_data); + char *filename = NULL; + GError *error = NULL; + NMVpnPluginUiInterface *plugin; + NMSettingConnection *s_con = NULL; + NMSettingVPN *s_vpn = NULL; + const char *service_type; + const char *id = NULL; + gboolean success = FALSE; + + if (response != GTK_RESPONSE_ACCEPT) + goto out; + + filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); + if (!filename) { + g_set_error (&error, NMA_ERROR, NMA_ERROR_GENERIC, "no filename"); + goto done; + } + + if (g_file_test (filename, G_FILE_TEST_EXISTS)) { + int replace_response; + GtkWidget *replace_dialog; + char *bname; + + bname = g_path_get_basename (filename); + replace_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_CANCEL, + _("A file named \"%s\" already exists."), + bname); + gtk_dialog_add_buttons (GTK_DIALOG (replace_dialog), _("_Replace"), GTK_RESPONSE_OK, NULL); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (replace_dialog), + _("Do you want to replace %s with the VPN connection you are saving?"), bname); + g_free (bname); + replace_response = gtk_dialog_run (GTK_DIALOG (replace_dialog)); + gtk_widget_destroy (replace_dialog); + if (replace_response != GTK_RESPONSE_OK) + goto out; + } + + s_con = nm_connection_get_setting_connection (connection); + id = s_con ? nm_setting_connection_get_id (s_con) : NULL; + if (!id) { + g_set_error (&error, NMA_ERROR, NMA_ERROR_GENERIC, "connection setting invalid"); + goto done; + } + + s_vpn = nm_connection_get_setting_vpn (connection); + service_type = s_vpn ? nm_setting_vpn_get_service_type (s_vpn) : NULL; + + if (!service_type) { + g_set_error (&error, NMA_ERROR, NMA_ERROR_GENERIC, "VPN setting invalid"); + goto done; + } + + plugin = vpn_get_plugin_by_service (service_type); + if (plugin) + success = nm_vpn_plugin_ui_interface_export (plugin, filename, connection, &error); + +done: + if (!success) { + GtkWidget *err_dialog; + char *bname = filename ? g_path_get_basename (filename) : g_strdup ("(none)"); + + err_dialog = gtk_message_dialog_new (NULL, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, + _("Cannot export VPN connection")); + gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (err_dialog), + _("The VPN connection '%s' could not be exported to %s.\n\nError: %s."), + id ? id : "(unknown)", bname, error ? error->message : "unknown error"); + g_free (bname); + g_signal_connect (err_dialog, "delete-event", G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (err_dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL); + gtk_widget_show_all (err_dialog); + gtk_window_present (GTK_WINDOW (err_dialog)); + } + +out: + if (error) + g_error_free (error); + g_object_unref (connection); + + gtk_widget_hide (dialog); + gtk_widget_destroy (dialog); +} + +void +vpn_export (NMConnection *connection) +{ + GtkWidget *dialog; + NMVpnPluginUiInterface *plugin; + NMSettingVPN *s_vpn = NULL; + const char *service_type; + const char *home_folder; + + s_vpn = nm_connection_get_setting_vpn (connection); + service_type = s_vpn ? nm_setting_vpn_get_service_type (s_vpn) : NULL; + + if (!service_type) { + g_warning ("%s: invalid VPN connection!", __func__); + return; + } + + dialog = gtk_file_chooser_dialog_new (_("Export VPN connection..."), + NULL, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + home_folder = g_get_home_dir (); + gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog), home_folder); + + plugin = vpn_get_plugin_by_service (service_type); + if (plugin) { + char *suggested = NULL; + + suggested = nm_vpn_plugin_ui_interface_get_suggested_name (plugin, connection); + if (suggested) { + gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), suggested); + g_free (suggested); + } + } + + g_signal_connect (G_OBJECT (dialog), "close", G_CALLBACK (gtk_widget_destroy), NULL); + g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (export_vpn_to_file_cb), g_object_ref (connection)); + gtk_widget_show_all (dialog); + gtk_window_present (GTK_WINDOW (dialog)); +} +#endif + +gboolean +vpn_supports_ipv6 (NMConnection *connection) +{ + NMSettingVPN *s_vpn; + const char *service_type; + NMVpnPluginUiInterface *plugin; + guint32 capabilities; + + s_vpn = nm_connection_get_setting_vpn (connection); + g_return_val_if_fail (s_vpn != NULL, FALSE); + + service_type = nm_setting_vpn_get_service_type (s_vpn); + g_return_val_if_fail (service_type != NULL, FALSE); + + plugin = vpn_get_plugin_by_service (service_type); + g_return_val_if_fail (plugin != NULL, FALSE); + + capabilities = nm_vpn_plugin_ui_interface_get_capabilities (plugin); + return (capabilities & NM_VPN_PLUGIN_UI_CAPABILITY_IPV6) != 0; +} diff --git a/tui/vpn-helpers.h b/tui/vpn-helpers.h new file mode 100644 index 0000000000..28019ca9c8 --- /dev/null +++ b/tui/vpn-helpers.h @@ -0,0 +1,39 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Copyright 2013 Red Hat, Inc. + */ + +#ifndef _VPN_HELPERS_H_ +#define _VPN_HELPERS_H_ + +#include <glib.h> +#include <nm-connection.h> + +#define NM_VPN_API_SUBJECT_TO_CHANGE +#include <nm-vpn-plugin-ui-interface.h> + +GHashTable *vpn_get_plugins (GError **error); + +NMVpnPluginUiInterface *vpn_get_plugin_by_service (const char *service); + +typedef void (*VpnImportSuccessCallback) (NMConnection *connection, gpointer user_data); +void vpn_import (VpnImportSuccessCallback callback, gpointer user_data); + +void vpn_export (NMConnection *connection); + +gboolean vpn_supports_ipv6 (NMConnection *connection); + +#endif /* _VPN_HELPERS_H_ */ |