/* This is -*- C -*- */
/* $Id: guppi-pie-state.c,v 1.36 2002/01/08 06:28:56 trow Exp $ */

/*
 * guppi-pie-state.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-pie-state.h"

#include <libgnome/gnome-i18n.h>
#include <libgnomeui/gnome-color-picker.h>
#include <glade/glade.h>

#include <math.h>
#include <guppi-useful.h>
#include <guppi-rgb.h>
#include <guppi-defaults.h>
#include <guppi-data-select.h>
#include <guppi-metrics.h>
#include <guppi-seq-scalar-core.h>
#include <guppi-seq-string.h>
#include <guppi-seq-integer.h>
#include <guppi-data-socket.h>
#include <guppi-data-flavor.h>
#include <guppi-color-palette.h>
#include <guppi-attribute-widget.h>

#include "guppi-pie-view.h"

static GtkObjectClass *parent_class = NULL;

static void
guppi_pie_state_finalize (GtkObject *obj)
{
  GuppiPieState *state = GUPPI_PIE_STATE (obj);

  guppi_unref0 (state->slice_offsets);

  if (parent_class->finalize)
    parent_class->finalize (obj);
}

static void
guppi_pie_state_make_config_model (GuppiElementState *state, GuppiConfigModel *model)
{
  guppi_config_model_add_glade_file (model,
				     "Pie", "Radius",
				     GUPPI_CONFIG_APPEARANCE,
				     guppi_element_state_attribute_bag (state),
				     "guppi-pie-state-config.glade", "radius",
				     NULL, NULL, NULL);

  guppi_config_model_add_glade_file (model,
				     "Pie", "Labels",
				     GUPPI_CONFIG_APPEARANCE,
				     guppi_element_state_attribute_bag (state),
				     "guppi-pie-state-config.glade", "labels",
				     NULL, NULL, NULL);

  if (GUPPI_ELEMENT_STATE_CLASS (parent_class)->make_config_model)
    GUPPI_ELEMENT_STATE_CLASS (parent_class)->make_config_model (state, model);
}

/***************************************************************************/

static GuppiSeqScalar *
get_slice_offsets (GuppiPieState *state)
{
  if (state->slice_offsets == NULL) {
    GuppiSeq *pie_data;
    gint i0, i1;

    guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			     "data", &pie_data,
			     NULL);

    if (pie_data == NULL)
      return NULL;

    guppi_seq_bounds (GUPPI_SEQ (pie_data), &i0, &i1);

    state->slice_offsets = GUPPI_SEQ_SCALAR (guppi_seq_scalar_core_new ());
    guppi_seq_size_hint (GUPPI_SEQ (state->slice_offsets), i1 - i0 + 1);
    guppi_seq_grow_to_include_range (GUPPI_SEQ (state->slice_offsets), i0, i1);

    guppi_unref0 (pie_data);
  }

  return state->slice_offsets;
}

static void
soff_insert_cb (GuppiSeq *slice_data, gint i0, gsize N,
		GuppiSeqScalar *soff)
{
  gsize j = 0;
  for (j = 0; j < N; ++j)
    guppi_seq_scalar_insert (soff, i0, 0);
}

static void
soff_delete_cb (GuppiSeq *slice_data, gint i, gsize N, GuppiSeqScalar *soff)
{
  guppi_seq_delete_many (GUPPI_SEQ (soff), i, N);
}

static void
synchronize_slice_and_offset_data (GuppiPieState *state)
{
  GuppiSeqScalar *pie_data;
  GuppiSeqScalar *soff;

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "data", &pie_data,
			   NULL);
  soff = get_slice_offsets (state);

  g_return_if_fail (soff != NULL);
  if (state->data_copy) {
    gtk_signal_disconnect_by_data (GTK_OBJECT (state->data_copy), soff);
    guppi_unref0 (state->data_copy);
  }

  gtk_signal_connect (GTK_OBJECT (pie_data),
		      "changed_insert",
		      GTK_SIGNAL_FUNC (soff_insert_cb), soff);

  gtk_signal_connect (GTK_OBJECT (pie_data),
		      "changed_delete",
		      GTK_SIGNAL_FUNC (soff_delete_cb), soff);

  state->data_copy = pie_data;
  /* No need to ref, since data is refed on a get */
  /* guppi_ref (state->data_copy); */
}

static void
bag_changed_cb (GuppiAttributeBag *bag, const gchar *key, gpointer closure)
{
  GuppiPieState *state = GUPPI_PIE_STATE (closure);

  if (!strcmp (key, "data")) {
    synchronize_slice_and_offset_data (state);
  }
}

/************************************************************************/

