X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fgui%2Fsheet%2Fpsppire-axis.c;h=9cc879dc124c789333b6b7ed721a3d2e556bb5a0;hb=e4ef4d0f708651807d91bd92d7ab92c1b5c6d675;hp=3aeea7c48c7ae15998af438323480733d4703e25;hpb=ffce2432a76f3ffbe2a19228d3b3d03613c3b4a3;p=pspp-builds.git diff --git a/src/ui/gui/sheet/psppire-axis.c b/src/ui/gui/sheet/psppire-axis.c index 3aeea7c4..9cc879dc 100644 --- a/src/ui/gui/sheet/psppire-axis.c +++ b/src/ui/gui/sheet/psppire-axis.c @@ -1,5 +1,5 @@ /* PSPPIRE - a graphical user interface for PSPP. - Copyright (C) 2008 Free Software Foundation + Copyright (C) 2008, 2009, 2010 Free Software Foundation 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 @@ -18,13 +18,12 @@ #include #include +#include #include #include -#include #include "psppire-axis.h" -#include -#include - +#include +#include /* Signals */ @@ -36,58 +35,212 @@ enum static guint signals[n_signals] ; +/* --- prototypes --- */ +static void psppire_axis_class_init (PsppireAxisClass *class); +static void psppire_axis_init (PsppireAxis *axis); +static void psppire_axis_finalize (GObject *object); -#define PSPPIRE_AXIS_GET_IFACE(obj) \ - (G_TYPE_INSTANCE_GET_INTERFACE ((obj), PSPPIRE_TYPE_AXIS_IFACE, PsppireAxisIface)) -GType -psppire_axis_iface_get_type (void) +/* --- variables --- */ +static GObjectClass *parent_class = NULL; + + +struct axis_node +{ + struct tower_node pixel_node; + struct tower_node unit_node; +}; + +void +psppire_axis_dump (const PsppireAxis *a) { - static GType psppire_axis_iface_type = 0; + struct tower_node *n = tower_first (&a->unit_tower); - if (! psppire_axis_iface_type) + g_debug ("Axis %p", a); + while (n) { - static const GTypeInfo psppire_axis_iface_info = - { - sizeof (PsppireAxisIface), /* class_size */ - NULL, /* base init */ - NULL, /* base_finalize */ - NULL, - NULL, /* class_finalize */ - NULL, /* class_data */ - 0, - 0, /* n_preallocs */ - NULL - }; + const struct axis_node *an = tower_data (n, struct axis_node, unit_node); + const struct tower_node *pn = &an->pixel_node; + g_debug ("%ld units of height %g", + n->size, pn->size / (gdouble) n->size); - psppire_axis_iface_type = - g_type_register_static (G_TYPE_INTERFACE, "PsppireAxisIface", - &psppire_axis_iface_info, 0); + n = tower_next (&a->unit_tower, n); } + g_debug ("\n"); +} + +/* Increment the size of every unit by INC. + Note that INC is signed. So if INC is negative, + then size will end up smaller. +*/ +static void +axis_increment (PsppireAxis *axis, gint inc) +{ + struct tower_node *n = tower_first (&axis->pixel_tower); - return psppire_axis_iface_type; + while (n) + { + struct axis_node *an = tower_data (n, struct axis_node, pixel_node); + struct tower_node *pn = &an->pixel_node; + const gint existing_size = tower_node_get_size (pn); + + tower_resize (&axis->pixel_tower, pn, existing_size + inc * + tower_node_get_size (&an->unit_node)); + + n = tower_next (&axis->pixel_tower, n); + } } -G_DEFINE_ABSTRACT_TYPE(PsppireAxis, psppire_axis, G_TYPE_OBJECT); +/* Return the unit covered by PIXEL */ +gint +psppire_axis_unit_at_pixel (const PsppireAxis *a, glong pixel) +{ + unsigned long int start; + struct tower_node *n; + struct axis_node *an; + gdouble fraction; + glong size = tower_height (&a->pixel_tower); -/* --- prototypes --- */ -static void psppire_axis_class_init (PsppireAxisClass *class); -static void psppire_axis_init (PsppireAxis *axis); -static void psppire_axis_finalize (GObject *object); + g_return_val_if_fail (pixel >= 0, -1); + if (pixel >= size) + { + gint n_items = tower_height (&a->unit_tower); + glong extra = pixel - size; -/* --- variables --- */ -static GObjectClass *parent_class = NULL; + return n_items - 1 + DIV_RND_UP (extra, a->default_size); + } + + + n = tower_lookup (&a->pixel_tower, pixel, &start); + an = tower_data (n, struct axis_node, pixel_node); + + fraction = (pixel - start) / (gdouble) tower_node_get_size (&an->pixel_node); + + return tower_node_get_level (&an->unit_node) + + fraction * tower_node_get_size (&an->unit_node); +} + + +gint +psppire_axis_unit_count (const PsppireAxis *a) +{ + glong filler = 0; + glong actual_size; + + actual_size = tower_height (&a->pixel_tower); + + if ( actual_size < a->min_extent ) + filler = DIV_RND_UP (a->min_extent - actual_size, a->default_size); + + return tower_height (&a->unit_tower) + filler; +} + + +/* Return the starting pixel of UNIT */ +glong +psppire_axis_start_pixel (const PsppireAxis *a, gint unit) +{ + gdouble fraction; + struct tower_node *n ; + struct axis_node *an; + + unsigned long int start; + + gint the_count, size ; + + the_count = tower_height (&a->unit_tower); + size = tower_height (&a->pixel_tower); + + if ( unit >= the_count) + { + return size + (unit - the_count) * a->default_size; + } + + if ( unit < 0) + return -1; + + if ( unit >= tower_height (&a->unit_tower)) + return -1; + + n = tower_lookup (&a->unit_tower, unit, &start); + + an = tower_data (n, struct axis_node, unit_node); + + fraction = (unit - start) / (gdouble) tower_node_get_size (&an->unit_node); + + return tower_node_get_level (&an->pixel_node) + + rint (fraction * tower_node_get_size (&an->pixel_node)); +} + +gint +psppire_axis_unit_size (const PsppireAxis *axis, gint unit) +{ + struct tower_node *n ; + struct axis_node *an; + + unsigned long int start; + + if (unit >= tower_height (&axis->unit_tower)) + return axis->default_size; + + if ( unit < 0) + return 0; + + if ( unit >= tower_height (&axis->unit_tower)) + return 0; + + n = tower_lookup (&axis->unit_tower, unit, &start); + + an = tower_data (n, struct axis_node, unit_node); + + return rint (tower_node_get_size (&an->pixel_node) + / (gdouble) tower_node_get_size (&an->unit_node)); +} + + + +/* --- functions --- */ +/** + * psppire_axis_get_type: + * @returns: the type ID for accelerator groups. + */ +GType +psppire_axis_get_type (void) +{ + static GType object_type = 0; + + if (!object_type) + { + static const GTypeInfo object_info = { + sizeof (PsppireAxisClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) psppire_axis_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (PsppireAxis), + 0, /* n_preallocs */ + (GInstanceInitFunc) psppire_axis_init, + }; + + object_type = g_type_register_static (G_TYPE_OBJECT, + "PsppireAxis", + &object_info, 0); + } + return object_type; +} enum { PROP_0, PROP_MIN_EXTENT, - PROP_DEFAULT_SIZE + PROP_DEFAULT_SIZE, + PROP_PADDING }; @@ -101,6 +254,9 @@ psppire_axis_get_property (GObject *object, switch (prop_id) { + case PROP_PADDING: + g_value_set_int (value, axis->padding); + break; case PROP_MIN_EXTENT: g_value_set_long (value, axis->min_extent); break; @@ -124,6 +280,13 @@ psppire_axis_set_property (GObject *object, switch (prop_id) { + case PROP_PADDING: + { + const gint old_value = axis->padding; + axis->padding = g_value_get_int (value); + axis_increment (axis, axis->padding - old_value); + } + break; case PROP_MIN_EXTENT: axis->min_extent = g_value_get_long (value); break; @@ -136,16 +299,22 @@ psppire_axis_set_property (GObject *object, }; } + static void psppire_axis_class_init (PsppireAxisClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); + + GParamSpec *padding_spec; GParamSpec *min_extent_spec; GParamSpec *default_size_spec; + parent_class = g_type_class_peek_parent (class); + object_class->set_property = psppire_axis_set_property; object_class->get_property = psppire_axis_get_property; + min_extent_spec = g_param_spec_long ("minimum-extent", "Minimum Extent", @@ -172,9 +341,19 @@ psppire_axis_class_init (PsppireAxisClass *class) PROP_DEFAULT_SIZE, default_size_spec); - parent_class = g_type_class_peek_parent (class); + padding_spec = + g_param_spec_int ("padding", + "Padding", + "Extra space implicitly added to each unit", + 0, G_MAXINT, + 0, + G_PARAM_CONSTRUCT | G_PARAM_WRITABLE | G_PARAM_READABLE ); + + + g_object_class_install_property (object_class, + PROP_PADDING, + padding_spec); - object_class->finalize = psppire_axis_finalize; signals[RESIZE_UNIT] = @@ -189,113 +368,267 @@ psppire_axis_class_init (PsppireAxisClass *class) G_TYPE_INT, G_TYPE_LONG ); + + + object_class->finalize = psppire_axis_finalize; } static void psppire_axis_init (PsppireAxis *axis) { + tower_init (&axis->pixel_tower); + tower_init (&axis->unit_tower); + + axis->pool = pool_create (); + axis->padding = 0; } static void psppire_axis_finalize (GObject *object) { + PsppireAxis *a = PSPPIRE_AXIS (object); + pool_destroy (a->pool); + G_OBJECT_CLASS (parent_class)->finalize (object); } -gint -psppire_axis_unit_size (const PsppireAxis *a, gint unit) +/** + * psppire_axis_new: + * @returns: a new #PsppireAxis object + * + * Creates a new #PsppireAxis. + */ +PsppireAxis* +psppire_axis_new (void) +{ + return g_object_new (G_TYPE_PSPPIRE_AXIS, NULL); +} + + + + +void +psppire_axis_append (PsppireAxis *a, gint size) { - g_return_val_if_fail (PSPPIRE_IS_AXIS (a), -1); + psppire_axis_append_n (a, 1, size); +} + - g_return_val_if_fail (PSPPIRE_AXIS_GET_IFACE (a)->unit_size, -1); +/* Append N_UNITS of size SIZE to A. + The value of the "padding" property will be added to SIZE + unit before appending. +*/ +void +psppire_axis_append_n (PsppireAxis *a, gint n_units, gint size) +{ + struct axis_node *node; + if (n_units == 0) + return; - if (unit >= PSPPIRE_AXIS_GET_IFACE (a)->unit_count(a)) - return a->default_size; + node = pool_malloc (a->pool, sizeof *node); - return PSPPIRE_AXIS_GET_IFACE (a)->unit_size (a, unit); + tower_insert (&a->unit_tower, n_units, &node->unit_node, NULL); + tower_insert (&a->pixel_tower, (size + a->padding) * n_units, + &node->pixel_node, NULL); } -gint -psppire_axis_unit_count (const PsppireAxis *a) + +/* Split the node of both towers at POSN */ +static void +split (PsppireAxis *a, gint posn) { - glong padding = 0; - glong actual_size; + unsigned long int existing_unit_size; + unsigned long int existing_pixel_size; + unsigned long int start; + gdouble fraction; + struct axis_node *new_node ; + struct tower_node *n; + struct axis_node *existing_node; - g_return_val_if_fail (PSPPIRE_IS_AXIS (a), -1); - g_return_val_if_fail (PSPPIRE_AXIS_GET_IFACE (a)->unit_count, -1); + g_return_if_fail (posn <= tower_height (&a->unit_tower)); - actual_size = PSPPIRE_AXIS_GET_IFACE (a)->total_size (a); + /* Nothing needs to be done */ + if ( posn == 0 || posn == tower_height (&a->unit_tower)) + return; - if ( actual_size < a->min_extent ) - padding = DIV_RND_UP (a->min_extent - actual_size, a->default_size); + n = tower_lookup (&a->unit_tower, posn, &start); + + existing_node = tower_data (n, struct axis_node, unit_node); + + /* Nothing needs to be done, if the range element is already split here */ + if ( posn - start == 0) + return; + + existing_unit_size = tower_node_get_size (&existing_node->unit_node); + existing_pixel_size = tower_node_get_size (&existing_node->pixel_node); + + fraction = (posn - start) / (gdouble) existing_unit_size; + + new_node = pool_malloc (a->pool, sizeof (*new_node)); + + tower_resize (&a->unit_tower, &existing_node->unit_node, posn - start); - return PSPPIRE_AXIS_GET_IFACE (a)->unit_count (a) + padding; + tower_resize (&a->pixel_tower, &existing_node->pixel_node, + rintf (fraction * existing_pixel_size)); + + tower_insert (&a->unit_tower, + existing_unit_size - (posn - start), + &new_node->unit_node, + tower_next (&a->unit_tower, &existing_node->unit_node)); + + + tower_insert (&a->pixel_tower, + rintf (existing_pixel_size * (1 - fraction)), + &new_node->pixel_node, + tower_next (&a->pixel_tower, &existing_node->pixel_node)); } -/* Return the starting pixel of UNIT */ -glong -psppire_axis_start_pixel (const PsppireAxis *a, gint unit) +/* Insert a new unit of size SIZE before POSN. + The value of the "padding" property will be added to SIZE before + the unit is inserted. + */ +void +psppire_axis_insert (PsppireAxis *a, gint posn, gint size) { - gint the_count, total_size ; - g_return_val_if_fail (PSPPIRE_IS_AXIS (a), -1); + struct axis_node *before = NULL; + struct axis_node *new_node; - the_count = PSPPIRE_AXIS_GET_IFACE (a)->unit_count (a); - total_size = PSPPIRE_AXIS_GET_IFACE (a)->total_size (a); + g_return_if_fail ( posn >= 0); + g_return_if_fail ( posn <= tower_height (&a->unit_tower)); - if ( unit >= the_count) + if ( posn < tower_height (&a->unit_tower)) { - return total_size + (unit - the_count) * a->default_size; + unsigned long int start = 0; + struct tower_node *n; + + split (a, posn); + + n = tower_lookup (&a->unit_tower, posn, &start); + g_assert (posn == start); + + before = tower_data (n, struct axis_node, unit_node); } - return PSPPIRE_AXIS_GET_IFACE (a)->start_pixel (a, unit); -} + new_node = pool_malloc (a->pool, sizeof (*new_node)); + tower_insert (&a->unit_tower, + 1, + &new_node->unit_node, + before ? &before->unit_node : NULL); -/* Return the unit covered by PIXEL */ -gint -psppire_axis_unit_at_pixel (const PsppireAxis *a, glong pixel) -{ - glong total_size; + tower_insert (&a->pixel_tower, + size + a->padding, + &new_node->pixel_node, + before ? &before->pixel_node : NULL); +} - g_return_val_if_fail (PSPPIRE_IS_AXIS (a), -1); - g_return_val_if_fail (PSPPIRE_AXIS_GET_IFACE (a), -1); +/* Make the element at POSN singular. + Return a pointer to the node for this element */ +static struct axis_node * +make_single (PsppireAxis *a, gint posn) +{ + unsigned long int start; + struct tower_node *n; - g_return_val_if_fail (PSPPIRE_AXIS_GET_IFACE (a)->unit_at_pixel, -1); + g_return_val_if_fail (posn < tower_height (&a->unit_tower), NULL); - total_size = PSPPIRE_AXIS_GET_IFACE (a)->total_size (a); + n = tower_lookup (&a->unit_tower, posn, &start); - if (pixel >= total_size) + if ( 1 != tower_node_get_size (n)) { - gint n_items = PSPPIRE_AXIS_GET_IFACE (a)->unit_count (a); - glong extra = pixel - total_size; - - return n_items - 1 + DIV_RND_UP (extra, a->default_size); + split (a, posn + 1); + n = tower_lookup (&a->unit_tower, posn, &start); + + if ( 1 != tower_node_get_size (n)) + { + split (a, posn); + n = tower_lookup (&a->unit_tower, posn, &start); + } } - return PSPPIRE_AXIS_GET_IFACE (a)->unit_at_pixel (a, pixel); + g_assert (1 == tower_node_get_size (n)); + + return tower_data (n, struct axis_node, unit_node); } -/* Set UNIT to size SIZE */ +/* + Set the size of the unit at POSN to be SIZE plus the + current value of "padding" + */ void -psppire_axis_resize (PsppireAxis *a, gint unit, glong size) +psppire_axis_resize (PsppireAxis *axis, gint posn, glong size) { - g_return_if_fail (PSPPIRE_IS_AXIS (a)); + struct axis_node *an; + g_return_if_fail (posn >= 0); + g_return_if_fail (size > 0); - g_return_if_fail (PSPPIRE_AXIS_GET_IFACE (a)); + /* Silently ignore this request if the position is greater than the number of + units in the axis */ + if (posn >= tower_height (&axis->unit_tower)) + return ; - g_return_if_fail (size > 0); + an = make_single (axis, posn); - if (PSPPIRE_AXIS_GET_IFACE (a)->resize) - PSPPIRE_AXIS_GET_IFACE (a)->resize (a, unit, size); + tower_resize (&axis->pixel_tower, &an->pixel_node, size + axis->padding); + g_signal_emit (axis, signals[RESIZE_UNIT], 0, posn, size + axis->padding); +} - g_signal_emit (a, signals [RESIZE_UNIT], 0, unit, size); + + + + + +void +psppire_axis_clear (PsppireAxis *a) +{ + pool_destroy (a->pool); + a->pool = pool_create (); + + tower_init (&a->pixel_tower); + tower_init (&a->unit_tower); } + +void +psppire_axis_delete (PsppireAxis *a, gint first, gint n_units) +{ + gint units_to_delete = n_units; + unsigned long int start; + struct tower_node *unit_node ; + g_return_if_fail (first + n_units <= tower_height (&a->unit_tower)); + + split (a, first); + split (a, first + n_units); + + unit_node = tower_lookup (&a->unit_tower, first, &start); + g_assert (start == first); + + while (units_to_delete > 0) + { + struct tower_node *next_unit_node; + struct axis_node *an = tower_data (unit_node, + struct axis_node, unit_node); + + g_assert (unit_node == &an->unit_node); + g_assert (unit_node->size <= n_units); + + units_to_delete -= unit_node->size; + + next_unit_node = tower_next (&a->unit_tower, unit_node); + + tower_delete (&a->unit_tower, unit_node); + tower_delete (&a->pixel_tower, &an->pixel_node); + + pool_free (a->pool, an); + + unit_node = next_unit_node; + } +}