/* This is -*- C -*- */
/* $Id: guppi-barchart-view.c,v 1.23 2001/10/15 20:58:40 trow Exp $ */
/*
* guppi-barchart-view.c
*
* Copyright (C) 2000 EMC Capital Management, Inc.
* Copyright (C) 2001 The Free Software Foundation
*
* Developed by Jon Trowbridge <trow@gnu.org> and
* Havoc Pennington <hp@pobox.com>.
*
* 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA
*/
#include <config.h>
#include "guppi-barchart-view.h"
#include "guppi-barchart-state.h"
#include "guppi-barchart-item.h"
#include "guppi-barchart-print.h"
#include "guppi-barchart-tools.h"
#include <guppi-data-table.h>
static GtkObjectClass *parent_class = NULL;
/***************************************************************************/
static gboolean
preferred_range (GuppiElementView *view, guppi_axis_t ax, double *a, double *b)
{
GuppiElementState *state = guppi_element_view_state (view);
gboolean vertical_bars;
double m, M, w;
guppi_element_state_get (state,
"vertical_bars", &vertical_bars,
NULL);
if (ax == (vertical_bars ? GUPPI_X_AXIS : GUPPI_Y_AXIS)) {
if (a)
*a = 0;
if (b) {
gint R;
guppi_barchart_state_table_dimensions (GUPPI_BARCHART_STATE (state), &R, NULL);
*b = R;
}
return TRUE;
}
if (! guppi_barchart_state_bar_bounds (GUPPI_BARCHART_STATE (state), &m, &M))
return FALSE;
/* Add a little padding. We like to keep the zero line flush, though. */
w = 0.025 * (M - m);
if (fabs (m) > 1e-8)
m -= w;
if (fabs (M) > 1e-8)
M += w;
if (a)
*a = m;
if (b)
*b = M;
return TRUE;
}
static void
update_axis_markers (GuppiElementView *view,
guppi_axis_t ax,
GuppiAxisMarkers *markers,
double x0, double x1)
{
GuppiElementState *state = guppi_element_view_state (view);
GuppiDataTable *table;
gboolean vertical_bars, normalize_stacks;
guppi_element_state_get (state,
"vertical_bars", &vertical_bars,
"normalize_stacks", &normalize_stacks,
"data", &table,
NULL);
if (table == NULL)
return;
guppi_axis_markers_freeze (markers);
guppi_axis_markers_clear (markers);
if (ax == (vertical_bars ? GUPPI_X_AXIS : GUPPI_Y_AXIS)) {
gint R, i, i0, i1;
guppi_barchart_state_table_dimensions (GUPPI_BARCHART_STATE (state), &R, NULL);
i0 = MAX ((gint) floor (x0), 0);
i1 = MIN ((gint) ceil (x1), R-1);
for (i = i0; i <= i1; ++i) {
const gchar *str = guppi_data_table_get_row_label (table, i);
double pos = (vertical_bars ? i : R-1-i)+0.5;
if (str && *str)
guppi_axis_markers_add_critical (markers, pos, GUPPI_TICK_NONE, str);
}
} else {
guppi_axis_markers_populate_generic (markers,
normalize_stacks ? GUPPI_AXIS_PERCENTAGE : GUPPI_AXIS_SCALAR,
x0, x1);
}
guppi_axis_markers_thaw (markers);
guppi_unref (table);
}
static void
changed_state (GuppiElementView *view)
{
const gchar *attr;
attr = guppi_element_state_get_changed_attribute (guppi_element_view_state (view));
if (attr) {
if (!strcmp (attr, "vertical_bars") || !strcmp (attr, "stacked")) {
guppi_element_view_set_preferred_view (view, GUPPI_X_AXIS);
guppi_element_view_set_preferred_view (view, GUPPI_Y_AXIS);
}
}
if (GUPPI_ELEMENT_VIEW_CLASS (parent_class)->changed_state)
GUPPI_ELEMENT_VIEW_CLASS (parent_class)->changed_state (view);
}
/***************************************************************************/
static void
guppi_barchart_view_finalize (GtkObject * obj)
{
if (parent_class->finalize)
parent_class->finalize (obj);
}
static void
guppi_barchart_view_class_init (GuppiBarchartViewClass * klass)
{
GtkObjectClass *object_class = (GtkObjectClass *) klass;
GuppiElementViewClass *view_class = GUPPI_ELEMENT_VIEW_CLASS (klass);
parent_class = gtk_type_class (GUPPI_TYPE_ELEMENT_VIEW);
object_class->finalize = guppi_barchart_view_finalize;
view_class->canvas_item_type = GUPPI_TYPE_BARCHART_ITEM;
view_class->print_type = GUPPI_TYPE_BARCHART_PRINT;
view_class->preferred_range = preferred_range;
view_class->update_axis_markers = update_axis_markers;
view_class->changed_state = changed_state;
}
static void
guppi_barchart_view_init (GuppiBarchartView * obj)
{
#if 0
/* The key is to just have something non-zero here, to trigger
axis construction. */
guppi_element_view_set_x_axis_marker_type (GUPPI_ELEMENT_VIEW (obj),
GUPPI_AXIS_SCALAR);
guppi_element_view_set_y_axis_marker_type (GUPPI_ELEMENT_VIEW (obj),
GUPPI_AXIS_SCALAR);
#endif
guppi_element_view_add_axis_markers (GUPPI_ELEMENT_VIEW (obj), GUPPI_X_AXIS);
guppi_element_view_add_axis_markers (GUPPI_ELEMENT_VIEW (obj), GUPPI_Y_AXIS);
}
GtkType guppi_barchart_view_get_type (void)
{
static GtkType guppi_barchart_view_type = 0;
if (!guppi_barchart_view_type) {
static const GtkTypeInfo guppi_barchart_view_info = {
"GuppiBarchartView",
sizeof (GuppiBarchartView),
sizeof (GuppiBarchartViewClass),
(GtkClassInitFunc) guppi_barchart_view_class_init,
(GtkObjectInitFunc) guppi_barchart_view_init,
NULL, NULL, (GtkClassInitFunc) NULL
};
guppi_barchart_view_type =
gtk_type_unique (GUPPI_TYPE_ELEMENT_VIEW, &guppi_barchart_view_info);
}
return guppi_barchart_view_type;
}
gboolean
guppi_barchart_view_bar_position (GuppiBarchartView *bc_view,
gint r, gint c,
double *bar_x0, double *bar_y0,
double *bar_x1, double *bar_y1,
guint32 *color)
{
GuppiElementView *view;
GuppiBarchartState *state;
double x0, y0, x1, y1, w, min, max;
double bx0, by0, bx1, by1;
double bar_margin, cluster_margin;
gboolean vertical_bars, stacked;
gint R, C, virtualC;
g_return_val_if_fail (GUPPI_IS_BARCHART_VIEW (bc_view), FALSE);
if (r < 0 || c < 0)
return FALSE;
view = GUPPI_ELEMENT_VIEW (bc_view);
state = GUPPI_BARCHART_STATE (guppi_element_view_state (view));
if (! guppi_barchart_state_table_dimensions (state, &R, &C))
return FALSE;
if (r >= R || c >= C)
return FALSE;
virtualC = C;
/* Maybe these should be view attributes */
guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
"bar_margin", &bar_margin,
"cluster_margin", &cluster_margin,
"vertical_bars", &vertical_bars,
"stacked", &stacked,
NULL);
if (stacked) {
virtualC = 1;
}
guppi_element_view_get_bbox_pt (view, &x0, &y0, &x1, &y1);
guppi_barchart_state_bar_info (state, r, c, &min, &max, color);
/* The bars from row r lie in the interval [r, r+1]. */
cluster_margin = CLAMP (cluster_margin, 0, 1);
bx0 = r + cluster_margin / 2;
bx1 = r + 1 - cluster_margin / 2;
if (virtualC > 1) {
w = (bx1 - bx0) / virtualC;
bar_margin = CLAMP (bar_margin, 0, 1);
bx0 = bx0 + c * w + (bar_margin / 2) * w;
bx1 = bx0 + w - bar_margin * w;
}
by0 = min;
by1 = max;
/* If horizontal, flip coordinates */
if (! vertical_bars) {
double t;
bx0 = R - bx0;
bx1 = R - bx1;
t = bx0;
bx0 = by0;
by0 = t;
t = bx1;
bx1 = by1;
by1 = t;
}
guppi_element_view_vp2pt (view, bx0, by0, &bx0, &by0);
guppi_element_view_vp2pt (view, bx1, by1, &bx1, &by1);
guppi_2sort (&bx0, &bx1);
guppi_2sort (&by0, &by1);
if (bar_x0)
*bar_x0 = bx0;
if (bar_y0)
*bar_y0 = by0;
if (bar_x1)
*bar_x1 = bx1;
if (bar_y1)
*bar_y1 = by1;
return TRUE;
}
/* This could be optimized substantially. */
gboolean
guppi_barchart_view_find_bar_at_position (GuppiBarchartView *view,
double x, double y,
gint *r, gint *c)
{
GuppiBarchartState *state;
gint i, j, R, C;
double x0, y0, x1, y1;
g_return_val_if_fail (view != NULL && GUPPI_IS_BARCHART_VIEW (view), FALSE);
state = GUPPI_BARCHART_STATE (guppi_element_view_state (GUPPI_ELEMENT_VIEW (view)));
guppi_barchart_state_table_dimensions (state, &R, &C);
for (j = 0; j < C; ++j) {
for (i = 0; i < R; ++i) {
guppi_barchart_view_bar_position (view, i, j, &x0, &y0, &x1, &y1, NULL);
if (guppi_between (x0, x, x1) && guppi_between (y0, y, y1)) {
if (r)
*r = i;
if (c)
*c = j;
return TRUE;
}
}
}
return FALSE;
}
/* $Id: guppi-barchart-view.c,v 1.23 2001/10/15 20:58:40 trow Exp $ */
syntax highlighted by Code2HTML, v. 0.9.1