void
guppi_pie_state_slice_bounds (GuppiPieState *state, gint *i0, gint *i1)
{
  GuppiSeqScalar *data;

  g_return_if_fail (GUPPI_IS_PIE_STATE (state));

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "data", &data,
			   NULL);
  if (data == NULL) {
    if (i0) *i0 = 0;
    if (i1) *i1 = -1;
    return;
  }

  guppi_seq_bounds (GUPPI_SEQ (data), i0, i1);

  guppi_unref (data);

#if 0
  if (GUPPI_IS_SEQ_SCALAR (data)) {
    guppi_seq_bounds (GUPPI_SEQ (data), i0, i1);
  } else if (GUPPI_IS_SEQ_CATEGORICAL (data)) {
    GuppiCategory *cat;
    cat = guppi_seq_categorical_category (GUPPI_SEQ_CATEGORICAL (data));
    if (i0) *i0 = (gint)guppi_category_min_code (cat);
    if (i1) *i1 = (gint)guppi_category_max_code (cat);
  } else {
    if (i0) *i0 = 0;
    if (i1) *i1 = -1;
    g_warning ("Unknown data type.");
  }
#endif
}

double
guppi_pie_state_slice_percentage (GuppiPieState *state, gint i)
{
  GuppiSeqScalar *data;
  gint i0, i1;
  double x, y;

  g_return_val_if_fail (GUPPI_IS_PIE_STATE (state), -1);
  
  guppi_pie_state_slice_bounds (state, &i0, &i1);
  g_return_val_if_fail (i0 <= i && i <= i1, -1);

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "data", &data,
			   NULL);
  if (data == NULL)
    return -1;

  x = fabs (guppi_seq_scalar_get (data, i));
  y = guppi_seq_scalar_sum_abs (data);

  guppi_unref (data);
  
  return y > 0 ? x/y : -1;

#if 0
  if (GUPPI_IS_SEQ_SCALAR (data)) {
    GuppiSeqScalar *ss = GUPPI_SEQ_SCALAR (data);
    double x = fabs (guppi_seq_scalar_get (ss, i));
    double y = guppi_seq_scalar_sum_abs (ss);
    return y > 0 ? x/y : -1;
  } else if (GUPPI_IS_SEQ_CATEGORICAL (data)) {
    GuppiSeqCategorical *seq;
    GuppiCategory *cat;
    const gchar *name;

    seq = GUPPI_SEQ_CATEGORICAL (data);
    cat = guppi_seq_categorical_category (seq);
    name = guppi_category_find_by_code (cat, i);
    return guppi_seq_categorical_percentage (seq, name);
  } else {
    g_warning ("Unknown data type.");
    return -1;
  }
#endif
}

gboolean
guppi_pie_state_need_separate_label_data (GuppiPieState *state)
{
  g_return_val_if_fail (GUPPI_IS_PIE_STATE (state), FALSE);

  return TRUE;

#if 0
  data = guppi_pie_state_data (state);
  if (data == NULL)
    return FALSE;

  return ! GUPPI_IS_SEQ_CATEGORICAL (data);
#endif
}

const gchar *
guppi_pie_state_slice_label (GuppiPieState *state, gint i)
{
  GuppiSeqString *data;
  const gchar *str = NULL;

  g_return_val_if_fail (GUPPI_IS_PIE_STATE (state), NULL);

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "label_data", &data,
			   NULL);
  if (data == NULL)
    return NULL;

  if (guppi_seq_in_bounds (GUPPI_SEQ (data), i))
    str = guppi_seq_string_get (data, i);
  
  guppi_unref (data);

  return str;

#if 0
  if (GUPPI_IS_SEQ_SCALAR (data)) {
    GuppiSeqString *ld = GUPPI_SEQ_STRING (guppi_pie_state_labels (state));
    if (ld == NULL)
      return NULL;
    if (!guppi_seq_in_bounds (GUPPI_SEQ (ld), i))
      return NULL;
    return guppi_seq_string_get (ld, i);
  } else if (GUPPI_IS_SEQ_CATEGORICAL (data)) {
    GuppiCategory *cat;
    cat = guppi_seq_categorical_category (GUPPI_SEQ_CATEGORICAL (data));
    return guppi_category_find_by_code (cat, i);
  } else {
    g_warning ("Unknown data type.");
    return NULL;
  }
#endif
}

guint32
guppi_pie_state_slice_color (GuppiPieState *state, gint i)
{
  guint32 color, fallback_color;
  gboolean use_stock_colors, fallback_to_stock_colors;
  GuppiColorPalette *pal;
  static GuppiColorPalette *stock_pal = NULL;

  g_return_val_if_fail (GUPPI_IS_PIE_STATE (state), RGBA_WHITE);

  if (stock_pal == NULL) {
    stock_pal = guppi_color_palette_new ();
    guppi_permanent_alloc (stock_pal);
  }

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "fallback_color",           &fallback_color,
			   "use_stock_colors",         &use_stock_colors,
			   "fallback_to_stock_colors", &fallback_to_stock_colors,
			   "slice_colors",             &pal,
			   NULL);

  color = fallback_color;

  if (use_stock_colors || (pal == NULL && fallback_to_stock_colors)) {
    color = guppi_color_palette_get (stock_pal, i);
  } else if (pal != NULL) {
    color = guppi_color_palette_get (pal, i);
  }

  guppi_unref (pal);
  return color;
}

