summary refs log tree commit diff
path: root/src/nmtui/nmt-editor-grid.c
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
committerMichael Biebl <biebl@debian.org>2021-10-01 23:05:04 +0200
commite74c568b07b50b97873fb4ee1d776dedefbd54d6 (patch)
tree3469f17ea9af91f7ff169b890633bda68b0cf76e /src/nmtui/nmt-editor-grid.c
parentbfe522304da217296e2a61040f58e35ec5d6f3f2 (diff)
New upstream version 1.32.12 upstream/1.32.12
Diffstat (limited to 'src/nmtui/nmt-editor-grid.c')
-rw-r--r--src/nmtui/nmt-editor-grid.c441
1 files changed, 441 insertions, 0 deletions
diff --git a/src/nmtui/nmt-editor-grid.c b/src/nmtui/nmt-editor-grid.c
new file mode 100644
index 00000000..8c79e8d9
--- /dev/null
+++ b/src/nmtui/nmt-editor-grid.c
@@ -0,0 +1,441 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2013 Red Hat, Inc.
+ */
+
+/**
+ * SECTION:nmt-editor-grid
+ * @short_description: Grid widget for #NmtEditorPages
+ *
+ * #NmtEditorGrid is the layout grid used by #NmtEditorPages. It
+ * consists of a number of rows, each containing either a single
+ * widget that spans the entire width of the row, or else containing a
+ * label, a widget, and an optional extra widget.
+ *
+ * Each row of the grid can take up multiple on-screen rows, if
+ * its main widget is multiple rows high. The label and extra widgets
+ * will be top-aligned if the row is taller than they are.
+ *
+ * The #NmtEditorGrids in a form behave as though they are all in a
+ * "size group" together; they will all use the same column widths,
+ * which will be wide enough for the widest labels/widgets in any of
+ * the grids. #NmtEditorGrid is also specially aware of #NmtNewtSection,
+ * and grids inside sections will automatically take the size of the
+ * section border into account as well.
+ */
+
+#include "libnm-client-aux-extern/nm-default-client.h"
+
+#include "nmt-editor-grid.h"
+
+G_DEFINE_TYPE(NmtEditorGrid, nmt_editor_grid, NMT_TYPE_NEWT_CONTAINER)
+
+#define NMT_EDITOR_GRID_GET_PRIVATE(o) \
+    (G_TYPE_INSTANCE_GET_PRIVATE((o), NMT_TYPE_EDITOR_GRID, NmtEditorGridPrivate))
+
+typedef struct {
+    GArray *rows;
+    int *   row_heights;
+    int     indent;
+} NmtEditorGridPrivate;
+
+typedef struct {
+    NmtNewtWidget *       label;
+    NmtNewtWidget *       widget;
+    NmtNewtWidget *       extra;
+    NmtEditorGridRowFlags flags;
+} NmtEditorGridRow;
+
+typedef struct {
+    int col_widths[3];
+} NmtEditorGridFormState;
+
+/**
+ * nmt_editor_grid_new:
+ *
+ * Creates a new #NmtEditorGrid
+ *
+ * Returns: a new #NmtEditorGrid
+ */
+NmtNewtWidget *
+nmt_editor_grid_new(void)
+{
+    return g_object_new(NMT_TYPE_EDITOR_GRID, NULL);
+}
+
+static void
+nmt_editor_grid_init(NmtEditorGrid *grid)
+{
+    NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(grid);
+
+    priv->rows = g_array_new(FALSE, TRUE, sizeof(NmtEditorGridRow));
+}
+
+static void
+nmt_editor_grid_finalize(GObject *object)
+{
+    NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(object);
+
+    g_array_unref(priv->rows);
+    nm_clear_g_free(&priv->row_heights);
+
+    G_OBJECT_CLASS(nmt_editor_grid_parent_class)->finalize(object);
+}
+
+/**
+ * nmt_editor_grid_append:
+ * @grid: the #NmtEditorGrid
+ * @label: (allow-none): the label text for @widget, or %NULL
+ * @widget: (allow-none): the (main) widget
+ * @extra: (allow-none): optional extra widget
+ *
+ * Adds a row to @grid.
+ *
+ * If @label and @widget are both non-%NULL, this will add a three-column row,
+ * containing a right-aligned #NmtNewtLabel in the first column, @widget in the
+ * second column, and @extra (if non-%NULL) in the third column.
+ *
+ * If either @label or @widget is %NULL, then the other column will expand into
+ * it.
+ *
+ * See also nmt_editor_grid_set_row_flags().
+ */
+void
+nmt_editor_grid_append(NmtEditorGrid *grid,
+                       const char *   label,
+                       NmtNewtWidget *widget,
+                       NmtNewtWidget *extra)
+{
+    NmtEditorGridPrivate * priv         = NMT_EDITOR_GRID_GET_PRIVATE(grid);
+    NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS(nmt_editor_grid_parent_class);
+    NmtNewtContainer *     container    = NMT_NEWT_CONTAINER(grid);
+    NmtEditorGridRow       row;
+
+    g_return_if_fail(label != NULL || widget != NULL);
+
+    memset(&row, 0, sizeof(row));
+
+    if (label && !widget) {
+        widget = nmt_newt_label_new(label);
+        label  = NULL;
+    }
+
+    if (label) {
+        row.label = nmt_newt_label_new(label);
+        parent_class->add(container, row.label);
+    }
+
+    row.widget = widget;
+    parent_class->add(container, widget);
+    if (row.label) {
+        g_object_bind_property(row.widget,
+                               "valid",
+                               row.label,
+                               "highlight",
+                               G_BINDING_INVERT_BOOLEAN | G_BINDING_SYNC_CREATE);
+    }
+
+    if (extra) {
+        row.extra = extra;
+        parent_class->add(container, extra);
+    }
+
+    g_array_append_val(priv->rows, row);
+}
+
+static int
+nmt_editor_grid_find_widget(NmtEditorGrid *grid, NmtNewtWidget *widget)
+{
+    NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(grid);
+    NmtEditorGridRow *    rows = (NmtEditorGridRow *) priv->rows->data;
+    int                   i;
+
+    for (i = 0; i < priv->rows->len; i++) {
+        if (rows[i].label == widget || rows[i].widget == widget || rows[i].extra == widget)
+            return i;
+    }
+
+    return -1;
+}
+
+/**
+ * NmtEditorGridRowFlags:
+ * @NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT: the row's label should be
+ *   aligned left instead of right.
+ * @NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT: the row's extra widget
+ *   should be aligned right instead of left.
+ *
+ * Flags to alter an #NmtEditorGrid row's layout.
+ */
+
+/**
+ * nmt_editor_grid_set_row_flags:
+ * @grid: an #NmtEditorGrid
+ * @widget: the widget whose row you want to adjust
+ * @flags: the flags to set
+ *
+ * Sets flags to adjust the layout of @widget's row in @grid.
+ */
+void
+nmt_editor_grid_set_row_flags(NmtEditorGrid *       grid,
+                              NmtNewtWidget *       widget,
+                              NmtEditorGridRowFlags flags)
+{
+    NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(grid);
+    NmtEditorGridRow *    rows = (NmtEditorGridRow *) priv->rows->data;
+    int                   i;
+
+    i = nmt_editor_grid_find_widget(grid, widget);
+    if (i != -1)
+        rows[i].flags = flags;
+}
+
+static void
+nmt_editor_grid_remove(NmtNewtContainer *container, NmtNewtWidget *widget)
+{
+    NmtEditorGrid *        grid         = NMT_EDITOR_GRID(container);
+    NmtEditorGridPrivate * priv         = NMT_EDITOR_GRID_GET_PRIVATE(grid);
+    NmtNewtContainerClass *parent_class = NMT_NEWT_CONTAINER_CLASS(nmt_editor_grid_parent_class);
+    NmtEditorGridRow *     rows         = (NmtEditorGridRow *) priv->rows->data;
+    int                    i;
+
+    i = nmt_editor_grid_find_widget(grid, widget);
+    if (i != -1) {
+        if (rows[i].label)
+            parent_class->remove(container, rows[i].label);
+        parent_class->remove(container, rows[i].widget);
+        if (rows[i].extra)
+            parent_class->remove(container, rows[i].extra);
+
+        g_array_remove_index(priv->rows, i);
+        return;
+    }
+
+    // FIXME: shouldn't happen
+    parent_class->remove(container, widget);
+}
+
+static newtComponent *
+nmt_editor_grid_get_components(NmtNewtWidget *widget)
+{
+    NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(widget);
+    NmtEditorGridRow *    rows = (NmtEditorGridRow *) priv->rows->data;
+    newtComponent *       child_cos;
+    GPtrArray *           cos;
+    int                   i, c;
+
+    cos = g_ptr_array_new();
+
+    for (i = 0; i < priv->rows->len; i++) {
+        if (!nmt_newt_widget_get_visible(rows[i].widget))
+            continue;
+
+        if (rows[i].label) {
+            child_cos = nmt_newt_widget_get_components(rows[i].label);
+            g_assert(child_cos[0] && !child_cos[1]);
+            g_ptr_array_add(cos, child_cos[0]);
+            g_free(child_cos);
+        }
+
+        child_cos = nmt_newt_widget_get_components(rows[i].widget);
+        for (c = 0; child_cos[c]; c++)
+            g_ptr_array_add(cos, child_cos[c]);
+        g_free(child_cos);
+
+        if (rows[i].extra) {
+            child_cos = nmt_newt_widget_get_components(rows[i].extra);
+            for (c = 0; child_cos[c]; c++)
+                g_ptr_array_add(cos, child_cos[c]);
+            g_free(child_cos);
+        }
+    }
+
+    g_ptr_array_add(cos, NULL);
+    return (newtComponent *) g_ptr_array_free(cos, FALSE);
+}
+
+static NmtEditorGridFormState *
+get_form_state(NmtNewtWidget *widget)
+{
+    NmtNewtForm *           form = nmt_newt_widget_get_form(widget);
+    NmtEditorGridFormState *state;
+
+    if (!form)
+        return NULL;
+
+    state = g_object_get_data(G_OBJECT(form), "NmtEditorGridFormState");
+    if (state)
+        return state;
+
+    state = g_new0(NmtEditorGridFormState, 1);
+    g_object_set_data_full(G_OBJECT(form), "NmtEditorGridFormState", state, g_free);
+    return state;
+}
+
+static void
+nmt_editor_grid_realize(NmtNewtWidget *widget)
+{
+    NmtEditorGridPrivate *priv = NMT_EDITOR_GRID_GET_PRIVATE(widget);
+    NmtNewtWidget *       parent;
+
+    NMT_NEWT_WIDGET_CLASS(nmt_editor_grid_parent_class)->realize(widget);
+
+    /* This is a hack, but it's the simplest way to make it work... */
+    priv->indent = 0;
+
+    parent = nmt_newt_widget_get_parent(widget);
+    while (parent) {
+        if (NMT_IS_NEWT_SECTION(parent)) {
+            priv->indent = 2;
+            break;
+        }
+        parent = nmt_newt_widget_get_parent(parent);
+    }
+}
+
+static void
+nmt_editor_grid_unrealize(NmtNewtWidget *widget)
+{
+    NmtEditorGridFormState *state = get_form_state(widget);
+
+    if (state)
+        memset(state->col_widths, 0, sizeof(state->col_widths));
+
+    NMT_NEWT_WIDGET_CLASS(nmt_editor_grid_parent_class)->unrealize(widget);
+}
+
+static void
+nmt_editor_grid_size_request(NmtNewtWidget *widget, int *width, int *height)
+{
+    NmtEditorGridPrivate *  priv        = NMT_EDITOR_GRID_GET_PRIVATE(widget);
+    NmtEditorGridRow *      rows        = (NmtEditorGridRow *) priv->rows->data;
+    NmtEditorGridFormState *state       = get_form_state(widget);
+    gboolean                add_padding = FALSE;
+    int                     i;
+
+    g_free(priv->row_heights);
+    priv->row_heights = g_new0(int, priv->rows->len);
+
+    *height = 0;
+    for (i = 0; i < priv->rows->len; i++) {
+        int lwidth, lheight, wwidth, wheight, ewidth, eheight;
+
+        if (!nmt_newt_widget_get_visible(rows[i].widget))
+            continue;
+
+        if (rows[i].label) {
+            nmt_newt_widget_size_request(rows[i].label, &lwidth, &lheight);
+            lwidth += priv->indent;
+            state->col_widths[0] = MAX(state->col_widths[0], lwidth);
+
+            nmt_newt_widget_size_request(rows[i].widget, &wwidth, &wheight);
+            state->col_widths[1] = MAX(state->col_widths[1], wwidth);
+            priv->row_heights[i] = wheight;
+
+            add_padding = TRUE;
+        } else {
+            nmt_newt_widget_size_request(rows[i].widget, &wwidth, &wheight);
+            priv->row_heights[i] = wheight;
+        }
+
+        if (rows[i].extra) {
+            nmt_newt_widget_size_request(rows[i].extra, &ewidth, &eheight);
+            state->col_widths[2] = MAX(state->col_widths[2], ewidth);
+            priv->row_heights[i] = MAX(priv->row_heights[i], eheight);
+        }
+
+        *height += priv->row_heights[i];
+    }
+
+    *width = state->col_widths[0] + state->col_widths[1] + state->col_widths[2];
+    if (add_padding)
+        *width += 2;
+}
+
+static void
+nmt_editor_grid_size_allocate(NmtNewtWidget *widget, int x, int y, int width, int height)
+{
+    NmtEditorGridPrivate *  priv  = NMT_EDITOR_GRID_GET_PRIVATE(widget);
+    NmtEditorGridRow *      rows  = (NmtEditorGridRow *) priv->rows->data;
+    NmtEditorGridFormState *state = get_form_state(widget);
+    int                     col0_width, col1_width, col2_width;
+    int                     i, row;
+
+    col0_width = state->col_widths[0] - priv->indent;
+    col1_width = state->col_widths[1];
+    col2_width = state->col_widths[2];
+
+    for (i = row = 0; i < priv->rows->len; i++) {
+        if (!nmt_newt_widget_get_visible(rows[i].widget))
+            continue;
+
+        if (rows[i].label) {
+            int lwidth, lheight, lx;
+
+            if (rows[i].flags & NMT_EDITOR_GRID_ROW_LABEL_ALIGN_LEFT)
+                lx = x;
+            else {
+                nmt_newt_widget_size_request(rows[i].label, &lwidth, &lheight);
+                lx = x + col0_width - lwidth;
+            }
+
+            nmt_newt_widget_size_allocate(rows[i].label,
+                                          lx,
+                                          y + row,
+                                          col0_width,
+                                          priv->row_heights[i]);
+
+            nmt_newt_widget_size_allocate(rows[i].widget,
+                                          x + col0_width + 1,
+                                          y + row,
+                                          col1_width,
+                                          priv->row_heights[i]);
+        } else {
+            nmt_newt_widget_size_allocate(rows[i].widget,
+                                          x,
+                                          y + row,
+                                          col0_width + col1_width + 1,
+                                          priv->row_heights[i]);
+        }
+
+        if (rows[i].extra) {
+            int wwidth, wheight, ex;
+
+            if (rows[i].flags & NMT_EDITOR_GRID_ROW_EXTRA_ALIGN_RIGHT)
+                ex = x + col0_width + col1_width + 2;
+            else {
+                nmt_newt_widget_size_request(rows[i].widget, &wwidth, &wheight);
+                ex = x + col0_width + wwidth + 2;
+            }
+
+            nmt_newt_widget_size_allocate(rows[i].extra,
+                                          ex,
+                                          y + row,
+                                          col2_width,
+                                          priv->row_heights[i]);
+        }
+
+        row += priv->row_heights[i];
+    }
+}
+
+static void
+nmt_editor_grid_class_init(NmtEditorGridClass *grid_class)
+{
+    GObjectClass *         object_class    = G_OBJECT_CLASS(grid_class);
+    NmtNewtWidgetClass *   widget_class    = NMT_NEWT_WIDGET_CLASS(grid_class);
+    NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS(grid_class);
+
+    g_type_class_add_private(grid_class, sizeof(NmtEditorGridPrivate));
+
+    /* virtual methods */
+    object_class->finalize = nmt_editor_grid_finalize;
+
+    widget_class->realize        = nmt_editor_grid_realize;
+    widget_class->unrealize      = nmt_editor_grid_unrealize;
+    widget_class->get_components = nmt_editor_grid_get_components;
+    widget_class->size_request   = nmt_editor_grid_size_request;
+    widget_class->size_allocate  = nmt_editor_grid_size_allocate;
+
+    container_class->remove = nmt_editor_grid_remove;
+}