/* 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