double
guppi_pie_state_slice_offset (GuppiPieState *state, gint i)
{
  GuppiSeqScalar *soff;
  double offset;

  g_return_val_if_fail (GUPPI_IS_PIE_STATE (state), 0);

  soff = get_slice_offsets (state);

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "base_offset", &offset,
			   NULL);

  if (guppi_seq_in_bounds (GUPPI_SEQ (soff), i)) {

    offset += MAX (guppi_seq_scalar_get (soff, i), 0);
  }

  return MAX (offset, 0);
}

void
guppi_pie_state_set_slice_offset (GuppiPieState *state, gint i, double x)
{
  GuppiSeqScalar *soff;
  double base_offset;

  g_return_if_fail (GUPPI_IS_PIE_STATE (state));

  guppi_element_state_get (GUPPI_ELEMENT_STATE (state),
			   "base_offset", &base_offset,
			   NULL);

  x -= base_offset;

  soff = get_slice_offsets (state);

  if (!guppi_seq_in_bounds (GUPPI_SEQ (soff), i))
    return;

  guppi_seq_scalar_set (soff, i, x);
  guppi_element_state_changed (GUPPI_ELEMENT_STATE (state));
}

/***************************************************************************/

static void
guppi_pie_state_class_init (GuppiPieStateClass *klass)
{
  GtkObjectClass *object_class = (GtkObjectClass *) klass;
  GuppiElementStateClass *state_class = GUPPI_ELEMENT_STATE_CLASS (klass);

  parent_class = gtk_type_class (GUPPI_TYPE_ELEMENT_STATE);

  object_class->finalize = guppi_pie_state_finalize;

  state_class->name = _("Pie Chart");

  state_class->view_type = GUPPI_TYPE_PIE_VIEW;
  state_class->make_config_model = guppi_pie_state_make_config_model;
}

static void
guppi_pie_state_init (GuppiPieState *obj)
{
  GuppiAttributeBag *bag;
  double inch = guppi_in2pt (1.0);

  bag = guppi_element_state_attribute_bag (GUPPI_ELEMENT_STATE (obj));

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DATA_SOCKET, "data::socket::adopt",       NULL, guppi_data_socket_new_by_type (GUPPI_TYPE_SEQ_SCALAR));
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DATA_SOCKET, "label_data::socket::adopt", NULL, guppi_data_socket_new_by_type (GUPPI_TYPE_SEQ_STRING));

  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION,     "radius",              NULL, inch);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,       "radius_maximize",     NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,       "radius_lock",         NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DIMENSION,     "edge_width",          NULL, inch / 48);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,          "edge_color",          NULL, RGBA_BLACK);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DOUBLE,        "base_offset",         NULL, 0.0);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_DOUBLE,        "base_angle",          NULL, 0.0);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_COLOR_PALETTE, "slice_colors",        NULL, NULL);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,       "use_stock_colors",    NULL, FALSE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,       "fallback_to_stock_colors", NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,          "fallback_color",      NULL, RGBA_RED);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_BOOLEAN,       "show_percentage",     NULL, TRUE);
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_FONT,          "label_font::adopt",   NULL, guppi_default_font ());
  guppi_attribute_bag_add_with_default (bag, GUPPI_ATTR_RGBA,          "label_color",         NULL, RGBA_BLACK);

  gtk_signal_connect (GTK_OBJECT (bag),
		      "changed",
		      GTK_SIGNAL_FUNC (bag_changed_cb),
		      obj);
}

GtkType
guppi_pie_state_get_type (void)
{
  static GtkType guppi_pie_state_type = 0;
  if (!guppi_pie_state_type) {
    static const GtkTypeInfo guppi_pie_state_info = {
      "GuppiPieState",
      sizeof (GuppiPieState),
      sizeof (GuppiPieStateClass),
      (GtkClassInitFunc) guppi_pie_state_class_init,
      (GtkObjectInitFunc) guppi_pie_state_init,
      NULL, NULL, (GtkClassInitFunc) NULL
    };
    guppi_pie_state_type = gtk_type_unique (GUPPI_TYPE_ELEMENT_STATE, &guppi_pie_state_info);
  }
  return guppi_pie_state_type;
}

GuppiElementState *
guppi_pie_state_new (void)
{
  return GUPPI_ELEMENT_STATE (guppi_type_new (guppi_pie_state_get_type ()));
}

/* $Id: guppi-pie-state.c,v 1.36 2002/01/08 06:28:56 trow Exp $ */


syntax highlighted by Code2HTML, v. 0.9.1