diff options
Diffstat (limited to 'src/libnmt-newt')
43 files changed, 7773 insertions, 0 deletions
diff --git a/src/libnmt-newt/meson.build b/src/libnmt-newt/meson.build new file mode 100644 index 0000000000..9a07fd58b0 --- /dev/null +++ b/src/libnmt-newt/meson.build @@ -0,0 +1,32 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +libnmt_newt = static_library( + 'nmt-newt', + sources: files( + 'nmt-newt-button-box.c', + 'nmt-newt-button.c', + 'nmt-newt-checkbox.c', + 'nmt-newt-component.c', + 'nmt-newt-container.c', + 'nmt-newt-entry-numeric.c', + 'nmt-newt-entry.c', + 'nmt-newt-form.c', + 'nmt-newt-grid.c', + 'nmt-newt-hacks.c', + 'nmt-newt-label.c', + 'nmt-newt-listbox.c', + 'nmt-newt-popup.c', + 'nmt-newt-section.c', + 'nmt-newt-separator.c', + 'nmt-newt-stack.c', + 'nmt-newt-textbox.c', + 'nmt-newt-toggle-button.c', + 'nmt-newt-utils.c', + 'nmt-newt-widget.c', + ), + dependencies: [ + libnm_dep, + newt_dep, + glib_dep, + ], +) diff --git a/src/libnmt-newt/nmt-newt-button-box.c b/src/libnmt-newt/nmt-newt-button-box.c new file mode 100644 index 0000000000..d86a154cb2 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-button-box.c @@ -0,0 +1,358 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.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->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) + *width += 1; + else + *height += 1; +} + +static void +nmt_newt_button_box_size_allocate(NmtNewtWidget *widget, int x, int y, int width, int height) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE(widget); + NmtNewtWidget * child; + int child_x, child_y, child_width, child_height; + int i; + + child_x = x; + child_y = y; + for (i = 0; i < priv->start_buttons->len; i++) { + child = priv->start_buttons->pdata[i]; + nmt_newt_widget_size_request(child, &child_width, &child_height); + + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) { + nmt_newt_widget_size_allocate(child, child_x, child_y, child_width, child_height); + child_x += child_width + 1; + } else { + nmt_newt_widget_size_allocate(child, child_x, child_y, child_width, child_height); + child_y += child_height + 1; + } + } + + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) + child_x = x + width; + else + child_y = y + height; + + for (i = priv->end_buttons->len - 1; i >= 0; i--) { + child = priv->end_buttons->pdata[i]; + nmt_newt_widget_size_request(child, &child_width, &child_height); + + if (priv->orientation == NMT_NEWT_BUTTON_BOX_HORIZONTAL) { + nmt_newt_widget_size_allocate(child, + child_x - child_width, + child_y, + child_width, + child_height); + child_x -= child_width + 1; + } else { + nmt_newt_widget_size_allocate(child, + child_x, + child_y - child_height, + child_width, + child_height); + child_y -= child_height + 1; + } + } +} + +static void +nmt_newt_button_box_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_ORIENTATION: + priv->orientation = g_value_get_int(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_box_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtNewtButtonBoxPrivate *priv = NMT_NEWT_BUTTON_BOX_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_ORIENTATION: + g_value_set_int(value, priv->orientation); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_box_class_init(NmtNewtButtonBoxClass *bbox_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(bbox_class); + NmtNewtWidgetClass * widget_class = NMT_NEWT_WIDGET_CLASS(bbox_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS(bbox_class); + + g_type_class_add_private(bbox_class, sizeof(NmtNewtButtonBoxPrivate)); + + object_class->get_property = nmt_newt_button_box_get_property; + object_class->set_property = nmt_newt_button_box_set_property; + + widget_class->get_components = nmt_newt_button_box_get_components; + widget_class->size_request = nmt_newt_button_box_size_request; + widget_class->size_allocate = nmt_newt_button_box_size_allocate; + + container_class->remove = nmt_newt_button_box_remove; + + g_object_class_install_property( + object_class, + PROP_ORIENTATION, + g_param_spec_int("orientation", + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/libnmt-newt/nmt-newt-button-box.h b/src/libnmt-newt/nmt-newt-button-box.h new file mode 100644 index 0000000000..6d17778b4f --- /dev/null +++ b/src/libnmt-newt/nmt-newt-button-box.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_BUTTON_BOX_H +#define NMT_NEWT_BUTTON_BOX_H + +#include "nmt-newt-grid.h" + +#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); + +#endif /* NMT_NEWT_BUTTON_BOX_H */ diff --git a/src/libnmt-newt/nmt-newt-button.c b/src/libnmt-newt/nmt-newt-button.c new file mode 100644 index 0000000000..09ca1648a8 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-button.c @@ -0,0 +1,232 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-button + * @short_description: Push buttons + * + * #NmtNewtButton implements a button widget. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-newt-button.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE(NmtNewtButton, nmt_newt_button, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_BUTTON_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_NEWT_BUTTON, NmtNewtButtonPrivate)) + +typedef struct { + char *label; +} NmtNewtButtonPrivate; + +enum { + PROP_0, + PROP_LABEL, + + LAST_PROP +}; + +enum { + CLICKED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +/** + * nmt_newt_button_new: + * @label: the (initial) button label + * + * Creates a new button. + * + * Returns: a new #NmtNewtButton + */ +NmtNewtWidget * +nmt_newt_button_new(const char *label) +{ + return g_object_new(NMT_TYPE_NEWT_BUTTON, "label", label, NULL); +} + +/** + * nmt_newt_button_set_label: + * @button: an #NmtNewtButton + * @label: the new label + * + * Updates @button's label. + */ +void +nmt_newt_button_set_label(NmtNewtButton *button, const char *label) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE(button); + + if (!g_strcmp0(priv->label, label)) + return; + + g_free(priv->label); + priv->label = g_strdup(label); + nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(button)); +} + +/** + * nmt_newt_button_get_label: + * @button: an #NmtNewtButton + * + * Gets @button's label. + * + * Returns: @button's label. + */ +const char * +nmt_newt_button_get_label(NmtNewtButton *button) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE(button); + + return priv->label; +} + +static void +nmt_newt_button_init(NmtNewtButton *button) +{} + +static void +nmt_newt_button_finalize(GObject *object) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE(object); + + g_free(priv->label); + + G_OBJECT_CLASS(nmt_newt_button_parent_class)->finalize(object); +} + +static newtComponent +nmt_newt_button_build_component(NmtNewtComponent *component, gboolean sensitive) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE(component); + newtComponent co; + char * label = NULL, *label_lc; + + if (sensitive) { + label_lc = nmt_newt_locale_from_utf8(priv->label); + co = newtCompactButton(-1, -1, label_lc); + g_free(label_lc); + } else { + label = g_strdup_printf(" <%s>", priv->label); + label_lc = nmt_newt_locale_from_utf8(label); + co = newtLabel(-1, -1, label_lc); + g_free(label_lc); + newtLabelSetColors(co, NMT_NEWT_COLORSET_DISABLED_BUTTON); + } + + return co; +} + +static void +nmt_newt_button_size_request(NmtNewtWidget *widget, int *width, int *height) +{ + NMT_NEWT_WIDGET_CLASS(nmt_newt_button_parent_class)->size_request(widget, width, height); + + /* remove the automatically-added left padding */ + (*width)--; +} + +static void +nmt_newt_button_size_allocate(NmtNewtWidget *widget, int x, int y, int width, int height) +{ + /* account for the automatically-added left padding */ + x--; + width++; + + NMT_NEWT_WIDGET_CLASS(nmt_newt_button_parent_class)->size_allocate(widget, x, y, width, height); +} + +static void +nmt_newt_button_activated(NmtNewtWidget *widget) +{ + g_signal_emit(widget, signals[CLICKED], 0); + + NMT_NEWT_WIDGET_CLASS(nmt_newt_button_parent_class)->activated(widget); +} + +static void +nmt_newt_button_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + switch (prop_id) { + case PROP_LABEL: + nmt_newt_button_set_label(NMT_NEWT_BUTTON(object), g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtNewtButtonPrivate *priv = NMT_NEWT_BUTTON_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_LABEL: + g_value_set_string(value, priv->label); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_button_class_init(NmtNewtButtonClass *button_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(button_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS(button_class); + NmtNewtWidgetClass * widget_class = NMT_NEWT_WIDGET_CLASS(button_class); + + g_type_class_add_private(button_class, sizeof(NmtNewtButtonPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_button_set_property; + object_class->get_property = nmt_newt_button_get_property; + object_class->finalize = nmt_newt_button_finalize; + + widget_class->size_request = nmt_newt_button_size_request; + widget_class->size_allocate = nmt_newt_button_size_allocate; + widget_class->activated = nmt_newt_button_activated; + + component_class->build_component = nmt_newt_button_build_component; + + /* signals */ + + /** + * NmtNewtButton::clicked: + * @button: the #NmtNewtButton + * + * Emitted when the button is clicked. + */ + signals[CLICKED] = g_signal_new("clicked", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + + /* properties */ + + /** + * NmtNewtButton:label: + * + * The button's label + */ + g_object_class_install_property( + object_class, + PROP_LABEL, + g_param_spec_string("label", "", "", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/libnmt-newt/nmt-newt-button.h b/src/libnmt-newt/nmt-newt-button.h new file mode 100644 index 0000000000..12649600f6 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-button.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_BUTTON_H +#define NMT_NEWT_BUTTON_H + +#include "nmt-newt-component.h" + +#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); + +#endif /* NMT_NEWT_BUTTON_H */ diff --git a/src/libnmt-newt/nmt-newt-checkbox.c b/src/libnmt-newt/nmt-newt-checkbox.c new file mode 100644 index 0000000000..cf825e08d9 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-checkbox.c @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-checkbox + * @short_description: Checkboxes + * + * #NmtNewtCheckbox implements a checkbox widget. + */ + +#include "libnm-client-aux-extern/nm-default-client.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/src/libnmt-newt/nmt-newt-checkbox.h b/src/libnmt-newt/nmt-newt-checkbox.h new file mode 100644 index 0000000000..c33c086a8f --- /dev/null +++ b/src/libnmt-newt/nmt-newt-checkbox.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_CHECKBOX_H +#define NMT_NEWT_CHECKBOX_H + +#include "nmt-newt-component.h" + +#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); + +#endif /* NMT_NEWT_CHECKBOX_H */ diff --git a/src/libnmt-newt/nmt-newt-component.c b/src/libnmt-newt/nmt-newt-component.c new file mode 100644 index 0000000000..8d7a9005c9 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-component.c @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.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/src/libnmt-newt/nmt-newt-component.h b/src/libnmt-newt/nmt-newt-component.h new file mode 100644 index 0000000000..54164c54ef --- /dev/null +++ b/src/libnmt-newt/nmt-newt-component.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_COMPONENT_H +#define NMT_NEWT_COMPONENT_H + +#include "nmt-newt-widget.h" + +#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); + +#endif /* NMT_NEWT_COMPONENT_H */ diff --git a/src/libnmt-newt/nmt-newt-container.c b/src/libnmt-newt/nmt-newt-container.c new file mode 100644 index 0000000000..4cc8db242a --- /dev/null +++ b/src/libnmt-newt/nmt-newt-container.c @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.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/src/libnmt-newt/nmt-newt-container.h b/src/libnmt-newt/nmt-newt-container.h new file mode 100644 index 0000000000..0258529418 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-container.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_CONTAINER_H +#define NMT_NEWT_CONTAINER_H + +#include "nmt-newt-widget.h" + +#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); + +#endif /* NMT_NEWT_CONTAINER_H */ diff --git a/src/libnmt-newt/nmt-newt-entry-numeric.c b/src/libnmt-newt/nmt-newt-entry-numeric.c new file mode 100644 index 0000000000..7e8edd6cad --- /dev/null +++ b/src/libnmt-newt/nmt-newt-entry-numeric.c @@ -0,0 +1,244 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.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 { + gint64 min, max; + bool optional; +} NmtNewtEntryNumericPrivate; + +enum { + PROP_0, + PROP_MINIMUM, + PROP_MAXIMUM, + PROP_OPTIONAL, + + LAST_PROP +}; + +/** + * nmt_newt_entry_numeric_new: + * @width: the entry's width in characters + * @min: the minimum valid value + * @max: the maximum valid value + * + * Creates a new #NmtNewtEntryNumeric, accepting values in the + * indicated range. + * + * Returns: a new #NmtNewtEntryNumeric + */ +NmtNewtWidget * +nmt_newt_entry_numeric_new(int width, gint64 min, gint64 max) +{ + return nmt_newt_entry_numeric_new_full(width, min, max, FALSE); +} + +/** + * nmt_newt_entry_numeric_new_full: + * @width: the entry's width in characters + * @min: the minimum valid value + * @max: the maximum valid value + * @optional: whether an empty entry is valid + * + * Creates a new #NmtNewtEntryNumeric, accepting values in the + * indicated range. + * + * Returns: a new #NmtNewtEntryNumeric + */ +NmtNewtWidget * +nmt_newt_entry_numeric_new_full(int width, gint64 min, gint64 max, gboolean optional) +{ + return g_object_new(NMT_TYPE_NEWT_ENTRY_NUMERIC, + "width", + width, + "minimum", + min, + "maximum", + max, + "optional", + optional, + 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); + gint64 val; + + if (!*text) + return priv->optional ? TRUE : FALSE; + + val = _nm_utils_ascii_str_to_int64(text, 10, priv->min, priv->max, G_MAXINT64); + return val != G_MAXINT64 || errno == 0; +} + +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), "%lld", (long long) 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_int64(value); + break; + case PROP_MAXIMUM: + priv->max = g_value_get_int64(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_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_int64(value, priv->min); + break; + case PROP_MAXIMUM: + g_value_set_int64(value, priv->max); + 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_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_int64("minimum", + "", + "", + G_MININT64, + G_MAXINT64, + 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_int64("maximum", + "", + "", + G_MININT64, + G_MAXINT64, + G_MAXINT64, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtEntryNumeric:optional: + * + * If %TRUE, allow empty string to indicate some default value. + * It means the property is optional and can be left at the default + */ + 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/src/libnmt-newt/nmt-newt-entry-numeric.h b/src/libnmt-newt/nmt-newt-entry-numeric.h new file mode 100644 index 0000000000..a10e4c5b22 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-entry-numeric.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_ENTRY_NUMERIC_H +#define NMT_NEWT_ENTRY_NUMERIC_H + +#include "nmt-newt-entry.h" + +#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, gint64 min, gint64 max); + +NmtNewtWidget * +nmt_newt_entry_numeric_new_full(int width, gint64 min, gint64 max, gboolean optional); + +#endif /* NMT_NEWT_ENTRY_NUMERIC_H */ diff --git a/src/libnmt-newt/nmt-newt-entry.c b/src/libnmt-newt/nmt-newt-entry.c new file mode 100644 index 0000000000..76a96df2f2 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-entry.c @@ -0,0 +1,506 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.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/src/libnmt-newt/nmt-newt-entry.h b/src/libnmt-newt/nmt-newt-entry.h new file mode 100644 index 0000000000..da1a9aeee8 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-entry.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_ENTRY_H +#define NMT_NEWT_ENTRY_H + +#include "nmt-newt-component.h" + +#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); + +#endif /* NMT_NEWT_ENTRY_H */ diff --git a/src/libnmt-newt/nmt-newt-form.c b/src/libnmt-newt/nmt-newt-form.c new file mode 100644 index 0000000000..825f7cfe61 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-form.c @@ -0,0 +1,739 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.h" + +#include <fcntl.h> +#include <unistd.h> + +#include "nmt-newt-form.h" +#include "nmt-newt-button.h" +#include "nmt-newt-grid.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE(NmtNewtForm, nmt_newt_form, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_FORM_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_NEWT_FORM, NmtNewtFormPrivate)) + +typedef struct { + newtComponent form; + NmtNewtWidget *content; + + guint x, y, width, height; + guint padding; + gboolean fixed_x, fixed_y; + gboolean fixed_width, fixed_height; + char * title_lc; + + gboolean dirty, escape_exits; + NmtNewtWidget *focus; +#ifdef HAVE_NEWTFORMGETSCROLLPOSITION + int scroll_position = 0; +#endif +} NmtNewtFormPrivate; + +enum { + PROP_0, + PROP_TITLE, + PROP_FULLSCREEN, + PROP_FULLSCREEN_VERTICAL, + PROP_FULLSCREEN_HORIZONTAL, + PROP_X, + PROP_Y, + PROP_WIDTH, + PROP_HEIGHT, + PROP_PADDING, + PROP_ESCAPE_EXITS, + + LAST_PROP +}; + +enum { + QUIT, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +static void nmt_newt_form_redraw(NmtNewtForm *form); + +/** + * nmt_newt_form_new: + * @title: (allow-none): the form title + * + * Creates a new form, which will be shown centered on the screen. + * Compare nmt_newt_form_new_fullscreen(). You can also position a + * form manually by setting its #NmtNewtForm:x and #NmtNewtForm:y + * properties at construct time, and/or by setting + * #NmtNewtForm:fullscreen, #NmtNewtform:fullscreen-horizontal, or + * #NmtNewtForm:fullscreen-vertical. + * + * If @title is NULL, the form will have no title. + * + * Returns: a new #NmtNewtForm + */ +NmtNewtForm * +nmt_newt_form_new(const char *title) +{ + return g_object_new(NMT_TYPE_NEWT_FORM, "title", title, NULL); +} + +/** + * nmt_newt_form_new_fullscreen: + * @title: (allow-none): the form title + * + * Creates a new fullscreen form. Compare nmt_newt_form_new(). + * + * If @title is NULL, the form will have no title. + * + * Returns: a new #NmtNewtForm + */ +NmtNewtForm * +nmt_newt_form_new_fullscreen(const char *title) +{ + return g_object_new(NMT_TYPE_NEWT_FORM, "title", title, "fullscreen", TRUE, NULL); +} + +static void +nmt_newt_form_init(NmtNewtForm *form) +{ + g_object_ref_sink(form); +} + +static void +nmt_newt_form_finalize(GObject *object) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(object); + + g_free(priv->title_lc); + g_clear_object(&priv->focus); + + G_OBJECT_CLASS(nmt_newt_form_parent_class)->finalize(object); +} + +static void +nmt_newt_form_needs_rebuild(NmtNewtWidget *widget) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(widget); + + if (!priv->dirty) { + priv->dirty = TRUE; + nmt_newt_form_redraw(NMT_NEWT_FORM(widget)); + } +} + +static void +nmt_newt_form_remove(NmtNewtContainer *container, NmtNewtWidget *widget) +{ + NmtNewtFormPrivate * priv = NMT_NEWT_FORM_GET_PRIVATE(container); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS(nmt_newt_form_parent_class); + + g_return_if_fail(widget == priv->content); + + parent_class->remove(container, widget); + priv->content = NULL; +} + +/** + * nmt_newt_form_set_content: + * @form: the #NmtNewtForm + * @content: the form's content + * + * Sets @form's content to be @content. + */ +void +nmt_newt_form_set_content(NmtNewtForm *form, NmtNewtWidget *content) +{ + NmtNewtFormPrivate * priv = NMT_NEWT_FORM_GET_PRIVATE(form); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS(nmt_newt_form_parent_class); + + if (priv->content) + nmt_newt_form_remove(NMT_NEWT_CONTAINER(form), priv->content); + + priv->content = content; + + if (priv->content) + parent_class->add(NMT_NEWT_CONTAINER(form), content); +} + +static void +nmt_newt_form_build(NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(form); + int screen_height, screen_width, form_height, form_width; + newtComponent * cos; + int i; + + priv->dirty = FALSE; + nmt_newt_widget_realize(NMT_NEWT_WIDGET(form)); + + nmt_newt_widget_size_request(priv->content, &form_width, &form_height); + newtGetScreenSize(&screen_width, &screen_height); + + if (!priv->fixed_width) + priv->width = MIN(form_width + 2 * priv->padding, screen_width - 2); + if (!priv->fixed_height) + priv->height = MIN(form_height + 2 * priv->padding, screen_height - 2); + + if (!priv->fixed_x) + priv->x = (screen_width - form_width) / 2; + if (!priv->fixed_y) + priv->y = (screen_height - form_height) / 2; + + nmt_newt_widget_size_allocate(priv->content, + priv->padding, + priv->padding, + priv->width - 2 * priv->padding, + priv->height - 2 * priv->padding); + + if (priv->height - 2 * priv->padding < form_height) { + newtComponent scroll_bar = newtVerticalScrollbar(priv->width - 1, + 0, + priv->height, + NEWT_COLORSET_WINDOW, + NEWT_COLORSET_ACTCHECKBOX); + + priv->form = newtForm(scroll_bar, NULL, NEWT_FLAG_NOF12); + newtFormAddComponent(priv->form, scroll_bar); + newtFormSetHeight(priv->form, priv->height - 2); + } else + priv->form = newtForm(NULL, NULL, NEWT_FLAG_NOF12); + + if (priv->escape_exits) + newtFormAddHotKey(priv->form, NEWT_KEY_ESCAPE); + + cos = nmt_newt_widget_get_components(priv->content); + for (i = 0; cos[i]; i++) + newtFormAddComponent(priv->form, cos[i]); + g_free(cos); + + if (priv->focus) { + newtComponent fco; + + fco = nmt_newt_widget_get_focus_component(priv->focus); + if (fco) + newtFormSetCurrent(priv->form, fco); + } +#ifdef HAVE_NEWTFORMGETSCROLLPOSITION + if (priv->scroll_position) + newtFormSetScrollPosition(priv->form, priv->scroll_position); +#endif + + newtOpenWindow(priv->x, priv->y, priv->width, priv->height, priv->title_lc); +} + +static void +nmt_newt_form_destroy(NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(form); + +#ifdef HAVE_NEWTFORMGETSCROLLPOSITION + priv->scroll_position = newtFormGetScrollPosition(priv->form); +#endif + + newtFormDestroy(priv->form); + priv->form = NULL; + newtPopWindowNoRefresh(); + + nmt_newt_widget_unrealize(NMT_NEWT_WIDGET(form)); +} + +/* A "normal" newt program would call newtFormRun() to run newt's main loop + * and process events. But we want to let GLib's main loop control the program + * (eg, so libnm can process D-Bus notifications). So we call this function + * to run a single iteration of newt's main loop (or rather, to run newt's + * main loop for 1ms) whenever there are events for newt to process (redrawing + * or keypresses). + */ +static void +nmt_newt_form_iterate(NmtNewtForm *form) +{ + NmtNewtFormPrivate * priv = NMT_NEWT_FORM_GET_PRIVATE(form); + NmtNewtWidget * focus; + struct newtExitStruct es; + + if (priv->dirty) { + nmt_newt_form_destroy(form); + nmt_newt_form_build(form); + } + + newtFormSetTimer(priv->form, 1); + newtFormRun(priv->form, &es); + + if (es.reason == NEWT_EXIT_HOTKEY || es.reason == NEWT_EXIT_ERROR) { + /* The user hit Esc or there was an error. */ + g_clear_object(&priv->focus); + nmt_newt_form_quit(form); + return; + } + + if (es.reason == NEWT_EXIT_COMPONENT) { + /* The user hit Return/Space on a component; update the form focus + * to point that component, and activate it. + */ + focus = nmt_newt_widget_find_component(priv->content, es.u.co); + if (focus) { + nmt_newt_form_set_focus(form, focus); + nmt_newt_widget_activated(focus); + } + } else { + /* The 1ms timer ran out. Update focus but don't do anything else. */ + focus = nmt_newt_widget_find_component(priv->content, newtFormGetCurrent(priv->form)); + if (focus) + nmt_newt_form_set_focus(form, focus); + } +} + +/* @form_stack keeps track of all currently-displayed forms, from top to bottom. + * @keypress_source is the global stdin-monitoring GSource. When it triggers, + * nmt_newt_form_keypress_callback() iterates the top-most form, so it can + * process the keypress. + */ +static GSList * form_stack; +static GSource *keypress_source; + +static gboolean +nmt_newt_form_keypress_callback(int fd, GIOCondition condition, gpointer user_data) +{ + g_return_val_if_fail(form_stack != NULL, FALSE); + + nmt_newt_form_iterate(form_stack->data); + return TRUE; +} + +static gboolean +nmt_newt_form_timeout_callback(gpointer user_data) +{ + if (form_stack) + nmt_newt_form_iterate(form_stack->data); + return FALSE; +} + +static void +nmt_newt_form_redraw(NmtNewtForm *form) +{ + g_timeout_add(0, nmt_newt_form_timeout_callback, NULL); +} + +static void +nmt_newt_form_real_show(NmtNewtForm *form) +{ + if (!keypress_source) { + keypress_source = nm_g_unix_fd_source_new(STDIN_FILENO, + G_IO_IN, + G_PRIORITY_DEFAULT, + nmt_newt_form_keypress_callback, + NULL, + NULL); + g_source_set_can_recurse(keypress_source, TRUE); + g_source_attach(keypress_source, NULL); + } + + nmt_newt_form_build(form); + form_stack = g_slist_prepend(form_stack, g_object_ref(form)); + nmt_newt_form_redraw(form); +} + +/** + * nmt_newt_form_show: + * @form: an #NmtNewtForm + * + * Displays @form and begins running it asynchronously in the default + * #GMainContext. If another form is currently running, it will remain + * visible in the background, but will not be able to receive keyboard + * input until @form exits. + * + * Call nmt_newt_form_quit() to quit the form. + */ +void +nmt_newt_form_show(NmtNewtForm *form) +{ + NMT_NEWT_FORM_GET_CLASS(form)->show(form); +} + +/** + * nmt_newt_form_run_sync: + * @form: an #NmtNewtForm + * + * Displays @form as with nmt_newt_form_show(), but then iterates the + * #GMainContext internally until @form exits. + * + * Returns: the widget whose activation caused @form to exit, or + * %NULL if it was not caused by a widget. FIXME: this exit value is + * sort of weird and may not be 100% accurate anyway. + */ +NmtNewtWidget * +nmt_newt_form_run_sync(NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(form); + + nmt_newt_form_show(form); + while (priv->form) + g_main_context_iteration(NULL, TRUE); + + return priv->focus; +} + +/** + * nmt_newt_form_quit: + * @form: an #NmtNewtForm + * + * Causes @form to exit. + */ +void +nmt_newt_form_quit(NmtNewtForm *form) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(form); + + g_return_if_fail(priv->form != NULL); + + nmt_newt_form_destroy(form); + + form_stack = g_slist_remove(form_stack, form); + + if (form_stack) + nmt_newt_form_iterate(form_stack->data); + else + nm_clear_g_source_inst(&keypress_source); + + g_signal_emit(form, signals[QUIT], 0); + g_object_unref(form); +} + +/** + * nmt_newt_form_set_focus: + * @form: an #NmtNewtForm + * @widget: the widget to focus + * + * Focuses @widget in @form. + */ +void +nmt_newt_form_set_focus(NmtNewtForm *form, NmtNewtWidget *widget) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(form); + + g_return_if_fail(priv->form != NULL); + + if (priv->focus == widget) + return; + + if (priv->focus) + g_object_unref(priv->focus); + priv->focus = widget; + if (priv->focus) + g_object_ref(priv->focus); +} + +static void +nmt_newt_form_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(object); + int screen_width, screen_height; + + switch (prop_id) { + case PROP_TITLE: + if (g_value_get_string(value)) { + priv->title_lc = nmt_newt_locale_from_utf8(g_value_get_string(value)); + } else + priv->title_lc = NULL; + break; + case PROP_FULLSCREEN: + if (g_value_get_boolean(value)) { + newtGetScreenSize(&screen_width, &screen_height); + priv->x = priv->y = 2; + priv->fixed_x = priv->fixed_y = TRUE; + priv->width = screen_width - 4; + priv->height = screen_height - 4; + priv->fixed_width = priv->fixed_height = TRUE; + } + break; + case PROP_FULLSCREEN_VERTICAL: + if (g_value_get_boolean(value)) { + newtGetScreenSize(&screen_width, &screen_height); + priv->y = 2; + priv->fixed_y = TRUE; + priv->height = screen_height - 4; + priv->fixed_height = TRUE; + } + break; + case PROP_FULLSCREEN_HORIZONTAL: + if (g_value_get_boolean(value)) { + newtGetScreenSize(&screen_width, &screen_height); + priv->x = 2; + priv->fixed_x = TRUE; + priv->width = screen_width - 4; + priv->fixed_width = TRUE; + } + break; + case PROP_X: + if (g_value_get_uint(value)) { + priv->x = g_value_get_uint(value); + priv->fixed_x = TRUE; + } + break; + case PROP_Y: + if (g_value_get_uint(value)) { + priv->y = g_value_get_uint(value); + priv->fixed_y = TRUE; + } + break; + case PROP_WIDTH: + if (g_value_get_uint(value)) { + priv->width = g_value_get_uint(value); + priv->fixed_width = TRUE; + } + break; + case PROP_HEIGHT: + if (g_value_get_uint(value)) { + priv->height = g_value_get_uint(value); + priv->fixed_height = TRUE; + } + break; + case PROP_PADDING: + priv->padding = g_value_get_uint(value); + break; + case PROP_ESCAPE_EXITS: + priv->escape_exits = g_value_get_boolean(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_form_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtNewtFormPrivate *priv = NMT_NEWT_FORM_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_TITLE: + if (priv->title_lc) { + g_value_take_string(value, nmt_newt_locale_to_utf8(priv->title_lc)); + } else + g_value_set_string(value, NULL); + break; + case PROP_X: + g_value_set_uint(value, priv->x); + break; + case PROP_Y: + g_value_set_uint(value, priv->y); + break; + case PROP_WIDTH: + g_value_set_uint(value, priv->width); + break; + case PROP_HEIGHT: + g_value_set_uint(value, priv->height); + break; + case PROP_PADDING: + g_value_set_uint(value, priv->padding); + break; + case PROP_ESCAPE_EXITS: + g_value_set_boolean(value, priv->escape_exits); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_form_class_init(NmtNewtFormClass *form_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(form_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS(form_class); + NmtNewtWidgetClass * widget_class = NMT_NEWT_WIDGET_CLASS(form_class); + + g_type_class_add_private(form_class, sizeof(NmtNewtFormPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_form_set_property; + object_class->get_property = nmt_newt_form_get_property; + object_class->finalize = nmt_newt_form_finalize; + + widget_class->needs_rebuild = nmt_newt_form_needs_rebuild; + + container_class->remove = nmt_newt_form_remove; + + form_class->show = nmt_newt_form_real_show; + + /* signals */ + + /** + * NmtNewtForm::quit: + * @form: the #NmtNewtForm + * + * Emitted when the form quits. + */ + signals[QUIT] = g_signal_new("quit", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NmtNewtFormClass, quit), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + + /** + * NmtNewtForm:title: + * + * The form's title. If non-%NULL, this will be displayed above + * the form in its border. + */ + g_object_class_install_property( + object_class, + PROP_TITLE, + g_param_spec_string("title", + "", + "", + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:fullscreen: + * + * If %TRUE, the form will fill the entire "screen" (ie, terminal + * window). + */ + g_object_class_install_property( + object_class, + PROP_FULLSCREEN, + g_param_spec_boolean("fullscreen", + "", + "", + FALSE, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:fullscreen-vertical: + * + * If %TRUE, the form will fill the entire "screen" (ie, terminal + * window) vertically, but not necessarily horizontally. + */ + g_object_class_install_property( + object_class, + PROP_FULLSCREEN_VERTICAL, + g_param_spec_boolean("fullscreen-vertical", + "", + "", + FALSE, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:fullscreen-horizontal: + * + * If %TRUE, the form will fill the entire "screen" (ie, terminal + * window) horizontally, but not necessarily vertically. + */ + g_object_class_install_property( + object_class, + PROP_FULLSCREEN_HORIZONTAL, + g_param_spec_boolean("fullscreen-horizontal", + "", + "", + FALSE, + G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:x: + * + * The form's x coordinate. By default, the form will be centered + * on the screen. + */ + g_object_class_install_property( + object_class, + PROP_X, + g_param_spec_uint("x", + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:y: + * + * The form's y coordinate. By default, the form will be centered + * on the screen. + */ + g_object_class_install_property( + object_class, + PROP_Y, + g_param_spec_uint("y", + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:width: + * + * The form's width. By default, this will be determined by the + * width of the form's content. + */ + g_object_class_install_property( + object_class, + PROP_WIDTH, + g_param_spec_uint("width", + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:height: + * + * The form's height. By default, this will be determined by the + * height of the form's content. + */ + g_object_class_install_property( + object_class, + PROP_HEIGHT, + g_param_spec_uint("height", + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:padding: + * + * The padding between the form's content and its border. + */ + g_object_class_install_property( + object_class, + PROP_PADDING, + g_param_spec_uint("padding", + "", + "", + 0, + G_MAXUINT, + 1, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); + /** + * NmtNewtForm:escape-exits: + * + * If %TRUE, then hitting the Escape key will cause the form to + * exit. + */ + g_object_class_install_property( + object_class, + PROP_ESCAPE_EXITS, + g_param_spec_boolean("escape-exits", + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY)); +} diff --git a/src/libnmt-newt/nmt-newt-form.h b/src/libnmt-newt/nmt-newt-form.h new file mode 100644 index 0000000000..55fb583b76 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-form.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_FORM_H +#define NMT_NEWT_FORM_H + +#include "nmt-newt-container.h" + +#define NMT_TYPE_NEWT_FORM (nmt_newt_form_get_type()) +#define NMT_NEWT_FORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_FORM, NmtNewtForm)) +#define NMT_NEWT_FORM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_FORM, NmtNewtFormClass)) +#define NMT_IS_NEWT_FORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_FORM)) +#define NMT_IS_NEWT_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_NEWT_FORM)) +#define NMT_NEWT_FORM_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_NEWT_FORM, NmtNewtFormClass)) + +struct _NmtNewtForm { + NmtNewtContainer parent; +}; + +typedef struct { + NmtNewtContainerClass parent; + + /* signals */ + void (*quit)(NmtNewtForm *form); + + /* methods */ + void (*show)(NmtNewtForm *form); + +} NmtNewtFormClass; + +GType nmt_newt_form_get_type(void); + +NmtNewtForm *nmt_newt_form_new(const char *title); +NmtNewtForm *nmt_newt_form_new_fullscreen(const char *title); + +void nmt_newt_form_set_content(NmtNewtForm *form, NmtNewtWidget *content); + +void nmt_newt_form_show(NmtNewtForm *form); +NmtNewtWidget *nmt_newt_form_run_sync(NmtNewtForm *form); +void nmt_newt_form_quit(NmtNewtForm *form); + +void nmt_newt_form_set_focus(NmtNewtForm *form, NmtNewtWidget *widget); + +#endif /* NMT_NEWT_FORM_H */ diff --git a/src/libnmt-newt/nmt-newt-grid.c b/src/libnmt-newt/nmt-newt-grid.c new file mode 100644 index 0000000000..a68a496bce --- /dev/null +++ b/src/libnmt-newt/nmt-newt-grid.c @@ -0,0 +1,439 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.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); + nm_clear_g_free(&priv->row_heights); + nm_clear_g_free(&priv->col_widths); + nm_clear_g_free(&priv->expand_rows); + nm_clear_g_free(&priv->expand_cols); + + 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/src/libnmt-newt/nmt-newt-grid.h b/src/libnmt-newt/nmt-newt-grid.h new file mode 100644 index 0000000000..7aac18bc52 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-grid.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_GRID_H +#define NMT_NEWT_GRID_H + +#include "nmt-newt-container.h" + +#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); + +#endif /* NMT_NEWT_GRID_H */ diff --git a/src/libnmt-newt/nmt-newt-hacks.c b/src/libnmt-newt/nmt-newt-hacks.c new file mode 100644 index 0000000000..40114a29a7 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-hacks.c @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.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/src/libnmt-newt/nmt-newt-hacks.h b/src/libnmt-newt/nmt-newt-hacks.h new file mode 100644 index 0000000000..92b376f960 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-hacks.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_HACKS_H +#define NMT_NEWT_HACKS_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/src/libnmt-newt/nmt-newt-label.c b/src/libnmt-newt/nmt-newt-label.c new file mode 100644 index 0000000000..32c7809d4d --- /dev/null +++ b/src/libnmt-newt/nmt-newt-label.c @@ -0,0 +1,303 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-label + * @short_description: Labels + * + * #NmtNewtLabel implements a single-line label. + * + * See also #NmtNewtTextbox, for multiline. + */ + +#include "libnm-client-aux-extern/nm-default-client.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/src/libnmt-newt/nmt-newt-label.h b/src/libnmt-newt/nmt-newt-label.h new file mode 100644 index 0000000000..ff2e471bff --- /dev/null +++ b/src/libnmt-newt/nmt-newt-label.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_LABEL_H +#define NMT_NEWT_LABEL_H + +#include "nmt-newt-component.h" + +#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); + +#endif /* NMT_NEWT_LABEL_H */ diff --git a/src/libnmt-newt/nmt-newt-listbox.c b/src/libnmt-newt/nmt-newt-listbox.c new file mode 100644 index 0000000000..cfe23647a1 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-listbox.c @@ -0,0 +1,518 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-newt-listbox.h" +#include "nmt-newt-form.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE(NmtNewtListbox, nmt_newt_listbox, NMT_TYPE_NEWT_COMPONENT) + +#define NMT_NEWT_LISTBOX_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxPrivate)) + +typedef struct { + int height, alloc_height, width; + gboolean fixed_height; + NmtNewtListboxFlags flags; + + GPtrArray *entries; + GPtrArray *keys; + + int active; + gpointer active_key; + gboolean skip_null_keys; + +} NmtNewtListboxPrivate; + +enum { + PROP_0, + PROP_HEIGHT, + PROP_FLAGS, + PROP_ACTIVE, + PROP_ACTIVE_KEY, + PROP_SKIP_NULL_KEYS, + + LAST_PROP +}; + +/** + * NmtNewtListboxFlags: + * @NMT_NEWT_LISTBOX_SCROLL: the listbox should have a scroll bar. + * @NMT_NEWT_LISTBOX_BORDER: the listbox should have a border around it. + * + * Flags describing an #NmtNewtListbox + */ + +/** + * nmt_newt_listbox_new: + * @height: the height of the listbox, or -1 for no fixed height + * @flags: the listbox flags + * + * Creates a new #NmtNewtListbox + * + * Returns: a new #NmtNewtListbox + */ +NmtNewtWidget * +nmt_newt_listbox_new(int height, NmtNewtListboxFlags flags) +{ + return g_object_new(NMT_TYPE_NEWT_LISTBOX, "height", height, "flags", flags, NULL); +} + +/** + * nmt_newt_listbox_append: + * @listbox: an #NmtNewtListbox + * @entry: the text for the new row + * @key: (allow-none): the key associated with @entry + * + * Adds a row to @listbox. + */ +void +nmt_newt_listbox_append(NmtNewtListbox *listbox, const char *entry, gpointer key) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE(listbox); + + g_ptr_array_add(priv->entries, nmt_newt_locale_from_utf8(entry)); + g_ptr_array_add(priv->keys, key); + nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(listbox)); +} + +/** + * nmt_newt_listbox_clear: + * @listbox: an #NmtNewtListbox + * + * Clears the contents of @listbox. + */ +void +nmt_newt_listbox_clear(NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE(listbox); + + g_ptr_array_set_size(priv->entries, 0); + g_ptr_array_set_size(priv->keys, 0); + + priv->active = -1; + priv->active_key = NULL; + + nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(listbox)); +} + +/** + * nmt_newt_listbox_set_active: + * @listbox: an #NmtNewtListbox + * @active: the row to make active + * + * Sets @active to be the currently-selected row in @listbox, + * scrolling it into view if needed. + */ +void +nmt_newt_listbox_set_active(NmtNewtListbox *listbox, int active) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE(listbox); + + if (active == priv->active) + return; + + g_return_if_fail(active >= 0 && active < priv->entries->len); + g_return_if_fail(!priv->skip_null_keys || priv->keys->pdata[active]); + + priv->active = active; + priv->active_key = priv->keys->pdata[active]; + + g_object_notify(G_OBJECT(listbox), "active"); + g_object_notify(G_OBJECT(listbox), "active-key"); +} + +/** + * nmt_newt_listbox_set_active_key: + * @listbox: an #NmtNewtListbox + * @active_key: the key for the row to make active + * + * Selects the (first) row in @listbox with @active_key as its key, + * scrolling it into view if needed. + */ +void +nmt_newt_listbox_set_active_key(NmtNewtListbox *listbox, gpointer active_key) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE(listbox); + int i; + + if (active_key == priv->active_key) + return; + + g_return_if_fail(!priv->skip_null_keys || active_key); + + for (i = 0; i < priv->keys->len; i++) { + if (priv->keys->pdata[i] == active_key) { + priv->active = i; + priv->active_key = active_key; + + g_object_notify(G_OBJECT(listbox), "active"); + g_object_notify(G_OBJECT(listbox), "active-key"); + return; + } + } +} + +/** + * nmt_newt_listbox_get_active: + * @listbox: an #NmtNewtListbox + * + * Gets the currently-selected row in @listbox. + * + * Returns: the currently-selected row in @listbox. + */ +int +nmt_newt_listbox_get_active(NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE(listbox); + + return priv->active; +} + +/** + * nmt_newt_listbox_get_active_key: + * @listbox: an #NmtNewtListbox + * + * Gets the key of the currently-selected row in @listbox. + * + * Returns: the key of the currently-selected row in @listbox. + */ +gpointer +nmt_newt_listbox_get_active_key(NmtNewtListbox *listbox) +{ + NmtNewtListboxPrivate *priv = NMT_NEWT_LISTBOX_GET_PRIVATE(listbox); + + return priv->active_key; +} + +/** + * nmt_newt_listbox_set_height: + * @listbox: an #NmtNewtListbox + * @height: the new height, or -1 for no fixed height + * + * 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/src/libnmt-newt/nmt-newt-listbox.h b/src/libnmt-newt/nmt-newt-listbox.h new file mode 100644 index 0000000000..602e33807a --- /dev/null +++ b/src/libnmt-newt/nmt-newt-listbox.h @@ -0,0 +1,50 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_LISTBOX_H +#define NMT_NEWT_LISTBOX_H + +#include "nmt-newt-component.h" + +#define NMT_TYPE_NEWT_LISTBOX (nmt_newt_listbox_get_type()) +#define NMT_NEWT_LISTBOX(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListbox)) +#define NMT_NEWT_LISTBOX_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass)) +#define NMT_IS_NEWT_LISTBOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_LISTBOX)) +#define NMT_IS_NEWT_LISTBOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_NEWT_LISTBOX)) +#define NMT_NEWT_LISTBOX_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_NEWT_LISTBOX, NmtNewtListboxClass)) + +struct _NmtNewtListbox { + NmtNewtComponent parent; +}; + +typedef struct { + NmtNewtComponentClass parent; + +} NmtNewtListboxClass; + +GType nmt_newt_listbox_get_type(void); + +typedef enum { + NMT_NEWT_LISTBOX_SCROLL = (1 << 0), + NMT_NEWT_LISTBOX_BORDER = (1 << 1) +} NmtNewtListboxFlags; + +NmtNewtWidget *nmt_newt_listbox_new(int height, NmtNewtListboxFlags flags); + +void nmt_newt_listbox_set_height(NmtNewtListbox *listbox, int height); + +void nmt_newt_listbox_append(NmtNewtListbox *listbox, const char *entry, gpointer key); +void nmt_newt_listbox_clear(NmtNewtListbox *listbox); + +void nmt_newt_listbox_set_active(NmtNewtListbox *listbox, int active); +void nmt_newt_listbox_set_active_key(NmtNewtListbox *listbox, gpointer active_key); + +int nmt_newt_listbox_get_active(NmtNewtListbox *listbox); +gpointer nmt_newt_listbox_get_active_key(NmtNewtListbox *listbox); + +#endif /* NMT_NEWT_LISTBOX_H */ diff --git a/src/libnmt-newt/nmt-newt-popup.c b/src/libnmt-newt/nmt-newt-popup.c new file mode 100644 index 0000000000..1822c9d953 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-popup.c @@ -0,0 +1,341 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-newt-popup.h" +#include "nmt-newt-form.h" +#include "nmt-newt-hacks.h" +#include "nmt-newt-listbox.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE(NmtNewtPopup, nmt_newt_popup, NMT_TYPE_NEWT_BUTTON) + +#define NMT_NEWT_POPUP_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_NEWT_POPUP, NmtNewtPopupPrivate)) + +typedef struct { + GArray *entries; + int active; +} NmtNewtPopupPrivate; + +enum { + PROP_0, + PROP_ACTIVE, + PROP_ACTIVE_ID, + + LAST_PROP +}; + +/** + * NmtNewtPopupEntry: + * @label: the user-visible label for the entry + * @id: the internal ID of the entry + * + * A single entry in a pop-up menu. + */ + +/** + * nmt_newt_popup_new: + * @entries: an array of #NmtNewtPopupEntry, terminated by an + * entry with a %NULL label + * + * Creates a new #NmtNewtPopup with the given entries. + * + * Returns: a new #NmtNewtPopup + */ +NmtNewtWidget * +nmt_newt_popup_new(NmtNewtPopupEntry *entries) +{ + NmtNewtWidget * widget; + NmtNewtPopupPrivate *priv; + int i; + + widget = g_object_new(NMT_TYPE_NEWT_POPUP, NULL); + priv = NMT_NEWT_POPUP_GET_PRIVATE(widget); + + for (i = 0; entries[i].label; i++) { + NmtNewtPopupEntry entry; + + entry.label = nmt_newt_locale_from_utf8(_(entries[i].label)); + entry.id = g_strdup(entries[i].id); + g_array_append_val(priv->entries, entry); + } + + return widget; +} + +static void +popup_entry_clear_func(NmtNewtPopupEntry *entry) +{ + g_free(entry->label); + g_free(entry->id); +} + +static void +nmt_newt_popup_init(NmtNewtPopup *popup) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE(popup); + + priv->entries = g_array_sized_new(FALSE, FALSE, sizeof(NmtNewtPopupEntry), 10); + g_array_set_clear_func(priv->entries, (GDestroyNotify) popup_entry_clear_func); +} + +static void +nmt_newt_popup_finalize(GObject *object) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE(object); + + g_array_unref(priv->entries); + + G_OBJECT_CLASS(nmt_newt_popup_parent_class)->finalize(object); +} + +static newtComponent +nmt_newt_popup_build_component(NmtNewtComponent *component, gboolean sensitive) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE(component); + NmtNewtPopupEntry * entries = (NmtNewtPopupEntry *) priv->entries->data; + + nmt_newt_button_set_label(NMT_NEWT_BUTTON(component), entries[priv->active].label); + return NMT_NEWT_COMPONENT_CLASS(nmt_newt_popup_parent_class) + ->build_component(component, sensitive); +} + +static void +nmt_newt_popup_activated(NmtNewtWidget *widget) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE(widget); + NmtNewtPopupEntry * entries = (NmtNewtPopupEntry *) priv->entries->data; + NmtNewtForm * form; + NmtNewtWidget * listbox, *ret; + int button_x, button_y; + int window_x, window_y; + int list_w, list_h; + int i, active; + + listbox = nmt_newt_listbox_new(priv->entries->len, 0); + nmt_newt_widget_set_exit_on_activate(listbox, TRUE); + for (i = 0; i < priv->entries->len; i++) + nmt_newt_listbox_append(NMT_NEWT_LISTBOX(listbox), entries[i].label, NULL); + nmt_newt_listbox_set_active(NMT_NEWT_LISTBOX(listbox), priv->active); + nmt_newt_widget_set_padding(listbox, 1, 0, 1, 0); + + nmt_newt_widget_size_request(listbox, &list_w, &list_h); + + g_object_get(nmt_newt_widget_get_form(widget), "x", &window_x, "y", &window_y, NULL); + newtComponentGetPosition(nmt_newt_component_get_component(NMT_NEWT_COMPONENT(widget)), + &button_x, + &button_y); + /* (window_x + button_x) is the screen X coordinate of the newtComponent. A + * newtButton labelled "Foo" is rendered as " <Foo>" (with a preceding + * space), so the "F" is at (window_x + button_x + 2). We've added 1 column + * of padding to the left of the listbox, so we need to position the popup + * at (window_x + button_x + 1) in order for its text to be aligned with the + * button's text. (The x and y coordinates given to NmtNewtForm are the + * coordinates of the top left of the window content, ignoring the border + * graphics.) + */ + window_x += button_x + 1; + window_y += button_y - priv->active; + + form = g_object_new(NMT_TYPE_NEWT_FORM, + "x", + window_x, + "y", + window_y, + "width", + list_w, + "height", + list_h, + "padding", + 0, + "escape-exits", + TRUE, + NULL); + nmt_newt_form_set_content(form, listbox); + + ret = nmt_newt_form_run_sync(form); + if (ret == listbox) + active = nmt_newt_listbox_get_active(NMT_NEWT_LISTBOX(listbox)); + else + active = priv->active; + + g_object_unref(form); + + if (active != priv->active) { + priv->active = active; + g_object_notify(G_OBJECT(widget), "active"); + g_object_notify(G_OBJECT(widget), "active-id"); + nmt_newt_widget_needs_rebuild(widget); + } + + NMT_NEWT_WIDGET_CLASS(nmt_newt_popup_parent_class)->activated(widget); +} + +/** + * nmt_newt_popup_get_active: + * @popup: a #NmtNewtPopup + * + * Gets the index of the active entry in @popup. + * + * Returns: the index of the active entry in @popup. + */ +int +nmt_newt_popup_get_active(NmtNewtPopup *popup) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE(popup); + + return priv->active; +} + +/** + * nmt_newt_popup_set_active: + * @popup: a #NmtNewtPopup + * @active: the index of the new active entry + * + * Sets the active entry in @popup. + */ +void +nmt_newt_popup_set_active(NmtNewtPopup *popup, int active) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE(popup); + + active = CLAMP(active, 0, priv->entries->len - 1); + + if (active != priv->active) { + priv->active = active; + g_object_notify(G_OBJECT(popup), "active"); + g_object_notify(G_OBJECT(popup), "active-id"); + } +} + +/** + * nmt_newt_popup_get_active_id: + * @popup: a #NmtNewtPopup + * + * Gets the textual ID of the active entry in @popup. + * + * Returns: the ID of the active entry in @popup. + */ +const char * +nmt_newt_popup_get_active_id(NmtNewtPopup *popup) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE(popup); + NmtNewtPopupEntry * entries = (NmtNewtPopupEntry *) priv->entries->data; + + return entries[priv->active].id; +} + +/** + * nmt_newt_popup_set_active_id: + * @popup: a #NmtNewtPopup + * @active_id: the ID of the new active entry + * + * Sets the active entry in @popup. + */ +void +nmt_newt_popup_set_active_id(NmtNewtPopup *popup, const char *active_id) +{ + NmtNewtPopupPrivate *priv = NMT_NEWT_POPUP_GET_PRIVATE(popup); + NmtNewtPopupEntry * entries = (NmtNewtPopupEntry *) priv->entries->data; + int i; + + for (i = 0; i < priv->entries->len; i++) { + if (!g_strcmp0(active_id, entries[i].id)) { + nmt_newt_popup_set_active(popup, i); + return; + } + } +} + +static void +nmt_newt_popup_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtNewtPopup *popup = NMT_NEWT_POPUP(object); + + switch (prop_id) { + case PROP_ACTIVE: + nmt_newt_popup_set_active(popup, g_value_get_uint(value)); + break; + case PROP_ACTIVE_ID: + nmt_newt_popup_set_active_id(popup, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_popup_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtNewtPopup *popup = NMT_NEWT_POPUP(object); + + switch (prop_id) { + case PROP_ACTIVE: + g_value_set_uint(value, nmt_newt_popup_get_active(popup)); + break; + case PROP_ACTIVE_ID: + g_value_set_string(value, nmt_newt_popup_get_active_id(popup)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_popup_class_init(NmtNewtPopupClass *popup_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(popup_class); + NmtNewtWidgetClass * widget_class = NMT_NEWT_WIDGET_CLASS(popup_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS(popup_class); + + g_type_class_add_private(popup_class, sizeof(NmtNewtPopupPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_popup_set_property; + object_class->get_property = nmt_newt_popup_get_property; + object_class->finalize = nmt_newt_popup_finalize; + + widget_class->activated = nmt_newt_popup_activated; + + component_class->build_component = nmt_newt_popup_build_component; + + /** + * NmtNewtPopup:active: + * + * The index of the currently-active entry. + */ + g_object_class_install_property(object_class, + PROP_ACTIVE, + g_param_spec_uint("active", + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtPopup:active-id: + * + * The textual ID of the currently-active entry. + */ + g_object_class_install_property( + object_class, + PROP_ACTIVE_ID, + g_param_spec_string("active-id", "", "", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/libnmt-newt/nmt-newt-popup.h b/src/libnmt-newt/nmt-newt-popup.h new file mode 100644 index 0000000000..15e247b41b --- /dev/null +++ b/src/libnmt-newt/nmt-newt-popup.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_POPUP_H +#define NMT_NEWT_POPUP_H + +#include "nmt-newt-button.h" + +#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); + +#endif /* NMT_NEWT_POPUP_H */ diff --git a/src/libnmt-newt/nmt-newt-section.c b/src/libnmt-newt/nmt-newt-section.c new file mode 100644 index 0000000000..fa73ea4a07 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-section.c @@ -0,0 +1,422 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-section + * @short_description: A collapsible section + * + * #NmtNewtSection is a container with two children; the header and + * the body. The header is always visible, but the body is only + * visible when the container is #NmtNewtSection:open. + * + * Note that there is no default way to open and close an + * #NmtNewtSection. You need to implement this yourself. (Eg, by + * binding the #NmtToggleButton:active property of an #NmtToggleButton + * in the section's header to the section's #NmtNewtSection:open + * property.) + * + * In addition to the header and body, the #NmtNewtSection also + * optionally draws a border along the left side, indicating the + * extent of the section. + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-newt-section.h" + +#include "nmt-newt-grid.h" +#include "nmt-newt-label.h" +#include "nmt-newt-utils.h" + +G_DEFINE_TYPE(NmtNewtSection, nmt_newt_section, NMT_TYPE_NEWT_CONTAINER) + +#define NMT_NEWT_SECTION_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_NEWT_SECTION, NmtNewtSectionPrivate)) + +typedef struct { + NmtNewtWidget *header; + int hheight_req, hwidth_req; + + NmtNewtWidget *body; + int bheight_req, bwidth_req; + + gboolean show_border; + NmtNewtWidget *border_grid; + NmtNewtWidget *border_open_label; + NmtNewtWidget *border_closed_label; + NmtNewtWidget *border_end_label; + GPtrArray * border_line_labels; + + gboolean open; +} NmtNewtSectionPrivate; + +static char *closed_glyph, *open_glyph, *line_glyph, *end_glyph; + +enum { + PROP_0, + + PROP_SHOW_BORDER, + PROP_OPEN, + + LAST_PROP +}; + +/** + * nmt_newt_section_new: + * @show_border: whether to show the border on the side of the section + * + * Creates a new #NmtNewtSection + * + * Returns: a new #NmtNewtSection + */ +NmtNewtWidget * +nmt_newt_section_new(gboolean show_border) +{ + return g_object_new(NMT_TYPE_NEWT_SECTION, "show-border", show_border, NULL); +} + +static void +nmt_newt_section_init(NmtNewtSection *section) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS(nmt_newt_section_parent_class); + + priv->show_border = TRUE; + + priv->border_grid = nmt_newt_grid_new(); + parent_class->add(NMT_NEWT_CONTAINER(section), priv->border_grid); + + priv->border_open_label = nmt_newt_label_new(open_glyph); + nmt_newt_widget_set_visible(priv->border_open_label, FALSE); + nmt_newt_grid_add(NMT_NEWT_GRID(priv->border_grid), priv->border_open_label, 0, 0); + + priv->border_closed_label = nmt_newt_label_new(closed_glyph); + nmt_newt_grid_add(NMT_NEWT_GRID(priv->border_grid), priv->border_closed_label, 0, 0); + + priv->border_end_label = nmt_newt_label_new(end_glyph); + nmt_newt_widget_set_visible(priv->border_open_label, FALSE); + nmt_newt_grid_add(NMT_NEWT_GRID(priv->border_grid), priv->border_end_label, 0, 1); + + priv->border_line_labels = g_ptr_array_new(); +} + +static void +nmt_newt_section_finalize(GObject *object) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(object); + + g_ptr_array_unref(priv->border_line_labels); + + G_OBJECT_CLASS(nmt_newt_section_parent_class)->finalize(object); +} + +/** + * nmt_newt_section_set_header: + * @section: an #NmtNewtSection + * @header: the header widget + * + * Sets @section's header widget. + */ +void +nmt_newt_section_set_header(NmtNewtSection *section, NmtNewtWidget *header) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS(nmt_newt_section_parent_class); + NmtNewtContainer * container = NMT_NEWT_CONTAINER(section); + + if (priv->header) + parent_class->remove(container, priv->header); + priv->header = header; + parent_class->add(container, header); +} + +/** + * nmt_newt_section_get_header: + * @section: an #NmtNewtSection + * + * Gets @section's header widget. + * + * Returns: @section's header widget. + */ +NmtNewtWidget * +nmt_newt_section_get_header(NmtNewtSection *section) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(section); + + return priv->header; +} + +/** + * nmt_newt_section_set_body: + * @section: an #NmtNewtSection + * @body: the body widget + * + * Sets @section's body widget. + */ +void +nmt_newt_section_set_body(NmtNewtSection *section, NmtNewtWidget *body) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS(nmt_newt_section_parent_class); + NmtNewtContainer * container = NMT_NEWT_CONTAINER(section); + + if (priv->body) + parent_class->remove(container, priv->body); + priv->body = body; + parent_class->add(container, body); +} + +/** + * nmt_newt_section_get_body: + * @section: an #NmtNewtSection + * + * Gets @section's body widget. + * + * Returns: @section's body widget. + */ +NmtNewtWidget * +nmt_newt_section_get_body(NmtNewtSection *section) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(section); + + return priv->body; +} + +static void +nmt_newt_section_remove(NmtNewtContainer *container, NmtNewtWidget *widget) +{ + NmtNewtSection * section = NMT_NEWT_SECTION(container); + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(section); + NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS(nmt_newt_section_parent_class); + + if (widget == priv->header) + priv->header = NULL; + else if (widget == priv->body) + priv->body = NULL; + else if (widget == priv->border_grid) + priv->border_grid = NULL; + + parent_class->remove(container, widget); +} + +static newtComponent * +nmt_newt_section_get_components(NmtNewtWidget *widget) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(widget); + newtComponent * child_cos; + GPtrArray * cos; + int i; + + g_return_val_if_fail(priv->header != NULL && priv->body != NULL, NULL); + + cos = g_ptr_array_new(); + + if (priv->show_border) { + child_cos = nmt_newt_widget_get_components(priv->border_grid); + for (i = 0; child_cos[i]; i++) + g_ptr_array_add(cos, child_cos[i]); + g_free(child_cos); + } + + child_cos = nmt_newt_widget_get_components(priv->header); + for (i = 0; child_cos[i]; i++) + g_ptr_array_add(cos, child_cos[i]); + g_free(child_cos); + + if (priv->open) { + child_cos = nmt_newt_widget_get_components(priv->body); + for (i = 0; child_cos[i]; i++) + g_ptr_array_add(cos, child_cos[i]); + g_free(child_cos); + } + + g_ptr_array_add(cos, NULL); + return (newtComponent *) g_ptr_array_free(cos, FALSE); +} + +static void +nmt_newt_section_size_request(NmtNewtWidget *widget, int *width, int *height) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(widget); + int w_ignore, h_ignore; + + g_return_if_fail(priv->header != NULL && priv->body != NULL); + + if (priv->show_border) + nmt_newt_widget_size_request(priv->border_grid, &w_ignore, &h_ignore); + nmt_newt_widget_size_request(priv->header, &priv->hwidth_req, &priv->hheight_req); + nmt_newt_widget_size_request(priv->body, &priv->bwidth_req, &priv->bheight_req); + + *width = MAX(priv->hwidth_req, priv->bwidth_req) + 2; + if (priv->open) + *height = priv->hheight_req + priv->bheight_req + (priv->show_border ? 1 : 0); + else + *height = priv->hheight_req; +} + +static void +adjust_border_for_allocation(NmtNewtSectionPrivate *priv, int height) +{ + int i; + + /* We have to use a series of one-line labels rather than a multi-line + * textbox, because newt will hide any component that's partially offscreen, + * but we want the on-screen portion of the border to show even if part of + * it is offscreen. + */ + + if (height == 1) { + nmt_newt_widget_set_visible(priv->border_closed_label, TRUE); + nmt_newt_widget_set_visible(priv->border_open_label, FALSE); + for (i = 0; i < priv->border_line_labels->len; i++) + nmt_newt_widget_set_visible(priv->border_line_labels->pdata[i], FALSE); + nmt_newt_widget_set_visible(priv->border_end_label, FALSE); + } else { + nmt_newt_widget_set_visible(priv->border_closed_label, FALSE); + nmt_newt_widget_set_visible(priv->border_open_label, TRUE); + for (i = 0; i < height - 2; i++) { + if (i >= priv->border_line_labels->len) { + NmtNewtWidget *label; + + label = nmt_newt_label_new(line_glyph); + g_ptr_array_add(priv->border_line_labels, label); + nmt_newt_grid_add(NMT_NEWT_GRID(priv->border_grid), label, 0, i + 1); + } else + nmt_newt_widget_set_visible(priv->border_line_labels->pdata[i], TRUE); + } + nmt_newt_widget_set_visible(priv->border_end_label, TRUE); + nmt_newt_grid_move(NMT_NEWT_GRID(priv->border_grid), priv->border_end_label, 0, height - 1); + } +} + +static void +nmt_newt_section_size_allocate(NmtNewtWidget *widget, int x, int y, int width, int height) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(widget); + + if (priv->show_border) { + int w_ignore, h_ignore; + + adjust_border_for_allocation(priv, height); + nmt_newt_widget_size_request(priv->border_grid, &w_ignore, &h_ignore); + nmt_newt_widget_size_allocate(priv->border_grid, x, y, 1, height); + nmt_newt_widget_size_allocate(priv->header, x + 2, y, width, priv->hheight_req); + } else + nmt_newt_widget_size_allocate(priv->header, x, y, width, priv->hheight_req); + + if (priv->open) { + nmt_newt_widget_size_allocate(priv->body, + x + 2, + y + priv->hheight_req, + width, + height - priv->hheight_req); + } +} + +static void +nmt_newt_section_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_SHOW_BORDER: + priv->show_border = g_value_get_boolean(value); + nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(object)); + break; + case PROP_OPEN: + priv->open = g_value_get_boolean(value); + nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(object)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_section_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtNewtSectionPrivate *priv = NMT_NEWT_SECTION_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_SHOW_BORDER: + g_value_set_boolean(value, priv->show_border); + break; + case PROP_OPEN: + g_value_set_boolean(value, priv->open); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_section_class_init(NmtNewtSectionClass *section_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(section_class); + NmtNewtWidgetClass * widget_class = NMT_NEWT_WIDGET_CLASS(section_class); + NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS(section_class); + + g_type_class_add_private(section_class, sizeof(NmtNewtSectionPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_section_set_property; + object_class->get_property = nmt_newt_section_get_property; + object_class->finalize = nmt_newt_section_finalize; + + widget_class->get_components = nmt_newt_section_get_components; + widget_class->size_request = nmt_newt_section_size_request; + widget_class->size_allocate = nmt_newt_section_size_allocate; + + container_class->remove = nmt_newt_section_remove; + + /* properties */ + + /** + * NmtNewtSection:show-border: + * + * %TRUE if the section should show a border along the left side. + */ + g_object_class_install_property( + object_class, + PROP_SHOW_BORDER, + g_param_spec_boolean("show-border", + "", + "", + TRUE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /** + * NmtNewtSection:open: + * + * %TRUE if the section is open (ie, its body is visible), %FALSE + * if not. + */ + g_object_class_install_property( + object_class, + PROP_OPEN, + g_param_spec_boolean("open", "", "", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + /* globals */ + closed_glyph = nmt_newt_locale_from_utf8("\342\225\220"); /* ═ */ + open_glyph = nmt_newt_locale_from_utf8("\342\225\244"); /* ╤ */ + line_glyph = nmt_newt_locale_from_utf8("\342\224\202"); /* │ */ + end_glyph = nmt_newt_locale_from_utf8("\342\224\224"); /* └ */ + if (!*closed_glyph || !*open_glyph || !*line_glyph || !*end_glyph) { + g_free(closed_glyph); + g_free(open_glyph); + g_free(line_glyph); + g_free(end_glyph); + + closed_glyph = g_strdup("-"); + open_glyph = g_strdup("+"); + line_glyph = g_strdup("|"); + end_glyph = g_strdup("\\"); + } +} diff --git a/src/libnmt-newt/nmt-newt-section.h b/src/libnmt-newt/nmt-newt-section.h new file mode 100644 index 0000000000..c3b9f909ee --- /dev/null +++ b/src/libnmt-newt/nmt-newt-section.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_SECTION_H +#define NMT_NEWT_SECTION_H + +#include "nmt-newt-container.h" + +#define NMT_TYPE_NEWT_SECTION (nmt_newt_section_get_type()) +#define NMT_NEWT_SECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSection)) +#define NMT_NEWT_SECTION_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass)) +#define NMT_IS_NEWT_SECTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_SECTION)) +#define NMT_IS_NEWT_SECTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_NEWT_SECTION)) +#define NMT_NEWT_SECTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_NEWT_SECTION, NmtNewtSectionClass)) + +struct _NmtNewtSection { + NmtNewtContainer parent; +}; + +typedef struct { + NmtNewtContainerClass parent; + +} NmtNewtSectionClass; + +GType nmt_newt_section_get_type(void); + +NmtNewtWidget *nmt_newt_section_new(gboolean show_border); + +void nmt_newt_section_set_header(NmtNewtSection *section, NmtNewtWidget *header); +NmtNewtWidget *nmt_newt_section_get_header(NmtNewtSection *section); + +void nmt_newt_section_set_body(NmtNewtSection *section, NmtNewtWidget *body); +NmtNewtWidget *nmt_newt_section_get_body(NmtNewtSection *section); + +#endif /* NMT_NEWT_SECTION_H */ diff --git a/src/libnmt-newt/nmt-newt-separator.c b/src/libnmt-newt/nmt-newt-separator.c new file mode 100644 index 0000000000..70c505ca6b --- /dev/null +++ b/src/libnmt-newt/nmt-newt-separator.c @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.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/src/libnmt-newt/nmt-newt-separator.h b/src/libnmt-newt/nmt-newt-separator.h new file mode 100644 index 0000000000..0aa0fd0eff --- /dev/null +++ b/src/libnmt-newt/nmt-newt-separator.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_SEPARATOR_H +#define NMT_NEWT_SEPARATOR_H + +#include "nmt-newt-component.h" + +#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); + +#endif /* NMT_NEWT_SEPARATOR_H */ diff --git a/src/libnmt-newt/nmt-newt-stack.c b/src/libnmt-newt/nmt-newt-stack.c new file mode 100644 index 0000000000..e0fdb3b9a2 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-stack.c @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.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/src/libnmt-newt/nmt-newt-stack.h b/src/libnmt-newt/nmt-newt-stack.h new file mode 100644 index 0000000000..ca6ad2038c --- /dev/null +++ b/src/libnmt-newt/nmt-newt-stack.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_STACK_H +#define NMT_NEWT_STACK_H + +#include "nmt-newt-container.h" + +#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); + +#endif /* NMT_NEWT_STACK_H */ diff --git a/src/libnmt-newt/nmt-newt-textbox.c b/src/libnmt-newt/nmt-newt-textbox.c new file mode 100644 index 0000000000..128e539b91 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-textbox.c @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.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 scrollable. + * @NMT_NEWT_TEXTBOX_SET_BACKGROUND: the textbox should have a + * white background + * + * Flags for an #NmtNewtTextbox + */ + +/** + * nmt_newt_textbox_new: + * @flags: the textbox's flags + * @wrap_width: width in characters at which to word-wrap, or + * 0 to not wrap. + * + * Creates a new #NmtNewtTextbox. + * + * Returns: a new #NmtNewtTextbox + */ +NmtNewtWidget * +nmt_newt_textbox_new(NmtNewtTextboxFlags flags, int wrap_width) +{ + return g_object_new(NMT_TYPE_NEWT_TEXTBOX, "flags", flags, "wrap-width", wrap_width, NULL); +} + +/** + * nmt_newt_textbox_get_text: + * @textbox: an #NmtNewtTextbox + * + * Gets @textbox's text + * + * Returns: @textbox's text + */ +void +nmt_newt_textbox_set_text(NmtNewtTextbox *textbox, const char *text) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE(textbox); + char ** lines; + int i, width; + + if (!text) + text = ""; + if (!strcmp(priv->text, text)) + return; + + g_free(priv->text); + priv->text = g_strdup(text); + + priv->width = priv->height = 0; + lines = g_strsplit(priv->text, "\n", -1); + for (i = 0; lines[i]; i++) { + width = nmt_newt_text_width(lines[i]); + if (width > priv->width) + priv->width = width; + } + g_free(lines); + priv->height = MIN(i, 1); + + g_object_notify(G_OBJECT(textbox), "text"); + nmt_newt_widget_needs_rebuild(NMT_NEWT_WIDGET(textbox)); +} + +/** + * nmt_newt_textbox_get_text: + * @textbox: an #NmtNewtTextbox + * + * Gets @textbox's text + * + * Returns: @textbox's text + */ +const char * +nmt_newt_textbox_get_text(NmtNewtTextbox *textbox) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE(textbox); + + return priv->text; +} + +static void +nmt_newt_textbox_init(NmtNewtTextbox *textbox) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE(textbox); + + priv->text = g_strdup(""); +} + +static void +nmt_newt_textbox_finalize(GObject *object) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE(object); + + g_free(priv->text); + + G_OBJECT_CLASS(nmt_newt_textbox_parent_class)->finalize(object); +} + +static guint +convert_flags(NmtNewtTextboxFlags flags) +{ + guint newt_flags = 0; + + if (flags & NMT_NEWT_TEXTBOX_SCROLLABLE) + newt_flags |= NEWT_FLAG_SCROLL; + + return newt_flags; +} + +static newtComponent +nmt_newt_textbox_build_component(NmtNewtComponent *component, gboolean sensitive) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE(component); + newtComponent co; + const char * text; + char * text_lc; + + text = priv->text; + if (!*text) + text = "\n"; + + text_lc = nmt_newt_locale_from_utf8(text); + if (priv->wrap_width > 0) { + co = newtTextboxReflowed(-1, -1, text_lc, priv->wrap_width, 0, 0, 0); + } else { + co = newtTextbox(-1, -1, priv->width, priv->height, convert_flags(priv->flags)); + newtTextboxSetText(co, text_lc); + } + g_free(text_lc); + + if (priv->flags & NMT_NEWT_TEXTBOX_SET_BACKGROUND) + newtTextboxSetColors(co, + NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, + NEWT_COLORSET_ACTTEXTBOX); + + return co; +} + +static void +nmt_newt_textbox_set_property(GObject * object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + NmtNewtTextbox * textbox = NMT_NEWT_TEXTBOX(object); + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE(textbox); + + switch (prop_id) { + case PROP_TEXT: + nmt_newt_textbox_set_text(textbox, g_value_get_string(value)); + break; + case PROP_FLAGS: + priv->flags = g_value_get_uint(value); + break; + case PROP_WRAP_WIDTH: + priv->wrap_width = g_value_get_int(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_textbox_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtNewtTextboxPrivate *priv = NMT_NEWT_TEXTBOX_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_TEXT: + g_value_set_string(value, priv->text); + break; + case PROP_FLAGS: + g_value_set_uint(value, priv->flags); + break; + case PROP_WRAP_WIDTH: + g_value_set_int(value, priv->wrap_width); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_textbox_class_init(NmtNewtTextboxClass *textbox_class) +{ + GObjectClass * object_class = G_OBJECT_CLASS(textbox_class); + NmtNewtComponentClass *component_class = NMT_NEWT_COMPONENT_CLASS(textbox_class); + + g_type_class_add_private(textbox_class, sizeof(NmtNewtTextboxPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_textbox_set_property; + object_class->get_property = nmt_newt_textbox_get_property; + object_class->finalize = nmt_newt_textbox_finalize; + + component_class->build_component = nmt_newt_textbox_build_component; + + /** + * NmtNewtTextbox:text: + * + * The textbox's text + */ + g_object_class_install_property( + object_class, + PROP_TEXT, + g_param_spec_string("text", "", "", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtTextbox:flags: + * + * The textbox's flags + */ + g_object_class_install_property( + object_class, + PROP_FLAGS, + g_param_spec_uint("flags", + "", + "", + 0, + G_MAXUINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtTextbox:wrap-width: + * + * The width in characters at which the textbox's text + * will wrap, or 0 if it does not wrap. + */ + g_object_class_install_property( + object_class, + PROP_WRAP_WIDTH, + g_param_spec_int("wrap-width", + "", + "", + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/libnmt-newt/nmt-newt-textbox.h b/src/libnmt-newt/nmt-newt-textbox.h new file mode 100644 index 0000000000..f589d62cd0 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-textbox.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_TEXTBOX_H +#define NMT_NEWT_TEXTBOX_H + +#include "nmt-newt-component.h" + +#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); + +#endif /* NMT_NEWT_TEXTBOX_H */ diff --git a/src/libnmt-newt/nmt-newt-toggle-button.c b/src/libnmt-newt/nmt-newt-toggle-button.c new file mode 100644 index 0000000000..c48931e5d7 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-toggle-button.c @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-toggle-button + * @short_description: Toggle buttons + * + * #NmtNewtToggleButton implements a two-state toggle button. + */ + +#include "libnm-client-aux-extern/nm-default-client.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/src/libnmt-newt/nmt-newt-toggle-button.h b/src/libnmt-newt/nmt-newt-toggle-button.h new file mode 100644 index 0000000000..bdaa445d18 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-toggle-button.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_TOGGLE_BUTTON_H +#define NMT_NEWT_TOGGLE_BUTTON_H + +#include "nmt-newt-button.h" + +#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); + +#endif /* NMT_NEWT_TOGGLE_BUTTON_H */ diff --git a/src/libnmt-newt/nmt-newt-types.h b/src/libnmt-newt/nmt-newt-types.h new file mode 100644 index 0000000000..daeb7c2457 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-types.h @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_TYPES_H +#define NMT_NEWT_TYPES_H + +#include <newt.h> + +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; + +#endif /* NMT_NEWT_COMPONENT_H */ diff --git a/src/libnmt-newt/nmt-newt-utils.c b/src/libnmt-newt/nmt-newt-utils.c new file mode 100644 index 0000000000..1216b8d356 --- /dev/null +++ b/src/libnmt-newt/nmt-newt-utils.c @@ -0,0 +1,366 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +/** + * SECTION:nmt-newt-utils + * @short_description: Utility functions + */ + +#include "libnm-client-aux-extern/nm-default-client.h" + +#include <stdarg.h> +#include <unistd.h> +#include <sys/wait.h> + +#include "nmt-newt-utils.h" + +static void +nmt_newt_dialog_g_log_handler(const char * log_domain, + GLogLevelFlags log_level, + const char * message, + gpointer user_data) +{ + const char * level_name; + char * full_message; + int screen_width, screen_height; + newtComponent text, ok, form; + newtGrid grid; + + g_assert(!(log_level & G_LOG_FLAG_RECURSION)); + + if (log_level & G_LOG_LEVEL_DEBUG) + return; + + switch (log_level & G_LOG_LEVEL_MASK) { + case G_LOG_LEVEL_ERROR: + level_name = "ERROR"; + break; + case G_LOG_LEVEL_CRITICAL: + level_name = "CRITICAL"; + break; + case G_LOG_LEVEL_WARNING: + level_name = "WARNING"; + break; + case G_LOG_LEVEL_MESSAGE: + level_name = "Message"; + break; + default: + level_name = NULL; + } + + full_message = g_strdup_printf("%s%s%s%s%s", + log_domain ?: "", + log_domain && 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 ?: "")); + newtGridFree(grid, TRUE); + + form = newtForm(NULL, NULL, 0); + newtFormAddComponents(form, text, ok, NULL); + newtRunForm(form); + newtFormDestroy(form); + newtPopWindow(); +} + +static void +nmt_newt_basic_g_log_handler(const char * log_domain, + GLogLevelFlags log_level, + const char * message, + gpointer user_data) +{ + newtSuspend(); + g_log_default_handler(log_domain, log_level, message, NULL); + newtResume(); +} + +static void +nmt_newt_suspend_callback(gpointer user_data) +{ + newtSuspend(); + kill(getpid(), SIGTSTP); + newtResume(); +} + +/** + * nmt_newt_init: + * + * Wrapper for newtInit() that also does some nmt-newt-internal setup. + * This should be called once, before any other nmt-newt functions. + */ +void +nmt_newt_init(void) +{ + newtInit(); + newtCls(); + + newtSetColor(NEWT_COLORSET_CHECKBOX, "black", "lightgray"); + newtSetColor(NMT_NEWT_COLORSET_BAD_LABEL, "red", "lightgray"); + newtSetColor(NMT_NEWT_COLORSET_PLAIN_LABEL, "black", "lightgray"); + newtSetColor(NMT_NEWT_COLORSET_DISABLED_BUTTON, "blue", "lightgray"); + newtSetColor(NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND, "black", "white"); + + if (g_getenv("NMTUI_DEBUG")) + g_log_set_default_handler(nmt_newt_dialog_g_log_handler, NULL); + else + g_log_set_default_handler(nmt_newt_basic_g_log_handler, NULL); + + newtSetSuspendCallback(nmt_newt_suspend_callback, NULL); +} + +/** + * nmt_newt_finished: + * + * Wrapper for newtFinished(). Should be called at the end of the program. + */ +void +nmt_newt_finished(void) +{ + newtFinished(); + g_log_set_default_handler(g_log_default_handler, NULL); +} + +/** + * nmt_newt_message_dialog: + * @message: a printf()-style message format + * @...: arguments + * + * Displays the given message in a dialog box with a single "OK" + * button, and returns after the user clicks "OK". + */ +void +nmt_newt_message_dialog(const char *message, ...) +{ + va_list ap; + char * msg, *msg_lc, *ok_lc; + + va_start(ap, message); + msg = g_strdup_vprintf(message, ap); + va_end(ap); + + msg_lc = nmt_newt_locale_from_utf8(msg); + ok_lc = nmt_newt_locale_from_utf8(_("OK")); + newtWinMessage(NULL, ok_lc, "%s", msg_lc); + + g_free(ok_lc); + g_free(msg_lc); + g_free(msg); +} + +/** + * nmt_newt_choice_dialog: + * @button1: the label for the first button + * @button2: the label for the second button + * @message: a printf()-style message format + * @...: arguments + * + * Displays the given message in a dialog box with two buttons with + * the indicated labels, and waits for the user to click one. + * + * Returns: which button was clicked: 0 for @button1 or 1 for @button2 + */ +int +nmt_newt_choice_dialog(const char *button1, const char *button2, const char *message, ...) +{ + va_list ap; + char * msg, *msg_lc, *button1_lc, *button2_lc; + int choice; + + va_start(ap, message); + msg = g_strdup_vprintf(message, ap); + va_end(ap); + + msg_lc = nmt_newt_locale_from_utf8(msg); + button1_lc = nmt_newt_locale_from_utf8(button1); + button2_lc = nmt_newt_locale_from_utf8(button2); + choice = newtWinChoice(NULL, button1_lc, button2_lc, "%s", msg_lc); + + g_free(button1_lc); + g_free(button2_lc); + g_free(msg_lc); + g_free(msg); + + return choice; +} + +/** + * nmt_newt_locale_to_utf8: + * @str_lc: a string in the user's locale encoding + * + * Convenience wrapper around g_locale_to_utf8(). + * + * Note that libnewt works in terms of the user's locale character + * set, NOT UTF-8, so all strings received from libnewt must be + * converted back to UTF-8 before being returned to the caller or used + * in other APIs. + * + * Returns: @str_lc, converted to UTF-8. + */ +char * +nmt_newt_locale_to_utf8(const char *str_lc) +{ + char *str_utf8; + + str_utf8 = g_locale_to_utf8(str_lc, -1, NULL, NULL, NULL); + if (!str_utf8) + str_utf8 = g_strdup(""); + return str_utf8; +} + +/** + * nmt_newt_locale_from_utf8: + * @str_utf8: a UTF-8 string + * + * Convenience wrapper around g_locale_from_utf8(). + * + * Note that libnewt works in terms of the user's locale character + * set, NOT UTF-8, so all strings from nmt-newt must be converted to + * locale encoding before being passed to libnewt. + * + * Returns: @str_utf8, converted to the user's locale encoding. + */ +char * +nmt_newt_locale_from_utf8(const char *str_utf8) +{ + char *str_lc; + + str_lc = g_locale_from_utf8(str_utf8, -1, NULL, NULL, NULL); + if (!str_lc) + str_lc = g_strdup(""); + return str_lc; +} + +/** + * nmt_newt_text_width + * @str: a UTF-8 string + * + * Computes the width (in terminal columns) of @str. + * + * Returns: the width of @str + */ +int +nmt_newt_text_width(const char *str) +{ + int width; + gunichar ch; + + for (width = 0; *str; str = g_utf8_next_char(str)) { + ch = g_utf8_get_char(str); + + /* Based on _vte_iso2022_unichar_width */ + if (G_LIKELY(ch < 0x80)) + width += 1; + else if (G_UNLIKELY(g_unichar_iszerowidth(ch))) + width += 0; + else if (G_UNLIKELY(g_unichar_iswide(ch))) + width += 2; + else + width += 1; + } + + return width; +} + +/** + * nmt_newt_edit_string: + * @data: data to edit + * + * libnewt does not have a multi-line editable text component, so + * nmt-newt provides this function instead, which will open the user's + * editor to edit a file containing the given @data (ensuring that the + * current screen state is saved before starting the editor and + * restored after it returns). + * + * Returns: the edited data, or %NULL if an error occurred. + */ +char * +nmt_newt_edit_string(const char *data) +{ + gssize len, nwrote; + char * filename, *argv[3]; + GError *error = NULL; + int fd, status; + char * new_data = NULL; + + fd = g_file_open_tmp("XXXXXX.json", &filename, &error); + if (fd == -1) { + nmt_newt_message_dialog(_("Could not create temporary file: %s"), error->message); + g_error_free(error); + return NULL; + } + + len = data ? strlen(data) : 0; + while (len) { + do + nwrote = write(fd, data, len); + while (nwrote == -1 && errno == EINTR); + + len -= nwrote; + data += nwrote; + } + nm_close(fd); + + argv[0] = (char *) g_getenv("VISUAL"); + if (!argv[0]) + argv[0] = (char *) g_getenv("EDITOR"); + if (!argv[0]) + argv[0] = (char *) "vi"; + argv[1] = filename; + argv[2] = NULL; + + newtSuspend(); + g_spawn_sync(NULL, + argv, + NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, + NULL, + NULL, + NULL, + NULL, + &status, + &error); + newtResume(); + + if (error) { + nmt_newt_message_dialog(_("Could not create temporary file: %s"), error->message); + g_error_free(error); + goto done; + } + + if (!g_spawn_check_exit_status(status, &error)) { + nmt_newt_message_dialog(_("Editor failed: %s"), error->message); + g_error_free(error); + goto done; + } + + if (!g_file_get_contents(filename, &new_data, NULL, &error)) { + nmt_newt_message_dialog(_("Could not re-read file: %s"), error->message); + g_error_free(error); + goto done; + } + +done: + unlink(filename); + g_free(filename); + + return new_data; +} diff --git a/src/libnmt-newt/nmt-newt-utils.h b/src/libnmt-newt/nmt-newt-utils.h new file mode 100644 index 0000000000..04089f9f8b --- /dev/null +++ b/src/libnmt-newt/nmt-newt-utils.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_UTILS_H +#define NMT_NEWT_UTILS_H + +#include <newt.h> + +void nmt_newt_init(void); +void nmt_newt_finished(void); + +typedef enum { + NMT_NEWT_COLORSET_BAD_LABEL = NEWT_COLORSET_CUSTOM(0), + NMT_NEWT_COLORSET_PLAIN_LABEL, + NMT_NEWT_COLORSET_DISABLED_BUTTON, + NMT_NEWT_COLORSET_TEXTBOX_WITH_BACKGROUND +} NmtNewtColorsets; + +char *nmt_newt_locale_to_utf8(const char *str_lc); +char *nmt_newt_locale_from_utf8(const char *str_utf8); + +int nmt_newt_text_width(const char *str); + +void nmt_newt_message_dialog(const char *message, ...) _nm_printf(1, 2); +int nmt_newt_choice_dialog(const char *button1, const char *button2, const char *message, ...) + _nm_printf(3, 4); + +char *nmt_newt_edit_string(const char *data); + +#endif /* NMT_NEWT_UTILS_H */ diff --git a/src/libnmt-newt/nmt-newt-widget.c b/src/libnmt-newt/nmt-newt-widget.c new file mode 100644 index 0000000000..8b5647aa9b --- /dev/null +++ b/src/libnmt-newt/nmt-newt-widget.c @@ -0,0 +1,642 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 "libnm-client-aux-extern/nm-default-client.h" + +#include "nmt-newt-widget.h" +#include "nmt-newt-form.h" + +G_DEFINE_ABSTRACT_TYPE(NmtNewtWidget, nmt_newt_widget, G_TYPE_INITIALLY_UNOWNED) + +#define NMT_NEWT_WIDGET_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetPrivate)) + +typedef struct { + NmtNewtWidget *parent; + gboolean visible, realized, valid; + gboolean exit_on_activate; + + int pad_left, pad_top, pad_right, pad_bottom; +} NmtNewtWidgetPrivate; + +enum { + PROP_0, + + PROP_PARENT, + PROP_VISIBLE, + PROP_VALID, + PROP_EXIT_ON_ACTIVATE, + + LAST_PROP +}; + +enum { + NEEDS_REBUILD, + ACTIVATED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +static void +nmt_newt_widget_init(NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + priv->visible = TRUE; + priv->valid = TRUE; +} + +static void +nmt_newt_widget_finalize(GObject *object) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(object); + + nmt_newt_widget_unrealize(NMT_NEWT_WIDGET(object)); + g_clear_object(&priv->parent); + + G_OBJECT_CLASS(nmt_newt_widget_parent_class)->finalize(object); +} + +/** + * nmt_newt_widget_realize: + * @widget: an #NmtNewtWidget + * + * "Realizes" @widget. That is, creates #newtComponents corresponding + * to @widget and its children. + * + * You should not need to call this yourself; an #NmtNewtForm will + * cause its children to be realized and unrealized as needed. + */ +void +nmt_newt_widget_realize(NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + if (!priv->realized) { + NMT_NEWT_WIDGET_GET_CLASS(widget)->realize(widget); + priv->realized = TRUE; + } +} + +/** + * nmt_newt_widget_unrealize: + * @widget: an #NmtNewtWidget + * + * "Unrealizes" @widget, destroying its #newtComponents. + * + * You should not need to call this yourself; an #NmtNewtForm will + * cause its children to be realized and unrealized as needed. + */ +void +nmt_newt_widget_unrealize(NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + if (priv->realized) { + NMT_NEWT_WIDGET_GET_CLASS(widget)->unrealize(widget); + priv->realized = FALSE; + } +} + +/** + * nmt_newt_widget_get_realized: + * @widget: an #NmtNewtWidget + * + * Checks if @widget is realized or not. + * + * Returns: whether @widget is realized. + */ +gboolean +nmt_newt_widget_get_realized(NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + return priv->realized; +} + +/** + * nmt_newt_widget_get_components: + * @widget: an #NmtNewtWidget + * + * Gets the #newtComponents that make up @widget, if @widget is + * visible. If @widget has not yet been realized, it will be realized + * first. + * + * If this function is called on a widget, then the widget will assume + * that someone else is now responsible for destroying the components, + * and so it will not destroy them itself when the widget is + * destroyed. Normally, components will end up being destroyed by the + * #NmtNewtForm they are added to. + * + * Returns: a %NULL-terminated array of components, in focus-chain + * order. You must free the array with g_free() when you are done + * with it. + */ +newtComponent * +nmt_newt_widget_get_components(NmtNewtWidget *widget) +{ + if (nmt_newt_widget_get_visible(widget)) { + nmt_newt_widget_realize(widget); + return NMT_NEWT_WIDGET_GET_CLASS(widget)->get_components(widget); + } else + return NULL; +} + +/** + * nmt_newt_widget_find_component: + * @widget: an #NmtNewtWidget + * @co: a #newtComponent + * + * Finds the widget inside @widget that owns @co. + * + * Return value: @co's owner, or %NULL if it was not found. + */ +NmtNewtWidget * +nmt_newt_widget_find_component(NmtNewtWidget *widget, newtComponent co) +{ + return NMT_NEWT_WIDGET_GET_CLASS(widget)->find_component(widget, co); +} + +/** + * nmt_newt_widget_set_padding: + * @widget: an #NmtNewtWidget + * @pad_left: padding on the left of @widget + * @pad_top: padding on the top of @widget + * @pad_right: padding on the right of @widget + * @pad_bottom: padding on the bottom of @widget + * + * Sets the padding on @widget. + */ +void +nmt_newt_widget_set_padding(NmtNewtWidget *widget, + int pad_left, + int pad_top, + int pad_right, + int pad_bottom) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + priv->pad_left = pad_left; + priv->pad_top = pad_top; + priv->pad_right = pad_right; + priv->pad_bottom = pad_bottom; +} + +/** + * nmt_newt_widget_size_request: + * @widget: an #NmtNewtWidget + * @width: (out): on output, the widget's requested width + * @height: (out): on output, the widget's requested height + * + * Asks @widget for its requested size. If @widget is not visible, + * this will return 0, 0. If @widget has not yet been realized, it + * will be realized first. + */ +void +nmt_newt_widget_size_request(NmtNewtWidget *widget, int *width, int *height) +{ + if (nmt_newt_widget_get_visible(widget)) { + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + nmt_newt_widget_realize(widget); + NMT_NEWT_WIDGET_GET_CLASS(widget)->size_request(widget, width, height); + + *width += priv->pad_left + priv->pad_right; + *height += priv->pad_top + priv->pad_bottom; + } else + *width = *height = 0; +} + +/** + * nmt_newt_widget_size_allocate: + * @widget: an #NmtNewtWidget + * @x: the widget's (absolute) X coordinate + * @y: the widget's (absolute) Y coordinate + * @width: the widget's allocated width + * @height: the widget's allocated height + * + * Positions @widget at the given coordinates, with the given size. If + * @widget is not visible, this has no effect. If @widget has not yet + * been realized, it will be realized first. + * + * @x and @y are absolute coordinates (ie, relative to the screen / + * terminal window, not relative to @widget's parent). + * + * In general, the results are undefined if @width or @height is less + * than the widget's requested size. If @width or @height is larger + * than the requested size, most #NmtNewtComponents will ignore the + * extra space, but some components and most containers will expand to + * fit. + */ +void +nmt_newt_widget_size_allocate(NmtNewtWidget *widget, int x, int y, int width, int height) +{ + if (nmt_newt_widget_get_visible(widget)) { + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + nmt_newt_widget_realize(widget); + x += priv->pad_left; + y += priv->pad_top; + width -= priv->pad_left + priv->pad_right; + height -= priv->pad_top + priv->pad_bottom; + + NMT_NEWT_WIDGET_GET_CLASS(widget)->size_allocate(widget, x, y, width, height); + } +} + +/** + * nmt_newt_widget_get_focus_component: + * @widget: an #NmtNewtWidget + * + * Gets the #newtComponent that should be given the keyboard focus when + * @widget is focused. + * + * Returns: the #newtComponent to focus, or %NULL if @widget can't + * take the focus. + */ +newtComponent +nmt_newt_widget_get_focus_component(NmtNewtWidget *widget) +{ + if (!NMT_NEWT_WIDGET_GET_CLASS(widget)->get_focus_component) + return NULL; + + return NMT_NEWT_WIDGET_GET_CLASS(widget)->get_focus_component(widget); +} + +static void +nmt_newt_widget_real_activated(NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + if (priv->exit_on_activate) + nmt_newt_form_quit(nmt_newt_widget_get_form(widget)); +} + +/** + * nmt_newt_widget_activated: + * @widget: an #NmtNewtWidget + * + * Tells @widget that its #newtComponent has been activated (ie, the + * user hit "Return" on it) and emits #NmtNewtWidget::activated. + * + * If #NmtNewtWidget:exit-on-activate is set on @widget, then this + * will call nmt_newt_form_quit() on the widget's form. + */ +void +nmt_newt_widget_activated(NmtNewtWidget *widget) +{ + g_signal_emit(widget, signals[ACTIVATED], 0); +} + +/** + * nmt_newt_widget_get_exit_on_activate: + * @widget: an #NmtNewtWidget + * + * Gets @widget's #NmtNewtWidget:exit-on-activate flag, qv. + * + * Returns: @widget's #NmtNewtWidget:exit-on-activate flag + */ +gboolean +nmt_newt_widget_get_exit_on_activate(NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + return priv->exit_on_activate; +} + +/** + * nmt_newt_widget_set_exit_on_activate: + * @widget: an #NmtNewtWidget + * @exit_on_activate: whether @widget should exit on activate. + * + * Sets @widget's #NmtNewtWidget:exit-on-activate flag, qv. + */ +void +nmt_newt_widget_set_exit_on_activate(NmtNewtWidget *widget, gboolean exit_on_activate) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + exit_on_activate = !!exit_on_activate; + if (priv->exit_on_activate != exit_on_activate) { + priv->exit_on_activate = exit_on_activate; + g_object_notify(G_OBJECT(widget), "exit-on-activate"); + } +} + +/** + * nmt_newt_widget_get_visible: + * @widget: an #NmtNewtWidget + * + * Gets @widget's #NmtNewtWidget:visible flag, qv. + * + * Returns: @widget's #NmtNewtWidget:visible flag + */ +gboolean +nmt_newt_widget_get_visible(NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + return priv->visible; +} + +/** + * nmt_newt_widget_set_visible: + * @widget: an #NmtNewtWidget + * @visible: whether @widget should be visible + * + * Sets @widget's #NmtNewtWidget:visible flag, qv. + */ +void +nmt_newt_widget_set_visible(NmtNewtWidget *widget, gboolean visible) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + visible = !!visible; + if (priv->visible != visible) { + priv->visible = visible; + g_object_notify(G_OBJECT(widget), "visible"); + nmt_newt_widget_needs_rebuild(widget); + } +} + +/** + * nmt_newt_widget_set_parent: + * @widget: an #NmtNewtWidget + * @parent: @widget's parent + * + * Sets @widget's parent to @parent. This is used internally by + * #NmtNewtContainer implementations; you must use an appropriate + * container-specific method to actually add a widget to a container. + */ +void +nmt_newt_widget_set_parent(NmtNewtWidget *widget, NmtNewtWidget *parent) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + g_clear_object(&priv->parent); + priv->parent = parent ? g_object_ref(parent) : NULL; + g_object_notify(G_OBJECT(widget), "parent"); +} + +/** + * nmt_newt_widget_get_parent: + * @widget: an #NmtNewtWidget + * + * Gets @widget's parent + * + * Returns: @widget's parent + */ +NmtNewtWidget * +nmt_newt_widget_get_parent(NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + return priv->parent; +} + +/** + * nmt_newt_widget_get_form: + * @widget: an #NmtNewtWidget + * + * Gets @widget's top-level form. + * + * Returns: @widget's #NmtNewtForm + */ +NmtNewtForm * +nmt_newt_widget_get_form(NmtNewtWidget *widget) +{ + while (widget) { + if (NMT_IS_NEWT_FORM(widget)) + return NMT_NEWT_FORM(widget); + widget = nmt_newt_widget_get_parent(widget); + } + + return NULL; +} + +/** + * nmt_newt_widget_get_valid: + * @widget: an #NmtNewtWidget + * + * Gets @widget's #NmtNewtWidget:valid flag, indicating whether its + * content is valid. + * + * Returns: @widget's #NmtNewtWidget:valid flag + */ +gboolean +nmt_newt_widget_get_valid(NmtNewtWidget *widget) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + return priv->valid; +} + +/** + * nmt_newt_widget_set_valid: + * @widget: an #NmtNewtWidget + * @valid: whether @widget is valid + * + * Sets @widget's #NmtNewtWidget:valid flag, indicating whether its + * content is valid. + * + * This method should be considered "protected"; if you change it, the + * widget implementation will likely just change it back at some + * point. + */ +void +nmt_newt_widget_set_valid(NmtNewtWidget *widget, gboolean valid) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(widget); + + valid = !!valid; + if (priv->valid == valid) + return; + + priv->valid = valid; + g_object_notify(G_OBJECT(widget), "valid"); +} + +/** + * nmt_newt_widget_needs_rebuilds: + * @widget: an #NmtNewtWidget + * + * Marks @widget as needing to be "rebuilt" (ie, re-realized). This is + * called automatically in some cases (such as when adding a widget to + * or removing it from a container). #NmtNewtComponent implementations + * should also call this if they need to make some change that can + * only be done by destroying their current #newtComponent and + * creating a new one. + */ +void +nmt_newt_widget_needs_rebuild(NmtNewtWidget *widget) +{ + g_signal_emit(widget, signals[NEEDS_REBUILD], 0); +} + +static void +nmt_newt_widget_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + NmtNewtWidget *widget = NMT_NEWT_WIDGET(object); + + switch (prop_id) { + case PROP_PARENT: + nmt_newt_widget_set_parent(widget, g_value_get_object(value)); + break; + case PROP_VISIBLE: + nmt_newt_widget_set_visible(widget, g_value_get_boolean(value)); + break; + case PROP_EXIT_ON_ACTIVATE: + nmt_newt_widget_set_exit_on_activate(widget, g_value_get_boolean(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_widget_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + NmtNewtWidgetPrivate *priv = NMT_NEWT_WIDGET_GET_PRIVATE(object); + + switch (prop_id) { + case PROP_PARENT: + g_value_set_object(value, priv->parent); + break; + case PROP_VISIBLE: + g_value_set_boolean(value, priv->visible); + break; + case PROP_VALID: + g_value_set_boolean(value, priv->valid); + break; + case PROP_EXIT_ON_ACTIVATE: + g_value_set_boolean(value, priv->exit_on_activate); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +nmt_newt_widget_class_init(NmtNewtWidgetClass *widget_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(widget_class); + + g_type_class_add_private(widget_class, sizeof(NmtNewtWidgetPrivate)); + + /* virtual methods */ + object_class->set_property = nmt_newt_widget_set_property; + object_class->get_property = nmt_newt_widget_get_property; + object_class->finalize = nmt_newt_widget_finalize; + + widget_class->activated = nmt_newt_widget_real_activated; + + /* signals */ + + /** + * NmtNewtWidget::needs-rebuild: + * @widget: the #NmtNewtWidget + * + * Emitted when nmt_newt_widget_need_rebuild() is called on @widget + * or any of its children. This signal propagates up the container + * hierarchy, eventually reaching the top-level #NmtNewtForm. + */ + signals[NEEDS_REBUILD] = g_signal_new("needs-rebuild", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NmtNewtWidgetClass, needs_rebuild), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + + /** + * NmtNewtWidget::activated: + * @widget: the #NmtNewtWidget + * + * Emitted when the widget's #newtComponent is activated. + */ + signals[ACTIVATED] = g_signal_new("activated", + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET(NmtNewtWidgetClass, activated), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0); + + /* properties */ + + /** + * NmtNewtWidget:parent: + * + * The widget's parent widget, or %NULL if it has no parent. + */ + g_object_class_install_property(object_class, + PROP_PARENT, + g_param_spec_object("parent", + "", + "", + NMT_TYPE_NEWT_WIDGET, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtWidget:visible: + * + * Whether the widget is visible. Invisible widgets do not get + * realized or sized. + */ + g_object_class_install_property( + object_class, + PROP_VISIBLE, + g_param_spec_boolean("visible", "", "", TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtWidget:valid: + * + * Whether the widget's content is considered valid. Components + * determine their own validity. A container, by default, is + * considered valid if all of its children are valid. + */ + g_object_class_install_property( + object_class, + PROP_VALID, + g_param_spec_boolean("valid", "", "", TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); + /** + * NmtNewtWidget:exit-on-activate: + * + * If %TRUE, the widget will call nmt_newt_form_quit() on its form + * when it is activated. + */ + g_object_class_install_property( + object_class, + PROP_EXIT_ON_ACTIVATE, + g_param_spec_boolean("exit-on-activate", + "", + "", + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} diff --git a/src/libnmt-newt/nmt-newt-widget.h b/src/libnmt-newt/nmt-newt-widget.h new file mode 100644 index 0000000000..70b62d296f --- /dev/null +++ b/src/libnmt-newt/nmt-newt-widget.h @@ -0,0 +1,85 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Red Hat, Inc. + */ + +#ifndef NMT_NEWT_WIDGET_H +#define NMT_NEWT_WIDGET_H + +#include "nmt-newt-types.h" + +#define NMT_TYPE_NEWT_WIDGET (nmt_newt_widget_get_type()) +#define NMT_NEWT_WIDGET(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidget)) +#define NMT_NEWT_WIDGET_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass)) +#define NMT_IS_NEWT_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), NMT_TYPE_NEWT_WIDGET)) +#define NMT_IS_NEWT_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), NMT_TYPE_NEWT_WIDGET)) +#define NMT_NEWT_WIDGET_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj), NMT_TYPE_NEWT_WIDGET, NmtNewtWidgetClass)) + +struct _NmtNewtWidget { + GInitiallyUnowned parent; +}; + +typedef struct { + GInitiallyUnownedClass parent; + + /* signals */ + void (*needs_rebuild)(NmtNewtWidget *widget); + void (*activated)(NmtNewtWidget *widget); + + /* methods */ + void (*realize)(NmtNewtWidget *widget); + void (*unrealize)(NmtNewtWidget *widget); + + newtComponent *(*get_components)(NmtNewtWidget *widget); + NmtNewtWidget *(*find_component)(NmtNewtWidget *widget, newtComponent co); + + void (*size_request)(NmtNewtWidget *widget, int *width, int *height); + void (*size_allocate)(NmtNewtWidget *widget, int x, int y, int width, int height); + + newtComponent (*get_focus_component)(NmtNewtWidget *widget); + +} NmtNewtWidgetClass; + +GType nmt_newt_widget_get_type(void); + +void nmt_newt_widget_realize(NmtNewtWidget *widget); +void nmt_newt_widget_unrealize(NmtNewtWidget *widget); +gboolean nmt_newt_widget_get_realized(NmtNewtWidget *widget); + +newtComponent *nmt_newt_widget_get_components(NmtNewtWidget *widget); + +void nmt_newt_widget_set_padding(NmtNewtWidget *widget, + int pad_left, + int pad_top, + int pad_right, + int pad_bottom); + +void nmt_newt_widget_size_request(NmtNewtWidget *widget, int *width, int *height); +void nmt_newt_widget_size_allocate(NmtNewtWidget *widget, int x, int y, int width, int height); + +void nmt_newt_widget_set_parent(NmtNewtWidget *widget, NmtNewtWidget *parent); +NmtNewtWidget *nmt_newt_widget_get_parent(NmtNewtWidget *widget); + +NmtNewtForm *nmt_newt_widget_get_form(NmtNewtWidget *widget); + +gboolean nmt_newt_widget_get_visible(NmtNewtWidget *widget); +void nmt_newt_widget_set_visible(NmtNewtWidget *widget, gboolean visible); + +newtComponent nmt_newt_widget_get_focus_component(NmtNewtWidget *widget); + +void nmt_newt_widget_activated(NmtNewtWidget *widget); +gboolean nmt_newt_widget_get_exit_on_activate(NmtNewtWidget *widget); +void nmt_newt_widget_set_exit_on_activate(NmtNewtWidget *widget, gboolean exit_on_activate); + +gboolean nmt_newt_widget_get_valid(NmtNewtWidget *widget); + +NmtNewtWidget *nmt_newt_widget_find_component(NmtNewtWidget *widget, newtComponent co); + +/* protected */ +void nmt_newt_widget_needs_rebuild(NmtNewtWidget *widget); +void nmt_newt_widget_set_valid(NmtNewtWidget *widget, gboolean valid); + +#endif /* NMT_NEWT_WIDGET_H */ diff --git a/src/libnmt-newt/nmt-newt.h b/src/libnmt-newt/nmt-newt.h new file mode 100644 index 0000000000..984c65e2a4 --- /dev/null +++ b/src/libnmt-newt/nmt-newt.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 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 */ |