/* PSPPIRE - a graphical user interface for PSPP.
- Copyright (C) 2008 Free Software Foundation
+ Copyright (C) 2008, 2009 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
#include <string.h>
#include <stdlib.h>
+#include <ui/gui/psppire-marshal.h>
#include <libpspp/tower.h>
#include <libpspp/pool.h>
-#include <libpspp/misc.h>
#include "psppire-axis.h"
-#include <ui/gui/psppire-marshal.h>
-#include <gtk/gtk.h>
-
+#include <math.h>
+#include <libpspp/misc.h>
/* Signals */
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) +
+ nearbyint (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 nearbyint (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
};
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;
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;
};
}
+
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",
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] =
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);
+}
+
+
+\f
+
+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,
+ nearbyintf (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,
+ nearbyintf (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;
+ }
+}