Files
NetworkManager/clients/tui/newt/nmt-newt-grid.c
Dan Winship 3d25d70461 clients: reorganize source tree, put all the installed clients together
Create a new clients/ subdirectory at the top level, and move cli/ and
tui/ into it, as well as nm-online.c (which was previously in test/,
which made no sense).

cli/ was split into two subdirectories, src/ and completion/. While
this does simplify things (given that the completion file and the
binary both need to be named "nmcli"), it bloats the source tree, and
we can work around it by just renaming the completion file at install
time. Then we can combine the two directories into one and just have
it all under clients/cli/.
2014-07-30 15:56:19 -04:00

473 lines
14 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2013 Red Hat, Inc.
*/
/**
* SECTION:nmt-newt-grid
* @short_description: Grid container
*
* #NmtNewtGrid is the most general-purpose container widget in NmtNewt.
*
* An #NmtNewtGrid consists of a number of rows and columns. There is
* no pre-established maximum row or columns. Rather, rows and columns
* exist if and only if there are widgets in them.
*
* The width of each column is the width of the widest widget in that
* column, and the height of each row is the height of the tallest
* widget in that row. Empty rows and empty columns take up no space,
* so a grid with a single widget at 0,0 would look exactly the same
* if the widget was at 5,10 instead.
*
* If a widget's cell ends up being larger than the widget's requested
* size, then by default the widget will be centered in its cell.
* However, this can be modified by changing its #NmtNewtGridFlags.
* FIXME: the FILL/ANCHOR flags can be implemented in #NmtNewtWidget
* and so should move there. Less clear about the EXPAND flags, which
* must be implemented by the container...
*/
#include "config.h"
#include <string.h>
#include "nmt-newt-grid.h"
G_DEFINE_TYPE (NmtNewtGrid, nmt_newt_grid, NMT_TYPE_NEWT_CONTAINER)
#define NMT_NEWT_GRID_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NMT_TYPE_NEWT_GRID, NmtNewtGridPrivate))
typedef struct {
NmtNewtWidget *widget;
int x, y;
NmtNewtGridFlags flags;
int req_height, req_width;
} NmtNewtGridChild;
typedef struct {
GArray *children;
int max_x, max_y;
int *row_heights, *col_widths;
gboolean *expand_rows, *expand_cols;
int n_expand_rows, n_expand_cols;
int req_height, req_width;
} NmtNewtGridPrivate;
/**
* nmt_newt_grid_new:
*
* Creates a new #NmtNewtGrid
*
* Returns: a new #NmtNewtGrid
*/
NmtNewtWidget *
nmt_newt_grid_new (void)
{
return g_object_new (NMT_TYPE_NEWT_GRID, NULL);
}
static void
nmt_newt_grid_init (NmtNewtGrid *grid)
{
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
priv->children = g_array_new (FALSE, FALSE, sizeof (NmtNewtGridChild));
}
static void
nmt_newt_grid_finalize (GObject *object)
{
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (object);
g_array_unref (priv->children);
g_clear_pointer (&priv->row_heights, g_free);
g_clear_pointer (&priv->col_widths, g_free);
g_clear_pointer (&priv->expand_rows, g_free);
g_clear_pointer (&priv->expand_cols, g_free);
G_OBJECT_CLASS (nmt_newt_grid_parent_class)->finalize (object);
}
static int
child_sort_func (gconstpointer a,
gconstpointer b)
{
NmtNewtGridChild *child_a = (NmtNewtGridChild *)a;
NmtNewtGridChild *child_b = (NmtNewtGridChild *)b;
if (child_a->y != child_b->y)
return child_a->y - child_b->y;
else
return child_a->x - child_b->x;
}
static newtComponent *
nmt_newt_grid_get_components (NmtNewtWidget *widget)
{
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (widget);
NmtNewtGridChild *children;
GPtrArray *cos;
newtComponent *child_cos;
int i, c;
g_array_sort (priv->children, child_sort_func);
children = (NmtNewtGridChild *)priv->children->data;
cos = g_ptr_array_new ();
for (i = 0; i < priv->children->len; i++) {
if (!nmt_newt_widget_get_visible (children[i].widget))
continue;
child_cos = nmt_newt_widget_get_components (children[i].widget);
for (c = 0; child_cos[c]; c++)
g_ptr_array_add (cos, child_cos[c]);
g_free (child_cos);
}
g_ptr_array_add (cos, NULL);
return (newtComponent *) g_ptr_array_free (cos, FALSE);
}
static void
nmt_newt_grid_size_request (NmtNewtWidget *widget,
int *width,
int *height)
{
NmtNewtGrid *grid = NMT_NEWT_GRID (widget);
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
int row, col, i;
g_free (priv->row_heights);
g_free (priv->col_widths);
g_free (priv->expand_rows);
g_free (priv->expand_cols);
priv->row_heights = g_new0 (int, priv->max_y + 1);
priv->col_widths = g_new0 (int, priv->max_x + 1);
priv->expand_rows = g_new0 (gboolean, priv->max_y + 1);
priv->expand_cols = g_new0 (gboolean, priv->max_x + 1);
priv->n_expand_rows = priv->n_expand_cols = 0;
for (row = 0; row < priv->max_y + 1; row++) {
for (col = 0; col < priv->max_x + 1; col++) {
for (i = 0; i < priv->children->len; i++) {
if (children[i].x != col || children[i].y != row)
continue;
if (!nmt_newt_widget_get_visible (children[i].widget))
continue;
nmt_newt_widget_size_request (children[i].widget,
&children[i].req_width,
&children[i].req_height);
if (children[i].req_height > priv->row_heights[row])
priv->row_heights[row] = children[i].req_height;
if (children[i].req_width > priv->col_widths[col])
priv->col_widths[col] = children[i].req_width;
if ( (children[i].flags & NMT_NEWT_GRID_EXPAND_X)
&& !priv->expand_cols[children[i].x]) {
priv->expand_cols[children[i].x] = TRUE;
priv->n_expand_cols++;
}
if ( (children[i].flags & NMT_NEWT_GRID_EXPAND_Y)
&& !priv->expand_rows[children[i].y]) {
priv->expand_rows[children[i].y] = TRUE;
priv->n_expand_rows++;
}
}
}
}
priv->req_height = priv->req_width = 0;
for (row = 0; row < priv->max_y + 1; row++)
priv->req_height += priv->row_heights[row];
for (col = 0; col < priv->max_x + 1; col++)
priv->req_width += priv->col_widths[col];
*height = priv->req_height;
*width = priv->req_width;
}
static void
nmt_newt_grid_size_allocate (NmtNewtWidget *widget,
int x,
int y,
int width,
int height)
{
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (widget);
NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data, *child;
int i, row, col;
int child_x, child_y, child_width, child_height;
int extra, extra_all, extra_some;
extra = width - priv->req_width;
if (extra > 0 && priv->n_expand_cols) {
extra_all = extra / priv->n_expand_cols;
extra_some = extra % priv->n_expand_cols;
for (col = 0; col < priv->max_x + 1; col++) {
if (!priv->expand_cols[col])
continue;
priv->col_widths[col] += extra_all;
if (extra_some) {
priv->col_widths[col]++;
extra_some--;
}
}
}
extra = height - priv->req_height;
if (extra > 0 && priv->n_expand_rows) {
extra_all = extra / priv->n_expand_rows;
extra_some = extra % priv->n_expand_rows;
for (row = 0; row < priv->max_y + 1; row++) {
if (!priv->expand_rows[row])
continue;
priv->row_heights[row] += extra_all;
if (extra_some) {
priv->row_heights[row]++;
extra_some--;
}
}
}
for (i = 0; i < priv->children->len; i++) {
child = &children[i];
if (!nmt_newt_widget_get_visible (child->widget))
continue;
child_x = x;
for (col = 0; col < child->x; col++)
child_x += priv->col_widths[col];
if ((child->flags & NMT_NEWT_GRID_FILL_X) == NMT_NEWT_GRID_FILL_X) {
child_width = priv->col_widths[child->x];
} else {
child_width = child->req_width;
if (child->flags & NMT_NEWT_GRID_ANCHOR_RIGHT)
child_x += priv->col_widths[child->x] - child->req_width;
else if (!(child->flags & NMT_NEWT_GRID_ANCHOR_LEFT))
child_x += (priv->col_widths[child->x] - child->req_width) / 2;
}
child_y = y;
for (row = 0; row < child->y; row++)
child_y += priv->row_heights[row];
if ((child->flags & NMT_NEWT_GRID_FILL_Y) == NMT_NEWT_GRID_FILL_Y) {
child_height = priv->row_heights[child->y];
} else {
child_height = child->req_height;
if (child->flags & NMT_NEWT_GRID_ANCHOR_BOTTOM)
child_y += priv->row_heights[child->y] - child->req_height;
else if (!(child->flags & NMT_NEWT_GRID_ANCHOR_TOP))
child_y += (priv->row_heights[child->y] - child->req_height) / 2;
}
nmt_newt_widget_size_allocate (child->widget,
child_x, child_y,
child_width, child_height);
}
}
static void
nmt_newt_grid_find_size (NmtNewtGrid *grid)
{
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
int i;
priv->max_x = priv->max_y = 0;
for (i = 0; i < priv->children->len; i++) {
if (children[i].x > priv->max_x)
priv->max_x = children[i].x;
if (children[i].y > priv->max_y)
priv->max_y = children[i].y;
}
}
/**
* nmt_newt_grid_add:
* @grid: an #NmtNewtGrid
* @widget: the widget to add
* @x: x coordinate
* @y: y coordinate
*
* Adds @widget to @grid at @x, @y. See the discussion above for more
* details of exactly how this works.
*/
void
nmt_newt_grid_add (NmtNewtGrid *grid,
NmtNewtWidget *widget,
int x,
int y)
{
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
NmtNewtGridChild child;
NMT_NEWT_CONTAINER_CLASS (nmt_newt_grid_parent_class)->add (NMT_NEWT_CONTAINER (grid), widget);
memset (&child, 0, sizeof (child));
child.widget = widget;
child.x = x;
child.y = y;
child.flags = NMT_NEWT_GRID_FILL_X | NMT_NEWT_GRID_FILL_Y;
g_array_append_val (priv->children, child);
if (x > priv->max_x)
priv->max_x = x;
if (y > priv->max_y)
priv->max_y = y;
}
static int
find_child (NmtNewtGrid *grid,
NmtNewtWidget *widget)
{
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
int i;
for (i = 0; i < priv->children->len; i++) {
if (children[i].widget == widget)
return i;
}
return -1;
}
static void
nmt_newt_grid_remove (NmtNewtContainer *container,
NmtNewtWidget *widget)
{
NmtNewtGrid *grid = NMT_NEWT_GRID (container);
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
int i;
i = find_child (grid, widget);
if (i != -1) {
g_array_remove_index (priv->children, i);
nmt_newt_grid_find_size (grid);
}
NMT_NEWT_CONTAINER_CLASS (nmt_newt_grid_parent_class)->remove (container, widget);
}
/**
* nmt_newt_grid_move:
* @grid: an #NmtNewtGrid
* @widget: a child of @grid
* @x: x coordinate
* @y: y coordinate
*
* Moves @widget to the given new coordinates.
*/
void
nmt_newt_grid_move (NmtNewtGrid *grid,
NmtNewtWidget *widget,
int x,
int y)
{
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
int i;
i = find_child (grid, widget);
if (i != -1 && (children[i].x != x || children[i].y != y)) {
children[i].x = x;
children[i].y = y;
nmt_newt_grid_find_size (grid);
nmt_newt_widget_needs_rebuild (NMT_NEWT_WIDGET (grid));
}
}
/**
* NmtNewtGridFlags:
* @NMT_NEWT_GRID_EXPAND_X: The widget's cell should expand
* horizontally if the grid as a whole is given more width than
* it requested.
* @NMT_NEWT_GRID_EXPAND_Y: The widget's cell should expand
* vertically if the grid as a whole is given more height than
* it requested.
* @NMT_NEWT_GRID_ANCHOR_LEFT: If the widget's cell is wider than
* the widget requested, the widget should be anchored to the
* left of its cell rather than being centered.
* @NMT_NEWT_GRID_ANCHOR_RIGHT: If the widget's cell is wider than
* the widget requested, the widget should be anchored to the
* right of its cell rather than being centered.
* @NMT_NEWT_GRID_FILL_X: If the widget's cell is wider than
* the widget requested, the widget should be allocated the
* full width of the cell; this is equivalent to specifying
* both %NMT_NEWT_GRID_ANCHOR_LEFT and %NMT_NEWT_GRID_ANCHOR_RIGHT.
* @NMT_NEWT_GRID_ANCHOR_TOP: If the widget's cell is taller than
* the widget requested, the widget should be anchored to the
* top of its cell rather than being centered.
* @NMT_NEWT_GRID_ANCHOR_BOTTOM: If the widget's cell is taller than
* the widget requested, the widget should be anchored to the
* bottom of its cell rather than being centered.
* @NMT_NEWT_GRID_FILL_Y: If the widget's cell is taller than
* the widget requested, the widget should be allocated the
* full height of the cell; this is equivalent to specifying
* both %NMT_NEWT_GRID_ANCHOR_TOP and %NMT_NEWT_GRID_ANCHOR_BOTTOM.
*
* Flags describing how a widget is placed within its grid cell.
*/
/**
* nmt_newt_grid_set_flags:
* @grid: an #NmtNewtGrid
* @widget: a child of @grid
* @flags: #NmtNewtGridFlags for @widget
*
* Sets the #NmtNewtGridFlags on @widget
*/
void
nmt_newt_grid_set_flags (NmtNewtGrid *grid,
NmtNewtWidget *widget,
NmtNewtGridFlags flags)
{
NmtNewtGridPrivate *priv = NMT_NEWT_GRID_GET_PRIVATE (grid);
NmtNewtGridChild *children = (NmtNewtGridChild *)priv->children->data;
int i;
i = find_child (grid, widget);
if (i != -1)
children[i].flags = flags;
}
static void
nmt_newt_grid_class_init (NmtNewtGridClass *grid_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (grid_class);
NmtNewtWidgetClass *widget_class = NMT_NEWT_WIDGET_CLASS (grid_class);
NmtNewtContainerClass *container_class = NMT_NEWT_CONTAINER_CLASS (grid_class);
g_type_class_add_private (grid_class, sizeof (NmtNewtGridPrivate));
/* virtual methods */
object_class->finalize = nmt_newt_grid_finalize;
widget_class->get_components = nmt_newt_grid_get_components;
widget_class->size_request = nmt_newt_grid_size_request;
widget_class->size_allocate = nmt_newt_grid_size_allocate;
container_class->remove = nmt_newt_grid_remove;
}