diff options
Diffstat (limited to 'src/nmtui/nmt-editor-grid.c')
| -rw-r--r-- | src/nmtui/nmt-editor-grid.c | 441 |
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; +} |