/* 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 and * Havoc Pennington . * * 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 #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 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 $ */