sparkline: Add num_sets property so a sparkline graph can have multiple lines (Guido Gunther)

all data is still stored in a 1D array so we can still use g_param_spec_double
for type checking. This removes the support for non cairo builds.
This commit is contained in:
Cole Robinson 2008-10-07 10:08:14 -04:00
parent 1401dff4e2
commit 251b5bc516

View File

@ -44,6 +44,7 @@ enum {
PROP_DATAARRAY, PROP_DATAARRAY,
PROP_FILLED, PROP_FILLED,
PROP_REVERSED, PROP_REVERSED,
PROP_NUMSETS,
}; };
static gpointer parent_class; static gpointer parent_class;
@ -55,6 +56,8 @@ struct _GtkSparklinePrivate
{ {
gboolean filled; gboolean filled;
gboolean reversed; gboolean reversed;
gint num_sets;
gint points_per_set;
GValueArray *data_array; GValueArray *data_array;
}; };
@ -93,6 +96,8 @@ static void gtk_sparkline_init (GtkSparkline *sparkline)
priv->filled = TRUE; priv->filled = TRUE;
priv->reversed = FALSE; priv->reversed = FALSE;
priv->data_array = g_value_array_new(0); priv->data_array = g_value_array_new(0);
priv->num_sets = 1;
priv->points_per_set = 0;
g_signal_connect (G_OBJECT (sparkline), "expose_event", g_signal_connect (G_OBJECT (sparkline), "expose_event",
G_CALLBACK (gtk_sparkline_expose), NULL); G_CALLBACK (gtk_sparkline_expose), NULL);
@ -103,6 +108,7 @@ static void gtk_sparkline_class_init (GtkSparklineClass *class)
{ {
GObjectClass *object_class = G_OBJECT_CLASS (class); GObjectClass *object_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
GParamSpec *data_array_spec;
parent_class = g_type_class_peek_parent (class); parent_class = g_type_class_peek_parent (class);
@ -114,19 +120,28 @@ static void gtk_sparkline_class_init (GtkSparklineClass *class)
widget_class->size_request = gtk_sparkline_size_request; widget_class->size_request = gtk_sparkline_size_request;
//widget_class->expose_event = gtk_sparkline_expose; //widget_class->expose_event = gtk_sparkline_expose;
data_array_spec = g_param_spec_double("data_array_value",
"Data array value",
"GValueArray element",
0.0,
100.0,
0,
G_PARAM_READABLE | G_PARAM_WRITABLE);
g_object_class_install_property (object_class, g_object_class_install_property (object_class,
PROP_DATAARRAY, PROP_DATAARRAY,
g_param_spec_value_array ("data_array", g_param_spec_value_array ("data_array",
"Data array", "Data array",
"GValueArray of data", "GValueArray of data",
g_param_spec_double("data_array_value", data_array_spec,
"Data array value",
"GValueArray element",
0.0,
100.0,
0,
G_PARAM_READABLE | G_PARAM_WRITABLE),
G_PARAM_READABLE | G_PARAM_WRITABLE)); G_PARAM_READABLE | G_PARAM_WRITABLE));
g_object_class_install_property (object_class,
PROP_NUMSETS,
g_param_spec_int ("num_sets",
"Num Sets",
"Number of data sets in data",
1, 2, 1,
G_PARAM_READABLE | G_PARAM_WRITABLE));
g_object_class_install_property (object_class, g_object_class_install_property (object_class,
PROP_FILLED, PROP_FILLED,
g_param_spec_boolean ("filled", g_param_spec_boolean ("filled",
@ -168,6 +183,10 @@ static void gtk_sparkline_get_property (GObject *object,
g_value_set_boxed(value, priv->data_array); g_value_set_boxed(value, priv->data_array);
break; break;
case PROP_NUMSETS:
g_value_set_int(value, priv->num_sets);
break;
case PROP_FILLED: case PROP_FILLED:
g_value_set_boolean(value, priv->filled); g_value_set_boolean(value, priv->filled);
break; break;
@ -197,6 +216,7 @@ gtk_sparkline_set_property (GObject *object,
case PROP_DATAARRAY: case PROP_DATAARRAY:
g_value_array_free(priv->data_array); g_value_array_free(priv->data_array);
priv->data_array = g_value_array_copy(g_value_get_boxed(value)); priv->data_array = g_value_array_copy(g_value_get_boxed(value));
priv->points_per_set = priv->data_array->n_values / priv->num_sets;
gtk_widget_queue_draw(GTK_WIDGET(object)); gtk_widget_queue_draw(GTK_WIDGET(object));
break; break;
@ -208,6 +228,10 @@ gtk_sparkline_set_property (GObject *object,
priv->reversed = g_value_get_boolean(value); priv->reversed = g_value_get_boolean(value);
break; break;
case PROP_NUMSETS:
priv->num_sets = g_value_get_int(value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
break; break;
@ -225,22 +249,22 @@ static void gtk_sparkline_size_request(GtkWidget *widget,
data = priv->data_array; data = priv->data_array;
if (area) { if (area) {
area->width = data->n_values; area->width = data->n_values / priv->num_sets;
area->height = 20; area->height = 20;
} }
} }
static double get_y (GtkAllocation *cell_area, static double get_y (GtkSparklinePrivate *priv, GtkAllocation *cell_area,
GValueArray *data, GValueArray *data, int set, int index)
int index, int reversed)
{ {
double baseline_y = cell_area->height; double baseline_y = cell_area->height;
int n; int n;
if (reversed) n = set * priv->points_per_set;
n = data->n_values - 1 - index; if (priv->reversed)
n += priv->points_per_set - 1 - index;
else else
n = index; n += index;
GValue *val = g_value_array_get_nth(data, n); GValue *val = g_value_array_get_nth(data, n);
return baseline_y - ((cell_area->height-1) * g_value_get_double(val)); return baseline_y - ((cell_area->height-1) * g_value_get_double(val));
@ -253,27 +277,15 @@ gtk_sparkline_expose (GtkWidget *widget,
{ {
GtkSparklinePrivate *priv; GtkSparklinePrivate *priv;
GValueArray *data; GValueArray *data;
GdkPoint *points; int index, set;
int index;
double pixels_per_point; double pixels_per_point;
GtkAllocation *cell_area = &widget->allocation; GtkAllocation *cell_area = &widget->allocation;
#if USE_CAIRO
cairo_t *cr; cairo_t *cr;
#endif
priv = GTK_SPARKLINE_GET_PRIVATE (widget); priv = GTK_SPARKLINE_GET_PRIVATE (widget);
data = priv->data_array; data = priv->data_array;
pixels_per_point = (double)cell_area->width / ((double)priv->points_per_set-1);
pixels_per_point = (double)cell_area->width / ((double)data->n_values-1);
points = g_new(GdkPoint, data->n_values);
for (index=0;index<data->n_values;index++) {
double cx = ((double)index * pixels_per_point);
double cy = get_y (cell_area, data, index, priv->reversed);
points[index].x = cx;
points[index].y = cy;
}
gdk_draw_rectangle(widget->window, gdk_draw_rectangle(widget->window,
widget->style->mid_gc[GTK_WIDGET_STATE (widget)], widget->style->mid_gc[GTK_WIDGET_STATE (widget)],
@ -300,7 +312,6 @@ gtk_sparkline_expose (GtkWidget *widget,
cell_area->height/NTICKS*index); cell_area->height/NTICKS*index);
} }
#if USE_CAIRO
cr = gdk_cairo_create (widget->window); cr = gdk_cairo_create (widget->window);
/* Clip to the cell: */ /* Clip to the cell: */
@ -308,26 +319,33 @@ gtk_sparkline_expose (GtkWidget *widget,
cairo_rectangle (cr, 0, 0, cell_area->width, cell_area->height); cairo_rectangle (cr, 0, 0, cell_area->width, cell_area->height);
cairo_clip (cr); cairo_clip (cr);
/* Render the line: */ /* Render the lines: */
cairo_set_line_width (cr, (double)0.5); cairo_set_line_width (cr, (double)0.5);
for (index=0;index<data->n_values;index++) { for (set=0; set < priv->num_sets; set++) {
double cx = points[index].x; /* FIXME: add property to add line color */
double cy = points[index].y; if (set)
if (index) { cairo_set_source_rgb (cr, 0.25, 0.25, 0.25);
cairo_line_to (cr, cx, cy); else
} else { cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
cairo_move_to (cr, cx, cy); for (index=0; index < priv->points_per_set; index++) {
double cx = ((double)index * pixels_per_point);
double cy = get_y (priv, cell_area, data, set, index);
if (index) {
cairo_line_to (cr, cx, cy);
} else {
cairo_move_to (cr, cx, cy);
}
} }
} if (priv->points_per_set) {
if (data->n_values) { if (priv->filled) {
if (priv->filled) { double baseline_y = cell_area->height + cell_area->y;
double baseline_y = cell_area->height + cell_area->y; cairo_line_to (cr, cell_area->x + cell_area->width, baseline_y);
cairo_line_to (cr, cell_area->x + cell_area->width, baseline_y); cairo_line_to (cr, 0, baseline_y);
cairo_line_to (cr, 0, baseline_y); cairo_fill (cr);
cairo_fill (cr); } else {
} else { cairo_stroke (cr);
cairo_stroke (cr); }
} }
} }
@ -336,14 +354,6 @@ gtk_sparkline_expose (GtkWidget *widget,
cairo_destroy (cr); cairo_destroy (cr);
#else
gdk_draw_lines(widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
points, data->n_values);
#endif
g_free(points);
return TRUE; return TRUE;
} }